1 use super::{
2 last_use, next_use, IntId, Intervals, Mention, MentionMap, OptimalSplitStrategy, RegUses,
3 Statistics, VirtualInterval,
4 };
5 use crate::{
6 data_structures::{InstPoint, Point, RegVecsAndBounds},
7 Function, InstIx, LinearScanOptions, RealReg, RealRegUniverse, Reg, RegAllocError, SpillSlot,
8 VirtualReg, NUM_REG_CLASSES,
9 };
10
11 use log::{debug, info, log_enabled, trace, Level};
12 use rustc_hash::FxHashMap as HashMap;
13 use smallvec::SmallVec;
14 use std::collections::BinaryHeap;
15 use std::{cmp, cmp::Ordering, fmt};
16
17 macro_rules! lsra_assert {
18 ($arg:expr) => {
19 #[cfg(debug_assertions)]
20 debug_assert!($arg);
21 };
22
23 ($arg:expr, $text:expr) => {
24 #[cfg(debug_assertions)]
25 debug_assert!($arg, $text);
26 };
27 }
28
29 #[derive(Clone, Copy, PartialEq)]
30 enum ActiveInt {
31 Virtual(IntId),
32 Fixed((RealReg, usize)),
33 }
34
35 impl fmt::Debug for ActiveInt {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result36 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
37 match self {
38 ActiveInt::Virtual(id) => write!(fmt, "virtual({:?})", id),
39 ActiveInt::Fixed((rreg, _)) => write!(fmt, "real({:?})", rreg),
40 }
41 }
42 }
43
44 struct ActivityTracker {
45 /// Intervals that are covering the current interval's start position.
46 /// TODO Invariant: they always have a register attached to them.
47 active: Vec<ActiveInt>,
48
49 /// Intervals that are not covering but end after the current interval's start position.
50 /// None means that the interval may have fragments, but they all live after the current
51 /// position.
52 /// TODO Invariant: they're all fixed registers, so they must have a register attached to them.
53 inactive: Vec<(RealReg, usize)>,
54 }
55
56 impl ActivityTracker {
new(intervals: &Intervals) -> Self57 fn new(intervals: &Intervals) -> Self {
58 let mut inactive = Vec::with_capacity(intervals.fixeds.len());
59 for fixed in &intervals.fixeds {
60 if !fixed.frags.is_empty() {
61 inactive.push((fixed.reg, 0))
62 }
63 }
64
65 Self {
66 active: Vec::new(),
67 inactive,
68 }
69 }
70
set_active(&mut self, id: IntId)71 fn set_active(&mut self, id: IntId) {
72 self.active.push(ActiveInt::Virtual(id));
73 }
74
update(&mut self, start: InstPoint, stats: &mut Option<Statistics>, intervals: &Intervals)75 fn update(&mut self, start: InstPoint, stats: &mut Option<Statistics>, intervals: &Intervals) {
76 // From active, only possible transitions are to active or expired.
77 // From inactive, only possible transitions are to inactive, active or expired.
78 // => active has an upper bound.
79 // => inactive only shrinks.
80 let mut to_delete: SmallVec<[usize; 16]> = SmallVec::new();
81 let mut new_inactive: SmallVec<[(RealReg, usize); 16]> = SmallVec::new();
82
83 for (i, id) in self.active.iter_mut().enumerate() {
84 match id {
85 ActiveInt::Virtual(int_id) => {
86 let int = intervals.get(*int_id);
87
88 if int.location.spill().is_some() {
89 // TODO these shouldn't appear here...
90 to_delete.push(i);
91 continue;
92 }
93 //debug_assert!(int.location.spill().is_none(), "active int must have a reg");
94
95 if int.end < start {
96 // It's expired, forget about it.
97 to_delete.push(i);
98 } else {
99 // Stays active.
100 lsra_assert!(int.covers(start), "no active to inactive transition");
101 }
102 }
103
104 ActiveInt::Fixed((rreg, ref mut fix)) => {
105 // Possible transitions: active => { active, inactive, expired }.
106 let frags = &intervals.fixeds[rreg.get_index()].frags;
107
108 // Fast-forward to the first fragment that contains or is after start.
109 while *fix < frags.len() && start > frags[*fix].last {
110 *fix += 1;
111 }
112
113 if *fix == frags.len() {
114 // It expired, remove it from the active list.
115 to_delete.push(i);
116 } else if start < frags[*fix].first {
117 // It is now inactive.
118 lsra_assert!(!frags[*fix].contains(&start));
119 new_inactive.push((*rreg, *fix));
120 to_delete.push(i);
121 } else {
122 // Otherwise, it's still active.
123 lsra_assert!(frags[*fix].contains(&start));
124 }
125 }
126 }
127 }
128
129 for &i in to_delete.iter().rev() {
130 self.active.swap_remove(i);
131 }
132 to_delete.clear();
133
134 for (i, (rreg, fix)) in self.inactive.iter_mut().enumerate() {
135 // Possible transitions: inactive => { active, inactive, expired }.
136 let frags = &intervals.fixeds[rreg.get_index()].frags;
137
138 // Fast-forward to the first fragment that contains or is after start.
139 while *fix < frags.len() && start > frags[*fix].last {
140 *fix += 1;
141 }
142
143 if *fix == frags.len() {
144 // It expired, remove it from the inactive list.
145 to_delete.push(i);
146 } else if start >= frags[*fix].first {
147 // It is now active.
148 lsra_assert!(frags[*fix].contains(&start));
149 self.active.push(ActiveInt::Fixed((*rreg, *fix)));
150 to_delete.push(i);
151 } else {
152 // Otherwise it remains inactive.
153 lsra_assert!(!frags[*fix].contains(&start));
154 }
155 }
156
157 for &i in to_delete.iter().rev() {
158 self.inactive.swap_remove(i);
159 }
160 self.inactive.extend(new_inactive.into_vec());
161
162 trace!("active:");
163 for aid in &self.active {
164 match aid {
165 ActiveInt::Virtual(id) => {
166 trace!(" {}", intervals.get(*id));
167 }
168 ActiveInt::Fixed((real_reg, _frag)) => {
169 trace!(" {}", intervals.fixeds[real_reg.get_index()]);
170 }
171 }
172 }
173 trace!("inactive:");
174 for &(rreg, fix) in &self.inactive {
175 trace!(
176 " {:?} {:?}",
177 rreg,
178 intervals.fixeds[rreg.get_index()].frags[fix]
179 );
180 }
181 trace!("end update state");
182
183 stats.as_mut().map(|stats| {
184 stats.peak_active = usize::max(stats.peak_active, self.active.len());
185 stats.peak_inactive = usize::max(stats.peak_inactive, self.inactive.len());
186 });
187 }
188 }
189
run<F: Function>( opts: &LinearScanOptions, func: &F, reg_uses: &RegVecsAndBounds, reg_universe: &RealRegUniverse, scratches_by_rc: &Vec<Option<RealReg>>, intervals: Intervals, stats: Option<Statistics>, ) -> Result<(Intervals, u32), RegAllocError>190 pub(crate) fn run<F: Function>(
191 opts: &LinearScanOptions,
192 func: &F,
193 reg_uses: &RegVecsAndBounds,
194 reg_universe: &RealRegUniverse,
195 scratches_by_rc: &Vec<Option<RealReg>>,
196 intervals: Intervals,
197 stats: Option<Statistics>,
198 ) -> Result<(Intervals, u32), RegAllocError> {
199 let mut state = State::new(opts, func, ®_uses, intervals, stats);
200 let mut reusable = ReusableState::new(reg_universe, &scratches_by_rc);
201
202 #[cfg(debug_assertions)]
203 let mut prev_start = InstPoint::min_value();
204
205 while let Some(id) = state.next_unhandled() {
206 info!("main loop: allocating {}", state.intervals.get(id));
207
208 #[cfg(debug_assertions)]
209 {
210 let int = state.intervals.get(id);
211 debug_assert!(prev_start <= int.start, "main loop must make progress");
212 prev_start = int.start;
213 }
214
215 if state.intervals.get(id).location.is_none() {
216 let int = state.intervals.get(id);
217
218 state
219 .activity
220 .update(int.start, &mut state.stats, &state.intervals);
221
222 let ok = try_allocate_reg(&mut reusable, id, &mut state);
223 if !ok {
224 allocate_blocked_reg(&mut reusable, id, &mut state)?;
225 }
226
227 if state.intervals.get(id).location.reg().is_some() {
228 state.activity.set_active(id);
229 }
230
231 // Reset reusable state.
232 reusable.computed_inactive = false;
233 }
234
235 debug!("");
236 }
237
238 if log_enabled!(Level::Debug) {
239 debug!("allocation results (in order):");
240 for int in state.intervals.virtuals.iter() {
241 debug!("{}", int);
242 }
243 debug!("");
244 }
245
246 Ok((state.intervals, state.next_spill_slot.get()))
247 }
248
249 /// A mapping from real reg to some T.
250 #[derive(Clone)]
251 struct RegisterMapping<T> {
252 offset: usize,
253 regs: Vec<(RealReg, T)>,
254 scratch: Option<RealReg>,
255 initial_value: T,
256 reg_class_index: usize,
257 }
258
259 impl<T: Copy> RegisterMapping<T> {
with_default( reg_class_index: usize, reg_universe: &RealRegUniverse, scratch: Option<RealReg>, initial_value: T, ) -> Self260 fn with_default(
261 reg_class_index: usize,
262 reg_universe: &RealRegUniverse,
263 scratch: Option<RealReg>,
264 initial_value: T,
265 ) -> Self {
266 let mut regs = Vec::new();
267 let mut offset = 0;
268 // Collect all the registers for the current class.
269 if let Some(ref info) = reg_universe.allocable_by_class[reg_class_index] {
270 lsra_assert!(info.first <= info.last);
271 offset = info.first;
272 for reg in ®_universe.regs[info.first..=info.last] {
273 lsra_assert!(regs.len() == reg.0.get_index() - offset);
274 regs.push((reg.0, initial_value));
275 }
276 };
277 Self {
278 offset,
279 regs,
280 scratch,
281 initial_value,
282 reg_class_index,
283 }
284 }
285
clear(&mut self)286 fn clear(&mut self) {
287 for reg in self.regs.iter_mut() {
288 reg.1 = self.initial_value;
289 }
290 }
291
iter<'a>(&'a self) -> RegisterMappingIter<T>292 fn iter<'a>(&'a self) -> RegisterMappingIter<T> {
293 RegisterMappingIter {
294 iter: self.regs.iter(),
295 scratch: self.scratch,
296 }
297 }
298 }
299
300 struct RegisterMappingIter<'a, T: Copy> {
301 iter: std::slice::Iter<'a, (RealReg, T)>,
302 scratch: Option<RealReg>,
303 }
304
305 impl<'a, T: Copy> std::iter::Iterator for RegisterMappingIter<'a, T> {
306 type Item = &'a (RealReg, T);
next(&mut self) -> Option<Self::Item>307 fn next(&mut self) -> Option<Self::Item> {
308 match self.iter.next() {
309 Some(pair) => {
310 if Some(pair.0) == self.scratch {
311 // Skip to the next one.
312 self.iter.next()
313 } else {
314 Some(pair)
315 }
316 }
317 None => None,
318 }
319 }
320 }
321
322 impl<T> std::ops::Index<RealReg> for RegisterMapping<T> {
323 type Output = T;
index(&self, rreg: RealReg) -> &Self::Output324 fn index(&self, rreg: RealReg) -> &Self::Output {
325 lsra_assert!(
326 rreg.get_class() as usize == self.reg_class_index,
327 "trying to index a reg from the wrong class"
328 );
329 lsra_assert!(Some(rreg) != self.scratch, "trying to use the scratch");
330 &self.regs[rreg.get_index() - self.offset].1
331 }
332 }
333
334 impl<T> std::ops::IndexMut<RealReg> for RegisterMapping<T> {
index_mut(&mut self, rreg: RealReg) -> &mut Self::Output335 fn index_mut(&mut self, rreg: RealReg) -> &mut Self::Output {
336 lsra_assert!(
337 rreg.get_class() as usize == self.reg_class_index,
338 "trying to index a reg from the wrong class"
339 );
340 lsra_assert!(Some(rreg) != self.scratch, "trying to use the scratch");
341 &mut self.regs[rreg.get_index() - self.offset].1
342 }
343 }
344
345 // State management.
346
347 /// Parts of state just reused for recycling memory.
348 struct ReusableState {
349 inactive_intersecting: Vec<(RealReg, InstPoint)>,
350 computed_inactive: bool,
351 reg_to_instpoint_1: Vec<RegisterMapping<InstPoint>>,
352 reg_to_instpoint_2: Vec<RegisterMapping<InstPoint>>,
353 }
354
355 impl ReusableState {
new(reg_universe: &RealRegUniverse, scratches: &[Option<RealReg>]) -> Self356 fn new(reg_universe: &RealRegUniverse, scratches: &[Option<RealReg>]) -> Self {
357 let mut reg_to_instpoint_1 = Vec::with_capacity(NUM_REG_CLASSES);
358
359 for i in 0..NUM_REG_CLASSES {
360 let scratch = scratches[i];
361 reg_to_instpoint_1.push(RegisterMapping::with_default(
362 i,
363 reg_universe,
364 scratch,
365 InstPoint::max_value(),
366 ));
367 }
368
369 let reg_to_instpoint_2 = reg_to_instpoint_1.clone();
370
371 Self {
372 inactive_intersecting: Vec::new(),
373 computed_inactive: false,
374 reg_to_instpoint_1,
375 reg_to_instpoint_2,
376 }
377 }
378 }
379
380 /// A small pair containing the interval id and the instruction point of an interval that is still
381 /// to be allocated, to be stored in the unhandled list of intervals.
382 struct IntervalStart(IntId, InstPoint);
383
384 impl cmp::PartialEq for IntervalStart {
385 #[inline(always)]
eq(&self, other: &Self) -> bool386 fn eq(&self, other: &Self) -> bool {
387 self.0 == other.0
388 }
389 }
390 impl cmp::Eq for IntervalStart {}
391
392 impl cmp::PartialOrd for IntervalStart {
393 #[inline(always)]
partial_cmp(&self, other: &Self) -> Option<Ordering>394 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
395 // Note: we want a reverse ordering on start positions, so that we have a MinHeap and not a
396 // MaxHeap in UnhandledIntervals.
397 other.1.partial_cmp(&self.1)
398 }
399 }
400
401 impl cmp::Ord for IntervalStart {
402 #[inline(always)]
cmp(&self, other: &Self) -> Ordering403 fn cmp(&self, other: &Self) -> Ordering {
404 self.partial_cmp(other).unwrap()
405 }
406 }
407
408 struct UnhandledIntervals {
409 heap: BinaryHeap<IntervalStart>,
410 }
411
412 impl UnhandledIntervals {
new() -> Self413 fn new() -> Self {
414 Self {
415 heap: BinaryHeap::with_capacity(16),
416 }
417 }
418
419 /// Insert a virtual interval that's unallocated in the list of unhandled intervals.
420 ///
421 /// This relies on the fact that unhandled intervals's start positions can't change over time.
insert(&mut self, id: IntId, intervals: &Intervals)422 fn insert(&mut self, id: IntId, intervals: &Intervals) {
423 self.heap.push(IntervalStart(id, intervals.get(id).start))
424 }
425
426 /// Get the new unhandled interval, in start order.
next_unhandled(&mut self, _intervals: &Intervals) -> Option<IntId>427 fn next_unhandled(&mut self, _intervals: &Intervals) -> Option<IntId> {
428 self.heap.pop().map(|entry| {
429 let ret = entry.0;
430 lsra_assert!(_intervals.get(ret).start == entry.1);
431 ret
432 })
433 }
434 }
435
436 /// State structure, which can be cleared between different calls to register allocation.
437 /// TODO: split this into clearable fields and non-clearable fields.
438 struct State<'a, F: Function> {
439 func: &'a F,
440 reg_uses: &'a RegUses,
441 opts: &'a LinearScanOptions,
442
443 intervals: Intervals,
444
445 /// Intervals that are starting after the current interval's start position.
446 unhandled: UnhandledIntervals,
447
448 /// Next available spill slot.
449 next_spill_slot: SpillSlot,
450
451 /// Maps given virtual registers to the spill slots they should be assigned
452 /// to.
453 spill_map: HashMap<VirtualReg, SpillSlot>,
454
455 activity: ActivityTracker,
456 stats: Option<Statistics>,
457 }
458
459 impl<'a, F: Function> State<'a, F> {
new( opts: &'a LinearScanOptions, func: &'a F, reg_uses: &'a RegUses, intervals: Intervals, stats: Option<Statistics>, ) -> Self460 fn new(
461 opts: &'a LinearScanOptions,
462 func: &'a F,
463 reg_uses: &'a RegUses,
464 intervals: Intervals,
465 stats: Option<Statistics>,
466 ) -> Self {
467 let mut unhandled = UnhandledIntervals::new();
468 for int in intervals.virtuals.iter() {
469 unhandled.insert(int.id, &intervals);
470 }
471
472 let activity = ActivityTracker::new(&intervals);
473
474 Self {
475 func,
476 reg_uses,
477 opts,
478 intervals,
479 unhandled,
480 next_spill_slot: SpillSlot::new(0),
481 spill_map: HashMap::default(),
482 stats,
483 activity,
484 }
485 }
486
next_unhandled(&mut self) -> Option<IntId>487 fn next_unhandled(&mut self) -> Option<IntId> {
488 self.unhandled.next_unhandled(&self.intervals)
489 }
insert_unhandled(&mut self, id: IntId)490 fn insert_unhandled(&mut self, id: IntId) {
491 self.unhandled.insert(id, &self.intervals);
492 }
493
spill(&mut self, id: IntId)494 fn spill(&mut self, id: IntId) {
495 let int = self.intervals.get(id);
496 debug_assert!(int.location.spill().is_none(), "already spilled");
497 debug!("spilling {:?}", id);
498
499 let vreg = int.vreg;
500 let spill_slot = if let Some(spill_slot) = self.spill_map.get(&vreg) {
501 *spill_slot
502 } else {
503 let size_slot = self.func.get_spillslot_size(vreg.get_class(), vreg);
504 let spill_slot = self.next_spill_slot.round_up(size_slot);
505 self.next_spill_slot = self.next_spill_slot.inc(1);
506 self.spill_map.insert(vreg, spill_slot);
507 spill_slot
508 };
509
510 self.intervals.set_spill(id, spill_slot);
511 }
512 }
513
514 #[inline(never)]
lazy_compute_inactive( intervals: &Intervals, activity: &ActivityTracker, cur_id: IntId, inactive_intersecting: &mut Vec<(RealReg, InstPoint)>, computed_inactive: &mut bool, )515 fn lazy_compute_inactive(
516 intervals: &Intervals,
517 activity: &ActivityTracker,
518 cur_id: IntId,
519 inactive_intersecting: &mut Vec<(RealReg, InstPoint)>,
520 computed_inactive: &mut bool,
521 ) {
522 if *computed_inactive {
523 return;
524 }
525 inactive_intersecting.clear();
526
527 let int = intervals.get(cur_id);
528 let reg_class = int.vreg.get_class();
529
530 for &(rreg, fix) in &activity.inactive {
531 if rreg.get_class() != reg_class {
532 continue;
533 }
534
535 let frags = &intervals.fixeds[rreg.get_index()].frags;
536 let mut i = fix;
537 while let Some(ref frag) = frags.get(i) {
538 if frag.first > int.end {
539 break;
540 }
541 if frag.first >= int.start {
542 inactive_intersecting.push((rreg, frag.first));
543 break;
544 }
545 i += 1;
546 }
547 }
548
549 *computed_inactive = true;
550 }
551
552 /// Transitions intervals from active/inactive into active/inactive/handled.
553 ///
554 /// An interval tree is stored in the state, containing all the active and
555 /// inactive intervals. The comparison key is the interval's start point.
556 ///
557 /// A state update consists in the following. We consider the next interval to
558 /// allocate, and in particular its start point S.
559 ///
560 /// 1. remove all the active/inactive intervals that have expired, i.e. their
561 /// end point is before S.
562 /// 2. reconsider active/inactive intervals:
563 /// - if they contain S, they become (or stay) active.
564 /// - otherwise, they become (or stay) inactive.
565 ///
566 /// Item 1 is easy to implement, and fast enough.
567 ///
568 /// Item 2 is a bit trickier. While we could just call `Intervals::covers` for
569 /// each interval on S, this is quite expensive. In addition to this, it happens
570 /// that most intervals are inactive. This is explained by the fact that linear
571 /// scan can create large intervals, if a value is used much later after it's
572 /// been created, *according to the block ordering*.
573 ///
574 /// For each interval, we remember the last active fragment, or the first
575 /// inactive fragment that starts after S. This makes search really fast:
576 ///
577 /// - if the considered (active or inactive) interval start is before S, then we
578 /// should look more precisely if it's active or inactive. This might include
579 /// seeking to the next fragment that contains S.
580 /// - otherwise, if the considered interval start is *after* S, then it means
581 /// this interval, as well as all the remaining ones in the interval tree (since
582 /// they're sorted by starting position) are inactive, and we can escape the
583 /// loop eagerly.
584 ///
585 /// The escape for inactive intervals make this function overall cheap.
586
587 /// Naive heuristic to select a register when we're not aware of any conflict.
588 /// Currently, it chooses the register with the furthest next use.
589 #[inline(never)]
select_naive_reg<F: Function>( reusable: &mut ReusableState, state: &mut State<F>, id: IntId, ) -> Option<(RealReg, InstPoint)>590 fn select_naive_reg<F: Function>(
591 reusable: &mut ReusableState,
592 state: &mut State<F>,
593 id: IntId,
594 ) -> Option<(RealReg, InstPoint)> {
595 let reg_class = state.intervals.get(id).vreg.get_class();
596 let free_until_pos = &mut reusable.reg_to_instpoint_1[reg_class as usize];
597 free_until_pos.clear();
598
599 let mut num_free = usize::max(1, free_until_pos.regs.len()) - 1;
600
601 // All registers currently in use are blocked.
602 for &aid in &state.activity.active {
603 let reg = match aid {
604 ActiveInt::Virtual(int_id) => {
605 if let Some(reg) = state.intervals.get(int_id).location.reg() {
606 reg
607 } else {
608 continue;
609 }
610 }
611 ActiveInt::Fixed((real_reg, _)) => real_reg,
612 };
613
614 if reg.get_class() == reg_class {
615 free_until_pos[reg] = InstPoint::min_value();
616 num_free -= 1;
617 }
618 }
619
620 // Shortcut: if all the registers are taken, don't even bother.
621 if num_free == 0 {
622 lsra_assert!(!free_until_pos
623 .iter()
624 .any(|pair| pair.1 != InstPoint::min_value()));
625 return None;
626 }
627
628 // All registers that would be used at the same time as the current interval
629 // are partially blocked, up to the point when they start being used.
630 lazy_compute_inactive(
631 &state.intervals,
632 &state.activity,
633 id,
634 &mut reusable.inactive_intersecting,
635 &mut reusable.computed_inactive,
636 );
637
638 for &(reg, intersect_at) in reusable.inactive_intersecting.iter() {
639 if intersect_at < free_until_pos[reg] {
640 free_until_pos[reg] = intersect_at;
641 }
642 }
643
644 // Find the register with the furthest next use, if there's any.
645 let mut best_reg = None;
646 let mut best_pos = InstPoint::min_value();
647 for &(reg, pos) in free_until_pos.iter() {
648 if pos > best_pos {
649 best_pos = pos;
650 best_reg = Some(reg);
651 }
652 }
653
654 best_reg.and_then(|reg| Some((reg, best_pos)))
655 }
656
657 #[inline(never)]
try_allocate_reg<F: Function>( reusable: &mut ReusableState, id: IntId, state: &mut State<F>, ) -> bool658 fn try_allocate_reg<F: Function>(
659 reusable: &mut ReusableState,
660 id: IntId,
661 state: &mut State<F>,
662 ) -> bool {
663 state
664 .stats
665 .as_mut()
666 .map(|stats| stats.num_try_allocate_reg += 1);
667
668 let (best_reg, best_pos) = if let Some(solution) = select_naive_reg(reusable, state, id) {
669 solution
670 } else {
671 debug!("try_allocate_reg: all registers taken, need to spill.");
672 return false;
673 };
674 debug!(
675 "try_allocate_reg: best register {:?} has next use at {:?}",
676 best_reg, best_pos
677 );
678
679 if best_pos <= state.intervals.get(id).end {
680 if !state.opts.partial_split || !try_split_regs(state, id, best_pos) {
681 return false;
682 }
683 }
684
685 // At least a partial match: allocate.
686 debug!(
687 "{:?}: {:?} <- {:?}",
688 id,
689 state.intervals.get(id).vreg,
690 best_reg
691 );
692 state.intervals.set_reg(id, best_reg);
693
694 state
695 .stats
696 .as_mut()
697 .map(|stats| stats.num_try_allocate_reg_success += 1);
698
699 true
700 }
701
702 #[inline(never)]
allocate_blocked_reg<F: Function>( reusable: &mut ReusableState, cur_id: IntId, state: &mut State<F>, ) -> Result<(), RegAllocError>703 fn allocate_blocked_reg<F: Function>(
704 reusable: &mut ReusableState,
705 cur_id: IntId,
706 state: &mut State<F>,
707 ) -> Result<(), RegAllocError> {
708 // If the current interval has no uses, spill it directly.
709 let first_use = match next_use(
710 &state.intervals.get(cur_id),
711 InstPoint::min_value(),
712 &state.reg_uses,
713 ) {
714 Some(u) => u,
715 None => {
716 state.spill(cur_id);
717 return Ok(());
718 }
719 };
720
721 let (start_pos, reg_class) = {
722 let int = state.intervals.get(cur_id);
723 (int.start, int.vreg.get_class())
724 };
725
726 // Note: in this function, "use" isn't just a use as in use-def; it really
727 // means a mention, so either a use or a definition.
728 //
729 // 1. Compute all the positions of next uses for registers of active intervals
730 // and inactive intervals that might intersect with the current one.
731 // 2. Then use this to select the interval with the further next use.
732 // 3. Spill either the current interval or active/inactive intervals with the
733 // selected register.
734 // 4. Make sure that the current interval doesn't intersect with the fixed
735 // interval for the selected register.
736
737 // Step 1: compute all the next use positions.
738 let next_use_pos = &mut reusable.reg_to_instpoint_1[reg_class as usize];
739 next_use_pos.clear();
740
741 let block_pos = &mut reusable.reg_to_instpoint_2[reg_class as usize];
742 block_pos.clear();
743
744 trace!(
745 "allocate_blocked_reg: searching reg with next use after {:?}",
746 start_pos
747 );
748
749 for &aid in &state.activity.active {
750 match aid {
751 ActiveInt::Virtual(int_id) => {
752 let int = state.intervals.get(int_id);
753 if int.vreg.get_class() != reg_class {
754 continue;
755 }
756 if let Some(reg) = int.location.reg() {
757 if next_use_pos[reg] != InstPoint::min_value() {
758 if let Some(next_use) =
759 next_use(&state.intervals.get(int_id), start_pos, &state.reg_uses)
760 {
761 next_use_pos[reg] = InstPoint::min(next_use_pos[reg], next_use);
762 }
763 }
764 }
765 }
766
767 ActiveInt::Fixed((reg, _frag)) => {
768 if reg.get_class() == reg_class {
769 block_pos[reg] = InstPoint::min_value();
770 next_use_pos[reg] = InstPoint::min_value();
771 }
772 }
773 }
774 }
775
776 lazy_compute_inactive(
777 &state.intervals,
778 &state.activity,
779 cur_id,
780 &mut reusable.inactive_intersecting,
781 &mut reusable.computed_inactive,
782 );
783
784 for &(reg, intersect_pos) in &reusable.inactive_intersecting {
785 debug_assert!(reg.get_class() == reg_class);
786 if block_pos[reg] == InstPoint::min_value() {
787 // This register is already blocked.
788 debug_assert!(next_use_pos[reg] == InstPoint::min_value());
789 continue;
790 }
791 block_pos[reg] = InstPoint::min(block_pos[reg], intersect_pos);
792 next_use_pos[reg] = InstPoint::min(next_use_pos[reg], intersect_pos);
793 }
794
795 // Step 2: find the register with the furthest next use.
796 let best_reg = {
797 let mut best = None;
798 for (reg, pos) in next_use_pos.iter() {
799 trace!("allocate_blocked_reg: {:?} has next use at {:?}", reg, pos);
800 match best {
801 None => best = Some((reg, pos)),
802 Some((ref mut best_reg, ref mut best_pos)) => {
803 if *best_pos < pos {
804 *best_pos = pos;
805 *best_reg = reg;
806 }
807 }
808 }
809 }
810 match best {
811 Some(best) => *best.0,
812 None => {
813 return Err(RegAllocError::Other(format!(
814 "the {:?} register class has no registers!",
815 reg_class
816 )));
817 }
818 }
819 };
820 debug!(
821 "selecting blocked register {:?} with furthest next use at {:?}",
822 best_reg, next_use_pos[best_reg]
823 );
824
825 // Step 3: if the next use of the current interval is after the furthest use
826 // of the selected register, then we should spill the current interval.
827 // Otherwise, spill other intervals.
828 debug!(
829 "current first used at {:?}, next use of best reg at {:?}",
830 first_use, next_use_pos[best_reg]
831 );
832
833 if first_use >= next_use_pos[best_reg] {
834 if first_use == start_pos {
835 return Err(RegAllocError::OutOfRegisters(reg_class));
836 }
837 debug!("spill current interval");
838 let new_int = split(state, cur_id, first_use);
839 state.insert_unhandled(new_int);
840 state.spill(cur_id);
841 } else {
842 debug!("taking over register, spilling intersecting intervals");
843
844 // Spill intervals that currently block the selected register.
845 state.intervals.set_reg(cur_id, best_reg);
846
847 // If there's an interference with a fixed interval, split at the
848 // intersection.
849 let int_end = state.intervals.get(cur_id).end;
850 if block_pos[best_reg] <= int_end {
851 debug!(
852 "allocate_blocked_reg: fixed conflict! blocked at {:?}, while ending at {:?}",
853 block_pos[best_reg], int_end
854 );
855
856 if !state.opts.partial_split || !try_split_regs(state, cur_id, block_pos[best_reg]) {
857 split_and_spill(state, cur_id, block_pos[best_reg]);
858 }
859 }
860
861 for &aid in &state.activity.active {
862 match aid {
863 ActiveInt::Virtual(int_id) => {
864 let int = state.intervals.get(int_id);
865 if int.vreg.get_class() != reg_class {
866 continue;
867 }
868 if let Some(reg) = int.location.reg() {
869 if reg == best_reg {
870 // spill it!
871 debug!("allocate_blocked_reg: split and spill active stolen reg");
872 split_and_spill(state, int_id, start_pos);
873 break;
874 }
875 }
876 }
877
878 ActiveInt::Fixed((_reg, _fix)) => {
879 lsra_assert!(
880 _reg != best_reg
881 || state.intervals.get(cur_id).end
882 < state.intervals.fixeds[_reg.get_index()].frags[_fix].first,
883 "can't split fixed active interval"
884 );
885 }
886 }
887 }
888
889 // Inactive virtual intervals would need to be split and spilled here too, but we can't
890 // have inactive virtual intervals.
891 #[cfg(debug_assertions)]
892 for &(reg, intersect_pos) in &reusable.inactive_intersecting {
893 debug_assert!(
894 reg != best_reg || state.intervals.get(cur_id).end < intersect_pos,
895 "can't split fixed inactive interval"
896 );
897 }
898 }
899
900 Ok(())
901 }
902
903 /// Finds an optimal split position, whenever we're given a range of possible
904 /// positions where to split.
find_optimal_split_pos<F: Function>( state: &State<F>, id: IntId, from: InstPoint, to: InstPoint, ) -> InstPoint905 fn find_optimal_split_pos<F: Function>(
906 state: &State<F>,
907 id: IntId,
908 from: InstPoint,
909 to: InstPoint,
910 ) -> InstPoint {
911 trace!("find_optimal_split_pos between {:?} and {:?}", from, to);
912
913 debug_assert!(from <= to, "split between positions are inconsistent");
914 let int = state.intervals.get(id);
915 debug_assert!(from >= int.start, "split should happen after the start");
916 debug_assert!(to <= int.end, "split should happen before the end");
917
918 if from == to {
919 return from;
920 }
921
922 let candidate = match state.opts.split_strategy {
923 OptimalSplitStrategy::To => Some(to),
924 OptimalSplitStrategy::NextFrom => Some(next_pos(from)),
925 OptimalSplitStrategy::NextNextFrom => Some(next_pos(next_pos(from))),
926 OptimalSplitStrategy::From => {
927 // This is the general setting, so win some time and eagerly return here.
928 return from;
929 }
930 OptimalSplitStrategy::PrevTo => Some(prev_pos(to)),
931 OptimalSplitStrategy::PrevPrevTo => Some(prev_pos(prev_pos(to))),
932 OptimalSplitStrategy::Mid => Some(InstPoint::new_use(InstIx::new(
933 (from.iix().get() + to.iix().get()) / 2,
934 ))),
935 };
936
937 if let Some(pos) = candidate {
938 if pos >= from && pos <= to && state.intervals.get(id).covers(pos) {
939 return pos;
940 }
941 }
942
943 from
944 }
945
prev_pos(mut pos: InstPoint) -> InstPoint946 fn prev_pos(mut pos: InstPoint) -> InstPoint {
947 match pos.pt() {
948 Point::Def => {
949 pos.set_pt(Point::Use);
950 pos
951 }
952 Point::Use => {
953 pos.set_iix(pos.iix().minus(1));
954 pos.set_pt(Point::Def);
955 pos
956 }
957 _ => unreachable!(),
958 }
959 }
960
next_pos(mut pos: InstPoint) -> InstPoint961 fn next_pos(mut pos: InstPoint) -> InstPoint {
962 match pos.pt() {
963 Point::Use => pos.set_pt(Point::Def),
964 Point::Def => {
965 pos.set_pt(Point::Use);
966 pos.set_iix(pos.iix().plus(1));
967 }
968 _ => unreachable!(),
969 };
970 pos
971 }
972
973 /// Splits the given interval between the last use before `split_pos` and
974 /// `split_pos`.
975 ///
976 /// In case of two-ways split (i.e. only place to split is precisely split_pos),
977 /// returns the live interval id for the middle child, to be added back to the
978 /// list of active/inactive intervals after iterating on these.
split_and_spill<F: Function>(state: &mut State<F>, id: IntId, split_pos: InstPoint)979 fn split_and_spill<F: Function>(state: &mut State<F>, id: IntId, split_pos: InstPoint) {
980 let child = match last_use(&state.intervals.get(id), split_pos, &state.reg_uses) {
981 Some(last_use) => {
982 debug!(
983 "split_and_spill {:?}: spill between {:?} and {:?}",
984 id, last_use, split_pos
985 );
986
987 // Maintain ascending order between the min and max positions.
988 let min_pos = InstPoint::min(next_pos(last_use), split_pos);
989
990 // Make sure that if the two positions are the same, we'll be splitting in
991 // a position that's in the current interval.
992 let optimal_pos = find_optimal_split_pos(state, id, min_pos, split_pos);
993
994 let child = split(state, id, optimal_pos);
995 state.spill(child);
996 child
997 }
998
999 None => {
1000 // The current interval has no uses before the split position, it can
1001 // safely be spilled.
1002 debug!(
1003 "split_and_spill {:?}: spilling it since no uses before split position",
1004 id
1005 );
1006 state.spill(id);
1007 id
1008 }
1009 };
1010
1011 // Split until the next register use.
1012 match next_use(&state.intervals.get(child), split_pos, &state.reg_uses) {
1013 Some(next_use_pos) => {
1014 debug!(
1015 "split spilled interval before next use @ {:?}",
1016 next_use_pos
1017 );
1018 let child = split(state, child, next_use_pos);
1019 state.insert_unhandled(child);
1020 }
1021 None => {
1022 // Let it be spilled for the rest of its lifetime.
1023 }
1024 }
1025
1026 // In both cases, the spilled child interval can remain on the stack.
1027 debug!("spilled split child {:?} silently expires", child);
1028 }
1029
1030 /// Try to find a (use) position where to split the interval until the next point at which it
1031 /// becomes unavailable, and put it back into the queue of intervals to allocate later on. Returns
1032 /// true if it succeeded in finding such a position, false otherwise.
try_split_regs<F: Function>( state: &mut State<F>, id: IntId, available_until: InstPoint, ) -> bool1033 fn try_split_regs<F: Function>(
1034 state: &mut State<F>,
1035 id: IntId,
1036 available_until: InstPoint,
1037 ) -> bool {
1038 state.stats.as_mut().map(|stats| stats.num_reg_splits += 1);
1039
1040 // Find a position for the split: we'll iterate backwards from the point until the register is
1041 // available, down to the previous use of the current interval.
1042 let prev_use = match last_use(&state.intervals.get(id), available_until, &state.reg_uses) {
1043 Some(prev_use) => prev_use,
1044 None => state.intervals.get(id).start,
1045 };
1046
1047 let split_pos = if state.opts.partial_split_near_end {
1048 // Split at the position closest to the available_until position.
1049 let pos = match available_until.pt() {
1050 Point::Use => prev_pos(prev_pos(available_until)),
1051 Point::Def => prev_pos(available_until),
1052 _ => unreachable!(),
1053 };
1054 if pos <= prev_use {
1055 return false;
1056 }
1057 pos
1058 } else {
1059 // Split at the position closest to the prev_use position. If it was a def, we can split
1060 // just thereafter, if it was at a use, go to the next use.
1061 let pos = match prev_use.pt() {
1062 Point::Use => next_pos(next_pos(prev_use)),
1063 Point::Def => next_pos(prev_use),
1064 _ => unreachable!(),
1065 };
1066 if pos >= available_until {
1067 return false;
1068 }
1069 pos
1070 };
1071
1072 let child = split(state, id, split_pos);
1073 state.insert_unhandled(child);
1074
1075 state
1076 .stats
1077 .as_mut()
1078 .map(|stats| stats.num_reg_splits_success += 1);
1079
1080 true
1081 }
1082
1083 /// Splits the interval at the given position.
1084 ///
1085 /// The split position must either be a Def of the current vreg, or it must be
1086 /// at a Use position (otherwise there's no place to put the moves created by
1087 /// the split).
1088 ///
1089 /// The id of the new interval is returned, while the parent interval is mutated
1090 /// in place. The child interval starts after (including) at_pos.
1091 #[inline(never)]
split<F: Function>(state: &mut State<F>, id: IntId, at_pos: InstPoint) -> IntId1092 fn split<F: Function>(state: &mut State<F>, id: IntId, at_pos: InstPoint) -> IntId {
1093 debug!("split {:?} at {:?}", id, at_pos);
1094 trace!("interval: {}", state.intervals.get(id));
1095
1096 let int = state.intervals.get(id);
1097 debug_assert!(int.start <= at_pos, "must split after the start");
1098 debug_assert!(at_pos <= int.end, "must split before the end");
1099
1100 // We're splitting in the middle of a fragment: [L, R].
1101 // Split it into two fragments: parent [L, pos[ + child [pos, R].
1102 debug_assert!(int.start < int.end, "trying to split unit fragment");
1103 debug_assert!(int.start <= at_pos, "no space to split fragment");
1104
1105 let parent_start = int.start;
1106 let parent_end = prev_pos(at_pos);
1107 let child_start = at_pos;
1108 let child_end = int.end;
1109
1110 trace!(
1111 "split fragment [{:?}; {:?}] into two parts: [{:?}; {:?}] to [{:?}; {:?}]",
1112 int.start,
1113 int.end,
1114 parent_start,
1115 parent_end,
1116 child_start,
1117 child_end
1118 );
1119
1120 debug_assert!(parent_start <= parent_end);
1121 debug_assert!(parent_end <= child_start);
1122 debug_assert!(child_start <= child_end);
1123
1124 let vreg = int.vreg;
1125 let ancestor = int.ancestor;
1126
1127 let parent_mentions = state.intervals.get_mut(id).mentions_mut();
1128 let index = parent_mentions.binary_search_by(|mention| {
1129 // The comparator function returns the position of the argument compared to the target.
1130
1131 // Search by index first.
1132 let iix = mention.0;
1133 if iix < at_pos.iix() {
1134 return Ordering::Less;
1135 }
1136 if iix > at_pos.iix() {
1137 return Ordering::Greater;
1138 }
1139
1140 // The instruction index is the same. Consider the instruction side now, and compare it
1141 // with the set. For the purpose of LSRA, mod means use and def.
1142 let set = mention.1;
1143 if at_pos.pt() == Point::Use {
1144 if set.is_use_or_mod() {
1145 Ordering::Equal
1146 } else {
1147 // It has to be Mod or Def. We need to look more to the right of the seeked array.
1148 // Thus indicate this mention is after the target.
1149 Ordering::Greater
1150 }
1151 } else {
1152 debug_assert!(at_pos.pt() == Point::Def);
1153 if set.is_mod_or_def() {
1154 Ordering::Equal
1155 } else {
1156 // Look to the left.
1157 Ordering::Less
1158 }
1159 }
1160 });
1161
1162 let (index, may_need_fixup) = match index {
1163 Ok(index) => (index, true),
1164 Err(index) => (index, false),
1165 };
1166
1167 // Emulate split_off for SmallVec here.
1168 let mut child_mentions = MentionMap::with_capacity(parent_mentions.len() - index);
1169 for mention in parent_mentions.iter().skip(index) {
1170 child_mentions.push(mention.clone());
1171 }
1172 parent_mentions.truncate(index);
1173
1174 // In the situation where we split at the def point of an instruction, and the mention set
1175 // contains the use point, we need to refine the sets:
1176 // - the parent must still contain the use point (and the modified point if present)
1177 // - the child must only contain the def point (and the modified point if present).
1178 // Note that if we split at the use point of an instruction, and the mention set contains the
1179 // def point, it is fine: we're not splitting between the two of them.
1180 if may_need_fixup && at_pos.pt() == Point::Def && child_mentions.first().unwrap().1.is_use() {
1181 let first_child_mention = child_mentions.first_mut().unwrap();
1182 first_child_mention.1.remove_use();
1183
1184 let last_parent_mention = parent_mentions.last_mut().unwrap();
1185 last_parent_mention.1.add_use();
1186
1187 if first_child_mention.1.is_mod() {
1188 last_parent_mention.1.add_mod();
1189 }
1190 }
1191
1192 let child_id = IntId(state.intervals.num_virtual_intervals());
1193 let mut child_int =
1194 VirtualInterval::new(child_id, vreg, child_start, child_end, child_mentions);
1195 child_int.parent = Some(id);
1196 child_int.ancestor = ancestor;
1197
1198 state.intervals.push_interval(child_int);
1199
1200 state.intervals.get_mut(id).end = parent_end;
1201 state.intervals.set_child(id, child_id);
1202
1203 if log_enabled!(Level::Trace) {
1204 trace!("split results:");
1205 trace!("- {}", state.intervals.get(id));
1206 trace!("- {}", state.intervals.get(child_id));
1207 }
1208
1209 child_id
1210 }
1211
_build_mention_map(reg_uses: &RegUses) -> HashMap<Reg, MentionMap>1212 fn _build_mention_map(reg_uses: &RegUses) -> HashMap<Reg, MentionMap> {
1213 // Maps reg to its mentions.
1214 let mut reg_mentions: HashMap<Reg, MentionMap> = HashMap::default();
1215
1216 // Collect all the mentions.
1217 for i in 0..reg_uses.num_insns() {
1218 let iix = InstIx::new(i as u32);
1219 let regsets = reg_uses.get_reg_sets_for_iix(iix);
1220 debug_assert!(regsets.is_sanitized());
1221
1222 for reg in regsets.uses.iter() {
1223 let mentions = reg_mentions.entry(*reg).or_default();
1224 if mentions.is_empty() || mentions.last().unwrap().0 != iix {
1225 mentions.push((iix, Mention::new()));
1226 }
1227 mentions.last_mut().unwrap().1.add_use();
1228 }
1229
1230 for reg in regsets.mods.iter() {
1231 let mentions = reg_mentions.entry(*reg).or_default();
1232 if mentions.is_empty() || mentions.last().unwrap().0 != iix {
1233 mentions.push((iix, Mention::new()));
1234 }
1235 mentions.last_mut().unwrap().1.add_mod();
1236 }
1237
1238 for reg in regsets.defs.iter() {
1239 let mentions = reg_mentions.entry(*reg).or_default();
1240 if mentions.is_empty() || mentions.last().unwrap().0 != iix {
1241 mentions.push((iix, Mention::new()));
1242 }
1243 mentions.last_mut().unwrap().1.add_def();
1244 }
1245 }
1246
1247 reg_mentions
1248 }
1249