Coverage Report

Created: 2026-08-15 07:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/regalloc2/src/fastalloc/mod.rs
Line
Count
Source
1
use crate::moves::{MoveAndScratchResolver, ParallelMoves};
2
use crate::{cfg::CFGInfo, ion::Stats, Allocation, RegAllocError};
3
use crate::{ssa::validate_ssa, Edit, Function, MachineEnv, Output, ProgPoint};
4
use crate::{
5
    AllocationKind, Block, FxHashMap, Inst, InstPosition, Operand, OperandConstraint, OperandKind,
6
    OperandPos, PReg, PRegSet, RegClass, SpillSlot, VReg,
7
};
8
use alloc::format;
9
use alloc::{vec, vec::Vec};
10
use core::convert::TryInto;
11
use core::fmt;
12
use core::iter::FromIterator;
13
use core::ops::{BitAnd, BitOr, Deref, DerefMut, Index, IndexMut, Not};
14
15
mod iter;
16
mod lru;
17
mod vregset;
18
use iter::*;
19
use lru::*;
20
use vregset::VRegSet;
21
22
#[cfg(test)]
23
mod tests;
24
25
#[derive(Debug)]
26
struct Allocs {
27
    allocs: Vec<Allocation>,
28
    /// `inst_alloc_offsets[i]` is the offset into `allocs` for the allocations of
29
    /// instruction `i`'s operands.
30
    inst_alloc_offsets: Vec<u32>,
31
}
32
33
impl Allocs {
34
0
    fn new<F: Function>(func: &F) -> (Self, u32) {
35
0
        let mut allocs = Vec::new();
36
0
        let mut inst_alloc_offsets = Vec::with_capacity(func.num_insts());
37
0
        let mut max_operand_len = 0;
38
0
        let mut no_of_operands = 0;
39
0
        for inst in 0..func.num_insts() {
40
0
            let operands_len = func.inst_operands(Inst::new(inst)).len() as u32;
41
0
            max_operand_len = max_operand_len.max(operands_len);
42
0
            inst_alloc_offsets.push(no_of_operands as u32);
43
0
            no_of_operands += operands_len;
44
0
        }
45
0
        allocs.resize(no_of_operands as usize, Allocation::none());
46
0
        (
47
0
            Self {
48
0
                allocs,
49
0
                inst_alloc_offsets,
50
0
            },
51
0
            max_operand_len,
52
0
        )
53
0
    }
54
}
55
56
impl Index<(usize, usize)> for Allocs {
57
    type Output = Allocation;
58
59
    /// Retrieve the allocation for operand `idx.1` at instruction `idx.0`
60
0
    fn index(&self, idx: (usize, usize)) -> &Allocation {
61
0
        &self.allocs[self.inst_alloc_offsets[idx.0] as usize + idx.1]
62
0
    }
63
}
64
65
impl IndexMut<(usize, usize)> for Allocs {
66
0
    fn index_mut(&mut self, idx: (usize, usize)) -> &mut Allocation {
67
0
        &mut self.allocs[self.inst_alloc_offsets[idx.0] as usize + idx.1]
68
0
    }
69
}
70
71
#[derive(Debug)]
72
struct Stack<'a, F: Function> {
73
    num_spillslots: u32,
74
    func: &'a F,
75
}
76
77
impl<'a, F: Function> Stack<'a, F> {
78
0
    fn new(func: &'a F) -> Self {
79
0
        Self {
80
0
            num_spillslots: 0,
81
0
            func,
82
0
        }
83
0
    }
84
85
    /// Allocates a spill slot on the stack for `vreg`
86
0
    fn allocstack(&mut self, class: RegClass) -> SpillSlot {
87
0
        trace!("Allocating a spillslot for class {class:?}");
88
0
        let size: u32 = self.func.spillslot_size(class).try_into().unwrap();
89
        // Rest of this function was copied verbatim
90
        // from `Env::allocate_spillslot` in src/ion/spill.rs.
91
0
        let mut offset = self.num_spillslots;
92
        // Align up to `size`.
93
0
        debug_assert!(size.is_power_of_two());
94
0
        offset = (offset + size - 1) & !(size - 1);
95
0
        let slot = if self.func.multi_spillslot_named_by_last_slot() {
96
0
            offset + size - 1
97
        } else {
98
0
            offset
99
        };
100
0
        offset += size;
101
0
        self.num_spillslots = offset;
102
0
        trace!("Allocated slot: {slot}");
103
0
        SpillSlot::new(slot as usize)
104
0
    }
105
}
106
107
#[derive(Debug)]
108
pub struct State<'a, F: Function> {
109
    func: &'a F,
110
    /// The final output edits.
111
    edits: Vec<(ProgPoint, Edit)>,
112
    fixed_stack_slots: PRegSet,
113
    /// The scratch registers being used in the instruction being
114
    /// currently processed.
115
    scratch_regs: PartedByRegClass<Option<PReg>>,
116
    dedicated_scratch_regs: PartedByRegClass<Option<PReg>>,
117
    /// The set of registers that can be used for allocation in the
118
    /// early and late phases of an instruction.
119
    ///
120
    /// Allocatable registers that contain no vregs, registers that can be
121
    /// evicted can be in the set, and fixed stack slots are in this set.
122
    available_pregs: PartedByOperandPos<PRegSet>,
123
    /// Number of registers available for allocation for Reg and Any
124
    /// operands
125
    num_available_pregs: PartedByExclusiveOperandPos<PartedByRegClass<i16>>,
126
    /// The current allocations for all virtual registers.
127
    vreg_allocs: Vec<Allocation>,
128
    /// Spillslots for all virtual registers.
129
    /// `vreg_spillslots[i]` is the spillslot for virtual register `i`.
130
    vreg_spillslots: Vec<SpillSlot>,
131
    /// `vreg_in_preg[i]` is the virtual register currently in the physical register
132
    /// with index `i`.
133
    vreg_in_preg: Vec<VReg>,
134
    stack: Stack<'a, F>,
135
    /// Least-recently-used caches for register classes Int, Float, and Vector, respectively.
136
    lrus: Lrus,
137
}
138
139
impl<'a, F: Function> State<'a, F> {
140
0
    fn is_stack(&self, alloc: Allocation) -> bool {
141
0
        alloc.is_stack()
142
0
            || (alloc.is_reg() && self.fixed_stack_slots.contains(alloc.as_reg().unwrap()))
143
0
    }
144
145
0
    fn get_spillslot(&mut self, vreg: VReg) -> SpillSlot {
146
0
        if self.vreg_spillslots[vreg.vreg()].is_invalid() {
147
0
            self.vreg_spillslots[vreg.vreg()] = self.stack.allocstack(vreg.class());
148
0
        }
149
0
        self.vreg_spillslots[vreg.vreg()]
150
0
    }
151
152
0
    fn evict_vreg_in_preg(
153
0
        &mut self,
154
0
        inst: Inst,
155
0
        preg: PReg,
156
0
        pos: InstPosition,
157
0
    ) -> Result<(), RegAllocError> {
158
0
        trace!("Removing the vreg in preg {} for eviction", preg);
159
0
        let evicted_vreg = self.vreg_in_preg[preg.index()];
160
0
        trace!("The removed vreg: {}", evicted_vreg);
161
0
        debug_assert_ne!(evicted_vreg, VReg::invalid());
162
0
        if self.vreg_spillslots[evicted_vreg.vreg()].is_invalid() {
163
0
            self.vreg_spillslots[evicted_vreg.vreg()] = self.stack.allocstack(evicted_vreg.class());
164
0
        }
165
0
        let slot = self.vreg_spillslots[evicted_vreg.vreg()];
166
0
        self.vreg_allocs[evicted_vreg.vreg()] = Allocation::stack(slot);
167
0
        trace!("Move reason: eviction");
168
0
        self.add_move(
169
0
            inst,
170
0
            self.vreg_allocs[evicted_vreg.vreg()],
171
0
            Allocation::reg(preg),
172
0
            evicted_vreg.class(),
173
0
            pos,
174
        )
175
0
    }
176
177
0
    fn alloc_scratch_reg(
178
0
        &mut self,
179
0
        inst: Inst,
180
0
        class: RegClass,
181
0
        pos: InstPosition,
182
0
    ) -> Result<(), RegAllocError> {
183
0
        let avail_regs =
184
0
            self.available_pregs[OperandPos::Late] & self.available_pregs[OperandPos::Early];
185
0
        trace!("Checking {avail_regs} for scratch register for {class:?}");
186
0
        if let Some(preg) = self.lrus[class].last(avail_regs) {
187
0
            if self.vreg_in_preg[preg.index()] != VReg::invalid() {
188
0
                self.evict_vreg_in_preg(inst, preg, pos)?;
189
0
            }
190
0
            self.scratch_regs[class] = Some(preg);
191
0
            self.available_pregs[OperandPos::Early].remove(preg);
192
0
            self.available_pregs[OperandPos::Late].remove(preg);
193
0
            Ok(())
194
        } else {
195
0
            trace!("Can't get a scratch register for {class:?}");
196
0
            Err(RegAllocError::TooManyLiveRegs)
197
        }
198
0
    }
199
200
0
    fn add_move(
201
0
        &mut self,
202
0
        inst: Inst,
203
0
        from: Allocation,
204
0
        to: Allocation,
205
0
        class: RegClass,
206
0
        pos: InstPosition,
207
0
    ) -> Result<(), RegAllocError> {
208
0
        if self.is_stack(from) && self.is_stack(to) {
209
0
            if self.scratch_regs[class].is_none() {
210
0
                self.alloc_scratch_reg(inst, class, pos)?;
211
0
                let dec_clamp_zero = |x: &mut i16| {
212
0
                    *x = 0i16.max(*x - 1);
213
0
                };
214
0
                dec_clamp_zero(&mut self.num_available_pregs[ExclusiveOperandPos::Both][class]);
215
0
                dec_clamp_zero(
216
0
                    &mut self.num_available_pregs[ExclusiveOperandPos::EarlyOnly][class],
217
0
                );
218
0
                dec_clamp_zero(&mut self.num_available_pregs[ExclusiveOperandPos::LateOnly][class]);
219
0
                trace!(
220
                    "Recording edit: {:?}",
221
0
                    (ProgPoint::new(inst, pos), Edit::Move { from, to }, class)
222
                );
223
0
            }
224
0
            trace!("Edit is stack-to-stack. Generating two edits with a scratch register");
225
0
            let scratch_reg = self.scratch_regs[class].unwrap();
226
0
            let scratch_alloc = Allocation::reg(scratch_reg);
227
0
            trace!("Move 1: {scratch_alloc:?} to {to:?}");
228
0
            self.edits.push((
229
0
                ProgPoint::new(inst, pos),
230
0
                Edit::Move {
231
0
                    from: scratch_alloc,
232
0
                    to,
233
0
                },
234
0
            ));
235
0
            trace!("Move 2: {from:?} to {scratch_alloc:?}");
236
0
            self.edits.push((
237
0
                ProgPoint::new(inst, pos),
238
0
                Edit::Move {
239
0
                    from,
240
0
                    to: scratch_alloc,
241
0
                },
242
0
            ));
243
0
        } else {
244
0
            self.edits
245
0
                .push((ProgPoint::new(inst, pos), Edit::Move { from, to }));
246
0
        }
247
0
        Ok(())
248
0
    }
249
250
    /// Given that `pred` is a predecessor of `block`, check if `vreg` is defined on `pred`s branch instruction
251
    /// in a fixed register and if it is, insert an edit to move from that fixed register to `slot` at the beginning of `block`.
252
0
    fn move_if_def_pred_branch(
253
0
        &mut self,
254
0
        block: Block,
255
0
        pred: Block,
256
0
        vreg: VReg,
257
0
        slot: SpillSlot,
258
0
    ) -> Result<(), RegAllocError> {
259
0
        let pred_last_inst = self.func.block_insns(pred).last();
260
0
        let move_from = self.func.inst_operands(pred_last_inst)
261
0
            .iter()
262
0
            .find_map(|op| if op.kind() == OperandKind::Def && op.vreg() == vreg {
263
0
                if self.func.block_preds(block).len() > 1 {
264
0
                    panic!("Multiple predecessors when a branch arg/livein is defined on the branch");
265
0
                }
266
0
                match op.constraint() {
267
0
                    OperandConstraint::FixedReg(reg) => {
268
0
                        trace!("Vreg {vreg} defined on pred {pred:?} branch");
269
0
                        Some(Allocation::reg(reg))
270
                    },
271
                    // In these cases, the vreg is defined directly into the block param
272
                    // spillslot.
273
0
                    OperandConstraint::Stack | OperandConstraint::Any => None,
274
0
                    constraint => panic!("fastalloc does not support using any-reg or reuse constraints ({}) defined on a branch instruction as a branch arg/livein on the same instruction", constraint),
275
                }
276
            } else {
277
0
                None
278
0
            });
279
0
        if let Some(from) = move_from {
280
0
            let to = Allocation::stack(slot);
281
0
            trace!("Inserting edit to move from {from} to {to}");
282
0
            self.add_move(
283
0
                self.func.block_insns(block).first(),
284
0
                from,
285
0
                to,
286
0
                vreg.class(),
287
0
                InstPosition::Before,
288
0
            )?;
289
0
        }
290
0
        Ok(())
291
0
    }
292
}
293
294
#[derive(Debug, Clone)]
295
struct PartedByOperandPos<T> {
296
    items: [T; 2],
297
}
298
299
impl<T: Copy> Copy for PartedByOperandPos<T> {}
300
301
impl<T: BitAnd<Output = T> + Copy> BitAnd for PartedByOperandPos<T> {
302
    type Output = Self;
303
0
    fn bitand(self, other: Self) -> Self {
304
0
        Self {
305
0
            items: [
306
0
                self.items[0] & other.items[0],
307
0
                self.items[1] & other.items[1],
308
0
            ],
309
0
        }
310
0
    }
311
}
312
313
impl<T: BitOr<Output = T> + Copy> BitOr for PartedByOperandPos<T> {
314
    type Output = Self;
315
0
    fn bitor(self, other: Self) -> Self {
316
0
        Self {
317
0
            items: [
318
0
                self.items[0] | other.items[0],
319
0
                self.items[1] | other.items[1],
320
0
            ],
321
0
        }
322
0
    }
323
}
324
325
impl Not for PartedByOperandPos<PRegSet> {
326
    type Output = Self;
327
0
    fn not(self) -> Self {
328
0
        Self {
329
0
            items: [self.items[0].invert(), self.items[1].invert()],
330
0
        }
331
0
    }
332
}
333
334
impl<T> Index<OperandPos> for PartedByOperandPos<T> {
335
    type Output = T;
336
0
    fn index(&self, index: OperandPos) -> &Self::Output {
337
0
        &self.items[index as usize]
338
0
    }
339
}
340
341
impl<T> IndexMut<OperandPos> for PartedByOperandPos<T> {
342
0
    fn index_mut(&mut self, index: OperandPos) -> &mut Self::Output {
343
0
        &mut self.items[index as usize]
344
0
    }
345
}
346
347
impl<T: fmt::Display> fmt::Display for PartedByOperandPos<T> {
348
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
349
0
        write!(f, "{{ early: {}, late: {} }}", self.items[0], self.items[1])
350
0
    }
351
}
352
353
#[derive(Debug, Clone, Copy)]
354
enum ExclusiveOperandPos {
355
    EarlyOnly = 0,
356
    LateOnly = 1,
357
    Both = 2,
358
}
359
360
#[derive(Debug, Clone)]
361
struct PartedByExclusiveOperandPos<T> {
362
    items: [T; 3],
363
}
364
365
impl<T: PartialEq> PartialEq for PartedByExclusiveOperandPos<T> {
366
0
    fn eq(&self, other: &Self) -> bool {
367
0
        self.items.eq(&other.items)
368
0
    }
369
}
370
371
impl<T> Index<ExclusiveOperandPos> for PartedByExclusiveOperandPos<T> {
372
    type Output = T;
373
0
    fn index(&self, index: ExclusiveOperandPos) -> &Self::Output {
374
0
        &self.items[index as usize]
375
0
    }
376
}
377
378
impl<T> IndexMut<ExclusiveOperandPos> for PartedByExclusiveOperandPos<T> {
379
0
    fn index_mut(&mut self, index: ExclusiveOperandPos) -> &mut Self::Output {
380
0
        &mut self.items[index as usize]
381
0
    }
382
}
383
384
impl From<Operand> for ExclusiveOperandPos {
385
0
    fn from(op: Operand) -> Self {
386
0
        match (op.kind(), op.pos()) {
387
            (OperandKind::Use, OperandPos::Late) | (OperandKind::Def, OperandPos::Early) => {
388
0
                ExclusiveOperandPos::Both
389
            }
390
0
            _ if matches!(op.constraint(), OperandConstraint::Reuse(_)) => {
391
0
                ExclusiveOperandPos::Both
392
            }
393
0
            (_, OperandPos::Early) => ExclusiveOperandPos::EarlyOnly,
394
0
            (_, OperandPos::Late) => ExclusiveOperandPos::LateOnly,
395
        }
396
0
    }
397
}
398
399
impl<'a, F: Function> Deref for Env<'a, F> {
400
    type Target = State<'a, F>;
401
402
0
    fn deref(&self) -> &Self::Target {
403
0
        &self.state
404
0
    }
405
}
406
407
impl<'a, F: Function> DerefMut for Env<'a, F> {
408
0
    fn deref_mut(&mut self) -> &mut Self::Target {
409
0
        &mut self.state
410
0
    }
411
}
412
413
#[derive(Debug)]
414
pub struct Env<'a, F: Function> {
415
    func: &'a F,
416
417
    /// The virtual registers that are currently live.
418
    live_vregs: VRegSet,
419
    /// `reused_input_to_reuse_op[i]` is the operand index of the reuse operand
420
    /// that uses the `i`th operand in the current instruction as its input.
421
    reused_input_to_reuse_op: Vec<usize>,
422
    /// Number of operands with any-reg constraints in the current inst
423
    /// to allocate for
424
    num_any_reg_ops: PartedByExclusiveOperandPos<PartedByRegClass<i16>>,
425
    init_num_available_pregs: PartedByRegClass<i16>,
426
    init_available_pregs: PRegSet,
427
    allocatable_regs: PRegSet,
428
    preferred_victim: PartedByRegClass<PReg>,
429
    vreg_to_live_inst_range: Vec<(ProgPoint, ProgPoint, Allocation)>,
430
431
    fixed_stack_slots: PRegSet,
432
433
    // Output.
434
    allocs: Allocs,
435
    state: State<'a, F>,
436
    stats: Stats,
437
    debug_locations: Vec<(u32, ProgPoint, ProgPoint, Allocation)>,
438
}
439
440
impl<'a, F: Function> Env<'a, F> {
441
0
    fn new(func: &'a F, env: &'a MachineEnv) -> Self {
442
0
        let mut regs = [
443
0
            env.preferred_regs_by_class[RegClass::Int as usize].clone(),
444
0
            env.preferred_regs_by_class[RegClass::Float as usize].clone(),
445
0
            env.preferred_regs_by_class[RegClass::Vector as usize].clone(),
446
0
        ];
447
0
        regs[0].union_from(env.non_preferred_regs_by_class[RegClass::Int as usize]);
448
0
        regs[1].union_from(env.non_preferred_regs_by_class[RegClass::Float as usize]);
449
0
        regs[2].union_from(env.non_preferred_regs_by_class[RegClass::Vector as usize]);
450
0
        let allocatable_regs = PRegSet::from(env);
451
0
        let num_available_pregs: PartedByRegClass<i16> = PartedByRegClass {
452
0
            items: [
453
0
                (env.preferred_regs_by_class[RegClass::Int as usize].len()
454
0
                    + env.non_preferred_regs_by_class[RegClass::Int as usize].len())
455
0
                .try_into()
456
0
                .unwrap(),
457
0
                (env.preferred_regs_by_class[RegClass::Float as usize].len()
458
0
                    + env.non_preferred_regs_by_class[RegClass::Float as usize].len())
459
0
                .try_into()
460
0
                .unwrap(),
461
0
                (env.preferred_regs_by_class[RegClass::Vector as usize].len()
462
0
                    + env.non_preferred_regs_by_class[RegClass::Vector as usize].len())
463
0
                .try_into()
464
0
                .unwrap(),
465
0
            ],
466
0
        };
467
0
        let init_available_pregs = {
468
0
            let mut regs = allocatable_regs;
469
0
            for preg in env.fixed_stack_slots.iter() {
470
0
                regs.add(*preg);
471
0
            }
472
0
            regs
473
        };
474
0
        let dedicated_scratch_regs = PartedByRegClass {
475
0
            items: [
476
0
                env.scratch_by_class[0],
477
0
                env.scratch_by_class[1],
478
0
                env.scratch_by_class[2],
479
0
            ],
480
0
        };
481
0
        trace!("{:#?}", env);
482
0
        let (allocs, max_operand_len) = Allocs::new(func);
483
0
        let fixed_stack_slots = PRegSet::from_iter(env.fixed_stack_slots.iter().cloned());
484
0
        Self {
485
0
            func,
486
0
            allocatable_regs,
487
0
            live_vregs: VRegSet::with_capacity(func.num_vregs()),
488
0
            fixed_stack_slots,
489
0
            vreg_to_live_inst_range: vec![
490
0
                (
491
0
                    ProgPoint::invalid(),
492
0
                    ProgPoint::invalid(),
493
0
                    Allocation::none()
494
0
                );
495
0
                func.num_vregs()
496
0
            ],
497
0
            preferred_victim: PartedByRegClass {
498
0
                items: [
499
0
                    regs[0].max_preg().unwrap_or(PReg::invalid()),
500
0
                    regs[1].max_preg().unwrap_or(PReg::invalid()),
501
0
                    regs[2].max_preg().unwrap_or(PReg::invalid()),
502
0
                ],
503
0
            },
504
0
            reused_input_to_reuse_op: vec![usize::MAX; max_operand_len as usize],
505
0
            init_available_pregs,
506
0
            init_num_available_pregs: num_available_pregs.clone(),
507
0
            num_any_reg_ops: PartedByExclusiveOperandPos {
508
0
                items: [
509
0
                    PartedByRegClass { items: [0; 3] },
510
0
                    PartedByRegClass { items: [0; 3] },
511
0
                    PartedByRegClass { items: [0; 3] },
512
0
                ],
513
0
            },
514
0
            allocs,
515
0
            state: State {
516
0
                func,
517
0
                // This guess is based on the sightglass benchmarks:
518
0
                // The average number of edits per instruction is 1.
519
0
                edits: Vec::with_capacity(func.num_insts()),
520
0
                fixed_stack_slots,
521
0
                scratch_regs: dedicated_scratch_regs.clone(),
522
0
                dedicated_scratch_regs,
523
0
                num_available_pregs: PartedByExclusiveOperandPos {
524
0
                    items: [
525
0
                        num_available_pregs.clone(),
526
0
                        num_available_pregs.clone(),
527
0
                        num_available_pregs.clone(),
528
0
                    ],
529
0
                },
530
0
                available_pregs: PartedByOperandPos {
531
0
                    items: [init_available_pregs, init_available_pregs],
532
0
                },
533
0
                lrus: Lrus::new(&regs[0], &regs[1], &regs[2]),
534
0
                vreg_in_preg: vec![VReg::invalid(); PReg::NUM_INDEX],
535
0
                stack: Stack::new(func),
536
0
                vreg_allocs: vec![Allocation::none(); func.num_vregs()],
537
0
                vreg_spillslots: vec![SpillSlot::invalid(); func.num_vregs()],
538
0
            },
539
0
            stats: Stats::default(),
540
0
            debug_locations: Vec::with_capacity(func.debug_value_labels().len()),
541
0
        }
542
0
    }
543
544
0
    fn reset_available_pregs_and_scratch_regs(&mut self) {
545
0
        trace!("Resetting the available pregs");
546
0
        self.available_pregs = PartedByOperandPos {
547
0
            items: [self.init_available_pregs, self.init_available_pregs],
548
0
        };
549
0
        self.scratch_regs = self.dedicated_scratch_regs.clone();
550
0
        self.num_available_pregs = PartedByExclusiveOperandPos {
551
0
            items: [self.init_num_available_pregs; 3],
552
0
        };
553
0
        debug_assert_eq!(
554
            self.num_any_reg_ops,
555
            PartedByExclusiveOperandPos {
556
                items: [PartedByRegClass { items: [0; 3] }; 3]
557
            }
558
        );
559
0
    }
560
561
0
    fn reserve_reg_for_operand(
562
0
        &mut self,
563
0
        op: Operand,
564
0
        op_idx: usize,
565
0
        preg: PReg,
566
0
    ) -> Result<(), RegAllocError> {
567
0
        trace!("Reserving register {preg} for operand {op}");
568
0
        let early_avail_pregs = self.available_pregs[OperandPos::Early];
569
0
        let late_avail_pregs = self.available_pregs[OperandPos::Late];
570
0
        match (op.pos(), op.kind()) {
571
            (OperandPos::Early, OperandKind::Use) => {
572
0
                if op.as_fixed_nonallocatable().is_none() && !early_avail_pregs.contains(preg) {
573
0
                    trace!("fixed {preg} for {op} isn't available");
574
0
                    return Err(RegAllocError::TooManyLiveRegs);
575
0
                }
576
0
                self.available_pregs[OperandPos::Early].remove(preg);
577
0
                if self.reused_input_to_reuse_op[op_idx] != usize::MAX {
578
0
                    if op.as_fixed_nonallocatable().is_none() && !late_avail_pregs.contains(preg) {
579
0
                        trace!("fixed {preg} for {op} isn't available");
580
0
                        return Err(RegAllocError::TooManyLiveRegs);
581
0
                    }
582
0
                    self.available_pregs[OperandPos::Late].remove(preg);
583
0
                }
584
            }
585
            (OperandPos::Late, OperandKind::Def) => {
586
0
                if op.as_fixed_nonallocatable().is_none() && !late_avail_pregs.contains(preg) {
587
0
                    trace!("fixed {preg} for {op} isn't available");
588
0
                    return Err(RegAllocError::TooManyLiveRegs);
589
0
                }
590
0
                self.available_pregs[OperandPos::Late].remove(preg);
591
            }
592
            _ => {
593
0
                if op.as_fixed_nonallocatable().is_none()
594
0
                    && (!early_avail_pregs.contains(preg) || !late_avail_pregs.contains(preg))
595
                {
596
0
                    trace!("fixed {preg} for {op} isn't available");
597
0
                    return Err(RegAllocError::TooManyLiveRegs);
598
0
                }
599
0
                self.available_pregs[OperandPos::Early].remove(preg);
600
0
                self.available_pregs[OperandPos::Late].remove(preg);
601
            }
602
        }
603
0
        Ok(())
604
0
    }
605
606
0
    fn allocd_within_constraint(&self, op: Operand, inst: Inst) -> bool {
607
0
        let alloc = self.vreg_allocs[op.vreg().vreg()];
608
0
        match op.constraint() {
609
            OperandConstraint::Any => {
610
0
                if let Some(preg) = alloc.as_reg() {
611
0
                    let exclusive_pos: ExclusiveOperandPos = op.into();
612
0
                    if !self.is_stack(alloc)
613
0
                        && self.num_available_pregs[exclusive_pos][op.class()]
614
0
                            < self.num_any_reg_ops[exclusive_pos][op.class()]
615
                    {
616
0
                        trace!("Need more registers to cover all any-reg ops. Going to evict {op} from {preg}");
617
0
                        return false;
618
0
                    }
619
0
                    if !self.available_pregs[op.pos()].contains(preg) {
620
                        // If a register isn't in the available pregs list, then
621
                        // there are two cases: either it's reserved for a
622
                        // fixed register constraint or a vreg allocated in the instruction
623
                        // is already assigned to it.
624
                        //
625
                        // For example:
626
                        // 1. use v0, use v0, use v0
627
                        //
628
                        // Say p0 is assigned to v0 during the processing of the first operand.
629
                        // When the second v0 operand is being processed, v0 will still be in
630
                        // v0, so it is still allocated within constraints.
631
0
                        trace!("The vreg in {preg}: {}", self.vreg_in_preg[preg.index()]);
632
0
                        self.vreg_in_preg[preg.index()] == op.vreg() &&
633
                            // If it's a late operand, it shouldn't be allocated to a
634
                            // clobber. For example:
635
                            // use v0 (fixed: p0), late use v0
636
                            // If p0 is a clobber, then v0 shouldn't be allocated to it.
637
0
                            (op.pos() != OperandPos::Late || !self.func.inst_clobbers(inst).contains(preg))
638
                    } else {
639
0
                        true
640
                    }
641
                } else {
642
0
                    !alloc.is_none()
643
                }
644
            }
645
            OperandConstraint::Reg => {
646
0
                if self.is_stack(alloc) {
647
0
                    return false;
648
0
                }
649
0
                if let Some(preg) = alloc.as_reg() {
650
0
                    if !self.available_pregs[op.pos()].contains(preg) {
651
0
                        trace!("The vreg in {preg}: {}", self.vreg_in_preg[preg.index()]);
652
0
                        self.vreg_in_preg[preg.index()] == op.vreg()
653
0
                            && (op.pos() != OperandPos::Late
654
0
                                || !self.func.inst_clobbers(inst).contains(preg))
655
                    } else {
656
0
                        true
657
                    }
658
                } else {
659
0
                    false
660
                }
661
            }
662
            // It is possible for an operand to have a fixed register constraint to
663
            // a clobber.
664
0
            OperandConstraint::FixedReg(preg) => alloc.is_reg() && alloc.as_reg().unwrap() == preg,
665
            OperandConstraint::Reuse(_) => {
666
0
                unreachable!()
667
            }
668
669
0
            OperandConstraint::Stack => self.is_stack(alloc),
670
            OperandConstraint::Limit(_) => {
671
0
                todo!("limit constraints are not yet supported in fastalloc")
672
            }
673
        }
674
0
    }
675
676
0
    fn freealloc(&mut self, vreg: VReg) {
677
0
        trace!("Freeing vreg {}", vreg);
678
0
        let alloc = self.vreg_allocs[vreg.vreg()];
679
0
        match alloc.kind() {
680
0
            AllocationKind::Reg => {
681
0
                let preg = alloc.as_reg().unwrap();
682
0
                self.vreg_in_preg[preg.index()] = VReg::invalid();
683
0
            }
684
0
            AllocationKind::Stack => (),
685
0
            AllocationKind::None => unreachable!("Attempting to free an unallocated operand!"),
686
        }
687
0
        self.vreg_allocs[vreg.vreg()] = Allocation::none();
688
0
        self.live_vregs.remove(vreg.vreg());
689
0
        trace!(
690
            "{} curr alloc is now {}",
691
            vreg,
692
0
            self.vreg_allocs[vreg.vreg()]
693
        );
694
0
    }
695
696
0
    fn select_suitable_reg_in_lru(&self, op: Operand) -> Result<PReg, RegAllocError> {
697
0
        let draw_from = match (op.pos(), op.kind()) {
698
            // No need to consider reuse constraints because they are
699
            // handled elsewhere
700
            (OperandPos::Late, OperandKind::Use) | (OperandPos::Early, OperandKind::Def) => {
701
0
                self.available_pregs[OperandPos::Late] & self.available_pregs[OperandPos::Early]
702
            }
703
0
            _ => self.available_pregs[op.pos()],
704
        };
705
0
        if draw_from.is_empty(op.class()) {
706
0
            trace!("No registers available for {op} in selection");
707
0
            return Err(RegAllocError::TooManyLiveRegs);
708
0
        }
709
0
        let Some(preg) = self.lrus[op.class()].last(draw_from) else {
710
0
            trace!(
711
                "Failed to find an available {:?} register in the LRU for operand {op}",
712
0
                op.class()
713
            );
714
0
            return Err(RegAllocError::TooManyLiveRegs);
715
        };
716
0
        Ok(preg)
717
0
    }
718
719
    /// Allocates a physical register for the operand `op`.
720
0
    fn alloc_reg_for_operand(
721
0
        &mut self,
722
0
        inst: Inst,
723
0
        op: Operand,
724
0
    ) -> Result<Allocation, RegAllocError> {
725
0
        trace!("available regs: {}", self.available_pregs);
726
0
        trace!("Int LRU: {:?}", self.lrus[RegClass::Int]);
727
0
        trace!("Float LRU: {:?}", self.lrus[RegClass::Float]);
728
0
        trace!("Vector LRU: {:?}", self.lrus[RegClass::Vector]);
729
0
        trace!("");
730
0
        let preg = self.select_suitable_reg_in_lru(op)?;
731
0
        if self.vreg_in_preg[preg.index()] != VReg::invalid() {
732
0
            self.evict_vreg_in_preg(inst, preg, InstPosition::After)?;
733
0
        }
734
0
        trace!("The allocated register for vreg {}: {}", op.vreg(), preg);
735
0
        self.lrus[op.class()].poke(preg);
736
0
        self.available_pregs[op.pos()].remove(preg);
737
0
        match (op.pos(), op.kind()) {
738
0
            (OperandPos::Late, OperandKind::Use) => {
739
0
                self.available_pregs[OperandPos::Early].remove(preg);
740
0
            }
741
0
            (OperandPos::Early, OperandKind::Def) => {
742
0
                self.available_pregs[OperandPos::Late].remove(preg);
743
0
            }
744
            (OperandPos::Late, OperandKind::Def)
745
0
                if matches!(op.constraint(), OperandConstraint::Reuse(_)) =>
746
0
            {
747
0
                self.available_pregs[OperandPos::Early].remove(preg);
748
0
            }
749
0
            _ => (),
750
        };
751
0
        Ok(Allocation::reg(preg))
752
0
    }
753
754
    /// Allocates for the operand `op` with index `op_idx` into the
755
    /// vector of instruction `inst`'s operands.
756
0
    fn alloc_operand(
757
0
        &mut self,
758
0
        inst: Inst,
759
0
        op: Operand,
760
0
        op_idx: usize,
761
0
    ) -> Result<Allocation, RegAllocError> {
762
0
        let new_alloc = match op.constraint() {
763
            OperandConstraint::Any => {
764
0
                if (op.kind() == OperandKind::Def
765
0
                    && self.vreg_allocs[op.vreg().vreg()] == Allocation::none())
766
                    // Not safe to alloc register because any-reg operands still
767
                    // need them
768
0
                    || self.num_any_reg_ops[op.into()][op.class()] >= self.num_available_pregs[op.into()][op.class()]
769
                {
770
                    // If the def is never used, it can just be put in its spillslot.
771
0
                    Allocation::stack(self.get_spillslot(op.vreg()))
772
                } else {
773
0
                    match self.alloc_reg_for_operand(inst, op) {
774
0
                        Ok(alloc) => alloc,
775
                        Err(RegAllocError::TooManyLiveRegs) => {
776
0
                            Allocation::stack(self.get_spillslot(op.vreg()))
777
                        }
778
0
                        Err(err) => return Err(err),
779
                    }
780
                }
781
            }
782
            OperandConstraint::Reg => {
783
0
                let alloc = self.alloc_reg_for_operand(inst, op)?;
784
0
                self.num_any_reg_ops[op.into()][op.class()] -= 1;
785
0
                trace!(
786
                    "Number of {:?} any-reg ops to allocate now: {}",
787
0
                    Into::<ExclusiveOperandPos>::into(op),
788
0
                    self.num_any_reg_ops[op.into()]
789
                );
790
0
                alloc
791
            }
792
0
            OperandConstraint::FixedReg(preg) => {
793
0
                trace!("The fixed preg: {} for operand {}", preg, op);
794
795
0
                Allocation::reg(preg)
796
            }
797
            OperandConstraint::Reuse(_) => {
798
                // This is handled elsewhere.
799
0
                unreachable!();
800
            }
801
802
0
            OperandConstraint::Stack => Allocation::stack(self.get_spillslot(op.vreg())),
803
            OperandConstraint::Limit(_) => {
804
0
                todo!("limit constraints are not yet supported in fastalloc")
805
            }
806
        };
807
0
        self.allocs[(inst.index(), op_idx)] = new_alloc;
808
0
        Ok(new_alloc)
809
0
    }
810
811
    /// Allocate operand the `op_idx`th operand `op` in instruction `inst` within its constraint.
812
    /// Since only fixed register constraints are allowed, `fixed_spillslot` is used when a
813
    /// fixed stack allocation is needed, like when transferring a stack allocation from a
814
    /// reuse operand allocation to the reused input.
815
0
    fn process_operand_allocation(
816
0
        &mut self,
817
0
        inst: Inst,
818
0
        op: Operand,
819
0
        op_idx: usize,
820
0
    ) -> Result<(), RegAllocError> {
821
0
        if let Some(preg) = op.as_fixed_nonallocatable() {
822
0
            self.allocs[(inst.index(), op_idx)] = Allocation::reg(preg);
823
0
            trace!(
824
                "Allocation for instruction {:?} and operand {}: {}",
825
                inst,
826
                op,
827
0
                self.allocs[(inst.index(), op_idx)]
828
            );
829
0
            return Ok(());
830
0
        }
831
0
        if !self.allocd_within_constraint(op, inst) {
832
0
            trace!(
833
                "{op} isn't allocated within constraints (the alloc: {}).",
834
0
                self.vreg_allocs[op.vreg().vreg()]
835
            );
836
0
            let curr_alloc = self.vreg_allocs[op.vreg().vreg()];
837
0
            let new_alloc = self.alloc_operand(inst, op, op_idx)?;
838
0
            if curr_alloc.is_none() {
839
0
                self.live_vregs.insert(op.vreg());
840
0
                self.vreg_to_live_inst_range[op.vreg().vreg()].1 = match (op.pos(), op.kind()) {
841
                    (OperandPos::Late, OperandKind::Use) | (_, OperandKind::Def) => {
842
                        // Live range ends just before the early phase of the
843
                        // next instruction.
844
0
                        ProgPoint::before(Inst::new(inst.index() + 1))
845
                    }
846
                    (OperandPos::Early, OperandKind::Use) => {
847
                        // Live range ends just before the late phase of the current instruction.
848
0
                        ProgPoint::after(inst)
849
                    }
850
                };
851
0
                self.vreg_to_live_inst_range[op.vreg().vreg()].2 = new_alloc;
852
853
0
                trace!("Setting vreg_allocs[{op}] to {new_alloc:?}");
854
0
                self.vreg_allocs[op.vreg().vreg()] = new_alloc;
855
0
                if let Some(preg) = new_alloc.as_reg() {
856
0
                    self.vreg_in_preg[preg.index()] = op.vreg();
857
0
                }
858
            }
859
            // Need to insert a move to propagate flow from the current
860
            // allocation to the subsequent places where the value was
861
            // used (in `prev_alloc`, that is).
862
            else {
863
0
                trace!("Move reason: Prev allocation doesn't meet constraints");
864
0
                if op.kind() == OperandKind::Def {
865
0
                    trace!("Adding edit from {new_alloc:?} to {curr_alloc:?} after inst {inst:?} for {op}");
866
0
                    self.add_move(inst, new_alloc, curr_alloc, op.class(), InstPosition::After)?;
867
0
                }
868
                // Edits for use operands are added later to avoid inserting
869
                // edits out of order.
870
871
0
                if let Some(preg) = new_alloc.as_reg() {
872
0
                    // Don't change the allocation.
873
0
                    self.vreg_in_preg[preg.index()] = VReg::invalid();
874
0
                }
875
            }
876
0
            trace!(
877
                "Allocation for instruction {:?} and operand {}: {}",
878
                inst,
879
                op,
880
0
                self.allocs[(inst.index(), op_idx)]
881
            );
882
        } else {
883
0
            trace!("{op} is already allocated within constraints");
884
0
            self.allocs[(inst.index(), op_idx)] = self.vreg_allocs[op.vreg().vreg()];
885
0
            if op.constraint() == OperandConstraint::Reg {
886
0
                self.num_any_reg_ops[op.into()][op.class()] -= 1;
887
0
                trace!("{op} is already within constraint. Number of reg-only ops that need to be allocated now: {}", self.num_any_reg_ops[op.into()]);
888
0
            }
889
0
            if let Some(preg) = self.allocs[(inst.index(), op_idx)].as_reg() {
890
0
                if self.allocatable_regs.contains(preg) {
891
0
                    self.lrus[preg.class()].poke(preg);
892
0
                }
893
0
                self.available_pregs[op.pos()].remove(preg);
894
0
                self.available_pregs[op.pos()].remove(preg);
895
0
                match (op.pos(), op.kind()) {
896
0
                    (OperandPos::Late, OperandKind::Use) => {
897
0
                        self.available_pregs[OperandPos::Early].remove(preg);
898
0
                        self.available_pregs[OperandPos::Early].remove(preg);
899
0
                    }
900
0
                    (OperandPos::Early, OperandKind::Def) => {
901
0
                        self.available_pregs[OperandPos::Late].remove(preg);
902
0
                        self.available_pregs[OperandPos::Late].remove(preg);
903
0
                    }
904
0
                    _ => (),
905
                };
906
0
            }
907
0
            trace!(
908
                "Allocation for instruction {:?} and operand {}: {}",
909
                inst,
910
                op,
911
0
                self.allocs[(inst.index(), op_idx)]
912
            );
913
        }
914
0
        trace!(
915
            "Late available regs: {}",
916
0
            self.available_pregs[OperandPos::Late]
917
        );
918
0
        trace!(
919
            "Early available regs: {}",
920
0
            self.available_pregs[OperandPos::Early]
921
        );
922
0
        Ok(())
923
0
    }
924
925
0
    fn remove_clobbers_from_available_pregs(&mut self, clobbers: PRegSet) {
926
0
        trace!("Removing clobbers {clobbers} from late available reg sets");
927
0
        let all_but_clobbers = clobbers.invert();
928
0
        self.available_pregs[OperandPos::Late].intersect_from(all_but_clobbers);
929
0
    }
930
931
    /// If instruction `inst` is a branch in `block`,
932
    /// this function places branch arguments in the spillslots
933
    /// expected by the destination blocks.
934
0
    fn process_branch(&mut self, block: Block, inst: Inst) -> Result<(), RegAllocError> {
935
0
        trace!("Processing branch instruction {inst:?} in block {block:?}");
936
937
0
        let mut int_parallel_moves = ParallelMoves::new();
938
0
        let mut float_parallel_moves = ParallelMoves::new();
939
0
        let mut vec_parallel_moves = ParallelMoves::new();
940
941
0
        for (succ_idx, succ) in self.func.block_succs(block).iter().enumerate() {
942
0
            for (pos, vreg) in self
943
0
                .func
944
0
                .branch_blockparams(block, inst, succ_idx)
945
0
                .iter()
946
0
                .enumerate()
947
            {
948
0
                if self
949
0
                    .func
950
0
                    .inst_operands(inst)
951
0
                    .iter()
952
0
                    .find(|op| op.vreg() == *vreg && op.kind() == OperandKind::Def)
953
0
                    .is_some()
954
                {
955
                    // vreg is defined in this instruction, so it's dead already.
956
                    // Can't move it.
957
0
                    continue;
958
0
                }
959
0
                let succ_params = self.func.block_params(*succ);
960
0
                let succ_param_vreg = succ_params[pos];
961
0
                if self.vreg_spillslots[succ_param_vreg.vreg()].is_invalid() {
962
0
                    self.vreg_spillslots[succ_param_vreg.vreg()] =
963
0
                        self.stack.allocstack(succ_param_vreg.class());
964
0
                }
965
0
                if self.vreg_spillslots[vreg.vreg()].is_invalid() {
966
0
                    self.vreg_spillslots[vreg.vreg()] = self.stack.allocstack(vreg.class());
967
0
                }
968
0
                let vreg_spill = Allocation::stack(self.vreg_spillslots[vreg.vreg()]);
969
0
                let curr_alloc = self.vreg_allocs[vreg.vreg()];
970
0
                if curr_alloc.is_none() {
971
0
                    self.live_vregs.insert(*vreg);
972
0
                    self.vreg_to_live_inst_range[vreg.vreg()].1 = ProgPoint::before(inst);
973
0
                } else if curr_alloc != vreg_spill {
974
0
                    self.add_move(
975
0
                        inst,
976
0
                        vreg_spill,
977
0
                        curr_alloc,
978
0
                        vreg.class(),
979
0
                        InstPosition::Before,
980
0
                    )?;
981
0
                }
982
0
                self.vreg_allocs[vreg.vreg()] = vreg_spill;
983
0
                let parallel_moves = match vreg.class() {
984
0
                    RegClass::Int => &mut int_parallel_moves,
985
0
                    RegClass::Float => &mut float_parallel_moves,
986
0
                    RegClass::Vector => &mut vec_parallel_moves,
987
                };
988
0
                let from = Allocation::stack(self.vreg_spillslots[vreg.vreg()]);
989
0
                let to = Allocation::stack(self.vreg_spillslots[succ_param_vreg.vreg()]);
990
0
                trace!("Recording parallel move from {from} to {to}");
991
0
                parallel_moves.add(from, to, Some(*vreg));
992
            }
993
        }
994
995
0
        let resolved_int = int_parallel_moves.resolve();
996
0
        let resolved_float = float_parallel_moves.resolve();
997
0
        let resolved_vec = vec_parallel_moves.resolve();
998
0
        let mut scratch_regs = self.scratch_regs.clone();
999
0
        let mut num_spillslots = self.stack.num_spillslots;
1000
0
        let mut avail_regs =
1001
0
            self.available_pregs[OperandPos::Early] & self.available_pregs[OperandPos::Late];
1002
1003
0
        trace!("Resolving parallel moves");
1004
0
        for (resolved, class) in [
1005
0
            (resolved_int, RegClass::Int),
1006
0
            (resolved_float, RegClass::Float),
1007
0
            (resolved_vec, RegClass::Vector),
1008
0
        ] {
1009
0
            let scratch_resolver = MoveAndScratchResolver {
1010
0
                find_free_reg: || {
1011
0
                    if let Some(reg) = scratch_regs[class] {
1012
0
                        trace!("Retrieved reg {reg} for scratch resolver");
1013
0
                        scratch_regs[class] = None;
1014
0
                        Some(Allocation::reg(reg))
1015
                    } else {
1016
0
                        let Some(preg) = self.lrus[class].last(avail_regs) else {
1017
0
                            trace!("Couldn't find any reg for scratch resolver");
1018
0
                            return None;
1019
                        };
1020
0
                        avail_regs.remove(preg);
1021
0
                        trace!("Retrieved reg {preg} for scratch resolver");
1022
0
                        Some(Allocation::reg(preg))
1023
                    }
1024
0
                },
1025
0
                get_stackslot: || {
1026
0
                    let size: u32 = self.func.spillslot_size(class).try_into().unwrap();
1027
0
                    let mut offset = num_spillslots;
1028
0
                    debug_assert!(size.is_power_of_two());
1029
0
                    offset = (offset + size - 1) & !(size - 1);
1030
0
                    let slot = if self.func.multi_spillslot_named_by_last_slot() {
1031
0
                        offset + size - 1
1032
                    } else {
1033
0
                        offset
1034
                    };
1035
0
                    offset += size;
1036
0
                    num_spillslots = offset;
1037
0
                    trace!("Retrieved slot {slot} for scratch resolver");
1038
0
                    Allocation::stack(SpillSlot::new(slot as usize))
1039
0
                },
1040
0
                is_stack_alloc: |alloc| self.is_stack(alloc),
1041
0
                borrowed_scratch_reg: self.preferred_victim[class],
1042
            };
1043
0
            let moves = scratch_resolver.compute(resolved);
1044
0
            trace!("Resolved {class:?} parallel moves");
1045
0
            for (from, to, _) in moves.into_iter().rev() {
1046
0
                self.edits
1047
0
                    .push((ProgPoint::before(inst), Edit::Move { from, to }))
1048
            }
1049
0
            self.stack.num_spillslots = num_spillslots;
1050
        }
1051
0
        trace!("Completed processing branch");
1052
0
        Ok(())
1053
0
    }
1054
1055
0
    fn alloc_def_op(
1056
0
        &mut self,
1057
0
        op_idx: usize,
1058
0
        op: Operand,
1059
0
        operands: &[Operand],
1060
0
        block: Block,
1061
0
        inst: Inst,
1062
0
    ) -> Result<(), RegAllocError> {
1063
0
        trace!("Allocating def operand {op}");
1064
0
        if let OperandConstraint::Reuse(reused_idx) = op.constraint() {
1065
0
            let reused_op = operands[reused_idx];
1066
            // Alloc as an operand alive in both early and late phases
1067
0
            let new_reuse_op = Operand::new(
1068
0
                op.vreg(),
1069
0
                reused_op.constraint(),
1070
0
                OperandKind::Def,
1071
0
                OperandPos::Early,
1072
            );
1073
0
            trace!("allocating reuse op {op} as {new_reuse_op}");
1074
0
            self.process_operand_allocation(inst, new_reuse_op, op_idx)?;
1075
0
        } else if self.func.is_branch(inst) {
1076
            // If the defined vreg is used as a branch arg and it has an
1077
            // any or stack constraint, define it into the block param spillslot
1078
0
            let mut param_spillslot = None;
1079
0
            'outer: for (succ_idx, succ) in self.func.block_succs(block).iter().cloned().enumerate()
1080
            {
1081
0
                for (param_idx, branch_arg_vreg) in self
1082
0
                    .func
1083
0
                    .branch_blockparams(block, inst, succ_idx)
1084
0
                    .iter()
1085
0
                    .cloned()
1086
0
                    .enumerate()
1087
                {
1088
0
                    if op.vreg() == branch_arg_vreg {
1089
0
                        if matches!(
1090
0
                            op.constraint(),
1091
                            OperandConstraint::Any | OperandConstraint::Stack
1092
0
                        ) {
1093
0
                            let block_param = self.func.block_params(succ)[param_idx];
1094
0
                            param_spillslot = Some(self.get_spillslot(block_param));
1095
0
                        }
1096
0
                        break 'outer;
1097
0
                    }
1098
                }
1099
            }
1100
0
            if let Some(param_spillslot) = param_spillslot {
1101
0
                let spillslot = self.vreg_spillslots[op.vreg().vreg()];
1102
0
                self.vreg_spillslots[op.vreg().vreg()] = param_spillslot;
1103
0
                let op = Operand::new(op.vreg(), OperandConstraint::Stack, op.kind(), op.pos());
1104
0
                self.process_operand_allocation(inst, op, op_idx)?;
1105
0
                self.vreg_spillslots[op.vreg().vreg()] = spillslot;
1106
            } else {
1107
0
                self.process_operand_allocation(inst, op, op_idx)?;
1108
            }
1109
        } else {
1110
0
            self.process_operand_allocation(inst, op, op_idx)?;
1111
        }
1112
0
        let slot = self.vreg_spillslots[op.vreg().vreg()];
1113
0
        if slot.is_valid() {
1114
0
            self.vreg_to_live_inst_range[op.vreg().vreg()].2 = Allocation::stack(slot);
1115
0
            let curr_alloc = self.vreg_allocs[op.vreg().vreg()];
1116
0
            let new_alloc = Allocation::stack(self.vreg_spillslots[op.vreg().vreg()]);
1117
0
            if curr_alloc != new_alloc {
1118
0
                self.add_move(inst, curr_alloc, new_alloc, op.class(), InstPosition::After)?;
1119
0
            }
1120
0
        }
1121
0
        self.vreg_to_live_inst_range[op.vreg().vreg()].0 = ProgPoint::after(inst);
1122
0
        self.freealloc(op.vreg());
1123
0
        Ok(())
1124
0
    }
1125
1126
0
    fn alloc_use(&mut self, op_idx: usize, op: Operand, inst: Inst) -> Result<(), RegAllocError> {
1127
0
        trace!("Allocating use op {op}");
1128
0
        if self.reused_input_to_reuse_op[op_idx] != usize::MAX {
1129
0
            let reuse_op_idx = self.reused_input_to_reuse_op[op_idx];
1130
0
            let reuse_op_alloc = self.allocs[(inst.index(), reuse_op_idx)];
1131
0
            let Some(preg) = reuse_op_alloc.as_reg() else {
1132
0
                unreachable!();
1133
            };
1134
0
            let new_reused_input_constraint = OperandConstraint::FixedReg(preg);
1135
0
            let new_reused_input =
1136
0
                Operand::new(op.vreg(), new_reused_input_constraint, op.kind(), op.pos());
1137
0
            trace!("Allocating reused input {op} as {new_reused_input}");
1138
0
            self.process_operand_allocation(inst, new_reused_input, op_idx)?;
1139
        } else {
1140
0
            self.process_operand_allocation(inst, op, op_idx)?;
1141
        }
1142
0
        Ok(())
1143
0
    }
1144
1145
0
    fn alloc_inst(&mut self, block: Block, inst: Inst) -> Result<(), RegAllocError> {
1146
0
        trace!("Allocating instruction {:?}", inst);
1147
0
        self.reset_available_pregs_and_scratch_regs();
1148
0
        let operands = Operands::new(self.func.inst_operands(inst));
1149
0
        let clobbers = self.func.inst_clobbers(inst);
1150
        // Number of registers that can be used for reg-only operands
1151
        // allocated to fixed-reg operands
1152
0
        let mut num_fixed_regs_allocatable_clobbers = 0u16;
1153
0
        trace!("init num avail pregs: {:?}", self.num_available_pregs);
1154
0
        for (op_idx, op) in operands.0.iter().cloned().enumerate() {
1155
0
            if let OperandConstraint::Reuse(reused_idx) = op.constraint() {
1156
0
                trace!("Initializing reused_input_to_reuse_op for {op}");
1157
0
                self.reused_input_to_reuse_op[reused_idx] = op_idx;
1158
0
                if operands.0[reused_idx].constraint() == OperandConstraint::Reg {
1159
0
                    trace!(
1160
                        "Counting {op} as an any-reg op that needs a reg in phase {:?}",
1161
                        ExclusiveOperandPos::Both
1162
                    );
1163
0
                    self.num_any_reg_ops[ExclusiveOperandPos::Both][op.class()] += 1;
1164
                    // When the reg-only operand is encountered, this will be incremented
1165
                    // Subtract by 1 to remove over-count.
1166
0
                    trace!(
1167
                        "Decreasing num any-reg ops in phase {:?}",
1168
                        ExclusiveOperandPos::EarlyOnly
1169
                    );
1170
0
                    self.num_any_reg_ops[ExclusiveOperandPos::EarlyOnly][op.class()] -= 1;
1171
0
                }
1172
0
            } else if op.constraint() == OperandConstraint::Reg {
1173
0
                trace!(
1174
                    "Counting {op} as an any-reg op that needs a reg in phase {:?}",
1175
0
                    Into::<ExclusiveOperandPos>::into(op)
1176
                );
1177
0
                self.num_any_reg_ops[op.into()][op.class()] += 1;
1178
0
            };
1179
        }
1180
0
        let mut seen = PRegSet::empty();
1181
0
        for (op_idx, op) in operands.fixed() {
1182
0
            let OperandConstraint::FixedReg(preg) = op.constraint() else {
1183
0
                unreachable!();
1184
            };
1185
0
            self.reserve_reg_for_operand(op, op_idx, preg)?;
1186
1187
0
            if !seen.contains(preg) {
1188
0
                seen.add(preg);
1189
0
                if self.allocatable_regs.contains(preg) {
1190
0
                    self.lrus[preg.class()].poke(preg);
1191
0
                    self.num_available_pregs[op.into()][op.class()] -= 1;
1192
0
                    debug_assert!(self.num_available_pregs[op.into()][op.class()] >= 0);
1193
0
                    if clobbers.contains(preg) {
1194
0
                        num_fixed_regs_allocatable_clobbers += 1;
1195
0
                    }
1196
0
                }
1197
0
            }
1198
        }
1199
0
        trace!("avail pregs after fixed: {:?}", self.num_available_pregs);
1200
1201
0
        self.remove_clobbers_from_available_pregs(clobbers);
1202
1203
0
        for (_, op) in operands.fixed() {
1204
0
            let OperandConstraint::FixedReg(preg) = op.constraint() else {
1205
0
                unreachable!();
1206
            };
1207
            // Eviction has to be done separately to avoid using a fixed register
1208
            // as a scratch register.
1209
0
            if self.vreg_in_preg[preg.index()] != VReg::invalid()
1210
0
                && self.vreg_in_preg[preg.index()] != op.vreg()
1211
            {
1212
0
                trace!(
1213
                    "Evicting {} from fixed register {preg}",
1214
0
                    self.vreg_in_preg[preg.index()]
1215
                );
1216
0
                self.evict_vreg_in_preg(inst, preg, InstPosition::After)?;
1217
0
                self.vreg_in_preg[preg.index()] = VReg::invalid();
1218
0
            }
1219
        }
1220
0
        for preg in clobbers {
1221
0
            if self.vreg_in_preg[preg.index()] != VReg::invalid() {
1222
0
                trace!(
1223
                    "Evicting {} from clobber {preg}",
1224
0
                    self.vreg_in_preg[preg.index()]
1225
                );
1226
0
                self.evict_vreg_in_preg(inst, preg, InstPosition::After)?;
1227
0
                self.vreg_in_preg[preg.index()] = VReg::invalid();
1228
0
            }
1229
0
            if self.allocatable_regs.contains(preg) {
1230
0
                if num_fixed_regs_allocatable_clobbers == 0 {
1231
0
                    trace!("Decrementing clobber avail preg");
1232
0
                    self.num_available_pregs[ExclusiveOperandPos::LateOnly][preg.class()] -= 1;
1233
0
                    self.num_available_pregs[ExclusiveOperandPos::Both][preg.class()] -= 1;
1234
0
                    debug_assert!(
1235
0
                        self.num_available_pregs[ExclusiveOperandPos::LateOnly][preg.class()] >= 0
1236
                    );
1237
0
                    debug_assert!(
1238
0
                        self.num_available_pregs[ExclusiveOperandPos::Both][preg.class()] >= 0
1239
                    );
1240
0
                } else {
1241
0
                    // Some fixed-reg operands may be clobbers and so the decrement
1242
0
                    // of the num avail regs has already been done.
1243
0
                    num_fixed_regs_allocatable_clobbers -= 1;
1244
0
                }
1245
0
            }
1246
        }
1247
1248
0
        trace!(
1249
            "Number of int, float, vector any-reg ops in early-only, respectively: {}",
1250
0
            self.num_any_reg_ops[ExclusiveOperandPos::EarlyOnly]
1251
        );
1252
0
        trace!(
1253
            "Number of any-reg ops in late-only: {}",
1254
0
            self.num_any_reg_ops[ExclusiveOperandPos::LateOnly]
1255
        );
1256
0
        trace!(
1257
            "Number of any-reg ops in both early and late: {}",
1258
0
            self.num_any_reg_ops[ExclusiveOperandPos::Both]
1259
        );
1260
0
        trace!(
1261
            "Number of available pregs for int, float, vector any-reg and any ops: {:?}",
1262
0
            self.num_available_pregs
1263
        );
1264
0
        trace!(
1265
            "registers available for early reg-only & any operands: {}",
1266
0
            self.available_pregs[OperandPos::Early]
1267
        );
1268
0
        trace!(
1269
            "registers available for late reg-only & any operands: {}",
1270
0
            self.available_pregs[OperandPos::Late]
1271
        );
1272
1273
0
        for (op_idx, op) in operands.late() {
1274
0
            if op.kind() == OperandKind::Def {
1275
0
                self.alloc_def_op(op_idx, op, operands.0, block, inst)?;
1276
            } else {
1277
0
                self.alloc_use(op_idx, op, inst)?;
1278
            }
1279
        }
1280
0
        for (op_idx, op) in operands.early() {
1281
0
            trace!("Allocating use operand {op}");
1282
0
            if op.kind() == OperandKind::Use {
1283
0
                self.alloc_use(op_idx, op, inst)?;
1284
            } else {
1285
0
                self.alloc_def_op(op_idx, op, operands.0, block, inst)?;
1286
            }
1287
        }
1288
1289
0
        for (op_idx, op) in operands.use_ops() {
1290
0
            if op.as_fixed_nonallocatable().is_some() {
1291
0
                continue;
1292
0
            }
1293
0
            let curr_alloc = self.vreg_allocs[op.vreg().vreg()];
1294
0
            let new_alloc = self.allocs[(inst.index(), op_idx)];
1295
0
            if curr_alloc != new_alloc {
1296
0
                trace!("Adding edit from {curr_alloc:?} to {new_alloc:?} before inst {inst:?} for {op}");
1297
0
                self.add_move(
1298
0
                    inst,
1299
0
                    curr_alloc,
1300
0
                    new_alloc,
1301
0
                    op.class(),
1302
0
                    InstPosition::Before,
1303
0
                )?;
1304
0
            }
1305
        }
1306
0
        if self.func.is_branch(inst) {
1307
0
            self.process_branch(block, inst)?;
1308
0
        }
1309
0
        for entry in self.reused_input_to_reuse_op.iter_mut() {
1310
0
            *entry = usize::MAX;
1311
0
        }
1312
0
        if trace_enabled!() {
1313
0
            self.log_post_inst_processing_state(inst);
1314
0
        }
1315
0
        Ok(())
1316
0
    }
1317
1318
    /// At the beginning of every block, all virtual registers that are
1319
    /// livein are expected to be in their respective spillslots.
1320
    /// This function sets the current allocations of livein registers
1321
    /// to their spillslots and inserts the edits to flow livein values to
1322
    /// the allocations where they are expected to be before the first
1323
    /// instruction.
1324
0
    fn reload_at_begin(&mut self, block: Block) -> Result<(), RegAllocError> {
1325
0
        trace!(
1326
            "Reloading live registers at the beginning of block {:?}",
1327
            block
1328
        );
1329
0
        trace!(
1330
            "Live registers at the beginning of block {:?}: {:?}",
1331
            block,
1332
            self.live_vregs
1333
        );
1334
0
        trace!(
1335
            "Block params at block {:?} beginning: {:?}",
1336
            block,
1337
0
            self.func.block_params(block)
1338
        );
1339
0
        self.reset_available_pregs_and_scratch_regs();
1340
0
        let first_inst = self.func.block_insns(block).first();
1341
        // We need to check for the registers that are still live.
1342
        // These registers are either livein or block params
1343
        // Liveins should be stack-allocated and block params should be freed.
1344
0
        for vreg in self.func.block_params(block).iter().cloned() {
1345
0
            trace!("Processing {}", vreg);
1346
0
            if self.vreg_allocs[vreg.vreg()] == Allocation::none() {
1347
                // If this block param was never used, its allocation will
1348
                // be none at this point.
1349
0
                continue;
1350
0
            }
1351
            // The allocation where the vreg is expected to be before
1352
            // the first instruction.
1353
0
            let prev_alloc = self.vreg_allocs[vreg.vreg()];
1354
0
            let slot = Allocation::stack(self.get_spillslot(vreg));
1355
0
            self.vreg_to_live_inst_range[vreg.vreg()].2 = slot;
1356
0
            self.vreg_to_live_inst_range[vreg.vreg()].0 = ProgPoint::before(first_inst);
1357
0
            trace!("{} is a block param. Freeing it", vreg);
1358
            // A block's block param is not live before the block.
1359
            // And `vreg_allocs[i]` of a virtual register i is none for
1360
            // dead vregs.
1361
0
            self.freealloc(vreg);
1362
0
            if slot == prev_alloc {
1363
                // No need to do any movements if the spillslot is where the vreg is expected to be.
1364
0
                trace!(
1365
                    "No need to reload {} because it's already in its expected allocation",
1366
                    vreg
1367
                );
1368
0
                continue;
1369
0
            }
1370
0
            trace!(
1371
                "Move reason: reload {} at begin - move from its spillslot",
1372
                vreg
1373
            );
1374
0
            self.state.add_move(
1375
0
                self.func.block_insns(block).first(),
1376
0
                slot,
1377
0
                prev_alloc,
1378
0
                vreg.class(),
1379
0
                InstPosition::Before,
1380
0
            )?;
1381
        }
1382
0
        for vreg in self.live_vregs.iter() {
1383
0
            trace!("Processing {}", vreg);
1384
0
            trace!(
1385
                "{} is not a block param. It's a liveout vreg from some predecessor",
1386
                vreg
1387
            );
1388
            // The allocation where the vreg is expected to be before
1389
            // the first instruction.
1390
0
            let prev_alloc = self.vreg_allocs[vreg.vreg()];
1391
0
            let slot = Allocation::stack(self.state.get_spillslot(vreg));
1392
0
            trace!("Setting {}'s current allocation to its spillslot", vreg);
1393
0
            self.state.vreg_allocs[vreg.vreg()] = slot;
1394
0
            if let Some(preg) = prev_alloc.as_reg() {
1395
0
                trace!("{} was in {}. Removing it", preg, vreg);
1396
                // Nothing is in that preg anymore.
1397
0
                self.state.vreg_in_preg[preg.index()] = VReg::invalid();
1398
0
            }
1399
0
            if slot == prev_alloc {
1400
                // No need to do any movements if the spillslot is where the vreg is expected to be.
1401
0
                trace!(
1402
                    "No need to reload {} because it's already in its expected allocation",
1403
                    vreg
1404
                );
1405
0
                continue;
1406
0
            }
1407
0
            trace!(
1408
                "Move reason: reload {} at begin - move from its spillslot",
1409
                vreg
1410
            );
1411
0
            self.state.add_move(
1412
0
                first_inst,
1413
0
                slot,
1414
0
                prev_alloc,
1415
0
                vreg.class(),
1416
0
                InstPosition::Before,
1417
0
            )?;
1418
        }
1419
        // Reset this, in case a fixed reg used by a branch arg defined on the branch
1420
        // is used as a scratch reg in the previous loop.
1421
0
        self.state.scratch_regs = self.state.dedicated_scratch_regs.clone();
1422
1423
0
        let get_succ_idx_of_pred = |pred, func: &F| {
1424
0
            for (idx, pred_succ) in func.block_succs(pred).iter().enumerate() {
1425
0
                if *pred_succ == block {
1426
0
                    return idx;
1427
0
                }
1428
            }
1429
0
            unreachable!(
1430
                "{:?} was not found in the successor list of its predecessor {:?}",
1431
                block, pred
1432
            );
1433
0
        };
1434
0
        trace!(
1435
            "Checking for predecessor branch args/livein vregs defined in the branch with fixed-reg constraint"
1436
        );
1437
0
        for (param_idx, block_param) in self.func.block_params(block).iter().cloned().enumerate() {
1438
            // Block param is never used. Don't bother.
1439
0
            if self.state.vreg_spillslots[block_param.vreg()].is_invalid() {
1440
0
                continue;
1441
0
            }
1442
0
            for pred in self.func.block_preds(block).iter().cloned() {
1443
0
                let pred_last_inst = self.func.block_insns(pred).last();
1444
0
                let curr_block_succ_idx = get_succ_idx_of_pred(pred, self.func);
1445
0
                let branch_arg_for_param =
1446
0
                    self.func
1447
0
                        .branch_blockparams(pred, pred_last_inst, curr_block_succ_idx)[param_idx];
1448
                // If the branch arg is defined in the branch instruction, the move will have to be done
1449
                // here, instead of at the end of the predecessor block.
1450
0
                self.state.move_if_def_pred_branch(
1451
0
                    block,
1452
0
                    pred,
1453
0
                    branch_arg_for_param,
1454
0
                    self.state.vreg_spillslots[block_param.vreg()],
1455
0
                )?;
1456
            }
1457
        }
1458
0
        for vreg in self.live_vregs.iter() {
1459
0
            for pred in self.func.block_preds(block).iter().cloned() {
1460
0
                let slot = self.state.vreg_spillslots[vreg.vreg()];
1461
                // Move from the reg into its spillslot if it's defined in a predecessor's
1462
                // branch instruction.
1463
0
                self.state
1464
0
                    .move_if_def_pred_branch(block, pred, vreg, slot)?;
1465
            }
1466
        }
1467
0
        if trace_enabled!() {
1468
0
            self.log_post_reload_at_begin_state(block);
1469
0
        }
1470
0
        Ok(())
1471
0
    }
1472
1473
0
    fn log_post_reload_at_begin_state(&self, block: Block) {
1474
0
        trace!("");
1475
0
        trace!("State after instruction reload_at_begin of {:?}", block);
1476
0
        let mut map = FxHashMap::default();
1477
0
        for (vreg_idx, alloc) in self.state.vreg_allocs.iter().enumerate() {
1478
0
            if *alloc != Allocation::none() {
1479
0
                map.insert(format!("vreg{vreg_idx}"), alloc);
1480
0
            }
1481
        }
1482
0
        trace!("vreg_allocs: {:?}", map);
1483
0
        let mut map = FxHashMap::default();
1484
0
        for i in 0..self.state.vreg_in_preg.len() {
1485
0
            if self.state.vreg_in_preg[i] != VReg::invalid() {
1486
0
                map.insert(PReg::from_index(i), self.state.vreg_in_preg[i]);
1487
0
            }
1488
        }
1489
0
        trace!("vreg_in_preg: {:?}", map);
1490
0
        trace!("Int LRU: {:?}", self.state.lrus[RegClass::Int]);
1491
0
        trace!("Float LRU: {:?}", self.state.lrus[RegClass::Float]);
1492
0
        trace!("Vector LRU: {:?}", self.state.lrus[RegClass::Vector]);
1493
0
    }
1494
1495
0
    fn log_post_inst_processing_state(&self, inst: Inst) {
1496
0
        trace!("");
1497
0
        trace!("State after instruction {:?}", inst);
1498
0
        let mut map = FxHashMap::default();
1499
0
        for (vreg_idx, alloc) in self.state.vreg_allocs.iter().enumerate() {
1500
0
            if *alloc != Allocation::none() {
1501
0
                map.insert(format!("vreg{vreg_idx}"), alloc);
1502
0
            }
1503
        }
1504
0
        trace!("vreg_allocs: {:?}", map);
1505
0
        let mut v = Vec::new();
1506
0
        for i in 0..self.state.vreg_in_preg.len() {
1507
0
            if self.state.vreg_in_preg[i] != VReg::invalid() {
1508
0
                v.push(format!(
1509
0
                    "{}: {}, ",
1510
0
                    PReg::from_index(i),
1511
0
                    self.state.vreg_in_preg[i]
1512
0
                ));
1513
0
            }
1514
        }
1515
0
        trace!("vreg_in_preg: {:?}", v);
1516
0
        trace!("Int LRU: {:?}", self.state.lrus[RegClass::Int]);
1517
0
        trace!("Float LRU: {:?}", self.state.lrus[RegClass::Float]);
1518
0
        trace!("Vector LRU: {:?}", self.state.lrus[RegClass::Vector]);
1519
0
        trace!(
1520
            "Number of any-reg early-only to allocate for: {}",
1521
0
            self.num_any_reg_ops[ExclusiveOperandPos::EarlyOnly]
1522
        );
1523
0
        trace!(
1524
            "Number of any-reg late-only to allocate for: {}",
1525
0
            self.num_any_reg_ops[ExclusiveOperandPos::LateOnly]
1526
        );
1527
0
        trace!(
1528
            "Number of any-reg early & late to allocate for: {}",
1529
0
            self.num_any_reg_ops[ExclusiveOperandPos::Both]
1530
        );
1531
0
        trace!("");
1532
0
    }
1533
1534
0
    fn alloc_block(&mut self, block: Block) -> Result<(), RegAllocError> {
1535
0
        trace!("{:?} start", block);
1536
0
        for inst in self.func.block_insns(block).iter().rev() {
1537
0
            self.alloc_inst(block, inst)?;
1538
        }
1539
0
        self.reload_at_begin(block)?;
1540
0
        trace!("{:?} end\n", block);
1541
0
        Ok(())
1542
0
    }
1543
1544
0
    fn build_debug_info(&mut self) {
1545
0
        trace!("Building debug location info");
1546
0
        for &(vreg, start, end, label) in self.func.debug_value_labels() {
1547
0
            let (point_start, point_end, alloc) = self.vreg_to_live_inst_range[vreg.vreg()];
1548
0
            if point_start.inst() <= start && end <= point_end.inst().next() {
1549
0
                self.debug_locations
1550
0
                    .push((label, point_start, point_end, alloc));
1551
0
            }
1552
        }
1553
0
        self.debug_locations.sort_by_key(|loc| loc.0);
1554
0
    }
1555
1556
0
    fn run(&mut self) -> Result<(), RegAllocError> {
1557
0
        debug_assert_eq!(self.func.entry_block().index(), 0);
1558
0
        for block in (0..self.func.num_blocks()).rev() {
1559
0
            self.alloc_block(Block::new(block))?;
1560
        }
1561
0
        self.state.edits.reverse();
1562
0
        self.build_debug_info();
1563
0
        Ok(())
1564
0
    }
1565
}
1566
1567
0
fn log_function<F: Function>(func: &F) {
1568
0
    trace!("Processing a new function");
1569
0
    for block in 0..func.num_blocks() {
1570
0
        let block = Block::new(block);
1571
0
        trace!(
1572
            "Block {:?}. preds: {:?}. succs: {:?}, params: {:?}",
1573
            block,
1574
0
            func.block_preds(block),
1575
0
            func.block_succs(block),
1576
0
            func.block_params(block)
1577
        );
1578
0
        for inst in func.block_insns(block).iter() {
1579
0
            let clobbers = func.inst_clobbers(inst);
1580
0
            trace!(
1581
                "inst{:?}: {:?}. Clobbers: {}",
1582
0
                inst.index(),
1583
0
                func.inst_operands(inst),
1584
                clobbers
1585
            );
1586
0
            if func.is_branch(inst) {
1587
0
                trace!("Block args: ");
1588
0
                for (succ_idx, _succ) in func.block_succs(block).iter().enumerate() {
1589
0
                    trace!(" {:?}", func.branch_blockparams(block, inst, succ_idx));
1590
                }
1591
0
            }
1592
        }
1593
0
        trace!("");
1594
    }
1595
0
}
1596
1597
0
fn log_output<'a, F: Function>(env: &Env<'a, F>) {
1598
0
    trace!("Done!");
1599
0
    let mut v = Vec::new();
1600
0
    for i in 0..env.func.num_vregs() {
1601
0
        if env.state.vreg_spillslots[i].is_valid() {
1602
0
            v.push((
1603
0
                format!("{}", VReg::new(i, RegClass::Int)),
1604
0
                format!("{}", Allocation::stack(env.state.vreg_spillslots[i])),
1605
0
            ));
1606
0
        }
1607
    }
1608
0
    trace!("VReg spillslots: {:?}", v);
1609
0
    trace!("Final edits: {:?}", env.state.edits);
1610
0
}
1611
1612
0
pub fn run<F: Function>(
1613
0
    func: &F,
1614
0
    mach_env: &MachineEnv,
1615
0
    verbose_log: bool,
1616
0
    enable_ssa_checker: bool,
1617
0
) -> Result<Output, RegAllocError> {
1618
0
    if enable_ssa_checker {
1619
0
        validate_ssa(func, &CFGInfo::new(func)?)?;
1620
0
    }
1621
1622
0
    if trace_enabled!() || verbose_log {
1623
0
        log_function(func);
1624
0
    }
1625
1626
0
    let mut env = Env::new(func, mach_env);
1627
0
    env.run()?;
1628
1629
0
    if trace_enabled!() || verbose_log {
1630
0
        log_output(&env);
1631
0
    }
1632
1633
0
    Ok(Output {
1634
0
        edits: env.state.edits,
1635
0
        allocs: env.allocs.allocs,
1636
0
        inst_alloc_offsets: env.allocs.inst_alloc_offsets,
1637
0
        num_spillslots: env.state.stack.num_spillslots as usize,
1638
0
        debug_locations: env.debug_locations,
1639
0
        stats: env.stats,
1640
0
    })
1641
0
}