/rust/registry/src/index.crates.io-1949cf8c6b5b557f/regalloc2-0.13.5/src/ion/merge.rs
Line | Count | Source |
1 | | /* |
2 | | * This file was initially derived from the files |
3 | | * `js/src/jit/BacktrackingAllocator.h` and |
4 | | * `js/src/jit/BacktrackingAllocator.cpp` in Mozilla Firefox, and was |
5 | | * originally licensed under the Mozilla Public License 2.0. We |
6 | | * subsequently relicensed it to Apache-2.0 WITH LLVM-exception (see |
7 | | * https://github.com/bytecodealliance/regalloc2/issues/7). |
8 | | * |
9 | | * Since the initial port, the design has been substantially evolved |
10 | | * and optimized. |
11 | | */ |
12 | | |
13 | | //! Bundle merging. |
14 | | |
15 | | use crate::ion::data_structures::{ |
16 | | BlockparamOut, CodeRange, Env, LiveBundleIndex, LiveRangeList, SpillSet, SpillSlotIndex, |
17 | | VRegIndex, |
18 | | }; |
19 | | use crate::{Function, Inst, OperandConstraint, OperandKind, PReg, ProgPoint}; |
20 | | use alloc::format; |
21 | | use core::convert::TryFrom; |
22 | | |
23 | | impl<'a, F: Function> Env<'a, F> { |
24 | 958k | fn merge_bundle_properties(&mut self, from: LiveBundleIndex, to: LiveBundleIndex) { |
25 | 958k | if self.bundles[from].cached_fixed() { |
26 | 81.2k | self.bundles[to].set_cached_fixed(); |
27 | 877k | } |
28 | 958k | if self.bundles[from].cached_fixed_def() { |
29 | 78.7k | self.bundles[to].set_cached_fixed_def(); |
30 | 880k | } |
31 | 958k | if self.bundles[from].cached_stack() { |
32 | 0 | self.bundles[to].set_cached_stack(); |
33 | 958k | } |
34 | 958k | if let Some(theirs) = self.bundles[from].limit { |
35 | 0 | match self.bundles[to].limit { |
36 | 0 | Some(ours) => self.bundles[to].limit = Some(ours.min(theirs)), |
37 | 0 | None => self.bundles[to].limit = Some(theirs), |
38 | | } |
39 | 958k | } |
40 | 958k | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::merge_bundle_properties Line | Count | Source | 24 | 106k | fn merge_bundle_properties(&mut self, from: LiveBundleIndex, to: LiveBundleIndex) { | 25 | 106k | if self.bundles[from].cached_fixed() { | 26 | 9.58k | self.bundles[to].set_cached_fixed(); | 27 | 96.9k | } | 28 | 106k | if self.bundles[from].cached_fixed_def() { | 29 | 9.54k | self.bundles[to].set_cached_fixed_def(); | 30 | 97.0k | } | 31 | 106k | if self.bundles[from].cached_stack() { | 32 | 0 | self.bundles[to].set_cached_stack(); | 33 | 106k | } | 34 | 106k | if let Some(theirs) = self.bundles[from].limit { | 35 | 0 | match self.bundles[to].limit { | 36 | 0 | Some(ours) => self.bundles[to].limit = Some(ours.min(theirs)), | 37 | 0 | None => self.bundles[to].limit = Some(theirs), | 38 | | } | 39 | 106k | } | 40 | 106k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::merge_bundle_properties Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::merge_bundle_properties Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::merge_bundle_properties <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::merge_bundle_properties Line | Count | Source | 24 | 852k | fn merge_bundle_properties(&mut self, from: LiveBundleIndex, to: LiveBundleIndex) { | 25 | 852k | if self.bundles[from].cached_fixed() { | 26 | 71.6k | self.bundles[to].set_cached_fixed(); | 27 | 780k | } | 28 | 852k | if self.bundles[from].cached_fixed_def() { | 29 | 69.1k | self.bundles[to].set_cached_fixed_def(); | 30 | 783k | } | 31 | 852k | if self.bundles[from].cached_stack() { | 32 | 0 | self.bundles[to].set_cached_stack(); | 33 | 852k | } | 34 | 852k | if let Some(theirs) = self.bundles[from].limit { | 35 | 0 | match self.bundles[to].limit { | 36 | 0 | Some(ours) => self.bundles[to].limit = Some(ours.min(theirs)), | 37 | 0 | None => self.bundles[to].limit = Some(theirs), | 38 | | } | 39 | 852k | } | 40 | 852k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::merge_bundle_properties Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::merge_bundle_properties |
41 | | |
42 | 1.14M | pub fn merge_bundles(&mut self, from: LiveBundleIndex, to: LiveBundleIndex) -> bool { |
43 | 1.14M | if from == to { |
44 | | // Merge bundle into self -- trivial merge. |
45 | 800 | return true; |
46 | 1.13M | } |
47 | 1.13M | trace!( |
48 | | "merging from bundle{} to bundle{}", |
49 | 0 | from.index(), |
50 | 0 | to.index() |
51 | | ); |
52 | | |
53 | | // Both bundles must deal with the same RegClass. |
54 | 1.13M | let from_rc = self.spillsets[self.bundles[from].spillset].class; |
55 | 1.13M | let to_rc = self.spillsets[self.bundles[to].spillset].class; |
56 | 1.13M | if from_rc != to_rc { |
57 | 0 | trace!(" -> mismatching reg classes"); |
58 | 0 | return false; |
59 | 1.13M | } |
60 | | |
61 | | // If either bundle is already assigned (due to a pinned vreg), don't merge. |
62 | 1.13M | if self.bundles[from].allocation.is_some() || self.bundles[to].allocation.is_some() { |
63 | 0 | trace!("one of the bundles is already assigned (pinned)"); |
64 | 0 | return false; |
65 | 1.13M | } |
66 | | |
67 | | #[cfg(debug_assertions)] |
68 | | { |
69 | | // Sanity check: both bundles should contain only ranges with appropriate VReg classes. |
70 | | 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 | | // If a bundle has a fixed-reg def then we need to be careful to not |
81 | | // extend the bundle to include another use in the same instruction. |
82 | | // This could result in a minimal bundle that is impossible to split. |
83 | | // |
84 | | // This can only happen with an early use and a late def, so we round |
85 | | // the start of each range containing a fixed def up to the start of |
86 | | // its instruction to detect overlaps. |
87 | 3.36M | let adjust_range_start = |bundle_idx, range: CodeRange| { |
88 | 3.36M | if self.bundles[bundle_idx].cached_fixed_def() { |
89 | 141k | ProgPoint::before(range.from.inst()) |
90 | | } else { |
91 | 3.22M | range.from |
92 | | } |
93 | 3.36M | }; <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::merge_bundles::{closure#0}Line | Count | Source | 87 | 274k | let adjust_range_start = |bundle_idx, range: CodeRange| { | 88 | 274k | if self.bundles[bundle_idx].cached_fixed_def() { | 89 | 10.6k | ProgPoint::before(range.from.inst()) | 90 | | } else { | 91 | 264k | range.from | 92 | | } | 93 | 274k | }; |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::merge_bundles::{closure#0}Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::merge_bundles::{closure#0}Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::merge_bundles::{closure#0}<regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::merge_bundles::{closure#0}Line | Count | Source | 87 | 3.09M | let adjust_range_start = |bundle_idx, range: CodeRange| { | 88 | 3.09M | if self.bundles[bundle_idx].cached_fixed_def() { | 89 | 130k | ProgPoint::before(range.from.inst()) | 90 | | } else { | 91 | 2.96M | range.from | 92 | | } | 93 | 3.09M | }; |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::merge_bundles::{closure#0}Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::merge_bundles::{closure#0} |
94 | | |
95 | | // Check for overlap in LiveRanges and for conflicting |
96 | | // requirements. |
97 | 1.13M | let ranges_from = &self.bundles[from].ranges[..]; |
98 | 1.13M | let ranges_to = &self.bundles[to].ranges[..]; |
99 | 1.13M | let mut idx_from = 0; |
100 | 1.13M | let mut idx_to = 0; |
101 | 1.13M | let mut range_count = 0; |
102 | 2.70M | while idx_from < ranges_from.len() && idx_to < ranges_to.len() { |
103 | 1.74M | range_count += 1; |
104 | 1.74M | if range_count > 200 { |
105 | 113 | trace!( |
106 | | "reached merge complexity (range_count = {}); exiting", |
107 | | range_count |
108 | | ); |
109 | | // Limit merge complexity. |
110 | 113 | return false; |
111 | 1.74M | } |
112 | | |
113 | 1.74M | if adjust_range_start(from, ranges_from[idx_from].range) >= ranges_to[idx_to].range.to { |
114 | 112k | idx_to += 1; |
115 | 1.62M | } else if adjust_range_start(to, ranges_to[idx_to].range) |
116 | 1.62M | >= ranges_from[idx_from].range.to |
117 | 1.45M | { |
118 | 1.45M | idx_from += 1; |
119 | 1.45M | } else { |
120 | | // Overlap -- cannot merge. |
121 | 175k | trace!( |
122 | | " -> overlap between {:?} and {:?}, exiting", |
123 | 0 | ranges_from[idx_from].index, |
124 | 0 | ranges_to[idx_to].index |
125 | | ); |
126 | 175k | return false; |
127 | | } |
128 | | } |
129 | | |
130 | | // Check for a requirements conflict. |
131 | 963k | if self.bundles[from].cached_stack() |
132 | 963k | || self.bundles[from].cached_fixed() |
133 | 877k | || self.bundles[from].limit.is_some() |
134 | 877k | || self.bundles[to].cached_stack() |
135 | 877k | || self.bundles[to].cached_fixed() |
136 | 693k | || self.bundles[to].limit.is_some() |
137 | | { |
138 | 270k | if self.merge_bundle_requirements(from, to).is_err() { |
139 | 4.52k | trace!(" -> conflicting requirements; aborting merge"); |
140 | 4.52k | return false; |
141 | 265k | } |
142 | 693k | } |
143 | | |
144 | 958k | trace!(" -> committing to merge"); |
145 | | |
146 | | // If we reach here, then the bundles do not overlap -- merge |
147 | | // them! We do this with a merge-sort-like scan over both |
148 | | // lists, building a new range list and replacing the list on |
149 | | // `to` when we're done. |
150 | 958k | if ranges_from.is_empty() { |
151 | | // `from` bundle is empty -- trivial merge. |
152 | 0 | trace!(" -> from bundle{} is empty; trivial merge", from.index()); |
153 | 0 | return true; |
154 | 958k | } |
155 | 958k | if ranges_to.is_empty() { |
156 | | // `to` bundle is empty -- just move the list over from |
157 | | // `from` and set `bundle` up-link on all ranges. |
158 | 0 | trace!(" -> to bundle{} is empty; trivial merge", to.index()); |
159 | 0 | let empty_vec = LiveRangeList::new_in(self.ctx.bump()); |
160 | 0 | let list = core::mem::replace(&mut self.bundles[from].ranges, empty_vec); |
161 | 0 | for entry in &list { |
162 | 0 | self.ranges[entry.index].bundle = to; |
163 | | |
164 | 0 | if self.annotations_enabled { |
165 | 0 | self.annotate( |
166 | 0 | entry.range.from, |
167 | 0 | format!( |
168 | 0 | " MERGE range{} v{} from bundle{} to bundle{}", |
169 | 0 | entry.index.index(), |
170 | 0 | self.ranges[entry.index].vreg.index(), |
171 | 0 | from.index(), |
172 | 0 | to.index(), |
173 | 0 | ), |
174 | 0 | ); |
175 | 0 | } |
176 | | } |
177 | 0 | self.bundles[to].ranges = list; |
178 | 0 | self.merge_bundle_properties(from, to); |
179 | 0 | return true; |
180 | 958k | } |
181 | | |
182 | 958k | trace!( |
183 | | "merging: ranges_from = {:?} ranges_to = {:?}", |
184 | | ranges_from, |
185 | | ranges_to |
186 | | ); |
187 | | |
188 | 958k | let empty_vec = LiveRangeList::new_in(self.ctx.bump()); |
189 | 958k | let mut from_list = core::mem::replace(&mut self.bundles[from].ranges, empty_vec); |
190 | 1.33M | for entry in &from_list { |
191 | 1.33M | self.ranges[entry.index].bundle = to; |
192 | 1.33M | } |
193 | | |
194 | 958k | if from_list.len() == 1 { |
195 | | // Optimize for the common case where `from_list` contains a single |
196 | | // item. Using a binary search to find the insertion point and then |
197 | | // calling `insert` is more efficient than re-sorting the entire |
198 | | // list, specially after the changes in sorting algorithms introduced |
199 | | // in rustc 1.81. |
200 | | // See: https://github.com/bytecodealliance/regalloc2/issues/203 |
201 | 766k | let single_entry = from_list.pop().unwrap(); |
202 | 766k | let pos = self.bundles[to] |
203 | 766k | .ranges |
204 | 766k | .binary_search_by_key(&single_entry.range.from, |entry| entry.range.from) |
205 | 766k | .unwrap_or_else(|pos| pos); |
206 | 766k | self.bundles[to].ranges.insert(pos, single_entry); |
207 | | } else { |
208 | | // Two non-empty lists of LiveRanges: concatenate and sort. This is |
209 | | // faster than a mergesort-like merge into a new list, empirically. |
210 | 192k | self.bundles[to].ranges.extend_from_slice(&from_list[..]); |
211 | 192k | self.bundles[to] |
212 | 192k | .ranges |
213 | 192k | .sort_unstable_by_key(|entry| entry.range.from); |
214 | | } |
215 | | |
216 | 958k | if self.annotations_enabled { |
217 | 0 | trace!("merging: merged = {:?}", self.bundles[to].ranges); |
218 | 0 | let mut last_range = None; |
219 | 0 | for i in 0..self.bundles[to].ranges.len() { |
220 | 0 | let entry = self.bundles[to].ranges[i]; |
221 | 0 | if last_range.is_some() { |
222 | 0 | debug_assert!(last_range.unwrap() < entry.range); |
223 | 0 | } |
224 | 0 | last_range = Some(entry.range); |
225 | | |
226 | 0 | if self.ranges[entry.index].bundle == from { |
227 | 0 | self.annotate( |
228 | 0 | entry.range.from, |
229 | 0 | format!( |
230 | 0 | " MERGE range{} v{} from bundle{} to bundle{}", |
231 | 0 | entry.index.index(), |
232 | 0 | self.ranges[entry.index].vreg.index(), |
233 | 0 | from.index(), |
234 | 0 | to.index(), |
235 | 0 | ), |
236 | 0 | ); |
237 | 0 | } |
238 | | |
239 | 0 | trace!( |
240 | | " -> merged result for bundle{}: range{}", |
241 | 0 | to.index(), |
242 | 0 | entry.index.index(), |
243 | | ); |
244 | | } |
245 | 958k | } |
246 | | |
247 | 958k | if self.bundles[from].spillset != self.bundles[to].spillset { |
248 | 958k | // Widen the range for the target spillset to include the one being merged in. |
249 | 958k | let from_range = self.spillsets[self.bundles[from].spillset].range; |
250 | 958k | let to_range = &mut self.ctx.spillsets[self.ctx.bundles[to].spillset].range; |
251 | 958k | *to_range = to_range.join(from_range); |
252 | 958k | } |
253 | | |
254 | 958k | self.merge_bundle_properties(from, to); |
255 | | |
256 | 958k | true |
257 | 1.14M | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::merge_bundles Line | Count | Source | 42 | 113k | pub fn merge_bundles(&mut self, from: LiveBundleIndex, to: LiveBundleIndex) -> bool { | 43 | 113k | if from == to { | 44 | | // Merge bundle into self -- trivial merge. | 45 | 8 | return true; | 46 | 113k | } | 47 | 113k | trace!( | 48 | | "merging from bundle{} to bundle{}", | 49 | 0 | from.index(), | 50 | 0 | to.index() | 51 | | ); | 52 | | | 53 | | // Both bundles must deal with the same RegClass. | 54 | 113k | let from_rc = self.spillsets[self.bundles[from].spillset].class; | 55 | 113k | let to_rc = self.spillsets[self.bundles[to].spillset].class; | 56 | 113k | if from_rc != to_rc { | 57 | 0 | trace!(" -> mismatching reg classes"); | 58 | 0 | return false; | 59 | 113k | } | 60 | | | 61 | | // If either bundle is already assigned (due to a pinned vreg), don't merge. | 62 | 113k | if self.bundles[from].allocation.is_some() || self.bundles[to].allocation.is_some() { | 63 | 0 | trace!("one of the bundles is already assigned (pinned)"); | 64 | 0 | return false; | 65 | 113k | } | 66 | | | 67 | | #[cfg(debug_assertions)] | 68 | | { | 69 | | // Sanity check: both bundles should contain only ranges with appropriate VReg classes. | 70 | | 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 | | // If a bundle has a fixed-reg def then we need to be careful to not | 81 | | // extend the bundle to include another use in the same instruction. | 82 | | // This could result in a minimal bundle that is impossible to split. | 83 | | // | 84 | | // This can only happen with an early use and a late def, so we round | 85 | | // the start of each range containing a fixed def up to the start of | 86 | | // its instruction to detect overlaps. | 87 | 113k | 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 | | // Check for overlap in LiveRanges and for conflicting | 96 | | // requirements. | 97 | 113k | let ranges_from = &self.bundles[from].ranges[..]; | 98 | 113k | let ranges_to = &self.bundles[to].ranges[..]; | 99 | 113k | let mut idx_from = 0; | 100 | 113k | let mut idx_to = 0; | 101 | 113k | let mut range_count = 0; | 102 | 248k | while idx_from < ranges_from.len() && idx_to < ranges_to.len() { | 103 | 141k | range_count += 1; | 104 | 141k | if range_count > 200 { | 105 | 0 | trace!( | 106 | | "reached merge complexity (range_count = {}); exiting", | 107 | | range_count | 108 | | ); | 109 | | // Limit merge complexity. | 110 | 0 | return false; | 111 | 141k | } | 112 | | | 113 | 141k | if adjust_range_start(from, ranges_from[idx_from].range) >= ranges_to[idx_to].range.to { | 114 | 8.65k | idx_to += 1; | 115 | 133k | } else if adjust_range_start(to, ranges_to[idx_to].range) | 116 | 133k | >= ranges_from[idx_from].range.to | 117 | 125k | { | 118 | 125k | idx_from += 1; | 119 | 125k | } else { | 120 | | // Overlap -- cannot merge. | 121 | 7.32k | trace!( | 122 | | " -> overlap between {:?} and {:?}, exiting", | 123 | 0 | ranges_from[idx_from].index, | 124 | 0 | ranges_to[idx_to].index | 125 | | ); | 126 | 7.32k | return false; | 127 | | } | 128 | | } | 129 | | | 130 | | // Check for a requirements conflict. | 131 | 106k | if self.bundles[from].cached_stack() | 132 | 106k | || self.bundles[from].cached_fixed() | 133 | 96.9k | || self.bundles[from].limit.is_some() | 134 | 96.9k | || self.bundles[to].cached_stack() | 135 | 96.9k | || self.bundles[to].cached_fixed() | 136 | 70.6k | || self.bundles[to].limit.is_some() | 137 | | { | 138 | 35.8k | if self.merge_bundle_requirements(from, to).is_err() { | 139 | 26 | trace!(" -> conflicting requirements; aborting merge"); | 140 | 26 | return false; | 141 | 35.8k | } | 142 | 70.6k | } | 143 | | | 144 | 106k | trace!(" -> committing to merge"); | 145 | | | 146 | | // If we reach here, then the bundles do not overlap -- merge | 147 | | // them! We do this with a merge-sort-like scan over both | 148 | | // lists, building a new range list and replacing the list on | 149 | | // `to` when we're done. | 150 | 106k | if ranges_from.is_empty() { | 151 | | // `from` bundle is empty -- trivial merge. | 152 | 0 | trace!(" -> from bundle{} is empty; trivial merge", from.index()); | 153 | 0 | return true; | 154 | 106k | } | 155 | 106k | if ranges_to.is_empty() { | 156 | | // `to` bundle is empty -- just move the list over from | 157 | | // `from` and set `bundle` up-link on all ranges. | 158 | 0 | trace!(" -> to bundle{} is empty; trivial merge", to.index()); | 159 | 0 | let empty_vec = LiveRangeList::new_in(self.ctx.bump()); | 160 | 0 | let list = core::mem::replace(&mut self.bundles[from].ranges, empty_vec); | 161 | 0 | for entry in &list { | 162 | 0 | self.ranges[entry.index].bundle = to; | 163 | | | 164 | 0 | if self.annotations_enabled { | 165 | 0 | self.annotate( | 166 | 0 | entry.range.from, | 167 | 0 | format!( | 168 | 0 | " MERGE range{} v{} from bundle{} to bundle{}", | 169 | 0 | entry.index.index(), | 170 | 0 | self.ranges[entry.index].vreg.index(), | 171 | 0 | from.index(), | 172 | 0 | to.index(), | 173 | 0 | ), | 174 | 0 | ); | 175 | 0 | } | 176 | | } | 177 | 0 | self.bundles[to].ranges = list; | 178 | 0 | self.merge_bundle_properties(from, to); | 179 | 0 | return true; | 180 | 106k | } | 181 | | | 182 | 106k | trace!( | 183 | | "merging: ranges_from = {:?} ranges_to = {:?}", | 184 | | ranges_from, | 185 | | ranges_to | 186 | | ); | 187 | | | 188 | 106k | let empty_vec = LiveRangeList::new_in(self.ctx.bump()); | 189 | 106k | let mut from_list = core::mem::replace(&mut self.bundles[from].ranges, empty_vec); | 190 | 125k | for entry in &from_list { | 191 | 125k | self.ranges[entry.index].bundle = to; | 192 | 125k | } | 193 | | | 194 | 106k | if from_list.len() == 1 { | 195 | | // Optimize for the common case where `from_list` contains a single | 196 | | // item. Using a binary search to find the insertion point and then | 197 | | // calling `insert` is more efficient than re-sorting the entire | 198 | | // list, specially after the changes in sorting algorithms introduced | 199 | | // in rustc 1.81. | 200 | | // See: https://github.com/bytecodealliance/regalloc2/issues/203 | 201 | 88.9k | let single_entry = from_list.pop().unwrap(); | 202 | 88.9k | let pos = self.bundles[to] | 203 | 88.9k | .ranges | 204 | 88.9k | .binary_search_by_key(&single_entry.range.from, |entry| entry.range.from) | 205 | 88.9k | .unwrap_or_else(|pos| pos); | 206 | 88.9k | self.bundles[to].ranges.insert(pos, single_entry); | 207 | | } else { | 208 | | // Two non-empty lists of LiveRanges: concatenate and sort. This is | 209 | | // faster than a mergesort-like merge into a new list, empirically. | 210 | 17.6k | self.bundles[to].ranges.extend_from_slice(&from_list[..]); | 211 | 17.6k | self.bundles[to] | 212 | 17.6k | .ranges | 213 | 17.6k | .sort_unstable_by_key(|entry| entry.range.from); | 214 | | } | 215 | | | 216 | 106k | if self.annotations_enabled { | 217 | 0 | trace!("merging: merged = {:?}", self.bundles[to].ranges); | 218 | 0 | let mut last_range = None; | 219 | 0 | for i in 0..self.bundles[to].ranges.len() { | 220 | 0 | let entry = self.bundles[to].ranges[i]; | 221 | 0 | if last_range.is_some() { | 222 | 0 | debug_assert!(last_range.unwrap() < entry.range); | 223 | 0 | } | 224 | 0 | last_range = Some(entry.range); | 225 | | | 226 | 0 | if self.ranges[entry.index].bundle == from { | 227 | 0 | self.annotate( | 228 | 0 | entry.range.from, | 229 | 0 | format!( | 230 | 0 | " MERGE range{} v{} from bundle{} to bundle{}", | 231 | 0 | entry.index.index(), | 232 | 0 | self.ranges[entry.index].vreg.index(), | 233 | 0 | from.index(), | 234 | 0 | to.index(), | 235 | 0 | ), | 236 | 0 | ); | 237 | 0 | } | 238 | | | 239 | 0 | trace!( | 240 | | " -> merged result for bundle{}: range{}", | 241 | 0 | to.index(), | 242 | 0 | entry.index.index(), | 243 | | ); | 244 | | } | 245 | 106k | } | 246 | | | 247 | 106k | if self.bundles[from].spillset != self.bundles[to].spillset { | 248 | 106k | // Widen the range for the target spillset to include the one being merged in. | 249 | 106k | let from_range = self.spillsets[self.bundles[from].spillset].range; | 250 | 106k | let to_range = &mut self.ctx.spillsets[self.ctx.bundles[to].spillset].range; | 251 | 106k | *to_range = to_range.join(from_range); | 252 | 106k | } | 253 | | | 254 | 106k | self.merge_bundle_properties(from, to); | 255 | | | 256 | 106k | true | 257 | 113k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::merge_bundles Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::merge_bundles Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::merge_bundles <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::merge_bundles Line | Count | Source | 42 | 1.02M | pub fn merge_bundles(&mut self, from: LiveBundleIndex, to: LiveBundleIndex) -> bool { | 43 | 1.02M | if from == to { | 44 | | // Merge bundle into self -- trivial merge. | 45 | 792 | return true; | 46 | 1.02M | } | 47 | 1.02M | trace!( | 48 | | "merging from bundle{} to bundle{}", | 49 | 0 | from.index(), | 50 | 0 | to.index() | 51 | | ); | 52 | | | 53 | | // Both bundles must deal with the same RegClass. | 54 | 1.02M | let from_rc = self.spillsets[self.bundles[from].spillset].class; | 55 | 1.02M | let to_rc = self.spillsets[self.bundles[to].spillset].class; | 56 | 1.02M | if from_rc != to_rc { | 57 | 0 | trace!(" -> mismatching reg classes"); | 58 | 0 | return false; | 59 | 1.02M | } | 60 | | | 61 | | // If either bundle is already assigned (due to a pinned vreg), don't merge. | 62 | 1.02M | if self.bundles[from].allocation.is_some() || self.bundles[to].allocation.is_some() { | 63 | 0 | trace!("one of the bundles is already assigned (pinned)"); | 64 | 0 | return false; | 65 | 1.02M | } | 66 | | | 67 | | #[cfg(debug_assertions)] | 68 | | { | 69 | | // Sanity check: both bundles should contain only ranges with appropriate VReg classes. | 70 | | 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 | | // If a bundle has a fixed-reg def then we need to be careful to not | 81 | | // extend the bundle to include another use in the same instruction. | 82 | | // This could result in a minimal bundle that is impossible to split. | 83 | | // | 84 | | // This can only happen with an early use and a late def, so we round | 85 | | // the start of each range containing a fixed def up to the start of | 86 | | // its instruction to detect overlaps. | 87 | 1.02M | 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 | | // Check for overlap in LiveRanges and for conflicting | 96 | | // requirements. | 97 | 1.02M | let ranges_from = &self.bundles[from].ranges[..]; | 98 | 1.02M | let ranges_to = &self.bundles[to].ranges[..]; | 99 | 1.02M | let mut idx_from = 0; | 100 | 1.02M | let mut idx_to = 0; | 101 | 1.02M | let mut range_count = 0; | 102 | 2.45M | while idx_from < ranges_from.len() && idx_to < ranges_to.len() { | 103 | 1.59M | range_count += 1; | 104 | 1.59M | if range_count > 200 { | 105 | 113 | trace!( | 106 | | "reached merge complexity (range_count = {}); exiting", | 107 | | range_count | 108 | | ); | 109 | | // Limit merge complexity. | 110 | 113 | return false; | 111 | 1.59M | } | 112 | | | 113 | 1.59M | if adjust_range_start(from, ranges_from[idx_from].range) >= ranges_to[idx_to].range.to { | 114 | 103k | idx_to += 1; | 115 | 1.49M | } else if adjust_range_start(to, ranges_to[idx_to].range) | 116 | 1.49M | >= ranges_from[idx_from].range.to | 117 | 1.32M | { | 118 | 1.32M | idx_from += 1; | 119 | 1.32M | } else { | 120 | | // Overlap -- cannot merge. | 121 | 168k | trace!( | 122 | | " -> overlap between {:?} and {:?}, exiting", | 123 | 0 | ranges_from[idx_from].index, | 124 | 0 | ranges_to[idx_to].index | 125 | | ); | 126 | 168k | return false; | 127 | | } | 128 | | } | 129 | | | 130 | | // Check for a requirements conflict. | 131 | 856k | if self.bundles[from].cached_stack() | 132 | 856k | || self.bundles[from].cached_fixed() | 133 | 780k | || self.bundles[from].limit.is_some() | 134 | 780k | || self.bundles[to].cached_stack() | 135 | 780k | || self.bundles[to].cached_fixed() | 136 | 622k | || self.bundles[to].limit.is_some() | 137 | | { | 138 | 234k | if self.merge_bundle_requirements(from, to).is_err() { | 139 | 4.49k | trace!(" -> conflicting requirements; aborting merge"); | 140 | 4.49k | return false; | 141 | 229k | } | 142 | 622k | } | 143 | | | 144 | 852k | trace!(" -> committing to merge"); | 145 | | | 146 | | // If we reach here, then the bundles do not overlap -- merge | 147 | | // them! We do this with a merge-sort-like scan over both | 148 | | // lists, building a new range list and replacing the list on | 149 | | // `to` when we're done. | 150 | 852k | if ranges_from.is_empty() { | 151 | | // `from` bundle is empty -- trivial merge. | 152 | 0 | trace!(" -> from bundle{} is empty; trivial merge", from.index()); | 153 | 0 | return true; | 154 | 852k | } | 155 | 852k | if ranges_to.is_empty() { | 156 | | // `to` bundle is empty -- just move the list over from | 157 | | // `from` and set `bundle` up-link on all ranges. | 158 | 0 | trace!(" -> to bundle{} is empty; trivial merge", to.index()); | 159 | 0 | let empty_vec = LiveRangeList::new_in(self.ctx.bump()); | 160 | 0 | let list = core::mem::replace(&mut self.bundles[from].ranges, empty_vec); | 161 | 0 | for entry in &list { | 162 | 0 | self.ranges[entry.index].bundle = to; | 163 | | | 164 | 0 | if self.annotations_enabled { | 165 | 0 | self.annotate( | 166 | 0 | entry.range.from, | 167 | 0 | format!( | 168 | 0 | " MERGE range{} v{} from bundle{} to bundle{}", | 169 | 0 | entry.index.index(), | 170 | 0 | self.ranges[entry.index].vreg.index(), | 171 | 0 | from.index(), | 172 | 0 | to.index(), | 173 | 0 | ), | 174 | 0 | ); | 175 | 0 | } | 176 | | } | 177 | 0 | self.bundles[to].ranges = list; | 178 | 0 | self.merge_bundle_properties(from, to); | 179 | 0 | return true; | 180 | 852k | } | 181 | | | 182 | 852k | trace!( | 183 | | "merging: ranges_from = {:?} ranges_to = {:?}", | 184 | | ranges_from, | 185 | | ranges_to | 186 | | ); | 187 | | | 188 | 852k | let empty_vec = LiveRangeList::new_in(self.ctx.bump()); | 189 | 852k | let mut from_list = core::mem::replace(&mut self.bundles[from].ranges, empty_vec); | 190 | 1.20M | for entry in &from_list { | 191 | 1.20M | self.ranges[entry.index].bundle = to; | 192 | 1.20M | } | 193 | | | 194 | 852k | if from_list.len() == 1 { | 195 | | // Optimize for the common case where `from_list` contains a single | 196 | | // item. Using a binary search to find the insertion point and then | 197 | | // calling `insert` is more efficient than re-sorting the entire | 198 | | // list, specially after the changes in sorting algorithms introduced | 199 | | // in rustc 1.81. | 200 | | // See: https://github.com/bytecodealliance/regalloc2/issues/203 | 201 | 677k | let single_entry = from_list.pop().unwrap(); | 202 | 677k | let pos = self.bundles[to] | 203 | 677k | .ranges | 204 | 677k | .binary_search_by_key(&single_entry.range.from, |entry| entry.range.from) | 205 | 677k | .unwrap_or_else(|pos| pos); | 206 | 677k | self.bundles[to].ranges.insert(pos, single_entry); | 207 | | } else { | 208 | | // Two non-empty lists of LiveRanges: concatenate and sort. This is | 209 | | // faster than a mergesort-like merge into a new list, empirically. | 210 | 174k | self.bundles[to].ranges.extend_from_slice(&from_list[..]); | 211 | 174k | self.bundles[to] | 212 | 174k | .ranges | 213 | 174k | .sort_unstable_by_key(|entry| entry.range.from); | 214 | | } | 215 | | | 216 | 852k | if self.annotations_enabled { | 217 | 0 | trace!("merging: merged = {:?}", self.bundles[to].ranges); | 218 | 0 | let mut last_range = None; | 219 | 0 | for i in 0..self.bundles[to].ranges.len() { | 220 | 0 | let entry = self.bundles[to].ranges[i]; | 221 | 0 | if last_range.is_some() { | 222 | 0 | debug_assert!(last_range.unwrap() < entry.range); | 223 | 0 | } | 224 | 0 | last_range = Some(entry.range); | 225 | | | 226 | 0 | if self.ranges[entry.index].bundle == from { | 227 | 0 | self.annotate( | 228 | 0 | entry.range.from, | 229 | 0 | format!( | 230 | 0 | " MERGE range{} v{} from bundle{} to bundle{}", | 231 | 0 | entry.index.index(), | 232 | 0 | self.ranges[entry.index].vreg.index(), | 233 | 0 | from.index(), | 234 | 0 | to.index(), | 235 | 0 | ), | 236 | 0 | ); | 237 | 0 | } | 238 | | | 239 | 0 | trace!( | 240 | | " -> merged result for bundle{}: range{}", | 241 | 0 | to.index(), | 242 | 0 | entry.index.index(), | 243 | | ); | 244 | | } | 245 | 852k | } | 246 | | | 247 | 852k | if self.bundles[from].spillset != self.bundles[to].spillset { | 248 | 852k | // Widen the range for the target spillset to include the one being merged in. | 249 | 852k | let from_range = self.spillsets[self.bundles[from].spillset].range; | 250 | 852k | let to_range = &mut self.ctx.spillsets[self.ctx.bundles[to].spillset].range; | 251 | 852k | *to_range = to_range.join(from_range); | 252 | 852k | } | 253 | | | 254 | 852k | self.merge_bundle_properties(from, to); | 255 | | | 256 | 852k | true | 257 | 1.02M | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::merge_bundles Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::merge_bundles |
258 | | |
259 | 430k | pub fn merge_vreg_bundles(&mut self) { |
260 | | // Create a bundle for every vreg, initially. |
261 | 430k | trace!("merge_vreg_bundles: creating vreg bundles"); |
262 | 105M | for vreg in 0..self.vregs.len() { |
263 | 105M | let vreg = VRegIndex::new(vreg); |
264 | 105M | if self.vregs[vreg].ranges.is_empty() { |
265 | 94.8M | continue; |
266 | 10.3M | } |
267 | | |
268 | 10.3M | let bundle = self.ctx.bundles.add(self.ctx.bump()); |
269 | 10.3M | let mut range = self.vregs[vreg].ranges.first().unwrap().range; |
270 | | |
271 | 10.3M | self.bundles[bundle].ranges = self.vregs[vreg].ranges.clone(); |
272 | 10.3M | trace!("vreg v{} gets bundle{}", vreg.index(), bundle.index()); |
273 | 10.7M | for entry in &self.ctx.bundles[bundle].ranges { |
274 | 10.7M | trace!( |
275 | | " -> with LR range{}: {:?}", |
276 | 0 | entry.index.index(), |
277 | | entry.range |
278 | | ); |
279 | 10.7M | range = range.join(entry.range); |
280 | 10.7M | self.ctx.ranges[entry.index].bundle = bundle; |
281 | | } |
282 | | |
283 | 10.3M | let mut fixed = false; |
284 | 10.3M | let mut fixed_def = false; |
285 | 10.3M | let mut stack = false; |
286 | 10.3M | let mut limit: Option<u8> = None; |
287 | 10.7M | for entry in &self.bundles[bundle].ranges { |
288 | 29.6M | for u in &self.ranges[entry.index].uses { |
289 | | use OperandConstraint::*; |
290 | 29.6M | match u.operand.constraint() { |
291 | | FixedReg(_) => { |
292 | 2.55M | fixed = true; |
293 | 2.55M | if u.operand.kind() == OperandKind::Def { |
294 | 1.31M | fixed_def = true; |
295 | 1.31M | } |
296 | | } |
297 | 0 | Stack => stack = true, |
298 | 0 | Limit(current) => { |
299 | 0 | let current = u8::try_from(current) |
300 | 0 | .expect("the current limit is too large to fit in a u8"); |
301 | 0 | match limit { |
302 | 0 | Some(prev) => limit = Some(prev.min(current)), |
303 | 0 | None => limit = Some(current), |
304 | | } |
305 | | } |
306 | | Any | Reg | Reuse(_) => { |
307 | 27.1M | continue; |
308 | | } |
309 | | } |
310 | 2.55M | if fixed && stack && fixed_def { |
311 | 0 | break; |
312 | 2.55M | } |
313 | | } |
314 | | } |
315 | 10.3M | if fixed { |
316 | 2.30M | self.bundles[bundle].set_cached_fixed(); |
317 | 7.99M | } |
318 | 10.3M | if fixed_def { |
319 | 1.31M | self.bundles[bundle].set_cached_fixed_def(); |
320 | 8.98M | } |
321 | 10.3M | if stack { |
322 | 0 | self.bundles[bundle].set_cached_stack(); |
323 | 10.3M | } |
324 | 10.3M | self.bundles[bundle].limit = limit; |
325 | | |
326 | | // Create a spillslot for this bundle. |
327 | 10.3M | let reg = self.vreg(vreg); |
328 | 10.3M | let ssidx = self.spillsets.push(SpillSet { |
329 | 10.3M | slot: SpillSlotIndex::invalid(), |
330 | 10.3M | required: false, |
331 | 10.3M | class: reg.class(), |
332 | 10.3M | hint: PReg::invalid(), |
333 | 10.3M | spill_bundle: LiveBundleIndex::invalid(), |
334 | 10.3M | splits: 0, |
335 | 10.3M | range, |
336 | 10.3M | }); |
337 | 10.3M | self.bundles[bundle].spillset = ssidx; |
338 | | } |
339 | | |
340 | 15.7M | for inst in 0..self.func.num_insts() { |
341 | 15.7M | let inst = Inst::new(inst); |
342 | | |
343 | | // Attempt to merge Reuse-constraint operand outputs with the |
344 | | // corresponding inputs. |
345 | 29.6M | for op in self.func.inst_operands(inst) { |
346 | 29.6M | if let OperandConstraint::Reuse(reuse_idx) = op.constraint() { |
347 | 774k | let src_vreg = op.vreg(); |
348 | 774k | let dst_vreg = self.func.inst_operands(inst)[reuse_idx].vreg(); |
349 | | |
350 | 774k | trace!( |
351 | | "trying to merge reused-input def: src {} to dst {}", |
352 | | src_vreg, |
353 | | dst_vreg |
354 | | ); |
355 | 774k | let src_bundle = self.ranges[self.vregs[src_vreg].ranges[0].index].bundle; |
356 | 774k | debug_assert!(src_bundle.is_valid()); |
357 | 774k | let dest_bundle = self.ranges[self.vregs[dst_vreg].ranges[0].index].bundle; |
358 | 774k | debug_assert!(dest_bundle.is_valid()); |
359 | 774k | self.merge_bundles(/* from */ dest_bundle, /* to */ src_bundle); |
360 | 28.8M | } |
361 | | } |
362 | | } |
363 | | |
364 | | // Attempt to merge blockparams with their inputs. |
365 | 430k | for i in 0..self.blockparam_outs.len() { |
366 | | let BlockparamOut { |
367 | 365k | from_vreg, to_vreg, .. |
368 | 365k | } = self.blockparam_outs[i]; |
369 | 365k | trace!( |
370 | | "trying to merge blockparam v{} with input v{}", |
371 | 0 | to_vreg.index(), |
372 | 0 | from_vreg.index() |
373 | | ); |
374 | 365k | let to_bundle = self.ranges[self.vregs[to_vreg].ranges[0].index].bundle; |
375 | 365k | debug_assert!(to_bundle.is_valid()); |
376 | 365k | let from_bundle = self.ranges[self.vregs[from_vreg].ranges[0].index].bundle; |
377 | 365k | debug_assert!(from_bundle.is_valid()); |
378 | 365k | trace!( |
379 | | " -> from bundle{} to bundle{}", |
380 | 0 | from_bundle.index(), |
381 | 0 | to_bundle.index() |
382 | | ); |
383 | 365k | self.merge_bundles(from_bundle, to_bundle); |
384 | | } |
385 | | |
386 | 430k | trace!("done merging bundles"); |
387 | 430k | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::merge_vreg_bundles Line | Count | Source | 259 | 96.6k | pub fn merge_vreg_bundles(&mut self) { | 260 | | // Create a bundle for every vreg, initially. | 261 | 96.6k | trace!("merge_vreg_bundles: creating vreg bundles"); | 262 | 22.3M | for vreg in 0..self.vregs.len() { | 263 | 22.3M | let vreg = VRegIndex::new(vreg); | 264 | 22.3M | if self.vregs[vreg].ranges.is_empty() { | 265 | 20.4M | continue; | 266 | 1.90M | } | 267 | | | 268 | 1.90M | let bundle = self.ctx.bundles.add(self.ctx.bump()); | 269 | 1.90M | let mut range = self.vregs[vreg].ranges.first().unwrap().range; | 270 | | | 271 | 1.90M | self.bundles[bundle].ranges = self.vregs[vreg].ranges.clone(); | 272 | 1.90M | trace!("vreg v{} gets bundle{}", vreg.index(), bundle.index()); | 273 | 1.92M | for entry in &self.ctx.bundles[bundle].ranges { | 274 | 1.92M | trace!( | 275 | | " -> with LR range{}: {:?}", | 276 | 0 | entry.index.index(), | 277 | | entry.range | 278 | | ); | 279 | 1.92M | range = range.join(entry.range); | 280 | 1.92M | self.ctx.ranges[entry.index].bundle = bundle; | 281 | | } | 282 | | | 283 | 1.90M | let mut fixed = false; | 284 | 1.90M | let mut fixed_def = false; | 285 | 1.90M | let mut stack = false; | 286 | 1.90M | let mut limit: Option<u8> = None; | 287 | 1.92M | for entry in &self.bundles[bundle].ranges { | 288 | 5.23M | for u in &self.ranges[entry.index].uses { | 289 | | use OperandConstraint::*; | 290 | 5.23M | match u.operand.constraint() { | 291 | | FixedReg(_) => { | 292 | 618k | fixed = true; | 293 | 618k | if u.operand.kind() == OperandKind::Def { | 294 | 306k | fixed_def = true; | 295 | 312k | } | 296 | | } | 297 | 0 | Stack => stack = true, | 298 | 0 | Limit(current) => { | 299 | 0 | let current = u8::try_from(current) | 300 | 0 | .expect("the current limit is too large to fit in a u8"); | 301 | 0 | match limit { | 302 | 0 | Some(prev) => limit = Some(prev.min(current)), | 303 | 0 | None => limit = Some(current), | 304 | | } | 305 | | } | 306 | | Any | Reg | Reuse(_) => { | 307 | 4.61M | continue; | 308 | | } | 309 | | } | 310 | 618k | if fixed && stack && fixed_def { | 311 | 0 | break; | 312 | 618k | } | 313 | | } | 314 | | } | 315 | 1.90M | if fixed { | 316 | 553k | self.bundles[bundle].set_cached_fixed(); | 317 | 1.35M | } | 318 | 1.90M | if fixed_def { | 319 | 306k | self.bundles[bundle].set_cached_fixed_def(); | 320 | 1.59M | } | 321 | 1.90M | if stack { | 322 | 0 | self.bundles[bundle].set_cached_stack(); | 323 | 1.90M | } | 324 | 1.90M | self.bundles[bundle].limit = limit; | 325 | | | 326 | | // Create a spillslot for this bundle. | 327 | 1.90M | let reg = self.vreg(vreg); | 328 | 1.90M | let ssidx = self.spillsets.push(SpillSet { | 329 | 1.90M | slot: SpillSlotIndex::invalid(), | 330 | 1.90M | required: false, | 331 | 1.90M | class: reg.class(), | 332 | 1.90M | hint: PReg::invalid(), | 333 | 1.90M | spill_bundle: LiveBundleIndex::invalid(), | 334 | 1.90M | splits: 0, | 335 | 1.90M | range, | 336 | 1.90M | }); | 337 | 1.90M | self.bundles[bundle].spillset = ssidx; | 338 | | } | 339 | | | 340 | 2.55M | for inst in 0..self.func.num_insts() { | 341 | 2.55M | let inst = Inst::new(inst); | 342 | | | 343 | | // Attempt to merge Reuse-constraint operand outputs with the | 344 | | // corresponding inputs. | 345 | 5.23M | for op in self.func.inst_operands(inst) { | 346 | 5.23M | if let OperandConstraint::Reuse(reuse_idx) = op.constraint() { | 347 | 76.3k | let src_vreg = op.vreg(); | 348 | 76.3k | let dst_vreg = self.func.inst_operands(inst)[reuse_idx].vreg(); | 349 | | | 350 | 76.3k | trace!( | 351 | | "trying to merge reused-input def: src {} to dst {}", | 352 | | src_vreg, | 353 | | dst_vreg | 354 | | ); | 355 | 76.3k | let src_bundle = self.ranges[self.vregs[src_vreg].ranges[0].index].bundle; | 356 | 76.3k | debug_assert!(src_bundle.is_valid()); | 357 | 76.3k | let dest_bundle = self.ranges[self.vregs[dst_vreg].ranges[0].index].bundle; | 358 | 76.3k | debug_assert!(dest_bundle.is_valid()); | 359 | 76.3k | self.merge_bundles(/* from */ dest_bundle, /* to */ src_bundle); | 360 | 5.15M | } | 361 | | } | 362 | | } | 363 | | | 364 | | // Attempt to merge blockparams with their inputs. | 365 | 96.6k | for i in 0..self.blockparam_outs.len() { | 366 | | let BlockparamOut { | 367 | 37.5k | from_vreg, to_vreg, .. | 368 | 37.5k | } = self.blockparam_outs[i]; | 369 | 37.5k | trace!( | 370 | | "trying to merge blockparam v{} with input v{}", | 371 | 0 | to_vreg.index(), | 372 | 0 | from_vreg.index() | 373 | | ); | 374 | 37.5k | let to_bundle = self.ranges[self.vregs[to_vreg].ranges[0].index].bundle; | 375 | 37.5k | debug_assert!(to_bundle.is_valid()); | 376 | 37.5k | let from_bundle = self.ranges[self.vregs[from_vreg].ranges[0].index].bundle; | 377 | 37.5k | debug_assert!(from_bundle.is_valid()); | 378 | 37.5k | trace!( | 379 | | " -> from bundle{} to bundle{}", | 380 | 0 | from_bundle.index(), | 381 | 0 | to_bundle.index() | 382 | | ); | 383 | 37.5k | self.merge_bundles(from_bundle, to_bundle); | 384 | | } | 385 | | | 386 | 96.6k | trace!("done merging bundles"); | 387 | 96.6k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::merge_vreg_bundles Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::merge_vreg_bundles Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::merge_vreg_bundles <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::merge_vreg_bundles Line | Count | Source | 259 | 333k | pub fn merge_vreg_bundles(&mut self) { | 260 | | // Create a bundle for every vreg, initially. | 261 | 333k | trace!("merge_vreg_bundles: creating vreg bundles"); | 262 | 82.8M | for vreg in 0..self.vregs.len() { | 263 | 82.8M | let vreg = VRegIndex::new(vreg); | 264 | 82.8M | if self.vregs[vreg].ranges.is_empty() { | 265 | 74.4M | continue; | 266 | 8.40M | } | 267 | | | 268 | 8.40M | let bundle = self.ctx.bundles.add(self.ctx.bump()); | 269 | 8.40M | let mut range = self.vregs[vreg].ranges.first().unwrap().range; | 270 | | | 271 | 8.40M | self.bundles[bundle].ranges = self.vregs[vreg].ranges.clone(); | 272 | 8.40M | trace!("vreg v{} gets bundle{}", vreg.index(), bundle.index()); | 273 | 8.81M | for entry in &self.ctx.bundles[bundle].ranges { | 274 | 8.81M | trace!( | 275 | | " -> with LR range{}: {:?}", | 276 | 0 | entry.index.index(), | 277 | | entry.range | 278 | | ); | 279 | 8.81M | range = range.join(entry.range); | 280 | 8.81M | self.ctx.ranges[entry.index].bundle = bundle; | 281 | | } | 282 | | | 283 | 8.40M | let mut fixed = false; | 284 | 8.40M | let mut fixed_def = false; | 285 | 8.40M | let mut stack = false; | 286 | 8.40M | let mut limit: Option<u8> = None; | 287 | 8.81M | for entry in &self.bundles[bundle].ranges { | 288 | 24.4M | for u in &self.ranges[entry.index].uses { | 289 | | use OperandConstraint::*; | 290 | 24.4M | match u.operand.constraint() { | 291 | | FixedReg(_) => { | 292 | 1.94M | fixed = true; | 293 | 1.94M | if u.operand.kind() == OperandKind::Def { | 294 | 1.01M | fixed_def = true; | 295 | 1.01M | } | 296 | | } | 297 | 0 | Stack => stack = true, | 298 | 0 | Limit(current) => { | 299 | 0 | let current = u8::try_from(current) | 300 | 0 | .expect("the current limit is too large to fit in a u8"); | 301 | 0 | match limit { | 302 | 0 | Some(prev) => limit = Some(prev.min(current)), | 303 | 0 | None => limit = Some(current), | 304 | | } | 305 | | } | 306 | | Any | Reg | Reuse(_) => { | 307 | 22.4M | continue; | 308 | | } | 309 | | } | 310 | 1.94M | if fixed && stack && fixed_def { | 311 | 0 | break; | 312 | 1.94M | } | 313 | | } | 314 | | } | 315 | 8.40M | if fixed { | 316 | 1.75M | self.bundles[bundle].set_cached_fixed(); | 317 | 6.64M | } | 318 | 8.40M | if fixed_def { | 319 | 1.01M | self.bundles[bundle].set_cached_fixed_def(); | 320 | 7.38M | } | 321 | 8.40M | if stack { | 322 | 0 | self.bundles[bundle].set_cached_stack(); | 323 | 8.40M | } | 324 | 8.40M | self.bundles[bundle].limit = limit; | 325 | | | 326 | | // Create a spillslot for this bundle. | 327 | 8.40M | let reg = self.vreg(vreg); | 328 | 8.40M | let ssidx = self.spillsets.push(SpillSet { | 329 | 8.40M | slot: SpillSlotIndex::invalid(), | 330 | 8.40M | required: false, | 331 | 8.40M | class: reg.class(), | 332 | 8.40M | hint: PReg::invalid(), | 333 | 8.40M | spill_bundle: LiveBundleIndex::invalid(), | 334 | 8.40M | splits: 0, | 335 | 8.40M | range, | 336 | 8.40M | }); | 337 | 8.40M | self.bundles[bundle].spillset = ssidx; | 338 | | } | 339 | | | 340 | 13.2M | for inst in 0..self.func.num_insts() { | 341 | 13.2M | let inst = Inst::new(inst); | 342 | | | 343 | | // Attempt to merge Reuse-constraint operand outputs with the | 344 | | // corresponding inputs. | 345 | 24.4M | for op in self.func.inst_operands(inst) { | 346 | 24.4M | if let OperandConstraint::Reuse(reuse_idx) = op.constraint() { | 347 | 698k | let src_vreg = op.vreg(); | 348 | 698k | let dst_vreg = self.func.inst_operands(inst)[reuse_idx].vreg(); | 349 | | | 350 | 698k | trace!( | 351 | | "trying to merge reused-input def: src {} to dst {}", | 352 | | src_vreg, | 353 | | dst_vreg | 354 | | ); | 355 | 698k | let src_bundle = self.ranges[self.vregs[src_vreg].ranges[0].index].bundle; | 356 | 698k | debug_assert!(src_bundle.is_valid()); | 357 | 698k | let dest_bundle = self.ranges[self.vregs[dst_vreg].ranges[0].index].bundle; | 358 | 698k | debug_assert!(dest_bundle.is_valid()); | 359 | 698k | self.merge_bundles(/* from */ dest_bundle, /* to */ src_bundle); | 360 | 23.7M | } | 361 | | } | 362 | | } | 363 | | | 364 | | // Attempt to merge blockparams with their inputs. | 365 | 333k | for i in 0..self.blockparam_outs.len() { | 366 | | let BlockparamOut { | 367 | 328k | from_vreg, to_vreg, .. | 368 | 328k | } = self.blockparam_outs[i]; | 369 | 328k | trace!( | 370 | | "trying to merge blockparam v{} with input v{}", | 371 | 0 | to_vreg.index(), | 372 | 0 | from_vreg.index() | 373 | | ); | 374 | 328k | let to_bundle = self.ranges[self.vregs[to_vreg].ranges[0].index].bundle; | 375 | 328k | debug_assert!(to_bundle.is_valid()); | 376 | 328k | let from_bundle = self.ranges[self.vregs[from_vreg].ranges[0].index].bundle; | 377 | 328k | debug_assert!(from_bundle.is_valid()); | 378 | 328k | trace!( | 379 | | " -> from bundle{} to bundle{}", | 380 | 0 | from_bundle.index(), | 381 | 0 | to_bundle.index() | 382 | | ); | 383 | 328k | self.merge_bundles(from_bundle, to_bundle); | 384 | | } | 385 | | | 386 | 333k | trace!("done merging bundles"); | 387 | 333k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::merge_vreg_bundles Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::merge_vreg_bundles |
388 | | |
389 | 13.0M | pub fn compute_bundle_prio(&self, bundle: LiveBundleIndex) -> u32 { |
390 | | // The priority is simply the total "length" -- the number of |
391 | | // instructions covered by all LiveRanges. |
392 | 13.0M | let mut total = 0; |
393 | 14.6M | for entry in &self.bundles[bundle].ranges { |
394 | 14.6M | total += entry.range.len() as u32; |
395 | 14.6M | } |
396 | 13.0M | trace!(" -> prio {total}"); |
397 | 13.0M | total |
398 | 13.0M | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::compute_bundle_prio Line | Count | Source | 389 | 2.91M | pub fn compute_bundle_prio(&self, bundle: LiveBundleIndex) -> u32 { | 390 | | // The priority is simply the total "length" -- the number of | 391 | | // instructions covered by all LiveRanges. | 392 | 2.91M | let mut total = 0; | 393 | 3.06M | for entry in &self.bundles[bundle].ranges { | 394 | 3.06M | total += entry.range.len() as u32; | 395 | 3.06M | } | 396 | 2.91M | trace!(" -> prio {total}"); | 397 | 2.91M | total | 398 | 2.91M | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::compute_bundle_prio Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::compute_bundle_prio Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::compute_bundle_prio <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::compute_bundle_prio Line | Count | Source | 389 | 10.1M | pub fn compute_bundle_prio(&self, bundle: LiveBundleIndex) -> u32 { | 390 | | // The priority is simply the total "length" -- the number of | 391 | | // instructions covered by all LiveRanges. | 392 | 10.1M | let mut total = 0; | 393 | 11.6M | for entry in &self.bundles[bundle].ranges { | 394 | 11.6M | total += entry.range.len() as u32; | 395 | 11.6M | } | 396 | 10.1M | trace!(" -> prio {total}"); | 397 | 10.1M | total | 398 | 10.1M | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::compute_bundle_prio Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::compute_bundle_prio |
399 | | |
400 | 13.0M | pub fn compute_bundle_limit(&self, bundle: LiveBundleIndex) -> Option<u8> { |
401 | 13.0M | let mut limit: Option<u8> = None; |
402 | 14.6M | for entry in &self.bundles[bundle].ranges { |
403 | 30.5M | for u in &self.ranges[entry.index].uses { |
404 | | use OperandConstraint::*; |
405 | 30.5M | match u.operand.constraint() { |
406 | 0 | Limit(current) => { |
407 | 0 | let current = u8::try_from(current) |
408 | 0 | .expect("the current limit is too large to fit in a u8"); |
409 | 0 | match limit { |
410 | 0 | Some(prev) => limit = Some(prev.min(current)), |
411 | 0 | None => limit = Some(current), |
412 | | } |
413 | | } |
414 | | FixedReg(_) | Stack => { |
415 | 3.31M | break; |
416 | | } |
417 | | Any | Reg | Reuse(_) => { |
418 | 27.1M | continue; |
419 | | } |
420 | | } |
421 | | } |
422 | | } |
423 | 13.0M | limit |
424 | 13.0M | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::compute_bundle_limit Line | Count | Source | 400 | 2.91M | pub fn compute_bundle_limit(&self, bundle: LiveBundleIndex) -> Option<u8> { | 401 | 2.91M | let mut limit: Option<u8> = None; | 402 | 3.06M | for entry in &self.bundles[bundle].ranges { | 403 | 5.63M | for u in &self.ranges[entry.index].uses { | 404 | | use OperandConstraint::*; | 405 | 5.63M | match u.operand.constraint() { | 406 | 0 | Limit(current) => { | 407 | 0 | let current = u8::try_from(current) | 408 | 0 | .expect("the current limit is too large to fit in a u8"); | 409 | 0 | match limit { | 410 | 0 | Some(prev) => limit = Some(prev.min(current)), | 411 | 0 | None => limit = Some(current), | 412 | | } | 413 | | } | 414 | | FixedReg(_) | Stack => { | 415 | 842k | break; | 416 | | } | 417 | | Any | Reg | Reuse(_) => { | 418 | 4.79M | continue; | 419 | | } | 420 | | } | 421 | | } | 422 | | } | 423 | 2.91M | limit | 424 | 2.91M | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::compute_bundle_limit Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::compute_bundle_limit Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::compute_bundle_limit <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::compute_bundle_limit Line | Count | Source | 400 | 10.1M | pub fn compute_bundle_limit(&self, bundle: LiveBundleIndex) -> Option<u8> { | 401 | 10.1M | let mut limit: Option<u8> = None; | 402 | 11.6M | for entry in &self.bundles[bundle].ranges { | 403 | 24.8M | for u in &self.ranges[entry.index].uses { | 404 | | use OperandConstraint::*; | 405 | 24.8M | match u.operand.constraint() { | 406 | 0 | Limit(current) => { | 407 | 0 | let current = u8::try_from(current) | 408 | 0 | .expect("the current limit is too large to fit in a u8"); | 409 | 0 | match limit { | 410 | 0 | Some(prev) => limit = Some(prev.min(current)), | 411 | 0 | None => limit = Some(current), | 412 | | } | 413 | | } | 414 | | FixedReg(_) | Stack => { | 415 | 2.47M | break; | 416 | | } | 417 | | Any | Reg | Reuse(_) => { | 418 | 22.4M | continue; | 419 | | } | 420 | | } | 421 | | } | 422 | | } | 423 | 10.1M | limit | 424 | 10.1M | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::compute_bundle_limit Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::compute_bundle_limit |
425 | | |
426 | 430k | pub fn queue_bundles(&mut self) { |
427 | 10.3M | for bundle in 0..self.bundles.len() { |
428 | 10.3M | trace!("enqueueing bundle{}", bundle); |
429 | 10.3M | let bundle = LiveBundleIndex::new(bundle); |
430 | 10.3M | if self.bundles[bundle].ranges.is_empty() { |
431 | 958k | trace!(" -> no ranges; skipping"); |
432 | 958k | continue; |
433 | 9.34M | } |
434 | 9.34M | self.recompute_bundle_properties(bundle); |
435 | 9.34M | let prio = self.bundles[bundle].prio as usize; |
436 | 9.34M | self.allocation_queue.insert(bundle, prio, PReg::invalid()); |
437 | | } |
438 | 430k | self.output.stats.merged_bundle_count = self.allocation_queue.heap.len(); |
439 | 430k | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::queue_bundles Line | Count | Source | 426 | 96.6k | pub fn queue_bundles(&mut self) { | 427 | 1.90M | for bundle in 0..self.bundles.len() { | 428 | 1.90M | trace!("enqueueing bundle{}", bundle); | 429 | 1.90M | let bundle = LiveBundleIndex::new(bundle); | 430 | 1.90M | if self.bundles[bundle].ranges.is_empty() { | 431 | 106k | trace!(" -> no ranges; skipping"); | 432 | 106k | continue; | 433 | 1.79M | } | 434 | 1.79M | self.recompute_bundle_properties(bundle); | 435 | 1.79M | let prio = self.bundles[bundle].prio as usize; | 436 | 1.79M | self.allocation_queue.insert(bundle, prio, PReg::invalid()); | 437 | | } | 438 | 96.6k | self.output.stats.merged_bundle_count = self.allocation_queue.heap.len(); | 439 | 96.6k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::queue_bundles Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::queue_bundles Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::queue_bundles <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::queue_bundles Line | Count | Source | 426 | 333k | pub fn queue_bundles(&mut self) { | 427 | 8.40M | for bundle in 0..self.bundles.len() { | 428 | 8.40M | trace!("enqueueing bundle{}", bundle); | 429 | 8.40M | let bundle = LiveBundleIndex::new(bundle); | 430 | 8.40M | if self.bundles[bundle].ranges.is_empty() { | 431 | 852k | trace!(" -> no ranges; skipping"); | 432 | 852k | continue; | 433 | 7.55M | } | 434 | 7.55M | self.recompute_bundle_properties(bundle); | 435 | 7.55M | let prio = self.bundles[bundle].prio as usize; | 436 | 7.55M | self.allocation_queue.insert(bundle, prio, PReg::invalid()); | 437 | | } | 438 | 333k | self.output.stats.merged_bundle_count = self.allocation_queue.heap.len(); | 439 | 333k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::queue_bundles Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::queue_bundles |
440 | | } |