makepad/libs/relax/backend/tests/x64_cabi.rs

78 lines
3.6 KiB
Rust

//! Check both explicit conventions against rustc-built callees and callers on every x86-64 OS.
#![cfg(target_arch = "x86_64")]
use relax_backend::{exec::Code, rir::*, x64};
#[repr(C)]
#[derive(Clone, Copy)]
struct Mixed { integer: i64, float: f64 }
fn mix(a: i64, b: f64, p: Mixed, c: i64, d: i64, e: f64, f: i64, g: i64, h: i64) -> Mixed {
Mixed { integer: a + 3 * p.integer + 5 * c + 7 * d + 11 * f + 13 * g + 17 * h, float: b + 2.0 * p.float + 4.0 * e }
}
extern "sysv64" fn sysv(a: i64, b: f64, p: Mixed, c: i64, d: i64, e: f64, f: i64, g: i64, h: i64) -> Mixed {
mix(a, b, p, c, d, e, f, g, h)
}
extern "win64" fn win(a: i64, b: f64, p: Mixed, c: i64, d: i64, e: f64, f: i64, g: i64, h: i64) -> Mixed {
mix(a, b, p, c, d, e, f, g, h)
}
fn signature(conv: CConv) -> CSig {
let i = CArg::Scalar(Mem::Int(8, true));
let f = CArg::Scalar(Mem::F64);
let pair = CAgg { size: 16, align: 8, fields: vec![(0, Mem::Int(8, true)), (8, Mem::F64)] };
CSig { conv, args: vec![i.clone(), f.clone(), CArg::Agg(pair.clone()), i.clone(), i.clone(), f, i.clone(), i.clone(), i],
ret: Some(pair), n_fixed: u32::MAX, ret_narrow: None, unwinds: false }
}
fn forwarding_body(body: CConv, callee: CConv, regs: bool) -> Code {
let mut f = Func::new("explicit_x64_abi".into(), 0);
f.cabi = Some(Box::new(signature(body)));
// Aggregate result address, then scalar values or addresses of aggregate arguments.
for cls in [Cls::I, Cls::I, Cls::F64, Cls::I, Cls::I, Cls::I, Cls::F64, Cls::I, Cls::I, Cls::I] {
let v = f.vreg(cls);
f.params.push(v);
}
let target = f.vreg(Cls::I);
let addr = match callee { CConv::SysV64 => sysv as *const () as usize, CConv::Win64 => win as *const () as usize, _ => unreachable!() };
let b = f.block();
f.blocks[b as usize].insts = vec![
Inst::Iconst(target, addr as i64),
Inst::Call(Callee::CIndirect(target, Box::new(signature(callee))), f.params.clone(), Vec::new()),
];
f.blocks[b as usize].pos = vec![0, 0];
f.blocks[b as usize].term = Term::Ret(Vec::new());
f.verify().unwrap();
let env = x64::Env { slots: SlotsPtr::NONE, tls_key: 0, tls_slow: 0, resume: 0, opt_regs: regs, syms: 0, sym_class: 0, near: false };
let c = x64::compile(&relax_session::Options::default(), &f, &env).unwrap();
assert!(c.relocs.is_empty());
Code::new(&c.code).unwrap()
}
#[test]
fn explicit_conventions_cross_native_boundaries() {
for body in [CConv::SysV64, CConv::Win64] {
for callee in [CConv::SysV64, CConv::Win64] {
for regs in [false, true] {
let code = forwarding_body(body, callee, regs);
let p = Mixed { integer: 3, float: 4.0 };
let result = unsafe {
match body {
CConv::SysV64 => {
let run: extern "sysv64" fn(i64, f64, Mixed, i64, i64, f64, i64, i64, i64) -> Mixed = std::mem::transmute(code.ptr());
run(2, 1.5, p, 5, 7, 2.25, 11, 13, 17)
}
CConv::Win64 => {
let run: extern "win64" fn(i64, f64, Mixed, i64, i64, f64, i64, i64, i64) -> Mixed = std::mem::transmute(code.ptr());
run(2, 1.5, p, 5, 7, 2.25, 11, 13, 17)
}
_ => unreachable!(),
}
};
assert_eq!(result.integer, 664, "{body:?} -> {callee:?}, regs={regs}");
assert_eq!(result.float, 18.5, "{body:?} -> {callee:?}, regs={regs}");
}
}
}
}