mod emit;
mod table;
pub use emit::{
CallSiteRecord, EmitError, EmittedFunction, Emitter, FunctionInfo, Program, Reloc, RelocKind,
};
#[cfg(test)]
use table::ENCODINGS;
pub use table::op;
use crate::mir::CondCode;
use crate::regs::{PReg, RegClass, RegSet};
use crate::target::abi::{Abi, RegSeq, StackRules};
use crate::target::{ClassInfo, ImmRange, Isa, MoveCaps, RegInfo, Target, TargetConfig};
macro_rules! pregs {
($($name:ident = $idx:expr, $text:literal;)*) => {
$(
#[doc = concat!("The `", $text, "` register.")]
pub const $name: PReg = PReg::from_index($idx);
)*
};
}
pregs! {
RAX = 0, "rax"; RCX = 1, "rcx"; RDX = 2, "rdx"; RBX = 3, "rbx";
RSP = 4, "rsp"; RBP = 5, "rbp"; RSI = 6, "rsi"; RDI = 7, "rdi";
R8 = 8, "r8"; R9 = 9, "r9"; R10 = 10, "r10"; R11 = 11, "r11";
R12 = 12, "r12"; R13 = 13, "r13"; R14 = 14, "r14"; R15 = 15, "r15";
XMM0 = 16, "xmm0"; XMM1 = 17, "xmm1"; XMM2 = 18, "xmm2"; XMM3 = 19, "xmm3";
XMM4 = 20, "xmm4"; XMM5 = 21, "xmm5"; XMM6 = 22, "xmm6"; XMM7 = 23, "xmm7";
XMM8 = 24, "xmm8"; XMM9 = 25, "xmm9"; XMM10 = 26, "xmm10"; XMM11 = 27, "xmm11";
XMM12 = 28, "xmm12"; XMM13 = 29, "xmm13"; XMM14 = 30, "xmm14"; XMM15 = 31, "xmm15";
}
pub const FLAGS: PReg = PReg::from_index(32);
pub const GPR: RegClass = RegClass::new(0);
pub const VEC: RegClass = RegClass::new(1);
pub const FLAGS_CLASS: RegClass = RegClass::new(2);
static REGS: [RegInfo; 33] = [
RegInfo::new("rax", GPR, 0),
RegInfo::new("rcx", GPR, 1),
RegInfo::new("rdx", GPR, 2),
RegInfo::new("rbx", GPR, 3),
RegInfo::new("rsp", GPR, 4),
RegInfo::new("rbp", GPR, 5),
RegInfo::new("rsi", GPR, 6),
RegInfo::new("rdi", GPR, 7),
RegInfo::new("r8", GPR, 8),
RegInfo::new("r9", GPR, 9),
RegInfo::new("r10", GPR, 10),
RegInfo::new("r11", GPR, 11),
RegInfo::new("r12", GPR, 12),
RegInfo::new("r13", GPR, 13),
RegInfo::new("r14", GPR, 14),
RegInfo::new("r15", GPR, 15),
RegInfo::new("xmm0", VEC, 0),
RegInfo::new("xmm1", VEC, 1),
RegInfo::new("xmm2", VEC, 2),
RegInfo::new("xmm3", VEC, 3),
RegInfo::new("xmm4", VEC, 4),
RegInfo::new("xmm5", VEC, 5),
RegInfo::new("xmm6", VEC, 6),
RegInfo::new("xmm7", VEC, 7),
RegInfo::new("xmm8", VEC, 8),
RegInfo::new("xmm9", VEC, 9),
RegInfo::new("xmm10", VEC, 10),
RegInfo::new("xmm11", VEC, 11),
RegInfo::new("xmm12", VEC, 12),
RegInfo::new("xmm13", VEC, 13),
RegInfo::new("xmm14", VEC, 14),
RegInfo::new("xmm15", VEC, 15),
RegInfo::new("flags", FLAGS_CLASS, 0),
];
const GPRS: RegSet = RegSet::of(&[
RAX, RCX, RDX, RBX, RSP, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15,
]);
const XMMS: RegSet = RegSet::of(&[
XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14,
XMM15,
]);
static CLASSES: [ClassInfo; 3] = [
ClassInfo::new("gpr", GPRS, GPRS.without(RSP).without(RBP), 8, 8),
ClassInfo::new("vec", XMMS, XMMS, 16, 16),
ClassInfo::new("flags", RegSet::of(&[FLAGS]), RegSet::EMPTY, 8, 8),
];
static CONDS: [&str; 16] = [
"o", "no", "b", "ae", "e", "ne", "be", "a", "s", "ns", "p", "np", "l", "ge", "le", "g",
];
pub static TARGET: Target = Target::new(
"x86-64",
Isa::X86_64,
®S,
&CLASSES,
&table::DESCS,
TargetConfig {
stack_pointer: RSP,
frame_pointer: RBP,
flags: Some(FLAGS),
addr_class: GPR,
move_caps: MoveCaps::new(true, false, ImmRange::I32),
max_mem_operands: 1,
conds: &CONDS,
},
);
pub static SYSV: Abi = Abi::new(
"sysv64",
&TARGET,
RegSeq::new(
&[RDI, RSI, RDX, RCX, R8, R9],
&[XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7],
false,
),
RegSeq::new(&[RAX, RDX], &[XMM0, XMM1], false),
RegSet::of(&[RBX, RBP, R12, R13, R14, R15]),
StackRules::new(16, 8, 0, 128, 4096),
);
pub static WIN64: Abi = Abi::new(
"win64",
&TARGET,
RegSeq::new(&[RCX, RDX, R8, R9], &[XMM0, XMM1, XMM2, XMM3], true),
RegSeq::new(&[RAX], &[XMM0], true),
RegSet::of(&[
RBX, RBP, RDI, RSI, R12, R13, R14, R15, XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13,
XMM14, XMM15,
]),
StackRules::new(16, 8, 32, 0, 4096),
);
#[inline]
#[must_use]
pub const fn cond(c: isa_lang::x86_64::Cond) -> CondCode {
CondCode::new(c.code())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::regs::MAX_PREGS;
use crate::target::{InstKind, Opcode};
#[test]
fn test_register_file_is_consistent() {
assert!(TARGET.regs().len() <= MAX_PREGS);
for (i, r) in TARGET.regs().iter().enumerate() {
let p = PReg::from_index(i as u8);
assert!(TARGET.has_class(r.class()));
assert!(
TARGET.class(r.class()).regs().contains(p),
"{} in its class",
r.name()
);
if r.class() != FLAGS_CLASS {
assert_eq!(usize::from(r.hw()), i % 16);
}
}
let mut all = RegSet::EMPTY;
for c in TARGET.classes() {
assert!(all.intersect(c.regs()).is_empty());
all = all.union(c.regs());
assert_eq!(c.allocatable().difference(c.regs()), RegSet::EMPTY);
}
assert_eq!(all.len(), TARGET.regs().len());
}
#[test]
fn test_inst_table_starts_with_the_generic_pair_and_names_are_unique() {
let insts = TARGET.insts();
assert_eq!(insts[0].kind(), InstKind::Copy);
assert_eq!(insts[1].kind(), InstKind::ParallelCopy);
assert_eq!(insts.len(), ENCODINGS.len());
for (i, d) in insts.iter().enumerate() {
assert_eq!(
TARGET.opcode(d.name()),
Some(Opcode::new(i as u16)),
"{}",
d.name()
);
}
}
#[test]
fn test_abis_belong_to_the_target() {
for abi in [&SYSV, &WIN64] {
assert!(core::ptr::eq(abi.target(), &TARGET));
for r in abi.args().int().iter().chain(abi.rets().int()) {
assert_eq!(TARGET.class_of(*r), Some(GPR));
}
for r in abi.args().float().iter().chain(abi.rets().float()) {
assert_eq!(TARGET.class_of(*r), Some(VEC));
}
}
}
}