use log::{debug, info, log_enabled, Level};
use smallvec::{smallvec, SmallVec};
use std::cmp::min;
use std::fmt;
use crate::analysis_control_flow::CFGInfo;
use crate::data_structures::{
BlockIx, InstIx, InstPoint, MoveInfo, MoveInfoElem, Point, Queue, RangeFrag, RangeFragIx,
RangeFragKind, RangeFragMetrics, RealRange, RealRangeIx, RealReg, RealRegUniverse, Reg,
RegClass, RegSets, RegToRangesMaps, RegUsageCollector, RegVecBounds, RegVecs, RegVecsAndBounds,
SortedRangeFragIxs, SortedRangeFrags, SpillCost, TypedIxVec, VirtualRange, VirtualRangeIx,
VirtualReg,
};
use crate::sparse_set::SparseSet;
use crate::union_find::{ToFromU32, UnionFind};
use crate::Function;
#[inline(never)]
fn remove_dups_from_group(regs: &mut Vec<Reg>, start: u32, len: &mut u8) {
regs[start as usize..start as usize + *len as usize].sort_unstable();
let mut wr = start as usize;
for rd in start as usize..start as usize + *len as usize {
let reg = regs[rd];
if rd == start as usize || regs[rd - 1] != reg {
if wr != rd {
regs[wr] = reg;
}
wr += 1;
}
}
let new_len_usize = wr - start as usize;
assert!(new_len_usize <= *len as usize);
*len = new_len_usize as u8;
}
#[inline(never)]
fn remove_mods_from_group(
group: &mut Vec<Reg>,
group_start: u32,
group_len: &mut u8,
mods: &Vec<Reg>,
mods_start: u32,
mods_len: u8,
) {
let mut wr = group_start as usize;
for rd in group_start as usize..group_start as usize + *group_len as usize {
let reg = group[rd];
let mut retain = true;
for i in mods_start as usize..mods_start as usize + mods_len as usize {
if reg == mods[i] {
retain = false;
break;
}
}
if retain {
if wr != rd {
group[wr] = reg;
}
wr += 1;
}
}
let new_group_len_usize = wr - group_start as usize;
assert!(new_group_len_usize <= *group_len as usize);
*group_len = new_group_len_usize as u8;
}
#[inline(never)]
pub fn add_raw_reg_vecs_for_insn<F: Function>(
inst: &F::Inst,
reg_vecs: &mut RegVecs,
bounds: &mut RegVecBounds,
) {
bounds.uses_start = reg_vecs.uses.len() as u32;
bounds.defs_start = reg_vecs.defs.len() as u32;
bounds.mods_start = reg_vecs.mods.len() as u32;
let mut collector = RegUsageCollector::new(reg_vecs);
F::get_regs(inst, &mut collector);
let uses_len = collector.reg_vecs.uses.len() as u32 - bounds.uses_start;
let defs_len = collector.reg_vecs.defs.len() as u32 - bounds.defs_start;
let mods_len = collector.reg_vecs.mods.len() as u32 - bounds.mods_start;
assert!((uses_len | defs_len | mods_len) < 256);
bounds.uses_len = uses_len as u8;
bounds.defs_len = defs_len as u8;
bounds.mods_len = mods_len as u8;
if bounds.uses_len > 0 {
remove_dups_from_group(
&mut collector.reg_vecs.uses,
bounds.uses_start,
&mut bounds.uses_len,
);
}
if bounds.defs_len > 0 {
remove_dups_from_group(
&mut collector.reg_vecs.defs,
bounds.defs_start,
&mut bounds.defs_len,
);
}
if bounds.mods_len > 0 {
remove_dups_from_group(
&mut collector.reg_vecs.mods,
bounds.mods_start,
&mut bounds.mods_len,
);
}
if bounds.mods_len > 0 {
if bounds.uses_len > 0 {
remove_mods_from_group(
&mut collector.reg_vecs.uses,
bounds.uses_start,
&mut bounds.uses_len,
&collector.reg_vecs.mods,
bounds.mods_start,
bounds.mods_len,
);
}
if bounds.defs_len > 0 {
remove_mods_from_group(
&mut collector.reg_vecs.defs,
bounds.defs_start,
&mut bounds.defs_len,
&collector.reg_vecs.mods,
bounds.mods_start,
bounds.mods_len,
);
}
}
}
#[inline(never)]
fn sanitize_should_retain_reg(
reg_universe: &RealRegUniverse,
reg: Reg,
reg_is_defd: bool,
) -> Result<bool, RealReg> {
if reg.is_virtual() {
return Ok(true);
}
let rreg_ix = reg.get_index();
if rreg_ix >= reg_universe.regs.len() {
return Err(reg.as_real_reg().unwrap());
}
if rreg_ix >= reg_universe.allocable {
return Ok(false);
}
for reg_info in ®_universe.allocable_by_class {
if let Some(reg_info) = reg_info {
if let Some(scratch_idx) = ®_info.suggested_scratch {
let scratch_reg = reg_universe.regs[*scratch_idx].0;
if reg.to_real_reg() == scratch_reg {
if !reg_is_defd {
return Err(reg.as_real_reg().unwrap());
}
}
}
}
}
Ok(true)
}
#[inline(never)]
fn sanitize_group(
reg_universe: &RealRegUniverse,
regs: &mut Vec<Reg>,
start: u32,
len: &mut u8,
is_def_group: bool,
) -> Result<(), RealReg> {
let mut wr = start as usize;
for rd in start as usize..start as usize + *len as usize {
let reg = regs[rd];
if sanitize_should_retain_reg(reg_universe, reg, is_def_group)? {
if wr != rd {
regs[wr] = reg;
}
wr += 1;
}
}
let new_len_usize = wr - start as usize;
assert!(new_len_usize <= *len as usize);
*len = new_len_usize as u8;
Ok(())
}
#[inline(never)]
fn add_san_reg_vecs_for_insn<F: Function>(
inst: &F::Inst,
reg_universe: &RealRegUniverse,
reg_vecs: &mut RegVecs,
bounds: &mut RegVecBounds,
) -> Result<(), RealReg> {
add_raw_reg_vecs_for_insn::<F>(inst, reg_vecs, bounds);
if bounds.uses_len > 0 {
sanitize_group(
®_universe,
&mut reg_vecs.uses,
bounds.uses_start,
&mut bounds.uses_len,
false,
)?;
}
if bounds.defs_len > 0 {
sanitize_group(
®_universe,
&mut reg_vecs.defs,
bounds.defs_start,
&mut bounds.defs_len,
true,
)?;
}
if bounds.mods_len > 0 {
sanitize_group(
®_universe,
&mut reg_vecs.mods,
bounds.mods_start,
&mut bounds.mods_len,
false,
)?;
}
Ok(())
}
#[inline(never)]
pub fn get_sanitized_reg_uses_for_func<F: Function>(
func: &F,
reg_universe: &RealRegUniverse,
) -> Result<RegVecsAndBounds, RealReg> {
let mut reg_vecs = RegVecs::new(false);
let mut bounds_vec = TypedIxVec::<InstIx, RegVecBounds>::new();
bounds_vec.reserve(func.insns().len());
for insn in func.insns() {
let mut bounds = RegVecBounds::new();
add_san_reg_vecs_for_insn::<F>(insn, ®_universe, &mut reg_vecs, &mut bounds)?;
bounds_vec.push(bounds);
}
assert!(!reg_vecs.is_sanitized());
reg_vecs.set_sanitized(true);
if log_enabled!(Level::Debug) {
let show_reg = |r: Reg| {
if r.is_real() {
reg_universe.regs[r.get_index()].1.clone()
} else {
format!("{:?}", r).to_string()
}
};
let show_regs = |r_vec: &[Reg]| {
let mut s = "".to_string();
for r in r_vec {
s = s + &show_reg(*r) + &" ".to_string();
}
s
};
for i in 0..bounds_vec.len() {
let iix = InstIx::new(i);
let s_use = show_regs(
®_vecs.uses[bounds_vec[iix].uses_start as usize
..bounds_vec[iix].uses_start as usize + bounds_vec[iix].uses_len as usize],
);
let s_mod = show_regs(
®_vecs.mods[bounds_vec[iix].mods_start as usize
..bounds_vec[iix].mods_start as usize + bounds_vec[iix].mods_len as usize],
);
let s_def = show_regs(
®_vecs.defs[bounds_vec[iix].defs_start as usize
..bounds_vec[iix].defs_start as usize + bounds_vec[iix].defs_len as usize],
);
debug!(
"{:?} SAN_RU: use {{ {}}} mod {{ {}}} def {{ {}}}",
iix, s_use, s_mod, s_def
);
}
}
Ok(RegVecsAndBounds::new(reg_vecs, bounds_vec))
}
#[inline(always)]
pub fn does_inst_use_def_or_mod_reg(
rvb: &RegVecsAndBounds,
iix: InstIx,
reg: Reg,
) -> (/*uses*/ bool, /*defs*/ bool, /*mods*/ bool) {
let bounds = &rvb.bounds[iix];
let vecs = &rvb.vecs;
let mut uses = false;
let mut defs = false;
let mut mods = false;
for i in bounds.uses_start as usize..bounds.uses_start as usize + bounds.uses_len as usize {
if vecs.uses[i] == reg {
uses = true;
break;
}
}
for i in bounds.defs_start as usize..bounds.defs_start as usize + bounds.defs_len as usize {
if vecs.defs[i] == reg {
defs = true;
break;
}
}
for i in bounds.mods_start as usize..bounds.mods_start as usize + bounds.mods_len as usize {
if vecs.mods[i] == reg {
mods = true;
break;
}
}
(uses, defs, mods)
}
#[allow(dead_code)]
#[inline(never)]
pub fn get_raw_reg_sets_for_insn<F: Function>(inst: &F::Inst) -> RegSets {
let mut reg_vecs = RegVecs::new(false);
let mut bounds = RegVecBounds::new();
add_raw_reg_vecs_for_insn::<F>(inst, &mut reg_vecs, &mut bounds);
let mut single_insn_bounds = TypedIxVec::<InstIx, RegVecBounds>::new();
single_insn_bounds.push(bounds);
assert!(!reg_vecs.is_sanitized());
let single_insn_rvb = RegVecsAndBounds::new(reg_vecs, single_insn_bounds);
single_insn_rvb.get_reg_sets_for_iix(InstIx::new(0))
}
#[inline(never)]
pub fn get_san_reg_sets_for_insn<F: Function>(
inst: &F::Inst,
reg_universe: &RealRegUniverse,
) -> Result<RegSets, RealReg> {
let mut reg_vecs = RegVecs::new(false);
let mut bounds = RegVecBounds::new();
add_san_reg_vecs_for_insn::<F>(inst, ®_universe, &mut reg_vecs, &mut bounds)?;
let mut single_insn_bounds = TypedIxVec::<InstIx, RegVecBounds>::new();
single_insn_bounds.push(bounds);
assert!(!reg_vecs.is_sanitized());
reg_vecs.set_sanitized(true);
let single_insn_rvb = RegVecsAndBounds::new(reg_vecs, single_insn_bounds);
Ok(single_insn_rvb.get_reg_sets_for_iix(InstIx::new(0)))
}
#[inline(never)]
pub fn calc_def_and_use<F: Function>(
func: &F,
rvb: &RegVecsAndBounds,
univ: &RealRegUniverse,
) -> (
TypedIxVec<BlockIx, SparseSet<Reg>>,
TypedIxVec<BlockIx, SparseSet<Reg>>,
) {
info!(" calc_def_and_use: begin");
assert!(rvb.is_sanitized());
let mut def_sets = TypedIxVec::new();
let mut use_sets = TypedIxVec::new();
for b in func.blocks() {
let mut def = SparseSet::empty();
let mut uce = SparseSet::empty();
for iix in func.block_insns(b) {
let bounds_for_iix = &rvb.bounds[iix];
for i in bounds_for_iix.uses_start as usize
..bounds_for_iix.uses_start as usize + bounds_for_iix.uses_len as usize
{
let u = rvb.vecs.uses[i];
if !def.contains(u) {
uce.insert(u);
}
}
for i in bounds_for_iix.mods_start as usize
..bounds_for_iix.mods_start as usize + bounds_for_iix.mods_len as usize
{
let m = rvb.vecs.mods[i];
if !def.contains(m) {
uce.insert(m);
}
}
for i in bounds_for_iix.defs_start as usize
..bounds_for_iix.defs_start as usize + bounds_for_iix.defs_len as usize
{
let d = rvb.vecs.defs[i];
def.insert(d);
}
for i in bounds_for_iix.mods_start as usize
..bounds_for_iix.mods_start as usize + bounds_for_iix.mods_len as usize
{
let m = rvb.vecs.mods[i];
def.insert(m);
}
}
def_sets.push(def);
use_sets.push(uce);
}
assert!(def_sets.len() == use_sets.len());
if log_enabled!(Level::Debug) {
let mut n = 0;
debug!("");
for (def_set, use_set) in def_sets.iter().zip(use_sets.iter()) {
let mut first = true;
let mut defs_str = "".to_string();
for def in def_set.to_vec() {
if !first {
defs_str = defs_str + &" ".to_string();
}
first = false;
defs_str = defs_str + &def.show_with_rru(univ);
}
first = true;
let mut uses_str = "".to_string();
for uce in use_set.to_vec() {
if !first {
uses_str = uses_str + &" ".to_string();
}
first = false;
uses_str = uses_str + &uce.show_with_rru(univ);
}
debug!(
"{:<3?} def {{{}}} use {{{}}}",
BlockIx::new(n),
defs_str,
uses_str
);
n += 1;
}
}
info!(" calc_def_and_use: end");
(def_sets, use_sets)
}
#[inline(never)]
pub fn calc_livein_and_liveout<F: Function>(
func: &F,
def_sets_per_block: &TypedIxVec<BlockIx, SparseSet<Reg>>,
use_sets_per_block: &TypedIxVec<BlockIx, SparseSet<Reg>>,
cfg_info: &CFGInfo,
univ: &RealRegUniverse,
) -> (
TypedIxVec<BlockIx, SparseSet<Reg>>,
TypedIxVec<BlockIx, SparseSet<Reg>>,
) {
info!(" calc_livein_and_liveout: begin");
let num_blocks = func.blocks().len() as u32;
let empty = SparseSet::<Reg>::empty();
let mut num_evals = 0;
let mut liveouts = TypedIxVec::<BlockIx, SparseSet<Reg>>::new();
liveouts.resize(num_blocks, empty.clone());
let mut work_queue = Queue::<BlockIx>::new();
for i in 0..num_blocks {
let block_ix = cfg_info.pre_ord[(num_blocks - 1 - i) as usize];
work_queue.push_back(block_ix);
}
let mut in_queue = Vec::<bool>::new();
in_queue.resize(num_blocks as usize, true);
while let Some(block_ix) = work_queue.pop_front() {
let i = block_ix.get() as usize;
assert!(in_queue[i]);
in_queue[i] = false;
let mut set = SparseSet::<Reg>::empty();
for block_j_ix in cfg_info.succ_map[block_ix].iter() {
let mut live_in_j = liveouts[*block_j_ix].clone();
live_in_j.remove(&def_sets_per_block[*block_j_ix]);
live_in_j.union(&use_sets_per_block[*block_j_ix]);
set.union(&live_in_j);
}
num_evals += 1;
if !set.equals(&liveouts[block_ix]) {
liveouts[block_ix] = set;
for block_j_ix in cfg_info.pred_map[block_ix].iter() {
let j = block_j_ix.get() as usize;
if !in_queue[j] {
work_queue.push_back(*block_j_ix);
in_queue[j] = true;
}
}
}
}
let mut liveins = TypedIxVec::<BlockIx, SparseSet<Reg>>::new();
liveins.resize(num_blocks, empty.clone());
for block_ix in BlockIx::new(0).dotdot(BlockIx::new(num_blocks)) {
let mut live_in = liveouts[block_ix].clone();
live_in.remove(&def_sets_per_block[block_ix]);
live_in.union(&use_sets_per_block[block_ix]);
liveins[block_ix] = live_in;
}
if false {
let mut sum_card_live_in = 0;
let mut sum_card_live_out = 0;
for bix in BlockIx::new(0).dotdot(BlockIx::new(num_blocks)) {
sum_card_live_in += liveins[bix].card();
sum_card_live_out += liveouts[bix].card();
}
println!(
"QQQQ calc_LI/LO: num_evals {}, tot LI {}, tot LO {}",
num_evals, sum_card_live_in, sum_card_live_out
);
}
let ratio: f32 = (num_evals as f32) / ((if num_blocks == 0 { 1 } else { num_blocks }) as f32);
info!(
" calc_livein_and_liveout: {} blocks, {} evals ({:<.2} per block)",
num_blocks, num_evals, ratio
);
if log_enabled!(Level::Debug) {
let mut n = 0;
debug!("");
for (livein, liveout) in liveins.iter().zip(liveouts.iter()) {
let mut first = true;
let mut li_str = "".to_string();
for li in livein.to_vec() {
if !first {
li_str = li_str + &" ".to_string();
}
first = false;
li_str = li_str + &li.show_with_rru(univ);
}
first = true;
let mut lo_str = "".to_string();
for lo in liveout.to_vec() {
if !first {
lo_str = lo_str + &" ".to_string();
}
first = false;
lo_str = lo_str + &lo.show_with_rru(univ);
}
debug!(
"{:<3?} livein {{{}}} liveout {{{}}}",
BlockIx::new(n),
li_str,
lo_str
);
n += 1;
}
}
info!(" calc_livein_and_liveout: end");
(liveins, liveouts)
}
#[derive(Clone)]
struct ProtoRangeFrag {
first: InstPoint,
last: InstPoint,
num_mentions: u16,
}
impl fmt::Debug for ProtoRangeFrag {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(
fmt,
"{:?}x {:?} - {:?}",
self.num_mentions, self.first, self.last
)
}
}
#[inline(always)]
pub(crate) fn reg_to_reg_ix(num_real_regs: u32, r: Reg) -> u32 {
if r.is_real() {
r.get_index_u32()
} else {
num_real_regs + r.get_index_u32()
}
}
#[inline(always)]
pub(crate) fn reg_ix_to_reg(
reg_universe: &RealRegUniverse,
vreg_classes: &Vec< RegClass>,
reg_ix: u32,
) -> Reg {
let reg_ix = reg_ix as usize;
let num_real_regs = reg_universe.regs.len();
if reg_ix < num_real_regs {
reg_universe.regs[reg_ix].0.to_reg()
} else {
let vreg_ix = reg_ix - num_real_regs;
Reg::new_virtual(vreg_classes[vreg_ix], vreg_ix as u32)
}
}
#[inline(always)]
fn emit_range_frag(
out_map: &mut Vec< SmallVec<[RangeFragIx; 8]>>,
out_frags: &mut TypedIxVec<RangeFragIx, RangeFrag>,
out_frag_metrics: &mut TypedIxVec<RangeFragIx, RangeFragMetrics>,
num_real_regs: u32,
reg: Reg,
frag: &RangeFrag,
frag_metrics: &RangeFragMetrics,
) {
debug_assert!(out_frags.len() == out_frag_metrics.len());
let mut new_fix = None;
let num_out_frags = out_frags.len();
if num_out_frags >= 2 {
let back_0 = RangeFragIx::new(num_out_frags - 1);
let back_1 = RangeFragIx::new(num_out_frags - 2);
if out_frags[back_0] == *frag && out_frag_metrics[back_0] == *frag_metrics {
new_fix = Some(back_0);
} else if out_frags[back_1] == *frag && out_frag_metrics[back_1] == *frag_metrics {
new_fix = Some(back_1);
}
}
let new_fix = match new_fix {
Some(fix) => fix,
None => {
out_frags.push(frag.clone());
out_frag_metrics.push(frag_metrics.clone());
RangeFragIx::new(out_frags.len() as u32 - 1)
}
};
out_map[reg_to_reg_ix(num_real_regs, reg) as usize].push(new_fix);
}
#[inline(never)]
fn get_range_frags_for_block<F: Function>(
func: &F,
rvb: &RegVecsAndBounds,
reg_universe: &RealRegUniverse,
vreg_classes: &Vec< RegClass>,
bix: BlockIx,
livein: &SparseSet<Reg>,
liveout: &SparseSet<Reg>,
visited: &mut Vec<u32>,
state: &mut Vec< Option<ProtoRangeFrag>>,
out_map: &mut Vec< SmallVec<[RangeFragIx; 8]>>,
out_frags: &mut TypedIxVec<RangeFragIx, RangeFrag>,
out_frag_metrics: &mut TypedIxVec<RangeFragIx, RangeFragMetrics>,
) {
#[inline(always)]
fn plus1(n: u16) -> u16 {
if n == 0xFFFFu16 {
n
} else {
n + 1
}
}
visited.clear();
assert!(func.block_insns(bix).len() >= 1);
let first_iix_in_block = func.block_insns(bix).first();
let last_iix_in_block = func.block_insns(bix).last();
let first_pt_in_block = InstPoint::new_use(first_iix_in_block);
let last_pt_in_block = InstPoint::new_def(last_iix_in_block);
let num_real_regs = reg_universe.regs.len() as u32;
for r in livein.iter() {
let r_state_ix = reg_to_reg_ix(num_real_regs, *r) as usize;
debug_assert!(state[r_state_ix].is_none());
state[r_state_ix] = Some(ProtoRangeFrag {
num_mentions: 0,
first: first_pt_in_block,
last: first_pt_in_block,
});
visited.push(r_state_ix as u32);
}
for iix in func.block_insns(bix) {
let bounds_for_iix = &rvb.bounds[iix];
for i in
bounds_for_iix.uses_start..bounds_for_iix.uses_start + bounds_for_iix.uses_len as u32
{
let r = rvb.vecs.uses[i as usize];
let r_state_ix = reg_to_reg_ix(num_real_regs, r) as usize;
match &mut state[r_state_ix] {
None => panic!("get_range_frags_for_block: fail #1"),
Some(ref mut pf) => {
pf.num_mentions = plus1(pf.num_mentions);
let new_last = InstPoint::new_use(iix);
debug_assert!(pf.last <= new_last);
pf.last = new_last;
}
}
}
for i in
bounds_for_iix.mods_start..bounds_for_iix.mods_start + bounds_for_iix.mods_len as u32
{
let r = &rvb.vecs.mods[i as usize];
let r_state_ix = reg_to_reg_ix(num_real_regs, *r) as usize;
match &mut state[r_state_ix] {
None => panic!("get_range_frags_for_block: fail #2"),
Some(ref mut pf) => {
pf.num_mentions = plus1(pf.num_mentions);
let new_last = InstPoint::new_def(iix);
debug_assert!(pf.last <= new_last);
pf.last = new_last;
}
}
}
for i in
bounds_for_iix.defs_start..bounds_for_iix.defs_start + bounds_for_iix.defs_len as u32
{
let r = &rvb.vecs.defs[i as usize];
let r_state_ix = reg_to_reg_ix(num_real_regs, *r) as usize;
match &mut state[r_state_ix] {
None => {
let new_pt = InstPoint::new_def(iix);
let new_pf = ProtoRangeFrag {
num_mentions: 1,
first: new_pt,
last: new_pt,
};
state[r_state_ix] = Some(new_pf);
visited.push(r_state_ix as u32);
}
Some(ProtoRangeFrag {
ref mut num_mentions,
ref mut first,
ref mut last,
}) => {
if first == last {
debug_assert!(*num_mentions == 1);
}
let (frag, frag_metrics) =
RangeFrag::new_with_metrics(func, bix, *first, *last, *num_mentions);
emit_range_frag(
out_map,
out_frags,
out_frag_metrics,
num_real_regs,
*r,
&frag,
&frag_metrics,
);
let new_pt = InstPoint::new_def(iix);
*num_mentions = 1;
*first = new_pt;
*last = new_pt;
}
}
}
}
for r in liveout.iter() {
let r_state_ix = reg_to_reg_ix(num_real_regs, *r) as usize;
let state_elem_p = &mut state[r_state_ix];
match state_elem_p {
None => panic!("get_range_frags_for_block: fail #3"),
Some(ref pf) => {
let (frag, frag_metrics) = RangeFrag::new_with_metrics(
func,
bix,
pf.first,
last_pt_in_block,
pf.num_mentions,
);
emit_range_frag(
out_map,
out_frags,
out_frag_metrics,
num_real_regs,
*r,
&frag,
&frag_metrics,
);
*state_elem_p = None;
}
}
}
for r_state_ix in visited {
let state_elem_p = &mut state[*r_state_ix as usize];
match state_elem_p {
None => {}
Some(pf) => {
if pf.first == pf.last {
debug_assert!(pf.num_mentions == 1);
}
let (frag, frag_metrics) =
RangeFrag::new_with_metrics(func, bix, pf.first, pf.last, pf.num_mentions);
let r = reg_ix_to_reg(reg_universe, vreg_classes, *r_state_ix);
emit_range_frag(
out_map,
out_frags,
out_frag_metrics,
num_real_regs,
r,
&frag,
&frag_metrics,
);
*state_elem_p = None;
}
}
}
}
#[inline(never)]
pub fn get_range_frags<F: Function>(
func: &F,
rvb: &RegVecsAndBounds,
reg_universe: &RealRegUniverse,
livein_sets_per_block: &TypedIxVec<BlockIx, SparseSet<Reg>>,
liveout_sets_per_block: &TypedIxVec<BlockIx, SparseSet<Reg>>,
) -> (
Vec< SmallVec<[RangeFragIx; 8]>>,
TypedIxVec<RangeFragIx, RangeFrag>,
TypedIxVec<RangeFragIx, RangeFragMetrics>,
Vec< RegClass>,
) {
info!(" get_range_frags: begin");
assert!(livein_sets_per_block.len() == func.blocks().len() as u32);
assert!(liveout_sets_per_block.len() == func.blocks().len() as u32);
assert!(rvb.is_sanitized());
let mut vreg_classes = vec![RegClass::INVALID; func.get_num_vregs()];
for r in rvb
.vecs
.uses
.iter()
.chain(rvb.vecs.defs.iter())
.chain(rvb.vecs.mods.iter())
{
if r.is_real() {
continue;
}
let r_ix = r.get_index();
let vreg_classes_ptr = &mut vreg_classes[r_ix];
if *vreg_classes_ptr == RegClass::INVALID {
*vreg_classes_ptr = r.get_class();
} else {
assert_eq!(*vreg_classes_ptr, r.get_class());
}
}
let num_real_regs = reg_universe.regs.len();
let num_virtual_regs = vreg_classes.len();
let num_regs = num_real_regs + num_virtual_regs;
let mut state = Vec::< Option<ProtoRangeFrag>>::new();
state.resize(num_regs, None);
let mut visited = Vec::<u32>::with_capacity(32);
let mut result_frags = TypedIxVec::<RangeFragIx, RangeFrag>::new();
let mut result_frag_metrics = TypedIxVec::<RangeFragIx, RangeFragMetrics>::new();
let mut result_map =
Vec::< SmallVec<[RangeFragIx; 8]>>::default();
result_map.resize(num_regs, smallvec![]);
for bix in func.blocks() {
get_range_frags_for_block(
func,
rvb,
reg_universe,
&vreg_classes,
bix,
&livein_sets_per_block[bix],
&liveout_sets_per_block[bix],
&mut visited,
&mut state,
&mut result_map,
&mut result_frags,
&mut result_frag_metrics,
);
}
assert!(state.len() == num_regs);
assert!(result_map.len() == num_regs);
assert!(vreg_classes.len() == num_virtual_regs);
for state_elem in &state {
assert!(state_elem.is_none());
}
if log_enabled!(Level::Debug) {
debug!("");
let mut n = 0;
for frag in result_frags.iter() {
debug!("{:<3?} {:?}", RangeFragIx::new(n), frag);
n += 1;
}
debug!("");
for (reg_ix, frag_ixs) in result_map.iter().enumerate() {
if frag_ixs.len() == 0 {
continue;
}
let reg = reg_ix_to_reg(reg_universe, &vreg_classes, reg_ix as u32);
debug!(
"frags for {} {:?}",
reg.show_with_rru(reg_universe),
frag_ixs
);
}
}
info!(" get_range_frags: end");
assert!(result_frags.len() == result_frag_metrics.len());
(result_map, result_frags, result_frag_metrics, vreg_classes)
}
fn frags_are_mergeable(
frag1: &RangeFrag,
frag1metrics: &RangeFragMetrics,
frag2: &RangeFrag,
frag2metrics: &RangeFragMetrics,
) -> bool {
assert!(frag1.first.pt().is_use_or_def());
assert!(frag1.last.pt().is_use_or_def());
assert!(frag2.first.pt().is_use_or_def());
assert!(frag2.last.pt().is_use_or_def());
if frag1metrics.bix != frag2metrics.bix
&& frag1.last.iix().plus(1) == frag2.first.iix()
&& frag1.last.pt() == Point::Def
&& frag2.first.pt() == Point::Use
{
assert!(
frag1metrics.kind == RangeFragKind::LiveOut || frag1metrics.kind == RangeFragKind::Thru
);
assert!(
frag2metrics.kind == RangeFragKind::LiveIn || frag2metrics.kind == RangeFragKind::Thru
);
return true;
}
false
}
#[inline(never)]
fn deref_and_compress_sorted_range_frag_ixs(
stats_num_vfrags_uncompressed: &mut usize,
stats_num_vfrags_compressed: &mut usize,
sorted_frag_ixs: &SortedRangeFragIxs,
frag_env: &TypedIxVec<RangeFragIx, RangeFrag>,
frag_metrics_env: &TypedIxVec<RangeFragIx, RangeFragMetrics>,
) -> SortedRangeFrags {
let mut res = SortedRangeFrags::empty();
let frag_ixs = &sorted_frag_ixs.frag_ixs;
let num_frags = frag_ixs.len();
*stats_num_vfrags_uncompressed += num_frags;
if num_frags == 1 {
res.frags.push(frag_env[frag_ixs[0]].clone());
*stats_num_vfrags_compressed += 1;
return res;
}
assert!(num_frags > 1);
let mut s = 0; let mut e = 0; loop {
if s >= num_frags {
break;
}
while e + 1 < num_frags
&& frags_are_mergeable(
&frag_env[frag_ixs[e]],
&frag_metrics_env[frag_ixs[e]],
&frag_env[frag_ixs[e + 1]],
&frag_metrics_env[frag_ixs[e + 1]],
)
{
e += 1;
}
if s == e {
res.frags.push(frag_env[frag_ixs[s]].clone());
} else {
let compressed_frag = RangeFrag {
first: frag_env[frag_ixs[s]].first,
last: frag_env[frag_ixs[e]].last,
};
res.frags.push(compressed_frag);
}
s = e + 1;
e = s;
}
*stats_num_vfrags_compressed += res.frags.len();
res
}
fn calc_virtual_range_metrics(
sorted_frag_ixs: &SortedRangeFragIxs,
frag_env: &TypedIxVec<RangeFragIx, RangeFrag>,
frag_metrics_env: &TypedIxVec<RangeFragIx, RangeFragMetrics>,
estimated_frequencies: &TypedIxVec<BlockIx, u32>,
) -> (u16, u32, SpillCost) {
assert!(frag_env.len() == frag_metrics_env.len());
let mut tot_size: u32 = 0;
let mut tot_cost: u32 = 0;
for fix in &sorted_frag_ixs.frag_ixs {
let frag = &frag_env[*fix];
let frag_metrics = &frag_metrics_env[*fix];
let mut frag_size: u32 = frag.last.iix().get() - frag.first.iix().get() + 1;
frag_size = min(frag_size, 0xFFFFu32);
tot_size += frag_size;
tot_size = min(tot_size, 0xFFFFu32);
let mut new_tot_cost: u64 = frag_metrics.count as u64; new_tot_cost *= estimated_frequencies[frag_metrics.bix] as u64; new_tot_cost += tot_cost as u64; new_tot_cost = min(new_tot_cost, 0xFFFF_FFFFu64);
tot_cost = new_tot_cost as u32;
}
debug_assert!(tot_size <= 0xFFFF);
let size = tot_size as u16;
let total_cost = tot_cost;
debug_assert!(tot_size >= 1);
let spill_cost = SpillCost::finite(tot_cost as f32 / tot_size as f32);
(size, total_cost, spill_cost)
}
#[inline(never)]
fn create_and_add_range(
stats_num_vfrags_uncompressed: &mut usize,
stats_num_vfrags_compressed: &mut usize,
result_real: &mut TypedIxVec<RealRangeIx, RealRange>,
result_virtual: &mut TypedIxVec<VirtualRangeIx, VirtualRange>,
reg: Reg,
sorted_frag_ixs: SortedRangeFragIxs,
frag_env: &TypedIxVec<RangeFragIx, RangeFrag>,
frag_metrics_env: &TypedIxVec<RangeFragIx, RangeFragMetrics>,
estimated_frequencies: &TypedIxVec<BlockIx, u32>,
) {
if reg.is_virtual() {
let (size, total_cost, spill_cost) = calc_virtual_range_metrics(
&sorted_frag_ixs,
frag_env,
frag_metrics_env,
estimated_frequencies,
);
let sorted_frags = deref_and_compress_sorted_range_frag_ixs(
stats_num_vfrags_uncompressed,
stats_num_vfrags_compressed,
&sorted_frag_ixs,
frag_env,
frag_metrics_env,
);
result_virtual.push(VirtualRange {
vreg: reg.to_virtual_reg(),
rreg: None,
sorted_frags,
is_ref: false, size,
total_cost,
spill_cost,
});
} else {
result_real.push(RealRange {
rreg: reg.to_real_reg(),
sorted_frags: sorted_frag_ixs,
is_ref: false, });
}
}
impl ToFromU32 for usize {
#[cfg(target_pointer_width = "64")]
fn to_u32(x: usize) -> u32 {
if x < 0x1_0000_0000usize {
x as u32
} else {
panic!("impl ToFromU32 for usize: to_u32: out of range")
}
}
#[cfg(target_pointer_width = "64")]
fn from_u32(x: u32) -> usize {
x as usize
}
#[cfg(target_pointer_width = "32")]
fn to_u32(x: usize) -> u32 {
x as u32
}
#[cfg(target_pointer_width = "32")]
fn from_u32(x: u32) -> usize {
x as usize
}
}
#[inline(never)]
pub fn merge_range_frags(
frag_ix_vec_per_reg: &Vec< SmallVec<[RangeFragIx; 8]>>,
frag_env: &TypedIxVec<RangeFragIx, RangeFrag>,
frag_metrics_env: &TypedIxVec<RangeFragIx, RangeFragMetrics>,
estimated_frequencies: &TypedIxVec<BlockIx, u32>,
cfg_info: &CFGInfo,
reg_universe: &RealRegUniverse,
vreg_classes: &Vec< RegClass>,
) -> (
TypedIxVec<RealRangeIx, RealRange>,
TypedIxVec<VirtualRangeIx, VirtualRange>,
) {
assert!(frag_env.len() == frag_metrics_env.len());
let mut stats_num_total_incoming_frags = 0;
let mut stats_num_total_incoming_regs = 0;
for all_frag_ixs_for_reg in frag_ix_vec_per_reg {
stats_num_total_incoming_frags += all_frag_ixs_for_reg.len();
if all_frag_ixs_for_reg.len() > 0 {
stats_num_total_incoming_regs += 1;
}
}
info!(" merge_range_frags: begin");
info!(" in: {} in frag_env", frag_env.len());
info!(
" in: {} regs containing in total {} frags",
stats_num_total_incoming_regs, stats_num_total_incoming_frags
);
let mut stats_num_single_grps = 0;
let mut stats_num_local_frags = 0;
let mut stats_num_multi_grps_small = 0;
let mut stats_num_multi_grps_large = 0;
let mut stats_size_multi_grps_small = 0;
let mut stats_size_multi_grps_large = 0;
let mut stats_num_vfrags_uncompressed = 0;
let mut stats_num_vfrags_compressed = 0;
let mut result_real = TypedIxVec::<RealRangeIx, RealRange>::new();
let mut result_virtual = TypedIxVec::<VirtualRangeIx, VirtualRange>::new();
for (reg_ix, all_frag_ixs_for_reg) in frag_ix_vec_per_reg.iter().enumerate() {
let n_frags_for_this_reg = all_frag_ixs_for_reg.len();
if n_frags_for_this_reg == 0 {
continue;
}
let reg_ix = reg_ix as u32;
let reg = reg_ix_to_reg(reg_universe, vreg_classes, reg_ix);
if n_frags_for_this_reg == 1 {
create_and_add_range(
&mut stats_num_vfrags_uncompressed,
&mut stats_num_vfrags_compressed,
&mut result_real,
&mut result_virtual,
reg,
SortedRangeFragIxs::unit(all_frag_ixs_for_reg[0], frag_env),
frag_env,
frag_metrics_env,
estimated_frequencies,
);
stats_num_single_grps += 1;
continue;
}
let mut triples = SmallVec::<[(RangeFragIx, RangeFragKind, BlockIx); 256]>::new();
for fix in all_frag_ixs_for_reg {
let frag_metrics = &frag_metrics_env[*fix];
if frag_metrics.kind == RangeFragKind::Local {
create_and_add_range(
&mut stats_num_vfrags_uncompressed,
&mut stats_num_vfrags_compressed,
&mut result_real,
&mut result_virtual,
reg,
SortedRangeFragIxs::unit(*fix, frag_env),
frag_env,
frag_metrics_env,
estimated_frequencies,
);
stats_num_local_frags += 1;
continue;
}
triples.push((*fix, frag_metrics.kind, frag_metrics.bix));
}
let triples_len = triples.len();
let mut eclasses_uf = UnionFind::<usize>::new(triples_len);
if triples_len <= 250 {
for (ix, (_fix, kind, bix)) in triples.iter().enumerate() {
if *kind == RangeFragKind::LiveOut || *kind == RangeFragKind::Thru {
for b in cfg_info.succ_map[*bix].iter() {
for (ix2, (_fix2, kind2, bix2)) in triples.iter().enumerate() {
if *bix2 != *b || *kind2 == RangeFragKind::LiveOut {
continue;
}
debug_assert!(
*kind2 == RangeFragKind::LiveIn || *kind2 == RangeFragKind::Thru
);
if ix != ix2 {
eclasses_uf.union(ix, ix2); }
}
}
}
}
stats_num_multi_grps_small += 1;
stats_size_multi_grps_small += triples_len;
} else {
triples.sort_unstable_by_key(|(_, _, bix)| *bix);
for (ix, (_fix, kind, bix)) in triples.iter().enumerate() {
if *kind == RangeFragKind::LiveOut || *kind == RangeFragKind::Thru {
for b in cfg_info.succ_map[*bix].iter() {
let mut ix_left = 0;
let mut ix_right = triples_len;
while ix_left < ix_right {
let m = (ix_left + ix_right) >> 1;
if triples[m].2 < *b {
ix_left = m + 1;
} else {
ix_right = m;
}
}
if ix_left < triples_len && *b < triples[ix_left].2 {
ix_left = triples_len;
}
if ix_left < triples_len {
assert!(ix_left == 0 || triples[ix_left - 1].2 < *b);
}
let mut ix2 = ix_left;
loop {
let (_fix2, kind2, bix2) = match triples.get(ix2) {
None => break,
Some(triple) => *triple,
};
if *b < bix2 {
break;
}
debug_assert!(*b == bix2);
if kind2 == RangeFragKind::LiveOut {
ix2 += 1;
continue;
}
eclasses_uf.union(ix, ix2);
ix2 += 1;
}
if ix2 + 1 < triples_len {
debug_assert!(*b < triples[ix2 + 1].2);
}
}
}
}
stats_num_multi_grps_large += 1;
stats_size_multi_grps_large += triples_len;
}
let eclasses = eclasses_uf.get_equiv_classes();
for leader_triple_ix in eclasses.equiv_class_leaders_iter() {
let mut frag_ixs = SmallVec::<[RangeFragIx; 4]>::new();
for triple_ix in eclasses.equiv_class_elems_iter(leader_triple_ix) {
frag_ixs.push(triples[triple_ix].0 );
}
let sorted_frags = SortedRangeFragIxs::new(frag_ixs, &frag_env);
create_and_add_range(
&mut stats_num_vfrags_uncompressed,
&mut stats_num_vfrags_compressed,
&mut result_real,
&mut result_virtual,
reg,
sorted_frags,
frag_env,
frag_metrics_env,
estimated_frequencies,
);
}
}
info!(" in: {} single groups", stats_num_single_grps);
info!(
" in: {} local frags in multi groups",
stats_num_local_frags
);
info!(
" in: {} small multi groups, {} small multi group total size",
stats_num_multi_grps_small, stats_size_multi_grps_small
);
info!(
" in: {} large multi groups, {} large multi group total size",
stats_num_multi_grps_large, stats_size_multi_grps_large
);
info!(
" out: {} VLRs, {} RLRs",
result_virtual.len(),
result_real.len()
);
info!(
" compress vfrags: in {}, out {}",
stats_num_vfrags_uncompressed, stats_num_vfrags_compressed
);
info!(" merge_range_frags: end");
(result_real, result_virtual)
}
#[inline(never)]
pub fn compute_reg_to_ranges_maps<F: Function>(
func: &F,
univ: &RealRegUniverse,
rlr_env: &TypedIxVec<RealRangeIx, RealRange>,
vlr_env: &TypedIxVec<VirtualRangeIx, VirtualRange>,
) -> RegToRangesMaps {
const MANY_FRAGS_THRESH: u8 = 200;
let add_u8_usize_saturate_to_u8 = |counter: &mut u8, mut to_add: usize| {
if to_add > 0xFF {
to_add = 0xFF;
}
let mut n = *counter as usize;
n += to_add as usize;
if n > 0xFF {
n = 0xFF;
}
*counter = n as u8;
};
let num_vregs = func.get_num_vregs();
let num_rregs = univ.allocable;
let mut vreg_approx_frag_counts = vec![0u8; num_vregs];
let mut vreg_to_vlrs_map = vec![SmallVec::<[VirtualRangeIx; 3]>::new(); num_vregs];
for (vlr, n) in vlr_env.iter().zip(0..) {
let vlrix = VirtualRangeIx::new(n);
let vreg: VirtualReg = vlr.vreg;
let vreg_index = vreg.get_index();
vreg_to_vlrs_map[vreg_index].push(vlrix);
let vlr_num_frags = vlr.sorted_frags.frags.len();
add_u8_usize_saturate_to_u8(&mut vreg_approx_frag_counts[vreg_index], vlr_num_frags);
}
let mut rreg_approx_frag_counts = vec![0u8; num_rregs];
let mut rreg_to_rlrs_map = vec![SmallVec::<[RealRangeIx; 6]>::new(); num_rregs];
for (rlr, n) in rlr_env.iter().zip(0..) {
let rlrix = RealRangeIx::new(n);
let rreg: RealReg = rlr.rreg;
let rreg_index = rreg.get_index();
rreg_to_rlrs_map[rreg_index].push(rlrix);
let rlr_num_frags = rlr.sorted_frags.frag_ixs.len();
add_u8_usize_saturate_to_u8(&mut rreg_approx_frag_counts[rreg_index], rlr_num_frags);
}
let mut vregs_with_many_frags = Vec::<u32 >::with_capacity(16);
for (count, vreg_ix) in vreg_approx_frag_counts.iter().zip(0..) {
if *count >= MANY_FRAGS_THRESH {
vregs_with_many_frags.push(vreg_ix);
}
}
let mut rregs_with_many_frags = Vec::<u32 >::with_capacity(64);
for (count, rreg_ix) in rreg_approx_frag_counts.iter().zip(0..) {
if *count >= MANY_FRAGS_THRESH {
rregs_with_many_frags.push(rreg_ix);
}
}
RegToRangesMaps {
rreg_to_rlrs_map,
vreg_to_vlrs_map,
rregs_with_many_frags,
vregs_with_many_frags,
many_frags_thresh: MANY_FRAGS_THRESH as usize,
}
}
#[inline(never)]
pub fn collect_move_info<F: Function>(
func: &F,
reg_vecs_and_bounds: &RegVecsAndBounds,
est_freqs: &TypedIxVec<BlockIx, u32>,
) -> MoveInfo {
let mut moves = Vec::<MoveInfoElem>::new();
for b in func.blocks() {
let block_eef = est_freqs[b];
for iix in func.block_insns(b) {
let insn = &func.get_insn(iix);
let im = func.is_move(insn);
match im {
None => {}
Some((wreg, reg)) => {
let iix_bounds = ®_vecs_and_bounds.bounds[iix];
assert!(iix_bounds.uses_len <= 1);
assert!(iix_bounds.defs_len <= 1);
assert!(iix_bounds.mods_len == 0);
if iix_bounds.uses_len == 1 && iix_bounds.defs_len == 1 {
let reg_vecs = ®_vecs_and_bounds.vecs;
assert!(reg_vecs.uses[iix_bounds.uses_start as usize] == reg);
assert!(reg_vecs.defs[iix_bounds.defs_start as usize] == wreg.to_reg());
let dst = wreg.to_reg();
let src = reg;
let est_freq = block_eef;
moves.push(MoveInfoElem {
dst,
src,
iix,
est_freq,
});
}
}
}
}
}
MoveInfo { moves }
}