/src/regalloc2/src/lib.rs
Line | Count | Source |
1 | | /* |
2 | | * The following license applies to this file, which derives many |
3 | | * details (register and constraint definitions, for example) from the |
4 | | * files `BacktrackingAllocator.h`, `BacktrackingAllocator.cpp`, |
5 | | * `LIR.h`, and possibly definitions in other related files in |
6 | | * `js/src/jit/`: |
7 | | * |
8 | | * This Source Code Form is subject to the terms of the Mozilla Public |
9 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
10 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
11 | | */ |
12 | | |
13 | | #![allow(dead_code)] |
14 | | #![allow(clippy::all)] |
15 | | #![no_std] |
16 | | |
17 | | #[cfg(feature = "fuzzing")] |
18 | | extern crate std; |
19 | | |
20 | | extern crate alloc; |
21 | | |
22 | | // Even when trace logging is disabled, the trace macro has a significant |
23 | | // performance cost so we disable it in release builds. |
24 | | macro_rules! trace { |
25 | | ($($tt:tt)*) => { |
26 | | if cfg!(feature = "trace-log") { |
27 | | ::log::trace!($($tt)*); |
28 | | } |
29 | | }; |
30 | | } |
31 | | |
32 | | macro_rules! trace_enabled { |
33 | | () => { |
34 | | cfg!(feature = "trace-log") && ::log::log_enabled!(::log::Level::Trace) |
35 | | }; |
36 | | } |
37 | | |
38 | | use alloc::rc::Rc; |
39 | | use allocator_api2::vec::Vec as Vec2; |
40 | | use core::ops::Deref as _; |
41 | | use core::{hash::BuildHasherDefault, iter::FromIterator}; |
42 | | use rustc_hash::FxHasher; |
43 | | type FxHashMap<K, V> = hashbrown::HashMap<K, V, BuildHasherDefault<FxHasher>>; |
44 | | type FxHashSet<V> = hashbrown::HashSet<V, BuildHasherDefault<FxHasher>>; |
45 | | |
46 | | pub(crate) mod cfg; |
47 | | pub(crate) mod domtree; |
48 | | pub(crate) mod fastalloc; |
49 | | pub mod indexset; |
50 | | pub(crate) mod ion; |
51 | | pub(crate) mod moves; |
52 | | pub(crate) mod postorder; |
53 | | pub mod ssa; |
54 | | |
55 | | #[macro_use] |
56 | | mod index; |
57 | | |
58 | | pub use self::ion::data_structures::Ctx; |
59 | | use alloc::vec::Vec; |
60 | | pub use index::{Block, Inst, InstRange}; |
61 | | |
62 | | pub mod checker; |
63 | | |
64 | | #[cfg(feature = "fuzzing")] |
65 | | pub mod fuzzing; |
66 | | |
67 | | #[cfg(feature = "enable-serde")] |
68 | | pub mod serialize; |
69 | | |
70 | | #[cfg(feature = "enable-serde")] |
71 | | use serde::{Deserialize, Serialize}; |
72 | | |
73 | | /// Register classes. |
74 | | /// |
75 | | /// Every value has a "register class", which is like a type at the |
76 | | /// register-allocator level. Every register must belong to only one |
77 | | /// class; i.e., they are disjoint. |
78 | | /// |
79 | | /// For tight bit-packing throughout our data structures, we support |
80 | | /// only three classes, "int", "float" and "vector". Usually two will |
81 | | /// be enough on modern machines, as they have one class of general-purpose |
82 | | /// integer registers of machine width (e.g. 64 bits), and another |
83 | | /// class of float/vector registers used both for FP and for vector |
84 | | /// operations. Additionally for machines with totally separate vector |
85 | | /// registers a third class is provided. |
86 | | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] |
87 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
88 | | pub enum RegClass { |
89 | | Int = 0, |
90 | | Float = 1, |
91 | | Vector = 2, |
92 | | } |
93 | | |
94 | | /// A physical register. Contains a physical register number and a class. |
95 | | /// |
96 | | /// The `hw_enc` field contains the physical register number and is in |
97 | | /// a logically separate index space per class; in other words, Int |
98 | | /// register 0 is different than Float register 0. |
99 | | /// |
100 | | /// Because of bit-packed encodings throughout the implementation, |
101 | | /// `hw_enc` must fit in 6 bits, i.e., at most 64 registers per class. |
102 | | /// |
103 | | /// The value returned by `index()`, in contrast, is in a single index |
104 | | /// space shared by all classes, in order to enable uniform reasoning |
105 | | /// about physical registers. This is done by putting the class bit at |
106 | | /// the MSB, or equivalently, declaring that indices 0..=63 are the 64 |
107 | | /// integer registers and indices 64..=127 are the 64 float registers. |
108 | | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
109 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
110 | | pub struct PReg { |
111 | | bits: u8, |
112 | | } |
113 | | |
114 | | impl PReg { |
115 | | pub const MAX_BITS: usize = 6; |
116 | | pub const MAX: usize = (1 << Self::MAX_BITS) - 1; |
117 | | pub const NUM_INDEX: usize = 1 << (Self::MAX_BITS + 2); // including RegClass bits |
118 | | pub const INVALID: u8 = ((RegClass::Int as u8) << Self::MAX_BITS) | (Self::MAX as u8); |
119 | | |
120 | | /// Create a new PReg. The `hw_enc` range is 6 bits. |
121 | | #[inline(always)] |
122 | 41.5M | pub const fn new(hw_enc: usize, class: RegClass) -> Self { |
123 | 41.5M | debug_assert!(hw_enc <= PReg::MAX); |
124 | 41.5M | PReg { |
125 | 41.5M | bits: ((class as u8) << Self::MAX_BITS) | (hw_enc as u8), |
126 | 41.5M | } |
127 | 41.5M | } |
128 | | |
129 | | /// The physical register number, as encoded by the ISA for the particular register class. |
130 | | #[inline(always)] |
131 | 73.1M | pub const fn hw_enc(self) -> usize { |
132 | 73.1M | self.bits as usize & Self::MAX |
133 | 73.1M | } |
134 | | |
135 | | /// The register class. |
136 | | #[inline(always)] |
137 | 3.00M | pub const fn class(self) -> RegClass { |
138 | 3.00M | match (self.bits >> Self::MAX_BITS) & 0b11 { |
139 | 859k | 0 => RegClass::Int, |
140 | 1.28M | 1 => RegClass::Float, |
141 | 863k | 2 => RegClass::Vector, |
142 | 0 | _ => unreachable!(), |
143 | | } |
144 | 3.00M | } |
145 | | |
146 | | /// Get an index into the (not necessarily contiguous) index space of |
147 | | /// all physical registers. Allows one to maintain an array of data for |
148 | | /// all PRegs and index it efficiently. |
149 | | #[inline(always)] |
150 | 131M | pub const fn index(self) -> usize { |
151 | 131M | self.bits as usize |
152 | 131M | } |
153 | | |
154 | | /// Construct a PReg from the value returned from `.index()`. |
155 | | #[inline(always)] |
156 | 91.3M | pub const fn from_index(index: usize) -> Self { |
157 | 91.3M | PReg { |
158 | 91.3M | bits: (index & (Self::NUM_INDEX - 1)) as u8, |
159 | 91.3M | } |
160 | 91.3M | } |
161 | | |
162 | | /// Return the "invalid PReg", which can be used to initialize |
163 | | /// data structures. |
164 | | #[inline(always)] |
165 | 36.5M | pub const fn invalid() -> Self { |
166 | 36.5M | PReg { |
167 | 36.5M | bits: Self::INVALID, |
168 | 36.5M | } |
169 | 36.5M | } |
170 | | |
171 | | /// Return a valid [`PReg`] or [`None`] if it is invalid. |
172 | | #[inline(always)] |
173 | 8.91M | pub const fn as_valid(self) -> Option<Self> { |
174 | 8.91M | if self.bits == Self::INVALID { |
175 | 7.39M | None |
176 | | } else { |
177 | 1.52M | Some(self) |
178 | | } |
179 | 8.91M | } |
180 | | } |
181 | | |
182 | | impl Default for PReg { |
183 | 3 | fn default() -> Self { |
184 | 3 | Self::invalid() |
185 | 3 | } |
186 | | } |
187 | | |
188 | | impl core::fmt::Debug for PReg { |
189 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
190 | 0 | write!( |
191 | 0 | f, |
192 | | "PReg(hw = {}, class = {:?}, index = {})", |
193 | 0 | self.hw_enc(), |
194 | 0 | self.class(), |
195 | 0 | self.index() |
196 | | ) |
197 | 0 | } |
198 | | } |
199 | | |
200 | | impl core::fmt::Display for PReg { |
201 | 2.90M | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
202 | 2.90M | let class = match self.class() { |
203 | 818k | RegClass::Int => "i", |
204 | 1.25M | RegClass::Float => "f", |
205 | 831k | RegClass::Vector => "v", |
206 | | }; |
207 | 2.90M | write!(f, "p{}{}", self.hw_enc(), class) |
208 | 2.90M | } |
209 | | } |
210 | | |
211 | | /// A type for internal bit arrays. |
212 | | type Bits = u64; |
213 | | |
214 | | /// A physical register set. Used to represent clobbers |
215 | | /// efficiently. |
216 | | /// |
217 | | /// The set is `Copy` and is guaranteed to have constant, and small, |
218 | | /// size, as it is based on a bitset internally. |
219 | | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] |
220 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
221 | | pub struct PRegSet { |
222 | | bits: [Bits; Self::LEN], |
223 | | } |
224 | | |
225 | | impl PRegSet { |
226 | | /// The number of bits per element in the internal bit array. |
227 | | const BITS: usize = core::mem::size_of::<Bits>() * 8; |
228 | | |
229 | | /// Length of the internal bit array. |
230 | | const LEN: usize = (PReg::NUM_INDEX + Self::BITS - 1) / Self::BITS; |
231 | | |
232 | | /// Create an empty set. |
233 | 21.3M | pub const fn empty() -> Self { |
234 | 21.3M | Self { |
235 | 21.3M | bits: [0; Self::LEN], |
236 | 21.3M | } |
237 | 21.3M | } |
238 | | |
239 | | /// Splits the given register index into parts to access the internal bit array. |
240 | 29.0M | const fn split_index(reg: PReg) -> (usize, usize) { |
241 | 29.0M | let index = reg.index(); |
242 | 29.0M | (index >> Self::BITS.ilog2(), index & (Self::BITS - 1)) |
243 | 29.0M | } |
244 | | |
245 | | /// Returns whether the given register is part of the set. |
246 | 2.40M | pub fn contains(&self, reg: PReg) -> bool { |
247 | 2.40M | let (index, bit) = Self::split_index(reg); |
248 | 2.40M | self.bits[index] & (1 << bit) != 0 |
249 | 2.40M | } |
250 | | |
251 | | /// Add a physical register (PReg) to the set, returning the new value. |
252 | 3.13M | pub const fn with(self, reg: PReg) -> Self { |
253 | 3.13M | let (index, bit) = Self::split_index(reg); |
254 | 3.13M | let mut out = self; |
255 | 3.13M | out.bits[index] |= 1 << bit; |
256 | 3.13M | out |
257 | 3.13M | } |
258 | | |
259 | | /// Add a physical register (PReg) to the set. |
260 | 2.10M | pub const fn add(&mut self, reg: PReg) { |
261 | 2.10M | let (index, bit) = Self::split_index(reg); |
262 | 2.10M | self.bits[index] |= 1 << bit; |
263 | 2.10M | } |
264 | | |
265 | | /// Remove a physical register (PReg) from the set. |
266 | 0 | pub fn remove(&mut self, reg: PReg) { |
267 | 0 | let (index, bit) = Self::split_index(reg); |
268 | 0 | self.bits[index] &= !(1 << bit); |
269 | 0 | } |
270 | | |
271 | | /// Add all of the registers in one set to this one, mutating in |
272 | | /// place. |
273 | 0 | pub fn union_from(&mut self, other: PRegSet) { |
274 | 0 | for i in 0..self.bits.len() { |
275 | 0 | self.bits[i] |= other.bits[i]; |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | 42.7M | pub fn intersect_from(&mut self, other: PRegSet) { |
280 | 171M | for i in 0..self.bits.len() { |
281 | 171M | self.bits[i] &= other.bits[i]; |
282 | 171M | } |
283 | 42.7M | } |
284 | | |
285 | 21.3M | pub fn invert(&self) -> PRegSet { |
286 | 21.3M | let mut set = self.bits; |
287 | 85.5M | for i in 0..self.bits.len() { |
288 | 85.5M | set[i] = !self.bits[i]; |
289 | 85.5M | } |
290 | 21.3M | PRegSet { bits: set } |
291 | 21.3M | } |
292 | | |
293 | 0 | pub fn is_empty(&self, regclass: RegClass) -> bool { |
294 | 0 | self.bits[regclass as usize] == 0 |
295 | 0 | } |
296 | | |
297 | | /// Returns the number of register in this set. |
298 | 0 | pub fn len(&self) -> u32 { |
299 | 0 | self.bits.iter().map(|s| s.count_ones()).sum() |
300 | 0 | } |
301 | | |
302 | | /// Returns the maximum register in this set, with the highest hw_enc value. |
303 | 73.0k | pub fn max_preg(&self) -> Option<PReg> { |
304 | 73.0k | self.into_iter().last() |
305 | 73.0k | } |
306 | | |
307 | | /// Add all registers from `0..reg` to this set, not including `reg` itself. |
308 | 21.3M | pub fn add_up_to(&mut self, reg: PReg) { |
309 | 21.3M | let (index, bit) = Self::split_index(reg); |
310 | 21.3M | for i in 0..index { |
311 | 20.8M | self.bits[i] = !0; |
312 | 20.8M | } |
313 | 21.3M | self.bits[index] = (1 << bit) - 1; |
314 | 21.3M | } |
315 | | } |
316 | | |
317 | | impl core::ops::BitAnd<PRegSet> for PRegSet { |
318 | | type Output = PRegSet; |
319 | | |
320 | 42.7M | fn bitand(self, rhs: PRegSet) -> Self::Output { |
321 | 42.7M | let mut out = self; |
322 | 42.7M | out.intersect_from(rhs); |
323 | 42.7M | out |
324 | 42.7M | } |
325 | | } |
326 | | |
327 | | impl core::ops::BitOr<PRegSet> for PRegSet { |
328 | | type Output = PRegSet; |
329 | | |
330 | 0 | fn bitor(self, rhs: PRegSet) -> Self::Output { |
331 | 0 | let mut out = self; |
332 | 0 | out.union_from(rhs); |
333 | 0 | out |
334 | 0 | } |
335 | | } |
336 | | |
337 | | impl IntoIterator for &PRegSet { |
338 | | type Item = PReg; |
339 | | type IntoIter = PRegSetIter; |
340 | 73.0k | fn into_iter(self) -> PRegSetIter { |
341 | 73.0k | (*self).into_iter() |
342 | 73.0k | } |
343 | | } |
344 | | |
345 | | impl IntoIterator for PRegSet { |
346 | | type Item = PReg; |
347 | | type IntoIter = PRegSetIter; |
348 | 53.3M | fn into_iter(self) -> PRegSetIter { |
349 | 53.3M | PRegSetIter { |
350 | 53.3M | bits: self.bits, |
351 | 53.3M | cur: 0, |
352 | 53.3M | } |
353 | 53.3M | } |
354 | | } |
355 | | |
356 | | pub struct PRegSetIter { |
357 | | bits: [Bits; PRegSet::LEN], |
358 | | cur: usize, |
359 | | } |
360 | | |
361 | | impl Iterator for PRegSetIter { |
362 | | type Item = PReg; |
363 | 151M | fn next(&mut self) -> Option<PReg> { |
364 | | loop { |
365 | 235M | let bits = self.bits.get_mut(self.cur)?; |
366 | 153M | if *bits != 0 { |
367 | 70.1M | let bit = bits.trailing_zeros(); |
368 | 70.1M | *bits &= !(1 << bit); |
369 | 70.1M | let index = bit as usize + self.cur * PRegSet::BITS; |
370 | 70.1M | return Some(PReg::from_index(index)); |
371 | 83.6M | } |
372 | 83.6M | self.cur += 1; |
373 | | } |
374 | 151M | } |
375 | | } |
376 | | |
377 | | impl From<&MachineEnv> for PRegSet { |
378 | 0 | fn from(env: &MachineEnv) -> Self { |
379 | 0 | let mut res = Self::default(); |
380 | | |
381 | 0 | for class in env.preferred_regs_by_class.iter() { |
382 | 0 | res.union_from(*class) |
383 | | } |
384 | | |
385 | 0 | for class in env.non_preferred_regs_by_class.iter() { |
386 | 0 | res.union_from(*class) |
387 | | } |
388 | | |
389 | 0 | res |
390 | 0 | } |
391 | | } |
392 | | |
393 | | impl FromIterator<PReg> for PRegSet { |
394 | 73.0k | fn from_iter<T: IntoIterator<Item = PReg>>(iter: T) -> Self { |
395 | 73.0k | let mut set = Self::default(); |
396 | 1.16M | for preg in iter { |
397 | 1.16M | set.add(preg); |
398 | 1.16M | } |
399 | 73.0k | set |
400 | 73.0k | } <regalloc2::PRegSet as core::iter::traits::collect::FromIterator<regalloc2::PReg>>::from_iter::<core::iter::adapters::map::Map<core::ops::range::Range<usize>, regalloc2::fuzzing::func::machine_env::regs::{closure#0}>>Line | Count | Source | 394 | 73.0k | fn from_iter<T: IntoIterator<Item = PReg>>(iter: T) -> Self { | 395 | 73.0k | let mut set = Self::default(); | 396 | 1.16M | for preg in iter { | 397 | 1.16M | set.add(preg); | 398 | 1.16M | } | 399 | 73.0k | set | 400 | 73.0k | } |
Unexecuted instantiation: <regalloc2::PRegSet as core::iter::traits::collect::FromIterator<regalloc2::PReg>>::from_iter::<core::iter::adapters::cloned::Cloned<core::slice::iter::Iter<regalloc2::PReg>>> |
401 | | } |
402 | | |
403 | | impl core::fmt::Display for PRegSet { |
404 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
405 | 0 | write!(f, "{{")?; |
406 | 0 | for preg in self.into_iter() { |
407 | 0 | write!(f, "{preg}, ")?; |
408 | | } |
409 | 0 | write!(f, "}}") |
410 | 0 | } |
411 | | } |
412 | | |
413 | | /// A virtual register. Contains a virtual register number and a |
414 | | /// class. |
415 | | /// |
416 | | /// A virtual register ("vreg") corresponds to an SSA value. All |
417 | | /// dataflow in the input program is specified via flow through a |
418 | | /// virtual register; even uses of specially-constrained locations, |
419 | | /// such as fixed physical registers, are done by using vregs, because |
420 | | /// we need the vreg's live range in order to track the use of that |
421 | | /// location. |
422 | | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
423 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
424 | | pub struct VReg { |
425 | | bits: u32, |
426 | | } |
427 | | |
428 | | impl VReg { |
429 | | pub const MAX_BITS: usize = 30; |
430 | | pub const MAX: usize = (1 << Self::MAX_BITS) - 1; |
431 | | |
432 | | #[inline(always)] |
433 | 464M | pub const fn new(virt_reg: usize, class: RegClass) -> Self { |
434 | 464M | debug_assert!(virt_reg <= VReg::MAX); |
435 | 464M | VReg { |
436 | 464M | bits: ((virt_reg as u32) << 2) | (class as u8 as u32), |
437 | 464M | } |
438 | 464M | } |
439 | | |
440 | | #[inline(always)] |
441 | 481M | pub const fn vreg(self) -> usize { |
442 | 481M | let vreg = (self.bits >> 2) as usize; |
443 | 481M | vreg |
444 | 481M | } |
445 | | |
446 | | #[inline(always)] |
447 | 122M | pub const fn class(self) -> RegClass { |
448 | 122M | match self.bits & 0b11 { |
449 | 38.8M | 0 => RegClass::Int, |
450 | 48.0M | 1 => RegClass::Float, |
451 | 36.0M | 2 => RegClass::Vector, |
452 | 0 | _ => unreachable!(), |
453 | | } |
454 | 122M | } |
455 | | |
456 | | #[inline(always)] |
457 | 0 | pub const fn invalid() -> Self { |
458 | 0 | VReg::new(Self::MAX, RegClass::Int) |
459 | 0 | } |
460 | | |
461 | | #[inline(always)] |
462 | 0 | pub const fn bits(self) -> usize { |
463 | 0 | self.bits as usize |
464 | 0 | } |
465 | | |
466 | | #[inline(always)] |
467 | 0 | pub const fn from_bits(bits: u32) -> VReg { |
468 | 0 | Self { bits } |
469 | 0 | } |
470 | | } |
471 | | |
472 | | impl From<u32> for VReg { |
473 | 0 | fn from(value: u32) -> Self { |
474 | 0 | Self { bits: value } |
475 | 0 | } |
476 | | } |
477 | | |
478 | | impl core::fmt::Debug for VReg { |
479 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
480 | 0 | write!( |
481 | 0 | f, |
482 | | "VReg(vreg = {}, class = {:?})", |
483 | 0 | self.vreg(), |
484 | 0 | self.class() |
485 | | ) |
486 | 0 | } |
487 | | } |
488 | | |
489 | | impl core::fmt::Display for VReg { |
490 | 1.54M | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
491 | 1.54M | write!(f, "v{}", self.vreg()) |
492 | 1.54M | } |
493 | | } |
494 | | |
495 | | /// A spillslot is a space in the stackframe used by the allocator to |
496 | | /// temporarily store a value. |
497 | | /// |
498 | | /// The allocator is responsible for allocating indices in this space, |
499 | | /// and will specify how many spillslots have been used when the |
500 | | /// allocation is completed. |
501 | | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] |
502 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
503 | | pub struct SpillSlot { |
504 | | bits: u32, |
505 | | } |
506 | | |
507 | | impl SpillSlot { |
508 | | /// The maximum spillslot index. |
509 | | pub const MAX: usize = (1 << 24) - 1; |
510 | | |
511 | | /// Create a new SpillSlot. |
512 | | #[inline(always)] |
513 | 141k | pub fn new(slot: usize) -> Self { |
514 | 141k | debug_assert!(slot <= Self::MAX); |
515 | 141k | SpillSlot { bits: slot as u32 } |
516 | 141k | } |
517 | | |
518 | | /// Get the spillslot index for this spillslot. |
519 | | #[inline(always)] |
520 | 346k | pub fn index(self) -> usize { |
521 | 346k | (self.bits & 0x00ffffff) as usize |
522 | 346k | } |
523 | | |
524 | | /// Get the spillslot `offset` slots away. |
525 | | #[inline(always)] |
526 | 0 | pub fn plus(self, offset: usize) -> Self { |
527 | 0 | SpillSlot::new(self.index() + offset) |
528 | 0 | } |
529 | | |
530 | | /// Get the invalid spillslot, used for initializing data structures. |
531 | | #[inline(always)] |
532 | 0 | pub fn invalid() -> Self { |
533 | 0 | SpillSlot { bits: 0xffff_ffff } |
534 | 0 | } |
535 | | |
536 | | /// Is this the invalid spillslot? |
537 | | #[inline(always)] |
538 | 0 | pub fn is_invalid(self) -> bool { |
539 | 0 | self == Self::invalid() |
540 | 0 | } |
541 | | |
542 | | /// Is this a valid spillslot (not `SpillSlot::invalid()`)? |
543 | | #[inline(always)] |
544 | 0 | pub fn is_valid(self) -> bool { |
545 | 0 | self != Self::invalid() |
546 | 0 | } |
547 | | } |
548 | | |
549 | | impl core::fmt::Display for SpillSlot { |
550 | 346k | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
551 | 346k | write!(f, "stack{}", self.index()) |
552 | 346k | } |
553 | | } |
554 | | |
555 | | /// An `OperandConstraint` specifies where a vreg's value must be |
556 | | /// placed at a particular reference to that vreg via an |
557 | | /// `Operand`. The constraint may be loose -- "any register of a given |
558 | | /// class", for example -- or very specific, such as "this particular |
559 | | /// physical register". The allocator's result will always satisfy all |
560 | | /// given constraints; however, if the input has a combination of |
561 | | /// constraints that are impossible to satisfy, then allocation may |
562 | | /// fail or the allocator may panic (providing impossible constraints |
563 | | /// is usually a programming error in the client, rather than a |
564 | | /// function of bad input). |
565 | | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
566 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
567 | | pub enum OperandConstraint { |
568 | | /// Any location is fine (register or stack slot). |
569 | | Any, |
570 | | /// Operand must be in a register. Register is read-only for Uses. |
571 | | Reg, |
572 | | /// Operand must be on the stack. |
573 | | Stack, |
574 | | /// Operand must be in a fixed register. |
575 | | FixedReg(PReg), |
576 | | /// On defs only: reuse a use's register. |
577 | | Reuse(usize), |
578 | | /// Operand must be in a specific range of registers. |
579 | | /// |
580 | | /// The contained `usize` indicates the (exclusive) upper limit of a |
581 | | /// register range, `n`. An operand with this constraint may allocate a |
582 | | /// register between `0 ..= n-1`. Due to encoding constraints, `n` must be a |
583 | | /// power of two and below 2^16. |
584 | | Limit(usize), |
585 | | } |
586 | | |
587 | | impl core::fmt::Display for OperandConstraint { |
588 | 1.20M | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
589 | 1.20M | match self { |
590 | 801k | Self::Any => write!(f, "any"), |
591 | 328k | Self::Reg => write!(f, "reg"), |
592 | 0 | Self::Stack => write!(f, "stack"), |
593 | 30.1k | Self::FixedReg(preg) => write!(f, "fixed({preg})"), |
594 | 41.8k | Self::Reuse(idx) => write!(f, "reuse({idx})"), |
595 | 0 | Self::Limit(max) => write!(f, "limit(0..={})", max - 1), |
596 | | } |
597 | 1.20M | } |
598 | | } |
599 | | |
600 | | /// The "kind" of the operand: whether it reads a vreg (Use) or writes |
601 | | /// a vreg (Def). |
602 | | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
603 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
604 | | pub enum OperandKind { |
605 | | Def = 0, |
606 | | Use = 1, |
607 | | } |
608 | | |
609 | | /// The "position" of the operand: where it has its read/write |
610 | | /// effects. These are positions "in" the instruction, and "early" and |
611 | | /// "late" are relative to the instruction's main effect or |
612 | | /// computation. In other words, the allocator assumes that the |
613 | | /// instruction (i) performs all reads and writes of "early" operands, |
614 | | /// (ii) does its work, and (iii) performs all reads and writes of its |
615 | | /// "late" operands. |
616 | | /// |
617 | | /// A "write" (def) at "early" or a "read" (use) at "late" may be |
618 | | /// slightly nonsensical, given the above, if the read is necessary |
619 | | /// for the computation or the write is a result of it. A way to think |
620 | | /// of it is that the value (even if a result of execution) *could* |
621 | | /// have been read or written at the given location without causing |
622 | | /// any register-usage conflicts. In other words, these write-early or |
623 | | /// use-late operands ensure that the particular allocations are valid |
624 | | /// for longer than usual and that a register is not reused between |
625 | | /// the use (normally complete at "Early") and the def (normally |
626 | | /// starting at "Late"). See `Operand` for more. |
627 | | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
628 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
629 | | pub enum OperandPos { |
630 | | Early = 0, |
631 | | Late = 1, |
632 | | } |
633 | | |
634 | | /// An `Operand` encodes everything about a mention of a register in |
635 | | /// an instruction: virtual register number, and any constraint that |
636 | | /// applies to the register at this program point. |
637 | | /// |
638 | | /// An Operand may be a use or def (this corresponds to `LUse` and |
639 | | /// `LAllocation` in Ion). |
640 | | /// |
641 | | /// Generally, regalloc2 considers operands to have their effects at |
642 | | /// one of two points that exist in an instruction: "Early" or |
643 | | /// "Late". All operands at a given program-point are assigned |
644 | | /// non-conflicting locations based on their constraints. Each operand |
645 | | /// has a "kind", one of use/def/mod, corresponding to |
646 | | /// read/write/read-write, respectively. |
647 | | /// |
648 | | /// Usually, an instruction's inputs will be "early uses" and outputs |
649 | | /// will be "late defs", though there are valid use-cases for other |
650 | | /// combinations too. For example, a single "instruction" seen by the |
651 | | /// regalloc that lowers into multiple machine instructions and reads |
652 | | /// some of its inputs after it starts to write outputs must either |
653 | | /// make those input(s) "late uses" or those output(s) "early defs" so |
654 | | /// that the conflict (overlap) is properly accounted for. See |
655 | | /// comments on the constructors below for more. |
656 | | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
657 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
658 | | pub struct Operand { |
659 | | /// Bit-pack into 64 bits. |
660 | | /// |
661 | | /// unused:21 constraint:7 kind:1 pos:1 class:2 vreg:32 |
662 | | /// |
663 | | /// where `constraint` is an `OperandConstraint`, `kind` is an |
664 | | /// `OperandKind`, `pos` is an `OperandPos`, `class` is a |
665 | | /// `RegClass`, and `vreg` is a vreg index. |
666 | | /// |
667 | | /// The constraints are encoded as follows: |
668 | | /// - 1xxxxxx => FixedReg(preg) |
669 | | /// - 01xxxxx => Reuse(index) |
670 | | /// - 001xxxx => Limit(max) |
671 | | /// - 0000000 => Any |
672 | | /// - 0000001 => Reg |
673 | | /// - 0000010 => Stack |
674 | | /// - _ => Unused for now |
675 | | bits: u64, |
676 | | } |
677 | | |
678 | | impl Operand { |
679 | | const VREG_BITS: usize = 32; |
680 | | const VREG_SHIFT: usize = 0; |
681 | | const VREG_MASK: u64 = (1 << Self::VREG_BITS) - 1; |
682 | | |
683 | | const CLASS_BITS: usize = 2; |
684 | | const CLASS_SHIFT: usize = Self::VREG_SHIFT + Self::VREG_BITS; |
685 | | const CLASS_MASK: u64 = (1 << Self::CLASS_BITS) - 1; |
686 | | |
687 | | const POS_BITS: usize = 1; |
688 | | const POS_SHIFT: usize = Self::CLASS_SHIFT + Self::CLASS_BITS; |
689 | | const POS_MASK: u64 = (1 << Self::POS_BITS) - 1; |
690 | | |
691 | | const KIND_BITS: usize = 1; |
692 | | const KIND_SHIFT: usize = Self::POS_SHIFT + Self::POS_BITS; |
693 | | const KIND_MASK: u64 = (1 << Self::KIND_BITS) - 1; |
694 | | |
695 | | const CONSTRAINT_BITS: usize = 7; |
696 | | const CONSTRAINT_SHIFT: usize = Self::KIND_SHIFT + Self::KIND_BITS; |
697 | | const CONSTRAINT_MASK: u64 = (1 << Self::CONSTRAINT_BITS) - 1; |
698 | | |
699 | | const TOTAL_BITS: usize = Self::CONSTRAINT_SHIFT + Self::CONSTRAINT_BITS; |
700 | | |
701 | | /// Construct a new operand. |
702 | | #[inline(always)] |
703 | 23.4M | pub fn new( |
704 | 23.4M | vreg: VReg, |
705 | 23.4M | constraint: OperandConstraint, |
706 | 23.4M | kind: OperandKind, |
707 | 23.4M | pos: OperandPos, |
708 | 23.4M | ) -> Self { |
709 | 23.4M | let constraint_field = match constraint { |
710 | 14.9M | OperandConstraint::Any => 0, |
711 | 7.10M | OperandConstraint::Reg => 1, |
712 | 0 | OperandConstraint::Stack => 2, |
713 | 676k | OperandConstraint::FixedReg(preg) => { |
714 | 676k | debug_assert_eq!(preg.class(), vreg.class()); |
715 | 676k | 0b1000000 | preg.hw_enc() as u64 |
716 | | } |
717 | 709k | OperandConstraint::Reuse(which) => { |
718 | 709k | debug_assert!(which <= 0b11111); |
719 | 709k | 0b0100000 | which as u64 |
720 | | } |
721 | 0 | OperandConstraint::Limit(max) => { |
722 | 0 | assert!(max.is_power_of_two()); |
723 | 0 | assert!( |
724 | 0 | max <= PReg::MAX + 1, |
725 | | "limit is larger than the allowed register encoding" |
726 | | ); |
727 | 0 | let log2 = max.ilog2(); |
728 | 0 | debug_assert!(log2 <= 0b1111); |
729 | 0 | 0b0010000 | log2 as u64 |
730 | | } |
731 | | }; |
732 | 23.4M | let class_field = vreg.class() as u8 as u64; |
733 | 23.4M | let pos_field = pos as u8 as u64; |
734 | 23.4M | let kind_field = kind as u8 as u64; |
735 | 23.4M | Operand { |
736 | 23.4M | bits: ((vreg.vreg() as u64) << Self::VREG_SHIFT) |
737 | 23.4M | | (class_field << Self::CLASS_SHIFT) |
738 | 23.4M | | (pos_field << Self::POS_SHIFT) |
739 | 23.4M | | (kind_field << Self::KIND_SHIFT) |
740 | 23.4M | | (constraint_field << Self::CONSTRAINT_SHIFT), |
741 | 23.4M | } |
742 | 23.4M | } |
743 | | |
744 | | /// Create an `Operand` that designates a use of a VReg that must |
745 | | /// be in a register, and that is used at the "before" point, |
746 | | /// i.e., can be overwritten by a result. |
747 | | #[inline(always)] |
748 | 0 | pub fn reg_use(vreg: VReg) -> Self { |
749 | 0 | Operand::new( |
750 | 0 | vreg, |
751 | 0 | OperandConstraint::Reg, |
752 | 0 | OperandKind::Use, |
753 | 0 | OperandPos::Early, |
754 | | ) |
755 | 0 | } |
756 | | |
757 | | /// Create an `Operand` that designates a use of a VReg that must |
758 | | /// be in a register, and that is used up until the "after" point, |
759 | | /// i.e., must not conflict with any results. |
760 | | #[inline(always)] |
761 | 0 | pub fn reg_use_at_end(vreg: VReg) -> Self { |
762 | 0 | Operand::new( |
763 | 0 | vreg, |
764 | 0 | OperandConstraint::Reg, |
765 | 0 | OperandKind::Use, |
766 | 0 | OperandPos::Late, |
767 | | ) |
768 | 0 | } |
769 | | |
770 | | /// Create an `Operand` that designates a definition of a VReg |
771 | | /// that must be in a register, and that occurs at the "after" |
772 | | /// point, i.e. may reuse a register that carried a use into this |
773 | | /// instruction. |
774 | | #[inline(always)] |
775 | 0 | pub fn reg_def(vreg: VReg) -> Self { |
776 | 0 | Operand::new( |
777 | 0 | vreg, |
778 | 0 | OperandConstraint::Reg, |
779 | 0 | OperandKind::Def, |
780 | 0 | OperandPos::Late, |
781 | | ) |
782 | 0 | } |
783 | | |
784 | | /// Create an `Operand` that designates a definition of a VReg |
785 | | /// that must be in a register, and that occurs early at the |
786 | | /// "before" point, i.e., must not conflict with any input to the |
787 | | /// instruction. |
788 | | /// |
789 | | /// Note that the register allocator will ensure that such an |
790 | | /// early-def operand is live throughout the instruction, i.e., also |
791 | | /// at the after-point. Hence it will also avoid conflicts with all |
792 | | /// outputs to the instruction. As such, early defs are appropriate |
793 | | /// for use as "temporary registers" that an instruction can use |
794 | | /// throughout its execution separately from the inputs and outputs. |
795 | | #[inline(always)] |
796 | 0 | pub fn reg_def_at_start(vreg: VReg) -> Self { |
797 | 0 | Operand::new( |
798 | 0 | vreg, |
799 | 0 | OperandConstraint::Reg, |
800 | 0 | OperandKind::Def, |
801 | 0 | OperandPos::Early, |
802 | | ) |
803 | 0 | } |
804 | | |
805 | | /// Create an `Operand` that designates a def (and use) of a |
806 | | /// temporary *within* the instruction. This register is assumed |
807 | | /// to be written by the instruction, and will not conflict with |
808 | | /// any input or output, but should not be used after the |
809 | | /// instruction completes. |
810 | | /// |
811 | | /// Note that within a single instruction, the dedicated scratch |
812 | | /// register (as specified in the `MachineEnv`) is also always |
813 | | /// available for use. The register allocator may use the register |
814 | | /// *between* instructions in order to implement certain sequences |
815 | | /// of moves, but will never hold a value live in the scratch |
816 | | /// register across an instruction. |
817 | | #[inline(always)] |
818 | 0 | pub fn reg_temp(vreg: VReg) -> Self { |
819 | | // For now a temp is equivalent to a def-at-start operand, |
820 | | // which gives the desired semantics but does not enforce the |
821 | | // "not reused later" constraint. |
822 | 0 | Operand::new( |
823 | 0 | vreg, |
824 | 0 | OperandConstraint::Reg, |
825 | 0 | OperandKind::Def, |
826 | 0 | OperandPos::Early, |
827 | | ) |
828 | 0 | } |
829 | | |
830 | | /// Create an `Operand` that designates a def of a vreg that must |
831 | | /// reuse the register assigned to an input to the |
832 | | /// instruction. The input is identified by `idx` (is the `idx`th |
833 | | /// `Operand` for the instruction) and must be constraint to a |
834 | | /// register, i.e., be the result of `Operand::reg_use(vreg)`. |
835 | | #[inline(always)] |
836 | 0 | pub fn reg_reuse_def(vreg: VReg, idx: usize) -> Self { |
837 | 0 | Operand::new( |
838 | 0 | vreg, |
839 | 0 | OperandConstraint::Reuse(idx), |
840 | 0 | OperandKind::Def, |
841 | 0 | OperandPos::Late, |
842 | | ) |
843 | 0 | } |
844 | | |
845 | | /// Create an `Operand` that designates a use of a vreg and |
846 | | /// ensures that it is placed in the given, fixed PReg at the |
847 | | /// use. It is guaranteed that the `Allocation` resulting for this |
848 | | /// operand will be `preg`. |
849 | | #[inline(always)] |
850 | 0 | pub fn reg_fixed_use(vreg: VReg, preg: PReg) -> Self { |
851 | 0 | Operand::new( |
852 | 0 | vreg, |
853 | 0 | OperandConstraint::FixedReg(preg), |
854 | 0 | OperandKind::Use, |
855 | 0 | OperandPos::Early, |
856 | | ) |
857 | 0 | } |
858 | | |
859 | | /// Create an `Operand` that designates a def of a vreg and |
860 | | /// ensures that it is placed in the given, fixed PReg at the |
861 | | /// def. It is guaranteed that the `Allocation` resulting for this |
862 | | /// operand will be `preg`. |
863 | | #[inline(always)] |
864 | 0 | pub fn reg_fixed_def(vreg: VReg, preg: PReg) -> Self { |
865 | 0 | Operand::new( |
866 | 0 | vreg, |
867 | 0 | OperandConstraint::FixedReg(preg), |
868 | 0 | OperandKind::Def, |
869 | 0 | OperandPos::Late, |
870 | | ) |
871 | 0 | } |
872 | | |
873 | | /// Same as `reg_fixed_use` but at `OperandPos::Late`. |
874 | | #[inline(always)] |
875 | 0 | pub fn reg_fixed_use_at_end(vreg: VReg, preg: PReg) -> Self { |
876 | 0 | Operand::new( |
877 | 0 | vreg, |
878 | 0 | OperandConstraint::FixedReg(preg), |
879 | 0 | OperandKind::Use, |
880 | 0 | OperandPos::Late, |
881 | | ) |
882 | 0 | } |
883 | | |
884 | | /// Same as `reg_fixed_def` but at `OperandPos::Early`. |
885 | | #[inline(always)] |
886 | 0 | pub fn reg_fixed_def_at_start(vreg: VReg, preg: PReg) -> Self { |
887 | 0 | Operand::new( |
888 | 0 | vreg, |
889 | 0 | OperandConstraint::FixedReg(preg), |
890 | 0 | OperandKind::Def, |
891 | 0 | OperandPos::Early, |
892 | | ) |
893 | 0 | } |
894 | | |
895 | | /// Create an `Operand` that designates a use of a vreg and places |
896 | | /// no constraints on its location (i.e., it can be allocated into |
897 | | /// either a register or on the stack). |
898 | | #[inline(always)] |
899 | 0 | pub fn any_use(vreg: VReg) -> Self { |
900 | 0 | Operand::new( |
901 | 0 | vreg, |
902 | 0 | OperandConstraint::Any, |
903 | 0 | OperandKind::Use, |
904 | 0 | OperandPos::Early, |
905 | | ) |
906 | 0 | } |
907 | | |
908 | | /// Create an `Operand` that designates a def of a vreg and places |
909 | | /// no constraints on its location (i.e., it can be allocated into |
910 | | /// either a register or on the stack). |
911 | | #[inline(always)] |
912 | 5.34M | pub fn any_def(vreg: VReg) -> Self { |
913 | 5.34M | Operand::new( |
914 | 5.34M | vreg, |
915 | 5.34M | OperandConstraint::Any, |
916 | 5.34M | OperandKind::Def, |
917 | 5.34M | OperandPos::Late, |
918 | | ) |
919 | 5.34M | } |
920 | | |
921 | | /// Create an `Operand` that always results in an assignment to the |
922 | | /// given fixed `preg`, *without* tracking liveranges in that |
923 | | /// `preg`. Must only be used for non-allocatable registers. |
924 | | #[inline(always)] |
925 | 103k | pub fn fixed_nonallocatable(preg: PReg) -> Self { |
926 | 103k | Operand::new( |
927 | 103k | VReg::new(VReg::MAX, preg.class()), |
928 | 103k | OperandConstraint::FixedReg(preg), |
929 | 103k | OperandKind::Use, |
930 | 103k | OperandPos::Early, |
931 | | ) |
932 | 103k | } |
933 | | |
934 | | /// Get the virtual register designated by an operand. Every |
935 | | /// operand must name some virtual register, even if it constrains |
936 | | /// the operand to a fixed physical register as well; the vregs |
937 | | /// are used to track dataflow. |
938 | | #[inline(always)] |
939 | 430M | pub fn vreg(self) -> VReg { |
940 | 430M | let vreg_idx = ((self.bits >> Self::VREG_SHIFT) & Self::VREG_MASK) as usize; |
941 | 430M | VReg::new(vreg_idx, self.class()) |
942 | 430M | } |
943 | | |
944 | | /// Get the register class used by this operand. |
945 | | #[inline(always)] |
946 | 449M | pub fn class(self) -> RegClass { |
947 | 449M | let class_field = (self.bits >> Self::CLASS_SHIFT) & Self::CLASS_MASK; |
948 | 449M | match class_field { |
949 | 137M | 0 => RegClass::Int, |
950 | 179M | 1 => RegClass::Float, |
951 | 133M | 2 => RegClass::Vector, |
952 | 0 | _ => unreachable!(), |
953 | | } |
954 | 449M | } |
955 | | |
956 | | /// Get the "kind" of this operand: a definition (write) or a use |
957 | | /// (read). |
958 | | #[inline(always)] |
959 | 293M | pub fn kind(self) -> OperandKind { |
960 | 293M | let kind_field = (self.bits >> Self::KIND_SHIFT) & Self::KIND_MASK; |
961 | 293M | match kind_field { |
962 | 160M | 0 => OperandKind::Def, |
963 | 133M | 1 => OperandKind::Use, |
964 | 0 | _ => unreachable!(), |
965 | | } |
966 | 293M | } |
967 | | |
968 | | /// Get the "position" of this operand, i.e., where its read |
969 | | /// and/or write occurs: either before the instruction executes, |
970 | | /// or after it does. Ordinarily, uses occur at "before" and defs |
971 | | /// at "after", though there are cases where this is not true. |
972 | | #[inline(always)] |
973 | 242M | pub fn pos(self) -> OperandPos { |
974 | 242M | let pos_field = (self.bits >> Self::POS_SHIFT) & Self::POS_MASK; |
975 | 242M | match pos_field { |
976 | 159M | 0 => OperandPos::Early, |
977 | 82.7M | 1 => OperandPos::Late, |
978 | 0 | _ => unreachable!(), |
979 | | } |
980 | 242M | } |
981 | | |
982 | | /// Get the "constraint" of this operand, i.e., what requirements |
983 | | /// its allocation must fulfill. |
984 | | #[inline(always)] |
985 | 512M | pub fn constraint(self) -> OperandConstraint { |
986 | 512M | let constraint_field = |
987 | 512M | ((self.bits >> Self::CONSTRAINT_SHIFT) & Self::CONSTRAINT_MASK) as usize; |
988 | 512M | if constraint_field & 0b1000000 != 0 { |
989 | 15.3M | OperandConstraint::FixedReg(PReg::new(constraint_field & 0b0111111, self.class())) |
990 | 497M | } else if constraint_field & 0b0100000 != 0 { |
991 | 16.2M | OperandConstraint::Reuse(constraint_field & 0b0011111) |
992 | 481M | } else if constraint_field & 0b0010000 != 0 { |
993 | 0 | OperandConstraint::Limit(1 << (constraint_field & 0b0001111)) |
994 | | } else { |
995 | 481M | match constraint_field { |
996 | 335M | 0 => OperandConstraint::Any, |
997 | 145M | 1 => OperandConstraint::Reg, |
998 | 0 | 2 => OperandConstraint::Stack, |
999 | 0 | _ => unreachable!(), |
1000 | | } |
1001 | | } |
1002 | 512M | } |
1003 | | |
1004 | | /// If this operand is for a fixed non-allocatable register (see |
1005 | | /// [`Operand::fixed_nonallocatable`]), then returns the physical register that it will |
1006 | | /// be assigned to. |
1007 | | #[inline(always)] |
1008 | 215M | pub fn as_fixed_nonallocatable(self) -> Option<PReg> { |
1009 | 215M | match self.constraint() { |
1010 | 6.07M | OperandConstraint::FixedReg(preg) if self.vreg().vreg() == VReg::MAX => Some(preg), |
1011 | 214M | _ => None, |
1012 | | } |
1013 | 215M | } |
1014 | | |
1015 | | /// Get the raw 64-bit encoding of this operand's fields. |
1016 | | #[inline(always)] |
1017 | 0 | pub fn bits(self) -> u64 { |
1018 | 0 | self.bits |
1019 | 0 | } |
1020 | | |
1021 | | /// Construct an `Operand` from the raw 64-bit encoding returned |
1022 | | /// from `bits()`. |
1023 | | #[inline(always)] |
1024 | 0 | pub fn from_bits(bits: u64) -> Self { |
1025 | 0 | debug_assert_eq!(bits >> Self::TOTAL_BITS, 0); |
1026 | 0 | Operand { bits } |
1027 | 0 | } |
1028 | | } |
1029 | | |
1030 | | impl core::fmt::Debug for Operand { |
1031 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
1032 | 0 | core::fmt::Display::fmt(self, f) |
1033 | 0 | } |
1034 | | } |
1035 | | |
1036 | | impl core::fmt::Display for Operand { |
1037 | 1.20M | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
1038 | 1.20M | if let Some(preg) = self.as_fixed_nonallocatable() { |
1039 | 3.48k | return write!(f, "Fixed: {preg}"); |
1040 | 1.20M | } |
1041 | 1.20M | match (self.kind(), self.pos()) { |
1042 | | (OperandKind::Def, OperandPos::Late) | (OperandKind::Use, OperandPos::Early) => { |
1043 | 1.11M | write!(f, "{:?}", self.kind())?; |
1044 | | } |
1045 | | _ => { |
1046 | 90.2k | write!(f, "{:?}@{:?}", self.kind(), self.pos())?; |
1047 | | } |
1048 | | } |
1049 | 1.20M | write!( |
1050 | 1.20M | f, |
1051 | | ": {}{} {}", |
1052 | 1.20M | self.vreg(), |
1053 | 1.20M | match self.class() { |
1054 | 346k | RegClass::Int => "i", |
1055 | 531k | RegClass::Float => "f", |
1056 | 323k | RegClass::Vector => "v", |
1057 | | }, |
1058 | 1.20M | self.constraint() |
1059 | | ) |
1060 | 1.20M | } |
1061 | | } |
1062 | | |
1063 | | /// An Allocation represents the end result of regalloc for an |
1064 | | /// Operand. |
1065 | | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
1066 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
1067 | | pub struct Allocation { |
1068 | | /// Bit-pack in 32 bits. |
1069 | | /// |
1070 | | /// kind:3 unused:1 index:28 |
1071 | | bits: u32, |
1072 | | } |
1073 | | |
1074 | | impl core::fmt::Debug for Allocation { |
1075 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
1076 | 0 | core::fmt::Display::fmt(self, f) |
1077 | 0 | } |
1078 | | } |
1079 | | |
1080 | | impl core::fmt::Display for Allocation { |
1081 | 3.16M | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
1082 | 3.16M | match self.kind() { |
1083 | 0 | AllocationKind::None => write!(f, "none"), |
1084 | 2.82M | AllocationKind::Reg => write!(f, "{}", self.as_reg().unwrap()), |
1085 | 346k | AllocationKind::Stack => write!(f, "{}", self.as_stack().unwrap()), |
1086 | | } |
1087 | 3.16M | } |
1088 | | } |
1089 | | |
1090 | | impl Allocation { |
1091 | | /// Construct a new Allocation. |
1092 | | #[inline(always)] |
1093 | 89.6M | pub(crate) fn new(kind: AllocationKind, index: usize) -> Self { |
1094 | 89.6M | debug_assert!(index < (1 << 28)); |
1095 | 89.6M | Self { |
1096 | 89.6M | bits: ((kind as u8 as u32) << 29) | (index as u32), |
1097 | 89.6M | } |
1098 | 89.6M | } |
1099 | | |
1100 | | /// Get the "none" allocation, which is distinct from the other |
1101 | | /// possibilities and is used to initialize data structures. |
1102 | | #[inline(always)] |
1103 | 71.5M | pub fn none() -> Allocation { |
1104 | 71.5M | Allocation::new(AllocationKind::None, 0) |
1105 | 71.5M | } |
1106 | | |
1107 | | /// Create an allocation into a register. |
1108 | | #[inline(always)] |
1109 | 17.9M | pub fn reg(preg: PReg) -> Allocation { |
1110 | 17.9M | Allocation::new(AllocationKind::Reg, preg.index()) |
1111 | 17.9M | } |
1112 | | |
1113 | | /// Create an allocation into a spillslot. |
1114 | | #[inline(always)] |
1115 | 141k | pub fn stack(slot: SpillSlot) -> Allocation { |
1116 | 141k | Allocation::new(AllocationKind::Stack, slot.bits as usize) |
1117 | 141k | } |
1118 | | |
1119 | | /// Get the allocation's "kind": none, register, or stack (spillslot). |
1120 | | #[inline(always)] |
1121 | 22.9M | pub fn kind(self) -> AllocationKind { |
1122 | 22.9M | match (self.bits >> 29) & 7 { |
1123 | 1.82M | 0 => AllocationKind::None, |
1124 | 18.7M | 1 => AllocationKind::Reg, |
1125 | 2.33M | 2 => AllocationKind::Stack, |
1126 | 0 | _ => unreachable!(), |
1127 | | } |
1128 | 22.9M | } |
1129 | | |
1130 | | /// Is the allocation "none"? |
1131 | | #[inline(always)] |
1132 | 10.9k | pub fn is_none(self) -> bool { |
1133 | 10.9k | self.kind() == AllocationKind::None |
1134 | 10.9k | } |
1135 | | |
1136 | | /// Is the allocation not "none"? |
1137 | | #[inline(always)] |
1138 | 1.81M | pub fn is_some(self) -> bool { |
1139 | 1.81M | self.kind() != AllocationKind::None |
1140 | 1.81M | } |
1141 | | |
1142 | | /// Is the allocation a register? |
1143 | | #[inline(always)] |
1144 | 3.04M | pub fn is_reg(self) -> bool { |
1145 | 3.04M | self.kind() == AllocationKind::Reg |
1146 | 3.04M | } |
1147 | | |
1148 | | /// Is the allocation on the stack (a spillslot)? |
1149 | | #[inline(always)] |
1150 | 418k | pub fn is_stack(self) -> bool { |
1151 | 418k | self.kind() == AllocationKind::Stack |
1152 | 418k | } |
1153 | | |
1154 | | /// Get the index of the spillslot or register. If register, this |
1155 | | /// is an index that can be used by `PReg::from_index()`. |
1156 | | #[inline(always)] |
1157 | 13.7M | pub fn index(self) -> usize { |
1158 | 13.7M | (self.bits & ((1 << 28) - 1)) as usize |
1159 | 13.7M | } |
1160 | | |
1161 | | /// Get the allocation as a physical register, if any. |
1162 | | #[inline(always)] |
1163 | 14.1M | pub fn as_reg(self) -> Option<PReg> { |
1164 | 14.1M | if self.kind() == AllocationKind::Reg { |
1165 | 13.3M | Some(PReg::from_index(self.index())) |
1166 | | } else { |
1167 | 731k | None |
1168 | | } |
1169 | 14.1M | } |
1170 | | |
1171 | | /// Get the allocation as a spillslot, if any. |
1172 | | #[inline(always)] |
1173 | 346k | pub fn as_stack(self) -> Option<SpillSlot> { |
1174 | 346k | if self.kind() == AllocationKind::Stack { |
1175 | 346k | Some(SpillSlot { |
1176 | 346k | bits: self.index() as u32, |
1177 | 346k | }) |
1178 | | } else { |
1179 | 0 | None |
1180 | | } |
1181 | 346k | } |
1182 | | |
1183 | | /// Get the raw bits for the packed encoding of this allocation. |
1184 | | #[inline(always)] |
1185 | 4.35M | pub fn bits(self) -> u32 { |
1186 | 4.35M | self.bits |
1187 | 4.35M | } |
1188 | | |
1189 | | /// Construct an allocation from its packed encoding. |
1190 | | #[inline(always)] |
1191 | 0 | pub fn from_bits(bits: u32) -> Self { |
1192 | 0 | debug_assert!(bits >> 29 >= 5); |
1193 | 0 | Self { bits } |
1194 | 0 | } |
1195 | | } |
1196 | | |
1197 | | /// An allocation is one of two "kinds" (or "none"): register or |
1198 | | /// spillslot/stack. |
1199 | | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] |
1200 | | #[repr(u8)] |
1201 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
1202 | | pub enum AllocationKind { |
1203 | | None = 0, |
1204 | | Reg = 1, |
1205 | | Stack = 2, |
1206 | | } |
1207 | | |
1208 | | /// A trait defined by the regalloc client to provide access to its |
1209 | | /// machine-instruction / CFG representation. |
1210 | | /// |
1211 | | /// (This trait's design is inspired by, and derives heavily from, the |
1212 | | /// trait of the same name in regalloc.rs.) |
1213 | | /// |
1214 | | /// The ids used in [`Block`] and [`VReg`] must start their numbering |
1215 | | /// at zero and be sequential. |
1216 | | pub trait Function { |
1217 | | // ------------- |
1218 | | // CFG traversal |
1219 | | // ------------- |
1220 | | |
1221 | | /// How many instructions are there? |
1222 | | fn num_insts(&self) -> usize; |
1223 | | |
1224 | | /// How many blocks are there? |
1225 | | fn num_blocks(&self) -> usize; |
1226 | | |
1227 | | /// Get the index of the entry block. |
1228 | | fn entry_block(&self) -> Block; |
1229 | | |
1230 | | /// Provide the range of instruction indices contained in each block. |
1231 | | fn block_insns(&self, block: Block) -> InstRange; |
1232 | | |
1233 | | /// Get CFG successors for a given block. |
1234 | | fn block_succs(&self, block: Block) -> &[Block]; |
1235 | | |
1236 | | /// Get the CFG predecessors for a given block. |
1237 | | fn block_preds(&self, block: Block) -> &[Block]; |
1238 | | |
1239 | | /// Get the block parameters for a given block. |
1240 | | fn block_params(&self, block: Block) -> &[VReg]; |
1241 | | |
1242 | | /// Determine whether an instruction is a return instruction. |
1243 | | fn is_ret(&self, insn: Inst) -> bool; |
1244 | | |
1245 | | /// Determine whether an instruction is the end-of-block |
1246 | | /// branch. |
1247 | | fn is_branch(&self, insn: Inst) -> bool; |
1248 | | |
1249 | | /// If `insn` is a branch at the end of `block`, returns the |
1250 | | /// outgoing blockparam arguments for the given successor. The |
1251 | | /// number of arguments must match the number incoming blockparams |
1252 | | /// for each respective successor block. |
1253 | | fn branch_blockparams(&self, block: Block, insn: Inst, succ_idx: usize) -> &[VReg]; |
1254 | | |
1255 | | // -------------------------- |
1256 | | // Instruction register slots |
1257 | | // -------------------------- |
1258 | | |
1259 | | /// Get the Operands for an instruction. |
1260 | | fn inst_operands(&self, insn: Inst) -> &[Operand]; |
1261 | | |
1262 | | /// Get the clobbers for an instruction; these are the registers |
1263 | | /// that, after the instruction has executed, hold values that are |
1264 | | /// arbitrary, separately from the usual outputs to the |
1265 | | /// instruction. It is invalid to read a register that has been |
1266 | | /// clobbered; the register allocator is free to assume that |
1267 | | /// clobbered registers are filled with garbage and available for |
1268 | | /// reuse. It will avoid storing any value in a clobbered register |
1269 | | /// that must be live across the instruction. |
1270 | | /// |
1271 | | /// Another way of seeing this is that a clobber is equivalent to |
1272 | | /// a "late def" of a fresh vreg that is not used anywhere else |
1273 | | /// in the program, with a fixed-register constraint that places |
1274 | | /// it in a given PReg chosen by the client prior to regalloc. |
1275 | | /// |
1276 | | /// Every register written by an instruction must either |
1277 | | /// correspond to (be assigned to) an Operand of kind `Def`, or |
1278 | | /// else must be a "clobber". |
1279 | | /// |
1280 | | /// This can be used to, for example, describe ABI-specified |
1281 | | /// registers that are not preserved by a call instruction, or |
1282 | | /// fixed physical registers written by an instruction but not |
1283 | | /// used as a vreg output, or fixed physical registers used as |
1284 | | /// temps within an instruction out of necessity. |
1285 | | /// |
1286 | | /// Note that clobbers and defs and late-uses must not collide: it |
1287 | | /// is illegal to name the same physical register both as a clobber |
1288 | | /// and in a fixed-register constraint on a def or late use. |
1289 | | /// Internally, clobbers are modeled as defs (of throwaway vregs) at |
1290 | | /// the instruction's late-point constrained to the named register. |
1291 | | fn inst_clobbers(&self, insn: Inst) -> PRegSet; |
1292 | | |
1293 | | /// Get the number of `VReg` in use in this function. |
1294 | | fn num_vregs(&self) -> usize; |
1295 | | |
1296 | | /// Get the VRegs for which we should generate value-location |
1297 | | /// metadata for debugging purposes. This can be used to generate |
1298 | | /// e.g. DWARF with valid prgram-point ranges for each value |
1299 | | /// expression in a way that is more efficient than a post-hoc |
1300 | | /// analysis of the allocator's output. |
1301 | | /// |
1302 | | /// Each tuple is (vreg, inclusive_start, exclusive_end, |
1303 | | /// label). In the `Output` there will be (label, inclusive_start, |
1304 | | /// exclusive_end, alloc)` tuples. The ranges may not exactly |
1305 | | /// match -- specifically, the returned metadata may cover only a |
1306 | | /// subset of the requested ranges -- if the value is not live for |
1307 | | /// the entire requested ranges. |
1308 | | /// |
1309 | | /// The instruction indices imply a program point just *before* |
1310 | | /// the instruction. |
1311 | | /// |
1312 | | /// Precondition: we require this slice to be sorted by vreg. |
1313 | 0 | fn debug_value_labels(&self) -> &[(VReg, Inst, Inst, u32)] { |
1314 | 0 | &[] |
1315 | 0 | } |
1316 | | |
1317 | | // -------------- |
1318 | | // Spills/reloads |
1319 | | // -------------- |
1320 | | |
1321 | | /// How many logical spill slots does the given regclass require? E.g., on |
1322 | | /// a 64-bit machine, spill slots may nominally be 64-bit words, but a |
1323 | | /// 128-bit vector value will require two slots. The regalloc will always |
1324 | | /// align on this size. |
1325 | | /// |
1326 | | /// (This trait method's design and doc text derives from |
1327 | | /// regalloc.rs' trait of the same name.) |
1328 | | fn spillslot_size(&self, regclass: RegClass) -> usize; |
1329 | | |
1330 | | /// When providing a spillslot number for a multi-slot spillslot, |
1331 | | /// do we provide the first or the last? This is usually related |
1332 | | /// to which direction the stack grows and different clients may |
1333 | | /// have different preferences. |
1334 | 140k | fn multi_spillslot_named_by_last_slot(&self) -> bool { |
1335 | 140k | false |
1336 | 140k | } |
1337 | | |
1338 | | // ----------- |
1339 | | // Misc config |
1340 | | // ----------- |
1341 | | |
1342 | | /// Allow a single instruction to define a vreg multiple times. If |
1343 | | /// allowed, the semantics are as if the definition occurs only |
1344 | | /// once, and all defs will get the same alloc. This flexibility is |
1345 | | /// meant to allow the embedder to more easily aggregate operands |
1346 | | /// together in macro/pseudoinstructions, or e.g. add additional |
1347 | | /// clobbered vregs without taking care to deduplicate. This may be |
1348 | | /// particularly useful when referring to physical registers via |
1349 | | /// pinned vregs. It is optional functionality because a strict mode |
1350 | | /// (at most one def per vreg) is also useful for finding bugs in |
1351 | | /// other applications. |
1352 | 22.5M | fn allow_multiple_vreg_defs(&self) -> bool { |
1353 | 22.5M | false |
1354 | 22.5M | } |
1355 | | } |
1356 | | |
1357 | | /// A position before or after an instruction at which we can make an |
1358 | | /// edit. |
1359 | | /// |
1360 | | /// Note that this differs from `OperandPos` in that the former |
1361 | | /// describes specifically a constraint on an operand, while this |
1362 | | /// describes a program point. `OperandPos` could grow more options in |
1363 | | /// the future, for example if we decide that an "early write" or |
1364 | | /// "late read" phase makes sense, while `InstPosition` will always |
1365 | | /// describe these two insertion points. |
1366 | | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] |
1367 | | #[repr(u8)] |
1368 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
1369 | | pub enum InstPosition { |
1370 | | Before = 0, |
1371 | | After = 1, |
1372 | | } |
1373 | | |
1374 | | /// A program point: a single point before or after a given instruction. |
1375 | | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
1376 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
1377 | | pub struct ProgPoint { |
1378 | | bits: u32, |
1379 | | } |
1380 | | |
1381 | | impl core::fmt::Debug for ProgPoint { |
1382 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
1383 | 0 | write!( |
1384 | 0 | f, |
1385 | | "progpoint{}{}", |
1386 | 0 | self.inst().index(), |
1387 | 0 | match self.pos() { |
1388 | 0 | InstPosition::Before => "-pre", |
1389 | 0 | InstPosition::After => "-post", |
1390 | | } |
1391 | | ) |
1392 | 0 | } |
1393 | | } |
1394 | | |
1395 | | impl ProgPoint { |
1396 | | /// Create a new ProgPoint before or after the given instruction. |
1397 | | #[inline(always)] |
1398 | 135M | pub fn new(inst: Inst, pos: InstPosition) -> Self { |
1399 | 135M | let bits = ((inst.0 as u32) << 1) | (pos as u8 as u32); |
1400 | 135M | Self { bits } |
1401 | 135M | } |
1402 | | |
1403 | | /// Create a new ProgPoint before the given instruction. |
1404 | | #[inline(always)] |
1405 | 106M | pub fn before(inst: Inst) -> Self { |
1406 | 106M | Self::new(inst, InstPosition::Before) |
1407 | 106M | } |
1408 | | |
1409 | | /// Create a new ProgPoint after the given instruction. |
1410 | | #[inline(always)] |
1411 | 28.8M | pub fn after(inst: Inst) -> Self { |
1412 | 28.8M | Self::new(inst, InstPosition::After) |
1413 | 28.8M | } |
1414 | | |
1415 | | /// Get the instruction that this ProgPoint is before or after. |
1416 | | #[inline(always)] |
1417 | 166M | pub fn inst(self) -> Inst { |
1418 | | // Cast to i32 to do an arithmetic right-shift, which will |
1419 | | // preserve an `Inst::invalid()` (which is -1, or all-ones). |
1420 | 166M | Inst::new(((self.bits as i32) >> 1) as usize) |
1421 | 166M | } |
1422 | | |
1423 | | /// Get the "position" (Before or After) relative to the |
1424 | | /// instruction. |
1425 | | #[inline(always)] |
1426 | 54.5M | pub fn pos(self) -> InstPosition { |
1427 | 54.5M | match self.bits & 1 { |
1428 | 31.6M | 0 => InstPosition::Before, |
1429 | 22.9M | 1 => InstPosition::After, |
1430 | 0 | _ => unreachable!(), |
1431 | | } |
1432 | 54.5M | } |
1433 | | |
1434 | | /// Get the "next" program point: for After, this is the Before of |
1435 | | /// the next instruction, while for Before, this is After of the |
1436 | | /// same instruction. |
1437 | | #[inline(always)] |
1438 | 42.2M | pub fn next(self) -> ProgPoint { |
1439 | 42.2M | Self { |
1440 | 42.2M | bits: self.bits + 1, |
1441 | 42.2M | } |
1442 | 42.2M | } |
1443 | | |
1444 | | /// Get the "previous" program point, the inverse of `.next()` |
1445 | | /// above. |
1446 | | #[inline(always)] |
1447 | 650k | pub fn prev(self) -> ProgPoint { |
1448 | 650k | Self { |
1449 | 650k | bits: self.bits - 1, |
1450 | 650k | } |
1451 | 650k | } |
1452 | | |
1453 | | /// Convert to a raw encoding in 32 bits. |
1454 | | #[inline(always)] |
1455 | 543M | pub fn to_index(self) -> u32 { |
1456 | 543M | self.bits |
1457 | 543M | } |
1458 | | |
1459 | | /// Construct from the raw 32-bit encoding. |
1460 | | #[inline(always)] |
1461 | 60.3M | pub fn from_index(index: u32) -> Self { |
1462 | 60.3M | Self { bits: index } |
1463 | 60.3M | } |
1464 | | |
1465 | | #[inline(always)] |
1466 | 0 | pub fn invalid() -> Self { |
1467 | 0 | Self::before(Inst::new(usize::MAX)) |
1468 | 0 | } |
1469 | | } |
1470 | | |
1471 | | /// An instruction to insert into the program to perform some data movement. |
1472 | | #[derive(Clone, Debug)] |
1473 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
1474 | | pub enum Edit { |
1475 | | /// Move one allocation to another. Each allocation may be a |
1476 | | /// register or a stack slot (spillslot). However, stack-to-stack |
1477 | | /// moves will never be generated. |
1478 | | /// |
1479 | | /// `Move` edits will be generated even if src and dst allocation |
1480 | | /// are the same if the vreg changes; this allows proper metadata |
1481 | | /// tracking even when moves are elided. |
1482 | | Move { from: Allocation, to: Allocation }, |
1483 | | } |
1484 | | |
1485 | | /// Wrapper around either an original instruction or an inserted edit. |
1486 | | #[derive(Clone, Debug)] |
1487 | | pub enum InstOrEdit<'a> { |
1488 | | Inst(Inst), |
1489 | | Edit(&'a Edit), |
1490 | | } |
1491 | | |
1492 | | /// Iterator over the instructions and edits in a block. |
1493 | | pub struct OutputIter<'a> { |
1494 | | /// List of edits starting at the first for the current block. |
1495 | | edits: &'a [(ProgPoint, Edit)], |
1496 | | |
1497 | | /// Remaining instructions in the current block. |
1498 | | inst_range: InstRange, |
1499 | | } |
1500 | | |
1501 | | impl<'a> Iterator for OutputIter<'a> { |
1502 | | type Item = InstOrEdit<'a>; |
1503 | | |
1504 | 5.03M | fn next(&mut self) -> Option<InstOrEdit<'a>> { |
1505 | | // There can't be any edits after the last instruction in a block, so |
1506 | | // we don't need to worry about that case. |
1507 | 5.03M | if self.inst_range.len() == 0 { |
1508 | 403k | return None; |
1509 | 4.62M | } |
1510 | | |
1511 | | // Return any edits that happen before the next instruction first. |
1512 | 4.62M | let next_inst = self.inst_range.first(); |
1513 | 4.62M | if let Some((edit, remaining_edits)) = self.edits.split_first() { |
1514 | 3.67M | if edit.0 <= ProgPoint::before(next_inst) { |
1515 | 957k | self.edits = remaining_edits; |
1516 | 957k | return Some(InstOrEdit::Edit(&edit.1)); |
1517 | 2.71M | } |
1518 | 950k | } |
1519 | | |
1520 | 3.66M | self.inst_range = self.inst_range.rest(); |
1521 | 3.66M | Some(InstOrEdit::Inst(next_inst)) |
1522 | 5.03M | } |
1523 | | } |
1524 | | |
1525 | | /// A machine environment tells the register allocator which registers |
1526 | | /// are available to allocate and what register may be used as a |
1527 | | /// scratch register for each class, and some other miscellaneous info |
1528 | | /// as well. |
1529 | | #[derive(Clone, Debug)] |
1530 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
1531 | | pub struct MachineEnv { |
1532 | | /// Preferred physical registers for each class. These are the |
1533 | | /// registers that will be allocated first, if free. |
1534 | | /// |
1535 | | /// If an explicit scratch register is provided in `scratch_by_class` then |
1536 | | /// it must not appear in this list. |
1537 | | pub preferred_regs_by_class: [PRegSet; 3], |
1538 | | |
1539 | | /// Non-preferred physical registers for each class. These are the |
1540 | | /// registers that will be allocated if a preferred register is |
1541 | | /// not available; using one of these is considered suboptimal, |
1542 | | /// but still better than spilling. |
1543 | | /// |
1544 | | /// If an explicit scratch register is provided in `scratch_by_class` then |
1545 | | /// it must not appear in this list. |
1546 | | pub non_preferred_regs_by_class: [PRegSet; 3], |
1547 | | |
1548 | | /// Optional dedicated scratch register per class. This is needed to perform |
1549 | | /// moves between registers when cyclic move patterns occur. The |
1550 | | /// register should not be placed in either the preferred or |
1551 | | /// non-preferred list (i.e., it is not otherwise allocatable). |
1552 | | /// |
1553 | | /// Note that the register allocator will freely use this register |
1554 | | /// between instructions, but *within* the machine code generated |
1555 | | /// by a single (regalloc-level) instruction, the client is free |
1556 | | /// to use the scratch register. E.g., if one "instruction" causes |
1557 | | /// the emission of two machine-code instructions, this lowering |
1558 | | /// can use the scratch register between them. |
1559 | | /// |
1560 | | /// If a scratch register is not provided then the register allocator will |
1561 | | /// automatically allocate one as needed, spilling a value to the stack if |
1562 | | /// necessary. |
1563 | | pub scratch_by_class: [Option<PReg>; 3], |
1564 | | |
1565 | | /// Some `PReg`s can be designated as locations on the stack rather than |
1566 | | /// actual registers. These can be used to tell the register allocator about |
1567 | | /// pre-defined stack slots used for function arguments and return values. |
1568 | | /// |
1569 | | /// `PReg`s in this list cannot be used as an allocatable or scratch |
1570 | | /// register. |
1571 | | pub fixed_stack_slots: Vec<PReg>, |
1572 | | } |
1573 | | |
1574 | | /// The output of the register allocator. |
1575 | | #[derive(Clone, Debug, Default)] |
1576 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
1577 | | pub struct Output { |
1578 | | /// How many spillslots are needed in the frame? |
1579 | | pub num_spillslots: usize, |
1580 | | |
1581 | | /// Edits (insertions or removals). Guaranteed to be sorted by |
1582 | | /// program point. |
1583 | | pub edits: Vec<(ProgPoint, Edit)>, |
1584 | | |
1585 | | /// Allocations for each operand. Mapping from instruction to |
1586 | | /// allocations provided by `inst_alloc_offsets` below. |
1587 | | pub allocs: Vec<Allocation>, |
1588 | | |
1589 | | /// Allocation offset in `allocs` for each instruction. |
1590 | | pub inst_alloc_offsets: Vec<u32>, |
1591 | | |
1592 | | /// Debug info: a labeled value (as applied to vregs by |
1593 | | /// `Function::debug_value_labels()` on the input side) is located |
1594 | | /// in the given allocation from the first program point |
1595 | | /// (inclusive) to the second (exclusive). Guaranteed to be sorted |
1596 | | /// by label and program point, and the ranges are guaranteed to |
1597 | | /// be disjoint. |
1598 | | pub debug_locations: Vec<(u32, ProgPoint, ProgPoint, Allocation)>, |
1599 | | |
1600 | | /// Internal stats from the allocator. |
1601 | | pub stats: ion::Stats, |
1602 | | } |
1603 | | |
1604 | | impl Output { |
1605 | | /// Get the allocations assigned to a given instruction. |
1606 | 3.66M | pub fn inst_allocs(&self, inst: Inst) -> &[Allocation] { |
1607 | 3.66M | let start = self.inst_alloc_offsets[inst.index()] as usize; |
1608 | 3.66M | let end = if inst.index() + 1 == self.inst_alloc_offsets.len() { |
1609 | 10.0k | self.allocs.len() |
1610 | | } else { |
1611 | 3.65M | self.inst_alloc_offsets[inst.index() + 1] as usize |
1612 | | }; |
1613 | 3.66M | &self.allocs[start..end] |
1614 | 3.66M | } |
1615 | | |
1616 | | /// Returns an iterator over the instructions and edits in a block, in |
1617 | | /// order. |
1618 | 403k | pub fn block_insts_and_edits(&self, func: &impl Function, block: Block) -> OutputIter<'_> { |
1619 | 403k | let inst_range = func.block_insns(block); |
1620 | | |
1621 | 403k | let edit_idx = self |
1622 | 403k | .edits |
1623 | 2.49M | .binary_search_by(|&(pos, _)| { |
1624 | | // This predicate effectively searches for a point *just* before |
1625 | | // the first ProgPoint. This never returns Ordering::Equal, but |
1626 | | // binary_search_by returns the index of where it would have |
1627 | | // been inserted in Err. |
1628 | 2.49M | if pos < ProgPoint::before(inst_range.first()) { |
1629 | 1.43M | core::cmp::Ordering::Less |
1630 | | } else { |
1631 | 1.05M | core::cmp::Ordering::Greater |
1632 | | } |
1633 | 2.49M | }) |
1634 | 403k | .unwrap_err(); |
1635 | | |
1636 | 403k | let edits = &self.edits[edit_idx..]; |
1637 | 403k | OutputIter { inst_range, edits } |
1638 | 403k | } |
1639 | | } |
1640 | | |
1641 | | /// An error that prevents allocation. |
1642 | | #[derive(Clone, Debug)] |
1643 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
1644 | | pub enum RegAllocError { |
1645 | | /// Critical edge is not split between given blocks. |
1646 | | CritEdge(Block, Block), |
1647 | | /// Invalid SSA for given vreg at given inst: multiple defs or |
1648 | | /// illegal use. `inst` may be `Inst::invalid()` if this concerns |
1649 | | /// a block param. |
1650 | | SSA(VReg, Inst), |
1651 | | /// Invalid basic block: does not end in branch/ret, or contains a |
1652 | | /// branch/ret in the middle, or the VReg ids do not start at zero |
1653 | | /// or aren't numbered sequentially. |
1654 | | BB(Block), |
1655 | | /// Invalid branch: operand count does not match sum of block |
1656 | | /// params of successor blocks, or the block ids do not start at |
1657 | | /// zero or aren't numbered sequentially. |
1658 | | Branch(Inst), |
1659 | | /// A VReg is live-in on entry; this is not allowed. |
1660 | | EntryLivein, |
1661 | | /// A branch has non-blockparam arg(s) and at least one of the |
1662 | | /// successor blocks has more than one predecessor, forcing |
1663 | | /// edge-moves before this branch. This is disallowed because it |
1664 | | /// places a use after the edge moves occur; insert an edge block |
1665 | | /// to avoid the situation. |
1666 | | DisallowedBranchArg(Inst), |
1667 | | /// Too many pinned VRegs + Reg-constrained Operands are live at |
1668 | | /// once, making allocation impossible. |
1669 | | TooManyLiveRegs, |
1670 | | /// Too many operands on a single instruction (beyond limit of |
1671 | | /// 2^16 - 1). |
1672 | | TooManyOperands, |
1673 | | } |
1674 | | |
1675 | | impl core::fmt::Display for RegAllocError { |
1676 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
1677 | 0 | write!(f, "{:?}", self) |
1678 | 0 | } |
1679 | | } |
1680 | | |
1681 | | impl core::error::Error for RegAllocError {} |
1682 | | |
1683 | | /// Run the allocator. |
1684 | 0 | pub fn run<F: Function>( |
1685 | 0 | func: &F, |
1686 | 0 | env: &MachineEnv, |
1687 | 0 | options: &RegallocOptions, |
1688 | 0 | ) -> Result<Output, RegAllocError> { |
1689 | 0 | match options.algorithm { |
1690 | | Algorithm::Ion => { |
1691 | 0 | let mut ctx = Ctx::default(); |
1692 | 0 | run_with_ctx(func, env, options, &mut ctx)?; |
1693 | 0 | Ok(ctx.output) |
1694 | | } |
1695 | | Algorithm::Fastalloc => { |
1696 | 0 | fastalloc::run(func, env, options.verbose_log, options.validate_ssa) |
1697 | | } |
1698 | | } |
1699 | 0 | } |
1700 | | |
1701 | | /// Run the allocator with reusable context. |
1702 | | /// |
1703 | | /// Return value points to `ctx.output` that can be alternatively `std::mem::take`n. |
1704 | 0 | pub fn run_with_ctx<'a, F: Function>( |
1705 | 0 | func: &F, |
1706 | 0 | env: &MachineEnv, |
1707 | 0 | options: &RegallocOptions, |
1708 | 0 | ctx: &'a mut Ctx, |
1709 | 0 | ) -> Result<&'a Output, RegAllocError> { |
1710 | 0 | match options.algorithm { |
1711 | 0 | Algorithm::Ion => ion::run(func, env, ctx, options.verbose_log, options.validate_ssa)?, |
1712 | | Algorithm::Fastalloc => { |
1713 | 0 | ctx.output = fastalloc::run(func, env, options.verbose_log, options.validate_ssa)? |
1714 | | } |
1715 | | } |
1716 | 0 | Ok(&ctx.output) |
1717 | 0 | } |
1718 | | |
1719 | | #[derive(Clone, Copy, Debug, Default)] |
1720 | | pub enum Algorithm { |
1721 | | #[default] |
1722 | | Ion, |
1723 | | Fastalloc, |
1724 | | } |
1725 | | |
1726 | | /// Options for allocation. |
1727 | | #[derive(Clone, Copy, Debug, Default)] |
1728 | | pub struct RegallocOptions { |
1729 | | /// Add extra verbosity to debug logs. |
1730 | | pub verbose_log: bool, |
1731 | | |
1732 | | /// Run the SSA validator before allocating registers. |
1733 | | pub validate_ssa: bool, |
1734 | | |
1735 | | /// The register allocation algorithm to be used. |
1736 | | pub algorithm: Algorithm, |
1737 | | } |
1738 | | |
1739 | | pub(crate) trait VecExt<T> { |
1740 | | /// Fills `self` with `value` up to `len` and return the mutable slice to the values. |
1741 | | fn repopulate(&mut self, len: usize, value: T) -> &mut [T] |
1742 | | where |
1743 | | T: Clone; |
1744 | | /// Clears the `self` and returns a mutable reference to it. |
1745 | | fn cleared(&mut self) -> &mut Self; |
1746 | | /// Makes sure `self` is empty and has at least `cap` capacity. |
1747 | | fn preallocate(&mut self, cap: usize) -> &mut Self; |
1748 | | } |
1749 | | |
1750 | | impl<T> VecExt<T> for Vec<T> { |
1751 | 134k | fn repopulate(&mut self, len: usize, value: T) -> &mut [T] |
1752 | 134k | where |
1753 | 134k | T: Clone, |
1754 | | { |
1755 | 134k | self.clear(); |
1756 | 134k | self.resize(len, value); |
1757 | 134k | self |
1758 | 134k | } <alloc::vec::Vec<core::option::Option<u32>> as regalloc2::VecExt<core::option::Option<u32>>>::repopulate Line | Count | Source | 1751 | 24.3k | fn repopulate(&mut self, len: usize, value: T) -> &mut [T] | 1752 | 24.3k | where | 1753 | 24.3k | T: Clone, | 1754 | | { | 1755 | 24.3k | self.clear(); | 1756 | 24.3k | self.resize(len, value); | 1757 | 24.3k | self | 1758 | 24.3k | } |
<alloc::vec::Vec<regalloc2::ProgPoint> as regalloc2::VecExt<regalloc2::ProgPoint>>::repopulate Line | Count | Source | 1751 | 24.3k | fn repopulate(&mut self, len: usize, value: T) -> &mut [T] | 1752 | 24.3k | where | 1753 | 24.3k | T: Clone, | 1754 | | { | 1755 | 24.3k | self.clear(); | 1756 | 24.3k | self.resize(len, value); | 1757 | 24.3k | self | 1758 | 24.3k | } |
<alloc::vec::Vec<regalloc2::index::Block> as regalloc2::VecExt<regalloc2::index::Block>>::repopulate Line | Count | Source | 1751 | 36.5k | fn repopulate(&mut self, len: usize, value: T) -> &mut [T] | 1752 | 36.5k | where | 1753 | 36.5k | T: Clone, | 1754 | | { | 1755 | 36.5k | self.clear(); | 1756 | 36.5k | self.resize(len, value); | 1757 | 36.5k | self | 1758 | 36.5k | } |
<alloc::vec::Vec<regalloc2::ion::data_structures::LiveRangeIndex> as regalloc2::VecExt<regalloc2::ion::data_structures::LiveRangeIndex>>::repopulate Line | Count | Source | 1751 | 12.1k | fn repopulate(&mut self, len: usize, value: T) -> &mut [T] | 1752 | 12.1k | where | 1753 | 12.1k | T: Clone, | 1754 | | { | 1755 | 12.1k | self.clear(); | 1756 | 12.1k | self.resize(len, value); | 1757 | 12.1k | self | 1758 | 12.1k | } |
<alloc::vec::Vec<bool> as regalloc2::VecExt<bool>>::repopulate Line | Count | Source | 1751 | 24.3k | fn repopulate(&mut self, len: usize, value: T) -> &mut [T] | 1752 | 24.3k | where | 1753 | 24.3k | T: Clone, | 1754 | | { | 1755 | 24.3k | self.clear(); | 1756 | 24.3k | self.resize(len, value); | 1757 | 24.3k | self | 1758 | 24.3k | } |
<alloc::vec::Vec<u32> as regalloc2::VecExt<u32>>::repopulate Line | Count | Source | 1751 | 12.1k | fn repopulate(&mut self, len: usize, value: T) -> &mut [T] | 1752 | 12.1k | where | 1753 | 12.1k | T: Clone, | 1754 | | { | 1755 | 12.1k | self.clear(); | 1756 | 12.1k | self.resize(len, value); | 1757 | 12.1k | self | 1758 | 12.1k | } |
|
1759 | | |
1760 | 12.1k | fn cleared(&mut self) -> &mut Self { |
1761 | 12.1k | self.clear(); |
1762 | 12.1k | self |
1763 | 12.1k | } |
1764 | | |
1765 | 85.2k | fn preallocate(&mut self, cap: usize) -> &mut Self { |
1766 | 85.2k | self.clear(); |
1767 | 85.2k | self.reserve(cap); |
1768 | 85.2k | self |
1769 | 85.2k | } <alloc::vec::Vec<regalloc2::Allocation> as regalloc2::VecExt<regalloc2::Allocation>>::preallocate Line | Count | Source | 1765 | 12.1k | fn preallocate(&mut self, cap: usize) -> &mut Self { | 1766 | 12.1k | self.clear(); | 1767 | 12.1k | self.reserve(cap); | 1768 | 12.1k | self | 1769 | 12.1k | } |
<alloc::vec::Vec<regalloc2::indexset::IndexSet> as regalloc2::VecExt<regalloc2::indexset::IndexSet>>::preallocate Line | Count | Source | 1765 | 24.3k | fn preallocate(&mut self, cap: usize) -> &mut Self { | 1766 | 24.3k | self.clear(); | 1767 | 24.3k | self.reserve(cap); | 1768 | 24.3k | self | 1769 | 24.3k | } |
<alloc::vec::Vec<regalloc2::ion::data_structures::LiveBundle> as regalloc2::VecExt<regalloc2::ion::data_structures::LiveBundle>>::preallocate Line | Count | Source | 1765 | 12.1k | fn preallocate(&mut self, cap: usize) -> &mut Self { | 1766 | 12.1k | self.clear(); | 1767 | 12.1k | self.reserve(cap); | 1768 | 12.1k | self | 1769 | 12.1k | } |
<alloc::vec::Vec<regalloc2::ion::data_structures::SpillSet> as regalloc2::VecExt<regalloc2::ion::data_structures::SpillSet>>::preallocate Line | Count | Source | 1765 | 12.1k | fn preallocate(&mut self, cap: usize) -> &mut Self { | 1766 | 12.1k | self.clear(); | 1767 | 12.1k | self.reserve(cap); | 1768 | 12.1k | self | 1769 | 12.1k | } |
<alloc::vec::Vec<regalloc2::ion::data_structures::VRegData> as regalloc2::VecExt<regalloc2::ion::data_structures::VRegData>>::preallocate Line | Count | Source | 1765 | 12.1k | fn preallocate(&mut self, cap: usize) -> &mut Self { | 1766 | 12.1k | self.clear(); | 1767 | 12.1k | self.reserve(cap); | 1768 | 12.1k | self | 1769 | 12.1k | } |
<alloc::vec::Vec<regalloc2::ion::data_structures::LiveRange> as regalloc2::VecExt<regalloc2::ion::data_structures::LiveRange>>::preallocate Line | Count | Source | 1765 | 12.1k | fn preallocate(&mut self, cap: usize) -> &mut Self { | 1766 | 12.1k | self.clear(); | 1767 | 12.1k | self.reserve(cap); | 1768 | 12.1k | self | 1769 | 12.1k | } |
|
1770 | | } |
1771 | | |
1772 | | #[derive(Debug, Clone, Default)] |
1773 | | pub(crate) struct Bump(Rc<bumpalo::Bump>); |
1774 | | |
1775 | | impl Bump { |
1776 | 12.1k | pub(crate) fn get_mut(&mut self) -> Option<&mut bumpalo::Bump> { |
1777 | 12.1k | Rc::get_mut(&mut self.0) |
1778 | 12.1k | } |
1779 | | } |
1780 | | |
1781 | | // Simply delegating because `Rc<bumpalo::Bump>` does not implement `Allocator`. |
1782 | | unsafe impl allocator_api2::alloc::Allocator for Bump { |
1783 | 36.2M | fn allocate( |
1784 | 36.2M | &self, |
1785 | 36.2M | layout: core::alloc::Layout, |
1786 | 36.2M | ) -> Result<core::ptr::NonNull<[u8]>, allocator_api2::alloc::AllocError> { |
1787 | 36.2M | self.0.deref().allocate(layout) |
1788 | 36.2M | } |
1789 | | |
1790 | 36.1M | unsafe fn deallocate(&self, ptr: core::ptr::NonNull<u8>, layout: core::alloc::Layout) { |
1791 | 36.1M | self.0.deref().deallocate(ptr, layout); |
1792 | 36.1M | } |
1793 | | |
1794 | 0 | fn allocate_zeroed( |
1795 | 0 | &self, |
1796 | 0 | layout: core::alloc::Layout, |
1797 | 0 | ) -> Result<core::ptr::NonNull<[u8]>, allocator_api2::alloc::AllocError> { |
1798 | 0 | self.0.deref().allocate_zeroed(layout) |
1799 | 0 | } |
1800 | | |
1801 | 3.06M | unsafe fn grow( |
1802 | 3.06M | &self, |
1803 | 3.06M | ptr: core::ptr::NonNull<u8>, |
1804 | 3.06M | old_layout: core::alloc::Layout, |
1805 | 3.06M | new_layout: core::alloc::Layout, |
1806 | 3.06M | ) -> Result<core::ptr::NonNull<[u8]>, allocator_api2::alloc::AllocError> { |
1807 | 3.06M | self.0.deref().grow(ptr, old_layout, new_layout) |
1808 | 3.06M | } |
1809 | | |
1810 | 0 | unsafe fn grow_zeroed( |
1811 | 0 | &self, |
1812 | 0 | ptr: core::ptr::NonNull<u8>, |
1813 | 0 | old_layout: core::alloc::Layout, |
1814 | 0 | new_layout: core::alloc::Layout, |
1815 | 0 | ) -> Result<core::ptr::NonNull<[u8]>, allocator_api2::alloc::AllocError> { |
1816 | 0 | self.0.deref().grow_zeroed(ptr, old_layout, new_layout) |
1817 | 0 | } |
1818 | | |
1819 | 969k | unsafe fn shrink( |
1820 | 969k | &self, |
1821 | 969k | ptr: core::ptr::NonNull<u8>, |
1822 | 969k | old_layout: core::alloc::Layout, |
1823 | 969k | new_layout: core::alloc::Layout, |
1824 | 969k | ) -> Result<core::ptr::NonNull<[u8]>, allocator_api2::alloc::AllocError> { |
1825 | 969k | self.0.deref().shrink(ptr, old_layout, new_layout) |
1826 | 969k | } |
1827 | | } |
1828 | | |
1829 | | #[cfg(test)] |
1830 | | mod tests { |
1831 | | use super::{PReg, PRegSet, RegClass::*}; |
1832 | | |
1833 | | #[test] |
1834 | | fn preg_set_len() { |
1835 | | let mut set = PRegSet::empty(); |
1836 | | assert_eq!(set.len(), 0); |
1837 | | |
1838 | | set.add(PReg::new(3, Int)); |
1839 | | assert_eq!(set.len(), 1); |
1840 | | set.add(PReg::new(3, Int)); |
1841 | | assert_eq!(set.len(), 1); |
1842 | | |
1843 | | set.add(PReg::new(4, Int)); |
1844 | | assert_eq!(set.len(), 2); |
1845 | | } |
1846 | | |
1847 | | #[test] |
1848 | | fn preg_set_max_preg() { |
1849 | | let mut set = PRegSet::empty(); |
1850 | | assert_eq!(set.max_preg(), None); |
1851 | | |
1852 | | set.add(PReg::new(3, Int)); |
1853 | | assert_eq!(set.max_preg(), Some(PReg::new(3, Int))); |
1854 | | |
1855 | | set.add(PReg::new(4, Int)); |
1856 | | assert_eq!(set.max_preg(), Some(PReg::new(4, Int))); |
1857 | | |
1858 | | set.add(PReg::new(2, Int)); |
1859 | | assert_eq!(set.max_preg(), Some(PReg::new(4, Int))); |
1860 | | } |
1861 | | |
1862 | | #[test] |
1863 | | fn preg_set_new_up_to() { |
1864 | | for class in [Int, Float, Vector] { |
1865 | | let p0 = PReg::new(0, class); |
1866 | | let p1 = PReg::new(1, class); |
1867 | | let p2 = PReg::new(2, class); |
1868 | | let p3 = PReg::new(3, class); |
1869 | | { |
1870 | | let mut set = PRegSet::empty(); |
1871 | | set.add_up_to(p1); |
1872 | | assert!(set.contains(p0)); |
1873 | | assert!(!set.contains(p1)); |
1874 | | } |
1875 | | { |
1876 | | let mut set = PRegSet::empty(); |
1877 | | set.add_up_to(p0); |
1878 | | assert!(!set.contains(p0)); |
1879 | | } |
1880 | | { |
1881 | | let mut set = PRegSet::empty(); |
1882 | | set.add_up_to(p3); |
1883 | | assert!(set.contains(p0)); |
1884 | | assert!(set.contains(p1)); |
1885 | | assert!(set.contains(p2)); |
1886 | | assert!(!set.contains(p3)); |
1887 | | } |
1888 | | for i in 1..64 { |
1889 | | let mut set = PRegSet::empty(); |
1890 | | set.add_up_to(PReg::new(i, class)); |
1891 | | assert!(set.contains(p0)); |
1892 | | assert!(set.contains(PReg::new(i - 1, class))); |
1893 | | assert!(!set.contains(PReg::new(i, class))); |
1894 | | } |
1895 | | } |
1896 | | } |
1897 | | } |