From 6b5dd4c687b1dfff09b450bcd66af8687629e4df Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Sat, 20 Jul 2024 00:58:39 +0200 Subject: [PATCH] Improved x64 encoder --- src/build/arch/x64/Add.go | 4 +- src/build/arch/x64/Compare.go | 4 +- src/build/arch/x64/ModRM.go | 7 ++ src/build/arch/x64/Move.go | 2 +- src/build/arch/x64/Mul.go | 4 +- src/build/arch/x64/REX.go | 2 +- src/build/arch/x64/SIB.go | 16 ++++ src/build/arch/x64/SIB_test.go | 34 +++++++ src/build/arch/x64/Store.go | 52 ++++++++++ src/build/arch/x64/Store_test.go | 157 +++++++++++++++++++++++++++++++ src/build/arch/x64/Sub.go | 4 +- src/build/arch/x64/encode.go | 27 ++++++ src/build/arch/x64/encodeNum.go | 14 +++ src/build/arch/x64/regReg.go | 29 ------ src/build/arch/x64/regRegNum.go | 14 --- 15 files changed, 317 insertions(+), 53 deletions(-) create mode 100644 src/build/arch/x64/SIB.go create mode 100644 src/build/arch/x64/SIB_test.go create mode 100644 src/build/arch/x64/Store.go create mode 100644 src/build/arch/x64/Store_test.go create mode 100644 src/build/arch/x64/encode.go create mode 100644 src/build/arch/x64/encodeNum.go delete mode 100644 src/build/arch/x64/regReg.go delete mode 100644 src/build/arch/x64/regRegNum.go diff --git a/src/build/arch/x64/Add.go b/src/build/arch/x64/Add.go index b92cc60..b60a19b 100644 --- a/src/build/arch/x64/Add.go +++ b/src/build/arch/x64/Add.go @@ -6,10 +6,10 @@ import ( // AddRegisterNumber adds a number to the given register. func AddRegisterNumber(code []byte, destination cpu.Register, number int) []byte { - return regRegNum(code, 0, byte(destination), number, 0x83, 0x81) + return encodeNum(code, 1, AddressDirect, 0, byte(destination), number, 0x83, 0x81) } // AddRegisterRegister adds a register value into another register. func AddRegisterRegister(code []byte, destination cpu.Register, operand cpu.Register) []byte { - return regReg(code, byte(operand), byte(destination), 0x01) + return encode(code, 1, AddressDirect, byte(operand), byte(destination), 0x01) } diff --git a/src/build/arch/x64/Compare.go b/src/build/arch/x64/Compare.go index 5e49f7b..406d016 100644 --- a/src/build/arch/x64/Compare.go +++ b/src/build/arch/x64/Compare.go @@ -4,10 +4,10 @@ import "git.akyoto.dev/cli/q/src/build/cpu" // Compares the register with the number and sets the status flags in the EFLAGS register. func CompareRegisterNumber(code []byte, register cpu.Register, number int) []byte { - return regRegNum(code, 0b111, byte(register), number, 0x83, 0x81) + return encodeNum(code, 1, AddressDirect, 0b111, byte(register), number, 0x83, 0x81) } // CompareRegisterRegister compares a register with a register and sets the status flags in the EFLAGS register. func CompareRegisterRegister(code []byte, registerA cpu.Register, registerB cpu.Register) []byte { - return regReg(code, byte(registerB), byte(registerA), 0x39) + return encode(code, 1, AddressDirect, byte(registerB), byte(registerA), 0x39) } diff --git a/src/build/arch/x64/ModRM.go b/src/build/arch/x64/ModRM.go index b6ab57f..ca6a327 100644 --- a/src/build/arch/x64/ModRM.go +++ b/src/build/arch/x64/ModRM.go @@ -1,5 +1,12 @@ package x64 +const ( + AddressMemory = byte(0b00) + AddressMemoryOffset8 = byte(0b01) + AddressMemoryOffset32 = byte(0b10) + AddressDirect = byte(0b11) +) + // ModRM is used to generate a ModRM suffix. // - mod: 2 bits // - reg: 3 bits diff --git a/src/build/arch/x64/Move.go b/src/build/arch/x64/Move.go index e266d33..4245a4b 100644 --- a/src/build/arch/x64/Move.go +++ b/src/build/arch/x64/Move.go @@ -19,5 +19,5 @@ func MoveRegisterNumber32(code []byte, destination cpu.Register, number uint32) // MoveRegisterRegister64 moves a register value into another register. func MoveRegisterRegister64(code []byte, destination cpu.Register, source cpu.Register) []byte { - return regReg(code, byte(source), byte(destination), 0x89) + return encode(code, 1, AddressDirect, byte(source), byte(destination), 0x89) } diff --git a/src/build/arch/x64/Mul.go b/src/build/arch/x64/Mul.go index a041799..181b40c 100644 --- a/src/build/arch/x64/Mul.go +++ b/src/build/arch/x64/Mul.go @@ -4,10 +4,10 @@ import "git.akyoto.dev/cli/q/src/build/cpu" // MulRegisterNumber multiplies a register with a number. func MulRegisterNumber(code []byte, destination cpu.Register, number int) []byte { - return regRegNum(code, byte(destination), byte(destination), number, 0x6B, 0x69) + return encodeNum(code, 1, AddressDirect, byte(destination), byte(destination), number, 0x6B, 0x69) } // MulRegisterRegister multiplies a register with another register. func MulRegisterRegister(code []byte, destination cpu.Register, operand cpu.Register) []byte { - return regReg(code, byte(destination), byte(operand), 0x0F, 0xAF) + return encode(code, 1, AddressDirect, byte(destination), byte(operand), 0x0F, 0xAF) } diff --git a/src/build/arch/x64/REX.go b/src/build/arch/x64/REX.go index ba1fa1a..36d7e58 100644 --- a/src/build/arch/x64/REX.go +++ b/src/build/arch/x64/REX.go @@ -3,5 +3,5 @@ package x64 // REX is used to generate a REX prefix. // w, r, x and b can only be set to either 0 or 1. func REX(w, r, x, b byte) byte { - return 0b_0100_0000 | (w << 3) | (r << 2) | (x << 1) | b + return 0b0100_0000 | (w << 3) | (r << 2) | (x << 1) | b } diff --git a/src/build/arch/x64/SIB.go b/src/build/arch/x64/SIB.go new file mode 100644 index 0000000..030ffac --- /dev/null +++ b/src/build/arch/x64/SIB.go @@ -0,0 +1,16 @@ +package x64 + +const ( + Scale1 = byte(0b00) + Scale2 = byte(0b01) + Scale4 = byte(0b10) + Scale8 = byte(0b11) +) + +// SIB is used to generate an SIB byte. +// - scale: 2 bits +// - index: 3 bits +// - base: 3 bits +func SIB(scale byte, index byte, base byte) byte { + return (scale << 6) | (index << 3) | base +} diff --git a/src/build/arch/x64/SIB_test.go b/src/build/arch/x64/SIB_test.go new file mode 100644 index 0000000..690f04b --- /dev/null +++ b/src/build/arch/x64/SIB_test.go @@ -0,0 +1,34 @@ +package x64_test + +import ( + "testing" + + "git.akyoto.dev/cli/q/src/build/arch/x64" + "git.akyoto.dev/go/assert" +) + +func TestSIB(t *testing.T) { + testData := []struct{ scale, index, base, expected byte }{ + {0b_00, 0b_111, 0b_000, 0b_00_111_000}, + {0b_00, 0b_110, 0b_001, 0b_00_110_001}, + {0b_00, 0b_101, 0b_010, 0b_00_101_010}, + {0b_00, 0b_100, 0b_011, 0b_00_100_011}, + {0b_00, 0b_011, 0b_100, 0b_00_011_100}, + {0b_00, 0b_010, 0b_101, 0b_00_010_101}, + {0b_00, 0b_001, 0b_110, 0b_00_001_110}, + {0b_00, 0b_000, 0b_111, 0b_00_000_111}, + {0b_11, 0b_111, 0b_000, 0b_11_111_000}, + {0b_11, 0b_110, 0b_001, 0b_11_110_001}, + {0b_11, 0b_101, 0b_010, 0b_11_101_010}, + {0b_11, 0b_100, 0b_011, 0b_11_100_011}, + {0b_11, 0b_011, 0b_100, 0b_11_011_100}, + {0b_11, 0b_010, 0b_101, 0b_11_010_101}, + {0b_11, 0b_001, 0b_110, 0b_11_001_110}, + {0b_11, 0b_000, 0b_111, 0b_11_000_111}, + } + + for _, test := range testData { + sib := x64.SIB(test.scale, test.index, test.base) + assert.Equal(t, sib, test.expected) + } +} diff --git a/src/build/arch/x64/Store.go b/src/build/arch/x64/Store.go new file mode 100644 index 0000000..e2df6a5 --- /dev/null +++ b/src/build/arch/x64/Store.go @@ -0,0 +1,52 @@ +package x64 + +import ( + "encoding/binary" + + "git.akyoto.dev/cli/q/src/build/cpu" +) + +// StoreNumber stores a number into the memory address included in the given register. +func StoreNumber(code []byte, register cpu.Register, offset byte, byteCount byte, number int) []byte { + if byteCount == 2 { + code = append(code, 0x66) + } + + opCode := byte(0xC7) + + if byteCount == 1 { + opCode = 0xC6 + } + + mod := AddressMemory + + if offset != 0 || register == RBP || register == R13 { + mod = AddressMemoryOffset8 + } + + is64Bit := byte(0) + + if byteCount == 8 { + is64Bit = 1 + } + + code = encode(code, is64Bit, mod, 0b000, byte(register), opCode) + + if register == RSP || register == R12 { + code = append(code, SIB(0b00, 0b100, 0b100)) + } + + if mod == AddressMemoryOffset8 { + code = append(code, offset) + } + + switch byteCount { + case 8, 4: + return binary.LittleEndian.AppendUint32(code, uint32(number)) + + case 2: + return binary.LittleEndian.AppendUint16(code, uint16(number)) + } + + return append(code, byte(number)) +} diff --git a/src/build/arch/x64/Store_test.go b/src/build/arch/x64/Store_test.go new file mode 100644 index 0000000..6d278fb --- /dev/null +++ b/src/build/arch/x64/Store_test.go @@ -0,0 +1,157 @@ +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 TestStoreNumber(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Offset byte + ByteCount byte + Number int + Code []byte + }{ + // No offset + {x64.RAX, 0, 8, 0x7F, []byte{0x48, 0xC7, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RAX, 0, 4, 0x7F, []byte{0xC7, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RAX, 0, 2, 0x7F, []byte{0x66, 0xC7, 0x00, 0x7F, 0x00}}, + {x64.RAX, 0, 1, 0x7F, []byte{0xC6, 0x00, 0x7F}}, + {x64.RCX, 0, 8, 0x7F, []byte{0x48, 0xC7, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RCX, 0, 4, 0x7F, []byte{0xC7, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RCX, 0, 2, 0x7F, []byte{0x66, 0xC7, 0x01, 0x7F, 0x00}}, + {x64.RCX, 0, 1, 0x7F, []byte{0xC6, 0x01, 0x7F}}, + {x64.RDX, 0, 8, 0x7F, []byte{0x48, 0xC7, 0x02, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RDX, 0, 4, 0x7F, []byte{0xC7, 0x02, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RDX, 0, 2, 0x7F, []byte{0x66, 0xC7, 0x02, 0x7F, 0x00}}, + {x64.RDX, 0, 1, 0x7F, []byte{0xC6, 0x02, 0x7F}}, + {x64.RBX, 0, 8, 0x7F, []byte{0x48, 0xC7, 0x03, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RBX, 0, 4, 0x7F, []byte{0xC7, 0x03, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RBX, 0, 2, 0x7F, []byte{0x66, 0xC7, 0x03, 0x7F, 0x00}}, + {x64.RBX, 0, 1, 0x7F, []byte{0xC6, 0x03, 0x7F}}, + {x64.RSP, 0, 8, 0x7F, []byte{0x48, 0xC7, 0x04, 0x24, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RSP, 0, 4, 0x7F, []byte{0xC7, 0x04, 0x24, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RSP, 0, 2, 0x7F, []byte{0x66, 0xC7, 0x04, 0x24, 0x7F, 0x00}}, + {x64.RSP, 0, 1, 0x7F, []byte{0xC6, 0x04, 0x24, 0x7F}}, + {x64.RBP, 0, 8, 0x7F, []byte{0x48, 0xC7, 0x45, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RBP, 0, 4, 0x7F, []byte{0xC7, 0x45, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RBP, 0, 2, 0x7F, []byte{0x66, 0xC7, 0x45, 0x00, 0x7F, 0x00}}, + {x64.RBP, 0, 1, 0x7F, []byte{0xC6, 0x45, 0x00, 0x7F}}, + {x64.RSI, 0, 8, 0x7F, []byte{0x48, 0xC7, 0x06, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RSI, 0, 4, 0x7F, []byte{0xC7, 0x06, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RSI, 0, 2, 0x7F, []byte{0x66, 0xC7, 0x06, 0x7F, 0x00}}, + {x64.RSI, 0, 1, 0x7F, []byte{0xC6, 0x06, 0x7F}}, + {x64.RDI, 0, 8, 0x7F, []byte{0x48, 0xC7, 0x07, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RDI, 0, 4, 0x7F, []byte{0xC7, 0x07, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RDI, 0, 2, 0x7F, []byte{0x66, 0xC7, 0x07, 0x7F, 0x00}}, + {x64.RDI, 0, 1, 0x7F, []byte{0xC6, 0x07, 0x7F}}, + {x64.R8, 0, 8, 0x7F, []byte{0x49, 0xC7, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R8, 0, 4, 0x7F, []byte{0x41, 0xC7, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R8, 0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x00, 0x7F, 0x00}}, + {x64.R8, 0, 1, 0x7F, []byte{0x41, 0xC6, 0x00, 0x7F}}, + {x64.R9, 0, 8, 0x7F, []byte{0x49, 0xC7, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R9, 0, 4, 0x7F, []byte{0x41, 0xC7, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R9, 0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x01, 0x7F, 0x00}}, + {x64.R9, 0, 1, 0x7F, []byte{0x41, 0xC6, 0x01, 0x7F}}, + {x64.R10, 0, 8, 0x7F, []byte{0x49, 0xC7, 0x02, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R10, 0, 4, 0x7F, []byte{0x41, 0xC7, 0x02, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R10, 0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x02, 0x7F, 0x00}}, + {x64.R10, 0, 1, 0x7F, []byte{0x41, 0xC6, 0x02, 0x7F}}, + {x64.R11, 0, 8, 0x7F, []byte{0x49, 0xC7, 0x03, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R11, 0, 4, 0x7F, []byte{0x41, 0xC7, 0x03, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R11, 0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x03, 0x7F, 0x00}}, + {x64.R11, 0, 1, 0x7F, []byte{0x41, 0xC6, 0x03, 0x7F}}, + {x64.R12, 0, 8, 0x7F, []byte{0x49, 0xC7, 0x04, 0x24, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R12, 0, 4, 0x7F, []byte{0x41, 0xC7, 0x04, 0x24, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R12, 0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x04, 0x24, 0x7F, 0x00}}, + {x64.R12, 0, 1, 0x7F, []byte{0x41, 0xC6, 0x04, 0x24, 0x7F}}, + {x64.R13, 0, 8, 0x7F, []byte{0x49, 0xC7, 0x45, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R13, 0, 4, 0x7F, []byte{0x41, 0xC7, 0x45, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R13, 0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x45, 0x00, 0x7F, 0x00}}, + {x64.R13, 0, 1, 0x7F, []byte{0x41, 0xC6, 0x45, 0x00, 0x7F}}, + {x64.R14, 0, 8, 0x7F, []byte{0x49, 0xC7, 0x06, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R14, 0, 4, 0x7F, []byte{0x41, 0xC7, 0x06, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R14, 0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x06, 0x7F, 0x00}}, + {x64.R14, 0, 1, 0x7F, []byte{0x41, 0xC6, 0x06, 0x7F}}, + {x64.R15, 0, 8, 0x7F, []byte{0x49, 0xC7, 0x07, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R15, 0, 4, 0x7F, []byte{0x41, 0xC7, 0x07, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R15, 0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x07, 0x7F, 0x00}}, + {x64.R15, 0, 1, 0x7F, []byte{0x41, 0xC6, 0x07, 0x7F}}, + + // Offset of 1 + {x64.RAX, 1, 8, 0x7F, []byte{0x48, 0xC7, 0x40, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RAX, 1, 4, 0x7F, []byte{0xC7, 0x40, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RAX, 1, 2, 0x7F, []byte{0x66, 0xC7, 0x40, 0x01, 0x7F, 0x00}}, + {x64.RAX, 1, 1, 0x7F, []byte{0xC6, 0x40, 0x01, 0x7F}}, + {x64.RCX, 1, 8, 0x7F, []byte{0x48, 0xC7, 0x41, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RCX, 1, 4, 0x7F, []byte{0xC7, 0x41, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RCX, 1, 2, 0x7F, []byte{0x66, 0xC7, 0x41, 0x01, 0x7F, 0x00}}, + {x64.RCX, 1, 1, 0x7F, []byte{0xC6, 0x41, 0x01, 0x7F}}, + {x64.RDX, 1, 8, 0x7F, []byte{0x48, 0xC7, 0x42, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RDX, 1, 4, 0x7F, []byte{0xC7, 0x42, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RDX, 1, 2, 0x7F, []byte{0x66, 0xC7, 0x42, 0x01, 0x7F, 0x00}}, + {x64.RDX, 1, 1, 0x7F, []byte{0xC6, 0x42, 0x01, 0x7F}}, + {x64.RBX, 1, 8, 0x7F, []byte{0x48, 0xC7, 0x43, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RBX, 1, 4, 0x7F, []byte{0xC7, 0x43, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RBX, 1, 2, 0x7F, []byte{0x66, 0xC7, 0x43, 0x01, 0x7F, 0x00}}, + {x64.RBX, 1, 1, 0x7F, []byte{0xC6, 0x43, 0x01, 0x7F}}, + {x64.RDI, 1, 8, 0x7F, []byte{0x48, 0xC7, 0x47, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RDI, 1, 4, 0x7F, []byte{0xC7, 0x47, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RDI, 1, 2, 0x7F, []byte{0x66, 0xC7, 0x47, 0x01, 0x7F, 0x00}}, + {x64.RDI, 1, 1, 0x7F, []byte{0xC6, 0x47, 0x01, 0x7F}}, + {x64.RSI, 1, 8, 0x7F, []byte{0x48, 0xC7, 0x46, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RSI, 1, 4, 0x7F, []byte{0xC7, 0x46, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RSI, 1, 2, 0x7F, []byte{0x66, 0xC7, 0x46, 0x01, 0x7F, 0x00}}, + {x64.RSI, 1, 1, 0x7F, []byte{0xC6, 0x46, 0x01, 0x7F}}, + {x64.RBP, 1, 8, 0x7F, []byte{0x48, 0xC7, 0x45, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RBP, 1, 4, 0x7F, []byte{0xC7, 0x45, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RBP, 1, 2, 0x7F, []byte{0x66, 0xC7, 0x45, 0x01, 0x7F, 0x00}}, + {x64.RBP, 1, 1, 0x7F, []byte{0xC6, 0x45, 0x01, 0x7F}}, + {x64.RSP, 1, 8, 0x7F, []byte{0x48, 0xC7, 0x44, 0x24, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RSP, 1, 4, 0x7F, []byte{0xC7, 0x44, 0x24, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RSP, 1, 2, 0x7F, []byte{0x66, 0xC7, 0x44, 0x24, 0x01, 0x7F, 0x00}}, + {x64.RSP, 1, 1, 0x7F, []byte{0xC6, 0x44, 0x24, 0x01, 0x7F}}, + {x64.R8, 1, 8, 0x7F, []byte{0x49, 0xC7, 0x40, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R8, 1, 4, 0x7F, []byte{0x41, 0xC7, 0x40, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R8, 1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x40, 0x01, 0x7F, 0x00}}, + {x64.R8, 1, 1, 0x7F, []byte{0x41, 0xC6, 0x40, 0x01, 0x7F}}, + {x64.R9, 1, 8, 0x7F, []byte{0x49, 0xC7, 0x41, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R9, 1, 4, 0x7F, []byte{0x41, 0xC7, 0x41, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R9, 1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x41, 0x01, 0x7F, 0x00}}, + {x64.R9, 1, 1, 0x7F, []byte{0x41, 0xC6, 0x41, 0x01, 0x7F}}, + {x64.R10, 1, 8, 0x7F, []byte{0x49, 0xC7, 0x42, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R10, 1, 4, 0x7F, []byte{0x41, 0xC7, 0x42, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R10, 1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x42, 0x01, 0x7F, 0x00}}, + {x64.R10, 1, 1, 0x7F, []byte{0x41, 0xC6, 0x42, 0x01, 0x7F}}, + {x64.R11, 1, 8, 0x7F, []byte{0x49, 0xC7, 0x43, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R11, 1, 4, 0x7F, []byte{0x41, 0xC7, 0x43, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R11, 1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x43, 0x01, 0x7F, 0x00}}, + {x64.R11, 1, 1, 0x7F, []byte{0x41, 0xC6, 0x43, 0x01, 0x7F}}, + {x64.R12, 1, 8, 0x7F, []byte{0x49, 0xC7, 0x44, 0x24, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R12, 1, 4, 0x7F, []byte{0x41, 0xC7, 0x44, 0x24, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R12, 1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x44, 0x24, 0x01, 0x7F, 0x00}}, + {x64.R12, 1, 1, 0x7F, []byte{0x41, 0xC6, 0x44, 0x24, 0x01, 0x7F}}, + {x64.R13, 1, 8, 0x7F, []byte{0x49, 0xC7, 0x45, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R13, 1, 4, 0x7F, []byte{0x41, 0xC7, 0x45, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R13, 1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x45, 0x01, 0x7F, 0x00}}, + {x64.R13, 1, 1, 0x7F, []byte{0x41, 0xC6, 0x45, 0x01, 0x7F}}, + {x64.R14, 1, 8, 0x7F, []byte{0x49, 0xC7, 0x46, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R14, 1, 4, 0x7F, []byte{0x41, 0xC7, 0x46, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R14, 1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x46, 0x01, 0x7F, 0x00}}, + {x64.R14, 1, 1, 0x7F, []byte{0x41, 0xC6, 0x46, 0x01, 0x7F}}, + {x64.R15, 1, 8, 0x7F, []byte{0x49, 0xC7, 0x47, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R15, 1, 4, 0x7F, []byte{0x41, 0xC7, 0x47, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R15, 1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x47, 0x01, 0x7F, 0x00}}, + {x64.R15, 1, 1, 0x7F, []byte{0x41, 0xC6, 0x47, 0x01, 0x7F}}, + } + + for _, pattern := range usagePatterns { + t.Logf("store %dB [%s+%d], %d", pattern.ByteCount, pattern.Register, pattern.Offset, pattern.Number) + code := x64.StoreNumber(nil, pattern.Register, pattern.Offset, pattern.ByteCount, pattern.Number) + assert.DeepEqual(t, code, pattern.Code) + } +} diff --git a/src/build/arch/x64/Sub.go b/src/build/arch/x64/Sub.go index 4dd9b23..ba1ce9b 100644 --- a/src/build/arch/x64/Sub.go +++ b/src/build/arch/x64/Sub.go @@ -6,10 +6,10 @@ import ( // SubRegisterNumber subtracts a number from the given register. func SubRegisterNumber(code []byte, destination cpu.Register, number int) []byte { - return regRegNum(code, 0b101, byte(destination), number, 0x83, 0x81) + return encodeNum(code, 1, AddressDirect, 0b101, byte(destination), number, 0x83, 0x81) } // SubRegisterRegister subtracts a register value from another register. func SubRegisterRegister(code []byte, destination cpu.Register, operand cpu.Register) []byte { - return regReg(code, byte(operand), byte(destination), 0x29) + return encode(code, 1, AddressDirect, byte(operand), byte(destination), 0x29) } diff --git a/src/build/arch/x64/encode.go b/src/build/arch/x64/encode.go new file mode 100644 index 0000000..10a1c30 --- /dev/null +++ b/src/build/arch/x64/encode.go @@ -0,0 +1,27 @@ +package x64 + +// encode is the core function that encodes an instruction. +func encode(code []byte, w byte, mod byte, reg byte, rm byte, opCodes ...byte) []byte { + r := byte(0) // Extension to the "reg" field in ModRM. + x := byte(0) // Extension to the SIB index field. + b := byte(0) // Extension to the "rm" field in ModRM or the SIB base (r8 up to r15 use this). + + if reg > 0b111 { + r = 1 + reg &= 0b111 + } + + if rm > 0b111 { + b = 1 + rm &= 0b111 + } + + if w != 0 || r != 0 || x != 0 || b != 0 { + code = append(code, REX(w, r, x, b)) + } + + code = append(code, opCodes...) + code = append(code, ModRM(mod, reg, rm)) + + return code +} diff --git a/src/build/arch/x64/encodeNum.go b/src/build/arch/x64/encodeNum.go new file mode 100644 index 0000000..6bcaec8 --- /dev/null +++ b/src/build/arch/x64/encodeNum.go @@ -0,0 +1,14 @@ +package x64 + +import "encoding/binary" + +// encodeNum encodes an instruction with up to two registers and a number parameter. +func encodeNum(code []byte, w byte, mod byte, reg byte, rm byte, number int, opCode8 byte, opCode32 byte) []byte { + if SizeOf(int64(number)) == 1 { + code = encode(code, w, mod, reg, rm, opCode8) + return append(code, byte(number)) + } + + code = encode(code, w, mod, reg, rm, opCode32) + return binary.LittleEndian.AppendUint32(code, uint32(number)) +} diff --git a/src/build/arch/x64/regReg.go b/src/build/arch/x64/regReg.go deleted file mode 100644 index 57b0f00..0000000 --- a/src/build/arch/x64/regReg.go +++ /dev/null @@ -1,29 +0,0 @@ -package x64 - -// regReg encodes an operation using 2 registers. -func regReg(code []byte, reg byte, rm byte, opCodes ...byte) []byte { - w := byte(1) // Indicates a 64-bit register. - r := byte(0) // Extension to the "reg" field in ModRM. - x := byte(0) // Extension to the SIB index field. - b := byte(0) // Extension to the "rm" field in ModRM or the SIB base (r8 up to r15 use this). - mod := byte(0b11) // Direct addressing mode, no register offsets. - - if reg > 0b111 { - r = 1 - reg &= 0b111 - } - - if rm > 0b111 { - b = 1 - rm &= 0b111 - } - - rex := REX(w, r, x, b) - modRM := ModRM(mod, reg, rm) - - code = append(code, rex) - code = append(code, opCodes...) - code = append(code, modRM) - - return code -} diff --git a/src/build/arch/x64/regRegNum.go b/src/build/arch/x64/regRegNum.go deleted file mode 100644 index 61ffefb..0000000 --- a/src/build/arch/x64/regRegNum.go +++ /dev/null @@ -1,14 +0,0 @@ -package x64 - -import "encoding/binary" - -// regRegNum encodes an instruction with up to two registers and a number parameter. -func regRegNum(code []byte, reg byte, rm byte, number int, opCode8 byte, opCode32 byte) []byte { - if SizeOf(int64(number)) == 1 { - code = regReg(code, reg, rm, opCode8) - return append(code, byte(number)) - } - - code = regReg(code, reg, rm, opCode32) - return binary.LittleEndian.AppendUint32(code, uint32(number)) -}