diff --git a/src/arm/Registers.go b/src/arm/Registers.go index ddee671..dedf85f 100644 --- a/src/arm/Registers.go +++ b/src/arm/Registers.go @@ -43,21 +43,27 @@ const ( var ( LinuxCPU = cpu.CPU{ - General: []cpu.Register{X9, X10, X11, X12, X13, X14, X15, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28}, + General: []cpu.Register{ + X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, // Clobbered + X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, // Preserved + }, Call: cpu.ABI{ - In: []cpu.Register{X0, X1, X2, X3, X4, X5, X6, X7}, - Out: []cpu.Register{X0, X1}, - Volatile: []cpu.Register{X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17}, + In: []cpu.Register{X0, X1, X2, X3, X4, X5, X6, X7}, + Out: []cpu.Register{X0, X1}, + Clobbered: []cpu.Register{X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18}, + Preserved: []cpu.Register{X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, FP, LR, SP}, }, ExternCall: cpu.ABI{ - In: []cpu.Register{X0, X1, X2, X3, X4, X5, X6, X7}, - Out: []cpu.Register{X0, X1}, - Volatile: []cpu.Register{X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17}, + In: []cpu.Register{X0, X1, X2, X3, X4, X5, X6, X7}, + Out: []cpu.Register{X0, X1}, + Clobbered: []cpu.Register{X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18}, + Preserved: []cpu.Register{X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, FP, LR, SP}, }, Syscall: cpu.ABI{ - In: []cpu.Register{X8, X0, X1, X2, X3, X4, X5}, - Out: []cpu.Register{X0}, - Volatile: []cpu.Register{X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17}, + In: []cpu.Register{X8, X0, X1, X2, X3, X4, X5}, + Out: []cpu.Register{X0}, + Clobbered: []cpu.Register{X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18}, + Preserved: []cpu.Register{X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, FP, LR, SP}, }, } @@ -66,9 +72,10 @@ var ( Call: LinuxCPU.Call, ExternCall: LinuxCPU.ExternCall, Syscall: cpu.ABI{ - In: []cpu.Register{X16, X0, X1, X2, X3, X4, X5, X6, X7}, - Out: LinuxCPU.Syscall.Out, - Volatile: LinuxCPU.Syscall.Volatile, + In: []cpu.Register{X16, X0, X1, X2, X3, X4, X5, X6, X7}, + Out: LinuxCPU.Syscall.Out, + Clobbered: LinuxCPU.Syscall.Clobbered, + Preserved: LinuxCPU.Syscall.Preserved, }, } diff --git a/src/asm/Instruction.go b/src/asm/Instruction.go index dbbf0ac..271bb67 100644 --- a/src/asm/Instruction.go +++ b/src/asm/Instruction.go @@ -51,8 +51,12 @@ type MoveRegisterRegister struct { Source cpu.Register } -type PushRegister struct { - Register cpu.Register +type PopRegisters struct { + Registers []cpu.Register +} + +type PushRegisters struct { + Registers []cpu.Register } type Return struct{} diff --git a/src/asm/compilerARM.go b/src/asm/compilerARM.go index 0b853ae..ae4def9 100644 --- a/src/asm/compilerARM.go +++ b/src/asm/compilerARM.go @@ -95,8 +95,28 @@ func (c *compilerARM) Compile(instr Instruction) { c.code = arm.MoveRegisterNumber(c.code, instr.Destination, instr.Number) case *MoveRegisterRegister: c.append(arm.MoveRegisterRegister(instr.Destination, instr.Source)) - case *PushRegister: - panic("not implemented") + case *PopRegisters: + registers := instr.Registers + count := len(registers) + + if count&1 != 0 { + count-- + c.append(arm.LoadPair(registers[count], registers[count], arm.SP, 16)) + } + + for i := count - 2; i >= 0; i -= 2 { + c.append(arm.LoadPair(registers[i], registers[i+1], arm.SP, 16)) + } + case *PushRegisters: + registers := instr.Registers + + for i := 0; i < len(registers); i += 2 { + if i+1 < len(registers) { + c.append(arm.StorePair(registers[i], registers[i+1], arm.SP, -16)) + } else { + c.append(arm.StorePair(registers[i], registers[i], arm.SP, -16)) + } + } case *Return: c.append(arm.Return()) case *StackFrameStart: diff --git a/src/asm/compilerX86.go b/src/asm/compilerX86.go index d6dd469..d9bb2b7 100644 --- a/src/asm/compilerX86.go +++ b/src/asm/compilerX86.go @@ -3,6 +3,7 @@ package asm import ( "encoding/binary" "fmt" + "slices" "git.urbach.dev/cli/q/src/sizeof" "git.urbach.dev/cli/q/src/x86" @@ -145,8 +146,14 @@ func (c *compilerX86) Compile(instr Instruction) { c.code = x86.MoveRegisterNumber(c.code, instr.Destination, instr.Number) case *MoveRegisterRegister: c.code = x86.MoveRegisterRegister(c.code, instr.Destination, instr.Source) - case *PushRegister: - c.code = x86.PushRegister(c.code, instr.Register) + case *PopRegisters: + for _, register := range slices.Backward(instr.Registers) { + c.code = x86.PopRegister(c.code, register) + } + case *PushRegisters: + for _, register := range instr.Registers { + c.code = x86.PushRegister(c.code, register) + } case *Return: c.code = x86.Return(c.code) case *SubRegisterNumber: diff --git a/src/codegen/Function.go b/src/codegen/Function.go index a3a9e3c..62e1188 100644 --- a/src/codegen/Function.go +++ b/src/codegen/Function.go @@ -3,6 +3,7 @@ package codegen import ( "git.urbach.dev/cli/q/src/asm" "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/set" "git.urbach.dev/cli/q/src/ssa" ) @@ -13,4 +14,5 @@ type Function struct { ValueToStep map[ssa.Value]*step CPU *cpu.CPU Count count + Preserved set.Ordered[cpu.Register] } \ No newline at end of file diff --git a/src/codegen/GenerateAssembly.go b/src/codegen/GenerateAssembly.go index 7506488..dc7205d 100644 --- a/src/codegen/GenerateAssembly.go +++ b/src/codegen/GenerateAssembly.go @@ -15,15 +15,25 @@ func (f *Function) GenerateAssembly(ir ssa.IR, stackFrame bool) { f.Steps = f.createSteps(ir) + if f.Preserved.Count() > 0 { + f.Assembler.Append(&asm.PushRegisters{Registers: f.Preserved.Slice()}) + } + for _, step := range f.Steps { f.exec(step) } + if f.FullName == "os.exit" { + return + } + + if f.Preserved.Count() > 0 { + f.Assembler.Append(&asm.PopRegisters{Registers: f.Preserved.Slice()}) + } + if stackFrame { f.Assembler.Append(&asm.StackFrameEnd{}) } - if f.FullName != "os.exit" { - f.Assembler.Append(&asm.Return{}) - } + f.Assembler.Append(&asm.Return{}) } \ No newline at end of file diff --git a/src/codegen/createSteps.go b/src/codegen/createSteps.go index e587b29..e5957f9 100644 --- a/src/codegen/createSteps.go +++ b/src/codegen/createSteps.go @@ -34,7 +34,7 @@ func (f *Function) createSteps(ir ssa.IR) []*step { for r, param := range instr.Arguments { if r >= len(f.CPU.ExternCall.In) { // Temporary hack to allow arguments 5 and 6 to be hinted as r10 and r11, then pushed later. - f.ValueToStep[param].Hint(f.CPU.ExternCall.Volatile[1+r]) + f.ValueToStep[param].Hint(f.CPU.ExternCall.Clobbered[1+r]) continue } @@ -112,18 +112,18 @@ func (f *Function) createSteps(ir ssa.IR) []*step { continue } - var volatileRegisters []cpu.Register + var clobbered []cpu.Register switch step.Value.(type) { case *ssa.Call: - volatileRegisters = f.CPU.Call.Volatile + clobbered = f.CPU.Call.Clobbered case *ssa.CallExtern: - volatileRegisters = f.CPU.ExternCall.Volatile + clobbered = f.CPU.ExternCall.Clobbered case *ssa.Syscall: - volatileRegisters = f.CPU.Syscall.Volatile + clobbered = f.CPU.Syscall.Clobbered } - if live.Value != step.Value && slices.Contains(volatileRegisters, live.Register) { + if live.Value != step.Value && slices.Contains(clobbered, live.Register) { live.Register = f.findFreeRegister(steps[live.Index : stepIndex+1]) goto next } @@ -148,5 +148,13 @@ func (f *Function) createSteps(ir ssa.IR) []*step { } } + mustPreserve := f.CPU.Call.Preserved + + for _, step := range f.Steps { + if slices.Contains(mustPreserve, step.Register) { + f.Preserved.Add(step.Register) + } + } + return steps } \ No newline at end of file diff --git a/src/codegen/exec.go b/src/codegen/exec.go index ce822b2..59d9b0e 100644 --- a/src/codegen/exec.go +++ b/src/codegen/exec.go @@ -4,6 +4,7 @@ import ( "strings" "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/cpu" "git.urbach.dev/cli/q/src/ssa" ) @@ -55,13 +56,11 @@ func (f *Function) exec(step *step) { case *ssa.CallExtern: f.Assembler.Append(&asm.CallExternStart{}) args := instr.Arguments + var pushed []cpu.Register for i, arg := range args { if i >= len(f.CPU.ExternCall.In) { - f.Assembler.Append(&asm.PushRegister{ - Register: f.ValueToStep[arg].Register, - }) - + pushed = append(pushed, f.ValueToStep[arg].Register) continue } @@ -75,6 +74,10 @@ func (f *Function) exec(step *step) { }) } + if len(pushed) > 0 { + f.Assembler.Append(&asm.PushRegisters{Registers: pushed}) + } + dot := strings.IndexByte(instr.Func.UniqueName, '.') library := instr.Func.UniqueName[:dot] function := instr.Func.UniqueName[dot+1:] diff --git a/src/codegen/findFreeRegister.go b/src/codegen/findFreeRegister.go index a2c71f1..8ec50ae 100644 --- a/src/codegen/findFreeRegister.go +++ b/src/codegen/findFreeRegister.go @@ -22,13 +22,13 @@ func (f *Function) findFreeRegister(steps []*step) cpu.Register { switch instr := step.Value.(type) { case *ssa.Call: - volatileRegisters = f.CPU.Call.Volatile + volatileRegisters = f.CPU.Call.Clobbered case *ssa.CallExtern: - volatileRegisters = f.CPU.ExternCall.Volatile + volatileRegisters = f.CPU.ExternCall.Clobbered case *ssa.Parameter: usedRegisters |= (1 << f.CPU.Call.In[instr.Index]) case *ssa.Syscall: - volatileRegisters = f.CPU.Syscall.Volatile + volatileRegisters = f.CPU.Syscall.Clobbered } for _, volatile := range volatileRegisters { diff --git a/src/cpu/ABI.go b/src/cpu/ABI.go index 0d43568..b44ca38 100644 --- a/src/cpu/ABI.go +++ b/src/cpu/ABI.go @@ -2,7 +2,8 @@ package cpu // ABI is the Application Binary Interface which defines the registers used in function calls. type ABI struct { - In []Register - Out []Register - Volatile []Register + In []Register + Out []Register + Clobbered []Register + Preserved []Register } \ No newline at end of file diff --git a/src/set/Ordered.go b/src/set/Ordered.go index 674c6ff..ac485dc 100644 --- a/src/set/Ordered.go +++ b/src/set/Ordered.go @@ -35,4 +35,9 @@ func (set *Ordered[T]) All() iter.Seq[T] { // Count returns the number of elements in the set. func (set *Ordered[T]) Count() int { return len(set.values) +} + +// Slice returns the set of elements as a slice. +func (set *Ordered[T]) Slice() []T { + return set.values } \ No newline at end of file diff --git a/src/x86/Registers.go b/src/x86/Registers.go index 37ce04d..62a4ec9 100644 --- a/src/x86/Registers.go +++ b/src/x86/Registers.go @@ -24,41 +24,52 @@ const ( var ( LinuxCPU = cpu.CPU{ General: []cpu.Register{ - // 1 byte encoding - R2, R3, R6, R7, - - // 2 bytes encoding with low chance of collision - R12, R13, R14, R15, - - // 2 bytes encoding with high chance of collision - R8, R9, R10, R11, + R1, R2, R6, R7, R8, R9, R10, R11, // Clobbered + R3, R12, R13, R14, R15, // Preserved }, Call: cpu.ABI{ - In: []cpu.Register{R0, R7, R6, R2, R10, R8, R9}, - Out: []cpu.Register{R0, R7, R6}, - Volatile: []cpu.Register{R0, R1, R2, R6, R7, R8, R9, R10, R11}, + In: []cpu.Register{R0, R7, R6, R2, R10, R8, R9}, + Out: []cpu.Register{R0, R2}, + Clobbered: []cpu.Register{R0, R1, R2, R6, R7, R8, R9, R10, R11}, + Preserved: []cpu.Register{R3, SP, R5, R12, R13, R14, R15}, }, ExternCall: cpu.ABI{ - In: []cpu.Register{R7, R6, R2, R1, R8, R9}, - Out: []cpu.Register{R0, R2}, - Volatile: []cpu.Register{R0, R1, R2, R6, R7, R8, R9, R10, R11}, + In: []cpu.Register{R7, R6, R2, R1, R8, R9}, + Out: []cpu.Register{R0, R2}, + Clobbered: []cpu.Register{R0, R1, R2, R6, R7, R8, R9, R10, R11}, + Preserved: []cpu.Register{R3, SP, R5, R12, R13, R14, R15}, }, Syscall: cpu.ABI{ - In: []cpu.Register{R0, R7, R6, R2, R10, R8, R9}, - Out: []cpu.Register{R0}, - Volatile: []cpu.Register{R0, R1, R11}, + In: []cpu.Register{R0, R7, R6, R2, R10, R8, R9}, + Out: []cpu.Register{R0}, + Clobbered: []cpu.Register{R0, R1, R11}, + Preserved: []cpu.Register{R2, R3, SP, R5, R6, R7, R8, R9, R10, R12, R13, R14, R15}, }, } - MacCPU = LinuxCPU + MacCPU = cpu.CPU{ + General: LinuxCPU.General, + Call: LinuxCPU.Call, + ExternCall: LinuxCPU.ExternCall, + Syscall: cpu.ABI{ + In: LinuxCPU.Syscall.In, + Out: LinuxCPU.Syscall.Out, + Clobbered: []cpu.Register{R0, R1, R2, R11}, + Preserved: LinuxCPU.Syscall.Preserved, + }, + } WindowsCPU = cpu.CPU{ - General: LinuxCPU.General, - Call: LinuxCPU.Call, + General: []cpu.Register{ + R1, R2, R8, R9, R10, R11, // Clobbered + R3, R6, R7, R12, R13, R14, R15, // Preserved + }, + Call: LinuxCPU.Call, ExternCall: cpu.ABI{ - In: []cpu.Register{R1, R2, R8, R9}, - Out: []cpu.Register{R0}, - Volatile: []cpu.Register{R0, R1, R2, R8, R9, R10, R11}, + In: []cpu.Register{R1, R2, R8, R9}, + Out: []cpu.Register{R0}, + Clobbered: []cpu.Register{R0, R1, R2, R8, R9, R10, R11}, + Preserved: []cpu.Register{R3, SP, R5, R6, R7, R12, R13, R14, R15}, }, } ) \ No newline at end of file diff --git a/tests/value-reuse.q b/tests/hello-3.q similarity index 100% rename from tests/value-reuse.q rename to tests/hello-3.q diff --git a/tests/swap.q b/tests/param-swap.q similarity index 63% rename from tests/swap.q rename to tests/param-swap.q index 7ba07be..219fd6c 100644 --- a/tests/swap.q +++ b/tests/param-swap.q @@ -1,10 +1,10 @@ import os main() { - os.exit(f(1, 2)) + os.exit(swap(1, 2)) } -f(a int, b int) -> int { +swap(a int, b int) -> int { return sum(b, a) } diff --git a/tests/tests_test.go b/tests/tests_test.go index f37c2f7..51a3d69 100644 --- a/tests/tests_test.go +++ b/tests/tests_test.go @@ -5,11 +5,11 @@ import ( ) var tests = []testRun{ - {"script", "", "Hello\n", 0}, {"sum-10", "", "", 10}, {"sum-36", "", "", 36}, - {"swap", "", "", 3}, - {"value-reuse", "", "Hello\nHello\nHello\n", 0}, + {"param-swap", "", "", 3}, + {"script", "", "Hello\n", 0}, + {"hello-3", "", "Hello\nHello\nHello\n", 0}, } func TestTests(t *testing.T) {