use rucc_base::{Idx, IdxRange, Symbol};
use rucc_target::{PhysReg, RegClass};
pub type Inst = Idx<InstData>;
pub type Block = Idx<BlockData>;
pub type OperandList = IdxRange<Operand>;
pub type ImmRef = Idx<Imm>;
pub type MemRef = Idx<Amode>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Opcode(Symbol);
impl Opcode {
#[must_use]
pub const fn new(name: Symbol) -> Self {
Self(name)
}
#[must_use]
pub const fn name(self) -> Symbol {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Reg(u32);
impl Reg {
const PHYSICAL: u32 = 1 << 31;
#[must_use]
pub const fn virtual_reg(number: u32) -> Self {
assert!(number < Self::PHYSICAL, "a function with two billion virtual registers");
Self(number)
}
#[must_use]
pub const fn physical(reg: PhysReg) -> Self {
Self(Self::PHYSICAL | reg.number() as u32)
}
#[must_use]
pub const fn is_virtual(self) -> bool {
self.0 & Self::PHYSICAL == 0
}
#[must_use]
pub const fn number(self) -> Option<u32> {
if self.is_virtual() { Some(self.0) } else { None }
}
#[must_use]
pub const fn phys(self) -> Option<PhysReg> {
if self.is_virtual() { None } else { Some(PhysReg::new((self.0 & 0xff) as u8)) }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Role {
Use,
Def,
EarlyDef,
}
impl Role {
#[must_use]
pub const fn is_def(self) -> bool {
matches!(self, Role::Def | Role::EarlyDef)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Constraint {
Reg,
Any,
Stack,
Fixed(PhysReg),
Reuse(u8),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Operand {
pub reg: Reg,
pub class: RegClass,
pub role: Role,
pub constraint: Constraint,
}
impl Operand {
#[must_use]
pub const fn read(reg: Reg, class: RegClass) -> Self {
Self { reg, class, role: Role::Use, constraint: Constraint::Reg }
}
#[must_use]
pub const fn write(reg: Reg, class: RegClass) -> Self {
Self { reg, class, role: Role::Def, constraint: Constraint::Reg }
}
#[must_use]
pub const fn write_early(reg: Reg, class: RegClass) -> Self {
Self { reg, class, role: Role::EarlyDef, constraint: Constraint::Reg }
}
#[must_use]
pub const fn with(mut self, constraint: Constraint) -> Self {
self.constraint = constraint;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Imm(pub i64);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Amode {
pub base: Option<u8>,
pub index: Option<u8>,
pub scale: u8,
pub disp: i32,
pub symbol: Option<Symbol>,
}
impl Amode {
pub const NOTHING: Self = Self { base: None, index: None, scale: 1, disp: 0, symbol: None };
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Mem {
pub base: Option<Operand>,
pub index: Option<Operand>,
pub scale: u8,
pub disp: i32,
pub symbol: Option<Symbol>,
}
impl Mem {
#[must_use]
pub const fn at(base: Operand) -> Self {
Self { base: Some(base), index: None, scale: 1, disp: 0, symbol: None }
}
#[must_use]
pub const fn of(symbol: Symbol) -> Self {
Self { base: None, index: None, scale: 1, disp: 0, symbol: Some(symbol) }
}
#[must_use]
pub const fn indexed(mut self, index: Operand, scale: u8) -> Self {
self.index = Some(index);
self.scale = scale;
self
}
#[must_use]
pub const fn plus(mut self, disp: i32) -> Self {
self.disp = disp;
self
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlockCall {
pub block: Block,
pub args: Vec<Reg>,
}
impl BlockCall {
#[must_use]
pub const fn to(block: Block) -> Self {
Self { block, args: Vec::new() }
}
#[must_use]
pub fn with(block: Block, args: Vec<Reg>) -> Self {
Self { block, args }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Param {
pub reg: Reg,
pub class: RegClass,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InstData {
pub opcode: Opcode,
pub operands: OperandList,
pub imm: Option<ImmRef>,
pub mem: Option<MemRef>,
pub symbol: Option<Symbol>,
}
impl InstData {
#[must_use]
pub const fn new(opcode: Opcode) -> Self {
Self { opcode, operands: OperandList::EMPTY, imm: None, mem: None, symbol: None }
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub(crate) struct InstLayout {
pub(crate) block: Option<Block>,
pub(crate) prev: Option<Inst>,
pub(crate) next: Option<Inst>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct BlockData {
pub params: Vec<Param>,
pub succs: Vec<BlockCall>,
pub(crate) first_inst: Option<Inst>,
pub(crate) last_inst: Option<Inst>,
pub(crate) prev: Option<Block>,
pub(crate) next: Option<Block>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_instruction_is_the_size_the_design_says() {
assert_eq!(size_of::<InstData>(), 24);
assert_eq!(size_of::<Operand>(), 8);
}
#[test]
fn a_virtual_register_is_its_own_number() {
let reg = Reg::virtual_reg(7);
assert!(reg.is_virtual());
assert_eq!(reg.number(), Some(7));
assert_eq!(reg.phys(), None);
}
#[test]
fn a_physical_register_is_not_a_virtual_one_of_the_same_number() {
let reg = Reg::physical(PhysReg::new(7));
assert!(!reg.is_virtual());
assert_eq!(reg.number(), None);
assert_eq!(reg.phys(), Some(PhysReg::new(7)));
assert_ne!(reg, Reg::virtual_reg(7));
}
#[test]
fn an_operand_keeps_what_it_was_constrained_to() {
let class = RegClass::new(0);
let plain = Operand::write(Reg::virtual_reg(1), class);
assert_eq!(plain.role, Role::Def);
assert_eq!(plain.constraint, Constraint::Reg);
let tied = plain.with(Constraint::Reuse(1));
assert_eq!(tied.constraint, Constraint::Reuse(1));
assert_eq!(tied.reg, plain.reg);
assert!(tied.role.is_def());
assert!(!Operand::read(Reg::virtual_reg(1), class).role.is_def());
}
}