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

52 lines
2.5 KiB
Rust

//! u32 / u16 byte swaps and u32 bit reversal as RIR has them (64-bit `IBit` then a shift down):
//! arm64 emits `rev w` / `rev16 w` (a zero-extended u16 from a narrow load) / `rbit w`; results
//! against Rust's swap_bytes / reverse_bits, run on the host.
#![cfg(any(all(target_arch = "x86_64", unix), all(target_arch = "aarch64", unix)))]
use relax_backend::{exec::Code, rir::*};
#[cfg(target_arch = "x86_64")]
fn compile(f: &Func) -> Code {
let env = relax_backend::x64::Env { slots: SlotsPtr::NONE, tls_key: 0, tls_slow: 0, resume: 0, opt_regs: true, syms: 0, sym_class: 0, near: false };
Code::new(&relax_backend::x64::compile(&relax_session::Options::default(), f, &env).unwrap().code).unwrap()
}
#[cfg(target_arch = "aarch64")]
fn compile(f: &Func) -> Code {
let env = relax_backend::arm64::Env { slots: SlotsPtr::NONE, tls_key: 0, tls_slow: 0, resume: 0, opt_regs: true, syms: 0, sym_class: 0, near: false };
Code::new(&relax_backend::arm64::compile(&relax_session::Options::default(), f, &env).unwrap().code).unwrap()
}
/// fn(p: *const u64) -> u64 { x = load(p) (8 bytes, or 2 zero-extended); op(x) >> sh }
fn func(op: BitOp, load: u8, sh: i64) -> Func {
let mut f = Func::new("narrow".into(), 0);
let p = f.vreg(Cls::I);
f.params = vec![p];
f.rets = vec![Cls::I];
f.block();
let (x, t, d) = (f.vreg(Cls::I), f.vreg(Cls::I), f.vreg(Cls::I));
for i in [Inst::Load(Mem::Int(load, false), x, p, 0), Inst::IBit(op, t, x), Inst::IBinI(IOp::Shr, IntTy { bits: 64, signed: false }, d, t, sh)] {
f.blocks[0].insts.push(i);
f.blocks[0].pos.push(0);
}
f.blocks[0].term = Term::Ret(vec![d]);
f.verify().unwrap();
f
}
#[test]
fn narrow_byte_swaps_and_bit_reversals() {
for x in [0u64, 1, 0x1234, 0x8001, 0x1234_5678, 0xdead_beef_cafe_f00d, u64::MAX] {
let cases: [(BitOp, u8, i64, u64); 5] = [
(BitOp::Bswap, 8, 32, (x as u32).swap_bytes() as u64),
(BitOp::Bswap, 2, 48, (x as u16).swap_bytes() as u64),
(BitOp::Bswap, 8, 48, (x as u16).swap_bytes() as u64),
(BitOp::Rev, 8, 32, (x as u32).reverse_bits() as u64),
(BitOp::Bswap, 8, 16, x.swap_bytes() >> 16),
];
for (op, load, sh, want) in cases {
let code = compile(&func(op, load, sh));
let run: extern "C" fn(*const u64) -> u64 = unsafe { std::mem::transmute(code.ptr()) };
assert_eq!(run(&x), want, "{op:?} load {load} >> {sh} of {x:#x}");
}
}
}