use log::{debug, info};
use crate::analysis_control_flow::{CFGInfo, InstIxToBlockIxMap};
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,
};
use crate::analysis_reftypes::do_reftypes_analysis;
use crate::data_structures::{
BlockIx, MoveInfo, RangeFrag, RangeFragIx, RangeFragMetrics, RealRange, RealRangeIx, RealReg,
RealRegUniverse, RegClass, RegToRangesMaps, RegVecsAndBounds, TypedIxVec, VirtualRange,
VirtualRangeIx, VirtualReg,
};
use crate::sparse_set::SparseSet;
use crate::AlgorithmWithDefaults;
use crate::{Function, Reg};
#[derive(Clone, Debug)]
pub enum AnalysisError {
CriticalEdge { from: BlockIx, to: BlockIx },
EntryLiveinValues(Vec<Reg>),
IllegalRealReg(RealReg),
UnreachableBlocks,
ImplementationLimitsExceeded,
LSRACantDoStackmaps,
}
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::LSRACantDoStackmaps => {
"LSRA *and* stackmap creation requested; but this combination is not yet supported".to_string()
}
}
}
}
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: TypedIxVec<BlockIx, u32>,
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> {
info!("run_analysis: begin");
info!(
" run_analysis: {} blocks, {} insns",
func.blocks().len(),
func.insns().len()
);
if client_wants_stackmaps {
assert!(algorithm != AlgorithmWithDefaults::LinearScan);
}
info!(" run_analysis: begin control flow analysis");
let cfg_info = CFGInfo::create(func)?;
let inst_to_block_map = InstIxToBlockIxMap::new(func);
let mut estimated_frequencies = 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(estimated_frequencies.len()));
estimated_frequencies.push(estimated_frequency);
}
info!(" run_analysis: end control flow analysis");
info!(" 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);
}
}
info!(" run_analysis: end data flow analysis");
info!(" 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());
debug!("");
let mut n = 0;
for rlr in rlr_env.iter() {
debug!(
"{:<4?} {}",
RealRangeIx::new(n),
rlr.show_with_rru(®_universe)
);
n += 1;
}
debug!("");
n = 0;
for vlr in vlr_env.iter() {
debug!("{:<4?} {:?}", VirtualRangeIx::new(n), vlr);
n += 1;
}
let reg_to_ranges_maps =
if client_wants_stackmaps || algorithm == AlgorithmWithDefaults::Backtracking {
Some(compute_reg_to_ranges_maps(
func,
®_universe,
&rlr_env,
&vlr_env,
))
} else {
None
};
let move_info = if client_wants_stackmaps || algorithm == AlgorithmWithDefaults::Backtracking {
Some(collect_move_info(
func,
®_vecs_and_bounds,
&estimated_frequencies,
))
} else {
None
};
info!(" run_analysis: end liveness analysis");
if client_wants_stackmaps {
info!(" 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,
);
info!(" run_analysis: end reftypes analysis");
}
info!("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,
})
}