diff --git a/examples/hello/hello.q b/examples/hello/hello.q index 1da41c1..aace245 100644 --- a/examples/hello/hello.q +++ b/examples/hello/hello.q @@ -1,19 +1,8 @@ -// Comment main() { - address := 4194304 + 1 - length := (0 + 50 - 20) * 10 / 100 - - loop { - print(address, length) - } + x := f(2, 3) + syscall(60, x) } -// Comment -print(address, length) { - write(length-2, address, length) -} - -// Comment -write(fd, address, length) { - syscall(1, fd, address, length) +f(x, y) { + return (x + y) * (x + y) } \ No newline at end of file diff --git a/src/build/Execute.go b/src/build/Execute.go index 42ffeb7..9939aaa 100644 --- a/src/build/Execute.go +++ b/src/build/Execute.go @@ -3,7 +3,6 @@ package build import ( "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" @@ -17,10 +16,17 @@ func (f *Function) Execute(operation token.Token, register cpu.Register, value * return f.ExecuteLeaf(operation, register, value.Token) } - temporary, found := f.cpu.FindFree(f.cpu.General) + var temporary cpu.Register - if !found { - panic("no free registers") + if 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) @@ -122,6 +128,20 @@ func (f *Function) ExpressionToRegister(root *expression.Expression, register cp return f.TokenToRegister(operation, register) } + if isFunctionCall(root) { + err := f.CompileFunctionCall(root) + + if err != nil { + return err + } + + if register != f.cpu.Return[0] { + f.assembler.RegisterRegister(asm.MOVE, register, f.cpu.Return[0]) + } + + return nil + } + left := root.Children[0] right := root.Children[1] @@ -131,46 +151,21 @@ func (f *Function) ExpressionToRegister(root *expression.Expression, register cp return err } + f.SaveRegister(register) return f.Execute(operation, register, right) } // 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-- { - 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 - } + register := registers[i] 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 diff --git a/src/build/FunctionCall.go b/src/build/FunctionCall.go index 06fa966..99b71ba 100644 --- a/src/build/FunctionCall.go +++ b/src/build/FunctionCall.go @@ -1,17 +1,38 @@ package build -import "git.akyoto.dev/cli/q/src/build/expression" +import ( + "git.akyoto.dev/cli/q/src/build/cpu" + "git.akyoto.dev/cli/q/src/build/expression" +) // CompileFunctionCall executes a function call. func (f *Function) CompileFunctionCall(expr *expression.Expression) error { - funcName := expr.Children[0].Token.Text() - parameters := expr.Children[1:] + var ( + funcName = expr.Children[0].Token.Text() + parameters = expr.Children[1:] + isSyscall = funcName == "syscall" + registers []cpu.Register + ) - if funcName == "syscall" { - defer f.assembler.Syscall() - return f.ExpressionsToRegisters(parameters, f.cpu.Syscall) + if isSyscall { + registers = f.cpu.Syscall + } else { + registers = f.cpu.Call } - defer f.assembler.Call(funcName) - return f.ExpressionsToRegisters(parameters, f.cpu.Call) + registers = registers[:len(parameters)] + + for _, register := range registers { + f.SaveRegister(register) + } + + err := f.ExpressionsToRegisters(parameters, registers) + + if isSyscall { + f.assembler.Syscall() + } else { + f.assembler.Call(funcName) + } + + return err } diff --git a/src/build/Return.go b/src/build/Return.go index 63ff276..8ffd8fb 100644 --- a/src/build/Return.go +++ b/src/build/Return.go @@ -7,12 +7,18 @@ import ( // CompileReturn compiles a return instruction. func (f *Function) CompileReturn(tokens token.List) error { - if len(tokens) > 1 { - value := expression.Parse(tokens[1:]) - defer value.Close() - // TODO: Set the return value + defer f.assembler.Return() + + if len(tokens) == 1 { + return nil } - f.assembler.Return() - return nil + value := expression.Parse(tokens[1:]) + + if value == nil { + return nil + } + + defer value.Close() + return f.ExpressionToRegister(value, f.cpu.Return[0]) } diff --git a/src/build/SaveRegister.go b/src/build/SaveRegister.go new file mode 100644 index 0000000..08b7d11 --- /dev/null +++ b/src/build/SaveRegister.go @@ -0,0 +1,37 @@ +package build + +import ( + "git.akyoto.dev/cli/q/src/build/asm" + "git.akyoto.dev/cli/q/src/build/cpu" +) + +// SaveRegister attempts to move a variable occupying this register to another register. +func (f *Function) SaveRegister(register cpu.Register) { + if f.cpu.IsFree(register) { + return + } + + var variable *Variable + + for _, v := range f.variables { + if v.Register == register { + variable = v + break + } + } + + if variable == nil || variable.UsesRemaining == 0 { + return + } + + newRegister, exists := f.cpu.FindFree(f.cpu.General) + + if !exists { + panic("no free registers") + } + + f.assembler.RegisterRegister(asm.MOVE, newRegister, register) + f.cpu.Free(register) + f.cpu.Use(newRegister) + variable.Register = newRegister +} diff --git a/src/build/arch/x64/Registers.go b/src/build/arch/x64/Registers.go index 44807c4..8104f84 100644 --- a/src/build/arch/x64/Registers.go +++ b/src/build/arch/x64/Registers.go @@ -22,7 +22,7 @@ const ( ) var ( - CallRegisters = []cpu.Register{RDI, RSI, RDX, RCX, R8, R9} + CallRegisters = SyscallRegisters GeneralRegisters = []cpu.Register{RBX, RBP, R12, R13, R14, R15} SyscallRegisters = []cpu.Register{RAX, RDI, RSI, RDX, R10, R8, R9} ReturnValueRegisters = []cpu.Register{RAX, RCX, R11} diff --git a/src/build/asm/Instructions.go b/src/build/asm/Instructions.go index 4856d1e..4126bcf 100644 --- a/src/build/asm/Instructions.go +++ b/src/build/asm/Instructions.go @@ -66,6 +66,10 @@ func (a *Assembler) Jump(name string) { // Return returns back to the caller. func (a *Assembler) Return() { + if len(a.Instructions) > 0 && a.Instructions[len(a.Instructions)-1].Mnemonic == RETURN { + return + } + a.Instructions = append(a.Instructions, Instruction{Mnemonic: RETURN}) }