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