Implemented return values

This commit is contained in:
Eduard Urbach 2024-06-29 21:06:59 +02:00
parent 3e47a12ecd
commit 83640ba590
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
7 changed files with 113 additions and 61 deletions

View File

@ -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)
}

View File

@ -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

View File

@ -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
}

View File

@ -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])
}

37
src/build/SaveRegister.go Normal file
View File

@ -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
}

View File

@ -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}

View File

@ -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})
}