makepad/libs/relax/codegen/tests/form_bzhi.rs

71 lines
2.3 KiB
Rust

//! form_bzhi: `x & ((1 << n) - 1)` and `x & !(-1 << n)` on 64-bit values become IOp::Bzhi when
//! the shift and mask are read only there and n is the same value at both.
use relax_backend::rir::*;
use relax_codegen::opt::form_bzhi;
fn push(f: &mut Func, i: Inst) {
f.blocks[0].insts.push(i);
f.blocks[0].pos.push(1);
}
fn options() -> relax_session::Options {
let mut o = relax_session::Options::default();
o.cpu.bmi2 = true;
o
}
/// ret x & ((1 << n) - 1) [`not`: x & !(-1 << n)]; `redef`: n reassigned between shift and mask
fn idiom(not: bool, redef: bool) -> (Func, VReg) {
let mut f = Func::new("mask".into(), 0);
let (x, n0) = (f.vreg(Cls::I), f.vreg(Cls::I));
f.params = vec![x, n0];
f.rets = vec![Cls::I];
f.block();
let (n, one, s, m, d) = (f.vreg(Cls::I), f.vreg(Cls::I), f.vreg(Cls::I), f.vreg(Cls::I), f.vreg(Cls::I));
push(&mut f, Inst::Mov(n, n0));
push(&mut f, Inst::Iconst(one, if not { -1 } else { 1 }));
push(&mut f, Inst::IBin(IOp::Shl, U64, s, one, n));
if not {
push(&mut f, Inst::IBinI(IOp::Xor, U64, m, s, -1));
} else {
push(&mut f, Inst::IBinI(IOp::Sub, U64, m, s, 1));
}
if redef {
push(&mut f, Inst::IBinI(IOp::Add, U64, n, n, 1));
}
push(&mut f, Inst::IBin(IOp::And, U64, d, m, x));
f.blocks[0].term = Term::Ret(vec![d, ]);
f.verify().unwrap();
(f, d)
}
fn bzhi_of(f: &Func, d: VReg) -> bool {
f.blocks[0].insts.iter().any(|i| matches!(i, Inst::IBin(IOp::Bzhi, _, dd, _, _) if *dd == d))
}
#[test]
fn the_mask_idiom_becomes_bzhi() {
for not in [false, true] {
let (mut f, d) = idiom(not, false);
assert!(form_bzhi(&options(), &mut f));
f.verify().unwrap();
assert!(bzhi_of(&f, d), "{}", f.dump());
}
}
#[test]
fn a_count_changed_in_between_is_left_alone() {
let (mut f, d) = idiom(false, true);
assert!(!form_bzhi(&options(), &mut f));
assert!(!bzhi_of(&f, d));
}
#[test]
fn a_mask_read_twice_is_left_alone() {
let (mut f, d) = idiom(false, false);
// the mask is also returned
let m = match f.blocks[0].insts[3] { Inst::IBinI(_, _, m, _, _) => m, _ => unreachable!() };
f.rets.push(Cls::I);
f.blocks[0].term = Term::Ret(vec![d, m]);
assert!(!form_bzhi(&options(), &mut f));
}