diff --git a/src/arm/Registers.go b/src/arm/Registers.go index 3cbf0fe..6945575 100644 --- a/src/arm/Registers.go +++ b/src/arm/Registers.go @@ -45,4 +45,4 @@ var CPU = cpu.CPU{ Call: []cpu.Register{X0, X1, X2, X3, X4, X5, X6}, Syscall: []cpu.Register{X8, X0, X1, X2, X3, X4, X5}, ExternCall: []cpu.Register{X0, X1, X2, X3, X4, X5, X6, X7}, -} +} \ No newline at end of file diff --git a/src/asm/compilerX86.go b/src/asm/compilerX86.go index aee625e..1eb7a10 100644 --- a/src/asm/compilerX86.go +++ b/src/asm/compilerX86.go @@ -41,8 +41,12 @@ func (c *compilerX86) Compile(instr Instruction) { binary.LittleEndian.PutUint32(c.code[end-4:end], uint32(offset)) }) case *CallExtern: + c.code = x86.MoveRegisterRegister(c.code, x86.R5, x86.SP) + c.code = x86.AndRegisterNumber(c.code, x86.SP, -16) + c.code = x86.SubRegisterNumber(c.code, x86.SP, 32) c.code = x86.CallAt(c.code, 0) end := len(c.code) + c.code = x86.MoveRegisterRegister(c.code, x86.SP, x86.R5) c.Defer(func() { index := c.libraries.Index(instr.Library, instr.Function) diff --git a/src/core/Compile.go b/src/core/Compile.go index 1910faf..2ca543a 100644 --- a/src/core/Compile.go +++ b/src/core/Compile.go @@ -1,13 +1,8 @@ package core import ( - "slices" - - "git.urbach.dev/cli/q/src/asm" - "git.urbach.dev/cli/q/src/cpu" "git.urbach.dev/cli/q/src/ssa" "git.urbach.dev/cli/q/src/types" - "git.urbach.dev/cli/q/src/x86" ) // Compile turns a function into machine code. @@ -64,84 +59,5 @@ func (f *Function) Compile() { return } - f.Assembler.Append(&asm.Label{Name: f.UniqueName}) - - if !f.IsLeaf() && f.UniqueName != "core.init" { - f.Assembler.Append(&asm.FunctionStart{}) - } - - for instr := range f.Values { - switch instr := instr.(type) { - case *ssa.Call: - arg := instr.Arguments[0].(*ssa.Function) - fn := f.All.Functions[arg.UniqueName] - - if fn.IsExtern() { - f.mv(instr.Arguments[1:], f.CPU.ExternCall) - f.Assembler.Append(&asm.MoveRegisterRegister{Destination: x86.R5, Source: x86.SP}) - f.Assembler.Append(&asm.AndRegisterNumber{Destination: x86.SP, Source: x86.SP, Number: -16}) - f.Assembler.Append(&asm.SubRegisterNumber{Destination: x86.SP, Source: x86.SP, Number: 32}) - f.Assembler.Append(&asm.CallExtern{Library: fn.Package, Function: fn.Name}) - f.Assembler.Append(&asm.MoveRegisterRegister{Destination: x86.SP, Source: x86.R5}) - } else { - f.mv(instr.Arguments[1:], f.CPU.Call) - f.Assembler.Append(&asm.Call{Label: fn.UniqueName}) - } - - case *ssa.Syscall: - f.mv(instr.Arguments, f.CPU.Syscall) - f.Assembler.Append(&asm.Syscall{}) - - case *ssa.Return: - f.Assembler.Append(&asm.Return{}) - } - } - - if !f.IsLeaf() && f.UniqueName != "core.init" { - f.Assembler.Append(&asm.FunctionEnd{}) - } - - if f.UniqueName != "core.exit" { - f.Assembler.Append(&asm.Return{}) - } -} - -func (f *Function) mv(args []ssa.Value, registers []cpu.Register) { - extra := 0 - - for _, arg := range args { - switch arg.(type) { - case *ssa.Bytes: - extra++ - } - } - - for i, arg := range slices.Backward(args) { - switch arg := arg.(type) { - case *ssa.Int: - f.Assembler.Append(&asm.MoveRegisterNumber{ - Destination: registers[i+extra], - Number: arg.Int, - }) - case *ssa.Parameter: - f.Assembler.Append(&asm.MoveRegisterRegister{ - Destination: registers[i+extra], - Source: f.CPU.Call[arg.Index], - }) - case *ssa.Bytes: - f.Assembler.SetData("data0", arg.Bytes) - - f.Assembler.Append(&asm.MoveRegisterNumber{ - Destination: registers[i+extra], - Number: len(arg.Bytes), - }) - - extra-- - - f.Assembler.Append(&asm.MoveRegisterLabel{ - Destination: registers[i+extra], - Label: "data0", - }) - } - } + f.ssaToAsm() } \ No newline at end of file diff --git a/src/core/CreateLabel.go b/src/core/CreateLabel.go new file mode 100644 index 0000000..5bbc48f --- /dev/null +++ b/src/core/CreateLabel.go @@ -0,0 +1,20 @@ +package core + +import ( + "strconv" + "strings" + + "git.urbach.dev/cli/q/src/asm" +) + +// CreateLabel creates a label that is tied to this function by using a suffix. +func (f *Function) CreateLabel(prefix string, count counter) *asm.Label { + tmp := strings.Builder{} + tmp.WriteString(prefix) + tmp.WriteString(" ") + tmp.WriteString(strconv.FormatUint(uint64(count), 10)) + tmp.WriteString(" [") + tmp.WriteString(f.UniqueName) + tmp.WriteString("]") + return &asm.Label{Name: tmp.String()} +} \ No newline at end of file diff --git a/src/core/Function.go b/src/core/Function.go index 4c25ff5..5125f76 100644 --- a/src/core/Function.go +++ b/src/core/Function.go @@ -29,6 +29,7 @@ type Function struct { CPU *cpu.CPU Type *types.Function Err error + count count } // NewFunction creates a new function. diff --git a/src/core/count.go b/src/core/count.go new file mode 100644 index 0000000..98e9005 --- /dev/null +++ b/src/core/count.go @@ -0,0 +1,8 @@ +package core + +type counter = uint8 + +// count stores how often a certain statement appeared so we can generate a unique label from it. +type count struct { + data counter +} \ No newline at end of file diff --git a/src/core/ssaToAsm.go b/src/core/ssaToAsm.go new file mode 100644 index 0000000..6a55f86 --- /dev/null +++ b/src/core/ssaToAsm.go @@ -0,0 +1,46 @@ +package core + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/ssa" +) + +// ssaToAsm converts the SSA IR to assembler instructions. +func (f *Function) ssaToAsm() { + f.Assembler.Append(&asm.Label{Name: f.UniqueName}) + + if !f.IsLeaf() && f.UniqueName != "core.init" { + f.Assembler.Append(&asm.FunctionStart{}) + } + + for instr := range f.Values { + switch instr := instr.(type) { + case *ssa.Call: + arg := instr.Arguments[0].(*ssa.Function) + fn := f.All.Functions[arg.UniqueName] + + if fn.IsExtern() { + f.ssaValuesToRegisters(instr.Arguments[1:], f.CPU.ExternCall) + f.Assembler.Append(&asm.CallExtern{Library: fn.Package, Function: fn.Name}) + } else { + f.ssaValuesToRegisters(instr.Arguments[1:], f.CPU.Call) + f.Assembler.Append(&asm.Call{Label: fn.UniqueName}) + } + + case *ssa.Syscall: + f.ssaValuesToRegisters(instr.Arguments, f.CPU.Syscall) + f.Assembler.Append(&asm.Syscall{}) + + case *ssa.Return: + f.Assembler.Append(&asm.Return{}) + } + } + + if !f.IsLeaf() && f.UniqueName != "core.init" { + f.Assembler.Append(&asm.FunctionEnd{}) + } + + if f.UniqueName != "core.exit" { + f.Assembler.Append(&asm.Return{}) + } +} \ No newline at end of file diff --git a/src/core/ssaValuesToRegisters.go b/src/core/ssaValuesToRegisters.go new file mode 100644 index 0000000..2932070 --- /dev/null +++ b/src/core/ssaValuesToRegisters.go @@ -0,0 +1,52 @@ +package core + +import ( + "slices" + + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/ssa" +) + +// ssaValuesToRegisters generates assembler instructions to move the SSA values to the given registers. +func (f *Function) ssaValuesToRegisters(args []ssa.Value, registers []cpu.Register) { + extra := 0 + + for _, arg := range args { + switch arg.(type) { + case *ssa.Bytes: + extra++ + } + } + + for i, arg := range slices.Backward(args) { + switch arg := arg.(type) { + case *ssa.Int: + f.Assembler.Append(&asm.MoveRegisterNumber{ + Destination: registers[i+extra], + Number: arg.Int, + }) + case *ssa.Parameter: + f.Assembler.Append(&asm.MoveRegisterRegister{ + Destination: registers[i+extra], + Source: f.CPU.Call[arg.Index], + }) + case *ssa.Bytes: + f.count.data++ + label := f.CreateLabel("data", f.count.data) + f.Assembler.SetData(label.Name, arg.Bytes) + + f.Assembler.Append(&asm.MoveRegisterNumber{ + Destination: registers[i+extra], + Number: len(arg.Bytes), + }) + + extra-- + + f.Assembler.Append(&asm.MoveRegisterLabel{ + Destination: registers[i+extra], + Label: label.Name, + }) + } + } +} \ No newline at end of file