From 43863928447c91aef9fe9720e47da3eb39dcf18d Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Tue, 9 Jul 2024 17:00:04 +0200 Subject: [PATCH] Improved code generation --- src/build/asm/Instructions.go | 10 -- src/build/asm/Optimizer.go | 4 - src/build/core/Compare.go | 1 - src/build/core/CompileCall.go | 12 +-- src/build/core/CompileCondition.go | 28 +++--- src/build/core/CompileDefinition.go | 4 +- src/build/core/CompileIf.go | 5 +- src/build/core/CompileLoop.go | 4 +- src/build/core/CompileReturn.go | 2 +- src/build/core/Execute.go | 1 - src/build/core/ExecuteRegisterNumber.go | 12 +-- src/build/core/ExecuteRegisterRegister.go | 12 +-- src/build/core/ExpressionToRegister.go | 7 +- src/build/core/Function.go | 4 +- src/build/core/Instructions.go | 98 +++++++++++++++++++ src/build/core/SaveRegister.go | 11 ++- src/build/core/TokenToRegister.go | 4 +- src/build/core/state.go | 42 +++++--- src/build/cpu/CPU.go | 16 +-- .../{branch-combined.q => branch-both.q} | 0 tests/programs/param-multi.q | 12 +++ tests/programs/{overwrite.q => param.q} | 0 tests/programs_test.go | 7 +- 23 files changed, 201 insertions(+), 95 deletions(-) create mode 100644 src/build/core/Instructions.go rename tests/programs/{branch-combined.q => branch-both.q} (100%) create mode 100644 tests/programs/param-multi.q rename tests/programs/{overwrite.q => param.q} (100%) diff --git a/src/build/asm/Instructions.go b/src/build/asm/Instructions.go index 57bab73..805a0ea 100644 --- a/src/build/asm/Instructions.go +++ b/src/build/asm/Instructions.go @@ -68,16 +68,6 @@ func (a *Assembler) Call(name string) { }) } -// Jump jumps to a position that is identified by a label. -func (a *Assembler) Jump(name string) { - a.Instructions = append(a.Instructions, Instruction{ - Mnemonic: JUMP, - Data: &Label{ - Name: name, - }, - }) -} - // Return returns back to the caller. func (a *Assembler) Return() { if len(a.Instructions) > 0 && a.Instructions[len(a.Instructions)-1].Mnemonic == RETURN { diff --git a/src/build/asm/Optimizer.go b/src/build/asm/Optimizer.go index d91daa1..2ef72ae 100644 --- a/src/build/asm/Optimizer.go +++ b/src/build/asm/Optimizer.go @@ -4,10 +4,6 @@ import "git.akyoto.dev/cli/q/src/build/cpu" // unnecessary returns true if the register/register operation can be skipped. func (a *Assembler) unnecessary(mnemonic Mnemonic, left cpu.Register, right cpu.Register) bool { - if mnemonic == MOVE && left == right { - return true - } - if len(a.Instructions) == 0 { return false } diff --git a/src/build/core/Compare.go b/src/build/core/Compare.go index 955f67e..2e2f29b 100644 --- a/src/build/core/Compare.go +++ b/src/build/core/Compare.go @@ -41,7 +41,6 @@ func (f *Function) Compare(comparison *expression.Expression) error { return err } - f.cpu.Use(tmp) defer f.cpu.Free(tmp) return f.Execute(comparison.Token, tmp, right) } diff --git a/src/build/core/CompileCall.go b/src/build/core/CompileCall.go index ade18b5..6ed36de 100644 --- a/src/build/core/CompileCall.go +++ b/src/build/core/CompileCall.go @@ -37,22 +37,18 @@ func (f *Function) CompileCall(root *expression.Expression) error { f.SaveRegister(f.cpu.Output[0]) - for _, register := range registers { - f.SaveRegister(register) - } - // Push for _, register := range f.cpu.General { if f.cpu.IsUsed(register) { - f.assembler.Register(asm.PUSH, register) + f.Register(asm.PUSH, register) } } // Call if isSyscall { - f.assembler.Syscall() + f.Syscall() } else { - f.assembler.Call(funcName) + f.Call(funcName) } // Pop @@ -60,7 +56,7 @@ func (f *Function) CompileCall(root *expression.Expression) error { register := f.cpu.General[i] if f.cpu.IsUsed(register) { - f.assembler.Register(asm.POP, register) + f.Register(asm.POP, register) } } diff --git a/src/build/core/CompileCondition.go b/src/build/core/CompileCondition.go index 0365bc4..84c4a38 100644 --- a/src/build/core/CompileCondition.go +++ b/src/build/core/CompileCondition.go @@ -25,7 +25,7 @@ func (f *Function) CompileCondition(condition *expression.Expression, successLab f.JumpIfTrue(left.Token.Text(), successLabel) // Right - f.assembler.Label(asm.LABEL, leftFailLabel) + f.AddLabel(leftFailLabel) right := condition.Children[1] err = f.CompileCondition(right, successLabel, failLabel) @@ -52,7 +52,7 @@ func (f *Function) CompileCondition(condition *expression.Expression, successLab f.JumpIfFalse(left.Token.Text(), failLabel) // Right - f.assembler.Label(asm.LABEL, leftSuccessLabel) + f.AddLabel(leftSuccessLabel) right := condition.Children[1] err = f.CompileCondition(right, successLabel, failLabel) @@ -79,17 +79,17 @@ func (f *Function) CompileCondition(condition *expression.Expression, successLab func (f *Function) JumpIfFalse(operator string, label string) { switch operator { case "==": - f.assembler.Label(asm.JNE, label) + f.Jump(asm.JNE, label) case "!=": - f.assembler.Label(asm.JE, label) + f.Jump(asm.JE, label) case ">": - f.assembler.Label(asm.JLE, label) + f.Jump(asm.JLE, label) case "<": - f.assembler.Label(asm.JGE, label) + f.Jump(asm.JGE, label) case ">=": - f.assembler.Label(asm.JL, label) + f.Jump(asm.JL, label) case "<=": - f.assembler.Label(asm.JG, label) + f.Jump(asm.JG, label) } } @@ -97,16 +97,16 @@ func (f *Function) JumpIfFalse(operator string, label string) { func (f *Function) JumpIfTrue(operator string, label string) { switch operator { case "==": - f.assembler.Label(asm.JE, label) + f.Jump(asm.JE, label) case "!=": - f.assembler.Label(asm.JNE, label) + f.Jump(asm.JNE, label) case ">": - f.assembler.Label(asm.JG, label) + f.Jump(asm.JG, label) case "<": - f.assembler.Label(asm.JL, label) + f.Jump(asm.JL, label) case ">=": - f.assembler.Label(asm.JGE, label) + f.Jump(asm.JGE, label) case "<=": - f.assembler.Label(asm.JLE, label) + f.Jump(asm.JLE, label) } } diff --git a/src/build/core/CompileDefinition.go b/src/build/core/CompileDefinition.go index 7243d17..f2fd234 100644 --- a/src/build/core/CompileDefinition.go +++ b/src/build/core/CompileDefinition.go @@ -43,7 +43,7 @@ func (f *Function) CompileDefinition(node *ast.Define) error { func (f *Function) AddVariable(variable *Variable) { if config.Comments { - f.assembler.Comment(fmt.Sprintf("%s = %s (%s, %d uses)", variable.Name, variable.Value, variable.Register, variable.Alive)) + f.Comment(fmt.Sprintf("%s = %s (%s, %d uses)", variable.Name, variable.Value, variable.Register, variable.Alive)) } f.variables[variable.Name] = variable @@ -60,7 +60,7 @@ func (f *Function) useVariable(variable *Variable) { if variable.Alive == 0 { if config.Comments { - f.assembler.Comment(fmt.Sprintf("%s died (%s)", variable.Name, variable.Register)) + f.Comment(fmt.Sprintf("%s died (%s)", variable.Name, variable.Register)) } f.cpu.Free(variable.Register) diff --git a/src/build/core/CompileIf.go b/src/build/core/CompileIf.go index 7f0117f..7721bf6 100644 --- a/src/build/core/CompileIf.go +++ b/src/build/core/CompileIf.go @@ -3,7 +3,6 @@ package core import ( "fmt" - "git.akyoto.dev/cli/q/src/build/asm" "git.akyoto.dev/cli/q/src/build/ast" ) @@ -17,8 +16,8 @@ func (f *Function) CompileIf(branch *ast.If) error { return err } - f.assembler.Label(asm.LABEL, success) - defer f.assembler.Label(asm.LABEL, fail) + f.AddLabel(success) + defer f.AddLabel(fail) f.count.branch++ return f.CompileAST(branch.Body) } diff --git a/src/build/core/CompileLoop.go b/src/build/core/CompileLoop.go index b8fd1d8..7bf00c4 100644 --- a/src/build/core/CompileLoop.go +++ b/src/build/core/CompileLoop.go @@ -10,8 +10,8 @@ import ( // CompileLoop compiles a loop instruction. func (f *Function) CompileLoop(loop *ast.Loop) error { label := fmt.Sprintf("%s_loop_%d", f.Name, f.count.loop) - f.assembler.Label(asm.LABEL, label) - defer f.assembler.Jump(label) + f.AddLabel(label) + defer f.Jump(asm.JUMP, label) f.count.loop++ return f.CompileAST(loop.Body) } diff --git a/src/build/core/CompileReturn.go b/src/build/core/CompileReturn.go index 4effe9a..57219c4 100644 --- a/src/build/core/CompileReturn.go +++ b/src/build/core/CompileReturn.go @@ -6,7 +6,7 @@ import ( // CompileReturn compiles a return instruction. func (f *Function) CompileReturn(node *ast.Return) error { - defer f.assembler.Return() + defer f.Return() if node.Value == nil { return nil diff --git a/src/build/core/Execute.go b/src/build/core/Execute.go index 36d7271..b401dab 100644 --- a/src/build/core/Execute.go +++ b/src/build/core/Execute.go @@ -24,7 +24,6 @@ func (f *Function) Execute(operation token.Token, register cpu.Register, value * } tmp := f.cpu.MustFindFree(f.cpu.General) - f.cpu.Use(tmp) defer f.cpu.Free(tmp) err := f.ExpressionToRegister(value, tmp) diff --git a/src/build/core/ExecuteRegisterNumber.go b/src/build/core/ExecuteRegisterNumber.go index c85ac70..19b07ea 100644 --- a/src/build/core/ExecuteRegisterNumber.go +++ b/src/build/core/ExecuteRegisterNumber.go @@ -11,22 +11,22 @@ import ( func (f *Function) ExecuteRegisterNumber(operation token.Token, register cpu.Register, number int) error { switch operation.Text() { case "+", "+=": - f.assembler.RegisterNumber(asm.ADD, register, number) + f.RegisterNumber(asm.ADD, register, number) case "-", "-=": - f.assembler.RegisterNumber(asm.SUB, register, number) + f.RegisterNumber(asm.SUB, register, number) case "*", "*=": - f.assembler.RegisterNumber(asm.MUL, register, number) + f.RegisterNumber(asm.MUL, register, number) case "/", "/=": - f.assembler.RegisterNumber(asm.DIV, register, number) + f.RegisterNumber(asm.DIV, register, number) case "==", "!=", "<", "<=", ">", ">=": - f.assembler.RegisterNumber(asm.COMPARE, register, number) + f.RegisterNumber(asm.COMPARE, register, number) case "=": - f.assembler.RegisterNumber(asm.MOVE, register, number) + f.RegisterNumber(asm.MOVE, register, number) default: return errors.New(&errors.InvalidOperator{Operator: operation.Text()}, f.File, operation.Position) diff --git a/src/build/core/ExecuteRegisterRegister.go b/src/build/core/ExecuteRegisterRegister.go index 22a6be0..c163e23 100644 --- a/src/build/core/ExecuteRegisterRegister.go +++ b/src/build/core/ExecuteRegisterRegister.go @@ -11,22 +11,22 @@ import ( 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) + f.RegisterRegister(asm.ADD, destination, source) case "-", "-=": - f.assembler.RegisterRegister(asm.SUB, destination, source) + f.RegisterRegister(asm.SUB, destination, source) case "*", "*=": - f.assembler.RegisterRegister(asm.MUL, destination, source) + f.RegisterRegister(asm.MUL, destination, source) case "/", "/=": - f.assembler.RegisterRegister(asm.DIV, destination, source) + f.RegisterRegister(asm.DIV, destination, source) case "==", "!=", "<", "<=", ">", ">=": - f.assembler.RegisterRegister(asm.COMPARE, destination, source) + f.RegisterRegister(asm.COMPARE, destination, source) case "=": - f.assembler.RegisterRegister(asm.MOVE, destination, source) + f.RegisterRegister(asm.MOVE, destination, source) default: return errors.New(&errors.InvalidOperator{Operator: operation.Text()}, f.File, operation.Position) diff --git a/src/build/core/ExpressionToRegister.go b/src/build/core/ExpressionToRegister.go index 580e96f..793403c 100644 --- a/src/build/core/ExpressionToRegister.go +++ b/src/build/core/ExpressionToRegister.go @@ -18,7 +18,7 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp err := f.CompileCall(node) if register != f.cpu.Output[0] { - f.assembler.RegisterRegister(asm.MOVE, register, f.cpu.Output[0]) + f.RegisterRegister(asm.MOVE, register, f.cpu.Output[0]) } return err @@ -34,8 +34,6 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp if f.UsesRegister(right, register) { register = f.cpu.MustFindFree(f.cpu.General) - } else { - f.SaveRegister(register) } f.cpu.Reserve(register) @@ -45,11 +43,10 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp return err } - f.cpu.Use(register) err = f.Execute(node.Token, register, right) if register != final { - f.assembler.RegisterRegister(asm.MOVE, final, register) + f.RegisterRegister(asm.MOVE, final, register) f.cpu.Free(register) } diff --git a/src/build/core/Function.go b/src/build/core/Function.go index a9d90b8..0af8d06 100644 --- a/src/build/core/Function.go +++ b/src/build/core/Function.go @@ -46,9 +46,9 @@ func NewFunction(name string, file *fs.File, body token.List) *Function { // Compile turns a function into machine code. func (f *Function) Compile() { defer close(f.finished) - f.assembler.Label(asm.LABEL, f.Name) + f.AddLabel(f.Name) f.err = f.CompileTokens(f.Body) - f.assembler.Return() + f.Return() } // CompileTokens compiles a token list. diff --git a/src/build/core/Instructions.go b/src/build/core/Instructions.go new file mode 100644 index 0000000..d9bae49 --- /dev/null +++ b/src/build/core/Instructions.go @@ -0,0 +1,98 @@ +package core + +import ( + "git.akyoto.dev/cli/q/src/build/asm" + "git.akyoto.dev/cli/q/src/build/config" + "git.akyoto.dev/cli/q/src/build/cpu" +) + +func (f *Function) AddLabel(label string) { + f.assembler.Label(asm.LABEL, label) + f.postInstruction() +} + +func (f *Function) Call(label string) { + f.assembler.Call(label) + f.cpu.Use(f.cpu.Output[0]) + f.postInstruction() +} + +func (f *Function) Comment(comment string) { + f.assembler.Comment(comment) + f.postInstruction() +} + +func (f *Function) Jump(mnemonic asm.Mnemonic, label string) { + f.assembler.Label(mnemonic, label) + f.postInstruction() +} + +func (f *Function) Register(mnemonic asm.Mnemonic, a cpu.Register) { + f.assembler.Register(mnemonic, a) + + if mnemonic == asm.POP { + f.cpu.Use(a) + } + + f.postInstruction() +} + +func (f *Function) RegisterNumber(mnemonic asm.Mnemonic, a cpu.Register, b int) { + if f.cpu.IsUsed(a) && isDestructive(mnemonic) { + f.SaveRegister(a) + } + + f.assembler.RegisterNumber(mnemonic, a, b) + + if mnemonic == asm.MOVE { + f.cpu.Use(a) + } + + f.postInstruction() +} + +func (f *Function) RegisterRegister(mnemonic asm.Mnemonic, a cpu.Register, b cpu.Register) { + if mnemonic == asm.MOVE && a == b { + return + } + + if f.cpu.IsUsed(a) && isDestructive(mnemonic) { + f.SaveRegister(a) + } + + f.assembler.RegisterRegister(mnemonic, a, b) + + if mnemonic == asm.MOVE { + f.cpu.Use(a) + } + + f.postInstruction() +} + +func (f *Function) Return() { + f.assembler.Return() + f.postInstruction() +} + +func (f *Function) Syscall() { + f.assembler.Syscall() + f.cpu.Use(f.cpu.Output[0]) + f.postInstruction() +} + +func (f *Function) postInstruction() { + if !config.Assembler { + return + } + + f.registerHistory = append(f.registerHistory, f.cpu.Used) +} + +func isDestructive(mnemonic asm.Mnemonic) bool { + switch mnemonic { + case asm.MOVE, asm.ADD, asm.SUB, asm.MUL, asm.DIV: + return true + default: + return false + } +} diff --git a/src/build/core/SaveRegister.go b/src/build/core/SaveRegister.go index 44c5129..17f16cb 100644 --- a/src/build/core/SaveRegister.go +++ b/src/build/core/SaveRegister.go @@ -14,6 +14,12 @@ func (f *Function) SaveRegister(register cpu.Register) { return } + for _, general := range f.cpu.General { + if register == general { + return + } + } + var variable *Variable for _, v := range f.variables { @@ -29,13 +35,12 @@ func (f *Function) SaveRegister(register cpu.Register) { newRegister := f.cpu.MustFindFree(f.cpu.General) f.cpu.Reserve(newRegister) - f.cpu.Use(newRegister) if config.Comments { - f.assembler.Comment(fmt.Sprintf("save %s to %s", register, newRegister)) + f.Comment(fmt.Sprintf("save %s to %s", register, newRegister)) } - f.assembler.RegisterRegister(asm.MOVE, newRegister, register) + f.RegisterRegister(asm.MOVE, newRegister, register) f.cpu.Free(register) variable.Register = newRegister } diff --git a/src/build/core/TokenToRegister.go b/src/build/core/TokenToRegister.go index 3e25685..bbc3808 100644 --- a/src/build/core/TokenToRegister.go +++ b/src/build/core/TokenToRegister.go @@ -21,8 +21,8 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error { return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position) } - f.assembler.RegisterRegister(asm.MOVE, register, variable.Register) f.useVariable(variable) + f.RegisterRegister(asm.MOVE, register, variable.Register) return nil case token.Number: @@ -33,7 +33,7 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error { return err } - f.assembler.RegisterNumber(asm.MOVE, register, n) + f.RegisterNumber(asm.MOVE, register, n) return nil case token.String: diff --git a/src/build/core/state.go b/src/build/core/state.go index c10dc7e..e185533 100644 --- a/src/build/core/state.go +++ b/src/build/core/state.go @@ -1,6 +1,7 @@ package core import ( + "bytes" "fmt" "git.akyoto.dev/cli/q/src/build/asm" @@ -10,13 +11,14 @@ import ( // state is the data structure we embed in each function to preserve compilation state. type state struct { - err error - variables map[string]*Variable - functions map[string]*Function - finished chan struct{} - assembler asm.Assembler - cpu cpu.CPU - count counter + err error + variables map[string]*Variable + functions map[string]*Function + registerHistory []uint64 + finished chan struct{} + assembler asm.Assembler + cpu cpu.CPU + count counter } // counter stores how often a certain statement appeared so we can generate a unique label from it. @@ -28,30 +30,42 @@ type counter struct { // PrintInstructions shows the assembly instructions. func (s *state) PrintInstructions() { - ansi.Dim.Println("╭────────────────────────────────────────────────────╮") + ansi.Dim.Println("╭──────────────────────────────────────────────────────────────────────────────╮") - for _, x := range s.assembler.Instructions { + for i, x := range s.assembler.Instructions { ansi.Dim.Print("│ ") switch x.Mnemonic { case asm.LABEL: - ansi.Yellow.Printf("%-50s", x.Data.String()+":") + ansi.Yellow.Printf("%-44s", x.Data.String()+":") case asm.COMMENT: - ansi.Dim.Printf("%-50s", x.Data.String()) + ansi.Dim.Printf("%-44s", x.Data.String()) default: ansi.Green.Printf("%-12s", x.Mnemonic.String()) if x.Data != nil { - fmt.Printf("%-38s", x.Data.String()) + fmt.Printf("%-32s", x.Data.String()) } else { - fmt.Printf("%-38s", "") + fmt.Printf("%-32s", "") } } + registers := bytes.Buffer{} + used := s.registerHistory[i] + + for _, reg := range s.cpu.All { + if used&(1<