pub mod abi;
pub mod frame;
use core::fmt;
use crate::regs::{PReg, RegClass, RegSet};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Isa {
X86_64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RegInfo {
name: &'static str,
class: RegClass,
hw: u8,
}
impl RegInfo {
#[must_use]
pub const fn new(name: &'static str, class: RegClass, hw: u8) -> RegInfo {
RegInfo { name, class, hw }
}
#[inline]
#[must_use]
pub const fn name(&self) -> &'static str {
self.name
}
#[inline]
#[must_use]
pub const fn class(&self) -> RegClass {
self.class
}
#[inline]
#[must_use]
pub const fn hw(&self) -> u8 {
self.hw
}
}
const INVALID_REG: RegInfo = RegInfo::new("<invalid>", RegClass::new(u8::MAX), u8::MAX);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ClassInfo {
name: &'static str,
regs: RegSet,
allocatable: RegSet,
spill_size: u32,
spill_align: u32,
}
impl ClassInfo {
#[must_use]
pub const fn new(
name: &'static str,
regs: RegSet,
allocatable: RegSet,
spill_size: u32,
spill_align: u32,
) -> ClassInfo {
ClassInfo {
name,
regs,
allocatable,
spill_size,
spill_align,
}
}
#[inline]
#[must_use]
pub const fn name(&self) -> &'static str {
self.name
}
#[inline]
#[must_use]
pub const fn regs(&self) -> RegSet {
self.regs
}
#[inline]
#[must_use]
pub const fn allocatable(&self) -> RegSet {
self.allocatable
}
#[inline]
#[must_use]
pub const fn spill_size(&self) -> u32 {
self.spill_size
}
#[inline]
#[must_use]
pub const fn spill_align(&self) -> u32 {
self.spill_align
}
}
const INVALID_CLASS: ClassInfo = ClassInfo::new("<invalid>", RegSet::EMPTY, RegSet::EMPTY, 0, 1);
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Opcode(u16);
impl Opcode {
pub const COPY: Opcode = Opcode(0);
pub const PCOPY: Opcode = Opcode(1);
#[inline]
#[must_use]
pub const fn new(index: u16) -> Opcode {
Opcode(index)
}
#[inline]
#[must_use]
pub const fn index(self) -> usize {
self.0 as usize
}
}
impl fmt::Debug for Opcode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Opcode({})", self.0)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum InstKind {
Plain,
Copy,
ParallelCopy,
Jump,
Branch,
Return,
Call,
Trap,
TailCall,
Switch,
}
impl InstKind {
#[inline]
#[must_use]
pub const fn is_terminator(self) -> bool {
matches!(
self,
InstKind::Jump
| InstKind::Branch
| InstKind::Return
| InstKind::Trap
| InstKind::TailCall
| InstKind::Switch
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ImmRange {
None,
I8,
U8,
I16,
I32,
I64,
}
impl ImmRange {
#[inline]
#[must_use]
pub const fn fits(self, value: i64) -> bool {
match self {
ImmRange::None => false,
ImmRange::I8 => value >= -128 && value <= 127,
ImmRange::U8 => value >= 0 && value <= 255,
ImmRange::I16 => value >= i16::MIN as i64 && value <= i16::MAX as i64,
ImmRange::I32 => value >= i32::MIN as i64 && value <= i32::MAX as i64,
ImmRange::I64 => true,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Accept {
reg: bool,
mem: bool,
symbol: bool,
imm: ImmRange,
}
impl Accept {
pub const REG: Accept = Accept {
reg: true,
mem: false,
symbol: false,
imm: ImmRange::None,
};
pub const MEM: Accept = Accept {
reg: false,
mem: true,
symbol: false,
imm: ImmRange::None,
};
pub const RM: Accept = Accept {
reg: true,
mem: true,
symbol: false,
imm: ImmRange::None,
};
pub const NONE: Accept = Accept {
reg: false,
mem: false,
symbol: false,
imm: ImmRange::None,
};
#[must_use]
pub const fn with_imm(self, range: ImmRange) -> Accept {
Accept { imm: range, ..self }
}
#[must_use]
pub const fn with_symbol(self) -> Accept {
Accept {
symbol: true,
..self
}
}
#[inline]
#[must_use]
pub const fn reg(self) -> bool {
self.reg
}
#[inline]
#[must_use]
pub const fn mem(self) -> bool {
self.mem
}
#[inline]
#[must_use]
pub const fn symbol(self) -> bool {
self.symbol
}
#[inline]
#[must_use]
pub const fn imm(self) -> ImmRange {
self.imm
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum OperandRule {
Def(RegClass),
Tied(u8),
Use(RegClass, Accept),
Cond,
Block,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct InstDesc {
name: &'static str,
kind: InstKind,
operands: &'static [OperandRule],
implicit_uses: &'static [PReg],
implicit_defs: &'static [PReg],
clobbers: RegSet,
}
impl InstDesc {
#[must_use]
pub const fn new(
name: &'static str,
kind: InstKind,
operands: &'static [OperandRule],
implicit_uses: &'static [PReg],
implicit_defs: &'static [PReg],
clobbers: RegSet,
) -> InstDesc {
InstDesc {
name,
kind,
operands,
implicit_uses,
implicit_defs,
clobbers,
}
}
#[inline]
#[must_use]
pub const fn name(&self) -> &'static str {
self.name
}
#[inline]
#[must_use]
pub const fn kind(&self) -> InstKind {
self.kind
}
#[inline]
#[must_use]
pub const fn operands(&self) -> &'static [OperandRule] {
self.operands
}
#[must_use]
pub fn defs(&self) -> usize {
self.operands
.iter()
.take_while(|r| matches!(r, OperandRule::Def(_)))
.count()
}
#[inline]
#[must_use]
pub const fn implicit_uses(&self) -> &'static [PReg] {
self.implicit_uses
}
#[inline]
#[must_use]
pub const fn implicit_defs(&self) -> &'static [PReg] {
self.implicit_defs
}
#[inline]
#[must_use]
pub const fn clobbers(&self) -> RegSet {
self.clobbers
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct MoveCaps {
swap: bool,
mem_to_mem: bool,
store_imm: ImmRange,
}
impl MoveCaps {
#[must_use]
pub const fn new(swap: bool, mem_to_mem: bool, store_imm: ImmRange) -> MoveCaps {
MoveCaps {
swap,
mem_to_mem,
store_imm,
}
}
#[inline]
#[must_use]
pub const fn swap(self) -> bool {
self.swap
}
#[inline]
#[must_use]
pub const fn mem_to_mem(self) -> bool {
self.mem_to_mem
}
#[inline]
#[must_use]
pub const fn store_imm(self) -> ImmRange {
self.store_imm
}
}
#[derive(Debug)]
pub struct Target {
name: &'static str,
isa: Isa,
regs: &'static [RegInfo],
classes: &'static [ClassInfo],
insts: &'static [InstDesc],
stack_pointer: PReg,
frame_pointer: PReg,
flags: Option<PReg>,
addr_class: RegClass,
move_caps: MoveCaps,
max_mem_operands: u8,
conds: &'static [&'static str],
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TargetConfig {
pub stack_pointer: PReg,
pub frame_pointer: PReg,
pub flags: Option<PReg>,
pub addr_class: RegClass,
pub move_caps: MoveCaps,
pub max_mem_operands: u8,
pub conds: &'static [&'static str],
}
impl TargetConfig {
#[must_use]
pub const fn of(target: &Target) -> TargetConfig {
TargetConfig {
stack_pointer: target.stack_pointer,
frame_pointer: target.frame_pointer,
flags: target.flags,
addr_class: target.addr_class,
move_caps: target.move_caps,
max_mem_operands: target.max_mem_operands,
conds: target.conds,
}
}
}
impl Target {
#[must_use]
pub const fn new(
name: &'static str,
isa: Isa,
regs: &'static [RegInfo],
classes: &'static [ClassInfo],
insts: &'static [InstDesc],
config: TargetConfig,
) -> Target {
Target {
name,
isa,
regs,
classes,
insts,
stack_pointer: config.stack_pointer,
frame_pointer: config.frame_pointer,
flags: config.flags,
addr_class: config.addr_class,
move_caps: config.move_caps,
max_mem_operands: config.max_mem_operands,
conds: config.conds,
}
}
#[must_use]
pub const fn generic_insts() -> [InstDesc; 2] {
[
InstDesc::new("copy", InstKind::Copy, &[], &[], &[], RegSet::EMPTY),
InstDesc::new(
"pcopy",
InstKind::ParallelCopy,
&[],
&[],
&[],
RegSet::EMPTY,
),
]
}
#[inline]
#[must_use]
pub const fn name(&self) -> &'static str {
self.name
}
#[inline]
#[must_use]
pub const fn isa(&self) -> Isa {
self.isa
}
#[inline]
#[must_use]
pub const fn regs(&self) -> &'static [RegInfo] {
self.regs
}
#[inline]
#[must_use]
pub fn reg(&self, reg: PReg) -> &RegInfo {
self.regs.get(reg.index()).unwrap_or(&INVALID_REG)
}
#[inline]
#[must_use]
pub fn has_reg(&self, reg: PReg) -> bool {
reg.index() < self.regs.len()
}
#[inline]
#[must_use]
pub const fn classes(&self) -> &'static [ClassInfo] {
self.classes
}
#[inline]
#[must_use]
pub fn class(&self, class: RegClass) -> &ClassInfo {
self.classes.get(class.index()).unwrap_or(&INVALID_CLASS)
}
#[inline]
#[must_use]
pub fn has_class(&self, class: RegClass) -> bool {
class.index() < self.classes.len()
}
#[inline]
#[must_use]
pub const fn insts(&self) -> &'static [InstDesc] {
self.insts
}
#[inline]
#[must_use]
pub fn inst(&self, opcode: Opcode) -> Option<&'static InstDesc> {
self.insts.get(opcode.index())
}
#[must_use]
pub fn opcode(&self, name: &str) -> Option<Opcode> {
self.insts
.iter()
.position(|d| d.name == name)
.and_then(|i| u16::try_from(i).ok())
.map(Opcode)
}
#[inline]
#[must_use]
pub const fn stack_pointer(&self) -> PReg {
self.stack_pointer
}
#[inline]
#[must_use]
pub const fn frame_pointer(&self) -> PReg {
self.frame_pointer
}
#[inline]
#[must_use]
pub const fn flags(&self) -> Option<PReg> {
self.flags
}
#[inline]
#[must_use]
pub const fn addr_class(&self) -> RegClass {
self.addr_class
}
#[inline]
#[must_use]
pub const fn move_caps(&self) -> MoveCaps {
self.move_caps
}
#[inline]
#[must_use]
pub const fn max_mem_operands(&self) -> u8 {
self.max_mem_operands
}
#[inline]
#[must_use]
pub const fn conds(&self) -> u8 {
if self.conds.len() > u8::MAX as usize {
u8::MAX
} else {
self.conds.len() as u8
}
}
#[inline]
#[must_use]
pub fn cond_name(&self, cond: crate::mir::CondCode) -> &'static str {
self.conds
.get(usize::from(cond.code()))
.copied()
.unwrap_or("<invalid>")
}
#[inline]
#[must_use]
pub fn class_of(&self, reg: PReg) -> Option<RegClass> {
self.regs.get(reg.index()).map(RegInfo::class)
}
}