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