use alloc::{string::String, vec::Vec};
use core::{fmt::Debug, hash::Hash};
use regalloc2::{Operand, OperandConstraint, OperandKind, OperandPos, PReg, PRegSet, VReg};
#[cfg(feature = "enable-serde")]
use serde_derive::{Deserialize, Serialize};
const PINNED_VREGS: usize = 192;
pub const fn pinned_vreg_to_preg(vreg: VReg) -> Option<PReg> {
if vreg.vreg() < PINNED_VREGS {
Some(PReg::from_index(vreg.vreg()))
} else {
None
}
}
pub const fn preg_to_pinned_vreg(preg: PReg) -> VReg {
VReg::new(preg.index(), preg.class())
}
pub fn first_user_vreg_index() -> usize {
PINNED_VREGS
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct Reg(u32);
const REG_SPILLSLOT_BIT: u32 = 0x8000_0000;
const REG_SPILLSLOT_MASK: u32 = !REG_SPILLSLOT_BIT;
impl Reg {
pub const fn from_virtual_reg(vreg: regalloc2::VReg) -> Reg {
let bits = vreg.bits() as u32;
debug_assert!(bits <= REG_SPILLSLOT_MASK);
Reg(bits)
}
pub const fn from_real_reg(preg: regalloc2::PReg) -> Reg {
let vreg = preg_to_pinned_vreg(preg);
let bits = vreg.bits() as u32;
debug_assert!(bits <= REG_SPILLSLOT_MASK);
Reg(bits)
}
pub fn from_virtual_reg_checked(vreg: regalloc2::VReg) -> Option<Reg> {
let bits = vreg.bits() as u32;
if bits <= REG_SPILLSLOT_MASK {
Some(Reg(bits))
} else {
None
}
}
pub const fn to_real_reg(self) -> Option<RealReg> {
match pinned_vreg_to_preg(VReg::from_bits(self.0)) {
Some(preg) => Some(RealReg(preg)),
None => None,
}
}
pub fn to_virtual_reg(self) -> Option<VirtualReg> {
if self.to_spillslot().is_some() {
None
} else if pinned_vreg_to_preg(self.0.into()).is_none() {
Some(VirtualReg(self.0.into()))
} else {
None
}
}
pub fn to_spillslot(self) -> Option<SpillSlot> {
if (self.0 & REG_SPILLSLOT_BIT) != 0 {
Some(SpillSlot::new((self.0 & REG_SPILLSLOT_MASK) as usize))
} else {
None
}
}
pub fn class(self) -> RegClass {
assert!(!self.to_spillslot().is_some());
VReg::from(self.0).class()
}
pub fn is_real(self) -> bool {
self.to_real_reg().is_some()
}
pub fn is_virtual(self) -> bool {
self.to_virtual_reg().is_some()
}
pub fn is_spillslot(self) -> bool {
self.to_spillslot().is_some()
}
}
impl core::fmt::Debug for Reg {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
if VReg::from(self.0) == VReg::invalid() {
write!(f, "<invalid>")
} else if let Some(spillslot) = self.to_spillslot() {
write!(f, "{spillslot}")
} else if let Some(rreg) = self.to_real_reg() {
let preg: PReg = rreg.into();
write!(f, "{preg}")
} else if let Some(vreg) = self.to_virtual_reg() {
let vreg: VReg = vreg.into();
write!(f, "{vreg}")
} else {
unreachable!()
}
}
}
impl AsMut<Reg> for Reg {
fn as_mut(&mut self) -> &mut Reg {
self
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct RealReg(PReg);
impl RealReg {
pub fn class(self) -> RegClass {
self.0.class()
}
pub fn hw_enc(self) -> u8 {
self.0.hw_enc() as u8
}
pub const fn preg(self) -> PReg {
self.0
}
}
impl core::fmt::Debug for RealReg {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
Reg::from(*self).fmt(f)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct VirtualReg(VReg);
impl VirtualReg {
pub fn class(self) -> RegClass {
self.0.class()
}
pub fn index(self) -> usize {
self.0.vreg()
}
}
impl core::fmt::Debug for VirtualReg {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
Reg::from(*self).fmt(f)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct Writable<T> {
reg: T,
}
impl<T> Writable<T> {
pub const fn from_reg(reg: T) -> Writable<T> {
Writable { reg }
}
pub fn to_reg(self) -> T {
self.reg
}
pub fn reg_mut(&mut self) -> &mut T {
&mut self.reg
}
pub fn map<U>(self, f: impl Fn(T) -> U) -> Writable<U> {
Writable { reg: f(self.reg) }
}
}
impl<R: cranelift_assembler_x64::AsReg> cranelift_assembler_x64::AsReg for Writable<R> {
fn enc(&self) -> u8 {
self.reg.enc()
}
fn to_string(&self, size: Option<cranelift_assembler_x64::gpr::Size>) -> String {
self.reg.to_string(size)
}
fn new(_: u8) -> Self {
panic!("disallow creation of new assembler registers")
}
}
impl core::convert::From<regalloc2::VReg> for Reg {
fn from(vreg: regalloc2::VReg) -> Reg {
Reg(vreg.bits() as u32)
}
}
impl core::convert::From<regalloc2::VReg> for VirtualReg {
fn from(vreg: regalloc2::VReg) -> VirtualReg {
debug_assert!(pinned_vreg_to_preg(vreg).is_none());
VirtualReg(vreg)
}
}
impl core::convert::From<Reg> for regalloc2::VReg {
fn from(reg: Reg) -> regalloc2::VReg {
reg.0.into()
}
}
impl core::convert::From<&Reg> for regalloc2::VReg {
fn from(reg: &Reg) -> regalloc2::VReg {
reg.0.into()
}
}
impl core::convert::From<VirtualReg> for regalloc2::VReg {
fn from(reg: VirtualReg) -> regalloc2::VReg {
reg.0
}
}
impl core::convert::From<RealReg> for regalloc2::VReg {
fn from(reg: RealReg) -> regalloc2::VReg {
VReg::new(reg.0.index(), reg.0.class())
}
}
impl core::convert::From<RealReg> for regalloc2::PReg {
fn from(reg: RealReg) -> regalloc2::PReg {
reg.0
}
}
impl core::convert::From<regalloc2::PReg> for RealReg {
fn from(preg: regalloc2::PReg) -> RealReg {
RealReg(preg)
}
}
impl core::convert::From<regalloc2::PReg> for Reg {
fn from(preg: regalloc2::PReg) -> Reg {
RealReg(preg).into()
}
}
impl core::convert::From<RealReg> for Reg {
fn from(reg: RealReg) -> Reg {
Reg(VReg::from(reg).bits() as u32)
}
}
impl core::convert::From<VirtualReg> for Reg {
fn from(reg: VirtualReg) -> Reg {
Reg(reg.0.bits() as u32)
}
}
pub type SpillSlot = regalloc2::SpillSlot;
impl core::convert::From<regalloc2::SpillSlot> for Reg {
fn from(spillslot: regalloc2::SpillSlot) -> Reg {
Reg(REG_SPILLSLOT_BIT | spillslot.index() as u32)
}
}
pub type RegClass = regalloc2::RegClass;
#[derive(Debug)]
pub struct OperandCollector<'a, F: Fn(VReg) -> VReg> {
operands: &'a mut Vec<Operand>,
clobbers: PRegSet,
allocatable: PRegSet,
renamer: F,
}
impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> {
pub fn new(operands: &'a mut Vec<Operand>, allocatable: PRegSet, renamer: F) -> Self {
Self {
operands,
clobbers: PRegSet::default(),
allocatable,
renamer,
}
}
pub fn finish(self) -> (usize, PRegSet) {
let end = self.operands.len();
(end, self.clobbers)
}
}
pub trait OperandVisitor {
fn add_operand(
&mut self,
reg: &mut Reg,
constraint: OperandConstraint,
kind: OperandKind,
pos: OperandPos,
);
fn debug_assert_is_allocatable_preg(&self, _reg: PReg, _expected: bool) {}
fn reg_clobbers(&mut self, _regs: PRegSet) {}
}
pub trait OperandVisitorImpl: OperandVisitor {
fn reg_fixed_nonallocatable(&mut self, preg: PReg) {
self.debug_assert_is_allocatable_preg(preg, false);
}
fn reg_use(&mut self, reg: &mut impl AsMut<Reg>) {
self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Early);
}
fn reg_late_use(&mut self, reg: &mut impl AsMut<Reg>) {
self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Late);
}
fn reg_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Late);
}
fn reg_early_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Early);
}
fn reg_fixed_late_use(&mut self, reg: &mut impl AsMut<Reg>, rreg: Reg) {
self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Late);
}
fn reg_fixed_use(&mut self, reg: &mut impl AsMut<Reg>, rreg: Reg) {
self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Early);
}
fn reg_fixed_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, rreg: Reg) {
self.reg_fixed(reg.reg.as_mut(), rreg, OperandKind::Def, OperandPos::Late);
}
fn reg_fixed(&mut self, reg: &mut Reg, rreg: Reg, kind: OperandKind, pos: OperandPos) {
debug_assert!(reg.is_virtual());
let rreg = rreg.to_real_reg().expect("fixed reg is not a RealReg");
self.debug_assert_is_allocatable_preg(rreg.into(), true);
let constraint = OperandConstraint::FixedReg(rreg.into());
self.add_operand(reg, constraint, kind, pos);
}
fn reg_maybe_fixed(&mut self, reg: &mut Reg, kind: OperandKind, pos: OperandPos) {
if let Some(rreg) = reg.to_real_reg() {
self.reg_fixed_nonallocatable(rreg.into());
} else {
debug_assert!(reg.is_virtual());
self.add_operand(reg, OperandConstraint::Reg, kind, pos);
}
}
fn reg_reuse_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, idx: usize) {
let reg = reg.reg.as_mut();
if let Some(rreg) = reg.to_real_reg() {
self.reg_fixed_nonallocatable(rreg.into());
} else {
debug_assert!(reg.is_virtual());
let constraint = OperandConstraint::Reuse(idx);
self.add_operand(reg, constraint, OperandKind::Def, OperandPos::Late);
}
}
fn any_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
self.add_operand(
reg.reg.as_mut(),
OperandConstraint::Any,
OperandKind::Def,
OperandPos::Late,
);
}
fn any_late_use(&mut self, reg: &mut impl AsMut<Reg>) {
self.add_operand(
reg.as_mut(),
OperandConstraint::Any,
OperandKind::Use,
OperandPos::Late,
);
}
}
impl<T: OperandVisitor> OperandVisitorImpl for T {}
impl<'a, F: Fn(VReg) -> VReg> OperandVisitor for OperandCollector<'a, F> {
fn add_operand(
&mut self,
reg: &mut Reg,
constraint: OperandConstraint,
kind: OperandKind,
pos: OperandPos,
) {
debug_assert!(!reg.is_spillslot());
reg.0 = (self.renamer)(VReg::from(reg.0)).bits() as u32;
self.operands
.push(Operand::new(VReg::from(reg.0), constraint, kind, pos));
}
fn debug_assert_is_allocatable_preg(&self, reg: PReg, expected: bool) {
debug_assert_eq!(
self.allocatable.contains(reg),
expected,
"{reg:?} should{} be allocatable",
if expected { "" } else { " not" }
);
}
fn reg_clobbers(&mut self, regs: PRegSet) {
self.clobbers.union_from(regs);
}
}
impl<T: FnMut(&mut Reg, OperandConstraint, OperandKind, OperandPos)> OperandVisitor for T {
fn add_operand(
&mut self,
reg: &mut Reg,
constraint: OperandConstraint,
kind: OperandKind,
pos: OperandPos,
) {
self(reg, constraint, kind, pos)
}
}
pub trait PrettyPrint {
fn pretty_print(&self, size_bytes: u8) -> String;
fn pretty_print_default(&self) -> String {
self.pretty_print(0)
}
}