Added simple variable lifetimes

This commit is contained in:
Eduard Urbach 2024-06-27 15:23:02 +02:00
parent dc497ba4fb
commit ec5240425b
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
6 changed files with 107 additions and 39 deletions

View File

@ -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])
}

View File

@ -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)

View File

@ -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:

View File

@ -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
}

View File

@ -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
}

View File

@ -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