regalloc2/ion/liveranges.rs
1/*
2 * This file was initially derived from the files
3 * `js/src/jit/BacktrackingAllocator.h` and
4 * `js/src/jit/BacktrackingAllocator.cpp` in Mozilla Firefox, and was
5 * originally licensed under the Mozilla Public License 2.0. We
6 * subsequently relicensed it to Apache-2.0 WITH LLVM-exception (see
7 * https://github.com/bytecodealliance/regalloc2/issues/7).
8 *
9 * Since the initial port, the design has been substantially evolved
10 * and optimized.
11 */
12
13//! Live-range computation.
14
15use super::{
16 CodeRange, Env, LiveRangeFlag, LiveRangeIndex, LiveRangeKey, LiveRangeList, LiveRangeListEntry,
17 LiveRangeSet, PRegData, PRegIndex, RegClass, Use, VRegData, VRegIndex,
18};
19use crate::indexset::IndexSet;
20use crate::ion::data_structures::{
21 BlockparamIn, BlockparamOut, FixedRegFixupLevel, MultiFixedRegFixup,
22};
23use crate::{
24 Allocation, Block, Function, Inst, InstPosition, Operand, OperandConstraint, OperandKind,
25 OperandPos, PReg, ProgPoint, RegAllocError, VReg, VecExt,
26};
27use core::convert::TryFrom;
28use core::usize;
29use smallvec::{smallvec, SmallVec};
30
31/// A spill weight computed for a certain Use.
32#[derive(Clone, Copy, Debug)]
33pub struct SpillWeight(f32);
34
35#[inline(always)]
36pub fn spill_weight_from_constraint(
37 constraint: OperandConstraint,
38 loop_depth: usize,
39 is_def: bool,
40) -> SpillWeight {
41 // A bonus of 1000 for one loop level, 4000 for two loop levels,
42 // 16000 for three loop levels, etc. Avoids exponentiation.
43 let loop_depth = core::cmp::min(10, loop_depth);
44 let hot_bonus: f32 = (0..loop_depth).fold(1000.0, |a, _| a * 4.0);
45 let def_bonus: f32 = if is_def { 2000.0 } else { 0.0 };
46 let constraint_bonus: f32 = match constraint {
47 OperandConstraint::Any => 1000.0,
48 OperandConstraint::Reg | OperandConstraint::FixedReg(_) => 2000.0,
49 _ => 0.0,
50 };
51 SpillWeight(hot_bonus + def_bonus + constraint_bonus)
52}
53
54impl SpillWeight {
55 /// Convert a floating-point weight to a u16 that can be compactly
56 /// stored in a `Use`. We simply take the top 16 bits of the f32; this
57 /// is equivalent to the bfloat16 format
58 /// (https://en.wikipedia.org/wiki/Bfloat16_floating-point_format).
59 pub fn to_bits(self) -> u16 {
60 (self.0.to_bits() >> 15) as u16
61 }
62
63 /// Convert a value that was returned from
64 /// `SpillWeight::to_bits()` back into a `SpillWeight`. Note that
65 /// some precision may be lost when round-tripping from a spill
66 /// weight to packed bits and back.
67 pub fn from_bits(bits: u16) -> SpillWeight {
68 let x = f32::from_bits((bits as u32) << 15);
69 SpillWeight(x)
70 }
71
72 /// Get a zero spill weight.
73 pub fn zero() -> SpillWeight {
74 SpillWeight(0.0)
75 }
76
77 /// Convert to a raw floating-point value.
78 pub fn to_f32(self) -> f32 {
79 self.0
80 }
81
82 /// Create a `SpillWeight` from a raw floating-point value.
83 pub fn from_f32(x: f32) -> SpillWeight {
84 SpillWeight(x)
85 }
86
87 pub fn to_int(self) -> u32 {
88 self.0 as u32
89 }
90}
91
92impl core::ops::Add<SpillWeight> for SpillWeight {
93 type Output = SpillWeight;
94 fn add(self, other: SpillWeight) -> Self {
95 SpillWeight(self.0 + other.0)
96 }
97}
98
99fn slot_idx(i: usize) -> Result<u16, RegAllocError> {
100 u16::try_from(i).map_err(|_| RegAllocError::TooManyOperands)
101}
102
103impl<'a, F: Function> Env<'a, F> {
104 pub fn create_pregs_and_vregs(&mut self) {
105 // Create PRegs from the env.
106 self.pregs.resize(
107 PReg::NUM_INDEX,
108 PRegData {
109 allocations: LiveRangeSet::new(),
110 is_stack: false,
111 },
112 );
113 for &preg in &self.env.fixed_stack_slots {
114 self.pregs[preg.index()].is_stack = true;
115 }
116 for class in 0..self.preferred_victim_by_class.len() {
117 self.preferred_victim_by_class[class] = self.env.non_preferred_regs_by_class[class]
118 .last()
119 .or(self.env.preferred_regs_by_class[class].last())
120 .cloned()
121 .unwrap_or(PReg::invalid());
122 }
123 // Create VRegs from the vreg count.
124 for idx in 0..self.func.num_vregs() {
125 // We'll fill in the real details when we see the def.
126 self.ctx.vregs.add(
127 VReg::new(idx, RegClass::Int),
128 VRegData {
129 ranges: LiveRangeList::new_in(self.ctx.bump()),
130 blockparam: Block::invalid(),
131 // We'll learn the RegClass as we scan the code.
132 class: None,
133 },
134 );
135 }
136 // Create allocations too.
137 for inst in 0..self.func.num_insts() {
138 let start = self.output.allocs.len() as u32;
139 self.output.inst_alloc_offsets.push(start);
140 for _ in 0..self.func.inst_operands(Inst::new(inst)).len() {
141 self.output.allocs.push(Allocation::none());
142 }
143 }
144 }
145
146 /// Mark `range` as live for the given `vreg`.
147 ///
148 /// Returns the liverange that contains the given range.
149 pub fn add_liverange_to_vreg(
150 &mut self,
151 vreg: VRegIndex,
152 mut range: CodeRange,
153 ) -> LiveRangeIndex {
154 trace!("add_liverange_to_vreg: vreg {:?} range {:?}", vreg, range);
155
156 // Invariant: as we are building liveness information, we
157 // *always* process instructions bottom-to-top, and as a
158 // consequence, new liveranges are always created before any
159 // existing liveranges for a given vreg. We assert this here,
160 // then use it to avoid an O(n) merge step (which would lead
161 // to O(n^2) liveness construction cost overall).
162 //
163 // We store liveranges in reverse order in the `.ranges`
164 // array, then reverse them at the end of
165 // `compute_liveness()`.
166
167 if !self.vregs[vreg].ranges.is_empty() {
168 let last_range_index = self.vregs[vreg].ranges.last().unwrap().index;
169 let last_range = self.ranges[last_range_index].range;
170 if self.func.allow_multiple_vreg_defs() {
171 if last_range.contains(&range) {
172 // Special case (may occur when multiple defs of pinned
173 // physical regs occur): if this new range overlaps the
174 // existing range, return it.
175 return last_range_index;
176 }
177 // If this range's end falls in the middle of the last
178 // range, truncate it to be contiguous so we can merge
179 // below.
180 if range.to >= last_range.from && range.to <= last_range.to {
181 range.to = last_range.from;
182 }
183 }
184 debug_assert!(
185 range.to <= last_range.from,
186 "range {:?}, last_range {:?}",
187 range,
188 last_range
189 );
190 }
191
192 if self.vregs[vreg].ranges.is_empty()
193 || range.to
194 < self.ranges[self.vregs[vreg].ranges.last().unwrap().index]
195 .range
196 .from
197 {
198 // Is not contiguous with previously-added (immediately
199 // following) range; create a new range.
200 let lr = self.ctx.ranges.add(range, self.ctx.bump());
201 self.ranges[lr].vreg = vreg;
202 self.vregs[vreg]
203 .ranges
204 .push(LiveRangeListEntry { range, index: lr });
205 lr
206 } else {
207 // Is contiguous with previously-added range; just extend
208 // its range and return it.
209 let lr = self.vregs[vreg].ranges.last().unwrap().index;
210 debug_assert!(range.to == self.ranges[lr].range.from);
211 self.ranges[lr].range.from = range.from;
212 lr
213 }
214 }
215
216 pub fn insert_use_into_liverange(&mut self, into: LiveRangeIndex, mut u: Use) {
217 let operand = u.operand;
218 let constraint = operand.constraint();
219 let block = self.cfginfo.insn_block[u.pos.inst().index()];
220 let loop_depth = self.cfginfo.approx_loop_depth[block.index()] as usize;
221 let weight = spill_weight_from_constraint(
222 constraint,
223 loop_depth,
224 operand.kind() != OperandKind::Use,
225 );
226 u.weight = weight.to_bits();
227
228 trace!(
229 "insert use {:?} into lr {:?} with weight {:?}",
230 u,
231 into,
232 weight,
233 );
234
235 // N.B.: we do *not* update `requirement` on the range,
236 // because those will be computed during the multi-fixed-reg
237 // fixup pass later (after all uses are inserted).
238
239 self.ranges[into].uses.push(u);
240
241 // Update stats.
242 let range_weight = self.ranges[into].uses_spill_weight() + weight;
243 self.ranges[into].set_uses_spill_weight(range_weight);
244 trace!(
245 " -> now range has weight {:?}",
246 self.ranges[into].uses_spill_weight(),
247 );
248 }
249
250 pub fn find_vreg_liverange_for_pos(
251 &self,
252 vreg: VRegIndex,
253 pos: ProgPoint,
254 ) -> Option<LiveRangeIndex> {
255 for entry in &self.vregs[vreg].ranges {
256 if entry.range.contains_point(pos) {
257 return Some(entry.index);
258 }
259 }
260 None
261 }
262
263 pub fn add_liverange_to_preg(&mut self, range: CodeRange, reg: PReg) {
264 trace!("adding liverange to preg: {:?} to {}", range, reg);
265 let preg_idx = PRegIndex::new(reg.index());
266 let res = self.pregs[preg_idx.index()]
267 .allocations
268 .btree
269 .insert(LiveRangeKey::from_range(&range), LiveRangeIndex::invalid());
270 debug_assert!(res.is_none());
271 }
272
273 pub fn is_live_in(&mut self, block: Block, vreg: VRegIndex) -> bool {
274 self.liveins[block.index()].get(vreg.index())
275 }
276
277 pub fn compute_liveness(&mut self) -> Result<(), RegAllocError> {
278 // Create initial LiveIn and LiveOut bitsets.
279 for _ in 0..self.func.num_blocks() {
280 self.liveins.push(IndexSet::new());
281 self.liveouts.push(IndexSet::new());
282 }
283
284 // Run a worklist algorithm to precisely compute liveins and
285 // liveouts.
286 let mut workqueue = core::mem::take(&mut self.ctx.scratch_workqueue);
287 let mut workqueue_set = core::mem::take(&mut self.ctx.scratch_workqueue_set);
288 workqueue_set.clear();
289 // Initialize workqueue with postorder traversal.
290 for &block in &self.cfginfo.postorder[..] {
291 workqueue.push_back(block);
292 workqueue_set.insert(block);
293 }
294
295 while let Some(block) = workqueue.pop_front() {
296 workqueue_set.remove(&block);
297 let insns = self.func.block_insns(block);
298
299 trace!("computing liveins for block{}", block.index());
300
301 self.output.stats.livein_iterations += 1;
302
303 let mut live = self.liveouts[block.index()].clone();
304 trace!(" -> initial liveout set: {:?}", live);
305
306 // Include outgoing blockparams in the initial live set.
307 if self.func.is_branch(insns.last()) {
308 for i in 0..self.func.block_succs(block).len() {
309 for ¶m in self.func.branch_blockparams(block, insns.last(), i) {
310 live.set(param.vreg(), true);
311 self.observe_vreg_class(param);
312 }
313 }
314 }
315
316 for inst in insns.iter().rev() {
317 for pos in &[OperandPos::Late, OperandPos::Early] {
318 for op in self.func.inst_operands(inst) {
319 if op.as_fixed_nonallocatable().is_some() {
320 continue;
321 }
322 if op.pos() == *pos {
323 let was_live = live.get(op.vreg().vreg());
324 trace!("op {:?} was_live = {}", op, was_live);
325 match op.kind() {
326 OperandKind::Use => {
327 live.set(op.vreg().vreg(), true);
328 }
329 OperandKind::Def => {
330 live.set(op.vreg().vreg(), false);
331 }
332 }
333 self.observe_vreg_class(op.vreg());
334 }
335 }
336 }
337 }
338 for &blockparam in self.func.block_params(block) {
339 live.set(blockparam.vreg(), false);
340 self.observe_vreg_class(blockparam);
341 }
342
343 for &pred in self.func.block_preds(block) {
344 if self.ctx.liveouts[pred.index()].union_with(&live) {
345 if !workqueue_set.contains(&pred) {
346 workqueue_set.insert(pred);
347 workqueue.push_back(pred);
348 }
349 }
350 }
351
352 trace!("computed liveins at block{}: {:?}", block.index(), live);
353 self.liveins[block.index()] = live;
354 }
355
356 // Check that there are no liveins to the entry block.
357 if !self.liveins[self.func.entry_block().index()].is_empty() {
358 trace!(
359 "non-empty liveins to entry block: {:?}",
360 self.liveins[self.func.entry_block().index()]
361 );
362 return Err(RegAllocError::EntryLivein);
363 }
364
365 self.ctx.scratch_workqueue = workqueue;
366 self.ctx.scratch_workqueue_set = workqueue_set;
367
368 Ok(())
369 }
370
371 pub fn build_liveranges(&mut self) -> Result<(), RegAllocError> {
372 // Create Uses and Defs referring to VRegs, and place the Uses
373 // in LiveRanges.
374 //
375 // We already computed precise liveouts and liveins for every
376 // block above, so we don't need to run an iterative algorithm
377 // here; instead, every block's computation is purely local,
378 // from end to start.
379
380 // Track current LiveRange for each vreg.
381 //
382 // Invariant: a stale range may be present here; ranges are
383 // only valid if `live.get(vreg)` is true.
384 let mut vreg_ranges = core::mem::take(&mut self.ctx.scratch_vreg_ranges);
385 vreg_ranges.repopulate(self.func.num_vregs(), LiveRangeIndex::invalid());
386 let mut operand_rewrites = core::mem::take(&mut self.ctx.scratch_operand_rewrites);
387
388 for i in (0..self.func.num_blocks()).rev() {
389 let block = Block::new(i);
390 let insns = self.func.block_insns(block);
391
392 self.output.stats.livein_blocks += 1;
393
394 // Init our local live-in set.
395 let mut live = self.liveouts[block.index()].clone();
396
397 // If the last instruction is a branch (rather than
398 // return), create blockparam_out entries.
399 if self.func.is_branch(insns.last()) {
400 for (i, &succ) in self.func.block_succs(block).iter().enumerate() {
401 let blockparams_in = self.func.block_params(succ);
402 let blockparams_out = self.func.branch_blockparams(block, insns.last(), i);
403 for (&blockparam_in, &blockparam_out) in
404 blockparams_in.iter().zip(blockparams_out)
405 {
406 let blockparam_out = VRegIndex::new(blockparam_out.vreg());
407 let blockparam_in = VRegIndex::new(blockparam_in.vreg());
408 self.blockparam_outs.push(BlockparamOut {
409 to_vreg: blockparam_in,
410 to_block: succ,
411 from_block: block,
412 from_vreg: blockparam_out,
413 });
414
415 // Include outgoing blockparams in the initial live set.
416 live.set(blockparam_out.index(), true);
417 }
418 }
419 }
420
421 // Initially, registers are assumed live for the whole block.
422 for vreg in live.iter() {
423 let range = CodeRange {
424 from: self.cfginfo.block_entry[block.index()],
425 to: self.cfginfo.block_exit[block.index()].next(),
426 };
427 trace!(
428 "vreg {:?} live at end of block --> create range {:?}",
429 VRegIndex::new(vreg),
430 range
431 );
432 let lr = self.add_liverange_to_vreg(VRegIndex::new(vreg), range);
433 vreg_ranges[vreg] = lr;
434 }
435
436 // Create vreg data for blockparams.
437 for ¶m in self.func.block_params(block) {
438 self.vregs[param].blockparam = block;
439 }
440
441 // For each instruction, in reverse order, process
442 // operands and clobbers.
443 for inst in insns.iter().rev() {
444 // Mark clobbers with CodeRanges on PRegs.
445 for clobber in self.func.inst_clobbers(inst) {
446 // Clobber range is at After point only: an
447 // instruction can still take an input in a reg
448 // that it later clobbers. (In other words, the
449 // clobber is like a normal def that never gets
450 // used.)
451 let range = CodeRange {
452 from: ProgPoint::after(inst),
453 to: ProgPoint::before(inst.next()),
454 };
455 self.add_liverange_to_preg(range, clobber);
456 }
457
458 // Does the instruction have any input-reusing
459 // outputs? This is important below to establish
460 // proper interference wrt other inputs. We note the
461 // *vreg* that is reused, not the index.
462 let mut reused_input = None;
463 for op in self.func.inst_operands(inst) {
464 if let OperandConstraint::Reuse(i) = op.constraint() {
465 debug_assert!(self.func.inst_operands(inst)[i]
466 .as_fixed_nonallocatable()
467 .is_none());
468 reused_input = Some(self.func.inst_operands(inst)[i].vreg());
469 break;
470 }
471 }
472
473 // Preprocess defs and uses. Specifically, if there
474 // are any fixed-reg-constrained defs at Late position
475 // and fixed-reg-constrained uses at Early position
476 // with the same preg, we need to (i) add a fixup move
477 // for the use, (ii) rewrite the use to have an Any
478 // constraint, and (ii) move the def to Early position
479 // to reserve the register for the whole instruction.
480 //
481 // We don't touch any fixed-early-def or fixed-late-use
482 // constraints: the only situation where the same physical
483 // register can be used multiple times in the same
484 // instruction is with an early-use and a late-def. Anything
485 // else is a user error.
486 operand_rewrites.clear();
487 let mut late_def_fixed: SmallVec<[PReg; 8]> = smallvec![];
488 for &operand in self.func.inst_operands(inst) {
489 if let OperandConstraint::FixedReg(preg) = operand.constraint() {
490 match (operand.pos(), operand.kind()) {
491 (OperandPos::Late, OperandKind::Def) => {
492 late_def_fixed.push(preg);
493 }
494 _ => {}
495 }
496 }
497 }
498 for (i, &operand) in self.func.inst_operands(inst).iter().enumerate() {
499 if operand.as_fixed_nonallocatable().is_some() {
500 continue;
501 }
502 if let OperandConstraint::FixedReg(preg) = operand.constraint() {
503 match (operand.pos(), operand.kind()) {
504 (OperandPos::Early, OperandKind::Use)
505 if live.get(operand.vreg().vreg()) =>
506 {
507 // If we have a use constraint at the
508 // Early point for a fixed preg, and
509 // this preg is also constrained with
510 // a *separate* def at Late or is
511 // clobbered, and *if* the vreg is
512 // live downward, we have to use the
513 // multi-fixed-reg mechanism for a
514 // fixup and rewrite here without the
515 // constraint. See #53.
516 //
517 // We adjust the def liverange and Use
518 // to an "early" position to reserve
519 // the register, it still must not be
520 // used by some other vreg at the
521 // use-site.
522 //
523 // Note that we handle multiple
524 // conflicting constraints for the
525 // same vreg in a separate pass (see
526 // `fixup_multi_fixed_vregs` below).
527 if late_def_fixed.contains(&preg)
528 || self.func.inst_clobbers(inst).contains(preg)
529 {
530 trace!(
531 concat!(
532 "-> operand {:?} is fixed to preg {:?}, ",
533 "is downward live, and there is also a ",
534 "def or clobber at this preg"
535 ),
536 operand,
537 preg
538 );
539 let pos = ProgPoint::before(inst);
540 self.multi_fixed_reg_fixups.push(MultiFixedRegFixup {
541 pos,
542 from_slot: slot_idx(i)?,
543 to_slot: slot_idx(i)?,
544 to_preg: PRegIndex::new(preg.index()),
545 vreg: VRegIndex::new(operand.vreg().vreg()),
546 level: FixedRegFixupLevel::Initial,
547 });
548
549 // We need to insert a reservation
550 // at the before-point to reserve
551 // the reg for the use too.
552 let range = CodeRange::singleton(pos);
553 self.add_liverange_to_preg(range, preg);
554
555 // Remove the fixed-preg
556 // constraint from the Use.
557 operand_rewrites.insert(
558 i,
559 Operand::new(
560 operand.vreg(),
561 OperandConstraint::Any,
562 operand.kind(),
563 operand.pos(),
564 ),
565 );
566 }
567 }
568 _ => {}
569 }
570 }
571 }
572
573 // Process defs and uses.
574 for &cur_pos in &[InstPosition::After, InstPosition::Before] {
575 for i in 0..self.func.inst_operands(inst).len() {
576 // don't borrow `self`
577 let operand = operand_rewrites
578 .get(&i)
579 .cloned()
580 .unwrap_or(self.func.inst_operands(inst)[i]);
581 let pos = match (operand.kind(), operand.pos()) {
582 (OperandKind::Def, OperandPos::Early) => ProgPoint::before(inst),
583 (OperandKind::Def, OperandPos::Late) => ProgPoint::after(inst),
584 (OperandKind::Use, OperandPos::Late) => ProgPoint::after(inst),
585 // If there are any reused inputs in this
586 // instruction, and this is *not* the
587 // reused vreg, force `pos` to
588 // `After`. This ensures that we correctly
589 // account for the interference between
590 // the other inputs and the
591 // input-that-is-reused/output.
592 (OperandKind::Use, OperandPos::Early)
593 if reused_input.is_some()
594 && reused_input.unwrap() != operand.vreg() =>
595 {
596 ProgPoint::after(inst)
597 }
598 (OperandKind::Use, OperandPos::Early) => ProgPoint::before(inst),
599 };
600
601 if pos.pos() != cur_pos {
602 continue;
603 }
604
605 trace!(
606 "processing inst{} operand at {:?}: {:?}",
607 inst.index(),
608 pos,
609 operand
610 );
611
612 // If this is a "fixed non-allocatable
613 // register" operand, set the alloc
614 // immediately and then ignore the operand
615 // hereafter.
616 if let Some(preg) = operand.as_fixed_nonallocatable() {
617 self.set_alloc(inst, i, Allocation::reg(preg));
618 continue;
619 }
620
621 match operand.kind() {
622 OperandKind::Def => {
623 trace!("Def of {} at {:?}", operand.vreg(), pos);
624
625 // Get or create the LiveRange.
626 let mut lr = vreg_ranges[operand.vreg().vreg()];
627 trace!(" -> has existing LR {:?}", lr);
628 // If there was no liverange (dead def), create a trivial one.
629 if !live.get(operand.vreg().vreg()) {
630 let from = pos;
631 // We want to we want to span
632 // until Before of the next
633 // inst. This ensures that early
634 // defs used for temps on an
635 // instruction are reserved across
636 // the whole instruction.
637 let to = ProgPoint::before(pos.inst().next());
638 lr = self.add_liverange_to_vreg(
639 VRegIndex::new(operand.vreg().vreg()),
640 CodeRange { from, to },
641 );
642 trace!(" -> invalid; created {:?}", lr);
643 vreg_ranges[operand.vreg().vreg()] = lr;
644 live.set(operand.vreg().vreg(), true);
645 }
646 // Create the use in the LiveRange.
647 self.insert_use_into_liverange(
648 lr,
649 Use::new(operand, pos, slot_idx(i)?),
650 );
651 // If def (not mod), this reg is now dead,
652 // scanning backward; make it so.
653 if operand.kind() == OperandKind::Def {
654 // Trim the range for this vreg to start
655 // at `pos` if it previously ended at the
656 // start of this block (i.e. was not
657 // merged into some larger LiveRange due
658 // to out-of-order blocks).
659 if self.ranges[lr].range.from
660 == self.cfginfo.block_entry[block.index()]
661 {
662 trace!(" -> started at block start; trimming to {:?}", pos);
663 self.ranges[lr].range.from = pos;
664 }
665
666 self.ranges[lr].set_flag(LiveRangeFlag::StartsAtDef);
667
668 // Remove from live-set.
669 live.set(operand.vreg().vreg(), false);
670 vreg_ranges[operand.vreg().vreg()] = LiveRangeIndex::invalid();
671 }
672 }
673 OperandKind::Use => {
674 // Create/extend the LiveRange if it
675 // doesn't already exist, and add the use
676 // to the range.
677 let mut lr = vreg_ranges[operand.vreg().vreg()];
678 if !live.get(operand.vreg().vreg()) {
679 let range = CodeRange {
680 from: self.cfginfo.block_entry[block.index()],
681 to: pos.next(),
682 };
683 lr = self.add_liverange_to_vreg(
684 VRegIndex::new(operand.vreg().vreg()),
685 range,
686 );
687 vreg_ranges[operand.vreg().vreg()] = lr;
688 }
689 debug_assert!(lr.is_valid());
690
691 trace!("Use of {:?} at {:?} -> {:?}", operand, pos, lr,);
692
693 self.insert_use_into_liverange(
694 lr,
695 Use::new(operand, pos, slot_idx(i)?),
696 );
697
698 // Add to live-set.
699 live.set(operand.vreg().vreg(), true);
700 }
701 }
702 }
703 }
704 }
705
706 // Block parameters define vregs at the very beginning of
707 // the block. Remove their live vregs from the live set
708 // here.
709 for vreg in self.func.block_params(block) {
710 if live.get(vreg.vreg()) {
711 live.set(vreg.vreg(), false);
712 } else {
713 // Create trivial liverange if blockparam is dead.
714 let start = self.cfginfo.block_entry[block.index()];
715 self.add_liverange_to_vreg(
716 VRegIndex::new(vreg.vreg()),
717 CodeRange {
718 from: start,
719 to: start.next(),
720 },
721 );
722 }
723 // add `blockparam_ins` entries.
724 let vreg_idx = VRegIndex::new(vreg.vreg());
725 for &pred in self.func.block_preds(block) {
726 self.blockparam_ins.push(BlockparamIn {
727 to_vreg: vreg_idx,
728 to_block: block,
729 from_block: pred,
730 });
731 }
732 }
733 }
734
735 // Make ranges in each vreg and uses in each range appear in
736 // sorted order. We built them in reverse order above, so this
737 // is a simple reversal, *not* a full sort.
738 //
739 // The ordering invariant is always maintained for uses and
740 // always for ranges in bundles (which are initialized later),
741 // but not always for ranges in vregs; those are sorted only
742 // when needed, here and then again at the end of allocation
743 // when resolving moves.
744
745 for vreg in &mut self.ctx.vregs {
746 vreg.ranges.reverse();
747 let mut last = None;
748 for entry in &mut vreg.ranges {
749 // Ranges may have been truncated above at defs. We
750 // need to update with the final range here.
751 entry.range = self.ctx.ranges[entry.index].range;
752 // Assert in-order and non-overlapping.
753 debug_assert!(last.is_none() || last.unwrap() <= entry.range.from);
754 last = Some(entry.range.to);
755 }
756 }
757
758 for range in &mut self.ranges {
759 range.uses.reverse();
760 debug_assert!(range.uses.windows(2).all(|win| win[0].pos <= win[1].pos));
761 }
762
763 self.blockparam_ins.sort_unstable_by_key(|x| x.key());
764 self.blockparam_outs.sort_unstable_by_key(|x| x.key());
765
766 self.output.stats.initial_liverange_count = self.ranges.len();
767 self.output.stats.blockparam_ins_count = self.blockparam_ins.len();
768 self.output.stats.blockparam_outs_count = self.blockparam_outs.len();
769 self.ctx.scratch_vreg_ranges = vreg_ranges;
770 self.ctx.scratch_operand_rewrites = operand_rewrites;
771
772 Ok(())
773 }
774
775 pub fn fixup_multi_fixed_vregs(&mut self) {
776 // Do a fixed-reg cleanup pass: if there are any LiveRanges with
777 // multiple uses at the same ProgPoint and there is
778 // more than one FixedReg constraint at that ProgPoint, we
779 // need to record all but one of them in a special fixup list
780 // and handle them later; otherwise, bundle-splitting to
781 // create minimal bundles becomes much more complex (we would
782 // have to split the multiple uses at the same progpoint into
783 // different bundles, which breaks invariants related to
784 // disjoint ranges and bundles).
785 let mut extra_clobbers: SmallVec<[(PReg, ProgPoint); 8]> = smallvec![];
786 for vreg in 0..self.vregs.len() {
787 let vreg = VRegIndex::new(vreg);
788 for range_idx in 0..self.vregs[vreg].ranges.len() {
789 let entry = self.vregs[vreg].ranges[range_idx];
790 let range = entry.index;
791 trace!("multi-fixed-reg cleanup: vreg {:?} range {:?}", vreg, range,);
792
793 // Find groups of uses that occur in at the same program point.
794 for uses in self.ctx.ranges[range]
795 .uses
796 .chunk_by_mut(|a, b| a.pos == b.pos)
797 {
798 if uses.len() < 2 {
799 continue;
800 }
801
802 // Search for conflicting constraints in the uses.
803 let mut requires_reg = false;
804 let mut num_fixed_reg = 0;
805 let mut num_fixed_stack = 0;
806 let mut first_reg_slot = None;
807 let mut first_stack_slot = None;
808 let mut min_limit = usize::MAX;
809 let mut max_fixed_reg = usize::MIN;
810 for u in uses.iter() {
811 match u.operand.constraint() {
812 OperandConstraint::Any => {
813 first_reg_slot.get_or_insert(u.slot);
814 first_stack_slot.get_or_insert(u.slot);
815 }
816 OperandConstraint::Reg | OperandConstraint::Reuse(_) => {
817 first_reg_slot.get_or_insert(u.slot);
818 requires_reg = true;
819 }
820 OperandConstraint::Limit(max) => {
821 first_reg_slot.get_or_insert(u.slot);
822 min_limit = min_limit.min(max);
823 requires_reg = true;
824 }
825 OperandConstraint::FixedReg(preg) => {
826 max_fixed_reg = max_fixed_reg.max(preg.hw_enc());
827 if self.ctx.pregs[preg.index()].is_stack {
828 num_fixed_stack += 1;
829 first_stack_slot.get_or_insert(u.slot);
830 } else {
831 requires_reg = true;
832 num_fixed_reg += 1;
833 first_reg_slot.get_or_insert(u.slot);
834 }
835 }
836 // Maybe this could be supported in this future...
837 OperandConstraint::Stack => panic!(
838 "multiple uses of vreg with a Stack constraint are not supported"
839 ),
840 }
841 }
842
843 // Fast path if there are no conflicts.
844 if num_fixed_reg + num_fixed_stack <= 1
845 && !(requires_reg && num_fixed_stack != 0)
846 && max_fixed_reg < min_limit
847 {
848 continue;
849 }
850
851 // We pick one constraint (in order: FixedReg, Reg, FixedStack)
852 // and then rewrite any incompatible constraints to Any.
853 // This allows register allocation to succeed and we will
854 // later insert moves to satisfy the rewritten constraints.
855 let source_slot = if requires_reg {
856 first_reg_slot.unwrap()
857 } else {
858 first_stack_slot.unwrap()
859 };
860 let mut first_preg = None;
861 for u in uses.iter_mut() {
862 if let OperandConstraint::FixedReg(preg) = u.operand.constraint() {
863 let vreg_idx = VRegIndex::new(u.operand.vreg().vreg());
864 let preg_idx = PRegIndex::new(preg.index());
865 trace!(
866 "at pos {:?}, vreg {:?} has fixed constraint to preg {:?}",
867 u.pos,
868 vreg_idx,
869 preg_idx
870 );
871
872 // FixedStack is incompatible if there are any
873 // Reg/FixedReg constraints. FixedReg is
874 // incompatible if there already is a different
875 // FixedReg constraint. If either condition is true,
876 // we edit the constraint below; otherwise, we can
877 // skip this edit.
878 if !(requires_reg && self.ctx.pregs[preg.index()].is_stack)
879 && *first_preg.get_or_insert(preg) == preg
880 && preg.hw_enc() < min_limit
881 {
882 continue;
883 }
884
885 trace!(" -> duplicate; switching to constraint Any");
886 self.ctx.multi_fixed_reg_fixups.push(MultiFixedRegFixup {
887 pos: u.pos,
888 from_slot: source_slot,
889 to_slot: u.slot,
890 to_preg: preg_idx,
891 vreg: vreg_idx,
892 level: FixedRegFixupLevel::Secondary,
893 });
894 u.operand = Operand::new(
895 u.operand.vreg(),
896 OperandConstraint::Any,
897 u.operand.kind(),
898 u.operand.pos(),
899 );
900 trace!(" -> extra clobber {} at inst{}", preg, u.pos.inst().index());
901 extra_clobbers.push((preg, u.pos));
902 }
903 }
904 }
905
906 for (clobber, pos) in extra_clobbers.drain(..) {
907 let range = CodeRange {
908 from: pos,
909 to: pos.next(),
910 };
911 self.add_liverange_to_preg(range, clobber);
912 }
913 }
914 }
915 }
916}