package 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, }) }