use log::{log_enabled, trace, Level};
use crate::data_structures::*;
use crate::sparse_set::SparseSet;
use crate::AlgorithmWithDefaults;
use crate::{
analysis_control_flow::{CFGInfo, InstIxToBlockIxMap},
analysis_reftypes::ReftypeAnalysis,
};
use crate::{
analysis_data_flow::{
calc_def_and_use, calc_livein_and_liveout, collect_move_info, compute_reg_to_ranges_maps,
get_range_frags, get_sanitized_reg_uses_for_func, merge_range_frags,
},
analysis_reftypes::core_reftypes_analysis,
};
use crate::{Function, Reg};
#[derive(Clone, Debug)]
pub enum AnalysisError {
CriticalEdge { from: BlockIx, to: BlockIx },
EntryLiveinValues(Vec<Reg>),
IllegalRealReg(RealReg),
UnreachableBlocks,
ImplementationLimitsExceeded,
LsraCriticalEdge { block: BlockIx, inst: InstIx },
}
impl ToString for AnalysisError {
fn to_string(&self) -> String {
match self {
AnalysisError::CriticalEdge { from, to } => {
format!("critical edge detected, from {:?} to {:?}", from, to)
}
AnalysisError::EntryLiveinValues(regs) => {
let regs_string = regs
.iter()
.map(|reg| format!("{:?}", reg))
.collect::<Vec<_>>()
.join(", ");
format!(
"entry block has love-in value not present in function liveins: {}",
regs_string
)
}
AnalysisError::IllegalRealReg(reg) => format!(
"instructions mention real register {:?}, which either isn't defined in the
register universe, or is a 'suggested_scratch' register",
reg
),
AnalysisError::UnreachableBlocks => "at least one block is unreachable".to_string(),
AnalysisError::ImplementationLimitsExceeded => {
"implementation limits exceeded (more than 1 million blocks or 16 million insns)"
.to_string()
}
AnalysisError::LsraCriticalEdge { block, inst } => format!(
"block {:?} ends with control flow instruction {:?} that mentions a register,
and at least one of the multiple successors has several predecessors; consider
splitting the outgoing edges!",
block, inst
),
}
}
}
pub struct AnalysisInfo {
pub(crate) reg_vecs_and_bounds: RegVecsAndBounds,
pub(crate) real_ranges: TypedIxVec<RealRangeIx, RealRange>,
pub(crate) virtual_ranges: TypedIxVec<VirtualRangeIx, VirtualRange>,
pub(crate) range_frags: TypedIxVec<RangeFragIx, RangeFrag>,
pub(crate) range_metrics: TypedIxVec<RangeFragIx, RangeFragMetrics>,
pub(crate) estimated_frequencies: DepthBasedFrequencies,
pub(crate) inst_to_block_map: InstIxToBlockIxMap,
pub(crate) reg_to_ranges_maps: Option<RegToRangesMaps>,
pub(crate) move_info: Option<MoveInfo>,
}
#[inline(never)]
pub fn run_analysis<F: Function>(
func: &F,
reg_universe: &RealRegUniverse,
algorithm: AlgorithmWithDefaults,
client_wants_stackmaps: bool,
reftype_class: RegClass,
reftyped_vregs: &Vec<VirtualReg>, ) -> Result<AnalysisInfo, AnalysisError> {
trace!("run_analysis: begin");
trace!(
" run_analysis: {} blocks, {} insns",
func.blocks().len(),
func.insns().len()
);
assert!(!client_wants_stackmaps || algorithm != AlgorithmWithDefaults::LinearScan);
trace!(" run_analysis: begin control flow analysis");
let cfg_info = CFGInfo::create(func)?;
let inst_to_block_map = InstIxToBlockIxMap::new(func);
let estimated_frequencies = DepthBasedFrequencies::new(func, &cfg_info);
trace!(" run_analysis: end control flow analysis");
trace!(" run_analysis: begin data flow analysis");
let reg_vecs_and_bounds = get_sanitized_reg_uses_for_func(func, reg_universe)
.map_err(|reg| AnalysisError::IllegalRealReg(reg))?;
assert!(reg_vecs_and_bounds.is_sanitized());
let (def_sets_per_block, use_sets_per_block) =
calc_def_and_use(func, ®_vecs_and_bounds, ®_universe);
debug_assert!(def_sets_per_block.len() == func.blocks().len() as u32);
debug_assert!(use_sets_per_block.len() == func.blocks().len() as u32);
let (livein_sets_per_block, mut liveout_sets_per_block) = calc_livein_and_liveout(
func,
&def_sets_per_block,
&use_sets_per_block,
&cfg_info,
®_universe,
);
debug_assert!(livein_sets_per_block.len() == func.blocks().len() as u32);
debug_assert!(liveout_sets_per_block.len() == func.blocks().len() as u32);
let func_liveins = SparseSet::from_vec(
func.func_liveins()
.to_vec()
.into_iter()
.map(|rreg| rreg.to_reg())
.collect(),
);
if !livein_sets_per_block[func.entry_block()].is_subset_of(&func_liveins) {
let mut regs = livein_sets_per_block[func.entry_block()].clone();
regs.remove(&func_liveins);
return Err(AnalysisError::EntryLiveinValues(regs.to_vec()));
}
let func_liveouts = SparseSet::from_vec(
func.func_liveouts()
.to_vec()
.into_iter()
.map(|rreg| rreg.to_reg())
.collect(),
);
for block in func.blocks() {
let last_iix = func.block_insns(block).last();
if func.is_ret(last_iix) {
liveout_sets_per_block[block].union(&func_liveouts);
}
}
trace!(" run_analysis: end data flow analysis");
trace!(" run_analysis: begin liveness analysis");
let (frag_ixs_per_reg, frag_env, frag_metrics_env, vreg_classes) = get_range_frags(
func,
®_vecs_and_bounds,
®_universe,
&livein_sets_per_block,
&liveout_sets_per_block,
);
let (mut rlr_env, mut vlr_env) = merge_range_frags(
&frag_ixs_per_reg,
&frag_env,
&frag_metrics_env,
&estimated_frequencies,
&cfg_info,
®_universe,
&vreg_classes,
);
debug_assert!(liveout_sets_per_block.len() == estimated_frequencies.len());
if log_enabled!(Level::Trace) {
trace!("");
let mut n = 0;
for rlr in rlr_env.iter() {
trace!(
"{:<4?} {}",
RealRangeIx::new(n),
rlr.show_with_rru(®_universe)
);
n += 1;
}
trace!("");
n = 0;
for vlr in vlr_env.iter() {
trace!("{:<4?} {:?}", VirtualRangeIx::new(n), vlr);
n += 1;
}
}
let (reg_to_ranges_maps, move_info) =
if client_wants_stackmaps || algorithm == AlgorithmWithDefaults::Backtracking {
(
Some(compute_reg_to_ranges_maps(
func,
®_universe,
&rlr_env,
&vlr_env,
)),
Some(collect_move_info(
func,
®_vecs_and_bounds,
&estimated_frequencies,
)),
)
} else {
(None, None)
};
trace!(" run_analysis: end liveness analysis");
if client_wants_stackmaps {
trace!(" run_analysis: begin reftypes analysis");
do_reftypes_analysis(
&mut rlr_env,
&mut vlr_env,
&frag_env,
reg_to_ranges_maps.as_ref().unwrap(),
&move_info.as_ref().unwrap(),
reftype_class,
reftyped_vregs,
);
trace!(" run_analysis: end reftypes analysis");
}
trace!("run_analysis: end");
Ok(AnalysisInfo {
reg_vecs_and_bounds,
real_ranges: rlr_env,
virtual_ranges: vlr_env,
range_frags: frag_env,
range_metrics: frag_metrics_env,
estimated_frequencies,
inst_to_block_map,
reg_to_ranges_maps,
move_info,
})
}
pub(crate) struct DepthBasedFrequencies(TypedIxVec<BlockIx, u32>);
impl DepthBasedFrequencies {
pub(crate) fn new<F: Function>(func: &F, cfg_info: &CFGInfo) -> Self {
let mut values = TypedIxVec::new();
for bix in func.blocks() {
let mut estimated_frequency = 1;
let depth = u32::min(cfg_info.depth_map[bix], 3);
for _ in 0..depth {
estimated_frequency *= 10;
}
assert!(bix == BlockIx::new(values.len()));
values.push(estimated_frequency);
}
Self(values)
}
pub(crate) fn len(&self) -> u32 {
self.0.len()
}
pub(crate) fn iter(&self) -> std::slice::Iter<u32> {
self.0.iter()
}
#[inline(always)]
pub(crate) fn cost(&self, bix: BlockIx) -> u32 {
self.0[bix]
}
}
struct BacktrackingReftypeAnalysis<'a> {
rlr_env: &'a mut TypedIxVec<RealRangeIx, RealRange>,
vlr_env: &'a mut TypedIxVec<VirtualRangeIx, VirtualRange>,
frag_env: &'a TypedIxVec<RangeFragIx, RangeFrag>,
reg_to_ranges_maps: &'a RegToRangesMaps,
}
impl<'a> ReftypeAnalysis for BacktrackingReftypeAnalysis<'a> {
type RangeId = RangeId;
#[inline(always)]
fn find_range_id_for_reg(&self, pt: InstPoint, reg: Reg) -> Self::RangeId {
if reg.is_real() {
for &rlrix in &self.reg_to_ranges_maps.rreg_to_rlrs_map[reg.get_index() as usize] {
if self.rlr_env[rlrix]
.sorted_frags
.contains_pt(self.frag_env, pt)
{
return RangeId::new_real(rlrix);
}
}
} else {
for &vlrix in &self.reg_to_ranges_maps.vreg_to_vlrs_map[reg.get_index() as usize] {
if self.vlr_env[vlrix].sorted_frags.contains_pt(pt) {
return RangeId::new_virtual(vlrix);
}
}
}
panic!("do_reftypes_analysis::find_range_for_reg: can't find range");
}
#[inline(always)]
fn mark_reffy(&mut self, range: &Self::RangeId) {
if range.is_real() {
let rrange = &mut self.rlr_env[range.to_real()];
debug_assert!(!rrange.is_ref);
trace!(" -> rrange {:?} is reffy", range.to_real());
rrange.is_ref = true;
} else {
let vrange = &mut self.vlr_env[range.to_virtual()];
debug_assert!(!vrange.is_ref);
trace!(" -> rrange {:?} is reffy", range.to_virtual());
vrange.is_ref = true;
}
}
#[inline(always)]
fn insert_reffy_ranges(&self, vreg: VirtualReg, set: &mut SparseSet<Self::RangeId>) {
for vlr_ix in &self.reg_to_ranges_maps.vreg_to_vlrs_map[vreg.get_index()] {
trace!("range {:?} is reffy due to reffy vreg {:?}", vlr_ix, vreg);
set.insert(RangeId::new_virtual(*vlr_ix));
}
}
}
fn do_reftypes_analysis(
rlr_env: &mut TypedIxVec<RealRangeIx, RealRange>,
vlr_env: &mut TypedIxVec<VirtualRangeIx, VirtualRange>,
frag_env: &TypedIxVec<RangeFragIx, RangeFrag>,
reg_to_ranges_maps: &RegToRangesMaps,
move_info: &MoveInfo,
reftype_class: RegClass,
reftyped_vregs: &Vec<VirtualReg>,
) {
let mut analysis = BacktrackingReftypeAnalysis {
rlr_env,
vlr_env,
frag_env,
reg_to_ranges_maps,
};
core_reftypes_analysis(&mut analysis, move_info, reftype_class, reftyped_vregs);
}