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

85 lines
3.5 KiB
Rust

//! `VTbl`: d byte k = the byte at [base + bidx.byte(k)] of a small table (n words at base, byte
//! indices below 4n), on both back ends (NEON: ld1 + TBL; x86-64: pshufb per 16-byte quarter),
//! for every table size the kernels use and byte indices covering each quarter, word lookups
//! (bytes 4i .. 4i + 3) and arbitrary byte orders.
#![cfg(any(all(target_arch = "x86_64", unix), all(target_arch = "aarch64", unix)))]
use relax_backend::{exec::Code, rir::*};
#[cfg(target_arch = "x86_64")]
type Entry = extern "sysv64" fn(*const u8, *const u8, *mut u8);
#[cfg(target_arch = "aarch64")]
type Entry = extern "C" fn(*const u8, *const u8, *mut u8);
fn options() -> relax_session::Options {
#[allow(unused_mut)]
let mut o = relax_session::Options::default();
#[cfg(target_arch = "x86_64")]
{
o.cpu = relax_backend::x64::detect_cpu(&o);
}
o
}
fn compile(f: &Func, opt_regs: bool) -> Code {
f.verify().unwrap();
#[cfg(target_arch = "x86_64")]
let c = {
let env = relax_backend::x64::Env { slots: SlotsPtr::NONE, tls_key: 0, tls_slow: 0, resume: 0, opt_regs, syms: 0, sym_class: 0, near: false };
relax_backend::x64::compile(&options(), f, &env).unwrap()
};
#[cfg(target_arch = "aarch64")]
let c = {
let env = relax_backend::arm64::Env { slots: SlotsPtr::NONE, tls_key: 0, tls_slow: 0, resume: 0, opt_regs, syms: 0, sym_class: 0, near: false };
relax_backend::arm64::compile(&options(), f, &env).unwrap()
};
assert!(c.relocs.is_empty());
Code::new(&c.code).unwrap()
}
/// fn(table: *const u8, bidx: *const [u8; 16], dst: *mut [u8; 16]) with one VTbl of n words.
fn tbl(n: u8, opt_regs: bool) -> Code {
let mut f = Func::new("vtbl".into(), 0);
let (pt, pi, pd) = (f.vreg(Cls::I), f.vreg(Cls::I), f.vreg(Cls::I));
f.params = vec![pt, pi, pd];
f.block();
let (i, d) = (f.vreg(Cls::V128), f.vreg(Cls::V128));
let insts = vec![Inst::VLoad(i, pi, 0), Inst::VTbl(d, pt, i, n), Inst::VStore(pd, 0, d)];
f.blocks[0].pos = vec![0; insts.len()];
f.blocks[0].insts = insts;
f.blocks[0].term = Term::Ret(vec![]);
compile(&f, opt_regs)
}
#[test]
fn vtbl_reads_each_byte_of_the_table() {
let table: Vec<u8> = (0..64u32).map(|k| (k * 37 + 11) as u8).collect();
let mut seed = 0x1234_5678u32;
for n in [4u8, 8, 12, 16] {
for opt_regs in [false, true] {
let code = tbl(n, opt_regs);
let run: Entry = unsafe { std::mem::transmute(code.ptr()) };
for round in 0..200 {
let mut bidx = [0u8; 16];
if round % 2 == 0 {
// word lookups: lane l reads word w_l
for l in 0..4 {
seed = seed.wrapping_mul(1664525).wrapping_add(1013904223);
let w = (seed >> 24) as u8 % n;
for k in 0..4 {
bidx[4 * l + k] = 4 * w + k as u8;
}
}
} else {
for b in bidx.iter_mut() {
seed = seed.wrapping_mul(1664525).wrapping_add(1013904223);
*b = (seed >> 24) as u8 % (4 * n);
}
}
let mut out = [0u8; 16];
run(table.as_ptr(), bidx.as_ptr(), out.as_mut_ptr());
let want: Vec<u8> = bidx.iter().map(|b| table[*b as usize]).collect();
assert_eq!(&out[..], &want[..], "n {} opt_regs {} bidx {:?}", n, opt_regs, bidx);
}
}
}
}