Path: blob/main/cranelift/codegen/src/machinst/reg.rs
1693 views
//! Definitions for registers, operands, etc. Provides a thin1//! interface over the register allocator so that we can more easily2//! swap it out or shim it when necessary.34use alloc::{string::String, vec::Vec};5use core::{fmt::Debug, hash::Hash};6use regalloc2::{Operand, OperandConstraint, OperandKind, OperandPos, PReg, PRegSet, VReg};78#[cfg(feature = "enable-serde")]9use serde_derive::{Deserialize, Serialize};1011/// The first 192 vregs (64 int, 64 float, 64 vec) are "pinned" to12/// physical registers. These must not be passed into the regalloc,13/// but they are used to represent physical registers in the same14/// `Reg` type post-regalloc.15const PINNED_VREGS: usize = 192;1617/// Convert a `VReg` to its pinned `PReg`, if any.18pub fn pinned_vreg_to_preg(vreg: VReg) -> Option<PReg> {19if vreg.vreg() < PINNED_VREGS {20Some(PReg::from_index(vreg.vreg()))21} else {22None23}24}2526/// Convert a `PReg` to its pinned `VReg`.27pub const fn preg_to_pinned_vreg(preg: PReg) -> VReg {28VReg::new(preg.index(), preg.class())29}3031/// Give the first available vreg for generated code (i.e., after all32/// pinned vregs).33pub fn first_user_vreg_index() -> usize {34// This is just the constant defined above, but we keep the35// constant private and expose only this helper function with the36// specific name in order to ensure other parts of the code don't37// open-code and depend on the index-space scheme.38PINNED_VREGS39}4041/// A register named in an instruction. This register can be a virtual42/// register, a fixed physical register, or a named spillslot (after43/// regalloc). It does not have any constraints applied to it: those44/// can be added later in `MachInst::get_operands()` when the `Reg`s45/// are converted to `Operand`s.46#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]47#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]48pub struct Reg(u32);4950const REG_SPILLSLOT_BIT: u32 = 0x8000_0000;51const REG_SPILLSLOT_MASK: u32 = !REG_SPILLSLOT_BIT;5253impl Reg {54/// Const constructor: create a new Reg from a regalloc2 VReg.55pub const fn from_virtual_reg(vreg: regalloc2::VReg) -> Reg {56Reg(vreg.bits() as u32)57}5859/// Const constructor: create a new Reg from a regalloc2 PReg.60pub const fn from_real_reg(preg: regalloc2::PReg) -> Reg {61Reg(preg_to_pinned_vreg(preg).bits() as u32)62}6364/// Get the physical register (`RealReg`), if this register is65/// one.66pub fn to_real_reg(self) -> Option<RealReg> {67pinned_vreg_to_preg(self.0.into()).map(RealReg)68}6970/// Get the virtual (non-physical) register, if this register is71/// one.72pub fn to_virtual_reg(self) -> Option<VirtualReg> {73if self.to_spillslot().is_some() {74None75} else if pinned_vreg_to_preg(self.0.into()).is_none() {76Some(VirtualReg(self.0.into()))77} else {78None79}80}8182/// Get the spillslot, if this register is one.83pub fn to_spillslot(self) -> Option<SpillSlot> {84if (self.0 & REG_SPILLSLOT_BIT) != 0 {85Some(SpillSlot::new((self.0 & REG_SPILLSLOT_MASK) as usize))86} else {87None88}89}9091/// Get the class of this register.92pub fn class(self) -> RegClass {93assert!(!self.to_spillslot().is_some());94VReg::from(self.0).class()95}9697/// Is this a real (physical) reg?98pub fn is_real(self) -> bool {99self.to_real_reg().is_some()100}101102/// Is this a virtual reg?103pub fn is_virtual(self) -> bool {104self.to_virtual_reg().is_some()105}106107/// Is this a spillslot?108pub fn is_spillslot(self) -> bool {109self.to_spillslot().is_some()110}111}112113impl std::fmt::Debug for Reg {114fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {115if VReg::from(self.0) == VReg::invalid() {116write!(f, "<invalid>")117} else if let Some(spillslot) = self.to_spillslot() {118write!(f, "{spillslot}")119} else if let Some(rreg) = self.to_real_reg() {120let preg: PReg = rreg.into();121write!(f, "{preg}")122} else if let Some(vreg) = self.to_virtual_reg() {123let vreg: VReg = vreg.into();124write!(f, "{vreg}")125} else {126unreachable!()127}128}129}130131impl AsMut<Reg> for Reg {132fn as_mut(&mut self) -> &mut Reg {133self134}135}136137/// A real (physical) register. This corresponds to one of the target138/// ISA's named registers and can be used as an instruction operand.139#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]140#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]141pub struct RealReg(PReg);142143impl RealReg {144/// Get the class of this register.145pub fn class(self) -> RegClass {146self.0.class()147}148149/// The physical register number.150pub fn hw_enc(self) -> u8 {151self.0.hw_enc() as u8152}153}154155impl std::fmt::Debug for RealReg {156fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {157Reg::from(*self).fmt(f)158}159}160161/// A virtual register. This can be allocated into a real (physical)162/// register of the appropriate register class, but which one is not163/// specified. Virtual registers are used when generating `MachInst`s,164/// before register allocation occurs, in order to allow us to name as165/// many register-carried values as necessary.166#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]167#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]168pub struct VirtualReg(VReg);169170impl VirtualReg {171/// Get the class of this register.172pub fn class(self) -> RegClass {173self.0.class()174}175176pub fn index(self) -> usize {177self.0.vreg()178}179}180181impl std::fmt::Debug for VirtualReg {182fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {183Reg::from(*self).fmt(f)184}185}186187/// A type wrapper that indicates a register type is writable. The188/// underlying register can be extracted, and the type wrapper can be189/// built using an arbitrary register. Hence, this type-level wrapper190/// is not strictly a guarantee. However, "casting" to a writable191/// register is an explicit operation for which we can192/// audit. Ordinarily, internal APIs in the compiler backend should193/// take a `Writable<Reg>` whenever the register is written, and the194/// usual, frictionless way to get one of these is to allocate a new195/// temporary.196#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]197#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]198pub struct Writable<T> {199reg: T,200}201202impl<T> Writable<T> {203/// Explicitly construct a `Writable<T>` from a `T`. As noted in204/// the documentation for `Writable`, this is not hidden or205/// disallowed from the outside; anyone can perform the "cast";206/// but it is explicit so that we can audit the use sites.207pub const fn from_reg(reg: T) -> Writable<T> {208Writable { reg }209}210211/// Get the underlying register, which can be read.212pub fn to_reg(self) -> T {213self.reg214}215216/// Get a mutable borrow of the underlying register.217pub fn reg_mut(&mut self) -> &mut T {218&mut self.reg219}220221/// Map the underlying register to another value or type.222pub fn map<U>(self, f: impl Fn(T) -> U) -> Writable<U> {223Writable { reg: f(self.reg) }224}225}226227// Proxy on assembler trait to the underlying register type.228impl<R: cranelift_assembler_x64::AsReg> cranelift_assembler_x64::AsReg for Writable<R> {229fn enc(&self) -> u8 {230self.reg.enc()231}232233fn to_string(&self, size: Option<cranelift_assembler_x64::gpr::Size>) -> String {234self.reg.to_string(size)235}236237fn new(_: u8) -> Self {238panic!("disallow creation of new assembler registers")239}240}241242// Conversions between regalloc2 types (VReg, PReg) and our types243// (VirtualReg, RealReg, Reg).244245impl std::convert::From<regalloc2::VReg> for Reg {246fn from(vreg: regalloc2::VReg) -> Reg {247Reg(vreg.bits() as u32)248}249}250251impl std::convert::From<regalloc2::VReg> for VirtualReg {252fn from(vreg: regalloc2::VReg) -> VirtualReg {253debug_assert!(pinned_vreg_to_preg(vreg).is_none());254VirtualReg(vreg)255}256}257258impl std::convert::From<Reg> for regalloc2::VReg {259/// Extract the underlying `regalloc2::VReg`. Note that physical260/// registers also map to particular (special) VRegs, so this261/// method can be used either on virtual or physical `Reg`s.262fn from(reg: Reg) -> regalloc2::VReg {263reg.0.into()264}265}266impl std::convert::From<&Reg> for regalloc2::VReg {267fn from(reg: &Reg) -> regalloc2::VReg {268reg.0.into()269}270}271272impl std::convert::From<VirtualReg> for regalloc2::VReg {273fn from(reg: VirtualReg) -> regalloc2::VReg {274reg.0275}276}277278impl std::convert::From<RealReg> for regalloc2::VReg {279fn from(reg: RealReg) -> regalloc2::VReg {280// This representation is redundant: the class is implied in the vreg281// index as well as being in the vreg class field.282VReg::new(reg.0.index(), reg.0.class())283}284}285286impl std::convert::From<RealReg> for regalloc2::PReg {287fn from(reg: RealReg) -> regalloc2::PReg {288reg.0289}290}291292impl std::convert::From<regalloc2::PReg> for RealReg {293fn from(preg: regalloc2::PReg) -> RealReg {294RealReg(preg)295}296}297298impl std::convert::From<regalloc2::PReg> for Reg {299fn from(preg: regalloc2::PReg) -> Reg {300RealReg(preg).into()301}302}303304impl std::convert::From<RealReg> for Reg {305fn from(reg: RealReg) -> Reg {306Reg(VReg::from(reg).bits() as u32)307}308}309310impl std::convert::From<VirtualReg> for Reg {311fn from(reg: VirtualReg) -> Reg {312Reg(reg.0.bits() as u32)313}314}315316/// A spill slot.317pub type SpillSlot = regalloc2::SpillSlot;318319impl std::convert::From<regalloc2::SpillSlot> for Reg {320fn from(spillslot: regalloc2::SpillSlot) -> Reg {321Reg(REG_SPILLSLOT_BIT | spillslot.index() as u32)322}323}324325/// A register class. Each register in the ISA has one class, and the326/// classes are disjoint. Most modern ISAs will have just two classes:327/// the integer/general-purpose registers (GPRs), and the float/vector328/// registers (typically used for both).329///330/// Note that unlike some other compiler backend/register allocator331/// designs, we do not allow for overlapping classes, i.e. registers332/// that belong to more than one class, because doing so makes the333/// allocation problem significantly more complex. Instead, when a334/// register can be addressed under different names for different335/// sizes (for example), the backend author should pick classes that336/// denote some fundamental allocation unit that encompasses the whole337/// register. For example, always allocate 128-bit vector registers338/// `v0`..`vN`, even though `f32` and `f64` values may use only the339/// low 32/64 bits of those registers and name them differently.340pub type RegClass = regalloc2::RegClass;341342/// An OperandCollector is a wrapper around a Vec of Operands343/// (flattened array for a whole sequence of instructions) that344/// gathers operands from a single instruction and provides the range345/// in the flattened array.346#[derive(Debug)]347pub struct OperandCollector<'a, F: Fn(VReg) -> VReg> {348operands: &'a mut Vec<Operand>,349clobbers: PRegSet,350351/// The subset of physical registers that are allocatable.352allocatable: PRegSet,353354renamer: F,355}356357impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> {358/// Start gathering operands into one flattened operand array.359pub fn new(operands: &'a mut Vec<Operand>, allocatable: PRegSet, renamer: F) -> Self {360Self {361operands,362clobbers: PRegSet::default(),363allocatable,364renamer,365}366}367368/// Finish the operand collection and return the tuple giving the369/// range of indices in the flattened operand array, and the370/// clobber set.371pub fn finish(self) -> (usize, PRegSet) {372let end = self.operands.len();373(end, self.clobbers)374}375}376377pub trait OperandVisitor {378fn add_operand(379&mut self,380reg: &mut Reg,381constraint: OperandConstraint,382kind: OperandKind,383pos: OperandPos,384);385386fn debug_assert_is_allocatable_preg(&self, _reg: PReg, _expected: bool) {}387388/// Add a register clobber set. This is a set of registers that389/// are written by the instruction, so must be reserved (not used)390/// for the whole instruction, but are not used afterward.391fn reg_clobbers(&mut self, _regs: PRegSet) {}392}393394pub trait OperandVisitorImpl: OperandVisitor {395/// Add a use of a fixed, nonallocatable physical register.396fn reg_fixed_nonallocatable(&mut self, preg: PReg) {397self.debug_assert_is_allocatable_preg(preg, false);398// Since this operand does not participate in register allocation,399// there's nothing to do here.400}401402/// Add a register use, at the start of the instruction (`Before`403/// position).404fn reg_use(&mut self, reg: &mut impl AsMut<Reg>) {405self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Early);406}407408/// Add a register use, at the end of the instruction (`After` position).409fn reg_late_use(&mut self, reg: &mut impl AsMut<Reg>) {410self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Late);411}412413/// Add a register def, at the end of the instruction (`After`414/// position). Use only when this def will be written after all415/// uses are read.416fn reg_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {417self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Late);418}419420/// Add a register "early def", which logically occurs at the421/// beginning of the instruction, alongside all uses. Use this422/// when the def may be written before all uses are read; the423/// regalloc will ensure that it does not overwrite any uses.424fn reg_early_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {425self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Early);426}427428/// Add a register "fixed use", which ties a vreg to a particular429/// RealReg at the end of the instruction.430fn reg_fixed_late_use(&mut self, reg: &mut impl AsMut<Reg>, rreg: Reg) {431self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Late);432}433434/// Add a register "fixed use", which ties a vreg to a particular435/// RealReg at this point.436fn reg_fixed_use(&mut self, reg: &mut impl AsMut<Reg>, rreg: Reg) {437self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Early);438}439440/// Add a register "fixed def", which ties a vreg to a particular441/// RealReg at this point.442fn reg_fixed_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, rreg: Reg) {443self.reg_fixed(reg.reg.as_mut(), rreg, OperandKind::Def, OperandPos::Late);444}445446/// Add an operand tying a virtual register to a physical register.447fn reg_fixed(&mut self, reg: &mut Reg, rreg: Reg, kind: OperandKind, pos: OperandPos) {448debug_assert!(reg.is_virtual());449let rreg = rreg.to_real_reg().expect("fixed reg is not a RealReg");450self.debug_assert_is_allocatable_preg(rreg.into(), true);451let constraint = OperandConstraint::FixedReg(rreg.into());452self.add_operand(reg, constraint, kind, pos);453}454455/// Add an operand which might already be a physical register.456fn reg_maybe_fixed(&mut self, reg: &mut Reg, kind: OperandKind, pos: OperandPos) {457if let Some(rreg) = reg.to_real_reg() {458self.reg_fixed_nonallocatable(rreg.into());459} else {460debug_assert!(reg.is_virtual());461self.add_operand(reg, OperandConstraint::Reg, kind, pos);462}463}464465/// Add a register def that reuses an earlier use-operand's466/// allocation. The index of that earlier operand (relative to the467/// current instruction's start of operands) must be known.468fn reg_reuse_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, idx: usize) {469let reg = reg.reg.as_mut();470if let Some(rreg) = reg.to_real_reg() {471// In some cases we see real register arguments to a reg_reuse_def472// constraint. We assume the creator knows what they're doing473// here, though we do also require that the real register be a474// fixed-nonallocatable register.475self.reg_fixed_nonallocatable(rreg.into());476} else {477debug_assert!(reg.is_virtual());478// The operand we're reusing must not be fixed-nonallocatable, as479// that would imply that the register has been allocated to a480// virtual register.481let constraint = OperandConstraint::Reuse(idx);482self.add_operand(reg, constraint, OperandKind::Def, OperandPos::Late);483}484}485486/// Add a def that can be allocated to either a register or a487/// spillslot, at the end of the instruction (`After`488/// position). Use only when this def will be written after all489/// uses are read.490fn any_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {491self.add_operand(492reg.reg.as_mut(),493OperandConstraint::Any,494OperandKind::Def,495OperandPos::Late,496);497}498499/// Add a use that can be allocated to either a register or a500/// spillslot, at the end of the instruction (`After` position).501fn any_late_use(&mut self, reg: &mut impl AsMut<Reg>) {502self.add_operand(503reg.as_mut(),504OperandConstraint::Any,505OperandKind::Use,506OperandPos::Late,507);508}509}510511impl<T: OperandVisitor> OperandVisitorImpl for T {}512513impl<'a, F: Fn(VReg) -> VReg> OperandVisitor for OperandCollector<'a, F> {514fn add_operand(515&mut self,516reg: &mut Reg,517constraint: OperandConstraint,518kind: OperandKind,519pos: OperandPos,520) {521debug_assert!(!reg.is_spillslot());522reg.0 = (self.renamer)(VReg::from(reg.0)).bits() as u32;523self.operands524.push(Operand::new(VReg::from(reg.0), constraint, kind, pos));525}526527fn debug_assert_is_allocatable_preg(&self, reg: PReg, expected: bool) {528debug_assert_eq!(529self.allocatable.contains(reg),530expected,531"{reg:?} should{} be allocatable",532if expected { "" } else { " not" }533);534}535536fn reg_clobbers(&mut self, regs: PRegSet) {537self.clobbers.union_from(regs);538}539}540541impl<T: FnMut(&mut Reg, OperandConstraint, OperandKind, OperandPos)> OperandVisitor for T {542fn add_operand(543&mut self,544reg: &mut Reg,545constraint: OperandConstraint,546kind: OperandKind,547pos: OperandPos,548) {549self(reg, constraint, kind, pos)550}551}552553/// Pretty-print part of a disassembly, with knowledge of554/// operand/instruction size, and optionally with regalloc555/// results. This can be used, for example, to print either `rax` or556/// `eax` for the register by those names on x86-64, depending on a557/// 64- or 32-bit context.558pub trait PrettyPrint {559fn pretty_print(&self, size_bytes: u8) -> String;560561fn pretty_print_default(&self) -> String {562self.pretty_print(0)563}564}565566567