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.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138package codegen
import ( "git.urbach.dev/cli/q/src/asm" "git.urbach.dev/cli/q/src/cpu" "git.urbach.dev/cli/q/src/types")
// findTempRegister finds a temporary physical register that is not in use.func (f *Function) findTempRegister(liveSteps []*Step, avoid ...cpu.Register) cpu.Register { usedRegisters := bitSet(0)
for _, live := range liveSteps { if live.Register == -1 { continue }
usedRegisters.Set(live.Register) }
for _, reg := range avoid { if reg == -1 { continue }
usedRegisters.Set(reg) }
for _, reg := range f.CPU.General { if !usedRegisters.Has(reg) { return reg } }
panic("no free registers for temporary")}
// freeTempRegisters returns the general purpose registers that are not in use by the given steps.func (f *Function) freeTempRegisters(steps []*Step) []cpu.Register { usedRegisters := bitSet(0)
for _, live := range steps { if live.Register == -1 { continue }
usedRegisters.Set(live.Register) }
var free []cpu.Register
for _, reg := range f.CPU.General { if !usedRegisters.Has(reg) { free = append(free, reg) } }
return free}
// isSpilled returns true if it's a virtual register.func (f *Function) isSpilled(reg cpu.Register) bool { return reg >= f.CPU.MaxRegisters}
// loadSpill loads a value from a virtual register into a physical register.func (f *Function) loadSpill(step *Step, destination cpu.Register) { f.Assembler.Append(&asm.LoadFixedOffset{ Index: f.spillOffset(step.Register), Base: f.CPU.StackPointer, Destination: destination, Length: 8, Scale: false, Signed: types.IsSigned(step.Value.Type()), })}
// resolveOperand returns the register to use for an operand.// If the operand is spilled, it loads it from the stack first.func (f *Function) resolveOperand(step *Step, liveSteps []*Step, avoid ...cpu.Register) cpu.Register { if !f.isSpilled(step.Register) { return step.Register }
tmp := f.findTempRegister(liveSteps, avoid...) f.loadSpill(step, tmp) return tmp}
// spillOffset returns the stack offset in bytes for a virtual register.func (f *Function) spillOffset(reg cpu.Register) int { return int(reg-f.CPU.MaxRegisters) * 8}
// storeSpill stores a value from a physical register to the spilled stack slot.func (f *Function) storeSpill(step *Step, source cpu.Register) { f.Assembler.Append(&asm.StoreFixedOffset{ Index: f.spillOffset(step.Register), Base: f.CPU.StackPointer, Source: source, Length: 8, Scale: false, })}
// storeSpillNumber stores a number on the stack.func (f *Function) storeSpillNumber(step *Step, typ types.Type, number int) { if typ == types.AnyInt { typ = types.Int }
if f.arch.canStoreNumber(typ, number) { f.Assembler.Append(&asm.StoreFixedOffsetNumber{ Index: f.spillOffset(step.Register), Base: f.CPU.StackPointer, Number: int32(number), Length: byte(typ.Size()), Scale: false, })
return }
tmp := f.findTempRegister(step.Live)
f.Assembler.Append(&asm.MoveNumber{ Destination: tmp, Number: number, })
f.Assembler.Append(&asm.StoreFixedOffset{ Index: f.spillOffset(step.Register), Base: f.CPU.StackPointer, Source: tmp, Length: byte(typ.Size()), Scale: false, })}