use crate::checker::Inst as CheckerInst;
use crate::checker::{CheckerContext, CheckerErrors};
use crate::data_structures::{
BlockIx, InstIx, InstPoint, Point, RangeFrag, RealReg, RealRegUniverse, Reg, SpillSlot,
TypedIxVec, VirtualReg, Writable,
};
use crate::{reg_maps::VrangeRegUsageMapper, Function, RegAllocError};
use log::trace;
use std::result::Result;
#[derive(Clone, Debug)]
pub(crate) enum InstToInsert {
Spill {
to_slot: SpillSlot,
from_reg: RealReg,
for_vreg: Option<VirtualReg>,
},
Reload {
to_reg: Writable<RealReg>,
from_slot: SpillSlot,
for_vreg: Option<VirtualReg>,
},
Move {
to_reg: Writable<RealReg>,
from_reg: RealReg,
for_vreg: VirtualReg,
},
ChangeSpillSlotOwnership {
inst_ix: InstIx,
slot: SpillSlot,
from_reg: Reg,
to_reg: Reg,
},
}
impl InstToInsert {
pub(crate) fn construct<F: Function>(&self, f: &F) -> Option<F::Inst> {
match self {
&InstToInsert::Spill {
to_slot,
from_reg,
for_vreg,
} => Some(f.gen_spill(to_slot, from_reg, for_vreg)),
&InstToInsert::Reload {
to_reg,
from_slot,
for_vreg,
} => Some(f.gen_reload(to_reg, from_slot, for_vreg)),
&InstToInsert::Move {
to_reg,
from_reg,
for_vreg,
} => Some(f.gen_move(to_reg, from_reg, for_vreg)),
&InstToInsert::ChangeSpillSlotOwnership { .. } => None,
}
}
pub(crate) fn to_checker_inst(&self) -> CheckerInst {
match self {
&InstToInsert::Spill {
to_slot, from_reg, ..
} => CheckerInst::Spill {
into: to_slot,
from: from_reg,
},
&InstToInsert::Reload {
to_reg, from_slot, ..
} => CheckerInst::Reload {
into: to_reg,
from: from_slot,
},
&InstToInsert::Move {
to_reg, from_reg, ..
} => CheckerInst::Move {
into: to_reg,
from: from_reg,
},
&InstToInsert::ChangeSpillSlotOwnership {
inst_ix,
slot,
from_reg,
to_reg,
} => CheckerInst::ChangeSpillSlotOwnership {
inst_ix,
slot,
from_reg,
to_reg,
},
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ExtPoint {
Reload = 0,
SpillBefore = 1,
Use = 2,
Def = 3,
ReloadAfter = 4,
Spill = 5,
}
impl ExtPoint {
#[inline(always)]
pub fn from_point(pt: Point) -> Self {
match pt {
Point::Reload => ExtPoint::Reload,
Point::Use => ExtPoint::Use,
Point::Def => ExtPoint::Def,
Point::Spill => ExtPoint::Spill,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct InstExtPoint {
pub iix: InstIx,
pub extpt: ExtPoint,
}
impl InstExtPoint {
#[inline(always)]
pub fn new(iix: InstIx, extpt: ExtPoint) -> Self {
Self { iix, extpt }
}
#[inline(always)]
pub fn from_inst_point(inst_pt: InstPoint) -> Self {
InstExtPoint {
iix: inst_pt.iix(),
extpt: ExtPoint::from_point(inst_pt.pt()),
}
}
}
#[derive(Debug)]
pub(crate) struct InstToInsertAndExtPoint {
pub(crate) inst: InstToInsert,
pub(crate) iep: InstExtPoint,
}
impl InstToInsertAndExtPoint {
#[inline(always)]
pub(crate) fn new(inst: InstToInsert, iep: InstExtPoint) -> Self {
Self { inst, iep }
}
}
#[inline(never)]
fn map_vregs_to_rregs<F: Function>(
func: &mut F,
frag_map: Vec<(RangeFrag, VirtualReg, RealReg)>,
insts_to_add: &Vec<InstToInsertAndExtPoint>,
iixs_to_nop_out: &Vec<InstIx>,
reg_universe: &RealRegUniverse,
use_checker: bool,
safepoint_insns: &[InstIx],
stackmaps: &[Vec<SpillSlot>],
reftyped_vregs: &[VirtualReg],
) -> Result<(), CheckerErrors> {
let mut checker: Option<CheckerContext> = None;
let mut insn_blocks: Vec<BlockIx> = vec![];
if use_checker {
checker = Some(CheckerContext::new(
func,
reg_universe,
insts_to_add,
safepoint_insns,
stackmaps,
reftyped_vregs,
));
insn_blocks.resize(func.insns().len(), BlockIx::new(0));
for block_ix in func.blocks() {
for insn_ix in func.block_insns(block_ix) {
insn_blocks[insn_ix.get() as usize] = block_ix;
}
}
}
let mut iixs_to_nop_out = iixs_to_nop_out.clone();
iixs_to_nop_out.sort();
let mut frag_maps_by_start = frag_map.clone();
let mut frag_maps_by_end = frag_map;
frag_maps_by_start.sort_unstable_by(|(frag, _, _), (other_frag, _, _)| {
frag.first
.iix()
.partial_cmp(&other_frag.first.iix())
.unwrap()
});
frag_maps_by_end.sort_unstable_by(|(frag, _, _), (other_frag, _, _)| {
frag.last.iix().partial_cmp(&other_frag.last.iix()).unwrap()
});
let mut cursor_starts = 0;
let mut cursor_ends = 0;
let mut cursor_nop = 0;
let mut mapper = VrangeRegUsageMapper::new(func.get_num_vregs());
fn is_sane(frag: &RangeFrag) -> bool {
if frag.first.pt().is_use_or_def()
&& frag.last.pt().is_use_or_def()
&& frag.first.iix() <= frag.last.iix()
{
return true;
}
if frag.first.pt().is_reload()
&& frag.last.pt().is_use()
&& frag.last.iix() == frag.first.iix()
{
return true;
}
if frag.first.pt().is_reload()
&& frag.last.pt().is_spill()
&& frag.last.iix() == frag.first.iix()
{
return true;
}
if frag.first.pt().is_def()
&& frag.last.pt().is_spill()
&& frag.last.iix() == frag.first.iix()
{
return true;
}
false
}
let mut last_insn_ix = -1;
for insn_ix in func.insn_indices() {
assert!(insn_ix.get() as i32 > last_insn_ix);
last_insn_ix = insn_ix.get() as i32;
while cursor_starts < frag_maps_by_start.len()
&& frag_maps_by_start[cursor_starts].0.first.iix() < insn_ix
{
cursor_starts += 1;
}
let mut num_starts = 0;
while cursor_starts + num_starts < frag_maps_by_start.len()
&& frag_maps_by_start[cursor_starts + num_starts].0.first.iix() == insn_ix
{
num_starts += 1;
}
while cursor_ends < frag_maps_by_end.len()
&& frag_maps_by_end[cursor_ends].0.last.iix() < insn_ix
{
cursor_ends += 1;
}
let mut num_ends = 0;
while cursor_ends + num_ends < frag_maps_by_end.len()
&& frag_maps_by_end[cursor_ends + num_ends].0.last.iix() == insn_ix
{
num_ends += 1;
}
while cursor_nop < iixs_to_nop_out.len() && iixs_to_nop_out[cursor_nop] < insn_ix {
cursor_nop += 1;
}
let nop_this_insn =
cursor_nop < iixs_to_nop_out.len() && iixs_to_nop_out[cursor_nop] == insn_ix;
for j in cursor_starts..cursor_starts + num_starts {
let frag = &frag_maps_by_start[j].0;
debug_assert!(frag.first.iix() == insn_ix);
debug_assert!(is_sane(&frag));
}
for j in cursor_ends..cursor_ends + num_ends {
let frag = &frag_maps_by_end[j].0;
debug_assert!(frag.last.iix() == insn_ix);
debug_assert!(is_sane(frag));
}
trace!("current mapper {:?}", mapper);
for j in cursor_starts..cursor_starts + num_starts {
let frag = &frag_maps_by_start[j].0;
if frag.first.pt().is_reload() {
mapper.set_direct(frag_maps_by_start[j].1, Some(frag_maps_by_start[j].2));
}
}
for j in cursor_starts..cursor_starts + num_starts {
let frag = &frag_maps_by_start[j].0;
if frag.first.pt().is_use() {
mapper.set_direct(frag_maps_by_start[j].1, Some(frag_maps_by_start[j].2));
}
}
for j in cursor_ends..cursor_ends + num_ends {
let frag = &frag_maps_by_end[j].0;
if frag.last.pt().is_use() {
mapper.set_overlay(frag_maps_by_end[j].1, None);
}
}
trace!("maps after I.u {:?}", mapper);
for j in cursor_starts..cursor_starts + num_starts {
let frag = &frag_maps_by_start[j].0;
if frag.first.pt().is_def() {
mapper.set_overlay(frag_maps_by_start[j].1, Some(frag_maps_by_start[j].2));
}
}
mapper.finish_overlay();
trace!("maps after I.d {:?}", mapper);
if let &mut Some(ref mut checker) = &mut checker {
let block_ix = insn_blocks[insn_ix.get() as usize];
checker
.handle_insn(reg_universe, func, block_ix, insn_ix, &mapper)
.unwrap();
}
if !nop_this_insn {
trace!("map_regs for {:?}", insn_ix);
let mut insn = func.get_insn_mut(insn_ix);
F::map_regs(&mut insn, &mapper);
trace!("mapped instruction: {:?}", insn);
} else {
trace!("nop'ing out {:?}", insn_ix);
let nop = func.gen_zero_len_nop();
let insn = func.get_insn_mut(insn_ix);
*insn = nop;
}
mapper.merge_overlay();
for j in cursor_ends..cursor_ends + num_ends {
let frag = &frag_maps_by_end[j].0;
if frag.last.pt().is_def() {
mapper.set_direct(frag_maps_by_end[j].1, None);
}
}
for j in cursor_ends..cursor_ends + num_ends {
let frag = &frag_maps_by_end[j].0;
if frag.last.pt().is_spill() {
mapper.set_direct(frag_maps_by_end[j].1, None);
}
}
cursor_starts += num_starts;
cursor_ends += num_ends;
}
debug_assert!(mapper.is_empty());
if use_checker {
checker.unwrap().run()
} else {
Ok(())
}
}
#[inline(never)]
pub(crate) fn add_spills_reloads_and_moves<F: Function>(
func: &mut F,
safepoint_insns: &Vec<InstIx>,
mut insts_to_add: Vec<InstToInsertAndExtPoint>,
) -> Result<
(
Vec<F::Inst>,
TypedIxVec<BlockIx, InstIx>,
TypedIxVec<InstIx, InstIx>,
Vec<InstIx>,
),
String,
> {
insts_to_add.sort_by_key(|to_add| to_add.iep.clone());
let mut cur_inst_to_add = 0;
let mut cur_block = BlockIx::new(0);
let mut insns: Vec<F::Inst> = vec![];
let mut target_map: TypedIxVec<BlockIx, InstIx> = TypedIxVec::new();
let mut new_to_old_insn_map: TypedIxVec<InstIx, InstIx> = TypedIxVec::new();
target_map.reserve(func.blocks().len());
new_to_old_insn_map.reserve(func.insn_indices().len() + insts_to_add.len());
let mut next_safepoint_insn_index = 0;
let mut new_safepoint_insns = Vec::<InstIx>::new();
new_safepoint_insns.reserve(safepoint_insns.len());
for iix in func.insn_indices() {
debug_assert!(cur_block.get() < func.blocks().len() as u32);
if func.block_insns(cur_block).start() == iix {
assert!(cur_block.get() == target_map.len());
target_map.push(InstIx::new(insns.len() as u32));
}
while cur_inst_to_add < insts_to_add.len()
&& insts_to_add[cur_inst_to_add].iep <= InstExtPoint::new(iix, ExtPoint::SpillBefore)
{
if let Some(inst) = insts_to_add[cur_inst_to_add].inst.construct(func) {
insns.push(inst);
new_to_old_insn_map.push(InstIx::invalid_value());
}
cur_inst_to_add += 1;
}
if next_safepoint_insn_index < safepoint_insns.len()
&& iix == safepoint_insns[next_safepoint_insn_index]
{
new_safepoint_insns.push(InstIx::new(insns.len() as u32));
next_safepoint_insn_index += 1;
}
new_to_old_insn_map.push(iix);
insns.push(func.get_insn(iix).clone());
while cur_inst_to_add < insts_to_add.len()
&& insts_to_add[cur_inst_to_add].iep <= InstExtPoint::new(iix, ExtPoint::Spill)
{
if let Some(inst) = insts_to_add[cur_inst_to_add].inst.construct(func) {
insns.push(inst);
new_to_old_insn_map.push(InstIx::invalid_value());
}
cur_inst_to_add += 1;
}
if iix == func.block_insns(cur_block).last() {
debug_assert!(cur_block.get() < func.blocks().len() as u32);
cur_block = cur_block.plus(1);
}
}
debug_assert!(cur_inst_to_add == insts_to_add.len());
debug_assert!(cur_block.get() == func.blocks().len() as u32);
debug_assert!(next_safepoint_insn_index == safepoint_insns.len());
debug_assert!(new_safepoint_insns.len() == safepoint_insns.len());
Ok((insns, target_map, new_to_old_insn_map, new_safepoint_insns))
}
#[inline(never)]
pub(crate) fn edit_inst_stream<F: Function>(
func: &mut F,
safepoint_insns: &Vec<InstIx>,
insts_to_add: Vec<InstToInsertAndExtPoint>,
iixs_to_nop_out: &Vec<InstIx>,
frag_map: Vec<(RangeFrag, VirtualReg, RealReg)>,
reg_universe: &RealRegUniverse,
use_checker: bool,
stackmaps: &[Vec<SpillSlot>],
reftyped_vregs: &[VirtualReg],
) -> Result<
(
Vec<F::Inst>,
TypedIxVec<BlockIx, InstIx>,
TypedIxVec<InstIx, InstIx>,
Vec<InstIx>,
),
RegAllocError,
> {
map_vregs_to_rregs(
func,
frag_map,
&insts_to_add,
iixs_to_nop_out,
reg_universe,
use_checker,
&safepoint_insns[..],
stackmaps,
reftyped_vregs,
)
.map_err(|e| RegAllocError::RegChecker(e))?;
add_spills_reloads_and_moves(func, safepoint_insns, insts_to_add)
.map_err(|e| RegAllocError::Other(e))
}