use crate::machinst::MachInst;
use alloc::{string::String, vec::Vec};
use core::{fmt::Debug, hash::Hash};
use regalloc2::{Allocation, Operand, PReg, PRegSet, VReg};
use smallvec::{smallvec, SmallVec};
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
const PINNED_VREGS: usize = 128;
pub fn pinned_vreg_to_preg(vreg: VReg) -> Option<PReg> {
if vreg.vreg() < PINNED_VREGS {
Some(PReg::from_index(vreg.vreg()))
} else {
None
}
}
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(VReg);
impl Reg {
pub fn to_real_reg(self) -> Option<RealReg> {
if pinned_vreg_to_preg(self.0).is_some() {
Some(RealReg(self.0))
} else {
None
}
}
pub fn to_virtual_reg(self) -> Option<VirtualReg> {
if pinned_vreg_to_preg(self.0).is_none() {
Some(VirtualReg(self.0))
} else {
None
}
}
pub fn class(self) -> RegClass {
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()
}
}
impl std::fmt::Debug for Reg {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
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!()
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct RealReg(VReg);
impl RealReg {
pub fn class(self) -> RegClass {
self.0.class()
}
pub fn hw_enc(self) -> u8 {
PReg::from(self).hw_enc() as u8
}
}
impl std::fmt::Debug for RealReg {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::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 std::fmt::Debug for VirtualReg {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::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: Clone + Copy + Debug + PartialEq + Eq + PartialOrd + Ord + Hash> {
reg: T,
}
impl<T: Clone + Copy + Debug + PartialEq + Eq + PartialOrd + Ord + Hash> Writable<T> {
pub fn from_reg(reg: T) -> Writable<T> {
Writable { reg }
}
pub fn to_reg(self) -> T {
self.reg
}
pub fn map<U, F>(self, f: F) -> Writable<U>
where
U: Clone + Copy + Debug + PartialEq + Eq + PartialOrd + Ord + Hash,
F: Fn(T) -> U,
{
Writable { reg: f(self.reg) }
}
}
impl std::convert::From<regalloc2::VReg> for Reg {
fn from(vreg: regalloc2::VReg) -> Reg {
Reg(vreg)
}
}
impl std::convert::From<regalloc2::VReg> for VirtualReg {
fn from(vreg: regalloc2::VReg) -> VirtualReg {
debug_assert!(pinned_vreg_to_preg(vreg).is_none());
VirtualReg(vreg)
}
}
impl std::convert::From<regalloc2::VReg> for RealReg {
fn from(vreg: regalloc2::VReg) -> RealReg {
debug_assert!(pinned_vreg_to_preg(vreg).is_some());
RealReg(vreg)
}
}
impl std::convert::From<Reg> for regalloc2::VReg {
fn from(reg: Reg) -> regalloc2::VReg {
reg.0
}
}
impl std::convert::From<VirtualReg> for regalloc2::VReg {
fn from(reg: VirtualReg) -> regalloc2::VReg {
reg.0
}
}
impl std::convert::From<RealReg> for regalloc2::VReg {
fn from(reg: RealReg) -> regalloc2::VReg {
reg.0
}
}
impl std::convert::From<RealReg> for regalloc2::PReg {
fn from(reg: RealReg) -> regalloc2::PReg {
PReg::from_index(reg.0.vreg())
}
}
impl std::convert::From<regalloc2::PReg> for RealReg {
fn from(preg: regalloc2::PReg) -> RealReg {
RealReg(VReg::new(preg.index(), preg.class()))
}
}
impl std::convert::From<regalloc2::PReg> for Reg {
fn from(preg: regalloc2::PReg) -> Reg {
Reg(VReg::new(preg.index(), preg.class()))
}
}
impl std::convert::From<RealReg> for Reg {
fn from(reg: RealReg) -> Reg {
Reg(reg.0)
}
}
impl std::convert::From<VirtualReg> for Reg {
fn from(reg: VirtualReg) -> Reg {
Reg(reg.0)
}
}
pub type SpillSlot = regalloc2::SpillSlot;
pub type RegClass = regalloc2::RegClass;
#[derive(Debug)]
pub struct OperandCollector<'a, F: Fn(VReg) -> VReg> {
operands: &'a mut Vec<Operand>,
operands_start: usize,
clobbers: PRegSet,
renamer: F,
}
impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> {
pub fn new(operands: &'a mut Vec<Operand>, renamer: F) -> Self {
let operands_start = operands.len();
Self {
operands,
operands_start,
clobbers: PRegSet::default(),
renamer,
}
}
fn add_operand(&mut self, operand: Operand) {
let vreg = (self.renamer)(operand.vreg());
let operand = Operand::new(vreg, operand.constraint(), operand.kind(), operand.pos());
self.operands.push(operand);
}
pub fn finish(self) -> ((u32, u32), PRegSet) {
let start = self.operands_start as u32;
let end = self.operands.len() as u32;
((start, end), self.clobbers)
}
pub fn reg_use(&mut self, reg: Reg) {
self.add_operand(Operand::reg_use(reg.into()));
}
pub fn reg_late_use(&mut self, reg: Reg) {
self.add_operand(Operand::reg_use_at_end(reg.into()));
}
pub fn reg_uses(&mut self, regs: &[Reg]) {
for ® in regs {
self.reg_use(reg);
}
}
pub fn reg_def(&mut self, reg: Writable<Reg>) {
self.add_operand(Operand::reg_def(reg.to_reg().into()));
}
pub fn reg_defs(&mut self, regs: &[Writable<Reg>]) {
for ® in regs {
self.reg_def(reg);
}
}
pub fn reg_early_def(&mut self, reg: Writable<Reg>) {
self.add_operand(Operand::reg_def_at_start(reg.to_reg().into()));
}
pub fn reg_fixed_use(&mut self, reg: Reg, rreg: Reg) {
let rreg = rreg.to_real_reg().expect("fixed reg is not a RealReg");
self.add_operand(Operand::reg_fixed_use(reg.into(), rreg.into()));
}
pub fn reg_fixed_def(&mut self, reg: Writable<Reg>, rreg: Reg) {
let rreg = rreg.to_real_reg().expect("fixed reg is not a RealReg");
self.add_operand(Operand::reg_fixed_def(reg.to_reg().into(), rreg.into()));
}
pub fn reg_reuse_def(&mut self, reg: Writable<Reg>, idx: usize) {
if reg.to_reg().to_virtual_reg().is_some() {
self.add_operand(Operand::reg_reuse_def(reg.to_reg().into(), idx));
} else {
self.add_operand(Operand::reg_def(reg.to_reg().into()));
}
}
pub fn reg_mod(&mut self, reg: Writable<Reg>) {
self.add_operand(Operand::new(
reg.to_reg().into(),
regalloc2::OperandConstraint::Reg,
regalloc2::OperandKind::Mod,
regalloc2::OperandPos::Early,
));
}
pub fn reg_clobbers(&mut self, regs: PRegSet) {
self.clobbers.union_from(regs);
}
}
pub fn count_operands<I: MachInst>(inst: &I) -> usize {
let mut ops = vec![];
let mut coll = OperandCollector::new(&mut ops, |vreg| vreg);
inst.get_operands(&mut coll);
let ((start, end), _) = coll.finish();
debug_assert_eq!(0, start);
end as usize
}
pub trait PrettyPrint {
fn pretty_print(&self, size_bytes: u8, allocs: &mut AllocationConsumer<'_>) -> String;
fn pretty_print_default(&self) -> String {
self.pretty_print(0, &mut AllocationConsumer::new(&[]))
}
}
#[derive(Clone)]
pub struct AllocationConsumer<'a> {
allocs: std::slice::Iter<'a, Allocation>,
}
impl<'a> AllocationConsumer<'a> {
pub fn new(allocs: &'a [Allocation]) -> Self {
Self {
allocs: allocs.iter(),
}
}
pub fn next(&mut self, pre_regalloc_reg: Reg) -> Reg {
let alloc = self.allocs.next();
let alloc = alloc.map(|alloc| {
Reg::from(
alloc
.as_reg()
.expect("Should not have gotten a stack allocation"),
)
});
match (pre_regalloc_reg.to_real_reg(), alloc) {
(Some(rreg), None) => rreg.into(),
(Some(rreg), Some(alloc)) => {
debug_assert_eq!(Reg::from(rreg), alloc);
alloc
}
(None, Some(alloc)) => alloc,
_ => pre_regalloc_reg,
}
}
pub fn next_writable(&mut self, pre_regalloc_reg: Writable<Reg>) -> Writable<Reg> {
Writable::from_reg(self.next(pre_regalloc_reg.to_reg()))
}
pub fn next_n(&mut self, count: usize) -> SmallVec<[Allocation; 4]> {
let mut allocs = smallvec![];
for _ in 0..count {
if let Some(next) = self.allocs.next() {
allocs.push(*next);
} else {
return allocs;
}
}
allocs
}
}
impl<'a> std::default::Default for AllocationConsumer<'a> {
fn default() -> Self {
Self { allocs: [].iter() }
}
}