use crate::data_structures::RegVecs;
use crate::*;
use std::borrow::Cow;
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
enum IRInstKind {
Spill { vreg: Option<VirtualReg> },
Reload { vreg: Option<VirtualReg> },
Move { vreg: VirtualReg },
ZeroLenNop,
UserReturn,
UserMove,
UserOther,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct IRInst {
reg_uses: Vec<Reg>,
reg_mods: Vec<Writable<Reg>>,
reg_defs: Vec<Writable<Reg>>,
kind: IRInstKind,
}
#[derive(Clone)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct IRFunction {
instructions: Vec<IRInst>,
block_ranges: Vec<Range<InstIx>>,
block_succs: Vec<Vec<BlockIx>>,
entry_block: BlockIx,
liveins: Set<RealReg>,
liveouts: Set<RealReg>,
vreg_spill_slot_sizes: Vec<Option<(u32, RegClass)>>,
num_vregs: usize,
}
#[derive(Clone)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct IRSnapshot {
reg_universe: RealRegUniverse,
func: IRFunction,
}
impl IRSnapshot {
fn new_inst<F: Function>(func: &F, ix: InstIx, inst: &F::Inst) -> IRInst {
let mut reg_vecs = RegVecs::new( false);
let mut collector = RegUsageCollector::new(&mut reg_vecs);
F::get_regs(inst, &mut collector);
let kind = if let Some((_wreg, _reg)) = func.is_move(inst) {
IRInstKind::UserMove
} else if func.is_ret(ix) {
IRInstKind::UserReturn
} else {
IRInstKind::UserOther
};
IRInst {
reg_uses: reg_vecs.uses,
reg_mods: reg_vecs
.mods
.into_iter()
.map(|reg| Writable::from_reg(reg))
.collect(),
reg_defs: reg_vecs
.defs
.into_iter()
.map(|reg| Writable::from_reg(reg))
.collect(),
kind,
}
}
pub fn from_function<F: Function>(func: &F, reg_universe: &RealRegUniverse) -> Self {
let instructions: Vec<IRInst> = func
.insns()
.iter()
.enumerate()
.map(|(ix, inst)| IRSnapshot::new_inst(func, InstIx::new(ix as u32), inst))
.collect();
let mut block_ranges = Vec::new();
let mut block_succs = Vec::new();
for block in func.blocks() {
block_ranges.push(func.block_insns(block));
block_succs.push(func.block_succs(block).into());
}
let vreg_spill_slot_sizes = {
let mut array: Vec<Option<(u32, RegClass)>> = Vec::new();
let mut handle_reg = |reg: &Reg| {
if let Some(vreg) = reg.as_virtual_reg() {
let rc = vreg.get_class();
let spill_slot_size = func.get_spillslot_size(rc, vreg);
let index = vreg.get_index();
if index >= array.len() {
array.resize(index + 1, None);
}
let entry = &mut array[vreg.get_index()];
match entry {
None => *entry = Some((spill_slot_size, rc)),
Some((prev_size, prev_rc)) => {
assert_eq!(*prev_rc, rc);
assert_eq!(*prev_size, spill_slot_size);
}
}
}
};
for inst in &instructions {
for reg in &inst.reg_uses {
handle_reg(reg);
}
for reg in &inst.reg_mods {
handle_reg(®.to_reg());
}
for reg in &inst.reg_defs {
handle_reg(®.to_reg());
}
}
array
};
let entry_block = func.entry_block();
let liveins = func.func_liveins();
let liveouts = func.func_liveouts();
Self {
reg_universe: reg_universe.clone(),
func: IRFunction {
instructions,
block_ranges,
block_succs,
entry_block,
liveins,
liveouts,
vreg_spill_slot_sizes,
num_vregs: func.get_num_vregs(),
},
}
}
pub fn allocate(&mut self, opts: Options) -> Result<RegAllocResult<IRFunction>, RegAllocError> {
allocate_registers_with_opts(
&mut self.func,
&self.reg_universe,
None,
opts,
)
}
}
impl Function for IRFunction {
type Inst = IRInst;
fn func_liveins(&self) -> Set<RealReg> {
self.liveins.clone()
}
fn func_liveouts(&self) -> Set<RealReg> {
self.liveouts.clone()
}
fn get_num_vregs(&self) -> usize {
self.num_vregs
}
fn insns(&self) -> &[Self::Inst] {
&self.instructions
}
fn insns_mut(&mut self) -> &mut [Self::Inst] {
&mut self.instructions
}
fn get_insn(&self, insn: InstIx) -> &Self::Inst {
&self.instructions[insn.get() as usize]
}
fn get_insn_mut(&mut self, insn: InstIx) -> &mut Self::Inst {
&mut self.instructions[insn.get() as usize]
}
fn is_ret(&self, insn: InstIx) -> bool {
let inst = &self.instructions[insn.get() as usize];
if let IRInstKind::UserReturn = inst.kind {
true
} else {
false
}
}
fn is_move(&self, insn: &Self::Inst) -> Option<(Writable<Reg>, Reg)> {
if let IRInstKind::UserMove = insn.kind {
let from = insn.reg_uses[0];
let to = insn.reg_defs[0];
Some((to, from))
} else {
None
}
}
fn blocks(&self) -> Range<BlockIx> {
Range::new(BlockIx::new(0), self.block_ranges.len())
}
fn entry_block(&self) -> BlockIx {
self.entry_block
}
fn block_insns(&self, block: BlockIx) -> Range<InstIx> {
self.block_ranges[block.get() as usize]
}
fn block_succs(&self, block: BlockIx) -> Cow<[BlockIx]> {
Cow::Borrowed(&self.block_succs[block.get() as usize])
}
fn get_regs(insn: &Self::Inst, collector: &mut RegUsageCollector) {
collector.add_uses(&insn.reg_uses);
collector.add_mods(&insn.reg_mods);
collector.add_defs(&insn.reg_defs);
}
fn map_regs<RUM: RegUsageMapper>(insn: &mut Self::Inst, maps: &RUM) {
for reg_use in insn.reg_uses.iter_mut() {
if let Some(vreg) = reg_use.as_virtual_reg() {
*reg_use = maps.get_use(vreg).expect("missing alloc for use").to_reg();
}
}
for reg_mod in insn.reg_mods.iter_mut() {
if let Some(vreg) = reg_mod.to_reg().as_virtual_reg() {
*reg_mod =
Writable::from_reg(maps.get_mod(vreg).expect("missing alloc for mod").to_reg());
}
}
for reg_def in insn.reg_defs.iter_mut() {
if let Some(vreg) = reg_def.to_reg().as_virtual_reg() {
*reg_def =
Writable::from_reg(maps.get_def(vreg).expect("missing alloc for def").to_reg());
}
}
}
fn gen_spill(
&self,
_to_slot: SpillSlot,
from_reg: RealReg,
for_vreg: Option<VirtualReg>,
) -> Self::Inst {
IRInst {
reg_uses: vec![from_reg.to_reg()],
reg_mods: vec![],
reg_defs: vec![],
kind: IRInstKind::Spill { vreg: for_vreg },
}
}
fn gen_reload(
&self,
to_reg: Writable<RealReg>,
_from_slot: SpillSlot,
for_vreg: Option<VirtualReg>,
) -> Self::Inst {
IRInst {
reg_uses: vec![],
reg_mods: vec![],
reg_defs: vec![Writable::from_reg(to_reg.to_reg().to_reg())],
kind: IRInstKind::Reload { vreg: for_vreg },
}
}
fn gen_move(
&self,
to_reg: Writable<RealReg>,
from_reg: RealReg,
for_vreg: VirtualReg,
) -> Self::Inst {
IRInst {
reg_uses: vec![from_reg.to_reg()],
reg_mods: vec![],
reg_defs: vec![Writable::from_reg(to_reg.to_reg().to_reg())],
kind: IRInstKind::Move { vreg: for_vreg },
}
}
fn gen_zero_len_nop(&self) -> Self::Inst {
IRInst {
reg_uses: vec![],
reg_mods: vec![],
reg_defs: vec![],
kind: IRInstKind::ZeroLenNop,
}
}
fn get_spillslot_size(&self, regclass: RegClass, for_vreg: VirtualReg) -> u32 {
let entry =
self.vreg_spill_slot_sizes[for_vreg.get_index()].expect("missing spillslot info");
assert_eq!(entry.1, regclass);
return entry.0;
}
fn maybe_direct_reload(
&self,
_insn: &Self::Inst,
_reg: VirtualReg,
_slot: SpillSlot,
) -> Option<Self::Inst> {
unimplemented!();
}
}