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.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178package asm
import ( "maps"
"git.urbach.dev/cli/q/src/config" "git.urbach.dev/cli/q/src/data" "git.urbach.dev/cli/q/src/dll")
// Assembler contains a list of instructions.type Assembler struct { Data data.Data Instructions []Instruction Libraries dll.List}
// Append adds another instruction.func (a *Assembler) Append(instr Instruction) { if a.Skip(instr) { return }
a.Instructions = append(a.Instructions, instr)}
// Last returns the last instruction.func (a *Assembler) Last() Instruction { return a.Instructions[len(a.Instructions)-1]}
// Compile compiles the instructions to machine code.func (a *Assembler) Compile(build *config.Build) (code []byte, data []byte, libs dll.List, labels map[string]int) { data, dataLabels := a.Data.Finalize()
c := compiler{ patcher: patcher{ code: make([]byte, 0, len(a.Instructions)*8), earlyPatches: make([]*patch, 0, len(a.Instructions)/8), latePatches: make([]*patch, 0, len(a.Instructions)/8), labels: make(map[string]int, 32), }, build: build, data: data, dataLabels: dataLabels, libraries: a.Libraries, }
switch build.Arch { case config.ARM: armc := compilerARM{compiler: &c}
for _, instr := range a.Instructions { armc.Compile(instr) } case config.X86: x86c := compilerX86{compiler: &c}
for _, instr := range a.Instructions { x86c.Compile(instr) } }
c.ApplyPatches(c.earlyPatches) c.AddDataLabels() c.ApplyPatches(c.latePatches) return c.code, c.data, c.libraries, c.labels}
// Merge combines the contents of this assembler with another one.func (a *Assembler) Merge(b *Assembler) { skip := 0
for a.Skip(b.Instructions[skip]) { skip++ }
a.Instructions = append(a.Instructions, b.Instructions[skip:]...)
if a.Data.Immutable == nil { a.Data.Immutable = b.Data.Immutable } else { maps.Copy(a.Data.Immutable, b.Data.Immutable) }
if a.Data.Mutable == nil { a.Data.Mutable = b.Data.Mutable } else { maps.Copy(a.Data.Mutable, b.Data.Mutable) }
for library := range b.Libraries.All() { for _, fn := range library.Functions { a.Libraries.Append(library.Name, fn) } }}
// SetLast sets the last instruction.func (a *Assembler) SetLast(instr Instruction) { a.Instructions[len(a.Instructions)-1] = instr}
// Skip returns true if appending the instruction can be skipped.func (a *Assembler) Skip(instr Instruction) bool { if len(a.Instructions) == 0 { return false }
// Call to os.exit + anything is skipped if it's not a label call, isCall := a.Last().(*Call)
if isCall && call.Label == "run.exit" { switch instr.(type) { case *Label: default: return true } }
switch instr := instr.(type) { case *Label: // Jump + Label can be replaced by just the Label if both addresses are equal jump, isJump := a.Last().(*Jump)
if isJump && jump.Label == instr.Name { instr.Align = 0 a.SetLast(instr) return true } case *Move: // A move following a move with inverted operands is unnecessary i := len(a.Instructions) - 1
for i >= 0 { previous := a.Instructions[i] move, isMove := previous.(*Move)
if !isMove { return false }
if instr.Destination == move.Source { if instr.Source == move.Destination { return true } else { return false } }
i-- } case *Return: // Call + Return can be replaced by a single Jump call, isCall := a.Last().(*Call)
if isCall { a.SetLast(&Jump{Label: call.Label}) return true }
// Jump + Return is unnecessary jump, isJump := a.Last().(*Jump)
if isJump && jump.Condition == None { return true }
// Return + Return is unnecessary _, isReturn := a.Last().(*Return)
if isReturn { return true } }
return false}