use vyre_foundation::ir::{BufferAccess, BufferDecl, DataType, Expr, Node, Program};
use vyre_foundation::optimizer::pre_lowering as optimize;
use vyre_reference::value::Value;
fn in_i() -> Expr {
Expr::load("in", Expr::u32(0))
}
const PROBES: &[u32] = &[
0,
1,
2,
3,
7,
8,
255,
0x5555_5555,
0xAAAA_AAAA,
0x8000_0000,
0x7FFF_FFFF,
0xFFFF_FFFF,
];
fn out_in(body: Vec<Node>) -> Program {
Program::wrapped(
vec![
BufferDecl::output("out", 0, DataType::U32).with_count(1),
BufferDecl::storage("in", 1, BufferAccess::ReadOnly, DataType::U32).with_count(1),
],
[1, 1, 1],
body,
)
}
fn out_in_scratch(body: Vec<Node>) -> Program {
Program::wrapped(
vec![
BufferDecl::output("out", 0, DataType::U32).with_count(1),
BufferDecl::storage("in", 1, BufferAccess::ReadOnly, DataType::U32).with_count(1),
BufferDecl::storage("scratch", 2, BufferAccess::ReadWrite, DataType::U32).with_count(1),
],
[1, 1, 1],
body,
)
}
fn single_input_programs() -> Vec<(&'static str, Program)> {
let cse = out_in(vec![
Node::let_bind(
"a",
Expr::add(Expr::mul(in_i(), Expr::u32(3)), Expr::u32(7)),
),
Node::let_bind(
"b",
Expr::add(Expr::mul(in_i(), Expr::u32(3)), Expr::u32(7)),
),
Node::store(
"out",
Expr::u32(0),
Expr::add(Expr::var("a"), Expr::var("b")),
),
]);
let copy_prop = out_in(vec![
Node::let_bind("a", in_i()),
Node::let_bind("b", Expr::var("a")),
Node::let_bind("c", Expr::var("b")),
Node::store("out", Expr::u32(0), Expr::shl(Expr::var("c"), Expr::u32(2))),
]);
let dead_store = out_in(vec![
Node::store("out", Expr::u32(0), Expr::u32(0xDEAD_BEEF)),
Node::store("out", Expr::u32(0), Expr::add(in_i(), Expr::u32(1))),
]);
let redundant_load = out_in(vec![
Node::let_bind("a", in_i()),
Node::let_bind("b", in_i()),
Node::store(
"out",
Expr::u32(0),
Expr::add(
Expr::mul(Expr::var("a"), Expr::u32(7)),
Expr::mul(Expr::var("b"), Expr::u32(3)),
),
),
]);
let branch_hoist = out_in(vec![Node::if_then_else(
Expr::bitand(in_i(), Expr::u32(1)),
vec![Node::store(
"out",
Expr::u32(0),
Expr::add(in_i(), Expr::u32(5)),
)],
vec![Node::store(
"out",
Expr::u32(0),
Expr::add(in_i(), Expr::u32(5)),
)],
)]);
let branch_distinct = out_in(vec![Node::if_then_else(
Expr::bitand(in_i(), Expr::u32(1)),
vec![Node::store(
"out",
Expr::u32(0),
Expr::mul(in_i(), Expr::u32(2)),
)],
vec![Node::store(
"out",
Expr::u32(0),
Expr::add(in_i(), Expr::u32(100)),
)],
)]);
vec![
("cse", cse),
("copy_prop", copy_prop),
("dead_store", dead_store),
("redundant_load", redundant_load),
("branch_hoist", branch_hoist),
("branch_distinct", branch_distinct),
]
}
fn scratch_programs() -> Vec<(&'static str, Program)> {
let forward_valid = out_in_scratch(vec![
Node::let_bind("v", Expr::bitxor(in_i(), Expr::u32(0x9E37_79B9))),
Node::store("scratch", Expr::u32(0), Expr::var("v")),
Node::let_bind("x", Expr::load("scratch", Expr::u32(0))),
Node::store("out", Expr::u32(0), Expr::add(Expr::var("x"), Expr::u32(1))),
]);
let forward_reassign_guard = out_in_scratch(vec![
Node::let_bind("t", in_i()),
Node::store("scratch", Expr::u32(0), Expr::var("t")),
Node::assign("t", Expr::bitxor(in_i(), Expr::u32(0xFFFF_FFFF))),
Node::let_bind("x", Expr::load("scratch", Expr::u32(0))),
Node::store("out", Expr::u32(0), Expr::var("x")),
]);
vec![
("forward_valid", forward_valid),
("forward_reassign_guard", forward_reassign_guard),
]
}
#[test]
fn cse_oracle_computes_the_real_value() {
let (_, cse) = single_input_programs()
.into_iter()
.find(|(name, _)| *name == "cse")
.expect("cse program present");
let out = vyre_reference::reference_eval(&cse, &[Value::U32(10)])
.expect("cse program must run on the reference oracle");
assert_eq!(
out[0],
Value::from(74u32.to_le_bytes().to_vec()),
"out == 2*(10*3+7) == 74"
);
}
#[test]
fn full_optimize_preserves_single_input_dataflow_value() {
for (name, program) in single_input_programs() {
let optimized = optimize::optimize(program.clone());
for &v in PROBES {
let inputs = [Value::U32(v)];
let base = vyre_reference::reference_eval(&program, &inputs)
.unwrap_or_else(|e| panic!("base `{name}` must run on the oracle: {e}"));
let opt = vyre_reference::reference_eval(&optimized, &inputs)
.unwrap_or_else(|e| panic!("optimized `{name}` must run on the oracle: {e}"));
assert_eq!(
base, opt,
"optimize::optimize changed the observable result of `{name}` at in[0]={v:#010x}"
);
}
}
}
#[test]
fn full_optimize_preserves_store_forward_value() {
for (name, program) in scratch_programs() {
let optimized = optimize::optimize(program.clone());
for &v in PROBES {
let inputs = [Value::U32(v), Value::U32(0)];
let base = vyre_reference::reference_eval(&program, &inputs)
.unwrap_or_else(|e| panic!("base `{name}` must run on the oracle: {e}"));
let opt = vyre_reference::reference_eval(&optimized, &inputs)
.unwrap_or_else(|e| panic!("optimized `{name}` must run on the oracle: {e}"));
assert_eq!(
base, opt,
"optimize::optimize changed the observable result of `{name}` at in[0]={v:#010x}"
);
}
}
}