Refactored arm package

This commit is contained in:
2025-04-16 17:38:48 +02:00
parent 53ecf40229
commit df725a2b23
26 changed files with 102 additions and 53 deletions

View File

@ -23,7 +23,7 @@ func TestAddRegisterNumber(t *testing.T) {
t.Logf("add %s, %s, %d", pattern.Destination, pattern.Source, pattern.Number) t.Logf("add %s, %s, %d", pattern.Destination, pattern.Source, pattern.Number)
code, encodable := arm.AddRegisterNumber(pattern.Destination, pattern.Source, pattern.Number) code, encodable := arm.AddRegisterNumber(pattern.Destination, pattern.Source, pattern.Number)
assert.True(t, encodable) assert.True(t, encodable)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }
@ -40,6 +40,6 @@ func TestAddRegisterRegister(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("add %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand) t.Logf("add %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand)
code := arm.AddRegisterRegister(pattern.Destination, pattern.Source, pattern.Operand) code := arm.AddRegisterRegister(pattern.Destination, pattern.Source, pattern.Operand)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -27,7 +27,7 @@ func TestAndRegisterNumber(t *testing.T) {
t.Logf("and %s, %s, %d", pattern.Destination, pattern.Source, pattern.Number) t.Logf("and %s, %s, %d", pattern.Destination, pattern.Source, pattern.Number)
code, encodable := arm.AndRegisterNumber(pattern.Destination, pattern.Source, pattern.Number) code, encodable := arm.AndRegisterNumber(pattern.Destination, pattern.Source, pattern.Number)
assert.True(t, encodable) assert.True(t, encodable)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }
@ -44,6 +44,6 @@ func TestAndRegisterRegister(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("and %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand) t.Logf("and %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand)
code := arm.AndRegisterRegister(pattern.Destination, pattern.Source, pattern.Operand) code := arm.AndRegisterRegister(pattern.Destination, pattern.Source, pattern.Operand)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -3,6 +3,6 @@ package arm
// Call branches to a PC-relative offset, setting the register X30 to PC+4. // Call branches to a PC-relative offset, setting the register X30 to PC+4.
// The offset starts from the address of this instruction and is encoded as "imm26" times 4. // The offset starts from the address of this instruction and is encoded as "imm26" times 4.
// This instruction is also known as BL (branch with link). // This instruction is also known as BL (branch with link).
func Call(offset uint32) uint32 { func Call(offset int) uint32 {
return uint32(0b100101<<26) | offset return uint32(0b100101<<26) | uint32(offset&mask26)
} }

25
src/arm/Call_test.go Normal file
View File

@ -0,0 +1,25 @@
package arm_test
import (
"testing"
"git.urbach.dev/cli/q/src/arm"
"git.urbach.dev/go/assert"
)
func TestCall(t *testing.T) {
usagePatterns := []struct {
Offset int
Code uint32
}{
{0, 0x94000000},
{1, 0x94000001},
{-1, 0x97FFFFFF},
}
for _, pattern := range usagePatterns {
t.Logf("bl %d", pattern.Offset)
code := arm.Call(pattern.Offset)
assert.Equal(t, code, pattern.Code)
}
}

View File

@ -24,7 +24,7 @@ func TestCompareRegisterNumber(t *testing.T) {
t.Logf("cmp %s, %d", pattern.Source, pattern.Number) t.Logf("cmp %s, %d", pattern.Source, pattern.Number)
code, encodable := arm.CompareRegisterNumber(pattern.Source, pattern.Number) code, encodable := arm.CompareRegisterNumber(pattern.Source, pattern.Number)
assert.True(t, encodable) assert.True(t, encodable)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }
@ -40,6 +40,6 @@ func TestCompareRegisterRegister(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("cmp %s, %s", pattern.Left, pattern.Right) t.Logf("cmp %s, %s", pattern.Left, pattern.Right)
code := arm.CompareRegisterRegister(pattern.Left, pattern.Right) code := arm.CompareRegisterRegister(pattern.Left, pattern.Right)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -21,6 +21,6 @@ func TestDivSigned(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("sdiv %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand) t.Logf("sdiv %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand)
code := arm.DivSigned(pattern.Destination, pattern.Source, pattern.Operand) code := arm.DivSigned(pattern.Destination, pattern.Source, pattern.Operand)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -2,42 +2,40 @@ package arm
// Jump continues program flow at the new offset. // Jump continues program flow at the new offset.
func Jump(offset int) uint32 { func Jump(offset int) uint32 {
offset &= 0b11_1111_1111_1111_1111_1111_1111 return 0b000101<<26 | uint32(offset&mask26)
return 0b000101<<26 | uint32(offset)
} }
// JumpIfEqual jumps if the result was equal. // JumpIfEqual jumps if the result was equal.
func JumpIfEqual(offset int) uint32 { func JumpIfEqual(offset int) uint32 {
return branchCond(0b0000, offset) return branchCond(EQ, offset)
} }
// JumpIfNotEqual jumps if the result was not equal. // JumpIfNotEqual jumps if the result was not equal.
func JumpIfNotEqual(offset int) uint32 { func JumpIfNotEqual(offset int) uint32 {
return branchCond(0b0001, offset) return branchCond(NE, offset)
} }
// JumpIfGreater jumps if the result was greater. // JumpIfGreater jumps if the result was greater.
func JumpIfGreater(offset int) uint32 { func JumpIfGreater(offset int) uint32 {
return branchCond(0b1100, offset) return branchCond(GT, offset)
} }
// JumpIfGreaterOrEqual jumps if the result was greater or equal. // JumpIfGreaterOrEqual jumps if the result was greater or equal.
func JumpIfGreaterOrEqual(offset int) uint32 { func JumpIfGreaterOrEqual(offset int) uint32 {
return branchCond(0b1010, offset) return branchCond(GE, offset)
} }
// JumpIfLess jumps if the result was less. // JumpIfLess jumps if the result was less.
func JumpIfLess(offset int) uint32 { func JumpIfLess(offset int) uint32 {
return branchCond(0b1001, offset) return branchCond(LS, offset)
} }
// JumpIfLessOrEqual jumps if the result was less or equal. // JumpIfLessOrEqual jumps if the result was less or equal.
func JumpIfLessOrEqual(offset int) uint32 { func JumpIfLessOrEqual(offset int) uint32 {
return branchCond(0b1101, offset) return branchCond(LE, offset)
} }
// branchCond performs a conditional branch to a PC-relative offset. // branchCond performs a conditional branch to a PC-relative offset.
func branchCond(cond uint32, offset int) uint32 { func branchCond(cond condition, imm19 int) uint32 {
offset &= 0b111_1111_1111_1111_1111 return 0b01010100<<24 | uint32(imm19&mask19)<<5 | uint32(cond)
return 0b01010100<<24 | uint32(offset)<<5 | cond
} }

View File

@ -63,6 +63,6 @@ func TestJump(t *testing.T) {
code = arm.JumpIfLessOrEqual(pattern.Offset) code = arm.JumpIfLessOrEqual(pattern.Offset)
} }
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -21,6 +21,6 @@ func TestLoadAddress(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("adr %s, %d", pattern.Destination, pattern.Number) t.Logf("adr %s, %d", pattern.Destination, pattern.Number)
code := arm.LoadAddress(pattern.Destination, pattern.Number) code := arm.LoadAddress(pattern.Destination, pattern.Number)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -23,6 +23,6 @@ func TestLoadPair(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("ldp %s, %s, [%s], #%d", pattern.Reg1, pattern.Reg2, pattern.Base, pattern.Offset) t.Logf("ldp %s, %s, [%s], #%d", pattern.Reg1, pattern.Reg2, pattern.Base, pattern.Offset)
code := arm.LoadPair(pattern.Reg1, pattern.Reg2, pattern.Base, pattern.Offset) code := arm.LoadPair(pattern.Reg1, pattern.Reg2, pattern.Base, pattern.Offset)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -34,6 +34,6 @@ func TestLoadRegister(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("ldur %s, [%s, %d] %db", pattern.Destination, pattern.Base, pattern.Offset, pattern.Length) t.Logf("ldur %s, [%s, %d] %db", pattern.Destination, pattern.Base, pattern.Offset, pattern.Length)
code := arm.LoadRegister(pattern.Destination, pattern.Base, pattern.Offset, pattern.Length) code := arm.LoadRegister(pattern.Destination, pattern.Base, pattern.Offset, pattern.Length)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -23,7 +23,7 @@ func TestMoveRegisterRegister(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("mov %s, %s", pattern.Destination, pattern.Source) t.Logf("mov %s, %s", pattern.Destination, pattern.Source)
code := arm.MoveRegisterRegister(pattern.Destination, pattern.Source) code := arm.MoveRegisterRegister(pattern.Destination, pattern.Source)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }
@ -79,7 +79,7 @@ func TestMoveRegisterNumberSI(t *testing.T) {
if pattern.Code != 0 { if pattern.Code != 0 {
assert.True(t, encodable) assert.True(t, encodable)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} else { } else {
assert.False(t, encodable) assert.False(t, encodable)
} }
@ -99,7 +99,7 @@ func TestMoveKeep(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("movk %s, %d", pattern.Register, pattern.Number) t.Logf("movk %s, %d", pattern.Register, pattern.Number)
code := arm.MoveKeep(pattern.Register, 0, pattern.Number) code := arm.MoveKeep(pattern.Register, 0, pattern.Number)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }
@ -116,6 +116,6 @@ func TestMoveZero(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("movz %s, %d", pattern.Register, pattern.Number) t.Logf("movz %s, %d", pattern.Register, pattern.Number)
code := arm.MoveZero(pattern.Register, 0, pattern.Number) code := arm.MoveZero(pattern.Register, 0, pattern.Number)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -21,7 +21,7 @@ func TestMulRegisterRegister(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("mul %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand) t.Logf("mul %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand)
code := arm.MulRegisterRegister(pattern.Destination, pattern.Source, pattern.Operand) code := arm.MulRegisterRegister(pattern.Destination, pattern.Source, pattern.Operand)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }
@ -40,6 +40,6 @@ func TestMultiplySubtract(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("msub %s, %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand, pattern.Extra) t.Logf("msub %s, %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand, pattern.Extra)
code := arm.MultiplySubtract(pattern.Destination, pattern.Source, pattern.Operand, pattern.Extra) code := arm.MultiplySubtract(pattern.Destination, pattern.Source, pattern.Operand, pattern.Extra)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -21,6 +21,6 @@ func TestNegateRegister(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("neg %s, %s", pattern.Destination, pattern.Source) t.Logf("neg %s, %s", pattern.Destination, pattern.Source)
code := arm.NegateRegister(pattern.Destination, pattern.Source) code := arm.NegateRegister(pattern.Destination, pattern.Source)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -27,7 +27,7 @@ func TestOrRegisterNumber(t *testing.T) {
t.Logf("orr %s, %s, %d", pattern.Destination, pattern.Source, pattern.Number) t.Logf("orr %s, %s, %d", pattern.Destination, pattern.Source, pattern.Number)
code, encodable := arm.OrRegisterNumber(pattern.Destination, pattern.Source, pattern.Number) code, encodable := arm.OrRegisterNumber(pattern.Destination, pattern.Source, pattern.Number)
assert.True(t, encodable) assert.True(t, encodable)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }
@ -44,6 +44,6 @@ func TestOrRegisterRegister(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("orr %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand) t.Logf("orr %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand)
code := arm.OrRegisterRegister(pattern.Destination, pattern.Source, pattern.Operand) code := arm.OrRegisterRegister(pattern.Destination, pattern.Source, pattern.Operand)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -26,7 +26,7 @@ func TestShiftLeftNumber(t *testing.T) {
t.Logf("%b", pattern.Code) t.Logf("%b", pattern.Code)
t.Logf("lsl %s, %s, %x", pattern.Destination, pattern.Source, pattern.Bits) t.Logf("lsl %s, %s, %x", pattern.Destination, pattern.Source, pattern.Bits)
code := arm.ShiftLeftNumber(pattern.Destination, pattern.Source, pattern.Bits) code := arm.ShiftLeftNumber(pattern.Destination, pattern.Source, pattern.Bits)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }
@ -47,6 +47,6 @@ func TestShiftRightSignedNumber(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("asr %s, %s, %x", pattern.Destination, pattern.Source, pattern.Bits) t.Logf("asr %s, %s, %x", pattern.Destination, pattern.Source, pattern.Bits)
code := arm.ShiftRightSignedNumber(pattern.Destination, pattern.Source, pattern.Bits) code := arm.ShiftRightSignedNumber(pattern.Destination, pattern.Source, pattern.Bits)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -23,6 +23,6 @@ func TestStorePair(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("stp %s, %s, [%s, #%d]!", pattern.Reg1, pattern.Reg2, pattern.Base, pattern.Offset) t.Logf("stp %s, %s, [%s, #%d]!", pattern.Reg1, pattern.Reg2, pattern.Base, pattern.Offset)
code := arm.StorePair(pattern.Reg1, pattern.Reg2, pattern.Base, pattern.Offset) code := arm.StorePair(pattern.Reg1, pattern.Reg2, pattern.Base, pattern.Offset)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -29,6 +29,6 @@ func TestStoreRegister(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("stur %s, [%s, #%d] %db", pattern.Source, pattern.Base, pattern.Offset, pattern.Length) t.Logf("stur %s, [%s, #%d] %db", pattern.Source, pattern.Base, pattern.Offset, pattern.Length)
code := arm.StoreRegister(pattern.Source, pattern.Base, pattern.Offset, pattern.Length) code := arm.StoreRegister(pattern.Source, pattern.Base, pattern.Offset, pattern.Length)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -24,7 +24,7 @@ func TestSubRegisterNumber(t *testing.T) {
t.Logf("sub %s, %s, %d", pattern.Destination, pattern.Source, pattern.Number) t.Logf("sub %s, %s, %d", pattern.Destination, pattern.Source, pattern.Number)
code, encodable := arm.SubRegisterNumber(pattern.Destination, pattern.Source, pattern.Number) code, encodable := arm.SubRegisterNumber(pattern.Destination, pattern.Source, pattern.Number)
assert.True(t, encodable) assert.True(t, encodable)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }
@ -41,6 +41,6 @@ func TestSubRegisterRegister(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("sub %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand) t.Logf("sub %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand)
code := arm.SubRegisterRegister(pattern.Destination, pattern.Source, pattern.Operand) code := arm.SubRegisterRegister(pattern.Destination, pattern.Source, pattern.Operand)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -27,7 +27,7 @@ func TestXorRegisterNumber(t *testing.T) {
t.Logf("eor %s, %s, %d", pattern.Destination, pattern.Source, pattern.Number) t.Logf("eor %s, %s, %d", pattern.Destination, pattern.Source, pattern.Number)
code, encodable := arm.XorRegisterNumber(pattern.Destination, pattern.Source, pattern.Number) code, encodable := arm.XorRegisterNumber(pattern.Destination, pattern.Source, pattern.Number)
assert.True(t, encodable) assert.True(t, encodable)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }
@ -44,6 +44,6 @@ func TestXorRegisterRegister(t *testing.T) {
for _, pattern := range usagePatterns { for _, pattern := range usagePatterns {
t.Logf("eor %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand) t.Logf("eor %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand)
code := arm.XorRegisterRegister(pattern.Destination, pattern.Source, pattern.Operand) code := arm.XorRegisterRegister(pattern.Destination, pattern.Source, pattern.Operand)
assert.DeepEqual(t, code, pattern.Code) assert.Equal(t, code, pattern.Code)
} }
} }

View File

@ -7,8 +7,7 @@ import (
"git.urbach.dev/go/assert" "git.urbach.dev/go/assert"
) )
func TestGeneral(t *testing.T) { func TestConstants(t *testing.T) {
assert.DeepEqual(t, arm.Call(0), 0x94000000)
assert.DeepEqual(t, arm.Nop(), 0xD503201F) assert.DeepEqual(t, arm.Nop(), 0xD503201F)
assert.DeepEqual(t, arm.Return(), 0xD65F03C0) assert.DeepEqual(t, arm.Return(), 0xD65F03C0)
assert.DeepEqual(t, arm.Syscall(), 0xD4000001) assert.DeepEqual(t, arm.Syscall(), 0xD4000001)

View File

@ -24,7 +24,7 @@ func encodeLogicalImmediate(val uint) (N int, immr int, imms int, encodable bool
return 0, 0, 0, false return 0, 0, 0, false
} }
return N, immr, (imms & 0x3f), true return N, immr, (imms & 0x3F), true
} }
// clearTrailingOnes clears trailing one bits. // clearTrailingOnes clears trailing one bits.

22
src/arm/condition.go Normal file
View File

@ -0,0 +1,22 @@
package arm
type condition uint8
const (
EQ condition = iota
NE
CS
CC
MI
PL
VS
VC
HI
LS
GE
LT
GT
LE
AL
NV
)

View File

@ -4,14 +4,6 @@ import (
"git.urbach.dev/cli/q/src/cpu" "git.urbach.dev/cli/q/src/cpu"
) )
const (
mask6 = 0b111111
mask7 = 0b1111111
mask9 = 0b1_11111111
mask12 = 0b1111_11111111
mask16 = 0b11111111_11111111
)
// memory encodes an instruction with a register, a base register and an offset. // memory encodes an instruction with a register, a base register and an offset.
func memory(destination cpu.Register, base cpu.Register, imm9 int) uint32 { func memory(destination cpu.Register, base cpu.Register, imm9 int) uint32 {
return uint32(imm9&mask9)<<12 | uint32(base)<<5 | uint32(destination) return uint32(imm9&mask9)<<12 | uint32(base)<<5 | uint32(destination)

11
src/arm/mask.go Normal file
View File

@ -0,0 +1,11 @@
package arm
const (
mask6 = 0b111111
mask7 = 0b1111111
mask9 = 0b1_11111111
mask12 = 0b1111_11111111
mask16 = 0b11111111_11111111
mask19 = 0b111_11111111_11111111
mask26 = 0b11_11111111_11111111_11111111
)

View File

@ -100,10 +100,12 @@ func (c *armCompiler) handleCallInstruction(instruction asm.Instruction) {
pointer.Resolve = func() Address { pointer.Resolve = func() Address {
destination, exists := c.codeLabels[label.Name] destination, exists := c.codeLabels[label.Name]
if !exists { if !exists {
panic(fmt.Sprintf("unknown jump label %s", label.Name)) panic(fmt.Sprintf("unknown jump label %s", label.Name))
} }
distance := (destination - position) / 4
distance := int(destination-position) / 4
return arm.Call(distance) return arm.Call(distance)
} }