Added simple variable lifetimes
This commit is contained in:
parent
dc497ba4fb
commit
ec5240425b
@ -14,5 +14,6 @@ func (f *Function) CompileAssignment(expr *expression.Expression) error {
|
||||
return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, expr.Children[0].Token.Position)
|
||||
}
|
||||
|
||||
defer f.useVariable(variable)
|
||||
return f.Execute(expr.Token, variable.Register, expr.Children[1])
|
||||
}
|
||||
|
@ -62,6 +62,17 @@ func (f *Function) ExecuteFunctionCall(expr *expression.Expression) error {
|
||||
// ExecuteLeaf performs an operation on a register with the given leaf operand.
|
||||
func (f *Function) ExecuteLeaf(operation token.Token, register cpu.Register, operand token.Token) error {
|
||||
switch operand.Kind {
|
||||
case token.Identifier:
|
||||
name := operand.Text()
|
||||
variable, exists := f.Variables[name]
|
||||
|
||||
if !exists {
|
||||
return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, operand.Position)
|
||||
}
|
||||
|
||||
defer f.useVariable(variable)
|
||||
return f.ExecuteRegisterRegister(operation, register, variable.Register)
|
||||
|
||||
case token.Number:
|
||||
value := operand.Text()
|
||||
number, err := strconv.Atoi(value)
|
||||
@ -71,16 +82,6 @@ func (f *Function) ExecuteLeaf(operation token.Token, register cpu.Register, ope
|
||||
}
|
||||
|
||||
return f.ExecuteRegisterNumber(operation, register, number)
|
||||
|
||||
case token.Identifier:
|
||||
name := operand.Text()
|
||||
variable, exists := f.Variables[name]
|
||||
|
||||
if !exists {
|
||||
return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, operand.Position)
|
||||
}
|
||||
|
||||
return f.ExecuteRegisterRegister(operation, register, variable.Register)
|
||||
}
|
||||
|
||||
return errors.New(errors.NotImplemented, f.File, operation.Position)
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/build/arch/x64"
|
||||
"git.akyoto.dev/cli/q/src/build/asm"
|
||||
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||
"git.akyoto.dev/cli/q/src/build/expression"
|
||||
@ -141,17 +142,51 @@ func (f *Function) ExpressionToRegister(root *expression.Expression, register cp
|
||||
|
||||
// ExpressionsToRegisters moves multiple expressions into the specified registers.
|
||||
func (f *Function) ExpressionsToRegisters(expressions []*expression.Expression, registers []cpu.Register) error {
|
||||
var destinations []cpu.Register
|
||||
|
||||
for i := len(expressions) - 1; i >= 0; i-- {
|
||||
err := f.ExpressionToRegister(expressions[i], registers[i])
|
||||
original := registers[i]
|
||||
expression := expressions[i]
|
||||
|
||||
if expression.IsLeaf() {
|
||||
variable, exists := f.Variables[expression.Token.Text()]
|
||||
|
||||
if exists && variable.Register == original {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
register := original
|
||||
save := !f.CPU.IsFree(register)
|
||||
|
||||
if save {
|
||||
register = x64.RAX
|
||||
}
|
||||
|
||||
err := f.ExpressionToRegister(expression, register)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if save {
|
||||
destinations = append(destinations, original)
|
||||
f.Assembler.Register(asm.PUSH, x64.RAX)
|
||||
}
|
||||
}
|
||||
|
||||
for i := len(destinations) - 1; i >= 0; i-- {
|
||||
f.Assembler.Register(asm.POP, destinations[i])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Function) Log(messages ...any) {
|
||||
fmt.Printf("[%s] ", f.Name)
|
||||
fmt.Println(messages...)
|
||||
}
|
||||
|
||||
// TokenToRegister moves a token into a register.
|
||||
// It only works with identifiers, numbers and strings.
|
||||
func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error {
|
||||
@ -168,6 +203,7 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error {
|
||||
f.Assembler.RegisterRegister(asm.MOVE, register, variable.Register)
|
||||
}
|
||||
|
||||
f.useVariable(variable)
|
||||
return nil
|
||||
|
||||
case token.Number:
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
|
||||
// Variable represents a variable in a function.
|
||||
type Variable struct {
|
||||
Name string
|
||||
Register cpu.Register
|
||||
Name string
|
||||
Register cpu.Register
|
||||
UsesRemaining int
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package build
|
||||
|
||||
import (
|
||||
"git.akyoto.dev/cli/q/src/build/expression"
|
||||
"git.akyoto.dev/cli/q/src/build/token"
|
||||
"git.akyoto.dev/cli/q/src/errors"
|
||||
)
|
||||
|
||||
@ -34,7 +35,37 @@ func (f *Function) CompileVariableDefinition(expr *expression.Expression) error
|
||||
Register: reg,
|
||||
}
|
||||
|
||||
f.Variables[name] = variable
|
||||
f.CPU.Use(reg)
|
||||
f.addVariable(variable)
|
||||
f.useVariable(variable)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Function) addVariable(variable *Variable) {
|
||||
variable.UsesRemaining = countIdentifier(f.Body, variable.Name)
|
||||
f.Variables[variable.Name] = variable
|
||||
f.CPU.Use(variable.Register)
|
||||
}
|
||||
|
||||
func (f *Function) useVariable(variable *Variable) {
|
||||
variable.UsesRemaining--
|
||||
|
||||
if variable.UsesRemaining < 0 {
|
||||
panic("incorrect number of variable use calls")
|
||||
}
|
||||
|
||||
if variable.UsesRemaining == 0 {
|
||||
f.CPU.Free(variable.Register)
|
||||
}
|
||||
}
|
||||
|
||||
func countIdentifier(tokens token.List, name string) int {
|
||||
count := 0
|
||||
|
||||
for _, t := range tokens {
|
||||
if t.Kind == token.Identifier && t.Text() == name {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
@ -224,43 +224,41 @@ func scanFile(path string, functions chan<- *Function) error {
|
||||
return errors.New(errors.ExpectedFunctionDefinition, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
cpu := cpu.CPU{
|
||||
Call: x64.CallRegisters,
|
||||
General: x64.GeneralRegisters,
|
||||
Syscall: x64.SyscallRegisters,
|
||||
Return: x64.ReturnValueRegisters,
|
||||
function := &Function{
|
||||
Name: tokens[nameStart].Text(),
|
||||
File: file,
|
||||
Body: tokens[bodyStart:i],
|
||||
Variables: map[string]*Variable{},
|
||||
CPU: cpu.CPU{
|
||||
Call: x64.CallRegisters,
|
||||
General: x64.GeneralRegisters,
|
||||
Syscall: x64.SyscallRegisters,
|
||||
Return: x64.ReturnValueRegisters,
|
||||
},
|
||||
Assembler: asm.Assembler{
|
||||
Instructions: make([]asm.Instruction, 0, 32),
|
||||
},
|
||||
}
|
||||
|
||||
parameters := tokens[paramsStart:paramsEnd]
|
||||
variables := map[string]*Variable{}
|
||||
|
||||
err := expression.EachParameter(parameters, func(parameter token.List) error {
|
||||
if len(parameter) == 1 {
|
||||
name := parameter[0].Text()
|
||||
register := x64.SyscallRegisters[1+len(variables)]
|
||||
variables[name] = &Variable{Name: name, Register: register}
|
||||
cpu.Use(register)
|
||||
err := expression.EachParameter(parameters, func(tokens token.List) error {
|
||||
if len(tokens) == 1 {
|
||||
name := tokens[0].Text()
|
||||
register := x64.CallRegisters[len(function.Variables)]
|
||||
variable := &Variable{Name: name, Register: register}
|
||||
function.addVariable(variable)
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New(errors.NotImplemented, file, parameter[0].Position)
|
||||
return errors.New(errors.NotImplemented, file, tokens[0].Position)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
functions <- &Function{
|
||||
Name: tokens[nameStart].Text(),
|
||||
File: file,
|
||||
Body: tokens[bodyStart:i],
|
||||
Variables: variables,
|
||||
CPU: cpu,
|
||||
Assembler: asm.Assembler{
|
||||
Instructions: make([]asm.Instruction, 0, 32),
|
||||
},
|
||||
}
|
||||
|
||||
functions <- function
|
||||
nameStart = -1
|
||||
paramsStart = -1
|
||||
bodyStart = -1
|
||||
|
Loading…
Reference in New Issue
Block a user