52 lines
2.5 KiB
Rust
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}");
|
|
}
|
|
}
|
|
}
|