/rust/registry/src/index.crates.io-6f17d22bba15001f/regalloc2-0.5.1/src/ion/liveranges.rs
Line | Count | Source (jump to first uncovered line) |
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 | | //! Live-range computation. |
14 | | |
15 | | use super::{ |
16 | | CodeRange, Env, InsertMovePrio, LiveBundle, LiveBundleIndex, LiveRange, LiveRangeFlag, |
17 | | LiveRangeIndex, LiveRangeKey, LiveRangeListEntry, LiveRangeSet, PRegData, PRegIndex, RegClass, |
18 | | SpillSetIndex, Use, VRegData, VRegIndex, SLOT_NONE, |
19 | | }; |
20 | | use crate::indexset::IndexSet; |
21 | | use crate::ion::data_structures::{ |
22 | | BlockparamIn, BlockparamOut, FixedRegFixupLevel, MultiFixedRegFixup, |
23 | | }; |
24 | | use crate::{ |
25 | | Allocation, Block, Function, Inst, InstPosition, Operand, OperandConstraint, OperandKind, |
26 | | OperandPos, PReg, ProgPoint, RegAllocError, VReg, |
27 | | }; |
28 | | use fxhash::{FxHashMap, FxHashSet}; |
29 | | use slice_group_by::GroupByMut; |
30 | | use smallvec::{smallvec, SmallVec}; |
31 | | use std::collections::{HashSet, VecDeque}; |
32 | | |
33 | | /// A spill weight computed for a certain Use. |
34 | | #[derive(Clone, Copy, Debug)] |
35 | | pub struct SpillWeight(f32); |
36 | | |
37 | | #[inline(always)] |
38 | 2.86M | pub fn spill_weight_from_constraint( |
39 | 2.86M | constraint: OperandConstraint, |
40 | 2.86M | loop_depth: usize, |
41 | 2.86M | is_def: bool, |
42 | 2.86M | ) -> SpillWeight { |
43 | 2.86M | // A bonus of 1000 for one loop level, 4000 for two loop levels, |
44 | 2.86M | // 16000 for three loop levels, etc. Avoids exponentiation. |
45 | 2.86M | let loop_depth = std::cmp::min(10, loop_depth); |
46 | 2.86M | let hot_bonus: f32 = (0..loop_depth).fold(1000.0, |a, _| a * 4.0); regalloc2::ion::liveranges::spill_weight_from_constraint::{closure#0}Line | Count | Source | 46 | 42.4k | let hot_bonus: f32 = (0..loop_depth).fold(1000.0, |a, _| a * 4.0); |
Unexecuted instantiation: regalloc2::ion::liveranges::spill_weight_from_constraint::{closure#0} |
47 | 2.86M | let def_bonus: f32 = if is_def { 2000.0 } else { 0.0 }; |
48 | 2.86M | let constraint_bonus: f32 = match constraint { |
49 | 43.2k | OperandConstraint::Any => 1000.0, |
50 | 2.71M | OperandConstraint::Reg | OperandConstraint::FixedReg(_) => 2000.0, |
51 | 100k | _ => 0.0, |
52 | | }; |
53 | 2.86M | SpillWeight(hot_bonus + def_bonus + constraint_bonus) |
54 | 2.86M | } |
55 | | |
56 | | impl SpillWeight { |
57 | | /// Convert a floating-point weight to a u16 that can be compactly |
58 | | /// stored in a `Use`. We simply take the top 16 bits of the f32; this |
59 | | /// is equivalent to the bfloat16 format |
60 | | /// (https://en.wikipedia.org/wiki/Bfloat16_floating-point_format). |
61 | 2.15M | pub fn to_bits(self) -> u16 { |
62 | 2.15M | (self.0.to_bits() >> 15) as u16 |
63 | 2.15M | } <regalloc2::ion::liveranges::SpillWeight>::to_bits Line | Count | Source | 61 | 2.15M | pub fn to_bits(self) -> u16 { | 62 | 2.15M | (self.0.to_bits() >> 15) as u16 | 63 | 2.15M | } |
Unexecuted instantiation: <regalloc2::ion::liveranges::SpillWeight>::to_bits |
64 | | |
65 | | /// Convert a value that was returned from |
66 | | /// `SpillWeight::to_bits()` back into a `SpillWeight`. Note that |
67 | | /// some precision may be lost when round-tripping from a spill |
68 | | /// weight to packed bits and back. |
69 | 537k | pub fn from_bits(bits: u16) -> SpillWeight { |
70 | 537k | let x = f32::from_bits((bits as u32) << 15); |
71 | 537k | SpillWeight(x) |
72 | 537k | } <regalloc2::ion::liveranges::SpillWeight>::from_bits Line | Count | Source | 69 | 537k | pub fn from_bits(bits: u16) -> SpillWeight { | 70 | 537k | let x = f32::from_bits((bits as u32) << 15); | 71 | 537k | SpillWeight(x) | 72 | 537k | } |
Unexecuted instantiation: <regalloc2::ion::liveranges::SpillWeight>::from_bits |
73 | | |
74 | | /// Get a zero spill weight. |
75 | 875k | pub fn zero() -> SpillWeight { |
76 | 875k | SpillWeight(0.0) |
77 | 875k | } <regalloc2::ion::liveranges::SpillWeight>::zero Line | Count | Source | 75 | 875k | pub fn zero() -> SpillWeight { | 76 | 875k | SpillWeight(0.0) | 77 | 875k | } |
Unexecuted instantiation: <regalloc2::ion::liveranges::SpillWeight>::zero |
78 | | |
79 | | /// Convert to a raw floating-point value. |
80 | 3.03M | pub fn to_f32(self) -> f32 { |
81 | 3.03M | self.0 |
82 | 3.03M | } <regalloc2::ion::liveranges::SpillWeight>::to_f32 Line | Count | Source | 80 | 3.03M | pub fn to_f32(self) -> f32 { | 81 | 3.03M | self.0 | 82 | 3.03M | } |
Unexecuted instantiation: <regalloc2::ion::liveranges::SpillWeight>::to_f32 |
83 | | |
84 | | /// Create a `SpillWeight` from a raw floating-point value. |
85 | 3.05M | pub fn from_f32(x: f32) -> SpillWeight { |
86 | 3.05M | SpillWeight(x) |
87 | 3.05M | } <regalloc2::ion::liveranges::SpillWeight>::from_f32 Line | Count | Source | 85 | 3.05M | pub fn from_f32(x: f32) -> SpillWeight { | 86 | 3.05M | SpillWeight(x) | 87 | 3.05M | } |
Unexecuted instantiation: <regalloc2::ion::liveranges::SpillWeight>::from_f32 |
88 | | |
89 | 703k | pub fn to_int(self) -> u32 { |
90 | 703k | self.0 as u32 |
91 | 703k | } <regalloc2::ion::liveranges::SpillWeight>::to_int Line | Count | Source | 89 | 703k | pub fn to_int(self) -> u32 { | 90 | 703k | self.0 as u32 | 91 | 703k | } |
Unexecuted instantiation: <regalloc2::ion::liveranges::SpillWeight>::to_int |
92 | | } |
93 | | |
94 | | impl std::ops::Add<SpillWeight> for SpillWeight { |
95 | | type Output = SpillWeight; |
96 | 3.59M | fn add(self, other: SpillWeight) -> Self { |
97 | 3.59M | SpillWeight(self.0 + other.0) |
98 | 3.59M | } <regalloc2::ion::liveranges::SpillWeight as core::ops::arith::Add>::add Line | Count | Source | 96 | 3.59M | fn add(self, other: SpillWeight) -> Self { | 97 | 3.59M | SpillWeight(self.0 + other.0) | 98 | 3.59M | } |
Unexecuted instantiation: <regalloc2::ion::liveranges::SpillWeight as core::ops::arith::Add>::add |
99 | | } |
100 | | |
101 | | impl<'a, F: Function> Env<'a, F> { |
102 | 139k | pub fn create_pregs_and_vregs(&mut self) { |
103 | 139k | // Create PRegs from the env. |
104 | 139k | self.pregs.resize( |
105 | 139k | PReg::NUM_INDEX, |
106 | 139k | PRegData { |
107 | 139k | allocations: LiveRangeSet::new(), |
108 | 139k | is_stack: false, |
109 | 139k | }, |
110 | 139k | ); |
111 | 139k | for &preg in &self.env.fixed_stack_slots { |
112 | 0 | self.pregs[preg.index()].is_stack = true; |
113 | 0 | } |
114 | 278k | for class in 0..self.preferred_victim_by_class.len() { |
115 | 278k | self.preferred_victim_by_class[class] = self.env.non_preferred_regs_by_class[class] |
116 | 278k | .last() |
117 | 278k | .or(self.env.preferred_regs_by_class[class].last()) |
118 | 278k | .cloned() |
119 | 278k | .unwrap_or(PReg::invalid()); |
120 | 278k | } |
121 | | // Create VRegs from the vreg count. |
122 | 19.7M | for idx in 0..self.func.num_vregs() { |
123 | | // We'll fill in the real details when we see the def. |
124 | 19.7M | let reg = VReg::new(idx, RegClass::Int); |
125 | 19.7M | self.add_vreg( |
126 | 19.7M | reg, |
127 | 19.7M | VRegData { |
128 | 19.7M | ranges: smallvec![], |
129 | 19.7M | blockparam: Block::invalid(), |
130 | 19.7M | is_ref: false, |
131 | 19.7M | // We'll learn the RegClass as we scan the code. |
132 | 19.7M | class: None, |
133 | | }, |
134 | | ); |
135 | | } |
136 | 139k | for v in self.func.reftype_vregs() { |
137 | 0 | self.vregs[v.vreg()].is_ref = true; |
138 | 0 | } |
139 | | // Create allocations too. |
140 | 1.27M | for inst in 0..self.func.num_insts() { |
141 | 1.27M | let start = self.allocs.len() as u32; |
142 | 1.27M | self.inst_alloc_offsets.push(start); |
143 | 2.16M | for _ in 0..self.func.inst_operands(Inst::new(inst)).len() { |
144 | 2.16M | self.allocs.push(Allocation::none()); |
145 | 2.16M | } |
146 | | } |
147 | 139k | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::create_pregs_and_vregs Line | Count | Source | 102 | 139k | pub fn create_pregs_and_vregs(&mut self) { | 103 | 139k | // Create PRegs from the env. | 104 | 139k | self.pregs.resize( | 105 | 139k | PReg::NUM_INDEX, | 106 | 139k | PRegData { | 107 | 139k | allocations: LiveRangeSet::new(), | 108 | 139k | is_stack: false, | 109 | 139k | }, | 110 | 139k | ); | 111 | 139k | for &preg in &self.env.fixed_stack_slots { | 112 | 0 | self.pregs[preg.index()].is_stack = true; | 113 | 0 | } | 114 | 278k | for class in 0..self.preferred_victim_by_class.len() { | 115 | 278k | self.preferred_victim_by_class[class] = self.env.non_preferred_regs_by_class[class] | 116 | 278k | .last() | 117 | 278k | .or(self.env.preferred_regs_by_class[class].last()) | 118 | 278k | .cloned() | 119 | 278k | .unwrap_or(PReg::invalid()); | 120 | 278k | } | 121 | | // Create VRegs from the vreg count. | 122 | 19.7M | for idx in 0..self.func.num_vregs() { | 123 | | // We'll fill in the real details when we see the def. | 124 | 19.7M | let reg = VReg::new(idx, RegClass::Int); | 125 | 19.7M | self.add_vreg( | 126 | 19.7M | reg, | 127 | 19.7M | VRegData { | 128 | 19.7M | ranges: smallvec![], | 129 | 19.7M | blockparam: Block::invalid(), | 130 | 19.7M | is_ref: false, | 131 | 19.7M | // We'll learn the RegClass as we scan the code. | 132 | 19.7M | class: None, | 133 | | }, | 134 | | ); | 135 | | } | 136 | 139k | for v in self.func.reftype_vregs() { | 137 | 0 | self.vregs[v.vreg()].is_ref = true; | 138 | 0 | } | 139 | | // Create allocations too. | 140 | 1.27M | for inst in 0..self.func.num_insts() { | 141 | 1.27M | let start = self.allocs.len() as u32; | 142 | 1.27M | self.inst_alloc_offsets.push(start); | 143 | 2.16M | for _ in 0..self.func.inst_operands(Inst::new(inst)).len() { | 144 | 2.16M | self.allocs.push(Allocation::none()); | 145 | 2.16M | } | 146 | | } | 147 | 139k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::create_pregs_and_vregs Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::create_pregs_and_vregs Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::create_pregs_and_vregs |
148 | | |
149 | 19.7M | pub fn add_vreg(&mut self, reg: VReg, data: VRegData) -> VRegIndex { |
150 | 19.7M | let idx = self.vregs.len(); |
151 | 19.7M | debug_assert_eq!(reg.vreg(), idx); |
152 | 19.7M | self.vregs.push(data); |
153 | 19.7M | VRegIndex::new(idx) |
154 | 19.7M | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::add_vreg Line | Count | Source | 149 | 19.7M | pub fn add_vreg(&mut self, reg: VReg, data: VRegData) -> VRegIndex { | 150 | 19.7M | let idx = self.vregs.len(); | 151 | 19.7M | debug_assert_eq!(reg.vreg(), idx); | 152 | 19.7M | self.vregs.push(data); | 153 | 19.7M | VRegIndex::new(idx) | 154 | 19.7M | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::add_vreg Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::add_vreg Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::add_vreg |
155 | | |
156 | 1.34M | pub fn create_bundle(&mut self) -> LiveBundleIndex { |
157 | 1.34M | let bundle = self.bundles.len(); |
158 | 1.34M | self.bundles.push(LiveBundle { |
159 | 1.34M | allocation: Allocation::none(), |
160 | 1.34M | ranges: smallvec![], |
161 | 1.34M | spillset: SpillSetIndex::invalid(), |
162 | 1.34M | prio: 0, |
163 | 1.34M | spill_weight_and_props: 0, |
164 | 1.34M | }); |
165 | 1.34M | LiveBundleIndex::new(bundle) |
166 | 1.34M | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::create_bundle Line | Count | Source | 156 | 1.34M | pub fn create_bundle(&mut self) -> LiveBundleIndex { | 157 | 1.34M | let bundle = self.bundles.len(); | 158 | 1.34M | self.bundles.push(LiveBundle { | 159 | 1.34M | allocation: Allocation::none(), | 160 | 1.34M | ranges: smallvec![], | 161 | 1.34M | spillset: SpillSetIndex::invalid(), | 162 | 1.34M | prio: 0, | 163 | 1.34M | spill_weight_and_props: 0, | 164 | 1.34M | }); | 165 | 1.34M | LiveBundleIndex::new(bundle) | 166 | 1.34M | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::create_bundle Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::create_bundle Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::create_bundle |
167 | | |
168 | 1.43M | pub fn create_liverange(&mut self, range: CodeRange) -> LiveRangeIndex { |
169 | 1.43M | let idx = self.ranges.len(); |
170 | 1.43M | |
171 | 1.43M | self.ranges.push(LiveRange { |
172 | 1.43M | range, |
173 | 1.43M | vreg: VRegIndex::invalid(), |
174 | 1.43M | bundle: LiveBundleIndex::invalid(), |
175 | 1.43M | uses_spill_weight_and_flags: 0, |
176 | 1.43M | |
177 | 1.43M | uses: smallvec![], |
178 | | |
179 | 1.43M | merged_into: LiveRangeIndex::invalid(), |
180 | 1.43M | }); |
181 | 1.43M | |
182 | 1.43M | LiveRangeIndex::new(idx) |
183 | 1.43M | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::create_liverange Line | Count | Source | 168 | 1.43M | pub fn create_liverange(&mut self, range: CodeRange) -> LiveRangeIndex { | 169 | 1.43M | let idx = self.ranges.len(); | 170 | 1.43M | | 171 | 1.43M | self.ranges.push(LiveRange { | 172 | 1.43M | range, | 173 | 1.43M | vreg: VRegIndex::invalid(), | 174 | 1.43M | bundle: LiveBundleIndex::invalid(), | 175 | 1.43M | uses_spill_weight_and_flags: 0, | 176 | 1.43M | | 177 | 1.43M | uses: smallvec![], | 178 | | | 179 | 1.43M | merged_into: LiveRangeIndex::invalid(), | 180 | 1.43M | }); | 181 | 1.43M | | 182 | 1.43M | LiveRangeIndex::new(idx) | 183 | 1.43M | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::create_liverange Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::create_liverange Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::create_liverange |
184 | | |
185 | | /// Mark `range` as live for the given `vreg`. |
186 | | /// |
187 | | /// Returns the liverange that contains the given range. |
188 | 1.78M | pub fn add_liverange_to_vreg( |
189 | 1.78M | &mut self, |
190 | 1.78M | vreg: VRegIndex, |
191 | 1.78M | mut range: CodeRange, |
192 | 1.78M | ) -> LiveRangeIndex { |
193 | 1.78M | trace!("add_liverange_to_vreg: vreg {:?} range {:?}", vreg, range); |
194 | | |
195 | | // Invariant: as we are building liveness information, we |
196 | | // *always* process instructions bottom-to-top, and as a |
197 | | // consequence, new liveranges are always created before any |
198 | | // existing liveranges for a given vreg. We assert this here, |
199 | | // then use it to avoid an O(n) merge step (which would lead |
200 | | // to O(n^2) liveness construction cost overall). |
201 | | // |
202 | | // We store liveranges in reverse order in the `.ranges` |
203 | | // array, then reverse them at the end of |
204 | | // `compute_liveness()`. |
205 | | |
206 | 1.78M | if !self.vregs[vreg.index()].ranges.is_empty() { |
207 | 628k | let last_range_index = self.vregs[vreg.index()].ranges.last().unwrap().index; |
208 | 628k | let last_range = self.ranges[last_range_index.index()].range; |
209 | 628k | if self.func.allow_multiple_vreg_defs() { |
210 | 628k | if last_range.contains(&range) { |
211 | | // Special case (may occur when multiple defs of pinned |
212 | | // physical regs occur): if this new range overlaps the |
213 | | // existing range, return it. |
214 | 0 | return last_range_index; |
215 | 628k | } |
216 | 628k | // If this range's end falls in the middle of the last |
217 | 628k | // range, truncate it to be contiguous so we can merge |
218 | 628k | // below. |
219 | 628k | if range.to >= last_range.from && range.to <= last_range.to { |
220 | 548k | range.to = last_range.from; |
221 | 548k | } |
222 | 0 | } |
223 | 628k | debug_assert!( |
224 | 0 | range.to <= last_range.from, |
225 | 0 | "range {:?}, last_range {:?}", |
226 | | range, |
227 | | last_range |
228 | | ); |
229 | 1.15M | } |
230 | | |
231 | 1.78M | if self.vregs[vreg.index()].ranges.is_empty() |
232 | 628k | || range.to |
233 | 628k | < self.ranges[self.vregs[vreg.index()] |
234 | 628k | .ranges |
235 | 628k | .last() |
236 | 628k | .unwrap() |
237 | 628k | .index |
238 | 628k | .index()] |
239 | 628k | .range |
240 | 628k | .from |
241 | | { |
242 | | // Is not contiguous with previously-added (immediately |
243 | | // following) range; create a new range. |
244 | 1.23M | let lr = self.create_liverange(range); |
245 | 1.23M | self.ranges[lr.index()].vreg = vreg; |
246 | 1.23M | self.vregs[vreg.index()] |
247 | 1.23M | .ranges |
248 | 1.23M | .push(LiveRangeListEntry { range, index: lr }); |
249 | 1.23M | lr |
250 | | } else { |
251 | | // Is contiguous with previously-added range; just extend |
252 | | // its range and return it. |
253 | 548k | let lr = self.vregs[vreg.index()].ranges.last().unwrap().index; |
254 | 548k | debug_assert!(range.to == self.ranges[lr.index()].range.from); |
255 | 548k | self.ranges[lr.index()].range.from = range.from; |
256 | 548k | lr |
257 | | } |
258 | 1.78M | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::add_liverange_to_vreg Line | Count | Source | 188 | 1.78M | pub fn add_liverange_to_vreg( | 189 | 1.78M | &mut self, | 190 | 1.78M | vreg: VRegIndex, | 191 | 1.78M | mut range: CodeRange, | 192 | 1.78M | ) -> LiveRangeIndex { | 193 | 1.78M | trace!("add_liverange_to_vreg: vreg {:?} range {:?}", vreg, range); | 194 | | | 195 | | // Invariant: as we are building liveness information, we | 196 | | // *always* process instructions bottom-to-top, and as a | 197 | | // consequence, new liveranges are always created before any | 198 | | // existing liveranges for a given vreg. We assert this here, | 199 | | // then use it to avoid an O(n) merge step (which would lead | 200 | | // to O(n^2) liveness construction cost overall). | 201 | | // | 202 | | // We store liveranges in reverse order in the `.ranges` | 203 | | // array, then reverse them at the end of | 204 | | // `compute_liveness()`. | 205 | | | 206 | 1.78M | if !self.vregs[vreg.index()].ranges.is_empty() { | 207 | 628k | let last_range_index = self.vregs[vreg.index()].ranges.last().unwrap().index; | 208 | 628k | let last_range = self.ranges[last_range_index.index()].range; | 209 | 628k | if self.func.allow_multiple_vreg_defs() { | 210 | 628k | if last_range.contains(&range) { | 211 | | // Special case (may occur when multiple defs of pinned | 212 | | // physical regs occur): if this new range overlaps the | 213 | | // existing range, return it. | 214 | 0 | return last_range_index; | 215 | 628k | } | 216 | 628k | // If this range's end falls in the middle of the last | 217 | 628k | // range, truncate it to be contiguous so we can merge | 218 | 628k | // below. | 219 | 628k | if range.to >= last_range.from && range.to <= last_range.to { | 220 | 548k | range.to = last_range.from; | 221 | 548k | } | 222 | 0 | } | 223 | 628k | debug_assert!( | 224 | 0 | range.to <= last_range.from, | 225 | 0 | "range {:?}, last_range {:?}", | 226 | | range, | 227 | | last_range | 228 | | ); | 229 | 1.15M | } | 230 | | | 231 | 1.78M | if self.vregs[vreg.index()].ranges.is_empty() | 232 | 628k | || range.to | 233 | 628k | < self.ranges[self.vregs[vreg.index()] | 234 | 628k | .ranges | 235 | 628k | .last() | 236 | 628k | .unwrap() | 237 | 628k | .index | 238 | 628k | .index()] | 239 | 628k | .range | 240 | 628k | .from | 241 | | { | 242 | | // Is not contiguous with previously-added (immediately | 243 | | // following) range; create a new range. | 244 | 1.23M | let lr = self.create_liverange(range); | 245 | 1.23M | self.ranges[lr.index()].vreg = vreg; | 246 | 1.23M | self.vregs[vreg.index()] | 247 | 1.23M | .ranges | 248 | 1.23M | .push(LiveRangeListEntry { range, index: lr }); | 249 | 1.23M | lr | 250 | | } else { | 251 | | // Is contiguous with previously-added range; just extend | 252 | | // its range and return it. | 253 | 548k | let lr = self.vregs[vreg.index()].ranges.last().unwrap().index; | 254 | 548k | debug_assert!(range.to == self.ranges[lr.index()].range.from); | 255 | 548k | self.ranges[lr.index()].range.from = range.from; | 256 | 548k | lr | 257 | | } | 258 | 1.78M | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::add_liverange_to_vreg Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::add_liverange_to_vreg Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::add_liverange_to_vreg |
259 | | |
260 | 2.15M | pub fn insert_use_into_liverange(&mut self, into: LiveRangeIndex, mut u: Use) { |
261 | 2.15M | let operand = u.operand; |
262 | 2.15M | let constraint = operand.constraint(); |
263 | 2.15M | let block = self.cfginfo.insn_block[u.pos.inst().index()]; |
264 | 2.15M | let loop_depth = self.cfginfo.approx_loop_depth[block.index()] as usize; |
265 | 2.15M | let weight = spill_weight_from_constraint( |
266 | 2.15M | constraint, |
267 | 2.15M | loop_depth, |
268 | 2.15M | operand.kind() != OperandKind::Use, |
269 | 2.15M | ); |
270 | 2.15M | u.weight = weight.to_bits(); |
271 | 2.15M | |
272 | 2.15M | trace!( |
273 | 0 | "insert use {:?} into lr {:?} with weight {:?}", |
274 | | u, |
275 | | into, |
276 | | weight, |
277 | | ); |
278 | | |
279 | | // N.B.: we do *not* update `requirement` on the range, |
280 | | // because those will be computed during the multi-fixed-reg |
281 | | // fixup pass later (after all uses are inserted). |
282 | | |
283 | 2.15M | self.ranges[into.index()].uses.push(u); |
284 | 2.15M | |
285 | 2.15M | // Update stats. |
286 | 2.15M | let range_weight = self.ranges[into.index()].uses_spill_weight() + weight; |
287 | 2.15M | self.ranges[into.index()].set_uses_spill_weight(range_weight); |
288 | 2.15M | trace!( |
289 | 0 | " -> now range has weight {:?}", |
290 | 0 | self.ranges[into.index()].uses_spill_weight(), |
291 | | ); |
292 | 2.15M | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::insert_use_into_liverange Line | Count | Source | 260 | 2.15M | pub fn insert_use_into_liverange(&mut self, into: LiveRangeIndex, mut u: Use) { | 261 | 2.15M | let operand = u.operand; | 262 | 2.15M | let constraint = operand.constraint(); | 263 | 2.15M | let block = self.cfginfo.insn_block[u.pos.inst().index()]; | 264 | 2.15M | let loop_depth = self.cfginfo.approx_loop_depth[block.index()] as usize; | 265 | 2.15M | let weight = spill_weight_from_constraint( | 266 | 2.15M | constraint, | 267 | 2.15M | loop_depth, | 268 | 2.15M | operand.kind() != OperandKind::Use, | 269 | 2.15M | ); | 270 | 2.15M | u.weight = weight.to_bits(); | 271 | 2.15M | | 272 | 2.15M | trace!( | 273 | 0 | "insert use {:?} into lr {:?} with weight {:?}", | 274 | | u, | 275 | | into, | 276 | | weight, | 277 | | ); | 278 | | | 279 | | // N.B.: we do *not* update `requirement` on the range, | 280 | | // because those will be computed during the multi-fixed-reg | 281 | | // fixup pass later (after all uses are inserted). | 282 | | | 283 | 2.15M | self.ranges[into.index()].uses.push(u); | 284 | 2.15M | | 285 | 2.15M | // Update stats. | 286 | 2.15M | let range_weight = self.ranges[into.index()].uses_spill_weight() + weight; | 287 | 2.15M | self.ranges[into.index()].set_uses_spill_weight(range_weight); | 288 | 2.15M | trace!( | 289 | 0 | " -> now range has weight {:?}", | 290 | 0 | self.ranges[into.index()].uses_spill_weight(), | 291 | | ); | 292 | 2.15M | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::insert_use_into_liverange Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::insert_use_into_liverange Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::insert_use_into_liverange |
293 | | |
294 | 0 | pub fn find_vreg_liverange_for_pos( |
295 | 0 | &self, |
296 | 0 | vreg: VRegIndex, |
297 | 0 | pos: ProgPoint, |
298 | 0 | ) -> Option<LiveRangeIndex> { |
299 | 0 | for entry in &self.vregs[vreg.index()].ranges { |
300 | 0 | if entry.range.contains_point(pos) { |
301 | 0 | return Some(entry.index); |
302 | 0 | } |
303 | | } |
304 | 0 | None |
305 | 0 | } |
306 | | |
307 | 2.61M | pub fn add_liverange_to_preg(&mut self, range: CodeRange, reg: PReg) { |
308 | 2.61M | trace!("adding liverange to preg: {:?} to {}", range, reg); |
309 | 2.61M | let preg_idx = PRegIndex::new(reg.index()); |
310 | 2.61M | self.pregs[preg_idx.index()] |
311 | 2.61M | .allocations |
312 | 2.61M | .btree |
313 | 2.61M | .insert(LiveRangeKey::from_range(&range), LiveRangeIndex::invalid()); |
314 | 2.61M | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::add_liverange_to_preg Line | Count | Source | 307 | 2.61M | pub fn add_liverange_to_preg(&mut self, range: CodeRange, reg: PReg) { | 308 | 2.61M | trace!("adding liverange to preg: {:?} to {}", range, reg); | 309 | 2.61M | let preg_idx = PRegIndex::new(reg.index()); | 310 | 2.61M | self.pregs[preg_idx.index()] | 311 | 2.61M | .allocations | 312 | 2.61M | .btree | 313 | 2.61M | .insert(LiveRangeKey::from_range(&range), LiveRangeIndex::invalid()); | 314 | 2.61M | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::add_liverange_to_preg Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::add_liverange_to_preg Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::add_liverange_to_preg |
315 | | |
316 | 735k | pub fn is_live_in(&mut self, block: Block, vreg: VRegIndex) -> bool { |
317 | 735k | self.liveins[block.index()].get(vreg.index()) |
318 | 735k | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::is_live_in Line | Count | Source | 316 | 735k | pub fn is_live_in(&mut self, block: Block, vreg: VRegIndex) -> bool { | 317 | 735k | self.liveins[block.index()].get(vreg.index()) | 318 | 735k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::is_live_in Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::is_live_in Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::is_live_in |
319 | | |
320 | 139k | pub fn compute_liveness(&mut self) -> Result<(), RegAllocError> { |
321 | 139k | // Create initial LiveIn and LiveOut bitsets. |
322 | 336k | for _ in 0..self.func.num_blocks() { |
323 | 336k | self.liveins.push(IndexSet::new()); |
324 | 336k | self.liveouts.push(IndexSet::new()); |
325 | 336k | } |
326 | | |
327 | | // Run a worklist algorithm to precisely compute liveins and |
328 | | // liveouts. |
329 | 139k | let mut workqueue = VecDeque::new(); |
330 | 139k | let mut workqueue_set = FxHashSet::default(); |
331 | | // Initialize workqueue with postorder traversal. |
332 | 336k | for &block in &self.cfginfo.postorder[..] { |
333 | 336k | workqueue.push_back(block); |
334 | 336k | workqueue_set.insert(block); |
335 | 336k | } |
336 | | |
337 | 478k | while !workqueue.is_empty() { |
338 | 339k | let block = workqueue.pop_front().unwrap(); |
339 | 339k | workqueue_set.remove(&block); |
340 | 339k | let insns = self.func.block_insns(block); |
341 | 339k | |
342 | 339k | trace!("computing liveins for block{}", block.index()); |
343 | | |
344 | 339k | self.stats.livein_iterations += 1; |
345 | 339k | |
346 | 339k | let mut live = self.liveouts[block.index()].clone(); |
347 | 339k | trace!(" -> initial liveout set: {:?}", live); |
348 | | |
349 | | // Include outgoing blockparams in the initial live set. |
350 | 339k | if self.func.is_branch(insns.last()) { |
351 | 207k | for i in 0..self.func.block_succs(block).len() { |
352 | 207k | for ¶m in self.func.branch_blockparams(block, insns.last(), i) { |
353 | 384 | live.set(param.vreg(), true); |
354 | 384 | self.observe_vreg_class(param); |
355 | 384 | } |
356 | | } |
357 | 161k | } |
358 | | |
359 | 1.28M | for inst in insns.rev().iter() { |
360 | 1.28M | if let Some((src, dst)) = self.func.is_move(inst) { |
361 | 2.88k | live.set(dst.vreg().vreg(), false); |
362 | 2.88k | live.set(src.vreg().vreg(), true); |
363 | 2.88k | self.observe_vreg_class(src.vreg()); |
364 | 2.88k | self.observe_vreg_class(dst.vreg()); |
365 | 1.27M | } |
366 | | |
367 | 3.84M | for pos in &[OperandPos::Late, OperandPos::Early] { |
368 | 4.36M | for op in self.func.inst_operands(inst) { |
369 | 4.36M | if op.as_fixed_nonallocatable().is_some() { |
370 | 9.87k | continue; |
371 | 4.35M | } |
372 | 4.35M | if op.pos() == *pos { |
373 | 2.17M | let was_live = live.get(op.vreg().vreg()); |
374 | 2.17M | trace!("op {:?} was_live = {}", op, was_live); |
375 | 2.17M | match op.kind() { |
376 | 1.01M | OperandKind::Use | OperandKind::Mod => { |
377 | 1.01M | live.set(op.vreg().vreg(), true); |
378 | 1.01M | } |
379 | 1.16M | OperandKind::Def => { |
380 | 1.16M | live.set(op.vreg().vreg(), false); |
381 | 1.16M | } |
382 | | } |
383 | 2.17M | self.observe_vreg_class(op.vreg()); |
384 | 2.17M | } |
385 | | } |
386 | | } |
387 | | } |
388 | 510k | for &blockparam in self.func.block_params(block) { |
389 | 510k | live.set(blockparam.vreg(), false); |
390 | 510k | self.observe_vreg_class(blockparam); |
391 | 510k | } |
392 | | |
393 | 339k | for &pred in self.func.block_preds(block) { |
394 | 207k | if self.liveouts[pred.index()].union_with(&live) { |
395 | 43.0k | if !workqueue_set.contains(&pred) { |
396 | 2.50k | workqueue_set.insert(pred); |
397 | 2.50k | workqueue.push_back(pred); |
398 | 40.5k | } |
399 | 164k | } |
400 | | } |
401 | | |
402 | 339k | trace!("computed liveins at block{}: {:?}", block.index(), live); |
403 | 339k | self.liveins[block.index()] = live; |
404 | | } |
405 | | |
406 | | // Check that there are no liveins to the entry block, except |
407 | | // for pinned vregs. (The client should create a virtual |
408 | | // instruction that defines any other liveins if necessary.) |
409 | 139k | for livein in self.liveins[self.func.entry_block().index()].iter() { |
410 | 0 | let livein = self.vreg(VRegIndex::new(livein)); |
411 | 0 | if self.func.is_pinned_vreg(livein).is_none() { |
412 | 0 | trace!("non-pinned-vreg livein to entry block: {}", livein); |
413 | 0 | return Err(RegAllocError::EntryLivein); |
414 | 0 | } |
415 | | } |
416 | | |
417 | 139k | Ok(()) |
418 | 139k | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::compute_liveness Line | Count | Source | 320 | 139k | pub fn compute_liveness(&mut self) -> Result<(), RegAllocError> { | 321 | 139k | // Create initial LiveIn and LiveOut bitsets. | 322 | 336k | for _ in 0..self.func.num_blocks() { | 323 | 336k | self.liveins.push(IndexSet::new()); | 324 | 336k | self.liveouts.push(IndexSet::new()); | 325 | 336k | } | 326 | | | 327 | | // Run a worklist algorithm to precisely compute liveins and | 328 | | // liveouts. | 329 | 139k | let mut workqueue = VecDeque::new(); | 330 | 139k | let mut workqueue_set = FxHashSet::default(); | 331 | | // Initialize workqueue with postorder traversal. | 332 | 336k | for &block in &self.cfginfo.postorder[..] { | 333 | 336k | workqueue.push_back(block); | 334 | 336k | workqueue_set.insert(block); | 335 | 336k | } | 336 | | | 337 | 478k | while !workqueue.is_empty() { | 338 | 339k | let block = workqueue.pop_front().unwrap(); | 339 | 339k | workqueue_set.remove(&block); | 340 | 339k | let insns = self.func.block_insns(block); | 341 | 339k | | 342 | 339k | trace!("computing liveins for block{}", block.index()); | 343 | | | 344 | 339k | self.stats.livein_iterations += 1; | 345 | 339k | | 346 | 339k | let mut live = self.liveouts[block.index()].clone(); | 347 | 339k | trace!(" -> initial liveout set: {:?}", live); | 348 | | | 349 | | // Include outgoing blockparams in the initial live set. | 350 | 339k | if self.func.is_branch(insns.last()) { | 351 | 207k | for i in 0..self.func.block_succs(block).len() { | 352 | 207k | for ¶m in self.func.branch_blockparams(block, insns.last(), i) { | 353 | 384 | live.set(param.vreg(), true); | 354 | 384 | self.observe_vreg_class(param); | 355 | 384 | } | 356 | | } | 357 | 161k | } | 358 | | | 359 | 1.28M | for inst in insns.rev().iter() { | 360 | 1.28M | if let Some((src, dst)) = self.func.is_move(inst) { | 361 | 2.88k | live.set(dst.vreg().vreg(), false); | 362 | 2.88k | live.set(src.vreg().vreg(), true); | 363 | 2.88k | self.observe_vreg_class(src.vreg()); | 364 | 2.88k | self.observe_vreg_class(dst.vreg()); | 365 | 1.27M | } | 366 | | | 367 | 3.84M | for pos in &[OperandPos::Late, OperandPos::Early] { | 368 | 4.36M | for op in self.func.inst_operands(inst) { | 369 | 4.36M | if op.as_fixed_nonallocatable().is_some() { | 370 | 9.87k | continue; | 371 | 4.35M | } | 372 | 4.35M | if op.pos() == *pos { | 373 | 2.17M | let was_live = live.get(op.vreg().vreg()); | 374 | 2.17M | trace!("op {:?} was_live = {}", op, was_live); | 375 | 2.17M | match op.kind() { | 376 | 1.01M | OperandKind::Use | OperandKind::Mod => { | 377 | 1.01M | live.set(op.vreg().vreg(), true); | 378 | 1.01M | } | 379 | 1.16M | OperandKind::Def => { | 380 | 1.16M | live.set(op.vreg().vreg(), false); | 381 | 1.16M | } | 382 | | } | 383 | 2.17M | self.observe_vreg_class(op.vreg()); | 384 | 2.17M | } | 385 | | } | 386 | | } | 387 | | } | 388 | 510k | for &blockparam in self.func.block_params(block) { | 389 | 510k | live.set(blockparam.vreg(), false); | 390 | 510k | self.observe_vreg_class(blockparam); | 391 | 510k | } | 392 | | | 393 | 339k | for &pred in self.func.block_preds(block) { | 394 | 207k | if self.liveouts[pred.index()].union_with(&live) { | 395 | 43.0k | if !workqueue_set.contains(&pred) { | 396 | 2.50k | workqueue_set.insert(pred); | 397 | 2.50k | workqueue.push_back(pred); | 398 | 40.5k | } | 399 | 164k | } | 400 | | } | 401 | | | 402 | 339k | trace!("computed liveins at block{}: {:?}", block.index(), live); | 403 | 339k | self.liveins[block.index()] = live; | 404 | | } | 405 | | | 406 | | // Check that there are no liveins to the entry block, except | 407 | | // for pinned vregs. (The client should create a virtual | 408 | | // instruction that defines any other liveins if necessary.) | 409 | 139k | for livein in self.liveins[self.func.entry_block().index()].iter() { | 410 | 0 | let livein = self.vreg(VRegIndex::new(livein)); | 411 | 0 | if self.func.is_pinned_vreg(livein).is_none() { | 412 | 0 | trace!("non-pinned-vreg livein to entry block: {}", livein); | 413 | 0 | return Err(RegAllocError::EntryLivein); | 414 | 0 | } | 415 | | } | 416 | | | 417 | 139k | Ok(()) | 418 | 139k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::compute_liveness Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::compute_liveness Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::compute_liveness |
419 | | |
420 | 139k | pub fn build_liveranges(&mut self) { |
421 | 139k | for &vreg in self.func.reftype_vregs() { |
422 | 0 | self.safepoints_per_vreg.insert(vreg.vreg(), HashSet::new()); |
423 | 0 | } |
424 | | |
425 | | // Create Uses and Defs referring to VRegs, and place the Uses |
426 | | // in LiveRanges. |
427 | | // |
428 | | // We already computed precise liveouts and liveins for every |
429 | | // block above, so we don't need to run an iterative algorithm |
430 | | // here; instead, every block's computation is purely local, |
431 | | // from end to start. |
432 | | |
433 | | // Track current LiveRange for each vreg. |
434 | | // |
435 | | // Invariant: a stale range may be present here; ranges are |
436 | | // only valid if `live.get(vreg)` is true. |
437 | 139k | let mut vreg_ranges: Vec<LiveRangeIndex> = |
438 | 139k | vec![LiveRangeIndex::invalid(); self.func.num_vregs()]; |
439 | | |
440 | 336k | for i in (0..self.func.num_blocks()).rev() { |
441 | 336k | let block = Block::new(i); |
442 | 336k | let insns = self.func.block_insns(block); |
443 | 336k | |
444 | 336k | self.stats.livein_blocks += 1; |
445 | 336k | |
446 | 336k | // Init our local live-in set. |
447 | 336k | let mut live = self.liveouts[block.index()].clone(); |
448 | 336k | |
449 | 336k | // If the last instruction is a branch (rather than |
450 | 336k | // return), create blockparam_out entries. |
451 | 336k | if self.func.is_branch(insns.last()) { |
452 | 204k | for (i, &succ) in self.func.block_succs(block).iter().enumerate() { |
453 | 204k | let blockparams_in = self.func.block_params(succ); |
454 | 204k | let blockparams_out = self.func.branch_blockparams(block, insns.last(), i); |
455 | 330 | for (&blockparam_in, &blockparam_out) in |
456 | 204k | blockparams_in.iter().zip(blockparams_out) |
457 | 330 | { |
458 | 330 | let blockparam_out = VRegIndex::new(blockparam_out.vreg()); |
459 | 330 | let blockparam_in = VRegIndex::new(blockparam_in.vreg()); |
460 | 330 | self.blockparam_outs.push(BlockparamOut { |
461 | 330 | to_vreg: blockparam_in, |
462 | 330 | to_block: succ, |
463 | 330 | from_block: block, |
464 | 330 | from_vreg: blockparam_out, |
465 | 330 | }); |
466 | 330 | |
467 | 330 | // Include outgoing blockparams in the initial live set. |
468 | 330 | live.set(blockparam_out.index(), true); |
469 | 330 | } |
470 | | } |
471 | 161k | } |
472 | | |
473 | | // Initially, registers are assumed live for the whole block. |
474 | 336k | for vreg in live.iter() { |
475 | 119k | let range = CodeRange { |
476 | 119k | from: self.cfginfo.block_entry[block.index()], |
477 | 119k | to: self.cfginfo.block_exit[block.index()].next(), |
478 | 119k | }; |
479 | 119k | trace!( |
480 | 0 | "vreg {:?} live at end of block --> create range {:?}", |
481 | 0 | VRegIndex::new(vreg), |
482 | | range |
483 | | ); |
484 | 119k | let lr = self.add_liverange_to_vreg(VRegIndex::new(vreg), range); |
485 | 119k | vreg_ranges[vreg] = lr; |
486 | | } |
487 | | |
488 | | // Create vreg data for blockparams. |
489 | 510k | for ¶m in self.func.block_params(block) { |
490 | 510k | self.vregs[param.vreg()].blockparam = block; |
491 | 510k | } |
492 | | |
493 | | // For each instruction, in reverse order, process |
494 | | // operands and clobbers. |
495 | 1.27M | for inst in insns.rev().iter() { |
496 | | // Mark clobbers with CodeRanges on PRegs. |
497 | 2.56M | for clobber in self.func.inst_clobbers(inst) { |
498 | 2.56M | // Clobber range is at After point only: an |
499 | 2.56M | // instruction can still take an input in a reg |
500 | 2.56M | // that it later clobbers. (In other words, the |
501 | 2.56M | // clobber is like a normal def that never gets |
502 | 2.56M | // used.) |
503 | 2.56M | let range = CodeRange { |
504 | 2.56M | from: ProgPoint::after(inst), |
505 | 2.56M | to: ProgPoint::before(inst.next()), |
506 | 2.56M | }; |
507 | 2.56M | self.add_liverange_to_preg(range, clobber); |
508 | 2.56M | } |
509 | | |
510 | | // Does the instruction have any input-reusing |
511 | | // outputs? This is important below to establish |
512 | | // proper interference wrt other inputs. We note the |
513 | | // *vreg* that is reused, not the index. |
514 | 1.27M | let mut reused_input = None; |
515 | 2.14M | for op in self.func.inst_operands(inst) { |
516 | 2.14M | if let OperandConstraint::Reuse(i) = op.constraint() { |
517 | 100k | debug_assert!(self.func.inst_operands(inst)[i] |
518 | 0 | .as_fixed_nonallocatable() |
519 | 0 | .is_none()); |
520 | 100k | reused_input = Some(self.func.inst_operands(inst)[i].vreg()); |
521 | 100k | break; |
522 | 2.04M | } |
523 | | } |
524 | | |
525 | | // If this is a move, handle specially. |
526 | 1.27M | if let Some((src, dst)) = self.func.is_move(inst) { |
527 | | // We can completely skip the move if it is |
528 | | // trivial (vreg to same vreg). |
529 | 2.85k | if src.vreg() != dst.vreg() { |
530 | 2.85k | trace!(" -> move inst{}: src {} -> dst {}", inst.index(), src, dst); |
531 | | |
532 | 2.85k | debug_assert_eq!(src.class(), dst.class()); |
533 | 2.85k | debug_assert_eq!(src.kind(), OperandKind::Use); |
534 | 2.85k | debug_assert_eq!(src.pos(), OperandPos::Early); |
535 | 2.85k | debug_assert_eq!(dst.kind(), OperandKind::Def); |
536 | 2.85k | debug_assert_eq!(dst.pos(), OperandPos::Late); |
537 | | |
538 | 2.85k | let src_pinned = self.func.is_pinned_vreg(src.vreg()); |
539 | 2.85k | let dst_pinned = self.func.is_pinned_vreg(dst.vreg()); |
540 | 2.85k | |
541 | 2.85k | match (src_pinned, dst_pinned) { |
542 | | // If both src and dest are pinned, emit |
543 | | // the move right here, right now. |
544 | 0 | (Some(src_preg), Some(dst_preg)) => { |
545 | 0 | // Update LRs. |
546 | 0 | if !live.get(src.vreg().vreg()) { |
547 | 0 | let lr = self.add_liverange_to_vreg( |
548 | 0 | VRegIndex::new(src.vreg().vreg()), |
549 | 0 | CodeRange { |
550 | 0 | from: self.cfginfo.block_entry[block.index()], |
551 | 0 | to: ProgPoint::after(inst), |
552 | 0 | }, |
553 | 0 | ); |
554 | 0 | live.set(src.vreg().vreg(), true); |
555 | 0 | vreg_ranges[src.vreg().vreg()] = lr; |
556 | 0 | } |
557 | 0 | if live.get(dst.vreg().vreg()) { |
558 | 0 | let lr = vreg_ranges[dst.vreg().vreg()]; |
559 | 0 | self.ranges[lr.index()].range.from = ProgPoint::after(inst); |
560 | 0 | live.set(dst.vreg().vreg(), false); |
561 | 0 | } else { |
562 | 0 | self.add_liverange_to_vreg( |
563 | 0 | VRegIndex::new(dst.vreg().vreg()), |
564 | 0 | CodeRange { |
565 | 0 | from: ProgPoint::after(inst), |
566 | 0 | to: ProgPoint::before(inst.next()), |
567 | 0 | }, |
568 | 0 | ); |
569 | 0 | } |
570 | | |
571 | 0 | self.insert_move( |
572 | 0 | ProgPoint::before(inst), |
573 | 0 | InsertMovePrio::MultiFixedRegInitial, |
574 | 0 | Allocation::reg(src_preg), |
575 | 0 | Allocation::reg(dst_preg), |
576 | 0 | dst.vreg(), |
577 | 0 | ); |
578 | | } |
579 | | |
580 | | // If exactly one of source and dest (but |
581 | | // not both) is a pinned-vreg, convert |
582 | | // this into a ghost use on the other vreg |
583 | | // with a FixedReg constraint. |
584 | 0 | (Some(preg), None) | (None, Some(preg)) => { |
585 | 0 | trace!( |
586 | 0 | " -> exactly one of src/dst is pinned; converting to ghost use" |
587 | | ); |
588 | 0 | let (vreg, pinned_vreg, kind, pos, progpoint) = |
589 | 0 | if src_pinned.is_some() { |
590 | | // Source is pinned: this is a def on the dst with a pinned preg. |
591 | 0 | ( |
592 | 0 | dst.vreg(), |
593 | 0 | src.vreg(), |
594 | 0 | OperandKind::Def, |
595 | 0 | OperandPos::Late, |
596 | 0 | ProgPoint::before(inst), |
597 | 0 | ) |
598 | | } else { |
599 | | // Dest is pinned: this is a use on the src with a pinned preg. |
600 | 0 | ( |
601 | 0 | src.vreg(), |
602 | 0 | dst.vreg(), |
603 | 0 | OperandKind::Use, |
604 | 0 | OperandPos::Early, |
605 | 0 | ProgPoint::after(inst), |
606 | 0 | ) |
607 | | }; |
608 | 0 | let constraint = OperandConstraint::FixedReg(preg); |
609 | 0 | let operand = Operand::new(vreg, constraint, kind, pos); |
610 | 0 |
|
611 | 0 | trace!( |
612 | 0 | concat!( |
613 | 0 | " -> preg {:?} vreg {:?} kind {:?} ", |
614 | 0 | "pos {:?} progpoint {:?} constraint {:?} operand {:?}" |
615 | 0 | ), |
616 | | preg, |
617 | | vreg, |
618 | | kind, |
619 | | pos, |
620 | | progpoint, |
621 | | constraint, |
622 | | operand |
623 | | ); |
624 | | |
625 | | // Get the LR for the vreg; if none, create one. |
626 | 0 | let mut lr = vreg_ranges[vreg.vreg()]; |
627 | 0 | if !live.get(vreg.vreg()) { |
628 | 0 | let from = match kind { |
629 | 0 | OperandKind::Use => self.cfginfo.block_entry[block.index()], |
630 | 0 | OperandKind::Def => progpoint, |
631 | 0 | _ => unreachable!(), |
632 | | }; |
633 | 0 | let to = progpoint.next(); |
634 | 0 | lr = self.add_liverange_to_vreg( |
635 | 0 | VRegIndex::new(vreg.vreg()), |
636 | 0 | CodeRange { from, to }, |
637 | 0 | ); |
638 | 0 | trace!(" -> dead; created LR"); |
639 | 0 | } |
640 | 0 | trace!(" -> LR {:?}", lr); |
641 | | |
642 | 0 | self.insert_use_into_liverange( |
643 | 0 | lr, |
644 | 0 | Use::new(operand, progpoint, SLOT_NONE), |
645 | 0 | ); |
646 | 0 |
|
647 | 0 | if kind == OperandKind::Def { |
648 | 0 | live.set(vreg.vreg(), false); |
649 | 0 | if self.ranges[lr.index()].range.from |
650 | 0 | == self.cfginfo.block_entry[block.index()] |
651 | 0 | { |
652 | 0 | self.ranges[lr.index()].range.from = progpoint; |
653 | 0 | } |
654 | 0 | self.ranges[lr.index()].set_flag(LiveRangeFlag::StartsAtDef); |
655 | 0 | } else { |
656 | 0 | live.set(vreg.vreg(), true); |
657 | 0 | vreg_ranges[vreg.vreg()] = lr; |
658 | 0 | } |
659 | | |
660 | | // Handle liveness of the other vreg. Note |
661 | | // that this is somewhat special. For the |
662 | | // destination case, we want the pinned |
663 | | // vreg's LR to start just *after* the |
664 | | // operand we inserted above, because |
665 | | // otherwise it would overlap, and |
666 | | // interfere, and prevent allocation. For |
667 | | // the source case, we want to "poke a |
668 | | // hole" in the LR: if it's live going |
669 | | // downward, end it just after the operand |
670 | | // and restart it before; if it isn't |
671 | | // (this is the last use), start it |
672 | | // before. |
673 | 0 | if kind == OperandKind::Def { |
674 | 0 | trace!(" -> src on pinned vreg {:?}", pinned_vreg); |
675 | | // The *other* vreg is a def, so the pinned-vreg |
676 | | // mention is a use. If already live, |
677 | | // end the existing LR just *after* |
678 | | // the `progpoint` defined above and |
679 | | // start a new one just *before* the |
680 | | // `progpoint` defined above, |
681 | | // preserving the start. If not, start |
682 | | // a new one live back to the top of |
683 | | // the block, starting just before |
684 | | // `progpoint`. |
685 | 0 | if live.get(pinned_vreg.vreg()) { |
686 | 0 | let pinned_lr = vreg_ranges[pinned_vreg.vreg()]; |
687 | 0 | let orig_start = self.ranges[pinned_lr.index()].range.from; |
688 | 0 | // Following instruction start |
689 | 0 | // (so we don't transition in |
690 | 0 | // middle of inst). |
691 | 0 | let new_start = ProgPoint::before(progpoint.inst().next()); |
692 | 0 | trace!( |
693 | 0 | " -> live with LR {:?}; truncating to start at {:?}", |
694 | | pinned_lr, |
695 | | new_start, |
696 | | ); |
697 | 0 | self.ranges[pinned_lr.index()].range.from = new_start; |
698 | 0 |
|
699 | 0 | let new_lr = self.add_liverange_to_vreg( |
700 | 0 | VRegIndex::new(pinned_vreg.vreg()), |
701 | 0 | CodeRange { |
702 | 0 | from: orig_start, |
703 | 0 | to: progpoint, |
704 | 0 | }, |
705 | 0 | ); |
706 | 0 | vreg_ranges[pinned_vreg.vreg()] = new_lr; |
707 | 0 | trace!(" -> created LR {:?} with remaining range from {:?} to {:?}", new_lr, orig_start, progpoint); |
708 | | |
709 | | // Add an edit right now to indicate that at |
710 | | // this program point, the given |
711 | | // preg is now known as that vreg, |
712 | | // not the preg, but immediately |
713 | | // after, it is known as the preg |
714 | | // again. This is used by the |
715 | | // checker. |
716 | 0 | self.insert_move( |
717 | 0 | ProgPoint::after(inst), |
718 | 0 | InsertMovePrio::Regular, |
719 | 0 | Allocation::reg(preg), |
720 | 0 | Allocation::reg(preg), |
721 | 0 | dst.vreg(), |
722 | 0 | ); |
723 | 0 | self.insert_move( |
724 | 0 | ProgPoint::before(inst.next()), |
725 | 0 | InsertMovePrio::MultiFixedRegInitial, |
726 | 0 | Allocation::reg(preg), |
727 | 0 | Allocation::reg(preg), |
728 | 0 | src.vreg(), |
729 | 0 | ); |
730 | | } else { |
731 | 0 | if inst > self.cfginfo.block_entry[block.index()].inst() { |
732 | 0 | let new_lr = self.add_liverange_to_vreg( |
733 | 0 | VRegIndex::new(pinned_vreg.vreg()), |
734 | 0 | CodeRange { |
735 | 0 | from: self.cfginfo.block_entry[block.index()], |
736 | 0 | to: progpoint, |
737 | 0 | }, |
738 | 0 | ); |
739 | 0 | vreg_ranges[pinned_vreg.vreg()] = new_lr; |
740 | 0 | live.set(pinned_vreg.vreg(), true); |
741 | 0 | trace!(" -> was not live; created new LR {:?}", new_lr); |
742 | 0 | } |
743 | | |
744 | | // Add an edit right now to indicate that at |
745 | | // this program point, the given |
746 | | // preg is now known as that vreg, |
747 | | // not the preg. This is used by |
748 | | // the checker. |
749 | 0 | self.insert_move( |
750 | 0 | ProgPoint::after(inst), |
751 | 0 | InsertMovePrio::BlockParam, |
752 | 0 | Allocation::reg(preg), |
753 | 0 | Allocation::reg(preg), |
754 | 0 | dst.vreg(), |
755 | 0 | ); |
756 | | } |
757 | | } else { |
758 | 0 | trace!(" -> dst on pinned vreg {:?}", pinned_vreg); |
759 | | // The *other* vreg is a use, so the pinned-vreg |
760 | | // mention is a def. Truncate its LR |
761 | | // just *after* the `progpoint` |
762 | | // defined above. |
763 | 0 | if live.get(pinned_vreg.vreg()) { |
764 | 0 | let pinned_lr = vreg_ranges[pinned_vreg.vreg()]; |
765 | 0 | self.ranges[pinned_lr.index()].range.from = |
766 | 0 | progpoint.next(); |
767 | 0 | trace!( |
768 | 0 | " -> was live with LR {:?}; truncated start to {:?}", |
769 | 0 | pinned_lr, |
770 | 0 | progpoint.next() |
771 | | ); |
772 | 0 | live.set(pinned_vreg.vreg(), false); |
773 | 0 |
|
774 | 0 | // Add a no-op edit right now to indicate that |
775 | 0 | // at this program point, the |
776 | 0 | // given preg is now known as that |
777 | 0 | // preg, not the vreg. This is |
778 | 0 | // used by the checker. |
779 | 0 | self.insert_move( |
780 | 0 | ProgPoint::before(inst.next()), |
781 | 0 | InsertMovePrio::PostRegular, |
782 | 0 | Allocation::reg(preg), |
783 | 0 | Allocation::reg(preg), |
784 | 0 | dst.vreg(), |
785 | 0 | ); |
786 | 0 | } |
787 | | // Otherwise, if dead, no need to create |
788 | | // a dummy LR -- there is no |
789 | | // reservation to make (the other vreg |
790 | | // will land in the reg with the |
791 | | // fixed-reg operand constraint, but |
792 | | // it's a dead move anyway). |
793 | | } |
794 | | } |
795 | | |
796 | | // Ordinary move between two non-pinned vregs. |
797 | | (None, None) => { |
798 | | // Redefine src and dst operands to have |
799 | | // positions of After and Before respectively |
800 | | // (see note below), and to have Any |
801 | | // constraints if they were originally Reg. |
802 | 2.85k | let src_constraint = match src.constraint() { |
803 | 2.85k | OperandConstraint::Reg => OperandConstraint::Any, |
804 | 0 | x => x, |
805 | | }; |
806 | 2.85k | let dst_constraint = match dst.constraint() { |
807 | 2.85k | OperandConstraint::Reg => OperandConstraint::Any, |
808 | 0 | x => x, |
809 | | }; |
810 | 2.85k | let src = Operand::new( |
811 | 2.85k | src.vreg(), |
812 | 2.85k | src_constraint, |
813 | 2.85k | OperandKind::Use, |
814 | 2.85k | OperandPos::Late, |
815 | 2.85k | ); |
816 | 2.85k | let dst = Operand::new( |
817 | 2.85k | dst.vreg(), |
818 | 2.85k | dst_constraint, |
819 | 2.85k | OperandKind::Def, |
820 | 2.85k | OperandPos::Early, |
821 | 2.85k | ); |
822 | 2.85k | |
823 | 2.85k | if self.annotations_enabled { |
824 | 0 | self.annotate( |
825 | 0 | ProgPoint::after(inst), |
826 | 0 | format!( |
827 | 0 | " prog-move v{} ({:?}) -> v{} ({:?})", |
828 | 0 | src.vreg().vreg(), |
829 | 0 | src_constraint, |
830 | 0 | dst.vreg().vreg(), |
831 | 0 | dst_constraint, |
832 | 0 | ), |
833 | 0 | ); |
834 | 2.85k | } |
835 | | |
836 | | // N.B.: in order to integrate with the move |
837 | | // resolution that joins LRs in general, we |
838 | | // conceptually treat the move as happening |
839 | | // between the move inst's After and the next |
840 | | // inst's Before. Thus the src LR goes up to |
841 | | // (exclusive) next-inst-pre, and the dst LR |
842 | | // starts at next-inst-pre. We have to take |
843 | | // care in our move insertion to handle this |
844 | | // like other inter-inst moves, i.e., at |
845 | | // `Regular` priority, so it properly happens |
846 | | // in parallel with other inter-LR moves. |
847 | | // |
848 | | // Why the progpoint between move and next |
849 | | // inst, and not the progpoint between prev |
850 | | // inst and move? Because a move can be the |
851 | | // first inst in a block, but cannot be the |
852 | | // last; so the following progpoint is always |
853 | | // within the same block, while the previous |
854 | | // one may be an inter-block point (and the |
855 | | // After of the prev inst in a different |
856 | | // block). |
857 | | |
858 | | // Handle the def w.r.t. liveranges: trim the |
859 | | // start of the range and mark it dead at this |
860 | | // point in our backward scan. |
861 | 2.85k | let pos = ProgPoint::before(inst.next()); |
862 | 2.85k | let mut dst_lr = vreg_ranges[dst.vreg().vreg()]; |
863 | 2.85k | if !live.get(dst.vreg().vreg()) { |
864 | 730 | let from = pos; |
865 | 730 | let to = pos.next(); |
866 | 730 | dst_lr = self.add_liverange_to_vreg( |
867 | 730 | VRegIndex::new(dst.vreg().vreg()), |
868 | 730 | CodeRange { from, to }, |
869 | 730 | ); |
870 | 730 | trace!(" -> invalid LR for def; created {:?}", dst_lr); |
871 | 2.12k | } |
872 | 2.85k | trace!(" -> has existing LR {:?}", dst_lr); |
873 | | // Trim the LR to start here. |
874 | 2.85k | if self.ranges[dst_lr.index()].range.from |
875 | 2.85k | == self.cfginfo.block_entry[block.index()] |
876 | | { |
877 | 2.12k | trace!(" -> started at block start; trimming to {:?}", pos); |
878 | 2.12k | self.ranges[dst_lr.index()].range.from = pos; |
879 | 730 | } |
880 | 2.85k | self.ranges[dst_lr.index()].set_flag(LiveRangeFlag::StartsAtDef); |
881 | 2.85k | live.set(dst.vreg().vreg(), false); |
882 | 2.85k | vreg_ranges[dst.vreg().vreg()] = LiveRangeIndex::invalid(); |
883 | 2.85k | |
884 | 2.85k | // Handle the use w.r.t. liveranges: make it live |
885 | 2.85k | // and create an initial LR back to the start of |
886 | 2.85k | // the block. |
887 | 2.85k | let pos = ProgPoint::after(inst); |
888 | 2.85k | let src_lr = if !live.get(src.vreg().vreg()) { |
889 | 2.85k | let range = CodeRange { |
890 | 2.85k | from: self.cfginfo.block_entry[block.index()], |
891 | 2.85k | to: pos.next(), |
892 | 2.85k | }; |
893 | 2.85k | let src_lr = self.add_liverange_to_vreg( |
894 | 2.85k | VRegIndex::new(src.vreg().vreg()), |
895 | 2.85k | range, |
896 | 2.85k | ); |
897 | 2.85k | vreg_ranges[src.vreg().vreg()] = src_lr; |
898 | 2.85k | src_lr |
899 | | } else { |
900 | 0 | vreg_ranges[src.vreg().vreg()] |
901 | | }; |
902 | | |
903 | 2.85k | trace!(" -> src LR {:?}", src_lr); |
904 | | |
905 | | // Add to live-set. |
906 | 2.85k | let src_is_dead_after_move = !live.get(src.vreg().vreg()); |
907 | 2.85k | live.set(src.vreg().vreg(), true); |
908 | 2.85k | |
909 | 2.85k | // Add to program-moves lists. |
910 | 2.85k | self.prog_move_srcs.push(( |
911 | 2.85k | (VRegIndex::new(src.vreg().vreg()), inst), |
912 | 2.85k | Allocation::none(), |
913 | 2.85k | )); |
914 | 2.85k | self.prog_move_dsts.push(( |
915 | 2.85k | (VRegIndex::new(dst.vreg().vreg()), inst.next()), |
916 | 2.85k | Allocation::none(), |
917 | 2.85k | )); |
918 | 2.85k | self.stats.prog_moves += 1; |
919 | 2.85k | if src_is_dead_after_move { |
920 | 2.85k | self.stats.prog_moves_dead_src += 1; |
921 | 2.85k | self.prog_move_merges.push((src_lr, dst_lr)); |
922 | 2.85k | } |
923 | | } |
924 | | } |
925 | 0 | } |
926 | | |
927 | 2.85k | continue; |
928 | 1.27M | } |
929 | 1.27M | |
930 | 1.27M | // Preprocess defs and uses. Specifically, if there |
931 | 1.27M | // are any fixed-reg-constrained defs at Late position |
932 | 1.27M | // and fixed-reg-constrained uses at Early position |
933 | 1.27M | // with the same preg, we need to (i) add a fixup move |
934 | 1.27M | // for the use, (ii) rewrite the use to have an Any |
935 | 1.27M | // constraint, and (ii) move the def to Early position |
936 | 1.27M | // to reserve the register for the whole instruction. |
937 | 1.27M | let mut operand_rewrites: FxHashMap<usize, Operand> = FxHashMap::default(); |
938 | 1.27M | let mut late_def_fixed: SmallVec<[PReg; 8]> = smallvec![]; |
939 | 2.16M | for &operand in self.func.inst_operands(inst) { |
940 | 2.16M | if let OperandConstraint::FixedReg(preg) = operand.constraint() { |
941 | 713k | match operand.pos() { |
942 | | OperandPos::Late => { |
943 | | // See note in fuzzing/func.rs: we |
944 | | // can't allow this, because there |
945 | | // would be no way to move a value |
946 | | // into place for a late use *after* |
947 | | // the early point (i.e. in the middle |
948 | | // of the instruction). |
949 | 494k | assert!( |
950 | 494k | operand.kind() == OperandKind::Def, |
951 | 494k | "Invalid operand: fixed constraint on Use/Mod at Late point" |
952 | 494k | ); |
953 | | |
954 | 494k | late_def_fixed.push(preg); |
955 | | } |
956 | 219k | _ => {} |
957 | | } |
958 | 1.44M | } |
959 | | } |
960 | 2.16M | for (i, &operand) in self.func.inst_operands(inst).iter().enumerate() { |
961 | 2.16M | if operand.as_fixed_nonallocatable().is_some() { |
962 | 4.93k | continue; |
963 | 2.15M | } |
964 | 2.15M | if let OperandConstraint::FixedReg(preg) = operand.constraint() { |
965 | 708k | match operand.pos() { |
966 | 214k | OperandPos::Early if live.get(operand.vreg().vreg()) => { |
967 | 43.6k | assert!(operand.kind() == OperandKind::Use, |
968 | 43.6k | "Invalid operand: fixed constraint on Def/Mod at Early position"); |
969 | | |
970 | | // If we have a constraint at the |
971 | | // Early point for a fixed preg, and |
972 | | // this preg is also constrained with |
973 | | // a *separate* def at Late or is |
974 | | // clobbered, and *if* the vreg is |
975 | | // live downward, we have to use the |
976 | | // multi-fixed-reg mechanism for a |
977 | | // fixup and rewrite here without the |
978 | | // constraint. See #53. |
979 | | // |
980 | | // We adjust the def liverange and Use |
981 | | // to an "early" position to reserve |
982 | | // the register, it still must not be |
983 | | // used by some other vreg at the |
984 | | // use-site. |
985 | | // |
986 | | // Note that we handle multiple |
987 | | // conflicting constraints for the |
988 | | // same vreg in a separate pass (see |
989 | | // `fixup_multi_fixed_vregs` below). |
990 | 43.6k | if late_def_fixed.contains(&preg) |
991 | 43.3k | || self.func.inst_clobbers(inst).contains(preg) |
992 | | { |
993 | 43.2k | log::trace!( |
994 | 0 | concat!( |
995 | 0 | "-> operand {:?} is fixed to preg {:?}, ", |
996 | 0 | "is downward live, and there is also a ", |
997 | 0 | "def or clobber at this preg" |
998 | 0 | ), |
999 | | operand, |
1000 | | preg |
1001 | | ); |
1002 | 43.2k | let pos = ProgPoint::before(inst); |
1003 | 43.2k | self.multi_fixed_reg_fixups.push(MultiFixedRegFixup { |
1004 | 43.2k | pos, |
1005 | 43.2k | from_slot: i as u8, |
1006 | 43.2k | to_slot: i as u8, |
1007 | 43.2k | to_preg: PRegIndex::new(preg.index()), |
1008 | 43.2k | vreg: VRegIndex::new(operand.vreg().vreg()), |
1009 | 43.2k | level: FixedRegFixupLevel::Initial, |
1010 | 43.2k | }); |
1011 | 43.2k | |
1012 | 43.2k | // We need to insert a reservation |
1013 | 43.2k | // at the before-point to reserve |
1014 | 43.2k | // the reg for the use too. |
1015 | 43.2k | let range = CodeRange::singleton(pos); |
1016 | 43.2k | self.add_liverange_to_preg(range, preg); |
1017 | 43.2k | |
1018 | 43.2k | // Remove the fixed-preg |
1019 | 43.2k | // constraint from the Use. |
1020 | 43.2k | operand_rewrites.insert( |
1021 | 43.2k | i, |
1022 | 43.2k | Operand::new( |
1023 | 43.2k | operand.vreg(), |
1024 | 43.2k | OperandConstraint::Any, |
1025 | 43.2k | operand.kind(), |
1026 | 43.2k | operand.pos(), |
1027 | 43.2k | ), |
1028 | 43.2k | ); |
1029 | 446 | } |
1030 | | } |
1031 | 664k | _ => {} |
1032 | | } |
1033 | 1.44M | } |
1034 | | } |
1035 | | |
1036 | | // Process defs and uses. |
1037 | 3.81M | for &cur_pos in &[InstPosition::After, InstPosition::Before] { |
1038 | 4.32M | for i in 0..self.func.inst_operands(inst).len() { |
1039 | | // don't borrow `self` |
1040 | 4.32M | let operand = operand_rewrites |
1041 | 4.32M | .get(&i) |
1042 | 4.32M | .cloned() |
1043 | 4.32M | .unwrap_or(self.func.inst_operands(inst)[i]); |
1044 | 4.32M | let pos = match (operand.kind(), operand.pos()) { |
1045 | 0 | (OperandKind::Mod, _) => ProgPoint::before(inst), |
1046 | 224k | (OperandKind::Def, OperandPos::Early) => ProgPoint::before(inst), |
1047 | 2.07M | (OperandKind::Def, OperandPos::Late) => ProgPoint::after(inst), |
1048 | 0 | (OperandKind::Use, OperandPos::Late) => ProgPoint::after(inst), |
1049 | | // If there are any reused inputs in this |
1050 | | // instruction, and this is *not* the |
1051 | | // reused vreg, force `pos` to |
1052 | | // `After`. This ensures that we correctly |
1053 | | // account for the interference between |
1054 | | // the other inputs and the |
1055 | | // input-that-is-reused/output. |
1056 | | (OperandKind::Use, OperandPos::Early) |
1057 | 2.02M | if reused_input.is_some() |
1058 | 253k | && reused_input.unwrap() != operand.vreg() => |
1059 | 51.8k | { |
1060 | 51.8k | ProgPoint::after(inst) |
1061 | | } |
1062 | 1.97M | (OperandKind::Use, OperandPos::Early) => ProgPoint::before(inst), |
1063 | | }; |
1064 | | |
1065 | 4.32M | if pos.pos() != cur_pos { |
1066 | 2.16M | continue; |
1067 | 2.16M | } |
1068 | 2.16M | |
1069 | 2.16M | trace!( |
1070 | 0 | "processing inst{} operand at {:?}: {:?}", |
1071 | 0 | inst.index(), |
1072 | | pos, |
1073 | | operand |
1074 | | ); |
1075 | | |
1076 | | // If this is a "fixed non-allocatable |
1077 | | // register" operand, set the alloc |
1078 | | // immediately and then ignore the operand |
1079 | | // hereafter. |
1080 | 2.16M | if let Some(preg) = operand.as_fixed_nonallocatable() { |
1081 | 4.93k | self.set_alloc(inst, i, Allocation::reg(preg)); |
1082 | 4.93k | continue; |
1083 | 2.15M | } |
1084 | 2.15M | |
1085 | 2.15M | match operand.kind() { |
1086 | | OperandKind::Def | OperandKind::Mod => { |
1087 | 1.15M | trace!("Def of {} at {:?}", operand.vreg(), pos); |
1088 | | |
1089 | | // Get or create the LiveRange. |
1090 | 1.15M | let mut lr = vreg_ranges[operand.vreg().vreg()]; |
1091 | 1.15M | trace!(" -> has existing LR {:?}", lr); |
1092 | | // If there was no liverange (dead def), create a trivial one. |
1093 | 1.15M | if !live.get(operand.vreg().vreg()) { |
1094 | 468k | let from = match operand.kind() { |
1095 | 468k | OperandKind::Def => pos, |
1096 | 0 | OperandKind::Mod => self.cfginfo.block_entry[block.index()], |
1097 | 0 | _ => unreachable!(), |
1098 | | }; |
1099 | | // We want to we want to span |
1100 | | // until Before of the next |
1101 | | // inst. This ensures that early |
1102 | | // defs used for temps on an |
1103 | | // instruction are reserved across |
1104 | | // the whole instruction. |
1105 | 468k | let to = ProgPoint::before(pos.inst().next()); |
1106 | 468k | lr = self.add_liverange_to_vreg( |
1107 | 468k | VRegIndex::new(operand.vreg().vreg()), |
1108 | 468k | CodeRange { from, to }, |
1109 | 468k | ); |
1110 | 468k | trace!(" -> invalid; created {:?}", lr); |
1111 | 468k | vreg_ranges[operand.vreg().vreg()] = lr; |
1112 | 468k | live.set(operand.vreg().vreg(), true); |
1113 | 682k | } |
1114 | | // Create the use in the LiveRange. |
1115 | 1.15M | self.insert_use_into_liverange(lr, Use::new(operand, pos, i as u8)); |
1116 | 1.15M | // If def (not mod), this reg is now dead, |
1117 | 1.15M | // scanning backward; make it so. |
1118 | 1.15M | if operand.kind() == OperandKind::Def { |
1119 | | // Trim the range for this vreg to start |
1120 | | // at `pos` if it previously ended at the |
1121 | | // start of this block (i.e. was not |
1122 | | // merged into some larger LiveRange due |
1123 | | // to out-of-order blocks). |
1124 | 1.15M | if self.ranges[lr.index()].range.from |
1125 | 1.15M | == self.cfginfo.block_entry[block.index()] |
1126 | | { |
1127 | 684k | trace!(" -> started at block start; trimming to {:?}", pos); |
1128 | 684k | self.ranges[lr.index()].range.from = pos; |
1129 | 466k | } |
1130 | | |
1131 | 1.15M | self.ranges[lr.index()].set_flag(LiveRangeFlag::StartsAtDef); |
1132 | 1.15M | |
1133 | 1.15M | // Remove from live-set. |
1134 | 1.15M | live.set(operand.vreg().vreg(), false); |
1135 | 1.15M | vreg_ranges[operand.vreg().vreg()] = LiveRangeIndex::invalid(); |
1136 | 0 | } |
1137 | | } |
1138 | | OperandKind::Use => { |
1139 | | // Create/extend the LiveRange if it |
1140 | | // doesn't already exist, and add the use |
1141 | | // to the range. |
1142 | 1.00M | let mut lr = vreg_ranges[operand.vreg().vreg()]; |
1143 | 1.00M | if !live.get(operand.vreg().vreg()) { |
1144 | 680k | let range = CodeRange { |
1145 | 680k | from: self.cfginfo.block_entry[block.index()], |
1146 | 680k | to: pos.next(), |
1147 | 680k | }; |
1148 | 680k | lr = self.add_liverange_to_vreg( |
1149 | 680k | VRegIndex::new(operand.vreg().vreg()), |
1150 | 680k | range, |
1151 | 680k | ); |
1152 | 680k | vreg_ranges[operand.vreg().vreg()] = lr; |
1153 | 680k | } |
1154 | 1.00M | debug_assert!(lr.is_valid()); |
1155 | | |
1156 | 1.00M | trace!("Use of {:?} at {:?} -> {:?}", operand, pos, lr,); |
1157 | | |
1158 | 1.00M | self.insert_use_into_liverange(lr, Use::new(operand, pos, i as u8)); |
1159 | 1.00M | |
1160 | 1.00M | // Add to live-set. |
1161 | 1.00M | live.set(operand.vreg().vreg(), true); |
1162 | | } |
1163 | | } |
1164 | | } |
1165 | | } |
1166 | | |
1167 | 1.27M | if self.func.requires_refs_on_stack(inst) { |
1168 | 166k | trace!("inst{} is safepoint", inst.index()); |
1169 | 166k | self.safepoints.push(inst); |
1170 | 342k | for vreg in live.iter() { |
1171 | 342k | if let Some(safepoints) = self.safepoints_per_vreg.get_mut(&vreg) { |
1172 | 0 | trace!("vreg v{} live at safepoint inst{}", vreg, inst.index()); |
1173 | 0 | safepoints.insert(inst); |
1174 | 342k | } |
1175 | | } |
1176 | 1.10M | } |
1177 | | } |
1178 | | |
1179 | | // Block parameters define vregs at the very beginning of |
1180 | | // the block. Remove their live vregs from the live set |
1181 | | // here. |
1182 | 510k | for vreg in self.func.block_params(block) { |
1183 | 510k | if live.get(vreg.vreg()) { |
1184 | 208 | live.set(vreg.vreg(), false); |
1185 | 510k | } else { |
1186 | 510k | // Create trivial liverange if blockparam is dead. |
1187 | 510k | let start = self.cfginfo.block_entry[block.index()]; |
1188 | 510k | self.add_liverange_to_vreg( |
1189 | 510k | VRegIndex::new(vreg.vreg()), |
1190 | 510k | CodeRange { |
1191 | 510k | from: start, |
1192 | 510k | to: start.next(), |
1193 | 510k | }, |
1194 | 510k | ); |
1195 | 510k | } |
1196 | | // add `blockparam_ins` entries. |
1197 | 510k | let vreg_idx = VRegIndex::new(vreg.vreg()); |
1198 | 510k | for &pred in self.func.block_preds(block) { |
1199 | 330 | self.blockparam_ins.push(BlockparamIn { |
1200 | 330 | to_vreg: vreg_idx, |
1201 | 330 | to_block: block, |
1202 | 330 | from_block: pred, |
1203 | 330 | }); |
1204 | 330 | } |
1205 | | } |
1206 | | } |
1207 | | |
1208 | 139k | self.safepoints.sort_unstable(); |
1209 | | |
1210 | | // Make ranges in each vreg and uses in each range appear in |
1211 | | // sorted order. We built them in reverse order above, so this |
1212 | | // is a simple reversal, *not* a full sort. |
1213 | | // |
1214 | | // The ordering invariant is always maintained for uses and |
1215 | | // always for ranges in bundles (which are initialized later), |
1216 | | // but not always for ranges in vregs; those are sorted only |
1217 | | // when needed, here and then again at the end of allocation |
1218 | | // when resolving moves. |
1219 | | |
1220 | 19.8M | for vreg in &mut self.vregs { |
1221 | 19.7M | vreg.ranges.reverse(); |
1222 | 19.7M | let mut last = None; |
1223 | 20.9M | for entry in &mut vreg.ranges { |
1224 | | // Ranges may have been truncated above at defs. We |
1225 | | // need to update with the final range here. |
1226 | 1.23M | entry.range = self.ranges[entry.index.index()].range; |
1227 | 1.23M | // Assert in-order and non-overlapping. |
1228 | 1.23M | debug_assert!(last.is_none() || last.unwrap() <= entry.range.from); |
1229 | 1.23M | last = Some(entry.range.to); |
1230 | | } |
1231 | | } |
1232 | | |
1233 | 1.23M | for range in 0..self.ranges.len() { |
1234 | 1.23M | self.ranges[range].uses.reverse(); |
1235 | 1.23M | debug_assert!(self.ranges[range] |
1236 | 0 | .uses |
1237 | 0 | .windows(2) |
1238 | 0 | .all(|win| win[0].pos <= win[1].pos)); |
1239 | | } |
1240 | | |
1241 | | // Insert safepoint virtual stack uses, if needed. |
1242 | 139k | for &vreg in self.func.reftype_vregs() { |
1243 | 0 | if self.func.is_pinned_vreg(vreg).is_some() { |
1244 | 0 | continue; |
1245 | 0 | } |
1246 | 0 | let vreg = VRegIndex::new(vreg.vreg()); |
1247 | 0 | let mut inserted = false; |
1248 | 0 | let mut safepoint_idx = 0; |
1249 | 0 | for range_idx in 0..self.vregs[vreg.index()].ranges.len() { |
1250 | 0 | let LiveRangeListEntry { range, index } = |
1251 | 0 | self.vregs[vreg.index()].ranges[range_idx]; |
1252 | 0 | while safepoint_idx < self.safepoints.len() |
1253 | 0 | && ProgPoint::before(self.safepoints[safepoint_idx]) < range.from |
1254 | 0 | { |
1255 | 0 | safepoint_idx += 1; |
1256 | 0 | } |
1257 | 0 | while safepoint_idx < self.safepoints.len() |
1258 | 0 | && range.contains_point(ProgPoint::before(self.safepoints[safepoint_idx])) |
1259 | | { |
1260 | | // Create a virtual use. |
1261 | 0 | let pos = ProgPoint::before(self.safepoints[safepoint_idx]); |
1262 | 0 | let operand = Operand::new( |
1263 | 0 | self.vreg(vreg), |
1264 | 0 | OperandConstraint::Stack, |
1265 | 0 | OperandKind::Use, |
1266 | 0 | OperandPos::Early, |
1267 | 0 | ); |
1268 | 0 |
|
1269 | 0 | trace!( |
1270 | 0 | "Safepoint-induced stack use of {:?} at {:?} -> {:?}", |
1271 | | operand, |
1272 | | pos, |
1273 | | index, |
1274 | | ); |
1275 | | |
1276 | 0 | self.insert_use_into_liverange(index, Use::new(operand, pos, SLOT_NONE)); |
1277 | 0 | safepoint_idx += 1; |
1278 | 0 |
|
1279 | 0 | inserted = true; |
1280 | | } |
1281 | | |
1282 | 0 | if inserted { |
1283 | 0 | self.ranges[index.index()] |
1284 | 0 | .uses |
1285 | 0 | .sort_unstable_by_key(|u| u.pos); Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::build_liveranges::{closure#0}Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::build_liveranges::{closure#0}Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::build_liveranges::{closure#0}Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::build_liveranges::{closure#0} |
1286 | 0 | } |
1287 | | |
1288 | 0 | if safepoint_idx >= self.safepoints.len() { |
1289 | 0 | break; |
1290 | 0 | } |
1291 | | } |
1292 | | } |
1293 | | |
1294 | 139k | self.blockparam_ins.sort_unstable_by_key(|x| x.key()); <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::build_liveranges::{closure#1}Line | Count | Source | 1294 | 1.29k | self.blockparam_ins.sort_unstable_by_key(|x| x.key()); |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::build_liveranges::{closure#1}Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::build_liveranges::{closure#1}Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::build_liveranges::{closure#1} |
1295 | 139k | self.blockparam_outs.sort_unstable_by_key(|x| x.key()); <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::build_liveranges::{closure#2}Line | Count | Source | 1295 | 1.42k | self.blockparam_outs.sort_unstable_by_key(|x| x.key()); |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::build_liveranges::{closure#2}Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::build_liveranges::{closure#2}Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::build_liveranges::{closure#2} |
1296 | 139k | self.prog_move_srcs.sort_unstable_by_key(|(pos, _)| *pos); <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::build_liveranges::{closure#3}Line | Count | Source | 1296 | 4.39k | self.prog_move_srcs.sort_unstable_by_key(|(pos, _)| *pos); |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::build_liveranges::{closure#3}Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::build_liveranges::{closure#3}Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::build_liveranges::{closure#3} |
1297 | 139k | self.prog_move_dsts.sort_unstable_by_key(|(pos, _)| *pos); <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::build_liveranges::{closure#4}Line | Count | Source | 1297 | 4.39k | self.prog_move_dsts.sort_unstable_by_key(|(pos, _)| *pos); |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::build_liveranges::{closure#4}Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::build_liveranges::{closure#4}Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::build_liveranges::{closure#4} |
1298 | 139k | |
1299 | 139k | trace!("prog_move_srcs = {:?}", self.prog_move_srcs); |
1300 | 139k | trace!("prog_move_dsts = {:?}", self.prog_move_dsts); |
1301 | | |
1302 | 139k | self.stats.initial_liverange_count = self.ranges.len(); |
1303 | 139k | self.stats.blockparam_ins_count = self.blockparam_ins.len(); |
1304 | 139k | self.stats.blockparam_outs_count = self.blockparam_outs.len(); |
1305 | 139k | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::build_liveranges Line | Count | Source | 420 | 139k | pub fn build_liveranges(&mut self) { | 421 | 139k | for &vreg in self.func.reftype_vregs() { | 422 | 0 | self.safepoints_per_vreg.insert(vreg.vreg(), HashSet::new()); | 423 | 0 | } | 424 | | | 425 | | // Create Uses and Defs referring to VRegs, and place the Uses | 426 | | // in LiveRanges. | 427 | | // | 428 | | // We already computed precise liveouts and liveins for every | 429 | | // block above, so we don't need to run an iterative algorithm | 430 | | // here; instead, every block's computation is purely local, | 431 | | // from end to start. | 432 | | | 433 | | // Track current LiveRange for each vreg. | 434 | | // | 435 | | // Invariant: a stale range may be present here; ranges are | 436 | | // only valid if `live.get(vreg)` is true. | 437 | 139k | let mut vreg_ranges: Vec<LiveRangeIndex> = | 438 | 139k | vec![LiveRangeIndex::invalid(); self.func.num_vregs()]; | 439 | | | 440 | 336k | for i in (0..self.func.num_blocks()).rev() { | 441 | 336k | let block = Block::new(i); | 442 | 336k | let insns = self.func.block_insns(block); | 443 | 336k | | 444 | 336k | self.stats.livein_blocks += 1; | 445 | 336k | | 446 | 336k | // Init our local live-in set. | 447 | 336k | let mut live = self.liveouts[block.index()].clone(); | 448 | 336k | | 449 | 336k | // If the last instruction is a branch (rather than | 450 | 336k | // return), create blockparam_out entries. | 451 | 336k | if self.func.is_branch(insns.last()) { | 452 | 204k | for (i, &succ) in self.func.block_succs(block).iter().enumerate() { | 453 | 204k | let blockparams_in = self.func.block_params(succ); | 454 | 204k | let blockparams_out = self.func.branch_blockparams(block, insns.last(), i); | 455 | 330 | for (&blockparam_in, &blockparam_out) in | 456 | 204k | blockparams_in.iter().zip(blockparams_out) | 457 | 330 | { | 458 | 330 | let blockparam_out = VRegIndex::new(blockparam_out.vreg()); | 459 | 330 | let blockparam_in = VRegIndex::new(blockparam_in.vreg()); | 460 | 330 | self.blockparam_outs.push(BlockparamOut { | 461 | 330 | to_vreg: blockparam_in, | 462 | 330 | to_block: succ, | 463 | 330 | from_block: block, | 464 | 330 | from_vreg: blockparam_out, | 465 | 330 | }); | 466 | 330 | | 467 | 330 | // Include outgoing blockparams in the initial live set. | 468 | 330 | live.set(blockparam_out.index(), true); | 469 | 330 | } | 470 | | } | 471 | 161k | } | 472 | | | 473 | | // Initially, registers are assumed live for the whole block. | 474 | 336k | for vreg in live.iter() { | 475 | 119k | let range = CodeRange { | 476 | 119k | from: self.cfginfo.block_entry[block.index()], | 477 | 119k | to: self.cfginfo.block_exit[block.index()].next(), | 478 | 119k | }; | 479 | 119k | trace!( | 480 | 0 | "vreg {:?} live at end of block --> create range {:?}", | 481 | 0 | VRegIndex::new(vreg), | 482 | | range | 483 | | ); | 484 | 119k | let lr = self.add_liverange_to_vreg(VRegIndex::new(vreg), range); | 485 | 119k | vreg_ranges[vreg] = lr; | 486 | | } | 487 | | | 488 | | // Create vreg data for blockparams. | 489 | 510k | for ¶m in self.func.block_params(block) { | 490 | 510k | self.vregs[param.vreg()].blockparam = block; | 491 | 510k | } | 492 | | | 493 | | // For each instruction, in reverse order, process | 494 | | // operands and clobbers. | 495 | 1.27M | for inst in insns.rev().iter() { | 496 | | // Mark clobbers with CodeRanges on PRegs. | 497 | 2.56M | for clobber in self.func.inst_clobbers(inst) { | 498 | 2.56M | // Clobber range is at After point only: an | 499 | 2.56M | // instruction can still take an input in a reg | 500 | 2.56M | // that it later clobbers. (In other words, the | 501 | 2.56M | // clobber is like a normal def that never gets | 502 | 2.56M | // used.) | 503 | 2.56M | let range = CodeRange { | 504 | 2.56M | from: ProgPoint::after(inst), | 505 | 2.56M | to: ProgPoint::before(inst.next()), | 506 | 2.56M | }; | 507 | 2.56M | self.add_liverange_to_preg(range, clobber); | 508 | 2.56M | } | 509 | | | 510 | | // Does the instruction have any input-reusing | 511 | | // outputs? This is important below to establish | 512 | | // proper interference wrt other inputs. We note the | 513 | | // *vreg* that is reused, not the index. | 514 | 1.27M | let mut reused_input = None; | 515 | 2.14M | for op in self.func.inst_operands(inst) { | 516 | 2.14M | if let OperandConstraint::Reuse(i) = op.constraint() { | 517 | 100k | debug_assert!(self.func.inst_operands(inst)[i] | 518 | 0 | .as_fixed_nonallocatable() | 519 | 0 | .is_none()); | 520 | 100k | reused_input = Some(self.func.inst_operands(inst)[i].vreg()); | 521 | 100k | break; | 522 | 2.04M | } | 523 | | } | 524 | | | 525 | | // If this is a move, handle specially. | 526 | 1.27M | if let Some((src, dst)) = self.func.is_move(inst) { | 527 | | // We can completely skip the move if it is | 528 | | // trivial (vreg to same vreg). | 529 | 2.85k | if src.vreg() != dst.vreg() { | 530 | 2.85k | trace!(" -> move inst{}: src {} -> dst {}", inst.index(), src, dst); | 531 | | | 532 | 2.85k | debug_assert_eq!(src.class(), dst.class()); | 533 | 2.85k | debug_assert_eq!(src.kind(), OperandKind::Use); | 534 | 2.85k | debug_assert_eq!(src.pos(), OperandPos::Early); | 535 | 2.85k | debug_assert_eq!(dst.kind(), OperandKind::Def); | 536 | 2.85k | debug_assert_eq!(dst.pos(), OperandPos::Late); | 537 | | | 538 | 2.85k | let src_pinned = self.func.is_pinned_vreg(src.vreg()); | 539 | 2.85k | let dst_pinned = self.func.is_pinned_vreg(dst.vreg()); | 540 | 2.85k | | 541 | 2.85k | match (src_pinned, dst_pinned) { | 542 | | // If both src and dest are pinned, emit | 543 | | // the move right here, right now. | 544 | 0 | (Some(src_preg), Some(dst_preg)) => { | 545 | 0 | // Update LRs. | 546 | 0 | if !live.get(src.vreg().vreg()) { | 547 | 0 | let lr = self.add_liverange_to_vreg( | 548 | 0 | VRegIndex::new(src.vreg().vreg()), | 549 | 0 | CodeRange { | 550 | 0 | from: self.cfginfo.block_entry[block.index()], | 551 | 0 | to: ProgPoint::after(inst), | 552 | 0 | }, | 553 | 0 | ); | 554 | 0 | live.set(src.vreg().vreg(), true); | 555 | 0 | vreg_ranges[src.vreg().vreg()] = lr; | 556 | 0 | } | 557 | 0 | if live.get(dst.vreg().vreg()) { | 558 | 0 | let lr = vreg_ranges[dst.vreg().vreg()]; | 559 | 0 | self.ranges[lr.index()].range.from = ProgPoint::after(inst); | 560 | 0 | live.set(dst.vreg().vreg(), false); | 561 | 0 | } else { | 562 | 0 | self.add_liverange_to_vreg( | 563 | 0 | VRegIndex::new(dst.vreg().vreg()), | 564 | 0 | CodeRange { | 565 | 0 | from: ProgPoint::after(inst), | 566 | 0 | to: ProgPoint::before(inst.next()), | 567 | 0 | }, | 568 | 0 | ); | 569 | 0 | } | 570 | | | 571 | 0 | self.insert_move( | 572 | 0 | ProgPoint::before(inst), | 573 | 0 | InsertMovePrio::MultiFixedRegInitial, | 574 | 0 | Allocation::reg(src_preg), | 575 | 0 | Allocation::reg(dst_preg), | 576 | 0 | dst.vreg(), | 577 | 0 | ); | 578 | | } | 579 | | | 580 | | // If exactly one of source and dest (but | 581 | | // not both) is a pinned-vreg, convert | 582 | | // this into a ghost use on the other vreg | 583 | | // with a FixedReg constraint. | 584 | 0 | (Some(preg), None) | (None, Some(preg)) => { | 585 | 0 | trace!( | 586 | 0 | " -> exactly one of src/dst is pinned; converting to ghost use" | 587 | | ); | 588 | 0 | let (vreg, pinned_vreg, kind, pos, progpoint) = | 589 | 0 | if src_pinned.is_some() { | 590 | | // Source is pinned: this is a def on the dst with a pinned preg. | 591 | 0 | ( | 592 | 0 | dst.vreg(), | 593 | 0 | src.vreg(), | 594 | 0 | OperandKind::Def, | 595 | 0 | OperandPos::Late, | 596 | 0 | ProgPoint::before(inst), | 597 | 0 | ) | 598 | | } else { | 599 | | // Dest is pinned: this is a use on the src with a pinned preg. | 600 | 0 | ( | 601 | 0 | src.vreg(), | 602 | 0 | dst.vreg(), | 603 | 0 | OperandKind::Use, | 604 | 0 | OperandPos::Early, | 605 | 0 | ProgPoint::after(inst), | 606 | 0 | ) | 607 | | }; | 608 | 0 | let constraint = OperandConstraint::FixedReg(preg); | 609 | 0 | let operand = Operand::new(vreg, constraint, kind, pos); | 610 | 0 |
| 611 | 0 | trace!( | 612 | 0 | concat!( | 613 | 0 | " -> preg {:?} vreg {:?} kind {:?} ", | 614 | 0 | "pos {:?} progpoint {:?} constraint {:?} operand {:?}" | 615 | 0 | ), | 616 | | preg, | 617 | | vreg, | 618 | | kind, | 619 | | pos, | 620 | | progpoint, | 621 | | constraint, | 622 | | operand | 623 | | ); | 624 | | | 625 | | // Get the LR for the vreg; if none, create one. | 626 | 0 | let mut lr = vreg_ranges[vreg.vreg()]; | 627 | 0 | if !live.get(vreg.vreg()) { | 628 | 0 | let from = match kind { | 629 | 0 | OperandKind::Use => self.cfginfo.block_entry[block.index()], | 630 | 0 | OperandKind::Def => progpoint, | 631 | 0 | _ => unreachable!(), | 632 | | }; | 633 | 0 | let to = progpoint.next(); | 634 | 0 | lr = self.add_liverange_to_vreg( | 635 | 0 | VRegIndex::new(vreg.vreg()), | 636 | 0 | CodeRange { from, to }, | 637 | 0 | ); | 638 | 0 | trace!(" -> dead; created LR"); | 639 | 0 | } | 640 | 0 | trace!(" -> LR {:?}", lr); | 641 | | | 642 | 0 | self.insert_use_into_liverange( | 643 | 0 | lr, | 644 | 0 | Use::new(operand, progpoint, SLOT_NONE), | 645 | 0 | ); | 646 | 0 |
| 647 | 0 | if kind == OperandKind::Def { | 648 | 0 | live.set(vreg.vreg(), false); | 649 | 0 | if self.ranges[lr.index()].range.from | 650 | 0 | == self.cfginfo.block_entry[block.index()] | 651 | 0 | { | 652 | 0 | self.ranges[lr.index()].range.from = progpoint; | 653 | 0 | } | 654 | 0 | self.ranges[lr.index()].set_flag(LiveRangeFlag::StartsAtDef); | 655 | 0 | } else { | 656 | 0 | live.set(vreg.vreg(), true); | 657 | 0 | vreg_ranges[vreg.vreg()] = lr; | 658 | 0 | } | 659 | | | 660 | | // Handle liveness of the other vreg. Note | 661 | | // that this is somewhat special. For the | 662 | | // destination case, we want the pinned | 663 | | // vreg's LR to start just *after* the | 664 | | // operand we inserted above, because | 665 | | // otherwise it would overlap, and | 666 | | // interfere, and prevent allocation. For | 667 | | // the source case, we want to "poke a | 668 | | // hole" in the LR: if it's live going | 669 | | // downward, end it just after the operand | 670 | | // and restart it before; if it isn't | 671 | | // (this is the last use), start it | 672 | | // before. | 673 | 0 | if kind == OperandKind::Def { | 674 | 0 | trace!(" -> src on pinned vreg {:?}", pinned_vreg); | 675 | | // The *other* vreg is a def, so the pinned-vreg | 676 | | // mention is a use. If already live, | 677 | | // end the existing LR just *after* | 678 | | // the `progpoint` defined above and | 679 | | // start a new one just *before* the | 680 | | // `progpoint` defined above, | 681 | | // preserving the start. If not, start | 682 | | // a new one live back to the top of | 683 | | // the block, starting just before | 684 | | // `progpoint`. | 685 | 0 | if live.get(pinned_vreg.vreg()) { | 686 | 0 | let pinned_lr = vreg_ranges[pinned_vreg.vreg()]; | 687 | 0 | let orig_start = self.ranges[pinned_lr.index()].range.from; | 688 | 0 | // Following instruction start | 689 | 0 | // (so we don't transition in | 690 | 0 | // middle of inst). | 691 | 0 | let new_start = ProgPoint::before(progpoint.inst().next()); | 692 | 0 | trace!( | 693 | 0 | " -> live with LR {:?}; truncating to start at {:?}", | 694 | | pinned_lr, | 695 | | new_start, | 696 | | ); | 697 | 0 | self.ranges[pinned_lr.index()].range.from = new_start; | 698 | 0 |
| 699 | 0 | let new_lr = self.add_liverange_to_vreg( | 700 | 0 | VRegIndex::new(pinned_vreg.vreg()), | 701 | 0 | CodeRange { | 702 | 0 | from: orig_start, | 703 | 0 | to: progpoint, | 704 | 0 | }, | 705 | 0 | ); | 706 | 0 | vreg_ranges[pinned_vreg.vreg()] = new_lr; | 707 | 0 | trace!(" -> created LR {:?} with remaining range from {:?} to {:?}", new_lr, orig_start, progpoint); | 708 | | | 709 | | // Add an edit right now to indicate that at | 710 | | // this program point, the given | 711 | | // preg is now known as that vreg, | 712 | | // not the preg, but immediately | 713 | | // after, it is known as the preg | 714 | | // again. This is used by the | 715 | | // checker. | 716 | 0 | self.insert_move( | 717 | 0 | ProgPoint::after(inst), | 718 | 0 | InsertMovePrio::Regular, | 719 | 0 | Allocation::reg(preg), | 720 | 0 | Allocation::reg(preg), | 721 | 0 | dst.vreg(), | 722 | 0 | ); | 723 | 0 | self.insert_move( | 724 | 0 | ProgPoint::before(inst.next()), | 725 | 0 | InsertMovePrio::MultiFixedRegInitial, | 726 | 0 | Allocation::reg(preg), | 727 | 0 | Allocation::reg(preg), | 728 | 0 | src.vreg(), | 729 | 0 | ); | 730 | | } else { | 731 | 0 | if inst > self.cfginfo.block_entry[block.index()].inst() { | 732 | 0 | let new_lr = self.add_liverange_to_vreg( | 733 | 0 | VRegIndex::new(pinned_vreg.vreg()), | 734 | 0 | CodeRange { | 735 | 0 | from: self.cfginfo.block_entry[block.index()], | 736 | 0 | to: progpoint, | 737 | 0 | }, | 738 | 0 | ); | 739 | 0 | vreg_ranges[pinned_vreg.vreg()] = new_lr; | 740 | 0 | live.set(pinned_vreg.vreg(), true); | 741 | 0 | trace!(" -> was not live; created new LR {:?}", new_lr); | 742 | 0 | } | 743 | | | 744 | | // Add an edit right now to indicate that at | 745 | | // this program point, the given | 746 | | // preg is now known as that vreg, | 747 | | // not the preg. This is used by | 748 | | // the checker. | 749 | 0 | self.insert_move( | 750 | 0 | ProgPoint::after(inst), | 751 | 0 | InsertMovePrio::BlockParam, | 752 | 0 | Allocation::reg(preg), | 753 | 0 | Allocation::reg(preg), | 754 | 0 | dst.vreg(), | 755 | 0 | ); | 756 | | } | 757 | | } else { | 758 | 0 | trace!(" -> dst on pinned vreg {:?}", pinned_vreg); | 759 | | // The *other* vreg is a use, so the pinned-vreg | 760 | | // mention is a def. Truncate its LR | 761 | | // just *after* the `progpoint` | 762 | | // defined above. | 763 | 0 | if live.get(pinned_vreg.vreg()) { | 764 | 0 | let pinned_lr = vreg_ranges[pinned_vreg.vreg()]; | 765 | 0 | self.ranges[pinned_lr.index()].range.from = | 766 | 0 | progpoint.next(); | 767 | 0 | trace!( | 768 | 0 | " -> was live with LR {:?}; truncated start to {:?}", | 769 | 0 | pinned_lr, | 770 | 0 | progpoint.next() | 771 | | ); | 772 | 0 | live.set(pinned_vreg.vreg(), false); | 773 | 0 |
| 774 | 0 | // Add a no-op edit right now to indicate that | 775 | 0 | // at this program point, the | 776 | 0 | // given preg is now known as that | 777 | 0 | // preg, not the vreg. This is | 778 | 0 | // used by the checker. | 779 | 0 | self.insert_move( | 780 | 0 | ProgPoint::before(inst.next()), | 781 | 0 | InsertMovePrio::PostRegular, | 782 | 0 | Allocation::reg(preg), | 783 | 0 | Allocation::reg(preg), | 784 | 0 | dst.vreg(), | 785 | 0 | ); | 786 | 0 | } | 787 | | // Otherwise, if dead, no need to create | 788 | | // a dummy LR -- there is no | 789 | | // reservation to make (the other vreg | 790 | | // will land in the reg with the | 791 | | // fixed-reg operand constraint, but | 792 | | // it's a dead move anyway). | 793 | | } | 794 | | } | 795 | | | 796 | | // Ordinary move between two non-pinned vregs. | 797 | | (None, None) => { | 798 | | // Redefine src and dst operands to have | 799 | | // positions of After and Before respectively | 800 | | // (see note below), and to have Any | 801 | | // constraints if they were originally Reg. | 802 | 2.85k | let src_constraint = match src.constraint() { | 803 | 2.85k | OperandConstraint::Reg => OperandConstraint::Any, | 804 | 0 | x => x, | 805 | | }; | 806 | 2.85k | let dst_constraint = match dst.constraint() { | 807 | 2.85k | OperandConstraint::Reg => OperandConstraint::Any, | 808 | 0 | x => x, | 809 | | }; | 810 | 2.85k | let src = Operand::new( | 811 | 2.85k | src.vreg(), | 812 | 2.85k | src_constraint, | 813 | 2.85k | OperandKind::Use, | 814 | 2.85k | OperandPos::Late, | 815 | 2.85k | ); | 816 | 2.85k | let dst = Operand::new( | 817 | 2.85k | dst.vreg(), | 818 | 2.85k | dst_constraint, | 819 | 2.85k | OperandKind::Def, | 820 | 2.85k | OperandPos::Early, | 821 | 2.85k | ); | 822 | 2.85k | | 823 | 2.85k | if self.annotations_enabled { | 824 | 0 | self.annotate( | 825 | 0 | ProgPoint::after(inst), | 826 | 0 | format!( | 827 | 0 | " prog-move v{} ({:?}) -> v{} ({:?})", | 828 | 0 | src.vreg().vreg(), | 829 | 0 | src_constraint, | 830 | 0 | dst.vreg().vreg(), | 831 | 0 | dst_constraint, | 832 | 0 | ), | 833 | 0 | ); | 834 | 2.85k | } | 835 | | | 836 | | // N.B.: in order to integrate with the move | 837 | | // resolution that joins LRs in general, we | 838 | | // conceptually treat the move as happening | 839 | | // between the move inst's After and the next | 840 | | // inst's Before. Thus the src LR goes up to | 841 | | // (exclusive) next-inst-pre, and the dst LR | 842 | | // starts at next-inst-pre. We have to take | 843 | | // care in our move insertion to handle this | 844 | | // like other inter-inst moves, i.e., at | 845 | | // `Regular` priority, so it properly happens | 846 | | // in parallel with other inter-LR moves. | 847 | | // | 848 | | // Why the progpoint between move and next | 849 | | // inst, and not the progpoint between prev | 850 | | // inst and move? Because a move can be the | 851 | | // first inst in a block, but cannot be the | 852 | | // last; so the following progpoint is always | 853 | | // within the same block, while the previous | 854 | | // one may be an inter-block point (and the | 855 | | // After of the prev inst in a different | 856 | | // block). | 857 | | | 858 | | // Handle the def w.r.t. liveranges: trim the | 859 | | // start of the range and mark it dead at this | 860 | | // point in our backward scan. | 861 | 2.85k | let pos = ProgPoint::before(inst.next()); | 862 | 2.85k | let mut dst_lr = vreg_ranges[dst.vreg().vreg()]; | 863 | 2.85k | if !live.get(dst.vreg().vreg()) { | 864 | 730 | let from = pos; | 865 | 730 | let to = pos.next(); | 866 | 730 | dst_lr = self.add_liverange_to_vreg( | 867 | 730 | VRegIndex::new(dst.vreg().vreg()), | 868 | 730 | CodeRange { from, to }, | 869 | 730 | ); | 870 | 730 | trace!(" -> invalid LR for def; created {:?}", dst_lr); | 871 | 2.12k | } | 872 | 2.85k | trace!(" -> has existing LR {:?}", dst_lr); | 873 | | // Trim the LR to start here. | 874 | 2.85k | if self.ranges[dst_lr.index()].range.from | 875 | 2.85k | == self.cfginfo.block_entry[block.index()] | 876 | | { | 877 | 2.12k | trace!(" -> started at block start; trimming to {:?}", pos); | 878 | 2.12k | self.ranges[dst_lr.index()].range.from = pos; | 879 | 730 | } | 880 | 2.85k | self.ranges[dst_lr.index()].set_flag(LiveRangeFlag::StartsAtDef); | 881 | 2.85k | live.set(dst.vreg().vreg(), false); | 882 | 2.85k | vreg_ranges[dst.vreg().vreg()] = LiveRangeIndex::invalid(); | 883 | 2.85k | | 884 | 2.85k | // Handle the use w.r.t. liveranges: make it live | 885 | 2.85k | // and create an initial LR back to the start of | 886 | 2.85k | // the block. | 887 | 2.85k | let pos = ProgPoint::after(inst); | 888 | 2.85k | let src_lr = if !live.get(src.vreg().vreg()) { | 889 | 2.85k | let range = CodeRange { | 890 | 2.85k | from: self.cfginfo.block_entry[block.index()], | 891 | 2.85k | to: pos.next(), | 892 | 2.85k | }; | 893 | 2.85k | let src_lr = self.add_liverange_to_vreg( | 894 | 2.85k | VRegIndex::new(src.vreg().vreg()), | 895 | 2.85k | range, | 896 | 2.85k | ); | 897 | 2.85k | vreg_ranges[src.vreg().vreg()] = src_lr; | 898 | 2.85k | src_lr | 899 | | } else { | 900 | 0 | vreg_ranges[src.vreg().vreg()] | 901 | | }; | 902 | | | 903 | 2.85k | trace!(" -> src LR {:?}", src_lr); | 904 | | | 905 | | // Add to live-set. | 906 | 2.85k | let src_is_dead_after_move = !live.get(src.vreg().vreg()); | 907 | 2.85k | live.set(src.vreg().vreg(), true); | 908 | 2.85k | | 909 | 2.85k | // Add to program-moves lists. | 910 | 2.85k | self.prog_move_srcs.push(( | 911 | 2.85k | (VRegIndex::new(src.vreg().vreg()), inst), | 912 | 2.85k | Allocation::none(), | 913 | 2.85k | )); | 914 | 2.85k | self.prog_move_dsts.push(( | 915 | 2.85k | (VRegIndex::new(dst.vreg().vreg()), inst.next()), | 916 | 2.85k | Allocation::none(), | 917 | 2.85k | )); | 918 | 2.85k | self.stats.prog_moves += 1; | 919 | 2.85k | if src_is_dead_after_move { | 920 | 2.85k | self.stats.prog_moves_dead_src += 1; | 921 | 2.85k | self.prog_move_merges.push((src_lr, dst_lr)); | 922 | 2.85k | } | 923 | | } | 924 | | } | 925 | 0 | } | 926 | | | 927 | 2.85k | continue; | 928 | 1.27M | } | 929 | 1.27M | | 930 | 1.27M | // Preprocess defs and uses. Specifically, if there | 931 | 1.27M | // are any fixed-reg-constrained defs at Late position | 932 | 1.27M | // and fixed-reg-constrained uses at Early position | 933 | 1.27M | // with the same preg, we need to (i) add a fixup move | 934 | 1.27M | // for the use, (ii) rewrite the use to have an Any | 935 | 1.27M | // constraint, and (ii) move the def to Early position | 936 | 1.27M | // to reserve the register for the whole instruction. | 937 | 1.27M | let mut operand_rewrites: FxHashMap<usize, Operand> = FxHashMap::default(); | 938 | 1.27M | let mut late_def_fixed: SmallVec<[PReg; 8]> = smallvec![]; | 939 | 2.16M | for &operand in self.func.inst_operands(inst) { | 940 | 2.16M | if let OperandConstraint::FixedReg(preg) = operand.constraint() { | 941 | 713k | match operand.pos() { | 942 | | OperandPos::Late => { | 943 | | // See note in fuzzing/func.rs: we | 944 | | // can't allow this, because there | 945 | | // would be no way to move a value | 946 | | // into place for a late use *after* | 947 | | // the early point (i.e. in the middle | 948 | | // of the instruction). | 949 | 494k | assert!( | 950 | 494k | operand.kind() == OperandKind::Def, | 951 | 494k | "Invalid operand: fixed constraint on Use/Mod at Late point" | 952 | 494k | ); | 953 | | | 954 | 494k | late_def_fixed.push(preg); | 955 | | } | 956 | 219k | _ => {} | 957 | | } | 958 | 1.44M | } | 959 | | } | 960 | 2.16M | for (i, &operand) in self.func.inst_operands(inst).iter().enumerate() { | 961 | 2.16M | if operand.as_fixed_nonallocatable().is_some() { | 962 | 4.93k | continue; | 963 | 2.15M | } | 964 | 2.15M | if let OperandConstraint::FixedReg(preg) = operand.constraint() { | 965 | 708k | match operand.pos() { | 966 | 214k | OperandPos::Early if live.get(operand.vreg().vreg()) => { | 967 | 43.6k | assert!(operand.kind() == OperandKind::Use, | 968 | 43.6k | "Invalid operand: fixed constraint on Def/Mod at Early position"); | 969 | | | 970 | | // If we have a constraint at the | 971 | | // Early point for a fixed preg, and | 972 | | // this preg is also constrained with | 973 | | // a *separate* def at Late or is | 974 | | // clobbered, and *if* the vreg is | 975 | | // live downward, we have to use the | 976 | | // multi-fixed-reg mechanism for a | 977 | | // fixup and rewrite here without the | 978 | | // constraint. See #53. | 979 | | // | 980 | | // We adjust the def liverange and Use | 981 | | // to an "early" position to reserve | 982 | | // the register, it still must not be | 983 | | // used by some other vreg at the | 984 | | // use-site. | 985 | | // | 986 | | // Note that we handle multiple | 987 | | // conflicting constraints for the | 988 | | // same vreg in a separate pass (see | 989 | | // `fixup_multi_fixed_vregs` below). | 990 | 43.6k | if late_def_fixed.contains(&preg) | 991 | 43.3k | || self.func.inst_clobbers(inst).contains(preg) | 992 | | { | 993 | 43.2k | log::trace!( | 994 | 0 | concat!( | 995 | 0 | "-> operand {:?} is fixed to preg {:?}, ", | 996 | 0 | "is downward live, and there is also a ", | 997 | 0 | "def or clobber at this preg" | 998 | 0 | ), | 999 | | operand, | 1000 | | preg | 1001 | | ); | 1002 | 43.2k | let pos = ProgPoint::before(inst); | 1003 | 43.2k | self.multi_fixed_reg_fixups.push(MultiFixedRegFixup { | 1004 | 43.2k | pos, | 1005 | 43.2k | from_slot: i as u8, | 1006 | 43.2k | to_slot: i as u8, | 1007 | 43.2k | to_preg: PRegIndex::new(preg.index()), | 1008 | 43.2k | vreg: VRegIndex::new(operand.vreg().vreg()), | 1009 | 43.2k | level: FixedRegFixupLevel::Initial, | 1010 | 43.2k | }); | 1011 | 43.2k | | 1012 | 43.2k | // We need to insert a reservation | 1013 | 43.2k | // at the before-point to reserve | 1014 | 43.2k | // the reg for the use too. | 1015 | 43.2k | let range = CodeRange::singleton(pos); | 1016 | 43.2k | self.add_liverange_to_preg(range, preg); | 1017 | 43.2k | | 1018 | 43.2k | // Remove the fixed-preg | 1019 | 43.2k | // constraint from the Use. | 1020 | 43.2k | operand_rewrites.insert( | 1021 | 43.2k | i, | 1022 | 43.2k | Operand::new( | 1023 | 43.2k | operand.vreg(), | 1024 | 43.2k | OperandConstraint::Any, | 1025 | 43.2k | operand.kind(), | 1026 | 43.2k | operand.pos(), | 1027 | 43.2k | ), | 1028 | 43.2k | ); | 1029 | 446 | } | 1030 | | } | 1031 | 664k | _ => {} | 1032 | | } | 1033 | 1.44M | } | 1034 | | } | 1035 | | | 1036 | | // Process defs and uses. | 1037 | 3.81M | for &cur_pos in &[InstPosition::After, InstPosition::Before] { | 1038 | 4.32M | for i in 0..self.func.inst_operands(inst).len() { | 1039 | | // don't borrow `self` | 1040 | 4.32M | let operand = operand_rewrites | 1041 | 4.32M | .get(&i) | 1042 | 4.32M | .cloned() | 1043 | 4.32M | .unwrap_or(self.func.inst_operands(inst)[i]); | 1044 | 4.32M | let pos = match (operand.kind(), operand.pos()) { | 1045 | 0 | (OperandKind::Mod, _) => ProgPoint::before(inst), | 1046 | 224k | (OperandKind::Def, OperandPos::Early) => ProgPoint::before(inst), | 1047 | 2.07M | (OperandKind::Def, OperandPos::Late) => ProgPoint::after(inst), | 1048 | 0 | (OperandKind::Use, OperandPos::Late) => ProgPoint::after(inst), | 1049 | | // If there are any reused inputs in this | 1050 | | // instruction, and this is *not* the | 1051 | | // reused vreg, force `pos` to | 1052 | | // `After`. This ensures that we correctly | 1053 | | // account for the interference between | 1054 | | // the other inputs and the | 1055 | | // input-that-is-reused/output. | 1056 | | (OperandKind::Use, OperandPos::Early) | 1057 | 2.02M | if reused_input.is_some() | 1058 | 253k | && reused_input.unwrap() != operand.vreg() => | 1059 | 51.8k | { | 1060 | 51.8k | ProgPoint::after(inst) | 1061 | | } | 1062 | 1.97M | (OperandKind::Use, OperandPos::Early) => ProgPoint::before(inst), | 1063 | | }; | 1064 | | | 1065 | 4.32M | if pos.pos() != cur_pos { | 1066 | 2.16M | continue; | 1067 | 2.16M | } | 1068 | 2.16M | | 1069 | 2.16M | trace!( | 1070 | 0 | "processing inst{} operand at {:?}: {:?}", | 1071 | 0 | inst.index(), | 1072 | | pos, | 1073 | | operand | 1074 | | ); | 1075 | | | 1076 | | // If this is a "fixed non-allocatable | 1077 | | // register" operand, set the alloc | 1078 | | // immediately and then ignore the operand | 1079 | | // hereafter. | 1080 | 2.16M | if let Some(preg) = operand.as_fixed_nonallocatable() { | 1081 | 4.93k | self.set_alloc(inst, i, Allocation::reg(preg)); | 1082 | 4.93k | continue; | 1083 | 2.15M | } | 1084 | 2.15M | | 1085 | 2.15M | match operand.kind() { | 1086 | | OperandKind::Def | OperandKind::Mod => { | 1087 | 1.15M | trace!("Def of {} at {:?}", operand.vreg(), pos); | 1088 | | | 1089 | | // Get or create the LiveRange. | 1090 | 1.15M | let mut lr = vreg_ranges[operand.vreg().vreg()]; | 1091 | 1.15M | trace!(" -> has existing LR {:?}", lr); | 1092 | | // If there was no liverange (dead def), create a trivial one. | 1093 | 1.15M | if !live.get(operand.vreg().vreg()) { | 1094 | 468k | let from = match operand.kind() { | 1095 | 468k | OperandKind::Def => pos, | 1096 | 0 | OperandKind::Mod => self.cfginfo.block_entry[block.index()], | 1097 | 0 | _ => unreachable!(), | 1098 | | }; | 1099 | | // We want to we want to span | 1100 | | // until Before of the next | 1101 | | // inst. This ensures that early | 1102 | | // defs used for temps on an | 1103 | | // instruction are reserved across | 1104 | | // the whole instruction. | 1105 | 468k | let to = ProgPoint::before(pos.inst().next()); | 1106 | 468k | lr = self.add_liverange_to_vreg( | 1107 | 468k | VRegIndex::new(operand.vreg().vreg()), | 1108 | 468k | CodeRange { from, to }, | 1109 | 468k | ); | 1110 | 468k | trace!(" -> invalid; created {:?}", lr); | 1111 | 468k | vreg_ranges[operand.vreg().vreg()] = lr; | 1112 | 468k | live.set(operand.vreg().vreg(), true); | 1113 | 682k | } | 1114 | | // Create the use in the LiveRange. | 1115 | 1.15M | self.insert_use_into_liverange(lr, Use::new(operand, pos, i as u8)); | 1116 | 1.15M | // If def (not mod), this reg is now dead, | 1117 | 1.15M | // scanning backward; make it so. | 1118 | 1.15M | if operand.kind() == OperandKind::Def { | 1119 | | // Trim the range for this vreg to start | 1120 | | // at `pos` if it previously ended at the | 1121 | | // start of this block (i.e. was not | 1122 | | // merged into some larger LiveRange due | 1123 | | // to out-of-order blocks). | 1124 | 1.15M | if self.ranges[lr.index()].range.from | 1125 | 1.15M | == self.cfginfo.block_entry[block.index()] | 1126 | | { | 1127 | 684k | trace!(" -> started at block start; trimming to {:?}", pos); | 1128 | 684k | self.ranges[lr.index()].range.from = pos; | 1129 | 466k | } | 1130 | | | 1131 | 1.15M | self.ranges[lr.index()].set_flag(LiveRangeFlag::StartsAtDef); | 1132 | 1.15M | | 1133 | 1.15M | // Remove from live-set. | 1134 | 1.15M | live.set(operand.vreg().vreg(), false); | 1135 | 1.15M | vreg_ranges[operand.vreg().vreg()] = LiveRangeIndex::invalid(); | 1136 | 0 | } | 1137 | | } | 1138 | | OperandKind::Use => { | 1139 | | // Create/extend the LiveRange if it | 1140 | | // doesn't already exist, and add the use | 1141 | | // to the range. | 1142 | 1.00M | let mut lr = vreg_ranges[operand.vreg().vreg()]; | 1143 | 1.00M | if !live.get(operand.vreg().vreg()) { | 1144 | 680k | let range = CodeRange { | 1145 | 680k | from: self.cfginfo.block_entry[block.index()], | 1146 | 680k | to: pos.next(), | 1147 | 680k | }; | 1148 | 680k | lr = self.add_liverange_to_vreg( | 1149 | 680k | VRegIndex::new(operand.vreg().vreg()), | 1150 | 680k | range, | 1151 | 680k | ); | 1152 | 680k | vreg_ranges[operand.vreg().vreg()] = lr; | 1153 | 680k | } | 1154 | 1.00M | debug_assert!(lr.is_valid()); | 1155 | | | 1156 | 1.00M | trace!("Use of {:?} at {:?} -> {:?}", operand, pos, lr,); | 1157 | | | 1158 | 1.00M | self.insert_use_into_liverange(lr, Use::new(operand, pos, i as u8)); | 1159 | 1.00M | | 1160 | 1.00M | // Add to live-set. | 1161 | 1.00M | live.set(operand.vreg().vreg(), true); | 1162 | | } | 1163 | | } | 1164 | | } | 1165 | | } | 1166 | | | 1167 | 1.27M | if self.func.requires_refs_on_stack(inst) { | 1168 | 166k | trace!("inst{} is safepoint", inst.index()); | 1169 | 166k | self.safepoints.push(inst); | 1170 | 342k | for vreg in live.iter() { | 1171 | 342k | if let Some(safepoints) = self.safepoints_per_vreg.get_mut(&vreg) { | 1172 | 0 | trace!("vreg v{} live at safepoint inst{}", vreg, inst.index()); | 1173 | 0 | safepoints.insert(inst); | 1174 | 342k | } | 1175 | | } | 1176 | 1.10M | } | 1177 | | } | 1178 | | | 1179 | | // Block parameters define vregs at the very beginning of | 1180 | | // the block. Remove their live vregs from the live set | 1181 | | // here. | 1182 | 510k | for vreg in self.func.block_params(block) { | 1183 | 510k | if live.get(vreg.vreg()) { | 1184 | 208 | live.set(vreg.vreg(), false); | 1185 | 510k | } else { | 1186 | 510k | // Create trivial liverange if blockparam is dead. | 1187 | 510k | let start = self.cfginfo.block_entry[block.index()]; | 1188 | 510k | self.add_liverange_to_vreg( | 1189 | 510k | VRegIndex::new(vreg.vreg()), | 1190 | 510k | CodeRange { | 1191 | 510k | from: start, | 1192 | 510k | to: start.next(), | 1193 | 510k | }, | 1194 | 510k | ); | 1195 | 510k | } | 1196 | | // add `blockparam_ins` entries. | 1197 | 510k | let vreg_idx = VRegIndex::new(vreg.vreg()); | 1198 | 510k | for &pred in self.func.block_preds(block) { | 1199 | 330 | self.blockparam_ins.push(BlockparamIn { | 1200 | 330 | to_vreg: vreg_idx, | 1201 | 330 | to_block: block, | 1202 | 330 | from_block: pred, | 1203 | 330 | }); | 1204 | 330 | } | 1205 | | } | 1206 | | } | 1207 | | | 1208 | 139k | self.safepoints.sort_unstable(); | 1209 | | | 1210 | | // Make ranges in each vreg and uses in each range appear in | 1211 | | // sorted order. We built them in reverse order above, so this | 1212 | | // is a simple reversal, *not* a full sort. | 1213 | | // | 1214 | | // The ordering invariant is always maintained for uses and | 1215 | | // always for ranges in bundles (which are initialized later), | 1216 | | // but not always for ranges in vregs; those are sorted only | 1217 | | // when needed, here and then again at the end of allocation | 1218 | | // when resolving moves. | 1219 | | | 1220 | 19.8M | for vreg in &mut self.vregs { | 1221 | 19.7M | vreg.ranges.reverse(); | 1222 | 19.7M | let mut last = None; | 1223 | 20.9M | for entry in &mut vreg.ranges { | 1224 | | // Ranges may have been truncated above at defs. We | 1225 | | // need to update with the final range here. | 1226 | 1.23M | entry.range = self.ranges[entry.index.index()].range; | 1227 | 1.23M | // Assert in-order and non-overlapping. | 1228 | 1.23M | debug_assert!(last.is_none() || last.unwrap() <= entry.range.from); | 1229 | 1.23M | last = Some(entry.range.to); | 1230 | | } | 1231 | | } | 1232 | | | 1233 | 1.23M | for range in 0..self.ranges.len() { | 1234 | 1.23M | self.ranges[range].uses.reverse(); | 1235 | 1.23M | debug_assert!(self.ranges[range] | 1236 | 0 | .uses | 1237 | 0 | .windows(2) | 1238 | 0 | .all(|win| win[0].pos <= win[1].pos)); | 1239 | | } | 1240 | | | 1241 | | // Insert safepoint virtual stack uses, if needed. | 1242 | 139k | for &vreg in self.func.reftype_vregs() { | 1243 | 0 | if self.func.is_pinned_vreg(vreg).is_some() { | 1244 | 0 | continue; | 1245 | 0 | } | 1246 | 0 | let vreg = VRegIndex::new(vreg.vreg()); | 1247 | 0 | let mut inserted = false; | 1248 | 0 | let mut safepoint_idx = 0; | 1249 | 0 | for range_idx in 0..self.vregs[vreg.index()].ranges.len() { | 1250 | 0 | let LiveRangeListEntry { range, index } = | 1251 | 0 | self.vregs[vreg.index()].ranges[range_idx]; | 1252 | 0 | while safepoint_idx < self.safepoints.len() | 1253 | 0 | && ProgPoint::before(self.safepoints[safepoint_idx]) < range.from | 1254 | 0 | { | 1255 | 0 | safepoint_idx += 1; | 1256 | 0 | } | 1257 | 0 | while safepoint_idx < self.safepoints.len() | 1258 | 0 | && range.contains_point(ProgPoint::before(self.safepoints[safepoint_idx])) | 1259 | | { | 1260 | | // Create a virtual use. | 1261 | 0 | let pos = ProgPoint::before(self.safepoints[safepoint_idx]); | 1262 | 0 | let operand = Operand::new( | 1263 | 0 | self.vreg(vreg), | 1264 | 0 | OperandConstraint::Stack, | 1265 | 0 | OperandKind::Use, | 1266 | 0 | OperandPos::Early, | 1267 | 0 | ); | 1268 | 0 |
| 1269 | 0 | trace!( | 1270 | 0 | "Safepoint-induced stack use of {:?} at {:?} -> {:?}", | 1271 | | operand, | 1272 | | pos, | 1273 | | index, | 1274 | | ); | 1275 | | | 1276 | 0 | self.insert_use_into_liverange(index, Use::new(operand, pos, SLOT_NONE)); | 1277 | 0 | safepoint_idx += 1; | 1278 | 0 |
| 1279 | 0 | inserted = true; | 1280 | | } | 1281 | | | 1282 | 0 | if inserted { | 1283 | 0 | self.ranges[index.index()] | 1284 | 0 | .uses | 1285 | 0 | .sort_unstable_by_key(|u| u.pos); | 1286 | 0 | } | 1287 | | | 1288 | 0 | if safepoint_idx >= self.safepoints.len() { | 1289 | 0 | break; | 1290 | 0 | } | 1291 | | } | 1292 | | } | 1293 | | | 1294 | 139k | self.blockparam_ins.sort_unstable_by_key(|x| x.key()); | 1295 | 139k | self.blockparam_outs.sort_unstable_by_key(|x| x.key()); | 1296 | 139k | self.prog_move_srcs.sort_unstable_by_key(|(pos, _)| *pos); | 1297 | 139k | self.prog_move_dsts.sort_unstable_by_key(|(pos, _)| *pos); | 1298 | 139k | | 1299 | 139k | trace!("prog_move_srcs = {:?}", self.prog_move_srcs); | 1300 | 139k | trace!("prog_move_dsts = {:?}", self.prog_move_dsts); | 1301 | | | 1302 | 139k | self.stats.initial_liverange_count = self.ranges.len(); | 1303 | 139k | self.stats.blockparam_ins_count = self.blockparam_ins.len(); | 1304 | 139k | self.stats.blockparam_outs_count = self.blockparam_outs.len(); | 1305 | 139k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::build_liveranges Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::build_liveranges Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::build_liveranges |
1306 | | |
1307 | 139k | pub fn fixup_multi_fixed_vregs(&mut self) { |
1308 | | // Do a fixed-reg cleanup pass: if there are any LiveRanges with |
1309 | | // multiple uses (or defs) at the same ProgPoint and there is |
1310 | | // more than one FixedReg constraint at that ProgPoint, we |
1311 | | // need to record all but one of them in a special fixup list |
1312 | | // and handle them later; otherwise, bundle-splitting to |
1313 | | // create minimal bundles becomes much more complex (we would |
1314 | | // have to split the multiple uses at the same progpoint into |
1315 | | // different bundles, which breaks invariants related to |
1316 | | // disjoint ranges and bundles). |
1317 | 139k | let mut extra_clobbers: SmallVec<[(PReg, ProgPoint); 8]> = smallvec![]; |
1318 | 19.7M | for vreg in 0..self.vregs.len() { |
1319 | 19.7M | for range_idx in 0..self.vregs[vreg].ranges.len() { |
1320 | 1.23M | let entry = self.vregs[vreg].ranges[range_idx]; |
1321 | 1.23M | let range = entry.index; |
1322 | 1.23M | trace!( |
1323 | 0 | "multi-fixed-reg cleanup: vreg {:?} range {:?}", |
1324 | 0 | VRegIndex::new(vreg), |
1325 | | range, |
1326 | | ); |
1327 | | |
1328 | | // Find groups of uses that occur in at the same program point. |
1329 | 2.11M | for uses in self.ranges[range.index()] |
1330 | 1.23M | .uses |
1331 | 1.99M | .linear_group_by_key_mut(|u| u.pos) <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::fixup_multi_fixed_vregs::{closure#0}Line | Count | Source | 1331 | 1.99M | .linear_group_by_key_mut(|u| u.pos) |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::fixup_multi_fixed_vregs::{closure#0}Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::fixup_multi_fixed_vregs::{closure#0}Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::fixup_multi_fixed_vregs::{closure#0} |
1332 | | { |
1333 | 2.11M | if uses.len() < 2 { |
1334 | 2.06M | continue; |
1335 | 46.3k | } |
1336 | 46.3k | |
1337 | 46.3k | // Search for conflicting constraints in the uses. |
1338 | 46.3k | let mut requires_reg = false; |
1339 | 46.3k | let mut num_fixed_reg = 0; |
1340 | 46.3k | let mut num_fixed_stack = 0; |
1341 | 46.3k | let mut first_reg_slot = None; |
1342 | 46.3k | let mut first_stack_slot = None; |
1343 | 92.6k | for u in uses.iter() { |
1344 | 92.6k | match u.operand.constraint() { |
1345 | 10 | OperandConstraint::Any => { |
1346 | 10 | first_reg_slot.get_or_insert(u.slot); |
1347 | 10 | first_stack_slot.get_or_insert(u.slot); |
1348 | 10 | } |
1349 | 92.5k | OperandConstraint::Reg | OperandConstraint::Reuse(_) => { |
1350 | 92.5k | first_reg_slot.get_or_insert(u.slot); |
1351 | 92.5k | requires_reg = true; |
1352 | 92.5k | } |
1353 | 44 | OperandConstraint::FixedReg(preg) => { |
1354 | 44 | if self.pregs[preg.index()].is_stack { |
1355 | 0 | num_fixed_stack += 1; |
1356 | 0 | first_stack_slot.get_or_insert(u.slot); |
1357 | 44 | } else { |
1358 | 44 | requires_reg = true; |
1359 | 44 | num_fixed_reg += 1; |
1360 | 44 | first_reg_slot.get_or_insert(u.slot); |
1361 | 44 | } |
1362 | | } |
1363 | | // Maybe this could be supported in this future... |
1364 | 0 | OperandConstraint::Stack => panic!( |
1365 | 0 | "multiple uses of vreg with a Stack constraint are not supported" |
1366 | 0 | ), |
1367 | | } |
1368 | | } |
1369 | | |
1370 | | // Fast path if there are no conflicts. |
1371 | 46.3k | if num_fixed_reg + num_fixed_stack <= 1 |
1372 | 46.3k | && !(requires_reg && num_fixed_stack != 0) |
1373 | | { |
1374 | 46.3k | continue; |
1375 | 2 | } |
1376 | | |
1377 | | // We pick one constraint (in order: FixedReg, Reg, FixedStack) |
1378 | | // and then rewrite any incompatible constraints to Any. |
1379 | | // This allows register allocation to succeed and we will |
1380 | | // later insert moves to satisfy the rewritten constraints. |
1381 | 2 | let source_slot = if requires_reg { |
1382 | 2 | first_reg_slot.unwrap() |
1383 | | } else { |
1384 | 0 | first_stack_slot.unwrap() |
1385 | | }; |
1386 | 2 | let mut first_preg = None; |
1387 | 4 | for u in uses.iter_mut() { |
1388 | 4 | if let OperandConstraint::FixedReg(preg) = u.operand.constraint() { |
1389 | 4 | let vreg_idx = VRegIndex::new(u.operand.vreg().vreg()); |
1390 | 4 | let preg_idx = PRegIndex::new(preg.index()); |
1391 | 4 | trace!( |
1392 | 0 | "at pos {:?}, vreg {:?} has fixed constraint to preg {:?}", |
1393 | | u.pos, |
1394 | | vreg_idx, |
1395 | | preg_idx |
1396 | | ); |
1397 | | |
1398 | | // FixedStack is incompatible if there are any |
1399 | | // Reg/FixedReg constraints. FixedReg is |
1400 | | // incompatible if there already is a different |
1401 | | // FixedReg constraint. If either condition is true, |
1402 | | // we edit the constraint below; otherwise, we can |
1403 | | // skip this edit. |
1404 | 4 | if !(requires_reg && self.pregs[preg.index()].is_stack) |
1405 | 4 | && *first_preg.get_or_insert(preg) == preg |
1406 | | { |
1407 | 2 | continue; |
1408 | 2 | } |
1409 | 2 | |
1410 | 2 | trace!(" -> duplicate; switching to constraint Any"); |
1411 | 2 | self.multi_fixed_reg_fixups.push(MultiFixedRegFixup { |
1412 | 2 | pos: u.pos, |
1413 | 2 | from_slot: source_slot, |
1414 | 2 | to_slot: u.slot, |
1415 | 2 | to_preg: preg_idx, |
1416 | 2 | vreg: vreg_idx, |
1417 | 2 | level: FixedRegFixupLevel::Secondary, |
1418 | 2 | }); |
1419 | 2 | u.operand = Operand::new( |
1420 | 2 | u.operand.vreg(), |
1421 | 2 | OperandConstraint::Any, |
1422 | 2 | u.operand.kind(), |
1423 | 2 | u.operand.pos(), |
1424 | 2 | ); |
1425 | 2 | trace!(" -> extra clobber {} at inst{}", preg, u.pos.inst().index()); |
1426 | 2 | extra_clobbers.push((preg, u.pos)); |
1427 | 0 | } |
1428 | | } |
1429 | | } |
1430 | | |
1431 | 1.23M | for &(clobber, pos) in &extra_clobbers { |
1432 | 2 | let range = CodeRange { |
1433 | 2 | from: pos, |
1434 | 2 | to: pos.next(), |
1435 | 2 | }; |
1436 | 2 | self.add_liverange_to_preg(range, clobber); |
1437 | 2 | } |
1438 | | |
1439 | 1.23M | extra_clobbers.clear(); |
1440 | | } |
1441 | | } |
1442 | 139k | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::fixup_multi_fixed_vregs Line | Count | Source | 1307 | 139k | pub fn fixup_multi_fixed_vregs(&mut self) { | 1308 | | // Do a fixed-reg cleanup pass: if there are any LiveRanges with | 1309 | | // multiple uses (or defs) at the same ProgPoint and there is | 1310 | | // more than one FixedReg constraint at that ProgPoint, we | 1311 | | // need to record all but one of them in a special fixup list | 1312 | | // and handle them later; otherwise, bundle-splitting to | 1313 | | // create minimal bundles becomes much more complex (we would | 1314 | | // have to split the multiple uses at the same progpoint into | 1315 | | // different bundles, which breaks invariants related to | 1316 | | // disjoint ranges and bundles). | 1317 | 139k | let mut extra_clobbers: SmallVec<[(PReg, ProgPoint); 8]> = smallvec![]; | 1318 | 19.7M | for vreg in 0..self.vregs.len() { | 1319 | 19.7M | for range_idx in 0..self.vregs[vreg].ranges.len() { | 1320 | 1.23M | let entry = self.vregs[vreg].ranges[range_idx]; | 1321 | 1.23M | let range = entry.index; | 1322 | 1.23M | trace!( | 1323 | 0 | "multi-fixed-reg cleanup: vreg {:?} range {:?}", | 1324 | 0 | VRegIndex::new(vreg), | 1325 | | range, | 1326 | | ); | 1327 | | | 1328 | | // Find groups of uses that occur in at the same program point. | 1329 | 2.11M | for uses in self.ranges[range.index()] | 1330 | 1.23M | .uses | 1331 | 1.23M | .linear_group_by_key_mut(|u| u.pos) | 1332 | | { | 1333 | 2.11M | if uses.len() < 2 { | 1334 | 2.06M | continue; | 1335 | 46.3k | } | 1336 | 46.3k | | 1337 | 46.3k | // Search for conflicting constraints in the uses. | 1338 | 46.3k | let mut requires_reg = false; | 1339 | 46.3k | let mut num_fixed_reg = 0; | 1340 | 46.3k | let mut num_fixed_stack = 0; | 1341 | 46.3k | let mut first_reg_slot = None; | 1342 | 46.3k | let mut first_stack_slot = None; | 1343 | 92.6k | for u in uses.iter() { | 1344 | 92.6k | match u.operand.constraint() { | 1345 | 10 | OperandConstraint::Any => { | 1346 | 10 | first_reg_slot.get_or_insert(u.slot); | 1347 | 10 | first_stack_slot.get_or_insert(u.slot); | 1348 | 10 | } | 1349 | 92.5k | OperandConstraint::Reg | OperandConstraint::Reuse(_) => { | 1350 | 92.5k | first_reg_slot.get_or_insert(u.slot); | 1351 | 92.5k | requires_reg = true; | 1352 | 92.5k | } | 1353 | 44 | OperandConstraint::FixedReg(preg) => { | 1354 | 44 | if self.pregs[preg.index()].is_stack { | 1355 | 0 | num_fixed_stack += 1; | 1356 | 0 | first_stack_slot.get_or_insert(u.slot); | 1357 | 44 | } else { | 1358 | 44 | requires_reg = true; | 1359 | 44 | num_fixed_reg += 1; | 1360 | 44 | first_reg_slot.get_or_insert(u.slot); | 1361 | 44 | } | 1362 | | } | 1363 | | // Maybe this could be supported in this future... | 1364 | 0 | OperandConstraint::Stack => panic!( | 1365 | 0 | "multiple uses of vreg with a Stack constraint are not supported" | 1366 | 0 | ), | 1367 | | } | 1368 | | } | 1369 | | | 1370 | | // Fast path if there are no conflicts. | 1371 | 46.3k | if num_fixed_reg + num_fixed_stack <= 1 | 1372 | 46.3k | && !(requires_reg && num_fixed_stack != 0) | 1373 | | { | 1374 | 46.3k | continue; | 1375 | 2 | } | 1376 | | | 1377 | | // We pick one constraint (in order: FixedReg, Reg, FixedStack) | 1378 | | // and then rewrite any incompatible constraints to Any. | 1379 | | // This allows register allocation to succeed and we will | 1380 | | // later insert moves to satisfy the rewritten constraints. | 1381 | 2 | let source_slot = if requires_reg { | 1382 | 2 | first_reg_slot.unwrap() | 1383 | | } else { | 1384 | 0 | first_stack_slot.unwrap() | 1385 | | }; | 1386 | 2 | let mut first_preg = None; | 1387 | 4 | for u in uses.iter_mut() { | 1388 | 4 | if let OperandConstraint::FixedReg(preg) = u.operand.constraint() { | 1389 | 4 | let vreg_idx = VRegIndex::new(u.operand.vreg().vreg()); | 1390 | 4 | let preg_idx = PRegIndex::new(preg.index()); | 1391 | 4 | trace!( | 1392 | 0 | "at pos {:?}, vreg {:?} has fixed constraint to preg {:?}", | 1393 | | u.pos, | 1394 | | vreg_idx, | 1395 | | preg_idx | 1396 | | ); | 1397 | | | 1398 | | // FixedStack is incompatible if there are any | 1399 | | // Reg/FixedReg constraints. FixedReg is | 1400 | | // incompatible if there already is a different | 1401 | | // FixedReg constraint. If either condition is true, | 1402 | | // we edit the constraint below; otherwise, we can | 1403 | | // skip this edit. | 1404 | 4 | if !(requires_reg && self.pregs[preg.index()].is_stack) | 1405 | 4 | && *first_preg.get_or_insert(preg) == preg | 1406 | | { | 1407 | 2 | continue; | 1408 | 2 | } | 1409 | 2 | | 1410 | 2 | trace!(" -> duplicate; switching to constraint Any"); | 1411 | 2 | self.multi_fixed_reg_fixups.push(MultiFixedRegFixup { | 1412 | 2 | pos: u.pos, | 1413 | 2 | from_slot: source_slot, | 1414 | 2 | to_slot: u.slot, | 1415 | 2 | to_preg: preg_idx, | 1416 | 2 | vreg: vreg_idx, | 1417 | 2 | level: FixedRegFixupLevel::Secondary, | 1418 | 2 | }); | 1419 | 2 | u.operand = Operand::new( | 1420 | 2 | u.operand.vreg(), | 1421 | 2 | OperandConstraint::Any, | 1422 | 2 | u.operand.kind(), | 1423 | 2 | u.operand.pos(), | 1424 | 2 | ); | 1425 | 2 | trace!(" -> extra clobber {} at inst{}", preg, u.pos.inst().index()); | 1426 | 2 | extra_clobbers.push((preg, u.pos)); | 1427 | 0 | } | 1428 | | } | 1429 | | } | 1430 | | | 1431 | 1.23M | for &(clobber, pos) in &extra_clobbers { | 1432 | 2 | let range = CodeRange { | 1433 | 2 | from: pos, | 1434 | 2 | to: pos.next(), | 1435 | 2 | }; | 1436 | 2 | self.add_liverange_to_preg(range, clobber); | 1437 | 2 | } | 1438 | | | 1439 | 1.23M | extra_clobbers.clear(); | 1440 | | } | 1441 | | } | 1442 | 139k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::fixup_multi_fixed_vregs Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::fixup_multi_fixed_vregs Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::fixup_multi_fixed_vregs |
1443 | | } |