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)
|
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])
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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:
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
// Variable represents a variable in a function.
|
// Variable represents a variable in a function.
|
||||||
type Variable struct {
|
type Variable struct {
|
||||||
Name string
|
Name string
|
||||||
Register cpu.Register
|
Register cpu.Register
|
||||||
|
UsesRemaining int
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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{
|
||||||
Call: x64.CallRegisters,
|
Name: tokens[nameStart].Text(),
|
||||||
General: x64.GeneralRegisters,
|
File: file,
|
||||||
Syscall: x64.SyscallRegisters,
|
Body: tokens[bodyStart:i],
|
||||||
Return: x64.ReturnValueRegisters,
|
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]
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user