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) 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]) 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. // 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 { func (f *Function) ExecuteLeaf(operation token.Token, register cpu.Register, operand token.Token) error {
switch operand.Kind { 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: case token.Number:
value := operand.Text() value := operand.Text()
number, err := strconv.Atoi(value) 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) 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) return errors.New(errors.NotImplemented, f.File, operation.Position)

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"strconv" "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/asm"
"git.akyoto.dev/cli/q/src/build/cpu" "git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/build/expression" "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. // ExpressionsToRegisters moves multiple expressions into the specified registers.
func (f *Function) ExpressionsToRegisters(expressions []*expression.Expression, registers []cpu.Register) error { func (f *Function) ExpressionsToRegisters(expressions []*expression.Expression, registers []cpu.Register) error {
var destinations []cpu.Register
for i := len(expressions) - 1; i >= 0; i-- { 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 { if err != nil {
return err 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 return nil
} }
func (f *Function) Log(messages ...any) {
fmt.Printf("[%s] ", f.Name)
fmt.Println(messages...)
}
// TokenToRegister moves a token into a register. // TokenToRegister moves a token into a register.
// It only works with identifiers, numbers and strings. // It only works with identifiers, numbers and strings.
func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error { 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.Assembler.RegisterRegister(asm.MOVE, register, variable.Register)
} }
f.useVariable(variable)
return nil return nil
case token.Number: case token.Number:

View File

@ -8,4 +8,5 @@ import (
type Variable struct { type Variable struct {
Name string Name string
Register cpu.Register Register cpu.Register
UsesRemaining int
} }

View File

@ -2,6 +2,7 @@ package build
import ( import (
"git.akyoto.dev/cli/q/src/build/expression" "git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/build/token"
"git.akyoto.dev/cli/q/src/errors" "git.akyoto.dev/cli/q/src/errors"
) )
@ -34,7 +35,37 @@ func (f *Function) CompileVariableDefinition(expr *expression.Expression) error
Register: reg, Register: reg,
} }
f.Variables[name] = variable f.addVariable(variable)
f.CPU.Use(reg) f.useVariable(variable)
return nil 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) return errors.New(errors.ExpectedFunctionDefinition, file, tokens[i].Position)
} }
cpu := cpu.CPU{ function := &Function{
Name: tokens[nameStart].Text(),
File: file,
Body: tokens[bodyStart:i],
Variables: map[string]*Variable{},
CPU: cpu.CPU{
Call: x64.CallRegisters, Call: x64.CallRegisters,
General: x64.GeneralRegisters, General: x64.GeneralRegisters,
Syscall: x64.SyscallRegisters, Syscall: x64.SyscallRegisters,
Return: x64.ReturnValueRegisters, Return: x64.ReturnValueRegisters,
},
Assembler: asm.Assembler{
Instructions: make([]asm.Instruction, 0, 32),
},
} }
parameters := tokens[paramsStart:paramsEnd] parameters := tokens[paramsStart:paramsEnd]
variables := map[string]*Variable{}
err := expression.EachParameter(parameters, func(parameter token.List) error { err := expression.EachParameter(parameters, func(tokens token.List) error {
if len(parameter) == 1 { if len(tokens) == 1 {
name := parameter[0].Text() name := tokens[0].Text()
register := x64.SyscallRegisters[1+len(variables)] register := x64.CallRegisters[len(function.Variables)]
variables[name] = &Variable{Name: name, Register: register} variable := &Variable{Name: name, Register: register}
cpu.Use(register) function.addVariable(variable)
return nil return nil
} }
return errors.New(errors.NotImplemented, file, parameter[0].Position) return errors.New(errors.NotImplemented, file, tokens[0].Position)
}) })
if err != nil { if err != nil {
return err return err
} }
functions <- &Function{ 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),
},
}
nameStart = -1 nameStart = -1
paramsStart = -1 paramsStart = -1
bodyStart = -1 bodyStart = -1