regalloc2/fastalloc/
mod.rs

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