mod encode;
mod insts;
mod text;
pub use crate::x86_64::encode::{
Addr, Encoding, Error, Fields, Fits, Holes, ImmSize, Kind, Size, Value, encode, encoding,
};
pub use crate::x86_64::insts::{ADDRESSES, Address, Form, INSTS, address, form};
pub use crate::x86_64::text::{Arg, Width, Written, gpr_name, written};
use crate::branch::BranchInsts;
use crate::frame::{ClassMoves, FrameInsts};
use crate::regs::{CallRegs, ClassInfo, PhysReg, RegClass, RegFile};
pub const GPR: RegClass = RegClass::new(0);
pub const XMM: RegClass = RegClass::new(1);
pub const X87: RegClass = RegClass::new(2);
pub const RAX: PhysReg = PhysReg::new(0);
pub const RCX: PhysReg = PhysReg::new(1);
pub const RDX: PhysReg = PhysReg::new(2);
pub const RBX: PhysReg = PhysReg::new(3);
pub const RSP: PhysReg = PhysReg::new(4);
pub const RBP: PhysReg = PhysReg::new(5);
pub const RSI: PhysReg = PhysReg::new(6);
pub const RDI: PhysReg = PhysReg::new(7);
pub const R8: PhysReg = PhysReg::new(8);
pub const R9: PhysReg = PhysReg::new(9);
pub const R10: PhysReg = PhysReg::new(10);
pub const R11: PhysReg = PhysReg::new(11);
pub const R12: PhysReg = PhysReg::new(12);
pub const R13: PhysReg = PhysReg::new(13);
pub const R14: PhysReg = PhysReg::new(14);
pub const R15: PhysReg = PhysReg::new(15);
#[must_use]
pub const fn xmm(number: u8) -> PhysReg {
assert!(number < 16, "x86-64 has sixteen vector registers without an extension");
PhysReg::new(number)
}
#[must_use]
pub const fn st(number: u8) -> PhysReg {
assert!(number < 8, "the x87 stack is eight deep");
PhysReg::new(number)
}
static GPR_NAMES: [&str; 16] = [
"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13",
"r14", "r15",
];
static XMM_NAMES: [&str; 16] = [
"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10",
"xmm11", "xmm12", "xmm13", "xmm14", "xmm15",
];
static X87_NAMES: [&str; 8] = ["st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7"];
static CLASSES: [ClassInfo; 3] = [
ClassInfo { name: "gpr", bits: 64, regs: &GPR_NAMES },
ClassInfo { name: "xmm", bits: 128, regs: &XMM_NAMES },
ClassInfo { name: "x87", bits: 80, regs: &X87_NAMES },
];
pub static REGS: RegFile = RegFile::new(&CLASSES);
static X86_64_MOVES: [ClassMoves; 2] = [
ClassMoves { mov: "mov_rr_64", load: "mov_rm_64", store: "mov_mr_64" },
ClassMoves { mov: "movaps_rr", load: "movaps_rm", store: "movaps_mr" },
];
pub static FRAME: FrameInsts = FrameInsts {
prefix: "x64.",
classes: &X86_64_MOVES,
push: "push_64",
pop: "pop_64",
add: "add_ri_64",
sub: "sub_ri_64",
align: "and_ri_64",
lea: "lea_64",
ret: "ret",
};
pub static BRANCH: BranchInsts = BranchInsts {
prefix: "x64.",
cond: "br_cond_8",
test: "test_rr_8",
if_true: "jcc_ne",
if_false: "jcc_e",
jump: "jmp",
};
static SYSV_INT_ARGS: [PhysReg; 6] = [RDI, RSI, RDX, RCX, R8, R9];
static SYSV_SSE_ARGS: [PhysReg; 8] =
[xmm(0), xmm(1), xmm(2), xmm(3), xmm(4), xmm(5), xmm(6), xmm(7)];
static SYSV_INT_RETURNS: [PhysReg; 2] = [RAX, RDX];
static SYSV_SSE_RETURNS: [PhysReg; 2] = [xmm(0), xmm(1)];
static SYSV_X87_RETURNS: [PhysReg; 2] = [st(0), st(1)];
static SYSV_INT_SAVED: [PhysReg; 6] = [RBX, RBP, R12, R13, R14, R15];
static SYSV_SSE_SAVED: [PhysReg; 0] = [];
static SYSV_INT_ORDER: [PhysReg; 14] =
[RAX, RCX, RDX, RSI, RDI, R8, R9, R10, R11, RBX, R12, R13, R14, R15];
static SSE_ORDER: [PhysReg; 16] = [
xmm(0),
xmm(1),
xmm(2),
xmm(3),
xmm(4),
xmm(5),
xmm(6),
xmm(7),
xmm(8),
xmm(9),
xmm(10),
xmm(11),
xmm(12),
xmm(13),
xmm(14),
xmm(15),
];
pub static SYSV: CallRegs = CallRegs {
int_class: GPR,
sse_class: XMM,
int_args: &SYSV_INT_ARGS,
sse_args: &SYSV_SSE_ARGS,
shared_positions: false,
int_returns: &SYSV_INT_RETURNS,
sse_returns: &SYSV_SSE_RETURNS,
x87_returns: &SYSV_X87_RETURNS,
int_saved: &SYSV_INT_SAVED,
sse_saved: &SYSV_SSE_SAVED,
int_order: &SYSV_INT_ORDER,
sse_order: &SSE_ORDER,
stack_pointer: RSP,
frame_pointer: RBP,
vector_count: Some(RAX),
red_zone: 128,
shadow: 0,
stack_align: 16,
return_address: 8,
word: 8,
};
static WIN64_INT_ARGS: [PhysReg; 4] = [RCX, RDX, R8, R9];
static WIN64_SSE_ARGS: [PhysReg; 4] = [xmm(0), xmm(1), xmm(2), xmm(3)];
static WIN64_INT_RETURNS: [PhysReg; 1] = [RAX];
static WIN64_SSE_RETURNS: [PhysReg; 1] = [xmm(0)];
static WIN64_X87_RETURNS: [PhysReg; 0] = [];
static WIN64_INT_SAVED: [PhysReg; 8] = [RBX, RBP, RSI, RDI, R12, R13, R14, R15];
static WIN64_SSE_SAVED: [PhysReg; 10] =
[xmm(6), xmm(7), xmm(8), xmm(9), xmm(10), xmm(11), xmm(12), xmm(13), xmm(14), xmm(15)];
static WIN64_INT_ORDER: [PhysReg; 14] =
[RAX, RCX, RDX, R8, R9, R10, R11, RBX, RSI, RDI, R12, R13, R14, R15];
pub static WIN64: CallRegs = CallRegs {
int_class: GPR,
sse_class: XMM,
int_args: &WIN64_INT_ARGS,
sse_args: &WIN64_SSE_ARGS,
shared_positions: true,
int_returns: &WIN64_INT_RETURNS,
sse_returns: &WIN64_SSE_RETURNS,
x87_returns: &WIN64_X87_RETURNS,
int_saved: &WIN64_INT_SAVED,
sse_saved: &WIN64_SSE_SAVED,
int_order: &WIN64_INT_ORDER,
sse_order: &SSE_ORDER,
stack_pointer: RSP,
frame_pointer: RBP,
vector_count: None,
red_zone: 0,
shadow: 32,
stack_align: 16,
return_address: 8,
word: 8,
};
#[cfg(test)]
mod tests {
use super::*;
fn covers(order: &[PhysReg], count: usize) -> bool {
let mut seen: Vec<u8> = order.iter().map(|reg| reg.number()).collect();
seen.sort_unstable();
seen.dedup();
seen.len() == order.len() && order.len() == count
}
#[test]
fn the_file_numbers_registers_the_way_the_encoding_does() {
assert_eq!(REGS.name(GPR, RAX), Some("rax"));
assert_eq!(REGS.name(GPR, RSP), Some("rsp"));
assert_eq!(REGS.name(GPR, R15), Some("r15"));
assert_eq!(REGS.reg_named("rdi"), Some((GPR, RDI)));
assert_eq!(REGS.reg_named("xmm9"), Some((XMM, xmm(9))));
assert_eq!(REGS.reg_named("st0"), Some((X87, st(0))));
}
#[test]
fn the_file_gives_no_name_to_two_registers() {
assert_eq!(REGS.duplicate(), None);
assert_eq!(REGS.len(GPR), 16);
assert_eq!(REGS.len(XMM), 16);
assert_eq!(REGS.len(X87), 8);
}
#[test]
fn the_allocator_is_offered_every_register_but_the_two_the_frame_needs() {
for convention in [&SYSV, &WIN64] {
assert!(covers(convention.int_order, 14));
assert!(covers(convention.sse_order, 16));
assert!(!convention.int_order.contains(&RSP));
assert!(!convention.int_order.contains(&RBP));
}
}
#[test]
fn a_register_a_call_destroys_is_offered_before_one_it_preserves() {
for convention in [&SYSV, &WIN64] {
let first_saved = convention
.int_order
.iter()
.position(|®| convention.preserves_int(reg))
.expect("some register in the order is preserved");
assert!(
convention.int_order[..first_saved]
.iter()
.all(|®| !convention.preserves_int(reg)),
"the preserved registers are not one run at the end"
);
}
}
#[test]
fn the_two_conventions_disagree_where_the_psabis_do() {
assert_eq!(SYSV.int_args[0], RDI);
assert_eq!(WIN64.int_args[0], RCX);
assert!(!SYSV.preserves_int(RDI));
assert!(WIN64.preserves_int(RDI));
assert!(!SYSV.preserves_sse(xmm(6)));
assert!(WIN64.preserves_sse(xmm(6)));
assert_eq!((SYSV.red_zone, SYSV.shadow), (128, 0));
assert_eq!((WIN64.red_zone, WIN64.shadow), (0, 32));
assert_eq!(SYSV.vector_count, Some(RAX));
assert_eq!(WIN64.vector_count, None);
}
#[test]
fn a_long_double_comes_back_on_the_x87_stack_only_where_there_is_one() {
assert_eq!(SYSV.x87_returns, [st(0), st(1)]);
assert!(WIN64.x87_returns.is_empty());
}
}