diff --git a/src/build/arch/x64/Pop.go b/src/build/arch/x64/Pop.go new file mode 100644 index 0000000..746f3a0 --- /dev/null +++ b/src/build/arch/x64/Pop.go @@ -0,0 +1,16 @@ +package x64 + +import "git.akyoto.dev/cli/q/src/build/cpu" + +// PopReg pops a value from the stack and saves it into the register. +func PopReg(code []byte, register cpu.Register) []byte { + if register >= 8 { + code = append(code, REX(0, 0, 0, 1)) + register -= 8 + } + + return append( + code, + 0x58+byte(register), + ) +} diff --git a/src/build/arch/x64/Pop_test.go b/src/build/arch/x64/Pop_test.go new file mode 100644 index 0000000..64acf13 --- /dev/null +++ b/src/build/arch/x64/Pop_test.go @@ -0,0 +1,39 @@ +package x64_test + +import ( + "testing" + + "git.akyoto.dev/cli/q/src/build/arch/x64" + "git.akyoto.dev/cli/q/src/build/cpu" + "git.akyoto.dev/go/assert" +) + +func TestPopRegister(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Code []byte + }{ + {x64.RAX, []byte{0x58}}, + {x64.RCX, []byte{0x59}}, + {x64.RDX, []byte{0x5A}}, + {x64.RBX, []byte{0x5B}}, + {x64.RSP, []byte{0x5C}}, + {x64.RBP, []byte{0x5D}}, + {x64.RSI, []byte{0x5E}}, + {x64.RDI, []byte{0x5F}}, + {x64.R8, []byte{0x41, 0x58}}, + {x64.R9, []byte{0x41, 0x59}}, + {x64.R10, []byte{0x41, 0x5A}}, + {x64.R11, []byte{0x41, 0x5B}}, + {x64.R12, []byte{0x41, 0x5C}}, + {x64.R13, []byte{0x41, 0x5D}}, + {x64.R14, []byte{0x41, 0x5E}}, + {x64.R15, []byte{0x41, 0x5F}}, + } + + for _, pattern := range usagePatterns { + t.Logf("pop %s", pattern.Register) + code := x64.PopReg(nil, pattern.Register) + assert.DeepEqual(t, code, pattern.Code) + } +} diff --git a/src/build/arch/x64/Push.go b/src/build/arch/x64/Push.go new file mode 100644 index 0000000..48bed9d --- /dev/null +++ b/src/build/arch/x64/Push.go @@ -0,0 +1,16 @@ +package x64 + +import "git.akyoto.dev/cli/q/src/build/cpu" + +// PushReg pushes the value inside the register onto the stack. +func PushReg(code []byte, register cpu.Register) []byte { + if register >= 8 { + code = append(code, REX(0, 0, 0, 1)) + register -= 8 + } + + return append( + code, + 0x50+byte(register), + ) +} diff --git a/src/build/arch/x64/Push_test.go b/src/build/arch/x64/Push_test.go new file mode 100644 index 0000000..22994bb --- /dev/null +++ b/src/build/arch/x64/Push_test.go @@ -0,0 +1,39 @@ +package x64_test + +import ( + "testing" + + "git.akyoto.dev/cli/q/src/build/arch/x64" + "git.akyoto.dev/cli/q/src/build/cpu" + "git.akyoto.dev/go/assert" +) + +func TestPushRegister(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Code []byte + }{ + {x64.RAX, []byte{0x50}}, + {x64.RCX, []byte{0x51}}, + {x64.RDX, []byte{0x52}}, + {x64.RBX, []byte{0x53}}, + {x64.RSP, []byte{0x54}}, + {x64.RBP, []byte{0x55}}, + {x64.RSI, []byte{0x56}}, + {x64.RDI, []byte{0x57}}, + {x64.R8, []byte{0x41, 0x50}}, + {x64.R9, []byte{0x41, 0x51}}, + {x64.R10, []byte{0x41, 0x52}}, + {x64.R11, []byte{0x41, 0x53}}, + {x64.R12, []byte{0x41, 0x54}}, + {x64.R13, []byte{0x41, 0x55}}, + {x64.R14, []byte{0x41, 0x56}}, + {x64.R15, []byte{0x41, 0x57}}, + } + + for _, pattern := range usagePatterns { + t.Logf("push %s", pattern.Register) + code := x64.PushReg(nil, pattern.Register) + assert.DeepEqual(t, code, pattern.Code) + } +} diff --git a/src/build/asm/Assembler.go b/src/build/asm/Assembler.go index fb93ed7..81d5299 100644 --- a/src/build/asm/Assembler.go +++ b/src/build/asm/Assembler.go @@ -48,11 +48,23 @@ func (a *Assembler) Finalize() ([]byte, []byte) { case DIV: switch operands := x.Data.(type) { case *RegisterNumber: - code = x64.MoveRegReg64(code, x64.RAX, operands.Register) - code = x64.MoveRegNum32(code, operands.Register, uint32(operands.Number)) - code = x64.ExtendRAXToRDX(code) - code = x64.DivReg(code, operands.Register) - code = x64.MoveRegReg64(code, operands.Register, x64.RAX) + if operands.Register == x64.RAX { + code = x64.PushReg(code, x64.RCX) + code = x64.MoveRegNum32(code, x64.RCX, uint32(operands.Number)) + code = x64.ExtendRAXToRDX(code) + code = x64.DivReg(code, x64.RCX) + code = x64.PopReg(code, x64.RCX) + } else { + code = x64.PushReg(code, x64.RAX) + code = x64.PushReg(code, x64.RDX) + code = x64.MoveRegReg64(code, x64.RAX, operands.Register) + code = x64.MoveRegNum32(code, operands.Register, uint32(operands.Number)) + code = x64.ExtendRAXToRDX(code) + code = x64.DivReg(code, operands.Register) + code = x64.MoveRegReg64(code, operands.Register, x64.RAX) + code = x64.PopReg(code, x64.RDX) + code = x64.PopReg(code, x64.RAX) + } } case CALL: diff --git a/src/build/asm/RegisterNumber.go b/src/build/asm/RegisterNumber.go index 5040b53..7e4ba23 100644 --- a/src/build/asm/RegisterNumber.go +++ b/src/build/asm/RegisterNumber.go @@ -14,5 +14,5 @@ type RegisterNumber struct { // String returns a human readable version. func (data *RegisterNumber) String() string { - return fmt.Sprintf("%s, %x", data.Register, data.Number) + return fmt.Sprintf("%s, %Xₕ", data.Register, data.Number) }