1use log::{log_enabled, trace, Level};
4
5use crate::data_structures::*;
6use crate::sparse_set::SparseSet;
7use crate::AlgorithmWithDefaults;
8use crate::{
9 analysis_control_flow::{CFGInfo, InstIxToBlockIxMap},
10 analysis_reftypes::ReftypeAnalysis,
11};
12use crate::{
13 analysis_data_flow::{
14 calc_def_and_use, calc_livein_and_liveout, collect_move_info, compute_reg_to_ranges_maps,
15 get_range_frags, get_sanitized_reg_uses_for_func, merge_range_frags,
16 },
17 analysis_reftypes::core_reftypes_analysis,
18};
19use crate::{Function, Reg};
20
21#[derive(Clone, Debug)]
27pub enum AnalysisError {
28 CriticalEdge { from: BlockIx, to: BlockIx },
31
32 EntryLiveinValues(Vec<Reg>),
35
36 IllegalRealReg(RealReg),
45
46 UnreachableBlocks,
48
49 ImplementationLimitsExceeded,
52
53 LsraCriticalEdge { block: BlockIx, inst: InstIx },
63}
64
65impl ToString for AnalysisError {
66 fn to_string(&self) -> String {
67 match self {
68 AnalysisError::CriticalEdge { from, to } => {
69 format!("critical edge detected, from {:?} to {:?}", from, to)
70 }
71 AnalysisError::EntryLiveinValues(regs) => {
72 let regs_string = regs
73 .iter()
74 .map(|reg| format!("{:?}", reg))
75 .collect::<Vec<_>>()
76 .join(", ");
77 format!(
78 "entry block has love-in value not present in function liveins: {}",
79 regs_string
80 )
81 }
82 AnalysisError::IllegalRealReg(reg) => format!(
83 "instructions mention real register {:?}, which either isn't defined in the
84 register universe, or is a 'suggested_scratch' register",
85 reg
86 ),
87 AnalysisError::UnreachableBlocks => "at least one block is unreachable".to_string(),
88 AnalysisError::ImplementationLimitsExceeded => {
89 "implementation limits exceeded (more than 1 million blocks or 16 million insns)"
90 .to_string()
91 }
92 AnalysisError::LsraCriticalEdge { block, inst } => format!(
93 "block {:?} ends with control flow instruction {:?} that mentions a register,
94 and at least one of the multiple successors has several predecessors; consider
95 splitting the outgoing edges!",
96 block, inst
97 ),
98 }
99 }
100}
101
102pub struct AnalysisInfo {
106 pub(crate) reg_vecs_and_bounds: RegVecsAndBounds,
108 pub(crate) real_ranges: TypedIxVec<RealRangeIx, RealRange>,
110 pub(crate) virtual_ranges: TypedIxVec<VirtualRangeIx, VirtualRange>,
112 pub(crate) range_frags: TypedIxVec<RangeFragIx, RangeFrag>,
114 pub(crate) range_metrics: TypedIxVec<RangeFragIx, RangeFragMetrics>,
116 pub(crate) estimated_frequencies: DepthBasedFrequencies,
118 pub(crate) inst_to_block_map: InstIxToBlockIxMap,
120 pub(crate) reg_to_ranges_maps: Option<RegToRangesMaps>,
124 pub(crate) move_info: Option<MoveInfo>,
127}
128
129#[inline(never)]
130pub fn run_analysis<F: Function>(
131 func: &F,
132 reg_universe: &RealRegUniverse,
133 algorithm: AlgorithmWithDefaults,
134 client_wants_stackmaps: bool,
135 reftype_class: RegClass,
136 reftyped_vregs: &Vec<VirtualReg>, ) -> Result<AnalysisInfo, AnalysisError> {
138 trace!("run_analysis: begin");
139 trace!(
140 " run_analysis: {} blocks, {} insns",
141 func.blocks().len(),
142 func.insns().len()
143 );
144
145 assert!(!client_wants_stackmaps || algorithm != AlgorithmWithDefaults::LinearScan);
147
148 trace!(" run_analysis: begin control flow analysis");
149
150 let cfg_info = CFGInfo::create(func)?;
153
154 let inst_to_block_map = InstIxToBlockIxMap::new(func);
157
158 let estimated_frequencies = DepthBasedFrequencies::new(func, &cfg_info);
160
161 trace!(" run_analysis: end control flow analysis");
162
163 trace!(" run_analysis: begin data flow analysis");
165
166 let reg_vecs_and_bounds = get_sanitized_reg_uses_for_func(func, reg_universe)
168 .map_err(|reg| AnalysisError::IllegalRealReg(reg))?;
169 assert!(reg_vecs_and_bounds.is_sanitized());
170
171 let (def_sets_per_block, use_sets_per_block) =
173 calc_def_and_use(func, ®_vecs_and_bounds, ®_universe);
174 debug_assert!(def_sets_per_block.len() == func.blocks().len() as u32);
175 debug_assert!(use_sets_per_block.len() == func.blocks().len() as u32);
176
177 let (livein_sets_per_block, mut liveout_sets_per_block) = calc_livein_and_liveout(
182 func,
183 &def_sets_per_block,
184 &use_sets_per_block,
185 &cfg_info,
186 ®_universe,
187 );
188 debug_assert!(livein_sets_per_block.len() == func.blocks().len() as u32);
189 debug_assert!(liveout_sets_per_block.len() == func.blocks().len() as u32);
190
191 let func_liveins = SparseSet::from_vec(
194 func.func_liveins()
195 .to_vec()
196 .into_iter()
197 .map(|rreg| rreg.to_reg())
198 .collect(),
199 );
200 if !livein_sets_per_block[func.entry_block()].is_subset_of(&func_liveins) {
201 let mut regs = livein_sets_per_block[func.entry_block()].clone();
202 regs.remove(&func_liveins);
203 return Err(AnalysisError::EntryLiveinValues(regs.to_vec()));
204 }
205
206 let func_liveouts = SparseSet::from_vec(
208 func.func_liveouts()
209 .to_vec()
210 .into_iter()
211 .map(|rreg| rreg.to_reg())
212 .collect(),
213 );
214 for block in func.blocks() {
215 let last_iix = func.block_insns(block).last();
216 if func.is_ret(last_iix) {
217 liveout_sets_per_block[block].union(&func_liveouts);
218 }
219 }
220
221 trace!(" run_analysis: end data flow analysis");
222
223 trace!(" run_analysis: begin liveness analysis");
228
229 let (frag_ixs_per_reg, frag_env, frag_metrics_env, vreg_classes) = get_range_frags(
230 func,
231 ®_vecs_and_bounds,
232 ®_universe,
233 &livein_sets_per_block,
234 &liveout_sets_per_block,
235 );
236
237 let (mut rlr_env, mut vlr_env) = merge_range_frags(
240 &frag_ixs_per_reg,
241 &frag_env,
242 &frag_metrics_env,
243 &estimated_frequencies,
244 &cfg_info,
245 ®_universe,
246 &vreg_classes,
247 );
248
249 debug_assert!(liveout_sets_per_block.len() == estimated_frequencies.len());
250
251 if log_enabled!(Level::Trace) {
252 trace!("");
253 let mut n = 0;
254 for rlr in rlr_env.iter() {
255 trace!(
256 "{:<4?} {}",
257 RealRangeIx::new(n),
258 rlr.show_with_rru(®_universe)
259 );
260 n += 1;
261 }
262
263 trace!("");
264 n = 0;
265 for vlr in vlr_env.iter() {
266 trace!("{:<4?} {:?}", VirtualRangeIx::new(n), vlr);
267 n += 1;
268 }
269 }
270
271 let (reg_to_ranges_maps, move_info) =
276 if client_wants_stackmaps || algorithm == AlgorithmWithDefaults::Backtracking {
277 (
278 Some(compute_reg_to_ranges_maps(
279 func,
280 ®_universe,
281 &rlr_env,
282 &vlr_env,
283 )),
284 Some(collect_move_info(
285 func,
286 ®_vecs_and_bounds,
287 &estimated_frequencies,
288 )),
289 )
290 } else {
291 (None, None)
292 };
293
294 trace!(" run_analysis: end liveness analysis");
295
296 if client_wants_stackmaps {
297 trace!(" run_analysis: begin reftypes analysis");
298 do_reftypes_analysis(
299 &mut rlr_env,
300 &mut vlr_env,
301 &frag_env,
302 reg_to_ranges_maps.as_ref().unwrap(), &move_info.as_ref().unwrap(), reftype_class,
305 reftyped_vregs,
306 );
307 trace!(" run_analysis: end reftypes analysis");
308 }
309
310 trace!("run_analysis: end");
311
312 Ok(AnalysisInfo {
313 reg_vecs_and_bounds,
314 real_ranges: rlr_env,
315 virtual_ranges: vlr_env,
316 range_frags: frag_env,
317 range_metrics: frag_metrics_env,
318 estimated_frequencies,
319 inst_to_block_map,
320 reg_to_ranges_maps,
321 move_info,
322 })
323}
324
325pub(crate) struct DepthBasedFrequencies(TypedIxVec<BlockIx, u32>);
327
328impl DepthBasedFrequencies {
329 pub(crate) fn new<F: Function>(func: &F, cfg_info: &CFGInfo) -> Self {
330 let mut values = TypedIxVec::new();
331 for bix in func.blocks() {
332 let mut estimated_frequency = 1;
333 let depth = u32::min(cfg_info.depth_map[bix], 3);
334 for _ in 0..depth {
335 estimated_frequency *= 10;
336 }
337 assert!(bix == BlockIx::new(values.len()));
338 values.push(estimated_frequency);
339 }
340 Self(values)
341 }
342 pub(crate) fn len(&self) -> u32 {
343 self.0.len()
344 }
345 pub(crate) fn iter(&self) -> std::slice::Iter<u32> {
346 self.0.iter()
347 }
348 #[inline(always)]
349 pub(crate) fn cost(&self, bix: BlockIx) -> u32 {
350 self.0[bix]
351 }
352}
353
354struct BacktrackingReftypeAnalysis<'a> {
356 rlr_env: &'a mut TypedIxVec<RealRangeIx, RealRange>,
357 vlr_env: &'a mut TypedIxVec<VirtualRangeIx, VirtualRange>,
358 frag_env: &'a TypedIxVec<RangeFragIx, RangeFrag>,
359 reg_to_ranges_maps: &'a RegToRangesMaps,
360}
361
362impl<'a> ReftypeAnalysis for BacktrackingReftypeAnalysis<'a> {
363 type RangeId = RangeId;
364
365 #[inline(always)]
366 fn find_range_id_for_reg(&self, pt: InstPoint, reg: Reg) -> Self::RangeId {
367 if reg.is_real() {
368 for &rlrix in &self.reg_to_ranges_maps.rreg_to_rlrs_map[reg.get_index() as usize] {
369 if self.rlr_env[rlrix]
370 .sorted_frags
371 .contains_pt(self.frag_env, pt)
372 {
373 return RangeId::new_real(rlrix);
374 }
375 }
376 } else {
377 for &vlrix in &self.reg_to_ranges_maps.vreg_to_vlrs_map[reg.get_index() as usize] {
378 if self.vlr_env[vlrix].sorted_frags.contains_pt(pt) {
379 return RangeId::new_virtual(vlrix);
380 }
381 }
382 }
383 panic!("do_reftypes_analysis::find_range_for_reg: can't find range");
384 }
385
386 #[inline(always)]
387 fn mark_reffy(&mut self, range: &Self::RangeId) {
388 if range.is_real() {
389 let rrange = &mut self.rlr_env[range.to_real()];
390 debug_assert!(!rrange.is_ref);
391 trace!(" -> rrange {:?} is reffy", range.to_real());
392 rrange.is_ref = true;
393 } else {
394 let vrange = &mut self.vlr_env[range.to_virtual()];
395 debug_assert!(!vrange.is_ref);
396 trace!(" -> rrange {:?} is reffy", range.to_virtual());
397 vrange.is_ref = true;
398 }
399 }
400
401 #[inline(always)]
402 fn insert_reffy_ranges(&self, vreg: VirtualReg, set: &mut SparseSet<Self::RangeId>) {
403 for vlr_ix in &self.reg_to_ranges_maps.vreg_to_vlrs_map[vreg.get_index()] {
404 trace!("range {:?} is reffy due to reffy vreg {:?}", vlr_ix, vreg);
405 set.insert(RangeId::new_virtual(*vlr_ix));
406 }
407 }
408}
409
410fn do_reftypes_analysis(
411 rlr_env: &mut TypedIxVec<RealRangeIx, RealRange>,
413 vlr_env: &mut TypedIxVec<VirtualRangeIx, VirtualRange>,
414 frag_env: &TypedIxVec<RangeFragIx, RangeFrag>,
416 reg_to_ranges_maps: &RegToRangesMaps,
417 move_info: &MoveInfo,
418 reftype_class: RegClass,
420 reftyped_vregs: &Vec<VirtualReg>,
421) {
422 let mut analysis = BacktrackingReftypeAnalysis {
423 rlr_env,
424 vlr_env,
425 frag_env,
426 reg_to_ranges_maps,
427 };
428 core_reftypes_analysis(&mut analysis, move_info, reftype_class, reftyped_vregs);
429}