1use super::{
16 Env, LiveBundleIndex, LiveRangeIndex, SpillSet, SpillSetIndex, SpillSlotIndex, VRegIndex,
17};
18use crate::{
19 ion::data_structures::BlockparamOut, Function, Inst, OperandConstraint, OperandKind, PReg,
20};
21use smallvec::smallvec;
22
23impl<'a, F: Function> Env<'a, F> {
24 pub fn merge_bundles(&mut self, from: LiveBundleIndex, to: LiveBundleIndex) -> bool {
25 if from == to {
26 return true;
28 }
29 trace!(
30 "merging from bundle{} to bundle{}",
31 from.index(),
32 to.index()
33 );
34
35 let from_rc = self.spillsets[self.bundles[from.index()].spillset.index()].class;
37 let to_rc = self.spillsets[self.bundles[to.index()].spillset.index()].class;
38 if from_rc != to_rc {
39 trace!(" -> mismatching reg classes");
40 return false;
41 }
42
43 if self.bundles[from.index()].allocation.is_some()
45 || self.bundles[to.index()].allocation.is_some()
46 {
47 trace!("one of the bundles is already assigned (pinned)");
48 return false;
49 }
50
51 #[cfg(debug_assertions)]
52 {
53 for entry in &self.bundles[from.index()].ranges {
55 let vreg = self.ranges[entry.index.index()].vreg;
56 debug_assert_eq!(from_rc, self.vreg(vreg).class());
57 }
58 for entry in &self.bundles[to.index()].ranges {
59 let vreg = self.ranges[entry.index.index()].vreg;
60 debug_assert_eq!(to_rc, self.vreg(vreg).class());
61 }
62 }
63
64 let ranges_from = &self.bundles[from.index()].ranges[..];
67 let ranges_to = &self.bundles[to.index()].ranges[..];
68 let mut idx_from = 0;
69 let mut idx_to = 0;
70 let mut range_count = 0;
71 while idx_from < ranges_from.len() && idx_to < ranges_to.len() {
72 range_count += 1;
73 if range_count > 200 {
74 trace!(
75 "reached merge complexity (range_count = {}); exiting",
76 range_count
77 );
78 return false;
80 }
81
82 if ranges_from[idx_from].range.from >= ranges_to[idx_to].range.to {
83 idx_to += 1;
84 } else if ranges_to[idx_to].range.from >= ranges_from[idx_from].range.to {
85 idx_from += 1;
86 } else {
87 trace!(
89 " -> overlap between {:?} and {:?}, exiting",
90 ranges_from[idx_from].index,
91 ranges_to[idx_to].index
92 );
93 return false;
94 }
95 }
96
97 if self.bundles[from.index()].cached_fixed_def()
102 || self.bundles[to.index()].cached_fixed_def()
103 {
104 trace!(" -> one bundle has a fixed def; aborting merge");
105 return false;
106 }
107
108 if self.bundles[from.index()].cached_stack()
110 || self.bundles[from.index()].cached_fixed()
111 || self.bundles[to.index()].cached_stack()
112 || self.bundles[to.index()].cached_fixed()
113 {
114 if self.merge_bundle_requirements(from, to).is_err() {
115 trace!(" -> conflicting requirements; aborting merge");
116 return false;
117 }
118 }
119
120 trace!(" -> committing to merge");
121
122 if ranges_from.is_empty() {
127 trace!(" -> from bundle{} is empty; trivial merge", from.index());
129 return true;
130 }
131 if ranges_to.is_empty() {
132 trace!(" -> to bundle{} is empty; trivial merge", to.index());
135 let list = std::mem::replace(&mut self.bundles[from.index()].ranges, smallvec![]);
136 for entry in &list {
137 self.ranges[entry.index.index()].bundle = to;
138
139 if self.annotations_enabled {
140 self.annotate(
141 entry.range.from,
142 format!(
143 " MERGE range{} v{} from bundle{} to bundle{}",
144 entry.index.index(),
145 self.ranges[entry.index.index()].vreg.index(),
146 from.index(),
147 to.index(),
148 ),
149 );
150 }
151 }
152 self.bundles[to.index()].ranges = list;
153
154 if self.bundles[from.index()].cached_stack() {
155 self.bundles[to.index()].set_cached_stack();
156 }
157 if self.bundles[from.index()].cached_fixed() {
158 self.bundles[to.index()].set_cached_fixed();
159 }
160
161 return true;
162 }
163
164 trace!(
165 "merging: ranges_from = {:?} ranges_to = {:?}",
166 ranges_from,
167 ranges_to
168 );
169
170 let from_list = std::mem::replace(&mut self.bundles[from.index()].ranges, smallvec![]);
174 for entry in &from_list {
175 self.ranges[entry.index.index()].bundle = to;
176 }
177 self.bundles[to.index()]
178 .ranges
179 .extend_from_slice(&from_list[..]);
180 self.bundles[to.index()]
181 .ranges
182 .sort_unstable_by_key(|entry| entry.range.from);
183
184 if self.annotations_enabled {
185 trace!("merging: merged = {:?}", self.bundles[to.index()].ranges);
186 let mut last_range = None;
187 for i in 0..self.bundles[to.index()].ranges.len() {
188 let entry = self.bundles[to.index()].ranges[i];
189 if last_range.is_some() {
190 debug_assert!(last_range.unwrap() < entry.range);
191 }
192 last_range = Some(entry.range);
193
194 if self.ranges[entry.index.index()].bundle == from {
195 self.annotate(
196 entry.range.from,
197 format!(
198 " MERGE range{} v{} from bundle{} to bundle{}",
199 entry.index.index(),
200 self.ranges[entry.index.index()].vreg.index(),
201 from.index(),
202 to.index(),
203 ),
204 );
205 }
206
207 trace!(
208 " -> merged result for bundle{}: range{}",
209 to.index(),
210 entry.index.index(),
211 );
212 }
213 }
214
215 if self.bundles[from.index()].spillset != self.bundles[to.index()].spillset {
216 let from_vregs = std::mem::replace(
217 &mut self.spillsets[self.bundles[from.index()].spillset.index()].vregs,
218 smallvec![],
219 );
220 let to_vregs = &mut self.spillsets[self.bundles[to.index()].spillset.index()].vregs;
221 for vreg in from_vregs {
222 if !to_vregs.contains(&vreg) {
223 to_vregs.push(vreg);
224 }
225 }
226 }
227
228 if self.bundles[from.index()].cached_stack() {
229 self.bundles[to.index()].set_cached_stack();
230 }
231 if self.bundles[from.index()].cached_fixed() {
232 self.bundles[to.index()].set_cached_fixed();
233 }
234
235 true
236 }
237
238 pub fn merge_vreg_bundles(&mut self) {
239 trace!("merge_vreg_bundles: creating vreg bundles");
241 for vreg in 0..self.vregs.len() {
242 let vreg = VRegIndex::new(vreg);
243 if self.vregs[vreg.index()].ranges.is_empty() {
244 continue;
245 }
246
247 let bundle = self.create_bundle();
248 self.bundles[bundle.index()].ranges = self.vregs[vreg.index()].ranges.clone();
249 trace!("vreg v{} gets bundle{}", vreg.index(), bundle.index());
250 for entry in &self.bundles[bundle.index()].ranges {
251 trace!(
252 " -> with LR range{}: {:?}",
253 entry.index.index(),
254 entry.range
255 );
256 self.ranges[entry.index.index()].bundle = bundle;
257 }
258
259 let mut fixed = false;
260 let mut fixed_def = false;
261 let mut stack = false;
262 for entry in &self.bundles[bundle.index()].ranges {
263 for u in &self.ranges[entry.index.index()].uses {
264 if let OperandConstraint::FixedReg(_) = u.operand.constraint() {
265 fixed = true;
266 if u.operand.kind() == OperandKind::Def {
267 fixed_def = true;
268 }
269 }
270 if let OperandConstraint::Stack = u.operand.constraint() {
271 stack = true;
272 }
273 if fixed && stack && fixed_def {
274 break;
275 }
276 }
277 }
278 if fixed {
279 self.bundles[bundle.index()].set_cached_fixed();
280 }
281 if fixed_def {
282 self.bundles[bundle.index()].set_cached_fixed_def();
283 }
284 if stack {
285 self.bundles[bundle.index()].set_cached_stack();
286 }
287
288 let ssidx = SpillSetIndex::new(self.spillsets.len());
290 let reg = self.vreg(vreg);
291 let size = self.func.spillslot_size(reg.class()) as u8;
292 self.spillsets.push(SpillSet {
293 vregs: smallvec![vreg],
294 slot: SpillSlotIndex::invalid(),
295 size,
296 required: false,
297 class: reg.class(),
298 reg_hint: PReg::invalid(),
299 spill_bundle: LiveBundleIndex::invalid(),
300 splits: 0,
301 });
302 self.bundles[bundle.index()].spillset = ssidx;
303 }
304
305 for inst in 0..self.func.num_insts() {
306 let inst = Inst::new(inst);
307
308 for op in self.func.inst_operands(inst) {
311 if let OperandConstraint::Reuse(reuse_idx) = op.constraint() {
312 let src_vreg = op.vreg();
313 let dst_vreg = self.func.inst_operands(inst)[reuse_idx].vreg();
314
315 trace!(
316 "trying to merge reused-input def: src {} to dst {}",
317 src_vreg,
318 dst_vreg
319 );
320 let src_bundle =
321 self.ranges[self.vregs[src_vreg.vreg()].ranges[0].index.index()].bundle;
322 debug_assert!(src_bundle.is_valid());
323 let dest_bundle =
324 self.ranges[self.vregs[dst_vreg.vreg()].ranges[0].index.index()].bundle;
325 debug_assert!(dest_bundle.is_valid());
326 self.merge_bundles(dest_bundle, src_bundle);
327 }
328 }
329 }
330
331 for i in 0..self.blockparam_outs.len() {
333 let BlockparamOut {
334 from_vreg, to_vreg, ..
335 } = self.blockparam_outs[i];
336 trace!(
337 "trying to merge blockparam v{} with input v{}",
338 to_vreg.index(),
339 from_vreg.index()
340 );
341 let to_bundle = self.ranges[self.vregs[to_vreg.index()].ranges[0].index.index()].bundle;
342 debug_assert!(to_bundle.is_valid());
343 let from_bundle =
344 self.ranges[self.vregs[from_vreg.index()].ranges[0].index.index()].bundle;
345 debug_assert!(from_bundle.is_valid());
346 trace!(
347 " -> from bundle{} to bundle{}",
348 from_bundle.index(),
349 to_bundle.index()
350 );
351 self.merge_bundles(from_bundle, to_bundle);
352 }
353
354 for i in 0..self.prog_move_merges.len() {
356 let (src, dst) = self.prog_move_merges[i];
357 trace!("trying to merge move src LR {:?} to dst LR {:?}", src, dst);
358 let src = self.resolve_merged_lr(src);
359 let dst = self.resolve_merged_lr(dst);
360 trace!(
361 "resolved LR-construction merging chains: move-merge is now src LR {:?} to dst LR {:?}",
362 src,
363 dst
364 );
365
366 let src_bundle = self.ranges[src.index()].bundle;
367 debug_assert!(src_bundle.is_valid());
368 let dest_bundle = self.ranges[dst.index()].bundle;
369 debug_assert!(dest_bundle.is_valid());
370 self.stats.prog_move_merge_attempt += 1;
371 if self.merge_bundles(dest_bundle, src_bundle) {
372 self.stats.prog_move_merge_success += 1;
373 }
374 }
375
376 trace!("done merging bundles");
377 }
378
379 pub fn resolve_merged_lr(&self, mut lr: LiveRangeIndex) -> LiveRangeIndex {
380 let mut iter = 0;
381 while iter < 100 && self.ranges[lr.index()].merged_into.is_valid() {
382 lr = self.ranges[lr.index()].merged_into;
383 iter += 1;
384 }
385 lr
386 }
387
388 pub fn compute_bundle_prio(&self, bundle: LiveBundleIndex) -> u32 {
389 let mut total = 0;
392 for entry in &self.bundles[bundle.index()].ranges {
393 total += entry.range.len() as u32;
394 }
395 total
396 }
397
398 pub fn queue_bundles(&mut self) {
399 for bundle in 0..self.bundles.len() {
400 trace!("enqueueing bundle{}", bundle);
401 if self.bundles[bundle].ranges.is_empty() {
402 trace!(" -> no ranges; skipping");
403 continue;
404 }
405 let bundle = LiveBundleIndex::new(bundle);
406 let prio = self.compute_bundle_prio(bundle);
407 trace!(" -> prio {}", prio);
408 self.bundles[bundle.index()].prio = prio;
409 self.recompute_bundle_properties(bundle);
410 self.allocation_queue
411 .insert(bundle, prio as usize, PReg::invalid());
412 }
413 self.stats.merged_bundle_count = self.allocation_queue.heap.len();
414 }
415}