Coverage Report

Created: 2026-04-09 08:09

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