From 4d88260333cd154d78e77aafb2b3459cde2683b4 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Wed, 3 Jul 2024 16:37:59 +0200 Subject: [PATCH] Improved code generation --- src/build/core/CompileCall.go | 56 ++++++---- src/build/core/CompileDefinition.go | 23 +--- src/build/core/CompileReturn.go | 2 +- src/build/core/Evaluate.go | 123 ---------------------- src/build/core/Execute.go | 57 +++------- src/build/core/ExecuteLeaf.go | 37 +++++++ src/build/core/ExecuteRegisterNumber.go | 33 ++++++ src/build/core/ExecuteRegisterRegister.go | 35 ++++++ src/build/core/ExpressionToRegister.go | 33 ++++++ src/build/core/ExpressionsToRegisters.go | 20 ++++ src/build/core/Function.go | 4 +- src/build/core/TokenToRegister.go | 2 +- src/build/core/opRegister.go | 60 ----------- src/build/core/state.go | 1 - src/build/cpu/CPU.go | 4 +- 15 files changed, 215 insertions(+), 275 deletions(-) delete mode 100644 src/build/core/Evaluate.go create mode 100644 src/build/core/ExecuteLeaf.go create mode 100644 src/build/core/ExecuteRegisterNumber.go create mode 100644 src/build/core/ExecuteRegisterRegister.go create mode 100644 src/build/core/ExpressionToRegister.go create mode 100644 src/build/core/ExpressionsToRegisters.go delete mode 100644 src/build/core/opRegister.go diff --git a/src/build/core/CompileCall.go b/src/build/core/CompileCall.go index 283439c..02943fa 100644 --- a/src/build/core/CompileCall.go +++ b/src/build/core/CompileCall.go @@ -1,7 +1,7 @@ package core import ( - "git.akyoto.dev/cli/q/src/build/cpu" + "git.akyoto.dev/cli/q/src/build/asm" "git.akyoto.dev/cli/q/src/build/expression" ) @@ -9,9 +9,43 @@ import ( // All call registers must hold the correct parameter values before the function invocation. // Registers that are in use must be saved if they are modified by the function. // After the function call, they must be restored in reverse order. -func (f *Function) CompileCall(expr *expression.Expression) error { - _, err := f.EvaluateCall(expr) - return err +func (f *Function) CompileCall(root *expression.Expression) error { + funcName := root.Children[0].Token.Text() + parameters := root.Children[1:] + registers := f.cpu.Input[:len(parameters)] + isSyscall := funcName == "syscall" + + if isSyscall { + registers = f.cpu.Syscall[:len(parameters)] + } + + err := f.ExpressionsToRegisters(parameters, registers) + + if err != nil { + return err + } + + for _, register := range f.cpu.General { + if !f.cpu.IsFree(register) { + f.assembler.Register(asm.PUSH, register) + } + } + + if isSyscall { + f.assembler.Syscall() + } else { + f.assembler.Call(funcName) + } + + for i := len(f.cpu.General) - 1; i >= 0; i-- { + register := f.cpu.General[i] + + if !f.cpu.IsFree(register) { + f.assembler.Register(asm.POP, register) + } + } + + return nil } // CompileSyscall executes a syscall. @@ -23,17 +57,3 @@ func (f *Function) CompileSyscall(expr *expression.Expression) error { f.sideEffects++ return err } - -// ExpressionsToRegisters moves multiple expressions into the specified registers. -func (f *Function) ExpressionsToRegisters(expressions []*expression.Expression, registers []cpu.Register) error { - for i := len(registers) - 1; i >= 0; i-- { - f.SaveRegister(registers[i]) - err := f.EvaluateTo(expressions[i], registers[i]) - - if err != nil { - return err - } - } - - return nil -} diff --git a/src/build/core/CompileDefinition.go b/src/build/core/CompileDefinition.go index 7abd309..13b361f 100644 --- a/src/build/core/CompileDefinition.go +++ b/src/build/core/CompileDefinition.go @@ -59,27 +59,6 @@ func (f *Function) AddVariable(variable *Variable) { f.cpu.Use(variable.Register) } -func (f *Function) addTemporary(root *expression.Expression) *Variable { - f.count.tmps++ - name := fmt.Sprintf("t%d", f.count.tmps) - register := f.cpu.MustUseFree(f.cpu.General) - - tmp := &Variable{ - Name: name, - Value: root, - Alive: 1, - Register: register, - } - - f.variables[name] = tmp - - if config.Comments { - f.assembler.Comment(fmt.Sprintf("%s = %s (%s)", name, root, register)) - } - - return tmp -} - func (f *Function) useVariable(variable *Variable) { variable.Alive-- @@ -121,7 +100,7 @@ func (f *Function) storeVariableInRegister(name string, value *expression.Expres panic("no free registers") } - err := f.EvaluateTo(value, reg) + err := f.ExpressionToRegister(value, reg) f.AddVariable(&Variable{ Name: name, diff --git a/src/build/core/CompileReturn.go b/src/build/core/CompileReturn.go index c829a91..4effe9a 100644 --- a/src/build/core/CompileReturn.go +++ b/src/build/core/CompileReturn.go @@ -12,5 +12,5 @@ func (f *Function) CompileReturn(node *ast.Return) error { return nil } - return f.EvaluateTo(node.Value, f.cpu.Return[0]) + return f.ExpressionToRegister(node.Value, f.cpu.Output[0]) } diff --git a/src/build/core/Evaluate.go b/src/build/core/Evaluate.go deleted file mode 100644 index ed65df0..0000000 --- a/src/build/core/Evaluate.go +++ /dev/null @@ -1,123 +0,0 @@ -package core - -import ( - "git.akyoto.dev/cli/q/src/build/asm" - "git.akyoto.dev/cli/q/src/build/ast" - "git.akyoto.dev/cli/q/src/build/cpu" - "git.akyoto.dev/cli/q/src/build/expression" - "git.akyoto.dev/cli/q/src/build/token" -) - -// Evaluate evaluates the result of an expression and saves it into a temporary register. -func (f *Function) Evaluate(root *expression.Expression) (*Variable, error) { - if root.IsLeaf() { - return f.EvaluateLeaf(root) - } - - if ast.IsFunctionCall(root) { - return f.EvaluateCall(root) - } - - left := root.Children[0] - right := root.Children[1] - - tmpLeft, err := f.Evaluate(left) - - if err != nil { - return nil, err - } - - tmpLeftExpr := expression.NewLeaf(token.Token{ - Kind: token.Identifier, - Position: left.Token.Position, - Bytes: []byte(tmpLeft.Name), - }) - - tmpLeftExpr.Parent = root - root.Children[0].Parent = nil - root.Children[0] = tmpLeftExpr - - tmpRight, err := f.Evaluate(right) - - if err != nil { - return nil, err - } - - tmpRightExpr := expression.NewLeaf(token.Token{ - Kind: token.Identifier, - Position: left.Token.Position, - Bytes: []byte(tmpRight.Name), - }) - - tmpRightExpr.Parent = root - root.Children[1].Parent = nil - root.Children[1] = tmpRightExpr - - tmp := f.addTemporary(root) - - f.assembler.RegisterRegister(asm.MOVE, tmp.Register, tmpLeft.Register) - f.useVariable(tmpLeft) - - err = f.opRegisterRegister(root.Token, tmp.Register, tmpRight.Register) - f.useVariable(tmpRight) - - return tmp, err -} - -func (f *Function) EvaluateCall(root *expression.Expression) (*Variable, error) { - funcName := root.Children[0].Token.Text() - parameters := root.Children[1:] - registers := f.cpu.Call[:len(parameters)] - isSyscall := funcName == "syscall" - - if isSyscall { - registers = f.cpu.Syscall[:len(parameters)] - } - - err := f.ExpressionsToRegisters(parameters, registers) - - for _, register := range f.cpu.General { - if !f.cpu.IsFree(register) { - f.assembler.Register(asm.PUSH, register) - } - } - - if isSyscall { - f.assembler.Syscall() - } else { - f.assembler.Call(funcName) - } - - for i := len(f.cpu.General) - 1; i >= 0; i-- { - register := f.cpu.General[i] - - if !f.cpu.IsFree(register) { - f.assembler.Register(asm.POP, register) - } - } - - tmp := f.addTemporary(root) - f.assembler.RegisterRegister(asm.MOVE, tmp.Register, f.cpu.Return[0]) - return tmp, err -} - -func (f *Function) EvaluateLeaf(root *expression.Expression) (*Variable, error) { - tmp := f.addTemporary(root) - err := f.TokenToRegister(root.Token, tmp.Register) - return tmp, err -} - -func (f *Function) EvaluateTo(root *expression.Expression, register cpu.Register) error { - tmp, err := f.Evaluate(root) - - if err != nil { - return err - } - - if register != tmp.Register { - f.assembler.RegisterRegister(asm.MOVE, register, tmp.Register) - } - - f.useVariable(tmp) - return nil -} diff --git a/src/build/core/Execute.go b/src/build/core/Execute.go index 067f854..c42c4d1 100644 --- a/src/build/core/Execute.go +++ b/src/build/core/Execute.go @@ -1,11 +1,8 @@ package core import ( - "strconv" - "git.akyoto.dev/cli/q/src/build/ast" "git.akyoto.dev/cli/q/src/build/cpu" - "git.akyoto.dev/cli/q/src/build/errors" "git.akyoto.dev/cli/q/src/build/expression" "git.akyoto.dev/cli/q/src/build/token" ) @@ -16,54 +13,24 @@ func (f *Function) Execute(operation token.Token, register cpu.Register, value * return f.ExecuteLeaf(operation, register, value.Token) } - var temporary cpu.Register - if ast.IsFunctionCall(value) { - temporary = f.cpu.Return[0] - } else { - found := false - temporary, found = f.cpu.FindFree(f.cpu.General) - - if !found { - panic("no free registers") - } - } - - f.cpu.Use(temporary) - defer f.cpu.Free(temporary) - err := f.EvaluateTo(value, temporary) - - if err != nil { - return err - } - - return f.opRegisterRegister(operation, register, temporary) -} - -// 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.opRegisterRegister(operation, register, variable.Register) - - case token.Number: - value := operand.Text() - number, err := strconv.Atoi(value) + err := f.CompileCall(value) if err != nil { return err } - return f.opRegisterNumber(operation, register, number) + return f.ExecuteRegisterRegister(operation, register, f.cpu.Output[0]) } - return errors.New(errors.NotImplemented, f.File, operation.Position) + tmp := f.cpu.MustUseFree(f.cpu.General) + defer f.cpu.Free(tmp) + + err := f.ExpressionToRegister(value, tmp) + + if err != nil { + return err + } + + return f.ExecuteRegisterRegister(operation, register, tmp) } diff --git a/src/build/core/ExecuteLeaf.go b/src/build/core/ExecuteLeaf.go new file mode 100644 index 0000000..1349870 --- /dev/null +++ b/src/build/core/ExecuteLeaf.go @@ -0,0 +1,37 @@ +package core + +import ( + "strconv" + + "git.akyoto.dev/cli/q/src/build/cpu" + "git.akyoto.dev/cli/q/src/build/errors" + "git.akyoto.dev/cli/q/src/build/token" +) + +// 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) + + if err != nil { + return err + } + + return f.ExecuteRegisterNumber(operation, register, number) + } + + return errors.New(errors.NotImplemented, f.File, operation.Position) +} diff --git a/src/build/core/ExecuteRegisterNumber.go b/src/build/core/ExecuteRegisterNumber.go new file mode 100644 index 0000000..01039ed --- /dev/null +++ b/src/build/core/ExecuteRegisterNumber.go @@ -0,0 +1,33 @@ +package core + +import ( + "git.akyoto.dev/cli/q/src/build/asm" + "git.akyoto.dev/cli/q/src/build/cpu" + "git.akyoto.dev/cli/q/src/build/errors" + "git.akyoto.dev/cli/q/src/build/token" +) + +// ExecuteRegisterNumber performs an operation on a register and a number. +func (f *Function) ExecuteRegisterNumber(operation token.Token, register cpu.Register, number int) error { + switch operation.Text() { + case "+", "+=": + f.assembler.RegisterNumber(asm.ADD, register, number) + + case "-", "-=": + f.assembler.RegisterNumber(asm.SUB, register, number) + + case "*", "*=": + f.assembler.RegisterNumber(asm.MUL, register, number) + + case "/", "/=": + f.assembler.RegisterNumber(asm.DIV, register, number) + + case "=": + f.assembler.RegisterNumber(asm.MOVE, register, number) + + default: + return errors.New(&errors.InvalidOperator{Operator: operation.Text()}, f.File, operation.Position) + } + + return nil +} diff --git a/src/build/core/ExecuteRegisterRegister.go b/src/build/core/ExecuteRegisterRegister.go new file mode 100644 index 0000000..b271f9a --- /dev/null +++ b/src/build/core/ExecuteRegisterRegister.go @@ -0,0 +1,35 @@ +package core + +import ( + "git.akyoto.dev/cli/q/src/build/asm" + "git.akyoto.dev/cli/q/src/build/cpu" + "git.akyoto.dev/cli/q/src/build/errors" + "git.akyoto.dev/cli/q/src/build/token" +) + +// ExecuteRegisterRegister performs an operation on two registers. +func (f *Function) ExecuteRegisterRegister(operation token.Token, destination cpu.Register, source cpu.Register) error { + switch operation.Text() { + case "+", "+=": + f.assembler.RegisterRegister(asm.ADD, destination, source) + + case "-", "-=": + f.assembler.RegisterRegister(asm.SUB, destination, source) + + case "*", "*=": + f.assembler.RegisterRegister(asm.MUL, destination, source) + + case "/", "/=": + f.assembler.RegisterRegister(asm.DIV, destination, source) + + case "=": + if destination != source { + f.assembler.RegisterRegister(asm.MOVE, destination, source) + } + + default: + return errors.New(&errors.InvalidOperator{Operator: operation.Text()}, f.File, operation.Position) + } + + return nil +} diff --git a/src/build/core/ExpressionToRegister.go b/src/build/core/ExpressionToRegister.go new file mode 100644 index 0000000..1528e26 --- /dev/null +++ b/src/build/core/ExpressionToRegister.go @@ -0,0 +1,33 @@ +package core + +import ( + "git.akyoto.dev/cli/q/src/build/asm" + "git.akyoto.dev/cli/q/src/build/ast" + "git.akyoto.dev/cli/q/src/build/cpu" + "git.akyoto.dev/cli/q/src/build/expression" +) + +// ExpressionToRegister puts the result of an expression into the specified register. +func (f *Function) ExpressionToRegister(root *expression.Expression, register cpu.Register) error { + if root.IsLeaf() { + return f.TokenToRegister(root.Token, register) + } + + if ast.IsFunctionCall(root) { + err := f.CompileCall(root) + f.assembler.RegisterRegister(asm.MOVE, register, f.cpu.Output[0]) + return err + } + + left := root.Children[0] + right := root.Children[1] + + err := f.ExpressionToRegister(left, register) + + if err != nil { + return err + } + + f.SaveRegister(register) + return f.Execute(root.Token, register, right) +} diff --git a/src/build/core/ExpressionsToRegisters.go b/src/build/core/ExpressionsToRegisters.go new file mode 100644 index 0000000..6e293e2 --- /dev/null +++ b/src/build/core/ExpressionsToRegisters.go @@ -0,0 +1,20 @@ +package core + +import ( + "git.akyoto.dev/cli/q/src/build/cpu" + "git.akyoto.dev/cli/q/src/build/expression" +) + +// ExpressionsToRegisters moves multiple expressions into the specified registers. +func (f *Function) ExpressionsToRegisters(expressions []*expression.Expression, registers []cpu.Register) error { + for i := len(registers) - 1; i >= 0; i-- { + f.SaveRegister(registers[i]) + err := f.ExpressionToRegister(expressions[i], registers[i]) + + if err != nil { + return err + } + } + + return nil +} diff --git a/src/build/core/Function.go b/src/build/core/Function.go index 518affb..a80e7d1 100644 --- a/src/build/core/Function.go +++ b/src/build/core/Function.go @@ -31,10 +31,10 @@ func NewFunction(name string, file *fs.File, body token.List) *Function { Instructions: make([]asm.Instruction, 0, 32), }, cpu: cpu.CPU{ - Call: x64.CallRegisters, + Input: x64.CallRegisters, General: x64.GeneralRegisters, Syscall: x64.SyscallRegisters, - Return: x64.ReturnValueRegisters, + Output: x64.ReturnValueRegisters, }, definitions: map[string]*Definition{}, variables: map[string]*Variable{}, diff --git a/src/build/core/TokenToRegister.go b/src/build/core/TokenToRegister.go index faf4036..bdce4d6 100644 --- a/src/build/core/TokenToRegister.go +++ b/src/build/core/TokenToRegister.go @@ -18,7 +18,7 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error { constant, exists := f.definitions[name] if exists { - return f.EvaluateTo(constant.Value, register) + return f.ExpressionToRegister(constant.Value, register) } variable, exists := f.variables[name] diff --git a/src/build/core/opRegister.go b/src/build/core/opRegister.go deleted file mode 100644 index 2cdd81c..0000000 --- a/src/build/core/opRegister.go +++ /dev/null @@ -1,60 +0,0 @@ -package core - -import ( - "git.akyoto.dev/cli/q/src/build/asm" - "git.akyoto.dev/cli/q/src/build/cpu" - "git.akyoto.dev/cli/q/src/build/errors" - "git.akyoto.dev/cli/q/src/build/token" -) - -// opRegisterNumber performs an operation on a register and a number. -func (f *Function) opRegisterNumber(operation token.Token, register cpu.Register, number int) error { - switch operation.Text() { - case "+", "+=": - f.assembler.RegisterNumber(asm.ADD, register, number) - - case "-", "-=": - f.assembler.RegisterNumber(asm.SUB, register, number) - - case "*", "*=": - f.assembler.RegisterNumber(asm.MUL, register, number) - - case "/", "/=": - f.assembler.RegisterNumber(asm.DIV, register, number) - - case "=": - f.assembler.RegisterNumber(asm.MOVE, register, number) - - default: - return errors.New(&errors.InvalidOperator{Operator: operation.Text()}, f.File, operation.Position) - } - - return nil -} - -// opRegisterRegister performs an operation on two registers. -func (f *Function) opRegisterRegister(operation token.Token, destination cpu.Register, source cpu.Register) error { - switch operation.Text() { - case "+", "+=": - f.assembler.RegisterRegister(asm.ADD, destination, source) - - case "-", "-=": - f.assembler.RegisterRegister(asm.SUB, destination, source) - - case "*", "*=": - f.assembler.RegisterRegister(asm.MUL, destination, source) - - case "/", "/=": - f.assembler.RegisterRegister(asm.DIV, destination, source) - - case "=": - if destination != source { - f.assembler.RegisterRegister(asm.MOVE, destination, source) - } - - default: - return errors.New(&errors.InvalidOperator{Operator: operation.Text()}, f.File, operation.Position) - } - - return nil -} diff --git a/src/build/core/state.go b/src/build/core/state.go index 70078c3..6a0d5e8 100644 --- a/src/build/core/state.go +++ b/src/build/core/state.go @@ -24,7 +24,6 @@ type state struct { // counter stores how often a certain statement appeared so we can generate a unique label from it. type counter struct { loop int - tmps int } // PrintInstructions shows the assembly instructions. diff --git a/src/build/cpu/CPU.go b/src/build/cpu/CPU.go index 8f0f9f3..2015a38 100644 --- a/src/build/cpu/CPU.go +++ b/src/build/cpu/CPU.go @@ -2,10 +2,10 @@ package cpu // CPU represents the processor. type CPU struct { - Call []Register General []Register Syscall []Register - Return []Register + Input []Register + Output []Register usage uint64 }