1use super::{
16 AllocRegResult, Env, LiveRangeKey, PRegIndex, RegTraversalIter, SpillSetIndex, SpillSlotData,
17 SpillSlotIndex,
18};
19use crate::{Allocation, Function, SpillSlot};
20
21impl<'a, F: Function> Env<'a, F> {
22 pub fn try_allocating_regs_for_spilled_bundles(&mut self) {
23 trace!("allocating regs for spilled bundles");
24 let mut scratch = core::mem::take(&mut self.ctx.scratch_conflicts);
25 for i in 0..self.ctx.spilled_bundles.len() {
26 let bundle = self.ctx.spilled_bundles[i]; if self.ctx.bundles[bundle].ranges.is_empty() {
29 continue;
30 }
31
32 let class = self.ctx.spillsets[self.ctx.bundles[bundle].spillset].class;
33 let hint = self.ctx.spillsets[self.ctx.bundles[bundle].spillset]
34 .hint
35 .as_valid();
36
37 self.ctx.bundles[bundle]
40 .ranges
41 .sort_unstable_by_key(|entry| entry.range.from);
42
43 let mut success = false;
44 self.ctx.output.stats.spill_bundle_reg_probes += 1;
45 for preg in RegTraversalIter::new(self.env, class, None, hint, bundle.index()) {
46 trace!("trying bundle {:?} to preg {:?}", bundle, preg);
47 let preg_idx = PRegIndex::new(preg.index());
48 if let AllocRegResult::Allocated(_) =
49 self.try_to_allocate_bundle_to_reg(bundle, preg_idx, None, &mut scratch)
50 {
51 self.ctx.output.stats.spill_bundle_reg_success += 1;
52 success = true;
53 break;
54 }
55 }
56 if !success {
57 trace!(
58 "spilling bundle {:?}: marking spillset {:?} as required",
59 bundle,
60 self.ctx.bundles[bundle].spillset
61 );
62 self.ctx.spillsets[self.ctx.bundles[bundle].spillset].required = true;
63 }
64 }
65 self.ctx.scratch_conflicts = scratch;
66 }
67
68 pub fn spillslot_can_fit_spillset(
69 &mut self,
70 spillslot: SpillSlotIndex,
71 spillset: SpillSetIndex,
72 ) -> bool {
73 !self.ctx.spillslots[spillslot.index()]
74 .ranges
75 .btree
76 .contains_key(&LiveRangeKey::from_range(
77 &self.ctx.spillsets[spillset].range,
78 ))
79 }
80
81 pub fn allocate_spillset_to_spillslot(
82 &mut self,
83 spillset: SpillSetIndex,
84 spillslot: SpillSlotIndex,
85 ) {
86 self.ctx.spillsets[spillset].slot = spillslot;
87
88 let res = self.ctx.spillslots[spillslot.index()].ranges.btree.insert(
89 LiveRangeKey::from_range(&self.ctx.spillsets[spillset].range),
90 spillset,
91 );
92
93 debug_assert!(res.is_none());
94 }
95
96 pub fn allocate_spillslots(&mut self) {
97 const MAX_ATTEMPTS: usize = 10;
98
99 for spillset in 0..self.ctx.spillsets.len() {
100 trace!("allocate spillslot: {}", spillset);
101 let spillset = SpillSetIndex::new(spillset);
102 if !self.ctx.spillsets[spillset].required {
103 continue;
104 }
105 let class = self.ctx.spillsets[spillset].class as usize;
106 let mut i = self.ctx.slots_by_class[class].probe_start;
108 let mut success = false;
109 for _attempt in
112 0..core::cmp::min(self.ctx.slots_by_class[class].slots.len(), MAX_ATTEMPTS)
113 {
114 let spillslot = self.ctx.slots_by_class[class].slots[i];
122
123 if self.spillslot_can_fit_spillset(spillslot, spillset) {
124 self.allocate_spillset_to_spillslot(spillset, spillslot);
125 success = true;
126 self.ctx.slots_by_class[class].probe_start = i;
127 break;
128 }
129
130 i = self.ctx.slots_by_class[class].next_index(i);
131 }
132
133 if !success {
134 let spillslot = SpillSlotIndex::new(self.ctx.spillslots.len());
136 self.ctx.spillslots.push(SpillSlotData {
137 ranges: self.ctx.scratch_spillset_pool.pop().unwrap_or_default(),
138 alloc: Allocation::none(),
139 slots: self.func.spillslot_size(self.ctx.spillsets[spillset].class) as u32,
140 });
141 self.ctx.slots_by_class[class].slots.push(spillslot);
142 self.ctx.slots_by_class[class].probe_start =
143 self.ctx.slots_by_class[class].slots.len() - 1;
144
145 self.allocate_spillset_to_spillslot(spillset, spillslot);
146 }
147 }
148
149 for i in 0..self.ctx.spillslots.len() {
151 self.ctx.spillslots[i].alloc = self.allocate_spillslot(self.ctx.spillslots[i].slots);
152 }
153
154 trace!("spillslot allocator done");
155 }
156
157 pub fn allocate_spillslot(&mut self, size: u32) -> Allocation {
158 let mut offset = self.ctx.output.num_spillslots as u32;
159 debug_assert!(size.is_power_of_two());
161 offset = (offset + size - 1) & !(size - 1);
162 let slot = if self.func.multi_spillslot_named_by_last_slot() {
163 offset + size - 1
164 } else {
165 offset
166 };
167 offset += size;
168 self.ctx.output.num_spillslots = offset as _;
169 Allocation::stack(SpillSlot::new(slot as usize))
170 }
171}