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 let limit = self.bundles[bundle].limit.map(|l| l as usize);
46 for preg in RegTraversalIter::new(self.env, class, None, hint, bundle.index(), limit) {
47 trace!("trying bundle {:?} to preg {:?}", bundle, preg);
48 let preg_idx = PRegIndex::new(preg.index());
49 if let AllocRegResult::Allocated(_) =
50 self.try_to_allocate_bundle_to_reg(bundle, preg_idx, None, &mut scratch)
51 {
52 self.ctx.output.stats.spill_bundle_reg_success += 1;
53 success = true;
54 break;
55 }
56 }
57
58 if !success {
59 trace!(
60 "spilling bundle {:?}: marking spillset {:?} as required",
61 bundle,
62 self.ctx.bundles[bundle].spillset
63 );
64 self.ctx.spillsets[self.ctx.bundles[bundle].spillset].required = true;
65 }
66 }
67 self.ctx.scratch_conflicts = scratch;
68 }
69
70 pub fn spillslot_can_fit_spillset(
71 &mut self,
72 spillslot: SpillSlotIndex,
73 spillset: SpillSetIndex,
74 ) -> bool {
75 !self.ctx.spillslots[spillslot.index()]
76 .ranges
77 .btree
78 .contains_key(&LiveRangeKey::from_range(
79 &self.ctx.spillsets[spillset].range,
80 ))
81 }
82
83 pub fn allocate_spillset_to_spillslot(
84 &mut self,
85 spillset: SpillSetIndex,
86 spillslot: SpillSlotIndex,
87 ) {
88 self.ctx.spillsets[spillset].slot = spillslot;
89
90 let res = self.ctx.spillslots[spillslot.index()].ranges.btree.insert(
91 LiveRangeKey::from_range(&self.ctx.spillsets[spillset].range),
92 spillset,
93 );
94
95 debug_assert!(res.is_none());
96 }
97
98 pub fn allocate_spillslots(&mut self) {
99 const MAX_ATTEMPTS: usize = 10;
100
101 for spillset in 0..self.ctx.spillsets.len() {
102 trace!("allocate spillslot: {}", spillset);
103 let spillset = SpillSetIndex::new(spillset);
104 if !self.ctx.spillsets[spillset].required {
105 continue;
106 }
107 let class = self.ctx.spillsets[spillset].class as usize;
108 let mut i = self.ctx.slots_by_class[class].probe_start;
110 let mut success = false;
111 for _attempt in
114 0..core::cmp::min(self.ctx.slots_by_class[class].slots.len(), MAX_ATTEMPTS)
115 {
116 let spillslot = self.ctx.slots_by_class[class].slots[i];
124
125 if self.spillslot_can_fit_spillset(spillslot, spillset) {
126 self.allocate_spillset_to_spillslot(spillset, spillslot);
127 success = true;
128 self.ctx.slots_by_class[class].probe_start = i;
129 break;
130 }
131
132 i = self.ctx.slots_by_class[class].next_index(i);
133 }
134
135 if !success {
136 let spillslot = SpillSlotIndex::new(self.ctx.spillslots.len());
138 self.ctx.spillslots.push(SpillSlotData {
139 ranges: self.ctx.scratch_spillset_pool.pop().unwrap_or_default(),
140 alloc: Allocation::none(),
141 slots: self.func.spillslot_size(self.ctx.spillsets[spillset].class) as u32,
142 });
143 self.ctx.slots_by_class[class].slots.push(spillslot);
144 self.ctx.slots_by_class[class].probe_start =
145 self.ctx.slots_by_class[class].slots.len() - 1;
146
147 self.allocate_spillset_to_spillslot(spillset, spillslot);
148 }
149 }
150
151 for i in 0..self.ctx.spillslots.len() {
153 self.ctx.spillslots[i].alloc = self.allocate_spillslot(self.ctx.spillslots[i].slots);
154 }
155
156 trace!("spillslot allocator done");
157 }
158
159 pub fn allocate_spillslot(&mut self, size: u32) -> Allocation {
160 let mut offset = self.ctx.output.num_spillslots as u32;
161 debug_assert!(size.is_power_of_two());
163 offset = (offset + size - 1) & !(size - 1);
164 let slot = if self.func.multi_spillslot_named_by_last_slot() {
165 offset + size - 1
166 } else {
167 offset
168 };
169 offset += size;
170 self.ctx.output.num_spillslots = offset as _;
171 Allocation::stack(SpillSlot::new(slot as usize))
172 }
173}