78 lines
3.6 KiB
Rust
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}");
|
|
}
|
|
}
|
|
}
|
|
}
|