#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
use log::{debug, info, log_enabled, Level};
use smallvec::{smallvec, SmallVec};
use crate::data_structures::{
InstIx, InstPoint, Map, MoveInfo, MoveInfoElem, RangeFrag, RangeFragIx, RealRange, RealRangeIx,
RealReg, RealRegUniverse, RegToRangesMaps, SpillCost, TypedIxVec, VirtualRange, VirtualRangeIx,
VirtualReg,
};
use crate::union_find::{ToFromU32, UnionFind, UnionFindEquivClasses};
use crate::Function;
#[derive(Clone)]
pub enum Hint {
SameAs(VirtualRangeIx, u32),
Exactly(RealReg, u32),
}
fn show_hint(h: &Hint, univ: &RealRegUniverse) -> String {
match h {
Hint::SameAs(vlrix, weight) => format!("(SameAs {:?}, weight={})", vlrix, weight),
Hint::Exactly(rreg, weight) => format!(
"(Exactly {}, weight={})",
rreg.to_reg().show_with_rru(&univ),
weight
),
}
}
impl Hint {
#[inline(always)]
fn get_weight(&self) -> u32 {
match self {
Hint::SameAs(_vlrix, weight) => *weight,
Hint::Exactly(_rreg, weight) => *weight,
}
}
}
impl ToFromU32 for VirtualRangeIx {
fn to_u32(x: VirtualRangeIx) -> u32 {
x.get()
}
fn from_u32(x: u32) -> VirtualRangeIx {
VirtualRangeIx::new(x)
}
}
#[inline(never)]
pub fn do_coalescing_analysis<F: Function>(
func: &F,
univ: &RealRegUniverse,
rlr_env: &TypedIxVec<RealRangeIx, RealRange>,
vlr_env: &mut TypedIxVec<VirtualRangeIx, VirtualRange>,
frag_env: &TypedIxVec<RangeFragIx, RangeFrag>,
reg_to_ranges_maps: &RegToRangesMaps,
move_info: &MoveInfo,
) -> (
TypedIxVec<VirtualRangeIx, SmallVec<[Hint; 8]>>,
UnionFindEquivClasses<VirtualRangeIx>,
TypedIxVec<InstIx, bool>,
) {
info!("");
info!("do_coalescing_analysis: begin");
struct ManyFragsInfoR {
sorted_firsts: Vec<(InstPoint, RealRangeIx)>,
sorted_lasts: Vec<(InstPoint, RealRangeIx)>,
}
let r_many_card = reg_to_ranges_maps.rregs_with_many_frags.len();
let mut r_many_map = Map::<u32 , ManyFragsInfoR>::default();
r_many_map.reserve(r_many_card);
for rreg_no in ®_to_ranges_maps.rregs_with_many_frags {
let mut many_frags_info = ManyFragsInfoR {
sorted_firsts: Vec::with_capacity(2 * reg_to_ranges_maps.many_frags_thresh),
sorted_lasts: Vec::with_capacity(2 * reg_to_ranges_maps.many_frags_thresh),
};
let rlrixs = ®_to_ranges_maps.rreg_to_rlrs_map[*rreg_no as usize];
for rlrix in rlrixs {
for fix in &rlr_env[*rlrix].sorted_frags.frag_ixs {
let frag = &frag_env[*fix];
many_frags_info.sorted_firsts.push((frag.first, *rlrix));
many_frags_info.sorted_lasts.push((frag.last, *rlrix));
}
}
many_frags_info
.sorted_firsts
.sort_unstable_by_key(|&(point, _)| point);
many_frags_info
.sorted_lasts
.sort_unstable_by_key(|&(point, _)| point);
debug_assert!(many_frags_info.sorted_firsts.len() == many_frags_info.sorted_lasts.len());
for i in 1..(many_frags_info.sorted_firsts.len()) {
debug_assert!(
many_frags_info.sorted_firsts[i - 1].0 < many_frags_info.sorted_firsts[i].0
);
}
for i in 1..(many_frags_info.sorted_lasts.len()) {
debug_assert!(
many_frags_info.sorted_lasts[i - 1].0 < many_frags_info.sorted_lasts[i].0
);
}
r_many_map.insert(*rreg_no, many_frags_info);
}
struct ManyFragsInfoV {
sorted_firsts: Vec<(InstPoint, VirtualRangeIx)>,
sorted_lasts: Vec<(InstPoint, VirtualRangeIx)>,
}
let v_many_card = reg_to_ranges_maps.vregs_with_many_frags.len();
let mut v_many_map = Map::<u32 , ManyFragsInfoV>::default();
v_many_map.reserve(v_many_card);
for vreg_no in ®_to_ranges_maps.vregs_with_many_frags {
let mut many_frags_info = ManyFragsInfoV {
sorted_firsts: Vec::with_capacity(2 * reg_to_ranges_maps.many_frags_thresh),
sorted_lasts: Vec::with_capacity(2 * reg_to_ranges_maps.many_frags_thresh),
};
let vlrixs = ®_to_ranges_maps.vreg_to_vlrs_map[*vreg_no as usize];
for vlrix in vlrixs {
for frag in &vlr_env[*vlrix].sorted_frags.frags {
many_frags_info.sorted_firsts.push((frag.first, *vlrix));
many_frags_info.sorted_lasts.push((frag.last, *vlrix));
}
}
many_frags_info
.sorted_firsts
.sort_unstable_by_key(|&(point, _)| point);
many_frags_info
.sorted_lasts
.sort_unstable_by_key(|&(point, _)| point);
debug_assert!(many_frags_info.sorted_firsts.len() == many_frags_info.sorted_lasts.len());
for i in 1..(many_frags_info.sorted_firsts.len()) {
debug_assert!(
many_frags_info.sorted_firsts[i - 1].0 < many_frags_info.sorted_firsts[i].0
);
}
for i in 1..(many_frags_info.sorted_lasts.len()) {
debug_assert!(
many_frags_info.sorted_lasts[i - 1].0 < many_frags_info.sorted_lasts[i].0
);
}
v_many_map.insert(*vreg_no, many_frags_info);
}
let doesVRegHaveLastUseAt_LINEAR = |vreg: VirtualReg, iix: InstIx| -> Option<VirtualRangeIx> {
let point_to_find = InstPoint::new_use(iix);
let vreg_no = vreg.get_index();
let vlrixs = ®_to_ranges_maps.vreg_to_vlrs_map[vreg_no];
for vlrix in vlrixs {
for frag in &vlr_env[*vlrix].sorted_frags.frags {
if frag.last == point_to_find {
return Some(*vlrix);
}
}
}
None
};
let doesVRegHaveLastUseAt = |vreg: VirtualReg, iix: InstIx| -> Option<VirtualRangeIx> {
let point_to_find = InstPoint::new_use(iix);
let vreg_no = vreg.get_index();
let mut binary_search_result = None;
if let Some(ref mfi) = v_many_map.get(&(vreg_no as u32)) {
match mfi
.sorted_lasts
.binary_search_by_key(&point_to_find, |(point, _)| *point)
{
Ok(found_at_ix) => binary_search_result = Some(mfi.sorted_lasts[found_at_ix].1),
Err(_) => {}
}
}
match binary_search_result {
None => doesVRegHaveLastUseAt_LINEAR(vreg, iix),
Some(_) => {
debug_assert!(binary_search_result == doesVRegHaveLastUseAt_LINEAR(vreg, iix));
binary_search_result
}
}
};
let doesVRegHaveFirstDefAt_LINEAR = |vreg: VirtualReg, iix: InstIx| -> Option<VirtualRangeIx> {
let point_to_find = InstPoint::new_def(iix);
let vreg_no = vreg.get_index();
let vlrixs = ®_to_ranges_maps.vreg_to_vlrs_map[vreg_no];
for vlrix in vlrixs {
for frag in &vlr_env[*vlrix].sorted_frags.frags {
if frag.first == point_to_find {
return Some(*vlrix);
}
}
}
None
};
let doesVRegHaveFirstDefAt = |vreg: VirtualReg, iix: InstIx| -> Option<VirtualRangeIx> {
let point_to_find = InstPoint::new_def(iix);
let vreg_no = vreg.get_index();
let mut binary_search_result = None;
if let Some(ref mfi) = v_many_map.get(&(vreg_no as u32)) {
match mfi
.sorted_firsts
.binary_search_by_key(&point_to_find, |(point, _)| *point)
{
Ok(found_at_ix) => binary_search_result = Some(mfi.sorted_firsts[found_at_ix].1),
Err(_) => {}
}
}
match binary_search_result {
None => doesVRegHaveFirstDefAt_LINEAR(vreg, iix),
Some(_) => {
debug_assert!(binary_search_result == doesVRegHaveFirstDefAt_LINEAR(vreg, iix));
binary_search_result
}
}
};
let doesRRegHaveLastUseAt_LINEAR = |rreg: RealReg, iix: InstIx| -> Option<RealRangeIx> {
let point_to_find = InstPoint::new_use(iix);
let rreg_no = rreg.get_index();
let rlrixs = ®_to_ranges_maps.rreg_to_rlrs_map[rreg_no];
for rlrix in rlrixs {
let frags = &rlr_env[*rlrix].sorted_frags;
for fix in &frags.frag_ixs {
let frag = &frag_env[*fix];
if frag.last == point_to_find {
return Some(*rlrix);
}
}
}
None
};
let doesRRegHaveLastUseAt = |rreg: RealReg, iix: InstIx| -> Option<RealRangeIx> {
let point_to_find = InstPoint::new_use(iix);
let rreg_no = rreg.get_index();
let mut binary_search_result = None;
if let Some(ref mfi) = r_many_map.get(&(rreg_no as u32)) {
match mfi
.sorted_lasts
.binary_search_by_key(&point_to_find, |(point, _)| *point)
{
Ok(found_at_ix) => binary_search_result = Some(mfi.sorted_lasts[found_at_ix].1),
Err(_) => {}
}
}
match binary_search_result {
None => doesRRegHaveLastUseAt_LINEAR(rreg, iix),
Some(_) => {
debug_assert!(binary_search_result == doesRRegHaveLastUseAt_LINEAR(rreg, iix));
binary_search_result
}
}
};
let doesRRegHaveFirstDefAt_LINEAR = |rreg: RealReg, iix: InstIx| -> Option<RealRangeIx> {
let point_to_find = InstPoint::new_def(iix);
let rreg_no = rreg.get_index();
let rlrixs = ®_to_ranges_maps.rreg_to_rlrs_map[rreg_no];
for rlrix in rlrixs {
let frags = &rlr_env[*rlrix].sorted_frags;
for fix in &frags.frag_ixs {
let frag = &frag_env[*fix];
if frag.first == point_to_find {
return Some(*rlrix);
}
}
}
None
};
let doesRRegHaveFirstDefAt = |rreg: RealReg, iix: InstIx| -> Option<RealRangeIx> {
let point_to_find = InstPoint::new_def(iix);
let rreg_no = rreg.get_index();
let mut binary_search_result = None;
if let Some(ref mfi) = r_many_map.get(&(rreg_no as u32)) {
match mfi
.sorted_firsts
.binary_search_by_key(&point_to_find, |(point, _)| *point)
{
Ok(found_at_ix) => binary_search_result = Some(mfi.sorted_firsts[found_at_ix].1),
Err(_) => {}
}
}
match binary_search_result {
None => doesRRegHaveFirstDefAt_LINEAR(rreg, iix),
Some(_) => {
debug_assert!(binary_search_result == doesRRegHaveFirstDefAt_LINEAR(rreg, iix));
binary_search_result
}
}
};
let mut hints = TypedIxVec::<VirtualRangeIx, SmallVec<[Hint; 8]>>::new();
hints.resize(vlr_env.len(), smallvec![]);
let mut is_vv_boundary_move = TypedIxVec::<InstIx, bool>::new();
is_vv_boundary_move.resize(func.insns().len() as u32, false);
let mut vlrEquivClassesUF = UnionFind::<VirtualRangeIx>::new(vlr_env.len() as usize);
let mut decVLRcosts = Vec::<(VirtualRangeIx, VirtualRangeIx, u32)>::new();
for MoveInfoElem {
dst,
src,
iix,
est_freq,
..
} in &move_info.moves
{
debug!(
"connected by moves: {:?} {:?} <- {:?} (est_freq {})",
iix, dst, src, est_freq
);
match (dst.is_virtual(), src.is_virtual()) {
(true, true) => {
let srcV = src.to_virtual_reg();
let dstV = dst.to_virtual_reg();
let mb_vlrixSrc = doesVRegHaveLastUseAt(srcV, *iix);
let mb_vlrixDst = doesVRegHaveFirstDefAt(dstV, *iix);
if mb_vlrixSrc.is_some() && mb_vlrixDst.is_some() {
let vlrixSrc = mb_vlrixSrc.unwrap();
let vlrixDst = mb_vlrixDst.unwrap();
if !vlr_env[vlrixSrc].overlaps(&vlr_env[vlrixDst]) {
hints[vlrixSrc].push(Hint::SameAs(vlrixDst, *est_freq));
hints[vlrixDst].push(Hint::SameAs(vlrixSrc, *est_freq));
vlrEquivClassesUF.union(vlrixDst, vlrixSrc);
is_vv_boundary_move[*iix] = true;
debug!("reduce cost of {:?} and {:?}", vlrixSrc, vlrixDst);
decVLRcosts.push((vlrixSrc, vlrixDst, 1 * est_freq));
}
}
}
(true, false) => {
let srcR = src.to_real_reg();
let dstV = dst.to_virtual_reg();
let mb_rlrSrc = doesRRegHaveLastUseAt(srcR, *iix);
let mb_vlrDst = doesVRegHaveFirstDefAt(dstV, *iix);
if mb_rlrSrc.is_some() && mb_vlrDst.is_some() {
let vlrDst = mb_vlrDst.unwrap();
hints[vlrDst].push(Hint::Exactly(srcR, *est_freq));
}
}
(false, true) => {
let srcV = src.to_virtual_reg();
let dstR = dst.to_real_reg();
let mb_vlrSrc = doesVRegHaveLastUseAt(srcV, *iix);
let mb_rlrDst = doesRRegHaveFirstDefAt(dstR, *iix);
if mb_vlrSrc.is_some() && mb_rlrDst.is_some() {
let vlrSrc = mb_vlrSrc.unwrap();
hints[vlrSrc].push(Hint::Exactly(dstR, *est_freq));
}
}
(false, false) => {
}
}
}
fn decrease_vlr_total_cost_by(vlr: &mut VirtualRange, decrease_total_cost_by: u32) {
if vlr.total_cost < decrease_total_cost_by {
vlr.total_cost = 0;
} else {
vlr.total_cost -= decrease_total_cost_by;
}
if vlr.total_cost == 0 {
vlr.spill_cost = SpillCost::finite(1.0e-6);
} else {
assert!(vlr.size > 0);
vlr.spill_cost = SpillCost::finite(vlr.total_cost as f32 / vlr.size as f32);
}
}
for (vlrix1, vlrix2, decrease_total_cost_by) in decVLRcosts {
decrease_vlr_total_cost_by(&mut vlr_env[vlrix1], decrease_total_cost_by);
decrease_vlr_total_cost_by(&mut vlr_env[vlrix2], decrease_total_cost_by);
}
for hints_for_one_vlr in hints.iter_mut() {
hints_for_one_vlr.sort_by(|h1, h2| h2.get_weight().partial_cmp(&h1.get_weight()).unwrap());
}
let vlrEquivClasses: UnionFindEquivClasses<VirtualRangeIx> =
vlrEquivClassesUF.get_equiv_classes();
if log_enabled!(Level::Debug) {
debug!("Revised VLRs:");
let mut n = 0;
for vlr in vlr_env.iter() {
debug!("{:<4?} {:?}", VirtualRangeIx::new(n), vlr);
n += 1;
}
debug!("Coalescing hints:");
n = 0;
for hints_for_one_vlr in hints.iter() {
let mut s = "".to_string();
for hint in hints_for_one_vlr {
s = s + &show_hint(hint, &univ) + &" ".to_string();
}
debug!(" hintsfor {:<4?} = {}", VirtualRangeIx::new(n), s);
n += 1;
}
for n in 0..vlr_env.len() {
let vlrix = VirtualRangeIx::new(n);
let mut tmpvec = vec![];
for elem in vlrEquivClasses.equiv_class_elems_iter(vlrix) {
tmpvec.reverse();
tmpvec.push(elem);
}
debug!(" eclassof {:?} = {:?}", vlrix, tmpvec);
}
for (b, i) in is_vv_boundary_move.iter().zip(0..) {
if *b {
debug!(" vv_boundary_move at {:?}", InstIx::new(i));
}
}
}
info!("do_coalescing_analysis: end");
info!("");
(hints, vlrEquivClasses, is_vv_boundary_move)
}