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

81 lines
3.1 KiB
Rust

//! fold_small_sets: compares and selects decided by the few values their operands can hold
//! (tuple `lt` through `Ordering` as an i8, tested against ControlFlow's niche 254).
use relax_backend::rir::*;
use relax_codegen::opt::fold_small_sets;
fn push(f: &mut Func, i: Inst) {
f.blocks[0].insts.push(i);
f.blocks[0].pos.push(1);
}
/// f(a, b): o = a < b ? -1 : ((a == b) ^ 1); t = (o as i8) & 255; returns (t != k, t <= 1, o)
fn ordering(k: i64) -> (Func, [VReg; 3]) {
let mut f = Func::new("ordering".into(), 0);
let (a, b) = (f.vreg(Cls::I), f.vreg(Cls::I));
f.params = vec![a, b];
f.rets = vec![Cls::I, Cls::I, Cls::I];
f.block();
let v: Vec<VReg> = (0..9).map(|_| f.vreg(Cls::I)).collect();
push(&mut f, Inst::ICmp(Cond::Lt, false, v[0], a, b));
push(&mut f, Inst::ICmp(Cond::Eq, false, v[1], a, b));
push(&mut f, Inst::IBinI(IOp::Xor, U64, v[2], v[1], 1));
push(&mut f, Inst::Iconst(v[3], -1));
push(&mut f, Inst::Sel(v[4], v[0], v[3], v[2]));
push(&mut f, Inst::Conv(Conv::IntToInt(IntTy { bits: 8, signed: true }), v[5], v[4]));
push(&mut f, Inst::IBinI(IOp::And, U64, v[6], v[5], 255));
push(&mut f, Inst::ICmpI(Cond::Ne, false, v[7], v[6], k));
push(&mut f, Inst::ICmpI(Cond::Le, false, v[8], v[6], 1));
f.blocks[0].term = Term::Ret(vec![v[7], v[8], v[4]]);
f.verify().unwrap();
(f, [v[7], v[8], v[4]])
}
fn def(f: &Func, v: VReg) -> Inst {
f.blocks[0].insts.iter().find(|i| {
let mut d = false;
relax_backend::regalloc::for_defs(i, |x| d |= x == v);
d
}).cloned().unwrap()
}
#[test]
fn a_niche_no_ordering_value_has_is_never_equal() {
let (mut f, [ne, le, _]) = ordering(254);
assert!(fold_small_sets(&mut f));
f.verify().unwrap();
assert!(matches!(def(&f, ne), Inst::Iconst(_, 1)), "{}", f.dump());
// t is 255, 0 or 1: `t <= 1` is not decided
assert!(matches!(def(&f, le), Inst::ICmpI(..)));
}
#[test]
fn a_value_in_the_set_is_left_alone() {
// 255 is Less: the compare depends on the operands
let (mut f, [ne, ..]) = ordering(255);
fold_small_sets(&mut f);
assert!(matches!(def(&f, ne), Inst::ICmpI(..)));
}
#[test]
fn a_select_on_a_decided_condition_is_a_copy() {
let (mut f, [ne, _, o]) = ordering(254);
// s = (t != 254) ? a : b
let (a, b) = (f.params[0], f.params[1]);
let s = f.vreg(Cls::I);
push(&mut f, Inst::Sel(s, ne, a, b));
f.blocks[0].term = Term::Ret(vec![s, o, o]);
assert!(fold_small_sets(&mut f));
assert!(matches!(def(&f, s), Inst::Mov(_, x) if x == a), "{}", f.dump());
}
#[test]
fn a_constant_defined_on_two_paths_is_one_value() {
// the -1 arm is a vreg assigned -1 twice (one constant per path after jump threading)
let (mut f, [ne, ..]) = ordering(254);
let k = f.blocks[0].insts.iter().position(|i| matches!(i, Inst::Iconst(_, -1))).unwrap();
let Inst::Iconst(m1, _) = f.blocks[0].insts[k] else { unreachable!() };
f.blocks[0].insts.insert(k, Inst::Iconst(m1, -1));
f.blocks[0].pos.insert(k, 1);
assert!(fold_small_sets(&mut f));
assert!(matches!(def(&f, ne), Inst::Iconst(_, 1)), "{}", f.dump());
}