Coverage Report

Created: 2025-12-09 07:28

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
0
                    "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
    fn bitand(self, other: Self) -> Self {
304
        Self {
305
            items: [
306
                self.items[0] & other.items[0],
307
                self.items[1] & other.items[1],
308
            ],
309
        }
310
    }
311
}
312
313
impl<T: BitOr<Output = T> + Copy> BitOr for PartedByOperandPos<T> {
314
    type Output = Self;
315
    fn bitor(self, other: Self) -> Self {
316
        Self {
317
            items: [
318
                self.items[0] | other.items[0],
319
                self.items[1] | other.items[1],
320
            ],
321
        }
322
    }
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
    fn eq(&self, other: &Self) -> bool {
367
        self.items.eq(&other.items)
368
    }
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].extend(
448
0
            env.non_preferred_regs_by_class[RegClass::Int as usize]
449
0
                .iter()
450
0
                .cloned(),
451
        );
452
0
        regs[1].extend(
453
0
            env.non_preferred_regs_by_class[RegClass::Float as usize]
454
0
                .iter()
455
0
                .cloned(),
456
        );
457
0
        regs[2].extend(
458
0
            env.non_preferred_regs_by_class[RegClass::Vector as usize]
459
0
                .iter()
460
0
                .cloned(),
461
        );
462
0
        let allocatable_regs = PRegSet::from(env);
463
0
        let num_available_pregs: PartedByRegClass<i16> = PartedByRegClass {
464
0
            items: [
465
0
                (env.preferred_regs_by_class[RegClass::Int as usize].len()
466
0
                    + env.non_preferred_regs_by_class[RegClass::Int as usize].len())
467
0
                .try_into()
468
0
                .unwrap(),
469
0
                (env.preferred_regs_by_class[RegClass::Float as usize].len()
470
0
                    + env.non_preferred_regs_by_class[RegClass::Float as usize].len())
471
0
                .try_into()
472
0
                .unwrap(),
473
0
                (env.preferred_regs_by_class[RegClass::Vector as usize].len()
474
0
                    + env.non_preferred_regs_by_class[RegClass::Vector as usize].len())
475
0
                .try_into()
476
0
                .unwrap(),
477
0
            ],
478
0
        };
479
0
        let init_available_pregs = {
480
0
            let mut regs = allocatable_regs;
481
0
            for preg in env.fixed_stack_slots.iter() {
482
0
                regs.add(*preg);
483
0
            }
484
0
            regs
485
        };
486
0
        let dedicated_scratch_regs = PartedByRegClass {
487
0
            items: [
488
0
                env.scratch_by_class[0],
489
0
                env.scratch_by_class[1],
490
0
                env.scratch_by_class[2],
491
0
            ],
492
0
        };
493
0
        trace!("{:#?}", env);
494
0
        let (allocs, max_operand_len) = Allocs::new(func);
495
0
        let fixed_stack_slots = PRegSet::from_iter(env.fixed_stack_slots.iter().cloned());
496
0
        Self {
497
0
            func,
498
0
            allocatable_regs,
499
0
            live_vregs: VRegSet::with_capacity(func.num_vregs()),
500
0
            fixed_stack_slots,
501
0
            vreg_to_live_inst_range: vec![
502
0
                (
503
0
                    ProgPoint::invalid(),
504
0
                    ProgPoint::invalid(),
505
0
                    Allocation::none()
506
0
                );
507
0
                func.num_vregs()
508
0
            ],
509
0
            preferred_victim: PartedByRegClass {
510
0
                items: [
511
0
                    regs[0].last().cloned().unwrap_or(PReg::invalid()),
512
0
                    regs[1].last().cloned().unwrap_or(PReg::invalid()),
513
0
                    regs[2].last().cloned().unwrap_or(PReg::invalid()),
514
0
                ],
515
0
            },
516
0
            reused_input_to_reuse_op: vec![usize::MAX; max_operand_len as usize],
517
0
            init_available_pregs,
518
0
            init_num_available_pregs: num_available_pregs.clone(),
519
0
            num_any_reg_ops: PartedByExclusiveOperandPos {
520
0
                items: [
521
0
                    PartedByRegClass { items: [0; 3] },
522
0
                    PartedByRegClass { items: [0; 3] },
523
0
                    PartedByRegClass { items: [0; 3] },
524
0
                ],
525
0
            },
526
0
            allocs,
527
0
            state: State {
528
0
                func,
529
0
                // This guess is based on the sightglass benchmarks:
530
0
                // The average number of edits per instruction is 1.
531
0
                edits: Vec::with_capacity(func.num_insts()),
532
0
                fixed_stack_slots,
533
0
                scratch_regs: dedicated_scratch_regs.clone(),
534
0
                dedicated_scratch_regs,
535
0
                num_available_pregs: PartedByExclusiveOperandPos {
536
0
                    items: [
537
0
                        num_available_pregs.clone(),
538
0
                        num_available_pregs.clone(),
539
0
                        num_available_pregs.clone(),
540
0
                    ],
541
0
                },
542
0
                available_pregs: PartedByOperandPos {
543
0
                    items: [init_available_pregs, init_available_pregs],
544
0
                },
545
0
                lrus: Lrus::new(&regs[0], &regs[1], &regs[2]),
546
0
                vreg_in_preg: vec![VReg::invalid(); PReg::NUM_INDEX],
547
0
                stack: Stack::new(func),
548
0
                vreg_allocs: vec![Allocation::none(); func.num_vregs()],
549
0
                vreg_spillslots: vec![SpillSlot::invalid(); func.num_vregs()],
550
0
            },
551
0
            stats: Stats::default(),
552
0
            debug_locations: Vec::with_capacity(func.debug_value_labels().len()),
553
0
        }
554
0
    }
555
556
0
    fn reset_available_pregs_and_scratch_regs(&mut self) {
557
0
        trace!("Resetting the available pregs");
558
0
        self.available_pregs = PartedByOperandPos {
559
0
            items: [self.init_available_pregs, self.init_available_pregs],
560
0
        };
561
0
        self.scratch_regs = self.dedicated_scratch_regs.clone();
562
0
        self.num_available_pregs = PartedByExclusiveOperandPos {
563
0
            items: [self.init_num_available_pregs; 3],
564
0
        };
565
0
        debug_assert_eq!(
566
            self.num_any_reg_ops,
567
            PartedByExclusiveOperandPos {
568
                items: [PartedByRegClass { items: [0; 3] }; 3]
569
            }
570
        );
571
0
    }
572
573
0
    fn reserve_reg_for_operand(
574
0
        &mut self,
575
0
        op: Operand,
576
0
        op_idx: usize,
577
0
        preg: PReg,
578
0
    ) -> Result<(), RegAllocError> {
579
0
        trace!("Reserving register {preg} for operand {op}");
580
0
        let early_avail_pregs = self.available_pregs[OperandPos::Early];
581
0
        let late_avail_pregs = self.available_pregs[OperandPos::Late];
582
0
        match (op.pos(), op.kind()) {
583
            (OperandPos::Early, OperandKind::Use) => {
584
0
                if op.as_fixed_nonallocatable().is_none() && !early_avail_pregs.contains(preg) {
585
0
                    trace!("fixed {preg} for {op} isn't available");
586
0
                    return Err(RegAllocError::TooManyLiveRegs);
587
0
                }
588
0
                self.available_pregs[OperandPos::Early].remove(preg);
589
0
                if self.reused_input_to_reuse_op[op_idx] != usize::MAX {
590
0
                    if op.as_fixed_nonallocatable().is_none() && !late_avail_pregs.contains(preg) {
591
0
                        trace!("fixed {preg} for {op} isn't available");
592
0
                        return Err(RegAllocError::TooManyLiveRegs);
593
0
                    }
594
0
                    self.available_pregs[OperandPos::Late].remove(preg);
595
0
                }
596
            }
597
            (OperandPos::Late, OperandKind::Def) => {
598
0
                if op.as_fixed_nonallocatable().is_none() && !late_avail_pregs.contains(preg) {
599
0
                    trace!("fixed {preg} for {op} isn't available");
600
0
                    return Err(RegAllocError::TooManyLiveRegs);
601
0
                }
602
0
                self.available_pregs[OperandPos::Late].remove(preg);
603
            }
604
            _ => {
605
0
                if op.as_fixed_nonallocatable().is_none()
606
0
                    && (!early_avail_pregs.contains(preg) || !late_avail_pregs.contains(preg))
607
                {
608
0
                    trace!("fixed {preg} for {op} isn't available");
609
0
                    return Err(RegAllocError::TooManyLiveRegs);
610
0
                }
611
0
                self.available_pregs[OperandPos::Early].remove(preg);
612
0
                self.available_pregs[OperandPos::Late].remove(preg);
613
            }
614
        }
615
0
        Ok(())
616
0
    }
617
618
0
    fn allocd_within_constraint(&self, op: Operand, inst: Inst) -> bool {
619
0
        let alloc = self.vreg_allocs[op.vreg().vreg()];
620
0
        match op.constraint() {
621
            OperandConstraint::Any => {
622
0
                if let Some(preg) = alloc.as_reg() {
623
0
                    let exclusive_pos: ExclusiveOperandPos = op.into();
624
0
                    if !self.is_stack(alloc)
625
0
                        && self.num_available_pregs[exclusive_pos][op.class()]
626
0
                            < self.num_any_reg_ops[exclusive_pos][op.class()]
627
                    {
628
0
                        trace!("Need more registers to cover all any-reg ops. Going to evict {op} from {preg}");
629
0
                        return false;
630
0
                    }
631
0
                    if !self.available_pregs[op.pos()].contains(preg) {
632
                        // If a register isn't in the available pregs list, then
633
                        // there are two cases: either it's reserved for a
634
                        // fixed register constraint or a vreg allocated in the instruction
635
                        // is already assigned to it.
636
                        //
637
                        // For example:
638
                        // 1. use v0, use v0, use v0
639
                        //
640
                        // Say p0 is assigned to v0 during the processing of the first operand.
641
                        // When the second v0 operand is being processed, v0 will still be in
642
                        // v0, so it is still allocated within constraints.
643
0
                        trace!("The vreg in {preg}: {}", self.vreg_in_preg[preg.index()]);
644
0
                        self.vreg_in_preg[preg.index()] == op.vreg() &&
645
                            // If it's a late operand, it shouldn't be allocated to a
646
                            // clobber. For example:
647
                            // use v0 (fixed: p0), late use v0
648
                            // If p0 is a clobber, then v0 shouldn't be allocated to it.
649
0
                            (op.pos() != OperandPos::Late || !self.func.inst_clobbers(inst).contains(preg))
650
                    } else {
651
0
                        true
652
                    }
653
                } else {
654
0
                    !alloc.is_none()
655
                }
656
            }
657
            OperandConstraint::Reg => {
658
0
                if self.is_stack(alloc) {
659
0
                    return false;
660
0
                }
661
0
                if let Some(preg) = alloc.as_reg() {
662
0
                    if !self.available_pregs[op.pos()].contains(preg) {
663
0
                        trace!("The vreg in {preg}: {}", self.vreg_in_preg[preg.index()]);
664
0
                        self.vreg_in_preg[preg.index()] == op.vreg()
665
0
                            && (op.pos() != OperandPos::Late
666
0
                                || !self.func.inst_clobbers(inst).contains(preg))
667
                    } else {
668
0
                        true
669
                    }
670
                } else {
671
0
                    false
672
                }
673
            }
674
            // It is possible for an operand to have a fixed register constraint to
675
            // a clobber.
676
0
            OperandConstraint::FixedReg(preg) => alloc.is_reg() && alloc.as_reg().unwrap() == preg,
677
            OperandConstraint::Reuse(_) => {
678
0
                unreachable!()
679
            }
680
681
0
            OperandConstraint::Stack => self.is_stack(alloc),
682
            OperandConstraint::Limit(_) => {
683
0
                todo!("limit constraints are not yet supported in fastalloc")
684
            }
685
        }
686
0
    }
687
688
0
    fn freealloc(&mut self, vreg: VReg) {
689
0
        trace!("Freeing vreg {}", vreg);
690
0
        let alloc = self.vreg_allocs[vreg.vreg()];
691
0
        match alloc.kind() {
692
0
            AllocationKind::Reg => {
693
0
                let preg = alloc.as_reg().unwrap();
694
0
                self.vreg_in_preg[preg.index()] = VReg::invalid();
695
0
            }
696
0
            AllocationKind::Stack => (),
697
0
            AllocationKind::None => unreachable!("Attempting to free an unallocated operand!"),
698
        }
699
0
        self.vreg_allocs[vreg.vreg()] = Allocation::none();
700
0
        self.live_vregs.remove(vreg.vreg());
701
0
        trace!(
702
0
            "{} curr alloc is now {}",
703
            vreg,
704
0
            self.vreg_allocs[vreg.vreg()]
705
        );
706
0
    }
707
708
0
    fn select_suitable_reg_in_lru(&self, op: Operand) -> Result<PReg, RegAllocError> {
709
0
        let draw_from = match (op.pos(), op.kind()) {
710
            // No need to consider reuse constraints because they are
711
            // handled elsewhere
712
            (OperandPos::Late, OperandKind::Use) | (OperandPos::Early, OperandKind::Def) => {
713
0
                self.available_pregs[OperandPos::Late] & self.available_pregs[OperandPos::Early]
714
            }
715
0
            _ => self.available_pregs[op.pos()],
716
        };
717
0
        if draw_from.is_empty(op.class()) {
718
0
            trace!("No registers available for {op} in selection");
719
0
            return Err(RegAllocError::TooManyLiveRegs);
720
0
        }
721
0
        let Some(preg) = self.lrus[op.class()].last(draw_from) else {
722
0
            trace!(
723
0
                "Failed to find an available {:?} register in the LRU for operand {op}",
724
0
                op.class()
725
            );
726
0
            return Err(RegAllocError::TooManyLiveRegs);
727
        };
728
0
        Ok(preg)
729
0
    }
730
731
    /// Allocates a physical register for the operand `op`.
732
0
    fn alloc_reg_for_operand(
733
0
        &mut self,
734
0
        inst: Inst,
735
0
        op: Operand,
736
0
    ) -> Result<Allocation, RegAllocError> {
737
0
        trace!("available regs: {}", self.available_pregs);
738
0
        trace!("Int LRU: {:?}", self.lrus[RegClass::Int]);
739
0
        trace!("Float LRU: {:?}", self.lrus[RegClass::Float]);
740
0
        trace!("Vector LRU: {:?}", self.lrus[RegClass::Vector]);
741
0
        trace!("");
742
0
        let preg = self.select_suitable_reg_in_lru(op)?;
743
0
        if self.vreg_in_preg[preg.index()] != VReg::invalid() {
744
0
            self.evict_vreg_in_preg(inst, preg, InstPosition::After)?;
745
0
        }
746
0
        trace!("The allocated register for vreg {}: {}", op.vreg(), preg);
747
0
        self.lrus[op.class()].poke(preg);
748
0
        self.available_pregs[op.pos()].remove(preg);
749
0
        match (op.pos(), op.kind()) {
750
0
            (OperandPos::Late, OperandKind::Use) => {
751
0
                self.available_pregs[OperandPos::Early].remove(preg);
752
0
            }
753
0
            (OperandPos::Early, OperandKind::Def) => {
754
0
                self.available_pregs[OperandPos::Late].remove(preg);
755
0
            }
756
            (OperandPos::Late, OperandKind::Def)
757
0
                if matches!(op.constraint(), OperandConstraint::Reuse(_)) =>
758
0
            {
759
0
                self.available_pregs[OperandPos::Early].remove(preg);
760
0
            }
761
0
            _ => (),
762
        };
763
0
        Ok(Allocation::reg(preg))
764
0
    }
765
766
    /// Allocates for the operand `op` with index `op_idx` into the
767
    /// vector of instruction `inst`'s operands.
768
0
    fn alloc_operand(
769
0
        &mut self,
770
0
        inst: Inst,
771
0
        op: Operand,
772
0
        op_idx: usize,
773
0
    ) -> Result<Allocation, RegAllocError> {
774
0
        let new_alloc = match op.constraint() {
775
            OperandConstraint::Any => {
776
0
                if (op.kind() == OperandKind::Def
777
0
                    && self.vreg_allocs[op.vreg().vreg()] == Allocation::none())
778
                    // Not safe to alloc register because any-reg operands still
779
                    // need them
780
0
                    || self.num_any_reg_ops[op.into()][op.class()] >= self.num_available_pregs[op.into()][op.class()]
781
                {
782
                    // If the def is never used, it can just be put in its spillslot.
783
0
                    Allocation::stack(self.get_spillslot(op.vreg()))
784
                } else {
785
0
                    match self.alloc_reg_for_operand(inst, op) {
786
0
                        Ok(alloc) => alloc,
787
                        Err(RegAllocError::TooManyLiveRegs) => {
788
0
                            Allocation::stack(self.get_spillslot(op.vreg()))
789
                        }
790
0
                        Err(err) => return Err(err),
791
                    }
792
                }
793
            }
794
            OperandConstraint::Reg => {
795
0
                let alloc = self.alloc_reg_for_operand(inst, op)?;
796
0
                self.num_any_reg_ops[op.into()][op.class()] -= 1;
797
0
                trace!(
798
0
                    "Number of {:?} any-reg ops to allocate now: {}",
799
0
                    Into::<ExclusiveOperandPos>::into(op),
800
0
                    self.num_any_reg_ops[op.into()]
801
                );
802
0
                alloc
803
            }
804
0
            OperandConstraint::FixedReg(preg) => {
805
0
                trace!("The fixed preg: {} for operand {}", preg, op);
806
807
0
                Allocation::reg(preg)
808
            }
809
            OperandConstraint::Reuse(_) => {
810
                // This is handled elsewhere.
811
0
                unreachable!();
812
            }
813
814
0
            OperandConstraint::Stack => Allocation::stack(self.get_spillslot(op.vreg())),
815
            OperandConstraint::Limit(_) => {
816
0
                todo!("limit constraints are not yet supported in fastalloc")
817
            }
818
        };
819
0
        self.allocs[(inst.index(), op_idx)] = new_alloc;
820
0
        Ok(new_alloc)
821
0
    }
822
823
    /// Allocate operand the `op_idx`th operand `op` in instruction `inst` within its constraint.
824
    /// Since only fixed register constraints are allowed, `fixed_spillslot` is used when a
825
    /// fixed stack allocation is needed, like when transferring a stack allocation from a
826
    /// reuse operand allocation to the reused input.
827
0
    fn process_operand_allocation(
828
0
        &mut self,
829
0
        inst: Inst,
830
0
        op: Operand,
831
0
        op_idx: usize,
832
0
    ) -> Result<(), RegAllocError> {
833
0
        if let Some(preg) = op.as_fixed_nonallocatable() {
834
0
            self.allocs[(inst.index(), op_idx)] = Allocation::reg(preg);
835
0
            trace!(
836
0
                "Allocation for instruction {:?} and operand {}: {}",
837
                inst,
838
                op,
839
0
                self.allocs[(inst.index(), op_idx)]
840
            );
841
0
            return Ok(());
842
0
        }
843
0
        if !self.allocd_within_constraint(op, inst) {
844
0
            trace!(
845
0
                "{op} isn't allocated within constraints (the alloc: {}).",
846
0
                self.vreg_allocs[op.vreg().vreg()]
847
            );
848
0
            let curr_alloc = self.vreg_allocs[op.vreg().vreg()];
849
0
            let new_alloc = self.alloc_operand(inst, op, op_idx)?;
850
0
            if curr_alloc.is_none() {
851
0
                self.live_vregs.insert(op.vreg());
852
0
                self.vreg_to_live_inst_range[op.vreg().vreg()].1 = match (op.pos(), op.kind()) {
853
                    (OperandPos::Late, OperandKind::Use) | (_, OperandKind::Def) => {
854
                        // Live range ends just before the early phase of the
855
                        // next instruction.
856
0
                        ProgPoint::before(Inst::new(inst.index() + 1))
857
                    }
858
                    (OperandPos::Early, OperandKind::Use) => {
859
                        // Live range ends just before the late phase of the current instruction.
860
0
                        ProgPoint::after(inst)
861
                    }
862
                };
863
0
                self.vreg_to_live_inst_range[op.vreg().vreg()].2 = new_alloc;
864
865
0
                trace!("Setting vreg_allocs[{op}] to {new_alloc:?}");
866
0
                self.vreg_allocs[op.vreg().vreg()] = new_alloc;
867
0
                if let Some(preg) = new_alloc.as_reg() {
868
0
                    self.vreg_in_preg[preg.index()] = op.vreg();
869
0
                }
870
            }
871
            // Need to insert a move to propagate flow from the current
872
            // allocation to the subsequent places where the value was
873
            // used (in `prev_alloc`, that is).
874
            else {
875
0
                trace!("Move reason: Prev allocation doesn't meet constraints");
876
0
                if op.kind() == OperandKind::Def {
877
0
                    trace!("Adding edit from {new_alloc:?} to {curr_alloc:?} after inst {inst:?} for {op}");
878
0
                    self.add_move(inst, new_alloc, curr_alloc, op.class(), InstPosition::After)?;
879
0
                }
880
                // Edits for use operands are added later to avoid inserting
881
                // edits out of order.
882
883
0
                if let Some(preg) = new_alloc.as_reg() {
884
0
                    // Don't change the allocation.
885
0
                    self.vreg_in_preg[preg.index()] = VReg::invalid();
886
0
                }
887
            }
888
0
            trace!(
889
0
                "Allocation for instruction {:?} and operand {}: {}",
890
                inst,
891
                op,
892
0
                self.allocs[(inst.index(), op_idx)]
893
            );
894
        } else {
895
0
            trace!("{op} is already allocated within constraints");
896
0
            self.allocs[(inst.index(), op_idx)] = self.vreg_allocs[op.vreg().vreg()];
897
0
            if op.constraint() == OperandConstraint::Reg {
898
0
                self.num_any_reg_ops[op.into()][op.class()] -= 1;
899
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()]);
900
0
            }
901
0
            if let Some(preg) = self.allocs[(inst.index(), op_idx)].as_reg() {
902
0
                if self.allocatable_regs.contains(preg) {
903
0
                    self.lrus[preg.class()].poke(preg);
904
0
                }
905
0
                self.available_pregs[op.pos()].remove(preg);
906
0
                self.available_pregs[op.pos()].remove(preg);
907
0
                match (op.pos(), op.kind()) {
908
0
                    (OperandPos::Late, OperandKind::Use) => {
909
0
                        self.available_pregs[OperandPos::Early].remove(preg);
910
0
                        self.available_pregs[OperandPos::Early].remove(preg);
911
0
                    }
912
0
                    (OperandPos::Early, OperandKind::Def) => {
913
0
                        self.available_pregs[OperandPos::Late].remove(preg);
914
0
                        self.available_pregs[OperandPos::Late].remove(preg);
915
0
                    }
916
0
                    _ => (),
917
                };
918
0
            }
919
0
            trace!(
920
0
                "Allocation for instruction {:?} and operand {}: {}",
921
                inst,
922
                op,
923
0
                self.allocs[(inst.index(), op_idx)]
924
            );
925
        }
926
0
        trace!(
927
0
            "Late available regs: {}",
928
0
            self.available_pregs[OperandPos::Late]
929
        );
930
0
        trace!(
931
0
            "Early available regs: {}",
932
0
            self.available_pregs[OperandPos::Early]
933
        );
934
0
        Ok(())
935
0
    }
936
937
0
    fn remove_clobbers_from_available_pregs(&mut self, clobbers: PRegSet) {
938
0
        trace!("Removing clobbers {clobbers} from late available reg sets");
939
0
        let all_but_clobbers = clobbers.invert();
940
0
        self.available_pregs[OperandPos::Late].intersect_from(all_but_clobbers);
941
0
    }
942
943
    /// If instruction `inst` is a branch in `block`,
944
    /// this function places branch arguments in the spillslots
945
    /// expected by the destination blocks.
946
0
    fn process_branch(&mut self, block: Block, inst: Inst) -> Result<(), RegAllocError> {
947
0
        trace!("Processing branch instruction {inst:?} in block {block:?}");
948
949
0
        let mut int_parallel_moves = ParallelMoves::new();
950
0
        let mut float_parallel_moves = ParallelMoves::new();
951
0
        let mut vec_parallel_moves = ParallelMoves::new();
952
953
0
        for (succ_idx, succ) in self.func.block_succs(block).iter().enumerate() {
954
0
            for (pos, vreg) in self
955
0
                .func
956
0
                .branch_blockparams(block, inst, succ_idx)
957
0
                .iter()
958
0
                .enumerate()
959
            {
960
0
                if self
961
0
                    .func
962
0
                    .inst_operands(inst)
963
0
                    .iter()
964
0
                    .find(|op| op.vreg() == *vreg && op.kind() == OperandKind::Def)
965
0
                    .is_some()
966
                {
967
                    // vreg is defined in this instruction, so it's dead already.
968
                    // Can't move it.
969
0
                    continue;
970
0
                }
971
0
                let succ_params = self.func.block_params(*succ);
972
0
                let succ_param_vreg = succ_params[pos];
973
0
                if self.vreg_spillslots[succ_param_vreg.vreg()].is_invalid() {
974
0
                    self.vreg_spillslots[succ_param_vreg.vreg()] =
975
0
                        self.stack.allocstack(succ_param_vreg.class());
976
0
                }
977
0
                if self.vreg_spillslots[vreg.vreg()].is_invalid() {
978
0
                    self.vreg_spillslots[vreg.vreg()] = self.stack.allocstack(vreg.class());
979
0
                }
980
0
                let vreg_spill = Allocation::stack(self.vreg_spillslots[vreg.vreg()]);
981
0
                let curr_alloc = self.vreg_allocs[vreg.vreg()];
982
0
                if curr_alloc.is_none() {
983
0
                    self.live_vregs.insert(*vreg);
984
0
                    self.vreg_to_live_inst_range[vreg.vreg()].1 = ProgPoint::before(inst);
985
0
                } else if curr_alloc != vreg_spill {
986
0
                    self.add_move(
987
0
                        inst,
988
0
                        vreg_spill,
989
0
                        curr_alloc,
990
0
                        vreg.class(),
991
0
                        InstPosition::Before,
992
0
                    )?;
993
0
                }
994
0
                self.vreg_allocs[vreg.vreg()] = vreg_spill;
995
0
                let parallel_moves = match vreg.class() {
996
0
                    RegClass::Int => &mut int_parallel_moves,
997
0
                    RegClass::Float => &mut float_parallel_moves,
998
0
                    RegClass::Vector => &mut vec_parallel_moves,
999
                };
1000
0
                let from = Allocation::stack(self.vreg_spillslots[vreg.vreg()]);
1001
0
                let to = Allocation::stack(self.vreg_spillslots[succ_param_vreg.vreg()]);
1002
0
                trace!("Recording parallel move from {from} to {to}");
1003
0
                parallel_moves.add(from, to, Some(*vreg));
1004
            }
1005
        }
1006
1007
0
        let resolved_int = int_parallel_moves.resolve();
1008
0
        let resolved_float = float_parallel_moves.resolve();
1009
0
        let resolved_vec = vec_parallel_moves.resolve();
1010
0
        let mut scratch_regs = self.scratch_regs.clone();
1011
0
        let mut num_spillslots = self.stack.num_spillslots;
1012
0
        let mut avail_regs =
1013
0
            self.available_pregs[OperandPos::Early] & self.available_pregs[OperandPos::Late];
1014
1015
0
        trace!("Resolving parallel moves");
1016
0
        for (resolved, class) in [
1017
0
            (resolved_int, RegClass::Int),
1018
0
            (resolved_float, RegClass::Float),
1019
0
            (resolved_vec, RegClass::Vector),
1020
        ] {
1021
0
            let scratch_resolver = MoveAndScratchResolver {
1022
0
                find_free_reg: || {
1023
0
                    if let Some(reg) = scratch_regs[class] {
1024
0
                        trace!("Retrieved reg {reg} for scratch resolver");
1025
0
                        scratch_regs[class] = None;
1026
0
                        Some(Allocation::reg(reg))
1027
                    } else {
1028
0
                        let Some(preg) = self.lrus[class].last(avail_regs) else {
1029
0
                            trace!("Couldn't find any reg for scratch resolver");
1030
0
                            return None;
1031
                        };
1032
0
                        avail_regs.remove(preg);
1033
0
                        trace!("Retrieved reg {preg} for scratch resolver");
1034
0
                        Some(Allocation::reg(preg))
1035
                    }
1036
0
                },
1037
0
                get_stackslot: || {
1038
0
                    let size: u32 = self.func.spillslot_size(class).try_into().unwrap();
1039
0
                    let mut offset = num_spillslots;
1040
0
                    debug_assert!(size.is_power_of_two());
1041
0
                    offset = (offset + size - 1) & !(size - 1);
1042
0
                    let slot = if self.func.multi_spillslot_named_by_last_slot() {
1043
0
                        offset + size - 1
1044
                    } else {
1045
0
                        offset
1046
                    };
1047
0
                    offset += size;
1048
0
                    num_spillslots = offset;
1049
0
                    trace!("Retrieved slot {slot} for scratch resolver");
1050
0
                    Allocation::stack(SpillSlot::new(slot as usize))
1051
0
                },
1052
0
                is_stack_alloc: |alloc| self.is_stack(alloc),
1053
0
                borrowed_scratch_reg: self.preferred_victim[class],
1054
            };
1055
0
            let moves = scratch_resolver.compute(resolved);
1056
0
            trace!("Resolved {class:?} parallel moves");
1057
0
            for (from, to, _) in moves.into_iter().rev() {
1058
0
                self.edits
1059
0
                    .push((ProgPoint::before(inst), Edit::Move { from, to }))
1060
            }
1061
0
            self.stack.num_spillslots = num_spillslots;
1062
        }
1063
0
        trace!("Completed processing branch");
1064
0
        Ok(())
1065
0
    }
1066
1067
0
    fn alloc_def_op(
1068
0
        &mut self,
1069
0
        op_idx: usize,
1070
0
        op: Operand,
1071
0
        operands: &[Operand],
1072
0
        block: Block,
1073
0
        inst: Inst,
1074
0
    ) -> Result<(), RegAllocError> {
1075
0
        trace!("Allocating def operand {op}");
1076
0
        if let OperandConstraint::Reuse(reused_idx) = op.constraint() {
1077
0
            let reused_op = operands[reused_idx];
1078
            // Alloc as an operand alive in both early and late phases
1079
0
            let new_reuse_op = Operand::new(
1080
0
                op.vreg(),
1081
0
                reused_op.constraint(),
1082
0
                OperandKind::Def,
1083
0
                OperandPos::Early,
1084
            );
1085
0
            trace!("allocating reuse op {op} as {new_reuse_op}");
1086
0
            self.process_operand_allocation(inst, new_reuse_op, op_idx)?;
1087
0
        } else if self.func.is_branch(inst) {
1088
            // If the defined vreg is used as a branch arg and it has an
1089
            // any or stack constraint, define it into the block param spillslot
1090
0
            let mut param_spillslot = None;
1091
0
            'outer: for (succ_idx, succ) in self.func.block_succs(block).iter().cloned().enumerate()
1092
            {
1093
0
                for (param_idx, branch_arg_vreg) in self
1094
0
                    .func
1095
0
                    .branch_blockparams(block, inst, succ_idx)
1096
0
                    .iter()
1097
0
                    .cloned()
1098
0
                    .enumerate()
1099
                {
1100
0
                    if op.vreg() == branch_arg_vreg {
1101
0
                        if matches!(
1102
0
                            op.constraint(),
1103
                            OperandConstraint::Any | OperandConstraint::Stack
1104
0
                        ) {
1105
0
                            let block_param = self.func.block_params(succ)[param_idx];
1106
0
                            param_spillslot = Some(self.get_spillslot(block_param));
1107
0
                        }
1108
0
                        break 'outer;
1109
0
                    }
1110
                }
1111
            }
1112
0
            if let Some(param_spillslot) = param_spillslot {
1113
0
                let spillslot = self.vreg_spillslots[op.vreg().vreg()];
1114
0
                self.vreg_spillslots[op.vreg().vreg()] = param_spillslot;
1115
0
                let op = Operand::new(op.vreg(), OperandConstraint::Stack, op.kind(), op.pos());
1116
0
                self.process_operand_allocation(inst, op, op_idx)?;
1117
0
                self.vreg_spillslots[op.vreg().vreg()] = spillslot;
1118
            } else {
1119
0
                self.process_operand_allocation(inst, op, op_idx)?;
1120
            }
1121
        } else {
1122
0
            self.process_operand_allocation(inst, op, op_idx)?;
1123
        }
1124
0
        let slot = self.vreg_spillslots[op.vreg().vreg()];
1125
0
        if slot.is_valid() {
1126
0
            self.vreg_to_live_inst_range[op.vreg().vreg()].2 = Allocation::stack(slot);
1127
0
            let curr_alloc = self.vreg_allocs[op.vreg().vreg()];
1128
0
            let new_alloc = Allocation::stack(self.vreg_spillslots[op.vreg().vreg()]);
1129
0
            if curr_alloc != new_alloc {
1130
0
                self.add_move(inst, curr_alloc, new_alloc, op.class(), InstPosition::After)?;
1131
0
            }
1132
0
        }
1133
0
        self.vreg_to_live_inst_range[op.vreg().vreg()].0 = ProgPoint::after(inst);
1134
0
        self.freealloc(op.vreg());
1135
0
        Ok(())
1136
0
    }
1137
1138
0
    fn alloc_use(&mut self, op_idx: usize, op: Operand, inst: Inst) -> Result<(), RegAllocError> {
1139
0
        trace!("Allocating use op {op}");
1140
0
        if self.reused_input_to_reuse_op[op_idx] != usize::MAX {
1141
0
            let reuse_op_idx = self.reused_input_to_reuse_op[op_idx];
1142
0
            let reuse_op_alloc = self.allocs[(inst.index(), reuse_op_idx)];
1143
0
            let Some(preg) = reuse_op_alloc.as_reg() else {
1144
0
                unreachable!();
1145
            };
1146
0
            let new_reused_input_constraint = OperandConstraint::FixedReg(preg);
1147
0
            let new_reused_input =
1148
0
                Operand::new(op.vreg(), new_reused_input_constraint, op.kind(), op.pos());
1149
0
            trace!("Allocating reused input {op} as {new_reused_input}");
1150
0
            self.process_operand_allocation(inst, new_reused_input, op_idx)?;
1151
        } else {
1152
0
            self.process_operand_allocation(inst, op, op_idx)?;
1153
        }
1154
0
        Ok(())
1155
0
    }
1156
1157
0
    fn alloc_inst(&mut self, block: Block, inst: Inst) -> Result<(), RegAllocError> {
1158
0
        trace!("Allocating instruction {:?}", inst);
1159
0
        self.reset_available_pregs_and_scratch_regs();
1160
0
        let operands = Operands::new(self.func.inst_operands(inst));
1161
0
        let clobbers = self.func.inst_clobbers(inst);
1162
        // Number of registers that can be used for reg-only operands
1163
        // allocated to fixed-reg operands
1164
0
        let mut num_fixed_regs_allocatable_clobbers = 0u16;
1165
0
        trace!("init num avail pregs: {:?}", self.num_available_pregs);
1166
0
        for (op_idx, op) in operands.0.iter().cloned().enumerate() {
1167
0
            if let OperandConstraint::Reuse(reused_idx) = op.constraint() {
1168
0
                trace!("Initializing reused_input_to_reuse_op for {op}");
1169
0
                self.reused_input_to_reuse_op[reused_idx] = op_idx;
1170
0
                if operands.0[reused_idx].constraint() == OperandConstraint::Reg {
1171
0
                    trace!(
1172
0
                        "Counting {op} as an any-reg op that needs a reg in phase {:?}",
1173
                        ExclusiveOperandPos::Both
1174
                    );
1175
0
                    self.num_any_reg_ops[ExclusiveOperandPos::Both][op.class()] += 1;
1176
                    // When the reg-only operand is encountered, this will be incremented
1177
                    // Subtract by 1 to remove over-count.
1178
0
                    trace!(
1179
0
                        "Decreasing num any-reg ops in phase {:?}",
1180
                        ExclusiveOperandPos::EarlyOnly
1181
                    );
1182
0
                    self.num_any_reg_ops[ExclusiveOperandPos::EarlyOnly][op.class()] -= 1;
1183
0
                }
1184
0
            } else if op.constraint() == OperandConstraint::Reg {
1185
0
                trace!(
1186
0
                    "Counting {op} as an any-reg op that needs a reg in phase {:?}",
1187
0
                    Into::<ExclusiveOperandPos>::into(op)
1188
                );
1189
0
                self.num_any_reg_ops[op.into()][op.class()] += 1;
1190
0
            };
1191
        }
1192
0
        let mut seen = PRegSet::empty();
1193
0
        for (op_idx, op) in operands.fixed() {
1194
0
            let OperandConstraint::FixedReg(preg) = op.constraint() else {
1195
0
                unreachable!();
1196
            };
1197
0
            self.reserve_reg_for_operand(op, op_idx, preg)?;
1198
1199
0
            if !seen.contains(preg) {
1200
0
                seen.add(preg);
1201
0
                if self.allocatable_regs.contains(preg) {
1202
0
                    self.lrus[preg.class()].poke(preg);
1203
0
                    self.num_available_pregs[op.into()][op.class()] -= 1;
1204
0
                    debug_assert!(self.num_available_pregs[op.into()][op.class()] >= 0);
1205
0
                    if clobbers.contains(preg) {
1206
0
                        num_fixed_regs_allocatable_clobbers += 1;
1207
0
                    }
1208
0
                }
1209
0
            }
1210
        }
1211
0
        trace!("avail pregs after fixed: {:?}", self.num_available_pregs);
1212
1213
0
        self.remove_clobbers_from_available_pregs(clobbers);
1214
1215
0
        for (_, op) in operands.fixed() {
1216
0
            let OperandConstraint::FixedReg(preg) = op.constraint() else {
1217
0
                unreachable!();
1218
            };
1219
            // Eviction has to be done separately to avoid using a fixed register
1220
            // as a scratch register.
1221
0
            if self.vreg_in_preg[preg.index()] != VReg::invalid()
1222
0
                && self.vreg_in_preg[preg.index()] != op.vreg()
1223
            {
1224
0
                trace!(
1225
0
                    "Evicting {} from fixed register {preg}",
1226
0
                    self.vreg_in_preg[preg.index()]
1227
                );
1228
0
                self.evict_vreg_in_preg(inst, preg, InstPosition::After)?;
1229
0
                self.vreg_in_preg[preg.index()] = VReg::invalid();
1230
0
            }
1231
        }
1232
0
        for preg in clobbers {
1233
0
            if self.vreg_in_preg[preg.index()] != VReg::invalid() {
1234
0
                trace!(
1235
0
                    "Evicting {} from clobber {preg}",
1236
0
                    self.vreg_in_preg[preg.index()]
1237
                );
1238
0
                self.evict_vreg_in_preg(inst, preg, InstPosition::After)?;
1239
0
                self.vreg_in_preg[preg.index()] = VReg::invalid();
1240
0
            }
1241
0
            if self.allocatable_regs.contains(preg) {
1242
0
                if num_fixed_regs_allocatable_clobbers == 0 {
1243
0
                    trace!("Decrementing clobber avail preg");
1244
0
                    self.num_available_pregs[ExclusiveOperandPos::LateOnly][preg.class()] -= 1;
1245
0
                    self.num_available_pregs[ExclusiveOperandPos::Both][preg.class()] -= 1;
1246
0
                    debug_assert!(
1247
0
                        self.num_available_pregs[ExclusiveOperandPos::LateOnly][preg.class()] >= 0
1248
                    );
1249
0
                    debug_assert!(
1250
0
                        self.num_available_pregs[ExclusiveOperandPos::Both][preg.class()] >= 0
1251
                    );
1252
0
                } else {
1253
0
                    // Some fixed-reg operands may be clobbers and so the decrement
1254
0
                    // of the num avail regs has already been done.
1255
0
                    num_fixed_regs_allocatable_clobbers -= 1;
1256
0
                }
1257
0
            }
1258
        }
1259
1260
0
        trace!(
1261
0
            "Number of int, float, vector any-reg ops in early-only, respectively: {}",
1262
0
            self.num_any_reg_ops[ExclusiveOperandPos::EarlyOnly]
1263
        );
1264
0
        trace!(
1265
0
            "Number of any-reg ops in late-only: {}",
1266
0
            self.num_any_reg_ops[ExclusiveOperandPos::LateOnly]
1267
        );
1268
0
        trace!(
1269
0
            "Number of any-reg ops in both early and late: {}",
1270
0
            self.num_any_reg_ops[ExclusiveOperandPos::Both]
1271
        );
1272
0
        trace!(
1273
0
            "Number of available pregs for int, float, vector any-reg and any ops: {:?}",
1274
0
            self.num_available_pregs
1275
        );
1276
0
        trace!(
1277
0
            "registers available for early reg-only & any operands: {}",
1278
0
            self.available_pregs[OperandPos::Early]
1279
        );
1280
0
        trace!(
1281
0
            "registers available for late reg-only & any operands: {}",
1282
0
            self.available_pregs[OperandPos::Late]
1283
        );
1284
1285
0
        for (op_idx, op) in operands.late() {
1286
0
            if op.kind() == OperandKind::Def {
1287
0
                self.alloc_def_op(op_idx, op, operands.0, block, inst)?;
1288
            } else {
1289
0
                self.alloc_use(op_idx, op, inst)?;
1290
            }
1291
        }
1292
0
        for (op_idx, op) in operands.early() {
1293
0
            trace!("Allocating use operand {op}");
1294
0
            if op.kind() == OperandKind::Use {
1295
0
                self.alloc_use(op_idx, op, inst)?;
1296
            } else {
1297
0
                self.alloc_def_op(op_idx, op, operands.0, block, inst)?;
1298
            }
1299
        }
1300
1301
0
        for (op_idx, op) in operands.use_ops() {
1302
0
            if op.as_fixed_nonallocatable().is_some() {
1303
0
                continue;
1304
0
            }
1305
0
            let curr_alloc = self.vreg_allocs[op.vreg().vreg()];
1306
0
            let new_alloc = self.allocs[(inst.index(), op_idx)];
1307
0
            if curr_alloc != new_alloc {
1308
0
                trace!("Adding edit from {curr_alloc:?} to {new_alloc:?} before inst {inst:?} for {op}");
1309
0
                self.add_move(
1310
0
                    inst,
1311
0
                    curr_alloc,
1312
0
                    new_alloc,
1313
0
                    op.class(),
1314
0
                    InstPosition::Before,
1315
0
                )?;
1316
0
            }
1317
        }
1318
0
        if self.func.is_branch(inst) {
1319
0
            self.process_branch(block, inst)?;
1320
0
        }
1321
0
        for entry in self.reused_input_to_reuse_op.iter_mut() {
1322
0
            *entry = usize::MAX;
1323
0
        }
1324
0
        if trace_enabled!() {
1325
0
            self.log_post_inst_processing_state(inst);
1326
0
        }
1327
0
        Ok(())
1328
0
    }
1329
1330
    /// At the beginning of every block, all virtual registers that are
1331
    /// livein are expected to be in their respective spillslots.
1332
    /// This function sets the current allocations of livein registers
1333
    /// to their spillslots and inserts the edits to flow livein values to
1334
    /// the allocations where they are expected to be before the first
1335
    /// instruction.
1336
0
    fn reload_at_begin(&mut self, block: Block) -> Result<(), RegAllocError> {
1337
0
        trace!(
1338
0
            "Reloading live registers at the beginning of block {:?}",
1339
            block
1340
        );
1341
0
        trace!(
1342
0
            "Live registers at the beginning of block {:?}: {:?}",
1343
            block,
1344
            self.live_vregs
1345
        );
1346
0
        trace!(
1347
0
            "Block params at block {:?} beginning: {:?}",
1348
            block,
1349
0
            self.func.block_params(block)
1350
        );
1351
0
        self.reset_available_pregs_and_scratch_regs();
1352
0
        let first_inst = self.func.block_insns(block).first();
1353
        // We need to check for the registers that are still live.
1354
        // These registers are either livein or block params
1355
        // Liveins should be stack-allocated and block params should be freed.
1356
0
        for vreg in self.func.block_params(block).iter().cloned() {
1357
0
            trace!("Processing {}", vreg);
1358
0
            if self.vreg_allocs[vreg.vreg()] == Allocation::none() {
1359
                // If this block param was never used, its allocation will
1360
                // be none at this point.
1361
0
                continue;
1362
0
            }
1363
            // The allocation where the vreg is expected to be before
1364
            // the first instruction.
1365
0
            let prev_alloc = self.vreg_allocs[vreg.vreg()];
1366
0
            let slot = Allocation::stack(self.get_spillslot(vreg));
1367
0
            self.vreg_to_live_inst_range[vreg.vreg()].2 = slot;
1368
0
            self.vreg_to_live_inst_range[vreg.vreg()].0 = ProgPoint::before(first_inst);
1369
0
            trace!("{} is a block param. Freeing it", vreg);
1370
            // A block's block param is not live before the block.
1371
            // And `vreg_allocs[i]` of a virtual register i is none for
1372
            // dead vregs.
1373
0
            self.freealloc(vreg);
1374
0
            if slot == prev_alloc {
1375
                // No need to do any movements if the spillslot is where the vreg is expected to be.
1376
0
                trace!(
1377
0
                    "No need to reload {} because it's already in its expected allocation",
1378
                    vreg
1379
                );
1380
0
                continue;
1381
0
            }
1382
0
            trace!(
1383
0
                "Move reason: reload {} at begin - move from its spillslot",
1384
                vreg
1385
            );
1386
0
            self.state.add_move(
1387
0
                self.func.block_insns(block).first(),
1388
0
                slot,
1389
0
                prev_alloc,
1390
0
                vreg.class(),
1391
0
                InstPosition::Before,
1392
0
            )?;
1393
        }
1394
0
        for vreg in self.live_vregs.iter() {
1395
0
            trace!("Processing {}", vreg);
1396
0
            trace!(
1397
0
                "{} is not a block param. It's a liveout vreg from some predecessor",
1398
                vreg
1399
            );
1400
            // The allocation where the vreg is expected to be before
1401
            // the first instruction.
1402
0
            let prev_alloc = self.vreg_allocs[vreg.vreg()];
1403
0
            let slot = Allocation::stack(self.state.get_spillslot(vreg));
1404
0
            trace!("Setting {}'s current allocation to its spillslot", vreg);
1405
0
            self.state.vreg_allocs[vreg.vreg()] = slot;
1406
0
            if let Some(preg) = prev_alloc.as_reg() {
1407
0
                trace!("{} was in {}. Removing it", preg, vreg);
1408
                // Nothing is in that preg anymore.
1409
0
                self.state.vreg_in_preg[preg.index()] = VReg::invalid();
1410
0
            }
1411
0
            if slot == prev_alloc {
1412
                // No need to do any movements if the spillslot is where the vreg is expected to be.
1413
0
                trace!(
1414
0
                    "No need to reload {} because it's already in its expected allocation",
1415
                    vreg
1416
                );
1417
0
                continue;
1418
0
            }
1419
0
            trace!(
1420
0
                "Move reason: reload {} at begin - move from its spillslot",
1421
                vreg
1422
            );
1423
0
            self.state.add_move(
1424
0
                first_inst,
1425
0
                slot,
1426
0
                prev_alloc,
1427
0
                vreg.class(),
1428
0
                InstPosition::Before,
1429
0
            )?;
1430
        }
1431
        // Reset this, in case a fixed reg used by a branch arg defined on the branch
1432
        // is used as a scratch reg in the previous loop.
1433
0
        self.state.scratch_regs = self.state.dedicated_scratch_regs.clone();
1434
1435
0
        let get_succ_idx_of_pred = |pred, func: &F| {
1436
0
            for (idx, pred_succ) in func.block_succs(pred).iter().enumerate() {
1437
0
                if *pred_succ == block {
1438
0
                    return idx;
1439
0
                }
1440
            }
1441
0
            unreachable!(
1442
                "{:?} was not found in the successor list of its predecessor {:?}",
1443
                block, pred
1444
            );
1445
0
        };
1446
0
        trace!(
1447
0
            "Checking for predecessor branch args/livein vregs defined in the branch with fixed-reg constraint"
1448
        );
1449
0
        for (param_idx, block_param) in self.func.block_params(block).iter().cloned().enumerate() {
1450
            // Block param is never used. Don't bother.
1451
0
            if self.state.vreg_spillslots[block_param.vreg()].is_invalid() {
1452
0
                continue;
1453
0
            }
1454
0
            for pred in self.func.block_preds(block).iter().cloned() {
1455
0
                let pred_last_inst = self.func.block_insns(pred).last();
1456
0
                let curr_block_succ_idx = get_succ_idx_of_pred(pred, self.func);
1457
0
                let branch_arg_for_param =
1458
0
                    self.func
1459
0
                        .branch_blockparams(pred, pred_last_inst, curr_block_succ_idx)[param_idx];
1460
                // If the branch arg is defined in the branch instruction, the move will have to be done
1461
                // here, instead of at the end of the predecessor block.
1462
0
                self.state.move_if_def_pred_branch(
1463
0
                    block,
1464
0
                    pred,
1465
0
                    branch_arg_for_param,
1466
0
                    self.state.vreg_spillslots[block_param.vreg()],
1467
0
                )?;
1468
            }
1469
        }
1470
0
        for vreg in self.live_vregs.iter() {
1471
0
            for pred in self.func.block_preds(block).iter().cloned() {
1472
0
                let slot = self.state.vreg_spillslots[vreg.vreg()];
1473
                // Move from the reg into its spillslot if it's defined in a predecessor's
1474
                // branch instruction.
1475
0
                self.state
1476
0
                    .move_if_def_pred_branch(block, pred, vreg, slot)?;
1477
            }
1478
        }
1479
0
        if trace_enabled!() {
1480
0
            self.log_post_reload_at_begin_state(block);
1481
0
        }
1482
0
        Ok(())
1483
0
    }
1484
1485
0
    fn log_post_reload_at_begin_state(&self, block: Block) {
1486
0
        trace!("");
1487
0
        trace!("State after instruction reload_at_begin of {:?}", block);
1488
0
        let mut map = FxHashMap::default();
1489
0
        for (vreg_idx, alloc) in self.state.vreg_allocs.iter().enumerate() {
1490
0
            if *alloc != Allocation::none() {
1491
0
                map.insert(format!("vreg{vreg_idx}"), alloc);
1492
0
            }
1493
        }
1494
0
        trace!("vreg_allocs: {:?}", map);
1495
0
        let mut map = FxHashMap::default();
1496
0
        for i in 0..self.state.vreg_in_preg.len() {
1497
0
            if self.state.vreg_in_preg[i] != VReg::invalid() {
1498
0
                map.insert(PReg::from_index(i), self.state.vreg_in_preg[i]);
1499
0
            }
1500
        }
1501
0
        trace!("vreg_in_preg: {:?}", map);
1502
0
        trace!("Int LRU: {:?}", self.state.lrus[RegClass::Int]);
1503
0
        trace!("Float LRU: {:?}", self.state.lrus[RegClass::Float]);
1504
0
        trace!("Vector LRU: {:?}", self.state.lrus[RegClass::Vector]);
1505
0
    }
1506
1507
0
    fn log_post_inst_processing_state(&self, inst: Inst) {
1508
0
        trace!("");
1509
0
        trace!("State after instruction {:?}", inst);
1510
0
        let mut map = FxHashMap::default();
1511
0
        for (vreg_idx, alloc) in self.state.vreg_allocs.iter().enumerate() {
1512
0
            if *alloc != Allocation::none() {
1513
0
                map.insert(format!("vreg{vreg_idx}"), alloc);
1514
0
            }
1515
        }
1516
0
        trace!("vreg_allocs: {:?}", map);
1517
0
        let mut v = Vec::new();
1518
0
        for i in 0..self.state.vreg_in_preg.len() {
1519
0
            if self.state.vreg_in_preg[i] != VReg::invalid() {
1520
0
                v.push(format!(
1521
0
                    "{}: {}, ",
1522
0
                    PReg::from_index(i),
1523
0
                    self.state.vreg_in_preg[i]
1524
0
                ));
1525
0
            }
1526
        }
1527
0
        trace!("vreg_in_preg: {:?}", v);
1528
0
        trace!("Int LRU: {:?}", self.state.lrus[RegClass::Int]);
1529
0
        trace!("Float LRU: {:?}", self.state.lrus[RegClass::Float]);
1530
0
        trace!("Vector LRU: {:?}", self.state.lrus[RegClass::Vector]);
1531
0
        trace!(
1532
0
            "Number of any-reg early-only to allocate for: {}",
1533
0
            self.num_any_reg_ops[ExclusiveOperandPos::EarlyOnly]
1534
        );
1535
0
        trace!(
1536
0
            "Number of any-reg late-only to allocate for: {}",
1537
0
            self.num_any_reg_ops[ExclusiveOperandPos::LateOnly]
1538
        );
1539
0
        trace!(
1540
0
            "Number of any-reg early & late to allocate for: {}",
1541
0
            self.num_any_reg_ops[ExclusiveOperandPos::Both]
1542
        );
1543
0
        trace!("");
1544
0
    }
1545
1546
0
    fn alloc_block(&mut self, block: Block) -> Result<(), RegAllocError> {
1547
0
        trace!("{:?} start", block);
1548
0
        for inst in self.func.block_insns(block).iter().rev() {
1549
0
            self.alloc_inst(block, inst)?;
1550
        }
1551
0
        self.reload_at_begin(block)?;
1552
0
        trace!("{:?} end\n", block);
1553
0
        Ok(())
1554
0
    }
1555
1556
0
    fn build_debug_info(&mut self) {
1557
0
        trace!("Building debug location info");
1558
0
        for &(vreg, start, end, label) in self.func.debug_value_labels() {
1559
0
            let (point_start, point_end, alloc) = self.vreg_to_live_inst_range[vreg.vreg()];
1560
0
            if point_start.inst() <= start && end <= point_end.inst().next() {
1561
0
                self.debug_locations
1562
0
                    .push((label, point_start, point_end, alloc));
1563
0
            }
1564
        }
1565
0
        self.debug_locations.sort_by_key(|loc| loc.0);
1566
0
    }
1567
1568
0
    fn run(&mut self) -> Result<(), RegAllocError> {
1569
0
        debug_assert_eq!(self.func.entry_block().index(), 0);
1570
0
        for block in (0..self.func.num_blocks()).rev() {
1571
0
            self.alloc_block(Block::new(block))?;
1572
        }
1573
0
        self.state.edits.reverse();
1574
0
        self.build_debug_info();
1575
0
        Ok(())
1576
0
    }
1577
}
1578
1579
0
fn log_function<F: Function>(func: &F) {
1580
0
    trace!("Processing a new function");
1581
0
    for block in 0..func.num_blocks() {
1582
0
        let block = Block::new(block);
1583
0
        trace!(
1584
0
            "Block {:?}. preds: {:?}. succs: {:?}, params: {:?}",
1585
            block,
1586
0
            func.block_preds(block),
1587
0
            func.block_succs(block),
1588
0
            func.block_params(block)
1589
        );
1590
0
        for inst in func.block_insns(block).iter() {
1591
0
            let clobbers = func.inst_clobbers(inst);
1592
0
            trace!(
1593
0
                "inst{:?}: {:?}. Clobbers: {}",
1594
0
                inst.index(),
1595
0
                func.inst_operands(inst),
1596
                clobbers
1597
            );
1598
0
            if func.is_branch(inst) {
1599
0
                trace!("Block args: ");
1600
0
                for (succ_idx, _succ) in func.block_succs(block).iter().enumerate() {
1601
0
                    trace!(" {:?}", func.branch_blockparams(block, inst, succ_idx));
1602
                }
1603
0
            }
1604
        }
1605
0
        trace!("");
1606
    }
1607
0
}
1608
1609
0
fn log_output<'a, F: Function>(env: &Env<'a, F>) {
1610
0
    trace!("Done!");
1611
0
    let mut v = Vec::new();
1612
0
    for i in 0..env.func.num_vregs() {
1613
0
        if env.state.vreg_spillslots[i].is_valid() {
1614
0
            v.push((
1615
0
                format!("{}", VReg::new(i, RegClass::Int)),
1616
0
                format!("{}", Allocation::stack(env.state.vreg_spillslots[i])),
1617
0
            ));
1618
0
        }
1619
    }
1620
0
    trace!("VReg spillslots: {:?}", v);
1621
0
    trace!("Final edits: {:?}", env.state.edits);
1622
0
}
1623
1624
0
pub fn run<F: Function>(
1625
0
    func: &F,
1626
0
    mach_env: &MachineEnv,
1627
0
    verbose_log: bool,
1628
0
    enable_ssa_checker: bool,
1629
0
) -> Result<Output, RegAllocError> {
1630
0
    if enable_ssa_checker {
1631
0
        validate_ssa(func, &CFGInfo::new(func)?)?;
1632
0
    }
1633
1634
0
    if trace_enabled!() || verbose_log {
1635
0
        log_function(func);
1636
0
    }
1637
1638
0
    let mut env = Env::new(func, mach_env);
1639
0
    env.run()?;
1640
1641
0
    if trace_enabled!() || verbose_log {
1642
0
        log_output(&env);
1643
0
    }
1644
1645
0
    Ok(Output {
1646
0
        edits: env.state.edits,
1647
0
        allocs: env.allocs.allocs,
1648
0
        inst_alloc_offsets: env.allocs.inst_alloc_offsets,
1649
0
        num_spillslots: env.state.stack.num_spillslots as usize,
1650
0
        debug_locations: env.debug_locations,
1651
0
        stats: env.stats,
1652
0
    })
1653
0
}