use crate::{
codegen::CodeGenContext,
frame::Frame,
isa::reg::Reg,
masm::{MacroAssembler, OperandSize, RegImm},
regset::RegSet,
stack::Val,
};
pub(crate) struct RegAlloc {
pub scratch: Reg,
regset: RegSet,
}
impl RegAlloc {
pub fn new(regset: RegSet, scratch: Reg) -> Self {
Self { regset, scratch }
}
pub fn pop_to_reg<M: MacroAssembler>(
&mut self,
context: &mut CodeGenContext<M>,
size: OperandSize,
) -> Reg {
if let Some(reg) = context.stack.pop_reg() {
return reg;
}
let dst = self.any_gpr(context);
let val = context.stack.pop().expect("a value at stack top");
Self::move_val_to_reg(val, dst, &mut context.masm, context.frame, size);
dst
}
pub fn pop_to_named_reg<M: MacroAssembler>(
&mut self,
context: &mut CodeGenContext<M>,
named: Reg,
size: OperandSize,
) -> Reg {
if let Some(reg) = context.stack.pop_named_reg(named) {
return reg;
}
let dst = self.gpr(context, named);
let val = context.stack.pop().expect("a value at stack top");
Self::move_val_to_reg(val, dst, &mut context.masm, context.frame, size);
dst
}
fn move_val_to_reg<M: MacroAssembler>(
src: Val,
dst: Reg,
masm: &mut M,
frame: &Frame,
size: OperandSize,
) {
match src {
Val::Reg(src) => masm.mov(RegImm::reg(src), RegImm::reg(dst), size),
Val::I32(imm) => masm.mov(RegImm::imm(imm), RegImm::reg(dst), size),
Val::Local(index) => {
let slot = frame
.get_local(index)
.expect(&format!("valid locat at index = {}", index));
let addr = masm.local_address(&slot);
masm.load(addr, dst, slot.ty.into());
}
v => panic!("Unsupported value {:?}", v),
};
}
pub fn any_gpr<M: MacroAssembler>(&mut self, context: &mut CodeGenContext<M>) -> Reg {
self.regset.any_gpr().unwrap_or_else(|| {
self.spill(context);
self.regset.any_gpr().expect("any gpr to be available")
})
}
pub fn gpr<M: MacroAssembler>(&mut self, context: &mut CodeGenContext<M>, named: Reg) -> Reg {
self.regset.gpr(named).unwrap_or_else(|| {
self.spill(context);
self.regset
.gpr(named)
.expect(&format!("gpr {:?} to be available", named))
})
}
pub fn free_gpr(&mut self, reg: Reg) {
self.regset.free_gpr(reg);
}
fn spill<M: MacroAssembler>(&mut self, context: &mut CodeGenContext<M>) {
context.stack.inner_mut().iter_mut().for_each(|v| match v {
Val::Reg(r) => {
let offset = context.masm.push(*r);
self.free_gpr(*r);
*v = Val::Memory(offset);
}
Val::Local(index) => {
let slot = context
.frame
.get_local(*index)
.expect("valid local at slot");
let addr = context.masm.local_address(&slot);
context
.masm
.store(RegImm::reg(self.scratch), addr, slot.ty.into());
let offset = context.masm.push(self.scratch);
*v = Val::Memory(offset);
}
_ => {}
});
}
}