Implemented bit shifting on arm64

This commit is contained in:
2025-03-15 19:45:14 +01:00
parent 93042f81e7
commit b8f05c8994
10 changed files with 120 additions and 16 deletions

View File

@ -6,8 +6,8 @@ import (
// AndRegisterNumber performs a bitwise AND using a register and a number.
func AndRegisterNumber(destination cpu.Register, source cpu.Register, number int) (uint32, bool) {
imm13, encodable := encodeLogicalImmediate(uint(number))
return 0b100100100<<23 | reg2BitmaskImm(destination, source, imm13), encodable
n, immr, imms, encodable := encodeLogicalImmediate(uint(number))
return 0b100100100<<23 | reg2BitmaskImm(destination, source, n, immr, imms), encodable
}
// AndRegisterRegister performs a bitwise AND using two registers.

View File

@ -4,8 +4,8 @@ import "git.urbach.dev/cli/q/src/cpu"
// OrRegisterNumber performs a bitwise OR using a register and a number.
func OrRegisterNumber(destination cpu.Register, source cpu.Register, number int) (uint32, bool) {
imm13, encodable := encodeLogicalImmediate(uint(number))
return 0b101100100<<23 | reg2BitmaskImm(destination, source, imm13), encodable
n, immr, imms, encodable := encodeLogicalImmediate(uint(number))
return 0b101100100<<23 | reg2BitmaskImm(destination, source, n, immr, imms), encodable
}
// OrRegisterRegister performs a bitwise OR using two registers.

15
src/arm/Shift.go Normal file
View File

@ -0,0 +1,15 @@
package arm
import (
"git.urbach.dev/cli/q/src/cpu"
)
// ShiftLeftNumber shifts the register value a specified amount of bits to the left.
func ShiftLeftNumber(destination cpu.Register, source cpu.Register, bits int) uint32 {
return 0b110100110<<23 | reg2BitmaskImm(destination, source, 1, 64-bits, (^bits)&mask6)
}
// ShiftRightSignedNumber shifts the signed register value a specified amount of bits to the right.
func ShiftRightSignedNumber(destination cpu.Register, source cpu.Register, bits int) uint32 {
return 0b100100110<<23 | reg2BitmaskImm(destination, source, 1, bits&mask6, 0b111111)
}

52
src/arm/Shift_test.go Normal file
View File

@ -0,0 +1,52 @@
package arm_test
import (
"testing"
"git.urbach.dev/cli/q/src/arm"
"git.urbach.dev/cli/q/src/cpu"
"git.urbach.dev/go/assert"
)
func TestShiftLeftNumber(t *testing.T) {
usagePatterns := []struct {
Destination cpu.Register
Source cpu.Register
Bits int
Code uint32
}{
{arm.X0, arm.X0, 0, 0xD340FC00},
{arm.X0, arm.X0, 1, 0xD37FF800},
{arm.X0, arm.X0, 8, 0xD378DC00},
{arm.X0, arm.X0, 16, 0xD370BC00},
{arm.X0, arm.X0, 63, 0xD3410000},
}
for _, pattern := range usagePatterns {
t.Logf("%b", pattern.Code)
t.Logf("lsl %s, %s, %x", pattern.Destination, pattern.Source, pattern.Bits)
code := arm.ShiftLeftNumber(pattern.Destination, pattern.Source, pattern.Bits)
assert.DeepEqual(t, code, pattern.Code)
}
}
func TestShiftRightSignedNumber(t *testing.T) {
usagePatterns := []struct {
Destination cpu.Register
Source cpu.Register
Bits int
Code uint32
}{
{arm.X0, arm.X0, 0, 0x9340FC00},
{arm.X0, arm.X0, 1, 0x9341FC00},
{arm.X0, arm.X0, 8, 0x9348FC00},
{arm.X0, arm.X0, 16, 0x9350FC00},
{arm.X0, arm.X0, 63, 0x937FFC00},
}
for _, pattern := range usagePatterns {
t.Logf("asr %s, %s, %x", pattern.Destination, pattern.Source, pattern.Bits)
code := arm.ShiftRightSignedNumber(pattern.Destination, pattern.Source, pattern.Bits)
assert.DeepEqual(t, code, pattern.Code)
}
}

View File

@ -4,8 +4,8 @@ import "git.urbach.dev/cli/q/src/cpu"
// XorRegisterNumber performs a bitwise XOR using a register and a number.
func XorRegisterNumber(destination cpu.Register, source cpu.Register, number int) (uint32, bool) {
imm13, encodable := encodeLogicalImmediate(uint(number))
return 0b110100100<<23 | reg2BitmaskImm(destination, source, imm13), encodable
n, immr, imms, encodable := encodeLogicalImmediate(uint(number))
return 0b110100100<<23 | reg2BitmaskImm(destination, source, n, immr, imms), encodable
}
// XorRegisterRegister performs a bitwise XOR using two registers.

View File

@ -7,9 +7,24 @@ import (
"git.urbach.dev/go/assert"
)
func TestARM(t *testing.T) {
func TestGeneral(t *testing.T) {
assert.DeepEqual(t, arm.Call(0), 0x94000000)
assert.DeepEqual(t, arm.Nop(), 0xD503201F)
assert.DeepEqual(t, arm.Return(), 0xD65F03C0)
assert.DeepEqual(t, arm.Syscall(), 0xD4000001)
}
func TestNotEncodable(t *testing.T) {
_, encodable := arm.AndRegisterNumber(arm.X0, arm.X0, 0)
assert.False(t, encodable)
_, encodable = arm.OrRegisterNumber(arm.X0, arm.X0, 0)
assert.False(t, encodable)
_, encodable = arm.XorRegisterNumber(arm.X0, arm.X0, 0)
assert.False(t, encodable)
_, encodable = arm.AndRegisterNumber(arm.X0, arm.X0, -1)
assert.False(t, encodable)
_, encodable = arm.OrRegisterNumber(arm.X0, arm.X0, -1)
assert.False(t, encodable)
_, encodable = arm.XorRegisterNumber(arm.X0, arm.X0, -1)
assert.False(t, encodable)
}

View File

@ -4,9 +4,9 @@ import "math/bits"
// encodeLogicalImmediate encodes a bitmask immediate.
// The algorithm used here was made by Dougall Johnson.
func encodeLogicalImmediate(val uint) (int, bool) {
func encodeLogicalImmediate(val uint) (N int, immr int, imms int, encodable bool) {
if val == 0 || ^val == 0 {
return 0, false
return 0, 0, 0, false
}
rotation := bits.TrailingZeros(clearTrailingOnes(val))
@ -16,15 +16,15 @@ func encodeLogicalImmediate(val uint) (int, bool) {
ones := bits.TrailingZeros(^normalized)
size := zeroes + ones
immr := -rotation & (size - 1)
imms := -(size << 1) | (ones - 1)
N := (size >> 6)
immr = -rotation & (size - 1)
imms = -(size << 1) | (ones - 1)
N = (size >> 6)
if bits.RotateLeft(val, -(size&63)) != val {
return 0, false
return 0, 0, 0, false
}
return N<<12 | immr<<6 | (imms & 0x3f), true
return N, immr, (imms & 0x3f), true
}
// clearTrailingOnes clears trailing one bits.

View File

@ -39,8 +39,8 @@ func reg2Imm(d cpu.Register, n cpu.Register, imm12 int) uint32 {
}
// reg2BitmaskImm encodes an instruction with 2 registers and a bitmask immediate.
func reg2BitmaskImm(d cpu.Register, n cpu.Register, imm13 int) uint32 {
return uint32(imm13)<<10 | uint32(n)<<5 | uint32(d)
func reg2BitmaskImm(d cpu.Register, n cpu.Register, N int, immr int, imms int) uint32 {
return uint32(N)<<22 | uint32(immr)<<16 | uint32(imms)<<10 | uint32(n)<<5 | uint32(d)
}
// reg3 encodes an instruction with 3 registers.

View File

@ -256,6 +256,24 @@ func (c *compiler) compileARM(x asm.Instruction) {
}
}
case asm.SHIFTL:
switch x.Type {
case asm.TypeRegisterNumber:
operands := c.assembler.Param.RegisterNumber[x.Index]
c.append(arm.ShiftLeftNumber(operands.Register, operands.Register, operands.Number&0b111111))
case asm.TypeRegisterRegister:
panic("not implemented")
}
case asm.SHIFTRS:
switch x.Type {
case asm.TypeRegisterNumber:
operands := c.assembler.Param.RegisterNumber[x.Index]
c.append(arm.ShiftRightSignedNumber(operands.Register, operands.Register, operands.Number&0b111111))
case asm.TypeRegisterRegister:
panic("not implemented")
}
case asm.RETURN:
c.append(arm.LoadPair(arm.FP, arm.LR, arm.SP, 16))
c.append(arm.Return())

View File

@ -155,6 +155,8 @@ func (c *compiler) compileX86(x asm.Instruction) {
case asm.TypeRegisterNumber:
operands := c.assembler.Param.RegisterNumber[x.Index]
c.code = x86.ShiftLeftNumber(c.code, operands.Register, byte(operands.Number)&0b111111)
case asm.TypeRegisterRegister:
panic("not implemented")
}
case asm.SHIFTRS:
@ -162,6 +164,8 @@ func (c *compiler) compileX86(x asm.Instruction) {
case asm.TypeRegisterNumber:
operands := c.assembler.Param.RegisterNumber[x.Index]
c.code = x86.ShiftRightSignedNumber(c.code, operands.Register, byte(operands.Number)&0b111111)
case asm.TypeRegisterRegister:
panic("not implemented")
}
case asm.STORE: