1 use super::{FixedInterval, IntId, Intervals, Mention, MentionMap, VirtualInterval};
2 use crate::{
3 analysis_control_flow::{CFGInfo, InstIxToBlockIxMap},
4 analysis_data_flow::{
5 calc_def_and_use, calc_livein_and_liveout, get_sanitized_reg_uses_for_func, reg_ix_to_reg,
6 reg_to_reg_ix,
7 },
8 data_structures::{BlockIx, InstPoint, RangeFragIx, RangeFragKind, Reg, RegVecsAndBounds},
9 sparse_set::SparseSet,
10 union_find::UnionFind,
11 AnalysisError, Function, RealRegUniverse, RegClass, TypedIxVec,
12 };
13 use log::{debug, info, log_enabled, Level};
14 use smallvec::{smallvec, SmallVec};
15 use std::{fmt, mem};
16
17 #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
18 pub(crate) struct RangeFrag {
19 pub(crate) first: InstPoint,
20 pub(crate) last: InstPoint,
21 pub(crate) mentions: MentionMap,
22 }
23
24 impl fmt::Debug for RangeFrag {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result25 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
26 write!(fmt, "[{:?}; {:?}]", self.first, self.last)
27 }
28 }
29
30 impl RangeFrag {
new<F: Function>( func: &F, bix: BlockIx, first: InstPoint, last: InstPoint, mentions: MentionMap, ) -> (Self, RangeFragMetrics)31 fn new<F: Function>(
32 func: &F,
33 bix: BlockIx,
34 first: InstPoint,
35 last: InstPoint,
36 mentions: MentionMap,
37 ) -> (Self, RangeFragMetrics) {
38 debug_assert!(func.block_insns(bix).len() >= 1);
39 debug_assert!(func.block_insns(bix).contains(first.iix()));
40 debug_assert!(func.block_insns(bix).contains(last.iix()));
41 debug_assert!(first <= last);
42
43 let first_in_block = InstPoint::new_use(func.block_insns(bix).first());
44 let last_in_block = InstPoint::new_def(func.block_insns(bix).last());
45 let kind = match (first == first_in_block, last == last_in_block) {
46 (false, false) => RangeFragKind::Local,
47 (false, true) => RangeFragKind::LiveOut,
48 (true, false) => RangeFragKind::LiveIn,
49 (true, true) => RangeFragKind::Thru,
50 };
51
52 (
53 RangeFrag {
54 first,
55 last,
56 mentions,
57 },
58 RangeFragMetrics { bix, kind },
59 )
60 }
61
62 #[inline(always)]
63 #[cfg(debug_assertions)]
contains(&self, inst: &InstPoint) -> bool64 pub(crate) fn contains(&self, inst: &InstPoint) -> bool {
65 self.first <= *inst && *inst <= self.last
66 }
67 }
68
69 struct RangeFragMetrics {
70 bix: BlockIx,
71 kind: RangeFragKind,
72 }
73
74 pub(crate) struct AnalysisInfo {
75 /// The sanitized per-insn reg-use info.
76 pub(crate) reg_vecs_and_bounds: RegVecsAndBounds,
77 /// All the intervals, fixed or virtual.
78 pub(crate) intervals: Intervals,
79 /// Liveins per block.
80 pub(crate) liveins: TypedIxVec<BlockIx, SparseSet<Reg>>,
81 /// Liveouts per block.
82 pub(crate) liveouts: TypedIxVec<BlockIx, SparseSet<Reg>>,
83 /// Blocks's loop depths.
84 pub(crate) _loop_depth: TypedIxVec<BlockIx, u32>,
85 /// Maps InstIxs to BlockIxs.
86 pub(crate) _inst_to_block_map: InstIxToBlockIxMap,
87 }
88
89 #[inline(never)]
run<F: Function>( func: &F, reg_universe: &RealRegUniverse, ) -> Result<AnalysisInfo, AnalysisError>90 pub(crate) fn run<F: Function>(
91 func: &F,
92 reg_universe: &RealRegUniverse,
93 ) -> Result<AnalysisInfo, AnalysisError> {
94 info!(
95 "run_analysis: begin: {} blocks, {} insns",
96 func.blocks().len(),
97 func.insns().len()
98 );
99
100 // First do control flow analysis. This is (relatively) simple. Note that this can fail, for
101 // various reasons; we propagate the failure if so. Also create the InstIx-to-BlockIx map;
102 // this isn't really control-flow analysis, but needs to be done at some point.
103
104 info!(" run_analysis: begin control flow analysis");
105 let cfg_info = CFGInfo::create(func)?;
106 let inst_to_block_map = InstIxToBlockIxMap::new(func);
107 info!(" run_analysis: end control flow analysis");
108
109 info!(" run_analysis: begin data flow analysis");
110
111 // See `get_sanitized_reg_uses_for_func` for the meaning of "sanitized".
112 let reg_vecs_and_bounds = get_sanitized_reg_uses_for_func(func, reg_universe)
113 .map_err(|reg| AnalysisError::IllegalRealReg(reg))?;
114 assert!(reg_vecs_and_bounds.is_sanitized());
115
116 // Calculate block-local def/use sets.
117 let (def_sets_per_block, use_sets_per_block) =
118 calc_def_and_use(func, ®_vecs_and_bounds, ®_universe);
119 debug_assert!(def_sets_per_block.len() == func.blocks().len() as u32);
120 debug_assert!(use_sets_per_block.len() == func.blocks().len() as u32);
121
122 // Calculate live-in and live-out sets per block, using the traditional
123 // iterate-to-a-fixed-point scheme.
124 // `liveout_sets_per_block` is amended below for return blocks, hence `mut`.
125
126 let (livein_sets_per_block, mut liveout_sets_per_block) = calc_livein_and_liveout(
127 func,
128 &def_sets_per_block,
129 &use_sets_per_block,
130 &cfg_info,
131 ®_universe,
132 );
133 debug_assert!(livein_sets_per_block.len() == func.blocks().len() as u32);
134 debug_assert!(liveout_sets_per_block.len() == func.blocks().len() as u32);
135
136 // Verify livein set of entry block against liveins specified by function (e.g., ABI params).
137 let func_liveins = SparseSet::from_vec(
138 func.func_liveins()
139 .to_vec()
140 .into_iter()
141 .map(|rreg| rreg.to_reg())
142 .collect(),
143 );
144 if !livein_sets_per_block[func.entry_block()].is_subset_of(&func_liveins) {
145 let mut regs = livein_sets_per_block[func.entry_block()].clone();
146 regs.remove(&func_liveins);
147 return Err(AnalysisError::EntryLiveinValues(regs.to_vec()));
148 }
149
150 // Add function liveouts to every block ending in a return.
151 let func_liveouts = SparseSet::from_vec(
152 func.func_liveouts()
153 .to_vec()
154 .into_iter()
155 .map(|rreg| rreg.to_reg())
156 .collect(),
157 );
158 for block in func.blocks() {
159 let last_iix = func.block_insns(block).last();
160 if func.is_ret(last_iix) {
161 liveout_sets_per_block[block].union(&func_liveouts);
162 }
163 }
164
165 info!(" run_analysis: end data flow analysis");
166
167 info!(" run_analysis: begin liveness analysis");
168 let (frag_ixs_per_reg, mut frag_env, frag_metrics_env, vreg_classes) = get_range_frags(
169 func,
170 ®_vecs_and_bounds,
171 ®_universe,
172 &livein_sets_per_block,
173 &liveout_sets_per_block,
174 );
175
176 let (mut fixed_intervals, virtual_intervals) = merge_range_frags(
177 ®_universe,
178 &frag_ixs_per_reg,
179 &mut frag_env,
180 &frag_metrics_env,
181 &cfg_info,
182 &vreg_classes,
183 );
184 info!(" run_analysis: end liveness analysis");
185
186 // Finalize interval construction by doing some last minute sort of the fixed intervals.
187 for fixed in fixed_intervals.iter_mut() {
188 fixed.frags.sort_unstable_by_key(|frag| frag.first);
189 }
190 let intervals = Intervals {
191 virtuals: virtual_intervals,
192 fixeds: fixed_intervals,
193 };
194
195 info!("run_analysis: end");
196
197 Ok(AnalysisInfo {
198 reg_vecs_and_bounds,
199 intervals,
200 liveins: livein_sets_per_block,
201 liveouts: liveout_sets_per_block,
202 _loop_depth: cfg_info.depth_map,
203 _inst_to_block_map: inst_to_block_map,
204 })
205 }
206
207 /// Calculate all the RangeFrags for `bix`. Add them to `out_frags` and
208 /// corresponding metrics data to `out_frag_metrics`. Add to `out_map`, the
209 /// associated RangeFragIxs, segregated by Reg. `bix`, `livein`, `liveout` and
210 /// `rvb` are expected to be valid in the context of the Func `f` (duh!).
211 #[inline(never)]
get_range_frags_for_block<F: Function>( func: &F, rvb: &RegVecsAndBounds, reg_universe: &RealRegUniverse, vreg_classes: &Vec<RegClass>, bix: BlockIx, livein: &SparseSet<Reg>, liveout: &SparseSet<Reg>, visited: &mut Vec<u32>, state: &mut Vec< Option<RangeFrag>>, out_map: &mut Vec<SmallVec<[RangeFragIx; 8]>>, out_frags: &mut Vec<RangeFrag>, out_frag_metrics: &mut Vec<RangeFragMetrics>, )212 fn get_range_frags_for_block<F: Function>(
213 func: &F,
214 rvb: &RegVecsAndBounds,
215 reg_universe: &RealRegUniverse,
216 vreg_classes: &Vec<RegClass>,
217 bix: BlockIx,
218 livein: &SparseSet<Reg>,
219 liveout: &SparseSet<Reg>,
220 // Temporary state reusable across function calls.
221 visited: &mut Vec<u32>,
222 state: &mut Vec</*rreg index, then vreg index, */ Option<RangeFrag>>,
223 // Effectively results.
224 out_map: &mut Vec<SmallVec<[RangeFragIx; 8]>>,
225 out_frags: &mut Vec<RangeFrag>,
226 out_frag_metrics: &mut Vec<RangeFragMetrics>,
227 ) {
228 let mut emit_range_frag =
229 |r: Reg, frag: RangeFrag, frag_metrics: RangeFragMetrics, num_real_regs: u32| {
230 let fix = RangeFragIx::new(out_frags.len() as u32);
231 out_frags.push(frag);
232 out_frag_metrics.push(frag_metrics);
233
234 let out_map_index = reg_to_reg_ix(num_real_regs, r) as usize;
235 out_map[out_map_index].push(fix);
236 };
237
238 // Some handy constants.
239 debug_assert!(func.block_insns(bix).len() >= 1);
240 let first_pt_in_block = InstPoint::new_use(func.block_insns(bix).first());
241 let last_pt_in_block = InstPoint::new_def(func.block_insns(bix).last());
242
243 // Clear the running state.
244 visited.clear();
245
246 let num_real_regs = reg_universe.regs.len() as u32;
247
248 // First, set up `state` as if all of `livein` had been written just prior to the block.
249 for r in livein.iter() {
250 let r_state_ix = reg_to_reg_ix(num_real_regs, *r) as usize;
251 debug_assert!(state[r_state_ix].is_none());
252 state[r_state_ix] = Some(RangeFrag {
253 mentions: MentionMap::new(),
254 first: first_pt_in_block,
255 last: first_pt_in_block,
256 });
257 visited.push(r_state_ix as u32);
258 }
259
260 // Now visit each instruction in turn, examining first the registers it reads, then those it
261 // modifies, and finally those it writes.
262 for iix in func.block_insns(bix) {
263 let bounds_for_iix = &rvb.bounds[iix];
264
265 // Examine reads: they extend an existing RangeFrag to the U point of the reading
266 // insn.
267 for i in bounds_for_iix.uses_start as usize
268 ..bounds_for_iix.uses_start as usize + bounds_for_iix.uses_len as usize
269 {
270 let r = &rvb.vecs.uses[i];
271 let r_state_ix = reg_to_reg_ix(num_real_regs, *r) as usize;
272
273 // There has to be an entry, otherwise we'd do a read of a register not listed in
274 // liveins.
275 let pf = match &mut state[r_state_ix] {
276 None => panic!("get_range_frags_for_block: fail #1"),
277 Some(ref mut pf) => pf,
278 };
279
280 // This the first or subsequent read after a write. Note that the "write" can be
281 // either a real write, or due to the fact that `r` is listed in `livein`. We don't
282 // care here.
283 let new_last = InstPoint::new_use(iix);
284 debug_assert!(pf.last <= new_last);
285 pf.last = new_last;
286
287 // This first loop iterates over all the uses for the first time, so there shouldn't be
288 // any duplicates.
289 debug_assert!(!pf.mentions.iter().any(|tuple| tuple.0 == iix));
290 let mut mention_set = Mention::new();
291 mention_set.add_use();
292 pf.mentions.push((iix, mention_set));
293 }
294
295 // Examine modifies. These are handled almost identically to
296 // reads, except that they extend an existing RangeFrag down to
297 // the D point of the modifying insn.
298 for i in bounds_for_iix.mods_start as usize
299 ..bounds_for_iix.mods_start as usize + bounds_for_iix.mods_len as usize
300 {
301 let r = &rvb.vecs.mods[i];
302 let r_state_ix = reg_to_reg_ix(num_real_regs, *r) as usize;
303
304 // There has to be an entry here too.
305 let pf = match &mut state[r_state_ix] {
306 None => panic!("get_range_frags_for_block: fail #2"),
307 Some(ref mut pf) => pf,
308 };
309
310 // This the first or subsequent modify after a write.
311 let new_last = InstPoint::new_def(iix);
312 debug_assert!(pf.last <= new_last);
313 pf.last = new_last;
314
315 pf.mentions.push((iix, {
316 let mut mention_set = Mention::new();
317 mention_set.add_mod();
318 mention_set
319 }));
320 }
321
322 // Examine writes (but not writes implied by modifies). The general idea is that a write
323 // causes us to terminate the existing RangeFrag, if any, add it to the results,
324 // and start a new frag.
325 for i in bounds_for_iix.defs_start as usize
326 ..bounds_for_iix.defs_start as usize + bounds_for_iix.defs_len as usize
327 {
328 let r = &rvb.vecs.defs[i];
329 let r_state_ix = reg_to_reg_ix(num_real_regs, *r) as usize;
330
331 match &mut state[r_state_ix] {
332 // First mention of a Reg we've never heard of before.
333 // Start a new RangeFrag for it and keep going.
334 None => {
335 let new_pt = InstPoint::new_def(iix);
336 let mut mention_set = Mention::new();
337 mention_set.add_def();
338 state[r_state_ix] = Some(RangeFrag {
339 first: new_pt,
340 last: new_pt,
341 mentions: smallvec![(iix, mention_set)],
342 })
343 }
344
345 // There's already a RangeFrag for `r`. This write will start a new one, so
346 // flush the existing one and note this write.
347 Some(RangeFrag {
348 ref mut first,
349 ref mut last,
350 ref mut mentions,
351 }) => {
352 // Steal the mentions and replace the mutable ref by an empty vector for reuse.
353 let stolen_mentions = mem::replace(mentions, MentionMap::new());
354
355 let (frag, frag_metrics) =
356 RangeFrag::new(func, bix, *first, *last, stolen_mentions);
357 emit_range_frag(*r, frag, frag_metrics, num_real_regs);
358
359 let mut mention_set = Mention::new();
360 mention_set.add_def();
361 mentions.push((iix, mention_set));
362
363 // Reuse the previous entry for this new definition of the same vreg.
364 let new_pt = InstPoint::new_def(iix);
365 *first = new_pt;
366 *last = new_pt;
367 }
368 }
369
370 visited.push(r_state_ix as u32);
371 }
372 }
373
374 // We are at the end of the block. We still have to deal with live-out Regs. We must also
375 // deal with RangeFrag in `state` that are for registers not listed as live-out.
376
377 // Deal with live-out Regs. Treat each one as if it is read just after the block.
378 for r in liveout.iter() {
379 // Remove the entry from `state` so that the following loop doesn't process it again.
380 let r_state_ix = reg_to_reg_ix(num_real_regs, *r) as usize;
381 let entry = mem::replace(&mut state[r_state_ix], None);
382 match entry {
383 None => panic!("get_range_frags_for_block: fail #3"),
384 Some(pf) => {
385 let (frag, frag_metrics) =
386 RangeFrag::new(func, bix, pf.first, last_pt_in_block, pf.mentions);
387 emit_range_frag(*r, frag, frag_metrics, num_real_regs);
388 }
389 }
390 }
391
392 // Finally, round up any remaining RangeFrag left in `state`.
393 for r_state_ix in visited {
394 if let Some(pf) = &mut state[*r_state_ix as usize] {
395 let r = reg_ix_to_reg(reg_universe, vreg_classes, *r_state_ix);
396 let (frag, frag_metrics) = RangeFrag::new(
397 func,
398 bix,
399 pf.first,
400 pf.last,
401 mem::replace(&mut pf.mentions, MentionMap::new()),
402 );
403 emit_range_frag(r, frag, frag_metrics, num_real_regs);
404 state[*r_state_ix as usize] = None;
405 }
406 }
407 }
408
409 #[inline(never)]
get_range_frags<F: Function>( func: &F, rvb: &RegVecsAndBounds, reg_universe: &RealRegUniverse, liveins: &TypedIxVec<BlockIx, SparseSet<Reg>>, liveouts: &TypedIxVec<BlockIx, SparseSet<Reg>>, ) -> ( Vec< SmallVec<[RangeFragIx; 8]>>, Vec<RangeFrag>, Vec<RangeFragMetrics>, Vec< RegClass>, )410 fn get_range_frags<F: Function>(
411 func: &F,
412 rvb: &RegVecsAndBounds,
413 reg_universe: &RealRegUniverse,
414 liveins: &TypedIxVec<BlockIx, SparseSet<Reg>>,
415 liveouts: &TypedIxVec<BlockIx, SparseSet<Reg>>,
416 ) -> (
417 Vec</*rreg index, then vreg index, */ SmallVec<[RangeFragIx; 8]>>,
418 Vec<RangeFrag>,
419 Vec<RangeFragMetrics>,
420 Vec</*vreg index,*/ RegClass>,
421 ) {
422 info!(" get_range_frags: begin");
423 debug_assert!(liveins.len() == func.blocks().len() as u32);
424 debug_assert!(liveouts.len() == func.blocks().len() as u32);
425 debug_assert!(rvb.is_sanitized());
426
427 let mut vreg_classes = vec![RegClass::INVALID; func.get_num_vregs()];
428 for r in rvb
429 .vecs
430 .uses
431 .iter()
432 .chain(rvb.vecs.defs.iter())
433 .chain(rvb.vecs.mods.iter())
434 {
435 if r.is_real() {
436 continue;
437 }
438 let r_ix = r.get_index();
439 let vreg_classes_ptr = &mut vreg_classes[r_ix];
440 if *vreg_classes_ptr == RegClass::INVALID {
441 *vreg_classes_ptr = r.get_class();
442 } else {
443 debug_assert_eq!(*vreg_classes_ptr, r.get_class());
444 }
445 }
446
447 let num_real_regs = reg_universe.regs.len();
448 let num_virtual_regs = vreg_classes.len();
449 let num_regs = num_real_regs + num_virtual_regs;
450
451 // Reused by the function below.
452 let mut tmp_state = vec![None; num_regs];
453 let mut tmp_visited = Vec::with_capacity(32);
454
455 let mut result_map = vec![SmallVec::new(); num_regs];
456 let mut result_frags = Vec::new();
457 let mut result_frag_metrics = Vec::new();
458 for bix in func.blocks() {
459 get_range_frags_for_block(
460 func,
461 &rvb,
462 reg_universe,
463 &vreg_classes,
464 bix,
465 &liveins[bix],
466 &liveouts[bix],
467 &mut tmp_visited,
468 &mut tmp_state,
469 &mut result_map,
470 &mut result_frags,
471 &mut result_frag_metrics,
472 );
473 }
474
475 assert!(tmp_state.len() == num_regs);
476 assert!(result_map.len() == num_regs);
477 assert!(vreg_classes.len() == num_virtual_regs);
478 // This is pretty cheap (once per fn) and any failure will be catastrophic since it means we
479 // may have forgotten some live range fragments. Hence `assert!` and not `debug_assert!`.
480 for state_elem in &tmp_state {
481 assert!(state_elem.is_none());
482 }
483
484 if log_enabled!(Level::Debug) {
485 debug!("");
486 let mut n = 0;
487 for frag in result_frags.iter() {
488 debug!("{:<3?} {:?}", RangeFragIx::new(n), frag);
489 n += 1;
490 }
491
492 debug!("");
493 for (reg_ix, frag_ixs) in result_map.iter().enumerate() {
494 if frag_ixs.len() == 0 {
495 continue;
496 }
497 let reg = reg_ix_to_reg(reg_universe, &vreg_classes, reg_ix as u32);
498 debug!(
499 "frags for {} {:?}",
500 reg.show_with_rru(reg_universe),
501 frag_ixs
502 );
503 }
504 }
505
506 info!(" get_range_frags: end");
507 assert!(result_frags.len() == result_frag_metrics.len());
508
509 (result_map, result_frags, result_frag_metrics, vreg_classes)
510 }
511
512 #[inline(never)]
merge_range_frags( reg_universe: &RealRegUniverse, frag_ix_vec_per_reg: &[SmallVec<[RangeFragIx; 8]>], frag_env: &mut Vec<RangeFrag>, frag_metrics_env: &Vec<RangeFragMetrics>, cfg_info: &CFGInfo, vreg_classes: &Vec< RegClass>, ) -> (Vec<FixedInterval>, Vec<VirtualInterval>)513 fn merge_range_frags(
514 reg_universe: &RealRegUniverse,
515 frag_ix_vec_per_reg: &[SmallVec<[RangeFragIx; 8]>],
516 frag_env: &mut Vec<RangeFrag>,
517 frag_metrics_env: &Vec<RangeFragMetrics>,
518 cfg_info: &CFGInfo,
519 vreg_classes: &Vec</*vreg index,*/ RegClass>,
520 ) -> (Vec<FixedInterval>, Vec<VirtualInterval>) {
521 info!(" merge_range_frags: begin");
522 if log_enabled!(Level::Info) {
523 let mut stats_num_total_incoming_frags = 0;
524 for all_frag_ixs_for_reg in frag_ix_vec_per_reg.iter() {
525 stats_num_total_incoming_frags += all_frag_ixs_for_reg.len();
526 }
527 info!(" in: {} in frag_env", frag_env.len());
528 info!(
529 " in: {} regs containing in total {} frags",
530 frag_ix_vec_per_reg.len(),
531 stats_num_total_incoming_frags
532 );
533 }
534
535 debug_assert!(frag_env.len() == frag_metrics_env.len());
536
537 // Prefill fixed intervals, one per real register.
538 let mut result_fixed = Vec::with_capacity(reg_universe.regs.len() as usize);
539 for rreg in reg_universe.regs.iter() {
540 result_fixed.push(FixedInterval {
541 reg: rreg.0,
542 frags: Vec::new(),
543 });
544 }
545
546 let mut result_virtual = Vec::new();
547
548 let mut triples = Vec::<(RangeFragIx, RangeFragKind, BlockIx)>::new();
549
550 // BEGIN per_reg_loop
551 for (reg_ix, all_frag_ixs_for_reg) in frag_ix_vec_per_reg.iter().enumerate() {
552 let reg = reg_ix_to_reg(reg_universe, vreg_classes, reg_ix as u32);
553
554 let num_reg_frags = all_frag_ixs_for_reg.len();
555
556 // The reg might never have been mentioned at all, especially if it's a real reg.
557 if num_reg_frags == 0 {
558 continue;
559 }
560
561 // Do some shortcutting. First off, if there's only one frag for this reg, we can directly
562 // give it its own live range, and have done.
563 if num_reg_frags == 1 {
564 flush_interval(
565 &mut result_fixed,
566 &mut result_virtual,
567 reg,
568 all_frag_ixs_for_reg,
569 frag_env,
570 );
571 continue;
572 }
573
574 // BEGIN merge `all_frag_ixs_for_reg` entries as much as possible.
575 // but .. if we come across independents (RangeKind::Local), pull them out
576 // immediately.
577 triples.clear();
578
579 // Create `triples`. We will use it to guide the merging phase, but it is immutable there.
580 for fix in all_frag_ixs_for_reg {
581 let frag_metrics = &frag_metrics_env[fix.get() as usize];
582
583 if frag_metrics.kind == RangeFragKind::Local {
584 // This frag is Local (standalone). Give it its own Range and move on. This is an
585 // optimisation, but it's also necessary: the main fragment-merging logic below
586 // relies on the fact that the fragments it is presented with are all either
587 // LiveIn, LiveOut or Thru.
588 flush_interval(
589 &mut result_fixed,
590 &mut result_virtual,
591 reg,
592 &[*fix],
593 frag_env,
594 );
595 continue;
596 }
597
598 // This frag isn't Local (standalone) so we have to process it the slow way.
599 triples.push((*fix, frag_metrics.kind, frag_metrics.bix));
600 }
601
602 let triples_len = triples.len();
603
604 // This is the core of the merging algorithm.
605 //
606 // For each ix@(fix, kind, bix) in `triples` (order unimportant):
607 //
608 // (1) "Merge with blocks that are live 'downstream' from here":
609 // if fix is live-out or live-through:
610 // for b in succs[bix]
611 // for each ix2@(fix2, kind2, bix2) in `triples`
612 // if bix2 == b && kind2 is live-in or live-through:
613 // merge(ix, ix2)
614 //
615 // (2) "Merge with blocks that are live 'upstream' from here":
616 // if fix is live-in or live-through:
617 // for b in preds[bix]
618 // for each ix2@(fix2, kind2, bix2) in `triples`
619 // if bix2 == b && kind2 is live-out or live-through:
620 // merge(ix, ix2)
621 //
622 // `triples` remains unchanged. The equivalence class info is accumulated
623 // in `eclasses_uf` instead. `eclasses_uf` entries are indices into
624 // `triples`.
625 //
626 // Now, you might think it necessary to do both (1) and (2). But no, they
627 // are mutually redundant, since if two blocks are connected by a live
628 // flow from one to the other, then they are also connected in the other
629 // direction. Hence checking one of the directions is enough.
630 let mut eclasses_uf = UnionFind::<usize>::new(triples_len);
631
632 // We have two schemes for group merging, one of which is N^2 in the
633 // length of triples, the other is N-log-N, but with higher constant
634 // factors. Some experimentation with the bz2 test on a Cortex A57 puts
635 // the optimal crossover point between 200 and 300; it's not critical.
636 // Having this protects us against bad behaviour for huge inputs whilst
637 // still being fast for small inputs.
638 if triples_len <= 250 {
639 // The simple way, which is N^2 in the length of `triples`.
640 for (ix, (_fix, kind, bix)) in triples.iter().enumerate() {
641 // Deal with liveness flows outbound from `fix`. Meaning, (1) above.
642 if *kind == RangeFragKind::LiveOut || *kind == RangeFragKind::Thru {
643 for b in cfg_info.succ_map[*bix].iter() {
644 // Visit all entries in `triples` that are for `b`.
645 for (ix2, (_fix2, kind2, bix2)) in triples.iter().enumerate() {
646 if *bix2 != *b || *kind2 == RangeFragKind::LiveOut {
647 continue;
648 }
649 debug_assert!(
650 *kind2 == RangeFragKind::LiveIn || *kind2 == RangeFragKind::Thru
651 );
652 // Now we know that liveness for this reg "flows" from `triples[ix]` to
653 // `triples[ix2]`. So those two frags must be part of the same live
654 // range. Note this.
655 if ix != ix2 {
656 eclasses_uf.union(ix, ix2); // Order of args irrelevant
657 }
658 }
659 }
660 }
661 } // outermost iteration over `triples`
662 } else {
663 // The more complex way, which is N-log-N in the length of `triples`. This is the same
664 // as the simple way, except that the innermost loop, which is a linear search in
665 // `triples` to find entries for some block `b`, is replaced by a binary search. This
666 // means that `triples` first needs to be sorted by block index.
667 triples.sort_unstable_by_key(|(_, _, bix)| *bix);
668
669 for (ix, (_fix, kind, bix)) in triples.iter().enumerate() {
670 // Deal with liveness flows outbound from `fix`. Meaning, (1) above.
671 if *kind == RangeFragKind::LiveOut || *kind == RangeFragKind::Thru {
672 for b in cfg_info.succ_map[*bix].iter() {
673 // Visit all entries in `triples` that are for `b`. Binary search
674 // `triples` to find the lowest-indexed entry for `b`.
675 let mut ix_left = 0;
676 let mut ix_right = triples_len;
677 while ix_left < ix_right {
678 let m = (ix_left + ix_right) >> 1;
679 if triples[m].2 < *b {
680 ix_left = m + 1;
681 } else {
682 ix_right = m;
683 }
684 }
685
686 // It might be that there is no block for `b` in the sequence. That's
687 // legit; it just means that block `bix` jumps to a successor where the
688 // associated register isn't live-in/thru. A failure to find `b` can be
689 // indicated one of two ways:
690 //
691 // * ix_left == triples_len
692 // * ix_left < triples_len and b < triples[ix_left].b
693 //
694 // In both cases I *think* the 'loop_over_entries_for_b below will not do
695 // anything. But this is all a bit hairy, so let's convert the second
696 // variant into the first, so as to make it obvious that the loop won't do
697 // anything.
698
699 // ix_left now holds the lowest index of any `triples` entry for block `b`.
700 // Assert this.
701 if ix_left < triples_len && *b < triples[ix_left].2 {
702 ix_left = triples_len;
703 }
704 if ix_left < triples_len {
705 assert!(ix_left == 0 || triples[ix_left - 1].2 < *b);
706 }
707
708 // ix2 plays the same role as in the quadratic version. ix_left and
709 // ix_right are not used after this point.
710 let mut ix2 = ix_left;
711 loop {
712 let (_fix2, kind2, bix2) = match triples.get(ix2) {
713 None => break,
714 Some(triple) => *triple,
715 };
716 if *b < bix2 {
717 // We've come to the end of the sequence of `b`-blocks.
718 break;
719 }
720 debug_assert!(*b == bix2);
721 if kind2 == RangeFragKind::LiveOut {
722 ix2 += 1;
723 continue;
724 }
725 // Now we know that liveness for this reg "flows" from `triples[ix]` to
726 // `triples[ix2]`. So those two frags must be part of the same live
727 // range. Note this.
728 eclasses_uf.union(ix, ix2);
729 ix2 += 1;
730 }
731
732 if ix2 + 1 < triples_len {
733 debug_assert!(*b < triples[ix2 + 1].2);
734 }
735 }
736 }
737 }
738 }
739
740 // Now `eclasses_uf` contains the results of the merging-search. Visit each of its
741 // equivalence classes in turn, and convert each into a virtual or real live range as
742 // appropriate.
743 let eclasses = eclasses_uf.get_equiv_classes();
744 for leader_triple_ix in eclasses.equiv_class_leaders_iter() {
745 // `leader_triple_ix` is an eclass leader. Enumerate the whole eclass.
746 let mut frag_ixs = SmallVec::<[RangeFragIx; 4]>::new();
747 for triple_ix in eclasses.equiv_class_elems_iter(leader_triple_ix) {
748 frag_ixs.push(triples[triple_ix].0 /*first field is frag ix*/);
749 }
750 flush_interval(
751 &mut result_fixed,
752 &mut result_virtual,
753 reg,
754 &frag_ixs,
755 frag_env,
756 );
757 }
758 // END merge `all_frag_ixs_for_reg` entries as much as possible
759 } // END per reg loop
760
761 info!(" merge_range_frags: end");
762
763 (result_fixed, result_virtual)
764 }
765
766 #[inline(never)]
flush_interval( result_real: &mut Vec<FixedInterval>, result_virtual: &mut Vec<VirtualInterval>, reg: Reg, frag_ixs: &[RangeFragIx], frags: &mut Vec<RangeFrag>, )767 fn flush_interval(
768 result_real: &mut Vec<FixedInterval>,
769 result_virtual: &mut Vec<VirtualInterval>,
770 reg: Reg,
771 frag_ixs: &[RangeFragIx],
772 frags: &mut Vec<RangeFrag>,
773 ) {
774 if reg.is_real() {
775 // Append all the RangeFrags to this fixed interval. They'll get sorted later.
776 result_real[reg.to_real_reg().get_index()]
777 .frags
778 .extend(frag_ixs.iter().map(|&i| {
779 let frag = &mut frags[i.get() as usize];
780 RangeFrag {
781 first: frag.first,
782 last: frag.last,
783 mentions: mem::replace(&mut frag.mentions, MentionMap::new()),
784 }
785 }));
786 return;
787 }
788
789 debug_assert!(reg.is_virtual());
790
791 let (start, end, mentions) = {
792 // Merge all the mentions together.
793 let capacity = frag_ixs
794 .iter()
795 .map(|fix| frags[fix.get() as usize].mentions.len())
796 .fold(0, |a, b| a + b);
797
798 let mut start = InstPoint::max_value();
799 let mut end = InstPoint::min_value();
800
801 // TODO rework this!
802 let mut mentions = MentionMap::with_capacity(capacity);
803 for frag in frag_ixs.iter().map(|fix| &frags[fix.get() as usize]) {
804 mentions.extend(frag.mentions.iter().cloned());
805 start = InstPoint::min(start, frag.first);
806 end = InstPoint::max(end, frag.last);
807 }
808 mentions.sort_unstable_by_key(|tuple| tuple.0);
809
810 // Merge mention set that are at the same instruction.
811 let mut s = 0;
812 let mut e;
813 let mut to_remove = Vec::new();
814 while s < mentions.len() {
815 e = s;
816 while e + 1 < mentions.len() && mentions[s].0 == mentions[e + 1].0 {
817 e += 1;
818 }
819 if s != e {
820 let mut i = s + 1;
821 while i <= e {
822 if mentions[i].1.is_use() {
823 mentions[s].1.add_use();
824 }
825 if mentions[i].1.is_mod() {
826 mentions[s].1.add_mod();
827 }
828 if mentions[i].1.is_def() {
829 mentions[s].1.add_def();
830 }
831 i += 1;
832 }
833 for i in s + 1..=e {
834 to_remove.push(i);
835 }
836 }
837 s = e + 1;
838 }
839
840 for &i in to_remove.iter().rev() {
841 // TODO not efficient.
842 mentions.remove(i);
843 }
844
845 (start, end, mentions)
846 };
847
848 let id = IntId(result_virtual.len());
849 let mut int = VirtualInterval::new(id, reg.to_virtual_reg(), start, end, mentions);
850 int.ancestor = Some(id);
851
852 result_virtual.push(int);
853 }
854