regalloc/
analysis_main.rs

1//! Top level module for all analysis activities.
2
3use 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//=============================================================================
22// Overall analysis return results, for both control- and data-flow analyses.
23// All of these failures refer to various problems with the code that the
24// client (caller) supplied to us.
25
26#[derive(Clone, Debug)]
27pub enum AnalysisError {
28    /// A critical edge from "from" to "to" has been found, and should have been
29    /// removed by the caller in the first place.
30    CriticalEdge { from: BlockIx, to: BlockIx },
31
32    /// Some values in the entry block are live in to the function, but are not
33    /// declared as such.
34    EntryLiveinValues(Vec<Reg>),
35
36    /// The incoming code has an explicit or implicit mention (use, def or mod)
37    /// of a real register, which either (1) isn't listed in the universe at
38    /// all, or (2) is one of the `suggested_scratch` registers in the universe.
39    /// (1) isn't allowed because the client must mention *all* real registers
40    /// in the universe.  (2) isn't allowed because the client promises to us
41    /// that the `suggested_scratch` registers really are completely unused in
42    /// the incoming code, so that the allocator can use them at literally any
43    /// point it wants.
44    IllegalRealReg(RealReg),
45
46    /// At least one block is dead.
47    UnreachableBlocks,
48
49    /// Implementation limits exceeded.  The incoming function is too big.  It
50    /// may contain at most 1 million basic blocks and 16 million instructions.
51    ImplementationLimitsExceeded,
52
53    /// Linear scan requires that if a block ends with a control flow
54    /// instruction that has at least one register mention (use, mod or def),
55    /// then the successor blocks must have a single predecessor.
56    ///
57    /// In practice, this means that users should consider associated edges to
58    /// be "critical" and split them (and maybe remove dead blocks afterwards).
59    ///
60    /// For details, see the comment in linear_scan::analysis generating this
61    /// error.
62    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
102//=============================================================================
103// Top level for all analysis activities.
104
105pub struct AnalysisInfo {
106    /// The sanitized per-insn reg-use info
107    pub(crate) reg_vecs_and_bounds: RegVecsAndBounds,
108    /// The real-reg live ranges
109    pub(crate) real_ranges: TypedIxVec<RealRangeIx, RealRange>,
110    /// The virtual-reg live ranges
111    pub(crate) virtual_ranges: TypedIxVec<VirtualRangeIx, VirtualRange>,
112    /// The fragment table
113    pub(crate) range_frags: TypedIxVec<RangeFragIx, RangeFrag>,
114    /// The fragment metrics table
115    pub(crate) range_metrics: TypedIxVec<RangeFragIx, RangeFragMetrics>,
116    /// Estimated execution frequency per block
117    pub(crate) estimated_frequencies: DepthBasedFrequencies,
118    /// Maps InstIxs to BlockIxs
119    pub(crate) inst_to_block_map: InstIxToBlockIxMap,
120    /// Maps from RealRegs to sets of RealRanges and VirtualRegs to sets of VirtualRanges
121    /// (all operating on indices, not the actual objects).  This is only generated in
122    /// situations where we need it, hence the `Option`.
123    pub(crate) reg_to_ranges_maps: Option<RegToRangesMaps>,
124    /// Information about registers connected by moves.  This is only generated in situations
125    /// where we need it, hence the `Option`.
126    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>, // as supplied by the client
137) -> 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    // LSRA uses its own analysis.
146    assert!(!client_wants_stackmaps || algorithm != AlgorithmWithDefaults::LinearScan);
147
148    trace!("  run_analysis: begin control flow analysis");
149
150    // First do control flow analysis.  This is (relatively) simple.  Note that
151    // this can fail, for various reasons; we propagate the failure if so.
152    let cfg_info = CFGInfo::create(func)?;
153
154    // Create the InstIx-to-BlockIx map.  This isn't really control-flow
155    // analysis, but needs to be done at some point.
156    let inst_to_block_map = InstIxToBlockIxMap::new(func);
157
158    // Annotate each Block with its estimated execution frequency.
159    let estimated_frequencies = DepthBasedFrequencies::new(func, &cfg_info);
160
161    trace!("  run_analysis: end control flow analysis");
162
163    // Now perform dataflow analysis.  This is somewhat more complex.
164    trace!("  run_analysis: begin data flow analysis");
165
166    // See `get_sanitized_reg_uses_for_func` for the meaning of "sanitized".
167    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    // Calculate block-local def/use sets.
172    let (def_sets_per_block, use_sets_per_block) =
173        calc_def_and_use(func, &reg_vecs_and_bounds, &reg_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    // Calculate live-in and live-out sets per block, using the traditional
178    // iterate-to-a-fixed-point scheme.
179
180    // `liveout_sets_per_block` is amended below for return blocks, hence `mut`.
181    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        &reg_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    // Verify livein set of entry block against liveins specified by function
192    // (e.g., ABI params).
193    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    // Add function liveouts to every block ending in a return.
207    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    // Dataflow analysis is now complete.  Now compute the virtual and real live
224    // ranges, in two steps: (1) compute RangeFrags, and (2) merge them
225    // together, guided by flow and liveness info, so as to create the final
226    // VirtualRanges and RealRanges.
227    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        &reg_vecs_and_bounds,
232        &reg_universe,
233        &livein_sets_per_block,
234        &liveout_sets_per_block,
235    );
236
237    // These have to be mut because they may get changed below by the call to
238    // `to_reftypes_analysis`.
239    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        &reg_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(&reg_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    // Now a bit of auxiliary info collection, which isn't really either control- or data-flow
272    // analysis.
273
274    // For BT and/or reftypes, we'll also need the reg-to-ranges maps and information about moves.
275    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                    &reg_universe,
281                    &rlr_env,
282                    &vlr_env,
283                )),
284                Some(collect_move_info(
285                    func,
286                    &reg_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(), /* safe because of logic just above */
303            &move_info.as_ref().unwrap(),         /* ditto */
304            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
325/// A small wrapper for estimated execution frequencies, based on the block's loop depth.
326pub(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
354/// Implementation of the reftype analysis for the backtracking algorithm.
355struct 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    // From dataflow/liveness analysis.  Modified by setting their is_ref bit.
412    rlr_env: &mut TypedIxVec<RealRangeIx, RealRange>,
413    vlr_env: &mut TypedIxVec<VirtualRangeIx, VirtualRange>,
414    // From dataflow analysis
415    frag_env: &TypedIxVec<RangeFragIx, RangeFrag>,
416    reg_to_ranges_maps: &RegToRangesMaps,
417    move_info: &MoveInfo,
418    // As supplied by the client
419    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}