use vyre_foundation::ir::{BinOp, BufferAccess, BufferDecl, DataType, Expr, Ident, Node, Program};
use vyre_foundation::optimizer::passes::loops::loop_software_pipeline::LoopSoftwarePipeline;
use vyre_reference::value::Value;
fn program_value_uses_loop_var() -> Program {
Program::wrapped(
vec![
BufferDecl::storage("buf_in", 0, BufferAccess::ReadOnly, DataType::U32).with_count(8),
BufferDecl::output("buf_out", 1, DataType::U32).with_count(8),
],
[1, 1, 1],
vec![Node::Loop {
var: Ident::from("i"),
from: Expr::u32(0),
to: Expr::u32(8),
body: vec![
Node::let_bind("x", Expr::load("buf_in", Expr::var("i"))),
Node::store(
"buf_out",
Expr::var("i"),
Expr::BinOp {
op: BinOp::Add,
left: Box::new(Expr::var("x")),
right: Box::new(Expr::var("i")),
},
),
],
}],
)
}
#[test]
fn software_pipeline_does_not_leak_loop_var_into_epilogue() {
let program = program_value_uses_loop_var();
let buf_in: Vec<u8> = (10u32..18).flat_map(u32::to_le_bytes).collect();
let inputs = [Value::from(buf_in)];
let original = vyre_reference::reference_eval(&program, &inputs)
.expect("original loop program is valid and must run");
let expected: Vec<u8> = (0u32..8).flat_map(|i| (10 + i + i).to_le_bytes()).collect();
assert_eq!(
original,
vec![Value::from(expected)],
"buf_out[i] == buf_in[i] + i",
);
let result = LoopSoftwarePipeline::transform(program);
assert!(result.changed, "the Load-then-Store loop must pipeline");
let after = vyre_reference::reference_eval(&result.program, &inputs).expect(
"pipelined program must still validate and run -- the epilogue must not \
reference the loop variable after the steady loop has exited",
);
assert_eq!(
after, original,
"software pipelining must preserve semantics when the store value uses \
the loop variable; the epilogue (iteration hi-1) must substitute the \
loop var with the literal hi-1",
);
}