1use crate::ion::data_structures::{
16 BlockparamOut, CodeRange, Env, LiveBundleIndex, LiveRangeList, SpillSet, SpillSlotIndex,
17 VRegIndex,
18};
19use crate::{Function, Inst, OperandConstraint, OperandKind, PReg, ProgPoint};
20use alloc::format;
21use core::convert::TryFrom;
22
23impl<'a, F: Function> Env<'a, F> {
24 fn merge_bundle_properties(&mut self, from: LiveBundleIndex, to: LiveBundleIndex) {
25 if self.bundles[from].cached_fixed() {
26 self.bundles[to].set_cached_fixed();
27 }
28 if self.bundles[from].cached_fixed_def() {
29 self.bundles[to].set_cached_fixed_def();
30 }
31 if self.bundles[from].cached_stack() {
32 self.bundles[to].set_cached_stack();
33 }
34 if let Some(theirs) = self.bundles[from].limit {
35 match self.bundles[to].limit {
36 Some(ours) => self.bundles[to].limit = Some(ours.min(theirs)),
37 None => self.bundles[to].limit = Some(theirs),
38 }
39 }
40 }
41
42 pub fn merge_bundles(&mut self, from: LiveBundleIndex, to: LiveBundleIndex) -> bool {
43 if from == to {
44 return true;
46 }
47 trace!(
48 "merging from bundle{} to bundle{}",
49 from.index(),
50 to.index()
51 );
52
53 let from_rc = self.spillsets[self.bundles[from].spillset].class;
55 let to_rc = self.spillsets[self.bundles[to].spillset].class;
56 if from_rc != to_rc {
57 trace!(" -> mismatching reg classes");
58 return false;
59 }
60
61 if self.bundles[from].allocation.is_some() || self.bundles[to].allocation.is_some() {
63 trace!("one of the bundles is already assigned (pinned)");
64 return false;
65 }
66
67 #[cfg(debug_assertions)]
68 {
69 for entry in &self.bundles[from].ranges {
71 let vreg = self.ranges[entry.index].vreg;
72 debug_assert_eq!(from_rc, self.vreg(vreg).class());
73 }
74 for entry in &self.bundles[to].ranges {
75 let vreg = self.ranges[entry.index].vreg;
76 debug_assert_eq!(to_rc, self.vreg(vreg).class());
77 }
78 }
79
80 let adjust_range_start = |bundle_idx, range: CodeRange| {
88 if self.bundles[bundle_idx].cached_fixed_def() {
89 ProgPoint::before(range.from.inst())
90 } else {
91 range.from
92 }
93 };
94
95 let ranges_from = &self.bundles[from].ranges[..];
98 let ranges_to = &self.bundles[to].ranges[..];
99 let mut idx_from = 0;
100 let mut idx_to = 0;
101 let mut range_count = 0;
102 while idx_from < ranges_from.len() && idx_to < ranges_to.len() {
103 range_count += 1;
104 if range_count > 200 {
105 trace!(
106 "reached merge complexity (range_count = {}); exiting",
107 range_count
108 );
109 return false;
111 }
112
113 if adjust_range_start(from, ranges_from[idx_from].range) >= ranges_to[idx_to].range.to {
114 idx_to += 1;
115 } else if adjust_range_start(to, ranges_to[idx_to].range)
116 >= ranges_from[idx_from].range.to
117 {
118 idx_from += 1;
119 } else {
120 trace!(
122 " -> overlap between {:?} and {:?}, exiting",
123 ranges_from[idx_from].index,
124 ranges_to[idx_to].index
125 );
126 return false;
127 }
128 }
129
130 if self.bundles[from].cached_stack()
132 || self.bundles[from].cached_fixed()
133 || self.bundles[from].limit.is_some()
134 || self.bundles[to].cached_stack()
135 || self.bundles[to].cached_fixed()
136 || self.bundles[to].limit.is_some()
137 {
138 if self.merge_bundle_requirements(from, to).is_err() {
139 trace!(" -> conflicting requirements; aborting merge");
140 return false;
141 }
142 }
143
144 trace!(" -> committing to merge");
145
146 if ranges_from.is_empty() {
151 trace!(" -> from bundle{} is empty; trivial merge", from.index());
153 return true;
154 }
155 if ranges_to.is_empty() {
156 trace!(" -> to bundle{} is empty; trivial merge", to.index());
159 let empty_vec = LiveRangeList::new_in(self.ctx.bump());
160 let list = core::mem::replace(&mut self.bundles[from].ranges, empty_vec);
161 for entry in &list {
162 self.ranges[entry.index].bundle = to;
163
164 if self.annotations_enabled {
165 self.annotate(
166 entry.range.from,
167 format!(
168 " MERGE range{} v{} from bundle{} to bundle{}",
169 entry.index.index(),
170 self.ranges[entry.index].vreg.index(),
171 from.index(),
172 to.index(),
173 ),
174 );
175 }
176 }
177 self.bundles[to].ranges = list;
178 self.merge_bundle_properties(from, to);
179 return true;
180 }
181
182 trace!(
183 "merging: ranges_from = {:?} ranges_to = {:?}",
184 ranges_from,
185 ranges_to
186 );
187
188 let empty_vec = LiveRangeList::new_in(self.ctx.bump());
189 let mut from_list = core::mem::replace(&mut self.bundles[from].ranges, empty_vec);
190 for entry in &from_list {
191 self.ranges[entry.index].bundle = to;
192 }
193
194 if from_list.len() == 1 {
195 let single_entry = from_list.pop().unwrap();
202 let pos = self.bundles[to]
203 .ranges
204 .binary_search_by_key(&single_entry.range.from, |entry| entry.range.from)
205 .unwrap_or_else(|pos| pos);
206 self.bundles[to].ranges.insert(pos, single_entry);
207 } else {
208 self.bundles[to].ranges.extend_from_slice(&from_list[..]);
211 self.bundles[to]
212 .ranges
213 .sort_unstable_by_key(|entry| entry.range.from);
214 }
215
216 if self.annotations_enabled {
217 trace!("merging: merged = {:?}", self.bundles[to].ranges);
218 let mut last_range = None;
219 for i in 0..self.bundles[to].ranges.len() {
220 let entry = self.bundles[to].ranges[i];
221 if last_range.is_some() {
222 debug_assert!(last_range.unwrap() < entry.range);
223 }
224 last_range = Some(entry.range);
225
226 if self.ranges[entry.index].bundle == from {
227 self.annotate(
228 entry.range.from,
229 format!(
230 " MERGE range{} v{} from bundle{} to bundle{}",
231 entry.index.index(),
232 self.ranges[entry.index].vreg.index(),
233 from.index(),
234 to.index(),
235 ),
236 );
237 }
238
239 trace!(
240 " -> merged result for bundle{}: range{}",
241 to.index(),
242 entry.index.index(),
243 );
244 }
245 }
246
247 if self.bundles[from].spillset != self.bundles[to].spillset {
248 let from_range = self.spillsets[self.bundles[from].spillset].range;
250 let to_range = &mut self.ctx.spillsets[self.ctx.bundles[to].spillset].range;
251 *to_range = to_range.join(from_range);
252 }
253
254 self.merge_bundle_properties(from, to);
255
256 true
257 }
258
259 pub fn merge_vreg_bundles(&mut self) {
260 trace!("merge_vreg_bundles: creating vreg bundles");
262 for vreg in 0..self.vregs.len() {
263 let vreg = VRegIndex::new(vreg);
264 if self.vregs[vreg].ranges.is_empty() {
265 continue;
266 }
267
268 let bundle = self.ctx.bundles.add(self.ctx.bump());
269 let mut range = self.vregs[vreg].ranges.first().unwrap().range;
270
271 self.bundles[bundle].ranges = self.vregs[vreg].ranges.clone();
272 trace!("vreg v{} gets bundle{}", vreg.index(), bundle.index());
273 for entry in &self.ctx.bundles[bundle].ranges {
274 trace!(
275 " -> with LR range{}: {:?}",
276 entry.index.index(),
277 entry.range
278 );
279 range = range.join(entry.range);
280 self.ctx.ranges[entry.index].bundle = bundle;
281 }
282
283 let mut fixed = false;
284 let mut fixed_def = false;
285 let mut stack = false;
286 let mut limit: Option<u8> = None;
287 for entry in &self.bundles[bundle].ranges {
288 for u in &self.ranges[entry.index].uses {
289 use OperandConstraint::*;
290 match u.operand.constraint() {
291 FixedReg(_) => {
292 fixed = true;
293 if u.operand.kind() == OperandKind::Def {
294 fixed_def = true;
295 }
296 }
297 Stack => stack = true,
298 Limit(current) => {
299 let current = u8::try_from(current)
300 .expect("the current limit is too large to fit in a u8");
301 match limit {
302 Some(prev) => limit = Some(prev.min(current)),
303 None => limit = Some(current),
304 }
305 }
306 Any | Reg | Reuse(_) => {
307 continue;
308 }
309 }
310 if fixed && stack && fixed_def {
311 break;
312 }
313 }
314 }
315 if fixed {
316 self.bundles[bundle].set_cached_fixed();
317 }
318 if fixed_def {
319 self.bundles[bundle].set_cached_fixed_def();
320 }
321 if stack {
322 self.bundles[bundle].set_cached_stack();
323 }
324 self.bundles[bundle].limit = limit;
325
326 let reg = self.vreg(vreg);
328 let ssidx = self.spillsets.push(SpillSet {
329 slot: SpillSlotIndex::invalid(),
330 required: false,
331 class: reg.class(),
332 hint: PReg::invalid(),
333 spill_bundle: LiveBundleIndex::invalid(),
334 splits: 0,
335 range,
336 });
337 self.bundles[bundle].spillset = ssidx;
338 }
339
340 for inst in 0..self.func.num_insts() {
341 let inst = Inst::new(inst);
342
343 for op in self.func.inst_operands(inst) {
346 if let OperandConstraint::Reuse(reuse_idx) = op.constraint() {
347 let src_vreg = op.vreg();
348 let dst_vreg = self.func.inst_operands(inst)[reuse_idx].vreg();
349
350 trace!(
351 "trying to merge reused-input def: src {} to dst {}",
352 src_vreg,
353 dst_vreg
354 );
355 let src_bundle = self.ranges[self.vregs[src_vreg].ranges[0].index].bundle;
356 debug_assert!(src_bundle.is_valid());
357 let dest_bundle = self.ranges[self.vregs[dst_vreg].ranges[0].index].bundle;
358 debug_assert!(dest_bundle.is_valid());
359 self.merge_bundles(dest_bundle, src_bundle);
360 }
361 }
362 }
363
364 for i in 0..self.blockparam_outs.len() {
366 let BlockparamOut {
367 from_vreg, to_vreg, ..
368 } = self.blockparam_outs[i];
369 trace!(
370 "trying to merge blockparam v{} with input v{}",
371 to_vreg.index(),
372 from_vreg.index()
373 );
374 let to_bundle = self.ranges[self.vregs[to_vreg].ranges[0].index].bundle;
375 debug_assert!(to_bundle.is_valid());
376 let from_bundle = self.ranges[self.vregs[from_vreg].ranges[0].index].bundle;
377 debug_assert!(from_bundle.is_valid());
378 trace!(
379 " -> from bundle{} to bundle{}",
380 from_bundle.index(),
381 to_bundle.index()
382 );
383 self.merge_bundles(from_bundle, to_bundle);
384 }
385
386 trace!("done merging bundles");
387 }
388
389 pub fn compute_bundle_prio(&self, bundle: LiveBundleIndex) -> u32 {
390 let mut total = 0;
393 for entry in &self.bundles[bundle].ranges {
394 total += entry.range.len() as u32;
395 }
396 trace!(" -> prio {total}");
397 total
398 }
399
400 pub fn compute_bundle_limit(&self, bundle: LiveBundleIndex) -> Option<u8> {
401 let mut limit: Option<u8> = None;
402 for entry in &self.bundles[bundle].ranges {
403 for u in &self.ranges[entry.index].uses {
404 use OperandConstraint::*;
405 match u.operand.constraint() {
406 Limit(current) => {
407 let current = u8::try_from(current)
408 .expect("the current limit is too large to fit in a u8");
409 match limit {
410 Some(prev) => limit = Some(prev.min(current)),
411 None => limit = Some(current),
412 }
413 }
414 FixedReg(_) | Stack => {
415 break;
416 }
417 Any | Reg | Reuse(_) => {
418 continue;
419 }
420 }
421 }
422 }
423 limit
424 }
425
426 pub fn queue_bundles(&mut self) {
427 for bundle in 0..self.bundles.len() {
428 trace!("enqueueing bundle{}", bundle);
429 let bundle = LiveBundleIndex::new(bundle);
430 if self.bundles[bundle].ranges.is_empty() {
431 trace!(" -> no ranges; skipping");
432 continue;
433 }
434 self.recompute_bundle_properties(bundle);
435 let prio = self.bundles[bundle].prio as usize;
436 self.allocation_queue.insert(bundle, prio, PReg::invalid());
437 }
438 self.output.stats.merged_bundle_count = self.allocation_queue.heap.len();
439 }
440}