1 //! Definitions for registers, operands, etc. Provides a thin 2 //! interface over the register allocator so that we can more easily 3 //! swap it out or shim it when necessary. 4 5 use alloc::{string::String, vec::Vec}; 6 use core::{fmt::Debug, hash::Hash}; 7 use regalloc2::{Operand, OperandConstraint, OperandKind, OperandPos, PReg, PRegSet, VReg}; 8 9 #[cfg(feature = "enable-serde")] 10 use serde_derive::{Deserialize, Serialize}; 11 12 /// The first 192 vregs (64 int, 64 float, 64 vec) are "pinned" to 13 /// physical registers: this means that they are always constrained to 14 /// the corresponding register at all use/mod/def sites. 15 /// 16 /// Arbitrary vregs can also be constrained to physical registers at 17 /// particular use/def/mod sites, and this is preferable; but pinned 18 /// vregs allow us to migrate code that has been written using 19 /// RealRegs directly. 20 const PINNED_VREGS: usize = 192; 21 22 /// Convert a `VReg` to its pinned `PReg`, if any. 23 pub fn pinned_vreg_to_preg(vreg: VReg) -> Option<PReg> { 24 if vreg.vreg() < PINNED_VREGS { 25 Some(PReg::from_index(vreg.vreg())) 26 } else { 27 None 28 } 29 } 30 31 /// Give the first available vreg for generated code (i.e., after all 32 /// pinned vregs). 33 pub fn first_user_vreg_index() -> usize { 34 // This is just the constant defined above, but we keep the 35 // constant private and expose only this helper function with the 36 // specific name in order to ensure other parts of the code don't 37 // open-code and depend on the index-space scheme. 38 PINNED_VREGS 39 } 40 41 /// A register named in an instruction. This register can be either a 42 /// virtual register or a fixed physical register. It does not have 43 /// any constraints applied to it: those can be added later in 44 /// `MachInst::get_operands()` when the `Reg`s are converted to 45 /// `Operand`s. 46 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 47 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] 48 pub struct Reg(VReg); 49 50 impl Reg { 51 /// Get the physical register (`RealReg`), if this register is 52 /// one. 53 pub fn to_real_reg(self) -> Option<RealReg> { 54 pinned_vreg_to_preg(self.0).map(RealReg) 55 } 56 57 /// Get the virtual (non-physical) register, if this register is 58 /// one. 59 pub fn to_virtual_reg(self) -> Option<VirtualReg> { 60 if pinned_vreg_to_preg(self.0).is_none() { 61 Some(VirtualReg(self.0)) 62 } else { 63 None 64 } 65 } 66 67 /// Get the class of this register. 68 pub fn class(self) -> RegClass { 69 self.0.class() 70 } 71 72 /// Is this a real (physical) reg? 73 pub fn is_real(self) -> bool { 74 self.to_real_reg().is_some() 75 } 76 77 /// Is this a virtual reg? 78 pub fn is_virtual(self) -> bool { 79 self.to_virtual_reg().is_some() 80 } 81 } 82 83 impl std::fmt::Debug for Reg { 84 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 85 if let Some(rreg) = self.to_real_reg() { 86 let preg: PReg = rreg.into(); 87 write!(f, "{}", preg) 88 } else if let Some(vreg) = self.to_virtual_reg() { 89 let vreg: VReg = vreg.into(); 90 write!(f, "{}", vreg) 91 } else { 92 unreachable!() 93 } 94 } 95 } 96 97 impl AsMut<Reg> for Reg { 98 fn as_mut(&mut self) -> &mut Reg { 99 self 100 } 101 } 102 103 /// A real (physical) register. This corresponds to one of the target 104 /// ISA's named registers and can be used as an instruction operand. 105 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 106 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] 107 pub struct RealReg(PReg); 108 109 impl RealReg { 110 /// Get the class of this register. 111 pub fn class(self) -> RegClass { 112 self.0.class() 113 } 114 115 /// The physical register number. 116 pub fn hw_enc(self) -> u8 { 117 self.0.hw_enc() as u8 118 } 119 } 120 121 impl std::fmt::Debug for RealReg { 122 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 123 Reg::from(*self).fmt(f) 124 } 125 } 126 127 /// A virtual register. This can be allocated into a real (physical) 128 /// register of the appropriate register class, but which one is not 129 /// specified. Virtual registers are used when generating `MachInst`s, 130 /// before register allocation occurs, in order to allow us to name as 131 /// many register-carried values as necessary. 132 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 133 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] 134 pub struct VirtualReg(VReg); 135 136 impl VirtualReg { 137 /// Get the class of this register. 138 pub fn class(self) -> RegClass { 139 self.0.class() 140 } 141 142 pub fn index(self) -> usize { 143 self.0.vreg() 144 } 145 } 146 147 impl std::fmt::Debug for VirtualReg { 148 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 149 Reg::from(*self).fmt(f) 150 } 151 } 152 153 /// A type wrapper that indicates a register type is writable. The 154 /// underlying register can be extracted, and the type wrapper can be 155 /// built using an arbitrary register. Hence, this type-level wrapper 156 /// is not strictly a guarantee. However, "casting" to a writable 157 /// register is an explicit operation for which we can 158 /// audit. Ordinarily, internal APIs in the compiler backend should 159 /// take a `Writable<Reg>` whenever the register is written, and the 160 /// usual, frictionless way to get one of these is to allocate a new 161 /// temporary. 162 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 163 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] 164 pub struct Writable<T> { 165 reg: T, 166 } 167 168 impl<T> Writable<T> { 169 /// Explicitly construct a `Writable<T>` from a `T`. As noted in 170 /// the documentation for `Writable`, this is not hidden or 171 /// disallowed from the outside; anyone can perform the "cast"; 172 /// but it is explicit so that we can audit the use sites. 173 pub fn from_reg(reg: T) -> Writable<T> { 174 Writable { reg } 175 } 176 177 /// Get the underlying register, which can be read. 178 pub fn to_reg(self) -> T { 179 self.reg 180 } 181 182 /// Map the underlying register to another value or type. 183 pub fn map<U>(self, f: impl Fn(T) -> U) -> Writable<U> { 184 Writable { reg: f(self.reg) } 185 } 186 } 187 188 // Conversions between regalloc2 types (VReg, PReg) and our types 189 // (VirtualReg, RealReg, Reg). 190 191 impl std::convert::From<regalloc2::VReg> for Reg { 192 fn from(vreg: regalloc2::VReg) -> Reg { 193 Reg(vreg) 194 } 195 } 196 197 impl std::convert::From<regalloc2::VReg> for VirtualReg { 198 fn from(vreg: regalloc2::VReg) -> VirtualReg { 199 debug_assert!(pinned_vreg_to_preg(vreg).is_none()); 200 VirtualReg(vreg) 201 } 202 } 203 204 impl std::convert::From<Reg> for regalloc2::VReg { 205 /// Extract the underlying `regalloc2::VReg`. Note that physical 206 /// registers also map to particular (special) VRegs, so this 207 /// method can be used either on virtual or physical `Reg`s. 208 fn from(reg: Reg) -> regalloc2::VReg { 209 reg.0 210 } 211 } 212 impl std::convert::From<&Reg> for regalloc2::VReg { 213 fn from(reg: &Reg) -> regalloc2::VReg { 214 reg.0 215 } 216 } 217 218 impl std::convert::From<VirtualReg> for regalloc2::VReg { 219 fn from(reg: VirtualReg) -> regalloc2::VReg { 220 reg.0 221 } 222 } 223 224 impl std::convert::From<RealReg> for regalloc2::VReg { 225 fn from(reg: RealReg) -> regalloc2::VReg { 226 // This representation is redundant: the class is implied in the vreg 227 // index as well as being in the vreg class field. 228 VReg::new(reg.0.index(), reg.0.class()) 229 } 230 } 231 232 impl std::convert::From<RealReg> for regalloc2::PReg { 233 fn from(reg: RealReg) -> regalloc2::PReg { 234 reg.0 235 } 236 } 237 238 impl std::convert::From<regalloc2::PReg> for RealReg { 239 fn from(preg: regalloc2::PReg) -> RealReg { 240 RealReg(preg) 241 } 242 } 243 244 impl std::convert::From<regalloc2::PReg> for Reg { 245 fn from(preg: regalloc2::PReg) -> Reg { 246 RealReg(preg).into() 247 } 248 } 249 250 impl std::convert::From<RealReg> for Reg { 251 fn from(reg: RealReg) -> Reg { 252 Reg(reg.into()) 253 } 254 } 255 256 impl std::convert::From<VirtualReg> for Reg { 257 fn from(reg: VirtualReg) -> Reg { 258 Reg(reg.0) 259 } 260 } 261 262 /// A spill slot. 263 pub type SpillSlot = regalloc2::SpillSlot; 264 265 /// A register class. Each register in the ISA has one class, and the 266 /// classes are disjoint. Most modern ISAs will have just two classes: 267 /// the integer/general-purpose registers (GPRs), and the float/vector 268 /// registers (typically used for both). 269 /// 270 /// Note that unlike some other compiler backend/register allocator 271 /// designs, we do not allow for overlapping classes, i.e. registers 272 /// that belong to more than one class, because doing so makes the 273 /// allocation problem significantly more complex. Instead, when a 274 /// register can be addressed under different names for different 275 /// sizes (for example), the backend author should pick classes that 276 /// denote some fundamental allocation unit that encompasses the whole 277 /// register. For example, always allocate 128-bit vector registers 278 /// `v0`..`vN`, even though `f32` and `f64` values may use only the 279 /// low 32/64 bits of those registers and name them differently. 280 pub type RegClass = regalloc2::RegClass; 281 282 /// An OperandCollector is a wrapper around a Vec of Operands 283 /// (flattened array for a whole sequence of instructions) that 284 /// gathers operands from a single instruction and provides the range 285 /// in the flattened array. 286 #[derive(Debug)] 287 pub struct OperandCollector<'a, F: Fn(VReg) -> VReg> { 288 operands: &'a mut Vec<Operand>, 289 clobbers: PRegSet, 290 291 /// The subset of physical registers that are allocatable. 292 allocatable: PRegSet, 293 294 renamer: F, 295 } 296 297 impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> { 298 /// Start gathering operands into one flattened operand array. 299 pub fn new(operands: &'a mut Vec<Operand>, allocatable: PRegSet, renamer: F) -> Self { 300 Self { 301 operands, 302 clobbers: PRegSet::default(), 303 allocatable, 304 renamer, 305 } 306 } 307 308 /// Finish the operand collection and return the tuple giving the 309 /// range of indices in the flattened operand array, and the 310 /// clobber set. 311 pub fn finish(self) -> (usize, PRegSet) { 312 let end = self.operands.len(); 313 (end, self.clobbers) 314 } 315 } 316 317 pub trait OperandVisitor { 318 fn add_operand( 319 &mut self, 320 reg: &mut Reg, 321 constraint: OperandConstraint, 322 kind: OperandKind, 323 pos: OperandPos, 324 ); 325 326 fn debug_assert_is_allocatable_preg(&self, _reg: PReg, _expected: bool) {} 327 328 /// Add a register clobber set. This is a set of registers that 329 /// are written by the instruction, so must be reserved (not used) 330 /// for the whole instruction, but are not used afterward. 331 fn reg_clobbers(&mut self, _regs: PRegSet) {} 332 } 333 334 pub trait OperandVisitorImpl: OperandVisitor { 335 /// Add a use of a fixed, nonallocatable physical register. 336 fn reg_fixed_nonallocatable(&mut self, preg: PReg) { 337 self.debug_assert_is_allocatable_preg(preg, false); 338 // Since this operand does not participate in register allocation, 339 // there's nothing to do here. 340 } 341 342 /// Add a register use, at the start of the instruction (`Before` 343 /// position). 344 fn reg_use(&mut self, reg: &mut impl AsMut<Reg>) { 345 self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Early); 346 } 347 348 /// Add a register use, at the end of the instruction (`After` position). 349 fn reg_late_use(&mut self, reg: &mut impl AsMut<Reg>) { 350 self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Late); 351 } 352 353 /// Add a register def, at the end of the instruction (`After` 354 /// position). Use only when this def will be written after all 355 /// uses are read. 356 fn reg_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) { 357 self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Late); 358 } 359 360 /// Add a register "early def", which logically occurs at the 361 /// beginning of the instruction, alongside all uses. Use this 362 /// when the def may be written before all uses are read; the 363 /// regalloc will ensure that it does not overwrite any uses. 364 fn reg_early_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) { 365 self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Early); 366 } 367 368 /// Add a register "fixed use", which ties a vreg to a particular 369 /// RealReg at the end of the instruction. 370 fn reg_fixed_late_use(&mut self, reg: &mut impl AsMut<Reg>, rreg: Reg) { 371 self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Late); 372 } 373 374 /// Add a register "fixed use", which ties a vreg to a particular 375 /// RealReg at this point. 376 fn reg_fixed_use(&mut self, reg: &mut impl AsMut<Reg>, rreg: Reg) { 377 self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Early); 378 } 379 380 /// Add a register "fixed def", which ties a vreg to a particular 381 /// RealReg at this point. 382 fn reg_fixed_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, rreg: Reg) { 383 self.reg_fixed(reg.reg.as_mut(), rreg, OperandKind::Def, OperandPos::Late); 384 } 385 386 /// Add an operand tying a virtual register to a physical register. 387 fn reg_fixed(&mut self, reg: &mut Reg, rreg: Reg, kind: OperandKind, pos: OperandPos) { 388 debug_assert!(reg.is_virtual()); 389 let rreg = rreg.to_real_reg().expect("fixed reg is not a RealReg"); 390 self.debug_assert_is_allocatable_preg(rreg.into(), true); 391 let constraint = OperandConstraint::FixedReg(rreg.into()); 392 self.add_operand(reg, constraint, kind, pos); 393 } 394 395 /// Add an operand which might already be a physical register. 396 fn reg_maybe_fixed(&mut self, reg: &mut Reg, kind: OperandKind, pos: OperandPos) { 397 if let Some(rreg) = reg.to_real_reg() { 398 self.reg_fixed_nonallocatable(rreg.into()); 399 } else { 400 debug_assert!(reg.is_virtual()); 401 self.add_operand(reg, OperandConstraint::Reg, kind, pos); 402 } 403 } 404 405 /// Add a register def that reuses an earlier use-operand's 406 /// allocation. The index of that earlier operand (relative to the 407 /// current instruction's start of operands) must be known. 408 fn reg_reuse_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, idx: usize) { 409 let reg = reg.reg.as_mut(); 410 if let Some(rreg) = reg.to_real_reg() { 411 // In some cases we see real register arguments to a reg_reuse_def 412 // constraint. We assume the creator knows what they're doing 413 // here, though we do also require that the real register be a 414 // fixed-nonallocatable register. 415 self.reg_fixed_nonallocatable(rreg.into()); 416 } else { 417 debug_assert!(reg.is_virtual()); 418 // The operand we're reusing must not be fixed-nonallocatable, as 419 // that would imply that the register has been allocated to a 420 // virtual register. 421 let constraint = OperandConstraint::Reuse(idx); 422 self.add_operand(reg, constraint, OperandKind::Def, OperandPos::Late); 423 } 424 } 425 } 426 427 impl<T: OperandVisitor> OperandVisitorImpl for T {} 428 429 impl<'a, F: Fn(VReg) -> VReg> OperandVisitor for OperandCollector<'a, F> { 430 fn add_operand( 431 &mut self, 432 reg: &mut Reg, 433 constraint: OperandConstraint, 434 kind: OperandKind, 435 pos: OperandPos, 436 ) { 437 reg.0 = (self.renamer)(reg.0); 438 self.operands 439 .push(Operand::new(reg.0, constraint, kind, pos)); 440 } 441 442 fn debug_assert_is_allocatable_preg(&self, reg: PReg, expected: bool) { 443 debug_assert_eq!( 444 self.allocatable.contains(reg), 445 expected, 446 "{reg:?} should{} be allocatable", 447 if expected { "" } else { " not" } 448 ); 449 } 450 451 fn reg_clobbers(&mut self, regs: PRegSet) { 452 self.clobbers.union_from(regs); 453 } 454 } 455 456 impl<T: FnMut(&mut Reg, OperandConstraint, OperandKind, OperandPos)> OperandVisitor for T { 457 fn add_operand( 458 &mut self, 459 reg: &mut Reg, 460 constraint: OperandConstraint, 461 kind: OperandKind, 462 pos: OperandPos, 463 ) { 464 self(reg, constraint, kind, pos) 465 } 466 } 467 468 /// Pretty-print part of a disassembly, with knowledge of 469 /// operand/instruction size, and optionally with regalloc 470 /// results. This can be used, for example, to print either `rax` or 471 /// `eax` for the register by those names on x86-64, depending on a 472 /// 64- or 32-bit context. 473 pub trait PrettyPrint { 474 fn pretty_print(&self, size_bytes: u8) -> String; 475 476 fn pretty_print_default(&self) -> String { 477 self.pretty_print(0) 478 } 479 } 480