Something went wrong. Try again.
🌱 A minimal programming language and compiler. git.urbach.dev/cli/q
high-performance programming-language compiler
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349package asm
import ( "encoding/binary" "fmt"
"git.urbach.dev/cli/q/src/arm" "git.urbach.dev/cli/q/src/config")
type compilerARM struct { *compiler}
func (c *compiler) append(code uint32) { c.code = binary.LittleEndian.AppendUint32(c.code, code)}
func (c *compilerARM) Compile(instr Instruction) { switch instr := instr.(type) { case *Add: c.append(arm.AddRegisterRegister(instr.Destination, instr.Source, instr.Operand)) case *AddNumber: code, _ := arm.AddRegisterNumber(instr.Destination, instr.Source, instr.Number) c.append(code) case *And: c.append(arm.AndRegisterRegister(instr.Destination, instr.Source, instr.Operand)) case *AndNumber: code, _ := arm.AndRegisterNumber(instr.Destination, instr.Source, instr.Number) c.append(code) case *Call: call, _ := arm.Call(0) c.append(call) patch := c.PatchLast4Bytes()
patch.apply = func(code []byte) []byte { address, exists := c.labels[instr.Label]
if !exists { panic("unknown label: " + instr.Label) }
offset := address - patch.start
if offset%4 != 0 { panic("call offset is not a multiple of 4") }
call, encodable := arm.Call(offset / 4)
if !encodable { panic("call offset outside of encodable range") }
binary.LittleEndian.PutUint32(code, call) return code } case *CallExtern: call, _ := arm.LoadAddress(arm.X16, 0) c.append(call) patch := c.PatchLast4Bytes() c.append(arm.LoadFixedOffset(arm.X16, arm.X16, arm.UnscaledImmediate, 0, 8)) c.append(arm.CallRegister(arm.X16))
patch.apply = func(code []byte) []byte { index := c.libraries.Index(instr.Library, instr.Function)
if index == -1 { panic(fmt.Sprintf("unknown extern function '%s' in library '%s'", instr.Function, instr.Library)) }
address := c.importsStart + index*8 offset := address - patch.start loadAddress, encodable := arm.LoadAddress(arm.X16, offset)
if !encodable { panic("label offset outside of encodable range") }
binary.LittleEndian.PutUint32(code, loadAddress) return code } case *CallExternStart: case *CallExternEnd: case *CallRegister: c.append(arm.CallRegister(instr.Address)) case *Compare: c.append(arm.CompareRegisterRegister(instr.Destination, instr.Source)) case *CompareAndSwap: c.append(arm.CompareAndSwap(instr.OldValue, instr.NewValue, instr.Address, instr.Length)) case *CompareNumber: code, _ := arm.CompareRegisterNumber(instr.Destination, instr.Number) c.append(code) case *ConditionalSet: switch instr.Condition { case Equal: c.append(arm.SetIfEqual(instr.Destination)) case NotEqual: c.append(arm.SetIfNotEqual(instr.Destination)) case Greater: c.append(arm.SetIfGreater(instr.Destination)) case GreaterEqual: c.append(arm.SetIfGreaterEqual(instr.Destination)) case Less: c.append(arm.SetIfLess(instr.Destination)) case LessEqual: c.append(arm.SetIfLessEqual(instr.Destination)) case UnsignedGreater: c.append(arm.SetIfUnsignedGreater(instr.Destination)) case UnsignedGreaterEqual: c.append(arm.SetIfUnsignedGreaterEqual(instr.Destination)) case UnsignedLess: c.append(arm.SetIfUnsignedLess(instr.Destination)) case UnsignedLessEqual: c.append(arm.SetIfUnsignedLessEqual(instr.Destination)) default: panic("unknown condition") } case *Divide: c.append(arm.DivUnsignedRegisterRegister(instr.Destination, instr.Source, instr.Operand)) case *DivideSigned: c.append(arm.DivSignedRegisterRegister(instr.Destination, instr.Source, instr.Operand)) case *Jump: jump, _ := arm.Jump(0) c.append(jump) patch := c.PatchLast4Bytes()
patch.apply = func(code []byte) []byte { address, exists := c.labels[instr.Label]
if !exists { panic("unknown label: " + instr.Label) }
offset := address - patch.start
if offset%4 != 0 { panic("jump offset is not a multiple of 4") }
offset /= 4
var ( jump uint32 encodable bool )
switch instr.Condition { case Equal: jump, encodable = arm.JumpIfEqual(offset) case NotEqual: jump, encodable = arm.JumpIfNotEqual(offset) case Greater: jump, encodable = arm.JumpIfGreater(offset) case GreaterEqual: jump, encodable = arm.JumpIfGreaterEqual(offset) case Less: jump, encodable = arm.JumpIfLess(offset) case LessEqual: jump, encodable = arm.JumpIfLessEqual(offset) case UnsignedGreater: jump, encodable = arm.JumpIfUnsignedGreater(offset) case UnsignedGreaterEqual: jump, encodable = arm.JumpIfUnsignedGreaterEqual(offset) case UnsignedLess: jump, encodable = arm.JumpIfUnsignedLess(offset) case UnsignedLessEqual: jump, encodable = arm.JumpIfUnsignedLessEqual(offset) default: jump, encodable = arm.Jump(offset) }
if !encodable { panic("jump offset outside of encodable range") }
binary.LittleEndian.PutUint32(code, jump) return code } case *Label: c.labels[instr.Name] = len(c.code) case *Load: scale := arm.Scale1
if instr.Scale { scale = arm.ScaleLength }
if instr.Signed && instr.Length <= 4 { c.append(arm.LoadSigned(instr.Destination, instr.Base, instr.Index, scale, instr.Length)) } else { c.append(arm.Load(instr.Destination, instr.Base, instr.Index, scale, instr.Length)) } case *LoadFixedOffset: if instr.Signed && instr.Length <= 4 { if instr.Scale { c.append(arm.LoadFixedOffsetScaledSigned(instr.Destination, instr.Base, arm.UnscaledImmediate, uint(instr.Index), instr.Length)) } else { c.append(arm.LoadFixedOffsetSigned(instr.Destination, instr.Base, arm.UnscaledImmediate, instr.Index, instr.Length)) } } else { if instr.Scale { c.append(arm.LoadFixedOffsetScaled(instr.Destination, instr.Base, arm.UnscaledImmediate, uint(instr.Index), instr.Length)) } else { c.append(arm.LoadFixedOffset(instr.Destination, instr.Base, arm.UnscaledImmediate, instr.Index, instr.Length)) } } case *Modulo: if instr.Destination == instr.Source || instr.Destination == instr.Operand { panic("modulo destination register cannot be equal to the source or operand register") }
c.append(arm.DivUnsignedRegisterRegister(instr.Destination, instr.Source, instr.Operand)) c.append(arm.MultiplySubtract(instr.Destination, instr.Destination, instr.Operand, instr.Source)) case *ModuloSigned: if instr.Destination == instr.Source || instr.Destination == instr.Operand { panic("modulo destination register cannot be equal to the source or operand register") }
c.append(arm.DivSignedRegisterRegister(instr.Destination, instr.Source, instr.Operand)) c.append(arm.MultiplySubtract(instr.Destination, instr.Destination, instr.Operand, instr.Source)) case *MoveLabel: encoding, _ := arm.LoadAddress(instr.Destination, 0) c.append(encoding) patch := c.PatchLast4Bytes()
patch.apply = func(code []byte) []byte { address, exists := c.labels[instr.Label]
if !exists { panic("unknown label: " + instr.Label) }
offset := address - patch.start encoding, encodable := arm.LoadAddress(instr.Destination, offset)
if !encodable { panic("label offset outside of encodable range") }
binary.LittleEndian.PutUint32(code, encoding) return code } case *Move: c.append(arm.MoveRegisterRegister(instr.Destination, instr.Source)) case *MoveNumber: c.code = arm.MoveRegisterNumber(c.code, instr.Destination, instr.Number) case *Multiply: c.append(arm.MulRegisterRegister(instr.Destination, instr.Source, instr.Operand)) case *Negate: c.append(arm.NegateRegister(instr.Destination, instr.Source)) case *Or: c.append(arm.OrRegisterRegister(instr.Destination, instr.Source, instr.Operand)) case *OrNumber: code, _ := arm.OrRegisterNumber(instr.Destination, instr.Source, instr.Number) c.append(code) case *Pop: registers := instr.Registers count := len(registers)
if count&1 != 0 { count-- c.append(arm.LoadFixedOffset(registers[count], arm.SP, arm.PostIndex, 16, 8)) }
for i := count - 2; i >= 0; i -= 2 { c.append(arm.LoadPair(registers[i], registers[i+1], arm.SP, 16)) } case *Push: 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.StoreFixedOffsetRegister(registers[i], arm.SP, arm.PreIndex, -16, 8)) } } case *ReadSystemRegister: switch instr.SystemRegister { case arm.TPIDR_EL0: c.append(arm.LoadTLS(instr.Destination)) default: panic("unknown system register") } case *Return: c.append(arm.Return()) case *ShiftLeft: c.append(arm.ShiftLeft(instr.Destination, instr.Source, instr.Operand)) case *ShiftLeftNumber: c.append(arm.ShiftLeftNumber(instr.Destination, instr.Source, instr.Number)) case *ShiftRight: c.append(arm.ShiftRight(instr.Destination, instr.Source, instr.Operand)) case *ShiftRightNumber: c.append(arm.ShiftRightNumber(instr.Destination, instr.Source, instr.Number)) case *ShiftRightSigned: c.append(arm.ShiftRightSigned(instr.Destination, instr.Source, instr.Operand)) case *ShiftRightSignedNumber: c.append(arm.ShiftRightSignedNumber(instr.Destination, instr.Source, instr.Number)) case *Store: scale := arm.Scale1
if instr.Scale { scale = arm.ScaleLength }
c.append(arm.StoreRegister(instr.Source, instr.Base, instr.Index, scale, instr.Length)) case *StoreFixedOffset: if instr.Scale { c.append(arm.StoreFixedOffsetRegisterScaled(instr.Source, instr.Base, arm.UnscaledImmediate, uint(instr.Index), instr.Length)) } else { c.append(arm.StoreFixedOffsetRegister(instr.Source, instr.Base, arm.UnscaledImmediate, instr.Index, instr.Length)) } case *StoreFixedOffsetNumber: panic("arm64 does not support memory stores of immediates") case *StoreNumber: panic("arm64 does not support memory stores of immediates") case *Subtract: c.append(arm.SubRegisterRegister(instr.Destination, instr.Source, instr.Operand)) case *SubtractNumber: code, _ := arm.SubRegisterNumber(instr.Destination, instr.Source, instr.Number) c.append(code) case *StackFrameStart: c.append(arm.StoreFixedOffsetRegister(arm.LR, arm.SP, arm.PreIndex, -16, 8)) case *StackFrameEnd: c.append(arm.LoadFixedOffset(arm.LR, arm.SP, arm.PostIndex, 16, 8)) case *Syscall: switch c.build.OS { case config.Mac: c.append(arm.Syscall(0x80)) default: c.append(arm.Syscall(0)) } case *WriteSystemRegister: switch instr.SystemRegister { case arm.TPIDR_EL0: c.append(arm.StoreTLS(instr.Source)) default: panic("unknown system register") } case *Xor: c.append(arm.XorRegisterRegister(instr.Destination, instr.Source, instr.Operand)) case *XorNumber: code, _ := arm.XorRegisterNumber(instr.Destination, instr.Source, instr.Number) c.append(code) default: panic("unknown instruction") }}