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: 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 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 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 let mut offset = self.num_spillslots;
92 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 edits: Vec<(ProgPoint, Edit)>,
112 fixed_stack_slots: PRegSet,
113 scratch_regs: PartedByRegClass<Option<PReg>>,
116 dedicated_scratch_regs: PartedByRegClass<Option<PReg>>,
117 available_pregs: PartedByOperandPos<PRegSet>,
123 num_available_pregs: PartedByExclusiveOperandPos<PartedByRegClass<i16>>,
126 vreg_allocs: Vec<Allocation>,
128 vreg_spillslots: Vec<SpillSlot>,
131 vreg_in_preg: Vec<VReg>,
134 stack: Stack<'a, F>,
135 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 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 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 live_vregs: VRegSet,
419 reused_input_to_reuse_op: Vec<usize>,
422 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 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 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(®s[0], ®s[1], ®s[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 trace!("The vreg in {preg}: {}", self.vreg_in_preg[preg.index()]);
644 self.vreg_in_preg[preg.index()] == op.vreg() &&
645 (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 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 (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 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 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 || self.num_any_reg_ops[op.into()][op.class()] >= self.num_available_pregs[op.into()][op.class()]
781 {
782 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 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 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 ProgPoint::before(Inst::new(inst.index() + 1))
857 }
858 (OperandPos::Early, OperandKind::Use) => {
859 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 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 if let Some(preg) = new_alloc.as_reg() {
884 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 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 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 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 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 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 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 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 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 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 for vreg in self.func.block_params(block).iter().cloned() {
1357 trace!("Processing {}", vreg);
1358 if self.vreg_allocs[vreg.vreg()] == Allocation::none() {
1359 continue;
1362 }
1363 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 self.freealloc(vreg);
1374 if slot == prev_alloc {
1375 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 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 self.state.vreg_in_preg[preg.index()] = VReg::invalid();
1410 }
1411 if slot == prev_alloc {
1412 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 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 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 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 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}