Coverage Report

Created: 2026-10-24 07:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/regalloc2/src/ion/process.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
//! Main allocation loop that processes bundles.
14
15
use super::{
16
    spill_weight_from_constraint, Env, LiveBundleIndex, LiveBundleVec, LiveRangeFlag,
17
    LiveRangeIndex, LiveRangeKey, LiveRangeList, LiveRangeListEntry, PRegIndex, RegTraversalIter,
18
    Requirement, SpillWeight, UseList, VRegIndex,
19
};
20
use crate::{
21
    ion::data_structures::{
22
        CodeRange, Use, BUNDLE_MAX_NORMAL_SPILL_WEIGHT, MAX_SPLITS_PER_SPILLSET,
23
        MINIMAL_BUNDLE_SPILL_WEIGHT, MINIMAL_FIXED_BUNDLE_SPILL_WEIGHT,
24
        MINIMAL_LIMITED_BUNDLE_SPILL_WEIGHT,
25
    },
26
    Allocation, Function, Inst, InstPosition, OperandConstraint, OperandKind, PReg, ProgPoint,
27
    RegAllocError,
28
};
29
use core::fmt::Debug;
30
use smallvec::{smallvec, SmallVec};
31
32
#[derive(Clone, Debug, PartialEq, Eq)]
33
pub enum AllocRegResult<'a> {
34
    Allocated(Allocation),
35
    Conflict(&'a [LiveBundleIndex], ProgPoint),
36
    ConflictWithFixed(u32, ProgPoint),
37
    ConflictHighCost,
38
}
39
40
impl<'a, F: Function> Env<'a, F> {
41
12.1k
    pub fn process_bundles(&mut self) -> Result<(), RegAllocError> {
42
11.0M
        while let Some((bundle, hint)) = self.ctx.allocation_queue.pop() {
43
11.0M
            self.ctx.output.stats.process_bundle_count += 1;
44
11.0M
            self.process_bundle(bundle, hint)?;
45
        }
46
10.0k
        self.ctx.output.stats.final_liverange_count = self.ranges.len();
47
10.0k
        self.ctx.output.stats.final_bundle_count = self.bundles.len();
48
10.0k
        self.ctx.output.stats.spill_bundle_count = self.spilled_bundles.len();
49
50
10.0k
        Ok(())
51
12.1k
    }
52
53
68.3M
    pub fn try_to_allocate_bundle_to_reg<'b>(
54
68.3M
        &mut self,
55
68.3M
        bundle: LiveBundleIndex,
56
68.3M
        reg: PRegIndex,
57
68.3M
        // if the max bundle weight in the conflict set exceeds this
58
68.3M
        // cost (if provided), just return
59
68.3M
        // `AllocRegResult::ConflictHighCost`.
60
68.3M
        max_allowable_cost: Option<u32>,
61
68.3M
        conflicts: &'b mut LiveBundleVec,
62
68.3M
    ) -> AllocRegResult<'b> {
63
68.3M
        trace!("try_to_allocate_bundle_to_reg: {:?} -> {:?}", bundle, reg);
64
68.3M
        conflicts.clear();
65
68.3M
        self.ctx.conflict_set.clear();
66
68.3M
        let mut max_conflict_weight = 0;
67
        // Traverse the BTreeMap in order by requesting the whole
68
        // range spanned by the bundle and iterating over that
69
        // concurrently with our ranges. Because our ranges are in
70
        // order, and the BTreeMap is as well, this allows us to have
71
        // an overall O(n log n) + O(b) complexity, where the PReg has
72
        // n current ranges and the bundle has b ranges, rather than
73
        // O(b * n log n) with the simple probe-for-each-bundle-range
74
        // approach.
75
        //
76
        // Note that the comparator function on a CodeRange tests for
77
        // *overlap*, so we are checking whether the BTree contains
78
        // any preg range that *overlaps* with range `range`, not
79
        // literally the range `range`.
80
68.3M
        let bundle_ranges = &self.ctx.bundles[bundle].ranges;
81
68.3M
        let from_key = LiveRangeKey::from_range(&CodeRange {
82
68.3M
            from: bundle_ranges.first().unwrap().range.from,
83
68.3M
            to: bundle_ranges.first().unwrap().range.from,
84
68.3M
        });
85
68.3M
        let mut preg_range_iter = self.ctx.pregs[reg.index()]
86
68.3M
            .allocations
87
68.3M
            .btree
88
68.3M
            .range(from_key..)
89
68.3M
            .peekable();
90
68.3M
        trace!(
91
            "alloc map for {:?} in range {:?}..: {:?}",
92
            reg,
93
            from_key,
94
0
            self.ctx.pregs[reg.index()].allocations.btree
95
        );
96
68.3M
        let mut first_conflict: Option<ProgPoint> = None;
97
98
174M
        'ranges: for entry in bundle_ranges {
99
174M
            trace!(" -> range LR {:?}: {:?}", entry.index, entry.range);
100
174M
            let key = LiveRangeKey::from_range(&entry.range);
101
102
174M
            let mut skips = 0;
103
            'alloc: loop {
104
313M
                trace!("  -> PReg range {:?}", preg_range_iter.peek());
105
106
                // Advance our BTree traversal until it is >= this bundle
107
                // range (i.e., skip PReg allocations in the BTree that
108
                // are completely before this bundle range).
109
110
313M
                if preg_range_iter.peek().is_some() && *preg_range_iter.peek().unwrap().0 < key {
111
8.46M
                    trace!(
112
                        "Skipping PReg range {:?}",
113
0
                        preg_range_iter.peek().unwrap().0
114
                    );
115
8.46M
                    preg_range_iter.next();
116
8.46M
                    skips += 1;
117
8.46M
                    if skips >= 16 {
118
4.44k
                        let from_pos = entry.range.from;
119
4.44k
                        let from_key = LiveRangeKey::from_range(&CodeRange {
120
4.44k
                            from: from_pos,
121
4.44k
                            to: from_pos,
122
4.44k
                        });
123
4.44k
                        preg_range_iter = self.ctx.pregs[reg.index()]
124
4.44k
                            .allocations
125
4.44k
                            .btree
126
4.44k
                            .range(from_key..)
127
4.44k
                            .peekable();
128
4.44k
                        skips = 0;
129
8.45M
                    }
130
8.46M
                    continue 'alloc;
131
304M
                }
132
304M
                skips = 0;
133
134
                // If there are no more PReg allocations, we're done!
135
304M
                if preg_range_iter.peek().is_none() {
136
9.07M
                    trace!(" -> no more PReg allocations; so no conflict possible!");
137
9.07M
                    break 'ranges;
138
295M
                }
139
140
                // If the current PReg range is beyond this range, there is no conflict; continue.
141
295M
                if *preg_range_iter.peek().unwrap().0 > key {
142
148M
                    trace!(
143
                        " -> next PReg allocation is at {:?}; moving to next VReg range",
144
0
                        preg_range_iter.peek().unwrap().0
145
                    );
146
148M
                    break 'alloc;
147
147M
                }
148
149
                // Otherwise, there is a conflict.
150
147M
                let preg_key = *preg_range_iter.peek().unwrap().0;
151
147M
                debug_assert_eq!(preg_key, key); // Assert that this range overlaps.
152
147M
                let preg_range = preg_range_iter.next().unwrap().1;
153
154
147M
                trace!(" -> btree contains range {:?} that overlaps", preg_range);
155
147M
                if preg_range.is_valid() {
156
132M
                    trace!("   -> from vreg {:?}", self.ctx.ranges[*preg_range].vreg);
157
                    // range from an allocated bundle: find the bundle and add to
158
                    // conflicts list.
159
132M
                    let conflict_bundle = self.ctx.ranges[*preg_range].bundle;
160
132M
                    trace!("   -> conflict bundle {:?}", conflict_bundle);
161
132M
                    if self.ctx.conflict_set.insert(conflict_bundle) {
162
64.3M
                        conflicts.push(conflict_bundle);
163
64.3M
                        max_conflict_weight = core::cmp::max(
164
64.3M
                            max_conflict_weight,
165
64.3M
                            self.ctx.bundles[conflict_bundle].cached_spill_weight(),
166
64.3M
                        );
167
64.3M
                        if max_allowable_cost.is_some()
168
15.6M
                            && max_conflict_weight > max_allowable_cost.unwrap()
169
                        {
170
2.37M
                            trace!("   -> reached high cost, retrying early");
171
2.37M
                            return AllocRegResult::ConflictHighCost;
172
61.9M
                        }
173
68.3M
                    }
174
175
130M
                    if first_conflict.is_none() {
176
45.9M
                        first_conflict = Some(ProgPoint::from_index(core::cmp::max(
177
45.9M
                            preg_key.from,
178
45.9M
                            key.from,
179
45.9M
                        )));
180
84.4M
                    }
181
                } else {
182
14.4M
                    trace!("   -> conflict with fixed reservation");
183
                    // range from a direct use of the PReg (due to clobber).
184
14.4M
                    return AllocRegResult::ConflictWithFixed(
185
14.4M
                        max_conflict_weight,
186
14.4M
                        ProgPoint::from_index(preg_key.from),
187
14.4M
                    );
188
                }
189
            }
190
        }
191
192
51.5M
        if conflicts.len() > 0 {
193
43.8M
            return AllocRegResult::Conflict(conflicts, first_conflict.unwrap());
194
7.75M
        }
195
196
        // We can allocate! Add our ranges to the preg's BTree.
197
7.75M
        let preg = PReg::from_index(reg.index());
198
7.75M
        trace!("  -> bundle {:?} assigned to preg {:?}", bundle, preg);
199
7.75M
        self.ctx.bundles[bundle].allocation = Allocation::reg(preg);
200
11.8M
        for entry in &self.ctx.bundles[bundle].ranges {
201
11.8M
            let key = LiveRangeKey::from_range(&entry.range);
202
11.8M
            let res = self.ctx.pregs[reg.index()]
203
11.8M
                .allocations
204
11.8M
                .btree
205
11.8M
                .insert(key, entry.index);
206
207
            // We disallow LR overlap within bundles, so this should never be possible.
208
11.8M
            debug_assert!(res.is_none());
209
        }
210
211
7.75M
        AllocRegResult::Allocated(Allocation::reg(preg))
212
68.3M
    }
213
214
286k
    pub fn evict_bundle(&mut self, bundle: LiveBundleIndex) {
215
286k
        trace!(
216
            "evicting bundle {:?}: alloc {:?}",
217
            bundle,
218
0
            self.ctx.bundles[bundle].allocation
219
        );
220
286k
        let preg = match self.ctx.bundles[bundle].allocation.as_reg() {
221
286k
            Some(preg) => preg,
222
            None => {
223
0
                trace!(
224
                    "  -> has no allocation! {:?}",
225
0
                    self.ctx.bundles[bundle].allocation
226
                );
227
0
                return;
228
            }
229
        };
230
286k
        let preg_idx = PRegIndex::new(preg.index());
231
286k
        self.ctx.bundles[bundle].allocation = Allocation::none();
232
2.30M
        for entry in &self.ctx.bundles[bundle].ranges {
233
2.30M
            trace!(" -> removing LR {:?} from reg {:?}", entry.index, preg_idx);
234
2.30M
            self.ctx.pregs[preg_idx.index()]
235
2.30M
                .allocations
236
2.30M
                .btree
237
2.30M
                .remove(&LiveRangeKey::from_range(&entry.range));
238
        }
239
286k
        let prio = self.ctx.bundles[bundle].prio;
240
286k
        trace!(" -> prio {}; back into queue", prio);
241
286k
        self.ctx
242
286k
            .allocation_queue
243
286k
            .insert(bundle, prio as usize, PReg::invalid());
244
286k
    }
245
246
848k
    pub fn bundle_spill_weight(&self, bundle: LiveBundleIndex) -> u32 {
247
848k
        self.ctx.bundles[bundle].cached_spill_weight()
248
848k
    }
249
250
13.1M
    pub fn maximum_spill_weight_in_bundle_set(&self, bundles: &[LiveBundleIndex]) -> u32 {
251
13.1M
        trace!("maximum_spill_weight_in_bundle_set: {:?}", bundles);
252
13.1M
        let m = bundles
253
13.1M
            .iter()
254
14.0M
            .map(|&b| {
255
14.0M
                let w = self.ctx.bundles[b].cached_spill_weight();
256
14.0M
                trace!("bundle{}: {}", b.index(), w);
257
14.0M
                w
258
14.0M
            })
259
13.1M
            .max()
260
13.1M
            .unwrap_or(0);
261
13.1M
        trace!(" -> max: {}", m);
262
13.1M
        m
263
13.1M
    }
264
265
12.3M
    pub fn recompute_bundle_properties(&mut self, bundle: LiveBundleIndex) {
266
12.3M
        trace!("recompute bundle properties: bundle {:?}", bundle);
267
268
        let minimal;
269
12.3M
        let mut fixed = false;
270
12.3M
        let mut fixed_def = false;
271
12.3M
        let mut stack = false;
272
12.3M
        let bundledata = &self.ctx.bundles[bundle];
273
12.3M
        let num_ranges = bundledata.ranges.len();
274
12.3M
        let first_range = bundledata.ranges[0].index;
275
12.3M
        let first_range_data = &self.ctx.ranges[first_range];
276
277
12.3M
        self.ctx.bundles[bundle].prio = self.compute_bundle_prio(bundle);
278
12.3M
        self.ctx.bundles[bundle].limit = self.compute_bundle_limit(bundle);
279
280
12.3M
        if first_range_data.vreg.is_invalid() {
281
0
            trace!("  -> no vreg; minimal and fixed");
282
0
            minimal = true;
283
0
            fixed = true;
284
12.3M
        } else if num_ranges == 1 {
285
18.1M
            for u in &first_range_data.uses {
286
18.1M
                trace!("  -> use: {:?}", u);
287
18.1M
                if let OperandConstraint::FixedReg(_) = u.operand.constraint() {
288
557k
                    trace!("  -> fixed operand at {:?}: {:?}", u.pos, u.operand);
289
557k
                    fixed = true;
290
557k
                    if u.operand.kind() == OperandKind::Def {
291
87.1k
                        trace!("  -> is fixed def");
292
87.1k
                        fixed_def = true;
293
470k
                    }
294
17.5M
                }
295
18.1M
                if let OperandConstraint::Stack = u.operand.constraint() {
296
0
                    trace!("  -> stack operand at {:?}: {:?}", u.pos, u.operand);
297
0
                    stack = true;
298
18.1M
                }
299
18.1M
                if stack && fixed {
300
0
                    break;
301
18.1M
                }
302
            }
303
            // Minimal if there is only one LR and only up to one use
304
            // in that LR, and extent of range is consistent with the
305
            // "minimal range for use" definition.
306
11.1M
            minimal = match &first_range_data.uses[..] {
307
11.1M
                [] => true,
308
9.92M
                [only_use] => {
309
                    // We use a "contains" rather than "exact" range
310
                    // check because a smaller-than-min range is also
311
                    // OK, if some logic produced it for a valid
312
                    // reason -- for example, a dead def. We don't
313
                    // want to livelock on "smaller than minimal"
314
                    // ranges.
315
9.92M
                    let min_range = minimal_range_for_use(only_use);
316
9.92M
                    min_range.contains(&first_range_data.range)
317
                }
318
1.16M
                _ => false,
319
            };
320
11.1M
            trace!("  -> minimal: {}", minimal);
321
1.22M
        } else {
322
1.22M
            minimal = false;
323
1.22M
        }
324
325
12.3M
        let spill_weight = if minimal {
326
9.85M
            if fixed {
327
159k
                trace!("  -> fixed and minimal");
328
159k
                MINIMAL_FIXED_BUNDLE_SPILL_WEIGHT
329
9.69M
            } else if let Some(limit) = self.ctx.bundles[bundle].limit {
330
0
                trace!("  -> limited({limit}) and minimal");
331
0
                MINIMAL_LIMITED_BUNDLE_SPILL_WEIGHT - u32::from(limit)
332
            } else {
333
9.69M
                trace!("  -> non-fixed and minimal");
334
9.69M
                MINIMAL_BUNDLE_SPILL_WEIGHT
335
            }
336
        } else {
337
2.49M
            let mut total = SpillWeight::zero();
338
10.4M
            for entry in &self.ctx.bundles[bundle].ranges {
339
10.4M
                let range_data = &self.ctx.ranges[entry.index];
340
10.4M
                trace!(
341
                    "  -> uses spill weight: +{:?}",
342
0
                    range_data.uses_spill_weight()
343
                );
344
10.4M
                total = total + range_data.uses_spill_weight();
345
            }
346
347
2.49M
            if self.ctx.bundles[bundle].prio > 0 {
348
2.47M
                let final_weight = (total.to_f32() as u32) / self.ctx.bundles[bundle].prio;
349
2.47M
                trace!(
350
                    " -> dividing by prio {}; final weight {}",
351
0
                    self.ctx.bundles[bundle].prio,
352
                    final_weight
353
                );
354
2.47M
                core::cmp::min(BUNDLE_MAX_NORMAL_SPILL_WEIGHT, final_weight)
355
            } else {
356
19.9k
                0
357
            }
358
        };
359
360
12.3M
        self.ctx.bundles[bundle].set_cached_spill_weight_and_props(
361
12.3M
            spill_weight,
362
12.3M
            minimal,
363
12.3M
            fixed,
364
12.3M
            fixed_def,
365
12.3M
            stack,
366
        );
367
12.3M
    }
368
369
1.69M
    pub fn minimal_bundle(&self, bundle: LiveBundleIndex) -> bool {
370
1.69M
        self.ctx.bundles[bundle].cached_minimal()
371
1.69M
    }
372
373
1.24M
    pub fn recompute_range_properties(&mut self, range: LiveRangeIndex) {
374
1.24M
        let rangedata = &mut self.ctx.ranges[range];
375
1.24M
        let mut w = SpillWeight::zero();
376
4.31M
        for u in &rangedata.uses {
377
4.31M
            w = w + SpillWeight::from_bits(u.weight);
378
4.31M
            trace!("range{}: use {:?}", range.index(), u);
379
        }
380
1.24M
        rangedata.set_uses_spill_weight(w);
381
1.24M
        if rangedata.uses.len() > 0 && rangedata.uses[0].operand.kind() == OperandKind::Def {
382
268k
            // Note that we *set* the flag here, but we never *clear*
383
268k
            // it: it may be set by a progmove as well (which does not
384
268k
            // create an explicit use or def), and we want to preserve
385
268k
            // that. We will never split or trim ranges in a way that
386
268k
            // removes a def at the front and requires the flag to be
387
268k
            // cleared.
388
268k
            rangedata.set_flag(LiveRangeFlag::StartsAtDef);
389
980k
        }
390
1.24M
    }
391
392
10.2M
    pub fn get_or_create_spill_bundle(
393
10.2M
        &mut self,
394
10.2M
        bundle: LiveBundleIndex,
395
10.2M
        create_if_absent: bool,
396
10.2M
    ) -> Option<LiveBundleIndex> {
397
10.2M
        let ssidx = self.ctx.bundles[bundle].spillset;
398
10.2M
        let idx = self.ctx.spillsets[ssidx].spill_bundle;
399
10.2M
        if idx.is_valid() {
400
2.32M
            Some(idx)
401
7.95M
        } else if create_if_absent {
402
332k
            let idx = self.ctx.bundles.add(self.ctx.scratch_bump.clone());
403
332k
            self.ctx.spillsets[ssidx].spill_bundle = idx;
404
332k
            self.ctx.bundles[idx].spillset = ssidx;
405
332k
            self.ctx.spilled_bundles.push(idx);
406
332k
            Some(idx)
407
        } else {
408
7.62M
            None
409
        }
410
10.2M
    }
411
412
767k
    pub fn split_and_requeue_bundle(
413
767k
        &mut self,
414
767k
        bundle: LiveBundleIndex,
415
767k
        mut split_at: ProgPoint,
416
767k
        hint: PReg,
417
767k
        // Do we trim the parts around the split and put them in the
418
767k
        // spill bundle?
419
767k
        mut trim_ends_into_spill_bundle: bool,
420
767k
    ) {
421
767k
        self.ctx.output.stats.splits += 1;
422
767k
        trace!(
423
            "split bundle {bundle:?} at {split_at:?} and requeue with reg hint (for first part) {hint:?}"
424
        );
425
426
        // Split `bundle` at `split_at`, creating new LiveRanges and
427
        // bundles (and updating vregs' linked lists appropriately),
428
        // and enqueue the new bundles.
429
430
767k
        let spillset = self.ctx.bundles[bundle].spillset;
431
432
        // Have we reached the maximum split count? If so, fall back
433
        // to a "minimal bundles and spill bundle" setup for this
434
        // bundle. See the doc-comment on
435
        // `split_into_minimal_bundles()` above for more.
436
767k
        if self.ctx.spillsets[spillset].splits >= MAX_SPLITS_PER_SPILLSET {
437
130k
            self.split_into_minimal_bundles(bundle, hint);
438
130k
            return;
439
637k
        }
440
637k
        self.ctx.spillsets[spillset].splits += 1;
441
442
637k
        debug_assert!(!self.ctx.bundles[bundle].ranges.is_empty());
443
        // Split point *at* start is OK; this means we peel off
444
        // exactly one use to create a minimal bundle.
445
637k
        let bundle_start = self.ctx.bundles[bundle].ranges.first().unwrap().range.from;
446
637k
        debug_assert!(split_at >= bundle_start);
447
637k
        let bundle_end = self.ctx.bundles[bundle].ranges.last().unwrap().range.to;
448
637k
        debug_assert!(split_at < bundle_end);
449
450
637k
        trace!(
451
            "bundle_start = {:?} bundle_end = {:?}",
452
            bundle_start,
453
            bundle_end
454
        );
455
456
        // Is the bundle at most spanning one instruction? Then
457
        // there's nothing we can do with this logic (we will not
458
        // split in the middle of the instruction). Fall back to the
459
        // minimal-bundle splitting in this case as well.
460
637k
        if bundle_end.prev().inst() == bundle_start.inst() {
461
2.63k
            trace!(" -> spans only one inst; splitting into minimal bundles");
462
2.63k
            self.split_into_minimal_bundles(bundle, hint);
463
2.63k
            return;
464
634k
        }
465
466
        // Is the split point *at* the start? If so, peel off the
467
        // program point with the first use: set the split point just
468
        // after it, or just before it if it comes after the start of
469
        // the bundle.
470
634k
        if split_at == bundle_start {
471
            // Find any uses; if none, just chop off one instruction.
472
120k
            let mut first_use = None;
473
121k
            'outer: for entry in &self.ctx.bundles[bundle].ranges {
474
121k
                for u in &self.ctx.ranges[entry.index].uses {
475
120k
                    first_use = Some(u.pos);
476
120k
                    break 'outer;
477
                }
478
            }
479
120k
            trace!(" -> first use loc is {:?}", first_use);
480
120k
            split_at = match first_use {
481
120k
                Some(pos) => {
482
120k
                    if pos.inst() == bundle_start.inst() {
483
119k
                        ProgPoint::before(pos.inst().next())
484
                    } else {
485
1.03k
                        ProgPoint::before(pos.inst())
486
                    }
487
                }
488
0
                None => ProgPoint::before(
489
0
                    self.ctx.bundles[bundle]
490
0
                        .ranges
491
0
                        .first()
492
0
                        .unwrap()
493
0
                        .range
494
0
                        .from
495
0
                        .inst()
496
0
                        .next(),
497
                ),
498
            };
499
120k
            trace!(
500
                "split point is at bundle start; advancing to {:?}",
501
                split_at
502
            );
503
        } else {
504
            // Don't split in the middle of an instruction -- this could
505
            // create impossible moves (we cannot insert a move between an
506
            // instruction's uses and defs).
507
513k
            if split_at.pos() == InstPosition::After {
508
309k
                split_at = split_at.next();
509
309k
            }
510
513k
            if split_at >= bundle_end {
511
6.97k
                split_at = split_at.prev().prev();
512
506k
            }
513
        }
514
515
634k
        debug_assert!(split_at > bundle_start && split_at < bundle_end);
516
517
        // We need to find which LRs fall on each side of the split,
518
        // which LR we need to split down the middle, then update the
519
        // current bundle, create a new one, and (re)-queue both.
520
521
634k
        trace!(" -> LRs: {:?}", self.ctx.bundles[bundle].ranges);
522
523
634k
        let mut last_lr_in_old_bundle_idx = 0; // last LR-list index in old bundle
524
634k
        let mut first_lr_in_new_bundle_idx = 0; // first LR-list index in new bundle
525
1.88M
        for (i, entry) in self.ctx.bundles[bundle].ranges.iter().enumerate() {
526
1.88M
            if split_at > entry.range.from {
527
1.87M
                last_lr_in_old_bundle_idx = i;
528
1.87M
                first_lr_in_new_bundle_idx = i;
529
1.87M
            }
530
1.88M
            if split_at < entry.range.to {
531
634k
                first_lr_in_new_bundle_idx = i;
532
533
                // When the bundle contains a fixed constraint, we advance the split point to right
534
                // before the first instruction with a fixed use present.
535
634k
                if self.ctx.bundles[bundle].cached_fixed() {
536
1.07M
                    for u in &self.ctx.ranges[entry.index].uses {
537
1.07M
                        if u.pos < split_at {
538
617k
                            continue;
539
457k
                        }
540
541
457k
                        if matches!(u.operand.constraint(), OperandConstraint::FixedReg { .. }) {
542
78.4k
                            split_at = ProgPoint::before(u.pos.inst());
543
544
78.4k
                            if split_at > entry.range.from {
545
78.4k
                                last_lr_in_old_bundle_idx = i;
546
78.4k
                            }
547
548
78.4k
                            trace!(" -> advancing split point to {split_at:?}");
549
550
78.4k
                            trim_ends_into_spill_bundle = false;
551
552
78.4k
                            break;
553
378k
                        }
554
                    }
555
508k
                }
556
557
634k
                break;
558
1.25M
            }
559
        }
560
561
634k
        trace!(
562
            " -> last LR in old bundle: LR {:?}",
563
0
            self.ctx.bundles[bundle].ranges[last_lr_in_old_bundle_idx]
564
        );
565
634k
        trace!(
566
            " -> first LR in new bundle: LR {:?}",
567
0
            self.ctx.bundles[bundle].ranges[first_lr_in_new_bundle_idx]
568
        );
569
570
        // Take the sublist of LRs that will go in the new bundle.
571
634k
        let mut new_lr_list: LiveRangeList = LiveRangeList::new_in(self.ctx.bump());
572
634k
        new_lr_list.extend(
573
634k
            self.ctx.bundles[bundle]
574
634k
                .ranges
575
634k
                .iter()
576
634k
                .cloned()
577
634k
                .skip(first_lr_in_new_bundle_idx),
578
        );
579
634k
        self.ctx.bundles[bundle]
580
634k
            .ranges
581
634k
            .truncate(last_lr_in_old_bundle_idx + 1);
582
634k
        self.ctx.bundles[bundle].ranges.shrink_to_fit();
583
584
        // If the first entry in `new_lr_list` is a LR that is split
585
        // down the middle, replace it with a new LR and chop off the
586
        // end of the same LR in the original list.
587
634k
        if split_at > new_lr_list[0].range.from {
588
624k
            debug_assert_eq!(last_lr_in_old_bundle_idx, first_lr_in_new_bundle_idx);
589
624k
            let orig_lr = new_lr_list[0].index;
590
624k
            let new_lr = self.ctx.ranges.add(
591
624k
                CodeRange {
592
624k
                    from: split_at,
593
624k
                    to: new_lr_list[0].range.to,
594
624k
                },
595
624k
                self.ctx.scratch_bump.clone(),
596
624k
            );
597
624k
            self.ctx.ranges[new_lr].vreg = self.ranges[orig_lr].vreg;
598
624k
            trace!(" -> splitting LR {:?} into {:?}", orig_lr, new_lr);
599
624k
            let first_use = self.ctx.ranges[orig_lr]
600
624k
                .uses
601
624k
                .iter()
602
2.37M
                .position(|u| u.pos >= split_at)
603
624k
                .unwrap_or(self.ctx.ranges[orig_lr].uses.len());
604
624k
            let mut rest_uses = UseList::new_in(self.ctx.bump());
605
624k
            rest_uses.extend(
606
624k
                self.ctx.ranges[orig_lr]
607
624k
                    .uses
608
624k
                    .iter()
609
624k
                    .cloned()
610
624k
                    .skip(first_use),
611
            );
612
624k
            self.ctx.ranges[new_lr].uses = rest_uses;
613
624k
            self.ctx.ranges[orig_lr].uses.truncate(first_use);
614
624k
            self.ctx.ranges[orig_lr].uses.shrink_to_fit();
615
624k
            self.recompute_range_properties(orig_lr);
616
624k
            self.recompute_range_properties(new_lr);
617
624k
            new_lr_list[0].index = new_lr;
618
624k
            new_lr_list[0].range = self.ctx.ranges[new_lr].range;
619
624k
            self.ctx.ranges[orig_lr].range.to = split_at;
620
624k
            self.ctx.bundles[bundle].ranges[last_lr_in_old_bundle_idx].range =
621
624k
                self.ctx.ranges[orig_lr].range;
622
623
            // Perform a lazy split in the VReg data. We just
624
            // append the new LR and its range; we will sort by
625
            // start of range, and fix up range ends, once when we
626
            // iterate over the VReg's ranges after allocation
627
            // completes (this is the only time when order
628
            // matters).
629
624k
            self.ctx.vregs[self.ctx.ranges[new_lr].vreg]
630
624k
                .ranges
631
624k
                .push(LiveRangeListEntry {
632
624k
                    range: self.ctx.ranges[new_lr].range,
633
624k
                    index: new_lr,
634
624k
                });
635
9.68k
        }
636
637
634k
        let new_bundle = self.ctx.bundles.add(self.ctx.scratch_bump.clone());
638
634k
        trace!(" -> creating new bundle {:?}", new_bundle);
639
634k
        self.ctx.bundles[new_bundle].spillset = spillset;
640
4.05M
        for entry in &new_lr_list {
641
4.05M
            self.ctx.ranges[entry.index].bundle = new_bundle;
642
4.05M
        }
643
634k
        self.ctx.bundles[new_bundle].ranges = new_lr_list;
644
645
634k
        if trim_ends_into_spill_bundle {
646
            // Finally, handle moving LRs to the spill bundle when
647
            // appropriate: If the first range in `new_bundle` or last
648
            // range in `bundle` has "empty space" beyond the first or
649
            // last use (respectively), trim it and put an empty LR into
650
            // the spill bundle.  (We are careful to treat the "starts at
651
            // def" flag as an implicit first def even if no def-type Use
652
            // is present.)
653
1.37M
            while let Some(entry) = self.ctx.bundles[bundle].ranges.last().cloned() {
654
1.37M
                let end = entry.range.to;
655
1.37M
                let vreg = self.ctx.ranges[entry.index].vreg;
656
1.37M
                let last_use = self.ctx.ranges[entry.index].uses.last().map(|u| u.pos);
657
1.37M
                if last_use.is_none() {
658
936k
                    let spill = self
659
936k
                        .get_or_create_spill_bundle(bundle, /* create_if_absent = */ true)
660
936k
                        .unwrap();
661
936k
                    trace!(
662
                        " -> bundle {:?} range {:?}: no uses; moving to spill bundle {:?}",
663
                        bundle,
664
                        entry.index,
665
                        spill
666
                    );
667
936k
                    self.ctx.bundles[spill].ranges.push(entry);
668
936k
                    self.ctx.bundles[bundle].ranges.pop();
669
936k
                    self.ctx.ranges[entry.index].bundle = spill;
670
936k
                    continue;
671
436k
                }
672
436k
                let last_use = last_use.unwrap();
673
436k
                let split = ProgPoint::before(last_use.inst().next());
674
436k
                if split < end {
675
294k
                    let spill = self
676
294k
                        .get_or_create_spill_bundle(bundle, /* create_if_absent = */ true)
677
294k
                        .unwrap();
678
294k
                    self.ctx.bundles[bundle].ranges.last_mut().unwrap().range.to = split;
679
294k
                    self.ctx.ranges[self.ctx.bundles[bundle].ranges.last().unwrap().index]
680
294k
                        .range
681
294k
                        .to = split;
682
294k
                    let range = CodeRange {
683
294k
                        from: split,
684
294k
                        to: end,
685
294k
                    };
686
294k
                    let empty_lr = self.ctx.ranges.add(range, self.ctx.scratch_bump.clone());
687
294k
                    self.ctx.bundles[spill].ranges.push(LiveRangeListEntry {
688
294k
                        range,
689
294k
                        index: empty_lr,
690
294k
                    });
691
294k
                    self.ctx.ranges[empty_lr].bundle = spill;
692
294k
                    self.ctx.vregs[vreg].ranges.push(LiveRangeListEntry {
693
294k
                        range,
694
294k
                        index: empty_lr,
695
294k
                    });
696
294k
                    trace!(
697
                        " -> bundle {:?} range {:?}: last use implies split point {:?}",
698
                        bundle,
699
                        entry.index,
700
                        split
701
                    );
702
294k
                    trace!(
703
                    " -> moving trailing empty region to new spill bundle {:?} with new LR {:?}",
704
                    spill,
705
                    empty_lr
706
                );
707
142k
                }
708
436k
                break;
709
            }
710
1.40M
            while let Some(entry) = self.ctx.bundles[new_bundle].ranges.first().cloned() {
711
1.27M
                if self.ctx.ranges[entry.index].has_flag(LiveRangeFlag::StartsAtDef) {
712
271
                    break;
713
1.27M
                }
714
1.27M
                let start = entry.range.from;
715
1.27M
                let vreg = self.ctx.ranges[entry.index].vreg;
716
1.27M
                let first_use = self.ctx.ranges[entry.index].uses.first().map(|u| u.pos);
717
1.27M
                if first_use.is_none() {
718
967k
                    let spill = self
719
967k
                        .get_or_create_spill_bundle(new_bundle, /* create_if_absent = */ true)
720
967k
                        .unwrap();
721
967k
                    trace!(
722
                        " -> bundle {:?} range {:?}: no uses; moving to spill bundle {:?}",
723
                        new_bundle,
724
                        entry.index,
725
                        spill
726
                    );
727
967k
                    self.ctx.bundles[spill].ranges.push(entry);
728
967k
                    self.ctx.bundles[new_bundle].ranges.drain(..1);
729
967k
                    self.ctx.ranges[entry.index].bundle = spill;
730
967k
                    continue;
731
304k
                }
732
304k
                let first_use = first_use.unwrap();
733
304k
                let split = ProgPoint::before(first_use.inst());
734
304k
                if split > start {
735
236k
                    let spill = self
736
236k
                        .get_or_create_spill_bundle(new_bundle, /* create_if_absent = */ true)
737
236k
                        .unwrap();
738
236k
                    self.ctx.bundles[new_bundle]
739
236k
                        .ranges
740
236k
                        .first_mut()
741
236k
                        .unwrap()
742
236k
                        .range
743
236k
                        .from = split;
744
236k
                    self.ctx.ranges[self.ctx.bundles[new_bundle].ranges.first().unwrap().index]
745
236k
                        .range
746
236k
                        .from = split;
747
236k
                    let range = CodeRange {
748
236k
                        from: start,
749
236k
                        to: split,
750
236k
                    };
751
236k
                    let empty_lr = self.ctx.ranges.add(range, self.ctx.scratch_bump.clone());
752
236k
                    self.ctx.bundles[spill].ranges.push(LiveRangeListEntry {
753
236k
                        range,
754
236k
                        index: empty_lr,
755
236k
                    });
756
236k
                    self.ctx.ranges[empty_lr].bundle = spill;
757
236k
                    self.ctx.vregs[vreg].ranges.push(LiveRangeListEntry {
758
236k
                        range,
759
236k
                        index: empty_lr,
760
236k
                    });
761
236k
                    trace!(
762
                        " -> bundle {:?} range {:?}: first use implies split point {:?}",
763
                        bundle,
764
                        entry.index,
765
                        first_use,
766
                    );
767
236k
                    trace!(
768
                        " -> moving leading empty region to new spill bundle {:?} with new LR {:?}",
769
                        spill,
770
                        empty_lr
771
                    );
772
67.4k
                }
773
304k
                break;
774
            }
775
196k
        }
776
777
634k
        if self.ctx.bundles[bundle].ranges.len() > 0 {
778
632k
            self.recompute_bundle_properties(bundle);
779
632k
            let prio = self.ctx.bundles[bundle].prio;
780
632k
            self.ctx
781
632k
                .allocation_queue
782
632k
                .insert(bundle, prio as usize, hint);
783
632k
        }
784
634k
        if self.ctx.bundles[new_bundle].ranges.len() > 0 {
785
500k
            self.recompute_bundle_properties(new_bundle);
786
500k
            let prio = self.ctx.bundles[new_bundle].prio;
787
500k
            self.ctx
788
500k
                .allocation_queue
789
500k
                .insert(new_bundle, prio as usize, hint);
790
500k
        }
791
767k
    }
792
793
    /// Splits the given bundle into minimal bundles per Use, falling
794
    /// back onto the spill bundle. This must work for any bundle no
795
    /// matter how many conflicts.
796
    ///
797
    /// This is meant to solve a quadratic-cost problem that exists
798
    /// with "normal" splitting as implemented above. With that
799
    /// procedure, splitting a bundle produces two
800
    /// halves. Furthermore, it has cost linear in the length of the
801
    /// bundle, because the resulting half-bundles have their
802
    /// requirements recomputed with a new scan, and because we copy
803
    /// half the use-list over to the tail end sub-bundle.
804
    ///
805
    /// This works fine when a bundle has a handful of splits overall,
806
    /// but not when an input has a systematic pattern of conflicts
807
    /// that will require O(|bundle|) splits (e.g., every Use is
808
    /// constrained to a different fixed register than the last
809
    /// one). In such a case, we get quadratic behavior.
810
    ///
811
    /// This method implements a direct split into minimal bundles
812
    /// along the whole length of the bundle, putting the regions
813
    /// without uses in the spill bundle. We do this once the number
814
    /// of splits in an original bundle (tracked by spillset) reaches
815
    /// a pre-determined limit.
816
    ///
817
    /// This basically approximates what a non-splitting allocator
818
    /// would do: it "spills" the whole bundle to possibly a
819
    /// stackslot, or a second-chance register allocation at best, via
820
    /// the spill bundle; and then does minimal reservations of
821
    /// registers just at uses/defs and moves the "spilled" value
822
    /// into/out of them immediately.
823
133k
    pub fn split_into_minimal_bundles(&mut self, bundle: LiveBundleIndex, hint: PReg) {
824
133k
        assert_eq!(self.ctx.scratch_removed_lrs_vregs.len(), 0);
825
133k
        self.ctx.scratch_removed_lrs.clear();
826
827
133k
        let mut new_lrs: SmallVec<[(VRegIndex, LiveRangeIndex); 16]> = smallvec![];
828
133k
        let mut new_bundles: SmallVec<[LiveBundleIndex; 16]> = smallvec![];
829
830
133k
        let spillset = self.ctx.bundles[bundle].spillset;
831
133k
        let spill = self
832
133k
            .get_or_create_spill_bundle(bundle, /* create_if_absent = */ true)
833
133k
            .unwrap();
834
835
133k
        trace!("Splitting bundle {bundle:?} into minimal bundles with reg hint {hint:?}");
836
837
133k
        let mut spill_uses = UseList::new_in(self.ctx.bump());
838
839
133k
        let empty_vec = LiveRangeList::new_in(self.ctx.bump());
840
860k
        for entry in core::mem::replace(&mut self.ctx.bundles[bundle].ranges, empty_vec) {
841
860k
            let vreg = self.ctx.ranges[entry.index].vreg;
842
843
860k
            self.ctx.scratch_removed_lrs.insert(entry.index);
844
860k
            self.ctx.scratch_removed_lrs_vregs.insert(vreg);
845
846
860k
            trace!(" -> removing old LR {:?} for vreg {:?}", entry.index, vreg);
847
848
860k
            let mut spill_range = entry.range;
849
860k
            let mut spill_starts_def = false;
850
851
860k
            let empty_vec = UseList::new_in(self.ctx.bump());
852
1.32M
            for u in core::mem::replace(&mut self.ctx.ranges[entry.index].uses, empty_vec) {
853
1.32M
                trace!("   -> use {:?}", u);
854
855
1.32M
                let is_def = u.operand.kind() == OperandKind::Def;
856
857
                // If this use has an `any` constraint, eagerly migrate it to the spill range. The
858
                // reasoning here is that in the second-chance allocation for the spill bundle,
859
                // any-constrained uses will be easy to satisfy. Solving those constraints earlier
860
                // could create unnecessary conflicts with existing bundles that need to fit in a
861
                // register, more strict requirements, so we delay them eagerly.
862
1.32M
                if u.operand.constraint() == OperandConstraint::Any {
863
738k
                    trace!("    -> migrating this any-constrained use to the spill range");
864
738k
                    spill_uses.push(u);
865
866
                    // Remember if we're moving the def of this vreg into the spill range, so that
867
                    // we can set the appropriate flags on it later.
868
738k
                    spill_starts_def = spill_starts_def || is_def;
869
870
738k
                    continue;
871
590k
                }
872
873
                // If this is a def, make sure that the spill range
874
                // starts before the next instruction rather than at
875
                // this position: the value is available only *after*
876
                // this position.
877
590k
                if is_def {
878
17.9k
                    trace!("    -> moving the spill range forward by one");
879
17.9k
                    spill_range.from = ProgPoint::before(u.pos.inst().next());
880
572k
                }
881
882
                // Create a new LR.
883
590k
                let cr = minimal_range_for_use(&u);
884
590k
                let lr = self.ctx.ranges.add(cr, self.ctx.scratch_bump.clone());
885
590k
                new_lrs.push((vreg, lr));
886
590k
                self.ctx.ranges[lr].uses.push(u);
887
590k
                self.ctx.ranges[lr].vreg = vreg;
888
889
                // Create a new bundle that contains only this LR.
890
590k
                let new_bundle = self.ctx.bundles.add(self.ctx.scratch_bump.clone());
891
590k
                self.ctx.ranges[lr].bundle = new_bundle;
892
590k
                self.ctx.bundles[new_bundle].spillset = spillset;
893
590k
                self.ctx.bundles[new_bundle]
894
590k
                    .ranges
895
590k
                    .push(LiveRangeListEntry {
896
590k
                        range: cr,
897
590k
                        index: lr,
898
590k
                    });
899
590k
                new_bundles.push(new_bundle);
900
901
                // If this use was a Def, set the StartsAtDef flag for the new LR.
902
590k
                if is_def {
903
17.9k
                    self.ctx.ranges[lr].set_flag(LiveRangeFlag::StartsAtDef);
904
572k
                }
905
906
590k
                trace!(
907
                    "    -> created new LR {:?} range {:?} with new bundle {:?} for this use",
908
                    lr,
909
                    cr,
910
                    new_bundle
911
                );
912
            }
913
914
860k
            if !spill_range.is_empty() {
915
                // Make one entry in the spill bundle that covers the whole range.
916
                // TODO: it might be worth tracking enough state to only create this LR when there is
917
                // open space in the original LR.
918
858k
                let spill_lr = self
919
858k
                    .ctx
920
858k
                    .ranges
921
858k
                    .add(spill_range, self.ctx.scratch_bump.clone());
922
858k
                self.ctx.ranges[spill_lr].vreg = vreg;
923
858k
                self.ctx.ranges[spill_lr].bundle = spill;
924
858k
                self.ctx.ranges[spill_lr].uses.extend(spill_uses.drain(..));
925
858k
                new_lrs.push((vreg, spill_lr));
926
927
858k
                if spill_starts_def {
928
4.60k
                    self.ctx.ranges[spill_lr].set_flag(LiveRangeFlag::StartsAtDef);
929
854k
                }
930
931
858k
                self.ctx.bundles[spill].ranges.push(LiveRangeListEntry {
932
858k
                    range: spill_range,
933
858k
                    index: spill_lr,
934
858k
                });
935
858k
                self.ctx.ranges[spill_lr].bundle = spill;
936
858k
                trace!(
937
                    "  -> added spill range {:?} in new LR {:?} in spill bundle {:?}",
938
                    spill_range,
939
                    spill_lr,
940
                    spill
941
                );
942
            } else {
943
2.05k
                assert!(spill_uses.is_empty());
944
            }
945
        }
946
947
        // Remove all of the removed LRs from respective vregs' lists.
948
148k
        for vreg in self.ctx.scratch_removed_lrs_vregs.drain() {
949
148k
            let lrs = &mut self.ctx.scratch_removed_lrs;
950
148k
            self.ctx.vregs[vreg]
951
148k
                .ranges
952
1.68M
                .retain(|entry| !lrs.contains(&entry.index));
953
        }
954
955
        // Add the new LRs to their respective vreg lists.
956
1.44M
        for (vreg, lr) in new_lrs {
957
1.44M
            let range = self.ctx.ranges[lr].range;
958
1.44M
            let entry = LiveRangeListEntry { range, index: lr };
959
1.44M
            self.ctx.vregs[vreg].ranges.push(entry);
960
1.44M
        }
961
962
        // Recompute bundle properties for all new bundles and enqueue
963
        // them.
964
590k
        for bundle in new_bundles {
965
590k
            if self.ctx.bundles[bundle].ranges.len() > 0 {
966
590k
                self.recompute_bundle_properties(bundle);
967
590k
                let prio = self.ctx.bundles[bundle].prio;
968
590k
                self.ctx
969
590k
                    .allocation_queue
970
590k
                    .insert(bundle, prio as usize, hint);
971
590k
            }
972
        }
973
133k
    }
974
975
11.0M
    pub fn process_bundle(
976
11.0M
        &mut self,
977
11.0M
        bundle: LiveBundleIndex,
978
11.0M
        hint: PReg,
979
11.0M
    ) -> Result<(), RegAllocError> {
980
11.0M
        let class = self.ctx.spillsets[self.bundles[bundle].spillset].class;
981
982
        // Grab a hint from either the queue or our spillset, if any.
983
11.0M
        let mut hint = if hint != PReg::invalid() {
984
1.02M
            hint
985
        } else {
986
10.0M
            self.ctx.spillsets[self.bundles[bundle].spillset].hint
987
        };
988
11.0M
        if self.ctx.pregs[hint.index()].is_stack {
989
76.5k
            hint = PReg::invalid();
990
11.0M
        }
991
11.0M
        trace!("process_bundle: bundle {bundle:?} hint {hint:?}");
992
993
11.0M
        let req = match self.compute_requirement(bundle) {
994
10.8M
            Ok(req) => req,
995
204k
            Err(conflict) => {
996
204k
                trace!("conflict!: {:?}", conflict);
997
                // We have to split right away. We'll find a point to
998
                // split that would allow at least the first half of the
999
                // split to be conflict-free.
1000
204k
                debug_assert!(
1001
0
                    !self.minimal_bundle(bundle),
1002
                    "Minimal bundle with conflict!"
1003
                );
1004
204k
                self.split_and_requeue_bundle(
1005
204k
                    bundle,
1006
204k
                    /* split_at_point = */ conflict.suggested_split_point(),
1007
204k
                    hint,
1008
                    /* trim_ends_into_spill_bundle = */
1009
204k
                    conflict.should_trim_edges_around_split(),
1010
                );
1011
204k
                return Ok(());
1012
            }
1013
        };
1014
1015
        // If no requirement at all (because no uses), and *if* a
1016
        // spill bundle is already present, then move the LRs over to
1017
        // the spill bundle right away.
1018
10.8M
        match req {
1019
            Requirement::Any => {
1020
88.3k
                if let Some(spill) =
1021
7.71M
                    self.get_or_create_spill_bundle(bundle, /* create_if_absent = */ false)
1022
                {
1023
88.3k
                    let empty_vec = LiveRangeList::new_in(self.ctx.bump());
1024
88.3k
                    let mut list =
1025
88.3k
                        core::mem::replace(&mut self.ctx.bundles[bundle].ranges, empty_vec);
1026
347k
                    for entry in &list {
1027
347k
                        self.ctx.ranges[entry.index].bundle = spill;
1028
347k
                    }
1029
88.3k
                    self.ctx.bundles[spill].ranges.extend(list.drain(..));
1030
88.3k
                    return Ok(());
1031
7.62M
                }
1032
            }
1033
3.16M
            _ => {}
1034
        }
1035
1036
        // Try to allocate!
1037
10.7M
        let mut attempts = 0;
1038
10.7M
        let mut scratch = core::mem::take(&mut self.ctx.scratch_conflicts);
1039
10.7M
        let mut lowest_cost_evict_conflict_set = core::mem::take(&mut self.ctx.scratch_bundle);
1040
        'outer: loop {
1041
11.0M
            attempts += 1;
1042
11.0M
            trace!("attempt {}, req {:?}", attempts, req);
1043
11.0M
            debug_assert!(attempts < 100 * self.func.num_insts());
1044
1045
11.0M
            let fixed_preg = match req {
1046
636k
                Requirement::FixedReg(preg) | Requirement::FixedStack(preg) => Some(preg),
1047
2.81M
                Requirement::Register | Requirement::Limit(..) => None,
1048
                Requirement::Stack => {
1049
                    // If we must be on the stack, mark our spillset
1050
                    // as required immediately.
1051
0
                    let spillset = self.bundles[bundle].spillset;
1052
0
                    self.spillsets[spillset].required = true;
1053
0
                    return Ok(());
1054
                }
1055
1056
                Requirement::Any => {
1057
7.62M
                    self.ctx.spilled_bundles.push(bundle);
1058
7.62M
                    break;
1059
                }
1060
            };
1061
            // Scan all pregs, or the one fixed preg, and attempt to allocate.
1062
1063
3.45M
            let mut lowest_cost_evict_conflict_cost: Option<u32> = None;
1064
3.45M
            lowest_cost_evict_conflict_set.clear();
1065
1066
3.45M
            let mut lowest_cost_split_conflict_cost: Option<u32> = None;
1067
3.45M
            let mut lowest_cost_split_conflict_point = ProgPoint::before(Inst::new(0));
1068
3.45M
            let mut lowest_cost_split_conflict_reg = PReg::invalid();
1069
1070
            // Heuristic: start the scan for an available
1071
            // register at an offset influenced both by our
1072
            // location in the code and by the bundle we're
1073
            // considering. This has the effect of spreading
1074
            // demand more evenly across registers.
1075
3.45M
            let scan_offset = self.ctx.ranges[self.bundles[bundle].ranges[0].index]
1076
3.45M
                .range
1077
3.45M
                .from
1078
3.45M
                .inst()
1079
3.45M
                .index()
1080
3.45M
                + bundle.index();
1081
1082
3.45M
            self.ctx.output.stats.process_bundle_reg_probe_start_any += 1;
1083
3.45M
            let limit = self.bundles[bundle].limit.map(|l| l as usize);
1084
31.2M
            for preg in RegTraversalIter::new(
1085
3.45M
                self.env,
1086
3.45M
                class,
1087
3.45M
                fixed_preg,
1088
3.45M
                hint.as_valid(),
1089
3.45M
                scan_offset,
1090
3.45M
                limit,
1091
            ) {
1092
31.2M
                self.ctx.output.stats.process_bundle_reg_probes_any += 1;
1093
31.2M
                let preg_idx = PRegIndex::new(preg.index());
1094
31.2M
                trace!("trying preg {:?}", preg_idx);
1095
1096
31.2M
                let scan_limit_cost = match (
1097
31.2M
                    lowest_cost_evict_conflict_cost,
1098
31.2M
                    lowest_cost_split_conflict_cost,
1099
                ) {
1100
24.8M
                    (Some(a), Some(b)) => Some(core::cmp::max(a, b)),
1101
6.41M
                    _ => None,
1102
                };
1103
31.2M
                match self.try_to_allocate_bundle_to_reg(
1104
31.2M
                    bundle,
1105
31.2M
                    preg_idx,
1106
31.2M
                    scan_limit_cost,
1107
31.2M
                    &mut scratch,
1108
31.2M
                ) {
1109
2.60M
                    AllocRegResult::Allocated(alloc) => {
1110
2.60M
                        self.ctx.output.stats.process_bundle_reg_success_any += 1;
1111
2.60M
                        trace!(" -> allocated to any {:?}", preg_idx);
1112
2.60M
                        self.ctx.spillsets[self.ctx.bundles[bundle].spillset].hint =
1113
2.60M
                            alloc.as_reg().unwrap();
1114
                        // Success, return scratch memory to context and finish
1115
2.60M
                        break 'outer;
1116
                    }
1117
13.1M
                    AllocRegResult::Conflict(bundles, first_conflict_point) => {
1118
13.1M
                        trace!(
1119
                            " -> conflict with bundles {:?}, first conflict at {:?}",
1120
                            bundles,
1121
                            first_conflict_point
1122
                        );
1123
1124
13.1M
                        let conflict_cost = self.maximum_spill_weight_in_bundle_set(bundles);
1125
1126
13.1M
                        if lowest_cost_evict_conflict_cost.is_none()
1127
11.0M
                            || conflict_cost < lowest_cost_evict_conflict_cost.unwrap()
1128
4.53M
                        {
1129
4.53M
                            lowest_cost_evict_conflict_cost = Some(conflict_cost);
1130
4.53M
                            lowest_cost_evict_conflict_set.clear();
1131
4.53M
                            lowest_cost_evict_conflict_set.extend(bundles);
1132
8.64M
                        }
1133
1134
13.1M
                        let loop_depth =
1135
13.1M
                            self.ctx.cfginfo.approx_loop_depth[self.ctx.cfginfo.insn_block
1136
13.1M
                                [first_conflict_point.inst().index()]
1137
13.1M
                            .index()];
1138
13.1M
                        let move_cost = spill_weight_from_constraint(
1139
13.1M
                            OperandConstraint::Reg,
1140
13.1M
                            loop_depth as usize,
1141
                            /* is_def = */ true,
1142
                        )
1143
13.1M
                        .to_int();
1144
13.1M
                        if lowest_cost_split_conflict_cost.is_none()
1145
11.4M
                            || (conflict_cost + move_cost)
1146
11.4M
                                < lowest_cost_split_conflict_cost.unwrap()
1147
3.17M
                        {
1148
3.17M
                            lowest_cost_split_conflict_cost = Some(conflict_cost + move_cost);
1149
3.17M
                            lowest_cost_split_conflict_point = first_conflict_point;
1150
3.17M
                            lowest_cost_split_conflict_reg = preg;
1151
10.0M
                        }
1152
                    }
1153
13.1M
                    AllocRegResult::ConflictWithFixed(max_cost, point) => {
1154
13.1M
                        trace!(" -> conflict with fixed alloc; cost of other bundles up to point is {}, conflict at {:?}", max_cost, point);
1155
1156
13.1M
                        let loop_depth = self.ctx.cfginfo.approx_loop_depth
1157
13.1M
                            [self.ctx.cfginfo.insn_block[point.inst().index()].index()];
1158
13.1M
                        let move_cost = spill_weight_from_constraint(
1159
13.1M
                            OperandConstraint::Reg,
1160
13.1M
                            loop_depth as usize,
1161
                            /* is_def = */ true,
1162
                        )
1163
13.1M
                        .to_int();
1164
1165
13.1M
                        if lowest_cost_split_conflict_cost.is_none()
1166
12.5M
                            || (max_cost + move_cost) < lowest_cost_split_conflict_cost.unwrap()
1167
1.68M
                        {
1168
1.68M
                            lowest_cost_split_conflict_cost = Some(max_cost + move_cost);
1169
1.68M
                            lowest_cost_split_conflict_point = point;
1170
1.68M
                            lowest_cost_split_conflict_reg = preg;
1171
11.4M
                        }
1172
                    }
1173
                    AllocRegResult::ConflictHighCost => {
1174
                        // Simply don't consider -- we already have
1175
                        // a lower-cost conflict bundle option
1176
                        // to evict.
1177
2.37M
                        continue;
1178
                    }
1179
                }
1180
            }
1181
1182
            // Otherwise, we *require* a register, but didn't fit into
1183
            // any with current bundle assignments. Hence, we will need
1184
            // to either split or attempt to evict some bundles.
1185
1186
848k
            trace!(
1187
                " -> lowest cost evict: set {:?}, cost {:?}",
1188
                lowest_cost_evict_conflict_set,
1189
                lowest_cost_evict_conflict_cost,
1190
            );
1191
848k
            trace!(
1192
                " -> lowest cost split: cost {:?}, point {:?}, reg {:?}",
1193
                lowest_cost_split_conflict_cost,
1194
                lowest_cost_split_conflict_point,
1195
                lowest_cost_split_conflict_reg
1196
            );
1197
1198
            // If we reach here, we *must* have an option either to split or evict.
1199
848k
            debug_assert!(
1200
0
                lowest_cost_split_conflict_cost.is_some()
1201
0
                    || lowest_cost_evict_conflict_cost.is_some()
1202
            );
1203
1204
848k
            let our_spill_weight = self.bundle_spill_weight(bundle);
1205
848k
            trace!(" -> our spill weight: {}", our_spill_weight);
1206
1207
            // We detect the "too-many-live-registers" case here and
1208
            // return an error cleanly, rather than panicking, because
1209
            // the regalloc.rs fuzzer depends on the register
1210
            // allocator to correctly reject impossible-to-allocate
1211
            // programs in order to discard invalid test cases.
1212
848k
            if self.minimal_bundle(bundle)
1213
33.1k
                && (attempts >= 2
1214
33.1k
                    || lowest_cost_evict_conflict_cost.is_none()
1215
30.9k
                    || lowest_cost_evict_conflict_cost.unwrap() >= our_spill_weight)
1216
            {
1217
                // A minimal bundle pinned to one physical register cannot
1218
                // move, and a fixed reservation on that register (a clobber
1219
                // is modeled as one) cannot be evicted. The overlap is
1220
                // illegal: a clobber must not collide with a fixed def or
1221
                // late use. Reject it instead of panicking.
1222
2.11k
                if matches!(req, Requirement::FixedReg(_))
1223
2.11k
                    && lowest_cost_evict_conflict_cost.is_none()
1224
                {
1225
2.11k
                    return Err(RegAllocError::TooManyLiveRegs);
1226
0
                }
1227
0
                if matches!(req, Requirement::Register | Requirement::Limit(_)) {
1228
                    // Check if this is a too-many-live-registers situation.
1229
0
                    let range = self.ctx.bundles[bundle].ranges[0].range;
1230
0
                    trace!("checking for too many live regs");
1231
0
                    let mut min_bundles_assigned = 0;
1232
0
                    let mut fixed_assigned = 0;
1233
0
                    let mut total_regs = 0;
1234
0
                    for preg in self.env.preferred_regs_by_class[class as u8 as usize]
1235
0
                        .into_iter()
1236
0
                        .chain(self.env.non_preferred_regs_by_class[class as u8 as usize])
1237
                    {
1238
0
                        trace!(" -> PR {:?}", preg);
1239
0
                        let start = LiveRangeKey::from_range(&CodeRange {
1240
0
                            from: range.from.prev(),
1241
0
                            to: range.from.prev(),
1242
0
                        });
1243
0
                        for (key, lr) in self.ctx.pregs[preg.index()]
1244
0
                            .allocations
1245
0
                            .btree
1246
0
                            .range(start..)
1247
                        {
1248
0
                            let preg_range = key.to_range();
1249
0
                            if preg_range.to <= range.from {
1250
0
                                continue;
1251
0
                            }
1252
0
                            if preg_range.from >= range.to {
1253
0
                                break;
1254
0
                            }
1255
0
                            if lr.is_valid() {
1256
0
                                if self.minimal_bundle(self.ranges[*lr].bundle) {
1257
0
                                    trace!("  -> min bundle {:?}", lr);
1258
0
                                    min_bundles_assigned += 1;
1259
                                } else {
1260
0
                                    trace!("  -> non-min bundle {:?}", lr);
1261
                                }
1262
                            } else {
1263
0
                                trace!("  -> fixed bundle");
1264
0
                                fixed_assigned += 1;
1265
                            }
1266
                        }
1267
1268
                        // We also need to discard any registers that do not fit
1269
                        // under the limit--we cannot allocate to them.
1270
0
                        if let Requirement::Limit(limit) = req {
1271
0
                            if preg.hw_enc() >= limit as usize {
1272
0
                                continue;
1273
0
                            }
1274
0
                        }
1275
1276
0
                        total_regs += 1;
1277
                    }
1278
0
                    trace!(
1279
                        " -> total {}, fixed {}, min {}",
1280
                        total_regs,
1281
                        fixed_assigned,
1282
                        min_bundles_assigned
1283
                    );
1284
0
                    if min_bundles_assigned + fixed_assigned >= total_regs {
1285
0
                        return Err(RegAllocError::TooManyLiveRegs);
1286
0
                    }
1287
0
                }
1288
1289
0
                panic!("Could not allocate minimal bundle, but the allocation problem should be possible to solve");
1290
846k
            }
1291
1292
            // If our bundle's weight is less than or equal to(*) the
1293
            // evict cost, choose to split.  Also pick splitting if
1294
            // we're on our second or more attempt and we didn't
1295
            // allocate.  Also pick splitting if the conflict set is
1296
            // empty, meaning a fixed conflict that can't be evicted.
1297
            //
1298
            // (*) the "equal to" part is very important: it prevents
1299
            // an infinite loop where two bundles with equal spill
1300
            // cost continually evict each other in an infinite
1301
            // allocation loop. In such a case, the first bundle in
1302
            // wins, and the other splits.
1303
            //
1304
            // Note that we don't split if the bundle is minimal.
1305
846k
            if !self.minimal_bundle(bundle)
1306
815k
                && (attempts >= 2
1307
815k
                    || lowest_cost_evict_conflict_cost.is_none()
1308
723k
                    || our_spill_weight <= lowest_cost_evict_conflict_cost.unwrap())
1309
            {
1310
563k
                trace!(
1311
                    " -> deciding to split: our spill weight is {}",
1312
0
                    self.bundle_spill_weight(bundle)
1313
                );
1314
563k
                let bundle_start = self.ctx.bundles[bundle].ranges[0].range.from;
1315
563k
                let mut split_at_point =
1316
563k
                    core::cmp::max(lowest_cost_split_conflict_point, bundle_start);
1317
563k
                let requeue_with_reg = lowest_cost_split_conflict_reg;
1318
1319
                // Adjust `split_at_point` if it is within a deeper loop
1320
                // than the bundle start -- hoist it to just before the
1321
                // first loop header it encounters.
1322
563k
                let bundle_start_depth = self.ctx.cfginfo.approx_loop_depth
1323
563k
                    [self.ctx.cfginfo.insn_block[bundle_start.inst().index()].index()];
1324
563k
                let split_at_depth = self.ctx.cfginfo.approx_loop_depth
1325
563k
                    [self.ctx.cfginfo.insn_block[split_at_point.inst().index()].index()];
1326
563k
                if split_at_depth > bundle_start_depth {
1327
106k
                    for block in (self.ctx.cfginfo.insn_block[bundle_start.inst().index()].index()
1328
47.7k
                        + 1)
1329
47.7k
                        ..=self.ctx.cfginfo.insn_block[split_at_point.inst().index()].index()
1330
                    {
1331
106k
                        if self.ctx.cfginfo.approx_loop_depth[block] > bundle_start_depth {
1332
47.7k
                            split_at_point = self.ctx.cfginfo.block_entry[block];
1333
47.7k
                            break;
1334
59.1k
                        }
1335
                    }
1336
515k
                }
1337
1338
563k
                self.split_and_requeue_bundle(
1339
563k
                    bundle,
1340
563k
                    split_at_point,
1341
563k
                    requeue_with_reg,
1342
                    /* should_trim = */ true,
1343
                );
1344
1345
                // Success, return scratch memory to context and finish
1346
563k
                break 'outer;
1347
            } else {
1348
                // Evict all bundles in `conflicting bundles` and try again.
1349
282k
                self.ctx.output.stats.evict_bundle_event += 1;
1350
286k
                for &bundle in &lowest_cost_evict_conflict_set {
1351
286k
                    trace!(" -> evicting {:?}", bundle);
1352
286k
                    self.evict_bundle(bundle);
1353
286k
                    self.ctx.output.stats.evict_bundle_count += 1;
1354
                }
1355
            }
1356
        }
1357
1358
10.7M
        self.ctx.scratch_conflicts = scratch;
1359
10.7M
        self.ctx.scratch_bundle = lowest_cost_evict_conflict_set;
1360
10.7M
        return Ok(());
1361
11.0M
    }
1362
}
1363
1364
/// Compute the minimal range that covers a given use in a minimal
1365
/// bundle. This definition needs to be consistent between bundle
1366
/// property computation and minimal-range splitting (fallback
1367
/// splitting).
1368
///
1369
/// The cases are:
1370
/// - early def: whole instruction
1371
/// - late def: Late only
1372
/// - early use: Early only
1373
/// - late use: whole instruction
1374
10.5M
fn minimal_range_for_use(u: &Use) -> CodeRange {
1375
10.5M
    let early = ProgPoint::before(u.pos.inst());
1376
10.5M
    let late = ProgPoint::after(u.pos.inst());
1377
10.5M
    let next_early = ProgPoint::before(u.pos.inst().next());
1378
10.5M
    match (u.pos.pos(), u.operand.kind()) {
1379
3.22M
        (InstPosition::Before, OperandKind::Def) => CodeRange {
1380
3.22M
            from: early,
1381
3.22M
            to: next_early,
1382
3.22M
        },
1383
1.14M
        (InstPosition::Before, OperandKind::Use) => CodeRange {
1384
1.14M
            from: early,
1385
1.14M
            to: late,
1386
1.14M
        },
1387
5.97M
        (InstPosition::After, OperandKind::Def) => CodeRange {
1388
5.97M
            from: late,
1389
5.97M
            to: next_early,
1390
5.97M
        },
1391
172k
        (InstPosition::After, OperandKind::Use) => CodeRange {
1392
172k
            from: early,
1393
172k
            to: next_early,
1394
172k
        },
1395
    }
1396
10.5M
}