/rust/registry/src/index.crates.io-6f17d22bba15001f/regalloc2-0.5.1/src/ion/spill.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 | | //! Spillslot allocation. |
14 | | |
15 | | use super::{ |
16 | | AllocRegResult, Env, LiveRangeKey, LiveRangeSet, PReg, PRegIndex, RegTraversalIter, |
17 | | SpillSetIndex, SpillSlotData, SpillSlotIndex, SpillSlotList, |
18 | | }; |
19 | | use crate::{Allocation, Function, SpillSlot}; |
20 | | use smallvec::smallvec; |
21 | | |
22 | | impl<'a, F: Function> Env<'a, F> { |
23 | 139k | pub fn try_allocating_regs_for_spilled_bundles(&mut self) { |
24 | 139k | trace!("allocating regs for spilled bundles"); |
25 | 139k | for i in 0..self.spilled_bundles.len() { |
26 | 70.4k | let bundle = self.spilled_bundles[i]; // don't borrow self |
27 | 70.4k | |
28 | 70.4k | if self.bundles[bundle.index()].ranges.is_empty() { |
29 | 0 | continue; |
30 | 70.4k | } |
31 | 70.4k | |
32 | 70.4k | let class = self.spillsets[self.bundles[bundle.index()].spillset.index()].class; |
33 | 70.4k | let hint = self.spillsets[self.bundles[bundle.index()].spillset.index()].reg_hint; |
34 | 70.4k | |
35 | 70.4k | // This may be an empty-range bundle whose ranges are not |
36 | 70.4k | // sorted; sort all range-lists again here. |
37 | 70.4k | self.bundles[bundle.index()] |
38 | 70.4k | .ranges |
39 | 564k | .sort_unstable_by_key(|entry| entry.range.from); <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::try_allocating_regs_for_spilled_bundles::{closure#0} Line | Count | Source | 39 | 564k | .sort_unstable_by_key(|entry| entry.range.from); |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::try_allocating_regs_for_spilled_bundles::{closure#0} Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::try_allocating_regs_for_spilled_bundles::{closure#0} Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::try_allocating_regs_for_spilled_bundles::{closure#0} |
40 | 70.4k | |
41 | 70.4k | let mut success = false; |
42 | 70.4k | self.stats.spill_bundle_reg_probes += 1; |
43 | 500k | for preg in |
44 | 70.4k | RegTraversalIter::new(self.env, class, hint, PReg::invalid(), bundle.index(), None) |
45 | | { |
46 | 500k | trace!("trying bundle {:?} to preg {:?}", bundle, preg); |
47 | 500k | let preg_idx = PRegIndex::new(preg.index()); |
48 | 500k | if let AllocRegResult::Allocated(_) = |
49 | 500k | self.try_to_allocate_bundle_to_reg(bundle, preg_idx, None) |
50 | | { |
51 | 58.0k | self.stats.spill_bundle_reg_success += 1; |
52 | 58.0k | success = true; |
53 | 58.0k | break; |
54 | 442k | } |
55 | | } |
56 | 70.4k | if !success { |
57 | 12.4k | trace!( |
58 | 0 | "spilling bundle {:?}: marking spillset {:?} as required", |
59 | 0 | bundle, |
60 | 0 | self.bundles[bundle.index()].spillset |
61 | | ); |
62 | 12.4k | self.spillsets[self.bundles[bundle.index()].spillset.index()].required = true; |
63 | 58.0k | } |
64 | | } |
65 | 139k | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::try_allocating_regs_for_spilled_bundles Line | Count | Source | 23 | 139k | pub fn try_allocating_regs_for_spilled_bundles(&mut self) { | 24 | 139k | trace!("allocating regs for spilled bundles"); | 25 | 139k | for i in 0..self.spilled_bundles.len() { | 26 | 70.4k | let bundle = self.spilled_bundles[i]; // don't borrow self | 27 | 70.4k | | 28 | 70.4k | if self.bundles[bundle.index()].ranges.is_empty() { | 29 | 0 | continue; | 30 | 70.4k | } | 31 | 70.4k | | 32 | 70.4k | let class = self.spillsets[self.bundles[bundle.index()].spillset.index()].class; | 33 | 70.4k | let hint = self.spillsets[self.bundles[bundle.index()].spillset.index()].reg_hint; | 34 | 70.4k | | 35 | 70.4k | // This may be an empty-range bundle whose ranges are not | 36 | 70.4k | // sorted; sort all range-lists again here. | 37 | 70.4k | self.bundles[bundle.index()] | 38 | 70.4k | .ranges | 39 | 70.4k | .sort_unstable_by_key(|entry| entry.range.from); | 40 | 70.4k | | 41 | 70.4k | let mut success = false; | 42 | 70.4k | self.stats.spill_bundle_reg_probes += 1; | 43 | 500k | for preg in | 44 | 70.4k | RegTraversalIter::new(self.env, class, hint, PReg::invalid(), bundle.index(), None) | 45 | | { | 46 | 500k | trace!("trying bundle {:?} to preg {:?}", bundle, preg); | 47 | 500k | let preg_idx = PRegIndex::new(preg.index()); | 48 | 500k | if let AllocRegResult::Allocated(_) = | 49 | 500k | self.try_to_allocate_bundle_to_reg(bundle, preg_idx, None) | 50 | | { | 51 | 58.0k | self.stats.spill_bundle_reg_success += 1; | 52 | 58.0k | success = true; | 53 | 58.0k | break; | 54 | 442k | } | 55 | | } | 56 | 70.4k | if !success { | 57 | 12.4k | trace!( | 58 | 0 | "spilling bundle {:?}: marking spillset {:?} as required", | 59 | 0 | bundle, | 60 | 0 | self.bundles[bundle.index()].spillset | 61 | | ); | 62 | 12.4k | self.spillsets[self.bundles[bundle.index()].spillset.index()].required = true; | 63 | 58.0k | } | 64 | | } | 65 | 139k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::try_allocating_regs_for_spilled_bundles Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::try_allocating_regs_for_spilled_bundles Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::try_allocating_regs_for_spilled_bundles |
66 | | |
67 | 17.7k | pub fn spillslot_can_fit_spillset( |
68 | 17.7k | &mut self, |
69 | 17.7k | spillslot: SpillSlotIndex, |
70 | 17.7k | spillset: SpillSetIndex, |
71 | 17.7k | ) -> bool { |
72 | 18.1k | for &vreg in &self.spillsets[spillset.index()].vregs { |
73 | 18.6k | for entry in &self.vregs[vreg.index()].ranges { |
74 | 18.6k | if self.spillslots[spillslot.index()] |
75 | 18.6k | .ranges |
76 | 18.6k | .btree |
77 | 18.6k | .contains_key(&LiveRangeKey::from_range(&entry.range)) |
78 | | { |
79 | 17.5k | return false; |
80 | 1.07k | } |
81 | | } |
82 | | } |
83 | 208 | true |
84 | 17.7k | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::spillslot_can_fit_spillset Line | Count | Source | 67 | 17.7k | pub fn spillslot_can_fit_spillset( | 68 | 17.7k | &mut self, | 69 | 17.7k | spillslot: SpillSlotIndex, | 70 | 17.7k | spillset: SpillSetIndex, | 71 | 17.7k | ) -> bool { | 72 | 18.1k | for &vreg in &self.spillsets[spillset.index()].vregs { | 73 | 18.6k | for entry in &self.vregs[vreg.index()].ranges { | 74 | 18.6k | if self.spillslots[spillslot.index()] | 75 | 18.6k | .ranges | 76 | 18.6k | .btree | 77 | 18.6k | .contains_key(&LiveRangeKey::from_range(&entry.range)) | 78 | | { | 79 | 17.5k | return false; | 80 | 1.07k | } | 81 | | } | 82 | | } | 83 | 208 | true | 84 | 17.7k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::spillslot_can_fit_spillset Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::spillslot_can_fit_spillset Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::spillslot_can_fit_spillset |
85 | | |
86 | 12.4k | pub fn allocate_spillset_to_spillslot( |
87 | 12.4k | &mut self, |
88 | 12.4k | spillset: SpillSetIndex, |
89 | 12.4k | spillslot: SpillSlotIndex, |
90 | 12.4k | ) { |
91 | 12.4k | self.spillsets[spillset.index()].slot = spillslot; |
92 | | |
93 | 13.4k | for vreg in &self.spillsets[spillset.index()].vregs { |
94 | 13.4k | trace!( |
95 | 0 | "spillslot {:?} alloc'ed to spillset {:?}: vreg {:?}", |
96 | | spillslot, |
97 | | spillset, |
98 | | vreg, |
99 | | ); |
100 | 34.8k | for entry in &self.vregs[vreg.index()].ranges { |
101 | 34.8k | trace!( |
102 | 0 | "spillslot {:?} getting range {:?} from LR {:?} from vreg {:?}", |
103 | | spillslot, |
104 | | entry.range, |
105 | | entry.index, |
106 | | vreg, |
107 | | ); |
108 | 34.8k | self.spillslots[spillslot.index()] |
109 | 34.8k | .ranges |
110 | 34.8k | .btree |
111 | 34.8k | .insert(LiveRangeKey::from_range(&entry.range), entry.index); |
112 | | } |
113 | | } |
114 | 12.4k | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::allocate_spillset_to_spillslot Line | Count | Source | 86 | 12.4k | pub fn allocate_spillset_to_spillslot( | 87 | 12.4k | &mut self, | 88 | 12.4k | spillset: SpillSetIndex, | 89 | 12.4k | spillslot: SpillSlotIndex, | 90 | 12.4k | ) { | 91 | 12.4k | self.spillsets[spillset.index()].slot = spillslot; | 92 | | | 93 | 13.4k | for vreg in &self.spillsets[spillset.index()].vregs { | 94 | 13.4k | trace!( | 95 | 0 | "spillslot {:?} alloc'ed to spillset {:?}: vreg {:?}", | 96 | | spillslot, | 97 | | spillset, | 98 | | vreg, | 99 | | ); | 100 | 34.8k | for entry in &self.vregs[vreg.index()].ranges { | 101 | 34.8k | trace!( | 102 | 0 | "spillslot {:?} getting range {:?} from LR {:?} from vreg {:?}", | 103 | | spillslot, | 104 | | entry.range, | 105 | | entry.index, | 106 | | vreg, | 107 | | ); | 108 | 34.8k | self.spillslots[spillslot.index()] | 109 | 34.8k | .ranges | 110 | 34.8k | .btree | 111 | 34.8k | .insert(LiveRangeKey::from_range(&entry.range), entry.index); | 112 | | } | 113 | | } | 114 | 12.4k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::allocate_spillset_to_spillslot Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::allocate_spillset_to_spillslot Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::allocate_spillset_to_spillslot |
115 | | |
116 | 139k | pub fn allocate_spillslots(&mut self) { |
117 | | const MAX_ATTEMPTS: usize = 10; |
118 | | |
119 | 1.15M | for spillset in 0..self.spillsets.len() { |
120 | 1.15M | trace!("allocate spillslot: {}", spillset); |
121 | 1.15M | let spillset = SpillSetIndex::new(spillset); |
122 | 1.15M | if !self.spillsets[spillset.index()].required { |
123 | 1.14M | continue; |
124 | 12.4k | } |
125 | 12.4k | // Get or create the spillslot list for this size. |
126 | 12.4k | let size = self.spillsets[spillset.index()].size as usize; |
127 | 12.4k | if size >= self.slots_by_size.len() { |
128 | 4.25k | self.slots_by_size.resize( |
129 | 4.25k | size + 1, |
130 | 4.25k | SpillSlotList { |
131 | 4.25k | slots: smallvec![], |
132 | | probe_start: 0, |
133 | | }, |
134 | | ); |
135 | 8.14k | } |
136 | | // Try a few existing spillslots. |
137 | 12.4k | let mut i = self.slots_by_size[size].probe_start; |
138 | 12.4k | let mut success = false; |
139 | | // Never probe the same element more than once: limit the |
140 | | // attempt count to the number of slots in existence. |
141 | 17.7k | for _attempt in 0..std::cmp::min(self.slots_by_size[size].slots.len(), MAX_ATTEMPTS) { |
142 | | // Note: this indexing of `slots` is always valid |
143 | | // because either the `slots` list is empty and the |
144 | | // iteration limit above consequently means we don't |
145 | | // run this loop at all, or else `probe_start` is |
146 | | // in-bounds (because it is made so below when we add |
147 | | // a slot, and it always takes on the last index `i` |
148 | | // after this loop). |
149 | 17.7k | let spillslot = self.slots_by_size[size].slots[i]; |
150 | 17.7k | |
151 | 17.7k | if self.spillslot_can_fit_spillset(spillslot, spillset) { |
152 | 208 | self.allocate_spillset_to_spillslot(spillset, spillslot); |
153 | 208 | success = true; |
154 | 208 | self.slots_by_size[size].probe_start = i; |
155 | 208 | break; |
156 | 17.5k | } |
157 | 17.5k | |
158 | 17.5k | i = self.slots_by_size[size].next_index(i); |
159 | | } |
160 | | |
161 | 12.4k | if !success { |
162 | 12.1k | // Allocate a new spillslot. |
163 | 12.1k | let spillslot = SpillSlotIndex::new(self.spillslots.len()); |
164 | 12.1k | self.spillslots.push(SpillSlotData { |
165 | 12.1k | ranges: LiveRangeSet::new(), |
166 | 12.1k | alloc: Allocation::none(), |
167 | 12.1k | slots: size as u32, |
168 | 12.1k | }); |
169 | 12.1k | self.slots_by_size[size].slots.push(spillslot); |
170 | 12.1k | self.slots_by_size[size].probe_start = self.slots_by_size[size].slots.len() - 1; |
171 | 12.1k | |
172 | 12.1k | self.allocate_spillset_to_spillslot(spillset, spillslot); |
173 | 12.1k | } |
174 | | } |
175 | | |
176 | | // Assign actual slot indices to spillslots. |
177 | 139k | for i in 0..self.spillslots.len() { |
178 | 12.1k | self.spillslots[i].alloc = self.allocate_spillslot(self.spillslots[i].slots); |
179 | 12.1k | } |
180 | | |
181 | 139k | trace!("spillslot allocator done"); |
182 | 139k | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::allocate_spillslots Line | Count | Source | 116 | 139k | pub fn allocate_spillslots(&mut self) { | 117 | | const MAX_ATTEMPTS: usize = 10; | 118 | | | 119 | 1.15M | for spillset in 0..self.spillsets.len() { | 120 | 1.15M | trace!("allocate spillslot: {}", spillset); | 121 | 1.15M | let spillset = SpillSetIndex::new(spillset); | 122 | 1.15M | if !self.spillsets[spillset.index()].required { | 123 | 1.14M | continue; | 124 | 12.4k | } | 125 | 12.4k | // Get or create the spillslot list for this size. | 126 | 12.4k | let size = self.spillsets[spillset.index()].size as usize; | 127 | 12.4k | if size >= self.slots_by_size.len() { | 128 | 4.25k | self.slots_by_size.resize( | 129 | 4.25k | size + 1, | 130 | 4.25k | SpillSlotList { | 131 | 4.25k | slots: smallvec![], | 132 | | probe_start: 0, | 133 | | }, | 134 | | ); | 135 | 8.14k | } | 136 | | // Try a few existing spillslots. | 137 | 12.4k | let mut i = self.slots_by_size[size].probe_start; | 138 | 12.4k | let mut success = false; | 139 | | // Never probe the same element more than once: limit the | 140 | | // attempt count to the number of slots in existence. | 141 | 17.7k | for _attempt in 0..std::cmp::min(self.slots_by_size[size].slots.len(), MAX_ATTEMPTS) { | 142 | | // Note: this indexing of `slots` is always valid | 143 | | // because either the `slots` list is empty and the | 144 | | // iteration limit above consequently means we don't | 145 | | // run this loop at all, or else `probe_start` is | 146 | | // in-bounds (because it is made so below when we add | 147 | | // a slot, and it always takes on the last index `i` | 148 | | // after this loop). | 149 | 17.7k | let spillslot = self.slots_by_size[size].slots[i]; | 150 | 17.7k | | 151 | 17.7k | if self.spillslot_can_fit_spillset(spillslot, spillset) { | 152 | 208 | self.allocate_spillset_to_spillslot(spillset, spillslot); | 153 | 208 | success = true; | 154 | 208 | self.slots_by_size[size].probe_start = i; | 155 | 208 | break; | 156 | 17.5k | } | 157 | 17.5k | | 158 | 17.5k | i = self.slots_by_size[size].next_index(i); | 159 | | } | 160 | | | 161 | 12.4k | if !success { | 162 | 12.1k | // Allocate a new spillslot. | 163 | 12.1k | let spillslot = SpillSlotIndex::new(self.spillslots.len()); | 164 | 12.1k | self.spillslots.push(SpillSlotData { | 165 | 12.1k | ranges: LiveRangeSet::new(), | 166 | 12.1k | alloc: Allocation::none(), | 167 | 12.1k | slots: size as u32, | 168 | 12.1k | }); | 169 | 12.1k | self.slots_by_size[size].slots.push(spillslot); | 170 | 12.1k | self.slots_by_size[size].probe_start = self.slots_by_size[size].slots.len() - 1; | 171 | 12.1k | | 172 | 12.1k | self.allocate_spillset_to_spillslot(spillset, spillslot); | 173 | 12.1k | } | 174 | | } | 175 | | | 176 | | // Assign actual slot indices to spillslots. | 177 | 139k | for i in 0..self.spillslots.len() { | 178 | 12.1k | self.spillslots[i].alloc = self.allocate_spillslot(self.spillslots[i].slots); | 179 | 12.1k | } | 180 | | | 181 | 139k | trace!("spillslot allocator done"); | 182 | 139k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::allocate_spillslots Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::allocate_spillslots Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::allocate_spillslots |
183 | | |
184 | 12.1k | pub fn allocate_spillslot(&mut self, size: u32) -> Allocation { |
185 | 12.1k | let mut offset = self.num_spillslots; |
186 | 12.1k | // Align up to `size`. |
187 | 12.1k | debug_assert!(size.is_power_of_two()); |
188 | 12.1k | offset = (offset + size - 1) & !(size - 1); |
189 | 12.1k | let slot = if self.func.multi_spillslot_named_by_last_slot() { |
190 | 0 | offset + size - 1 |
191 | | } else { |
192 | 12.1k | offset |
193 | | }; |
194 | 12.1k | offset += size; |
195 | 12.1k | self.num_spillslots = offset; |
196 | 12.1k | Allocation::stack(SpillSlot::new(slot as usize)) |
197 | 12.1k | } <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::allocate_spillslot Line | Count | Source | 184 | 12.1k | pub fn allocate_spillslot(&mut self, size: u32) -> Allocation { | 185 | 12.1k | let mut offset = self.num_spillslots; | 186 | 12.1k | // Align up to `size`. | 187 | 12.1k | debug_assert!(size.is_power_of_two()); | 188 | 12.1k | offset = (offset + size - 1) & !(size - 1); | 189 | 12.1k | let slot = if self.func.multi_spillslot_named_by_last_slot() { | 190 | 0 | offset + size - 1 | 191 | | } else { | 192 | 12.1k | offset | 193 | | }; | 194 | 12.1k | offset += size; | 195 | 12.1k | self.num_spillslots = offset; | 196 | 12.1k | Allocation::stack(SpillSlot::new(slot as usize)) | 197 | 12.1k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::allocate_spillslot Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::allocate_spillslot Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::allocate_spillslot |
198 | | } |