use std::sync::Arc;
use vyre_foundation::ir::model::expr::Ident;
use vyre_foundation::ir::{BufferDecl, DataType, Expr, Node, Program};
use vyre_foundation::MemoryOrdering;
fn nested_fence_program() -> Program {
Program::wrapped(
vec![BufferDecl::read_write("state", 0, DataType::U32).with_count(256)],
[256, 1, 1],
vec![Node::Region {
generator: Ident::from("grid-sync-nested-fence-probe"),
source_region: None,
body: Arc::new(vec![Node::loop_for(
"iter",
Expr::u32(0),
Expr::u32(4),
vec![
Node::store("state", Expr::gid_x(), Expr::u32(1)),
Node::barrier_with_ordering(MemoryOrdering::GridSync),
],
)]),
}],
)
}
fn grid_sync_fences(nodes: &[Node]) -> usize {
nodes
.iter()
.map(|node| match node {
Node::Barrier {
ordering: MemoryOrdering::GridSync,
} => 1,
Node::If {
then, otherwise, ..
} => grid_sync_fences(then) + grid_sync_fences(otherwise),
Node::Loop { body, .. } | Node::Block(body) => grid_sync_fences(body),
Node::Region { body, .. } => grid_sync_fences(body),
_ => 0,
})
.sum()
}
#[test]
fn a_loop_nested_grid_sync_fence_is_preserved_through_the_split() {
let program = nested_fence_program();
assert_eq!(
grid_sync_fences(program.entry()),
1,
"the probe program must declare exactly one loop-nested grid fence"
);
assert!(
vyre_driver::grid_sync::contains_grid_sync(&program),
"contains_grid_sync must recurse into loop bodies; a nested fence that reads as absent \
would route this program down the non-grid path with no fence and no error"
);
let segments = vyre_driver::grid_sync::split_on_grid_sync(&program);
assert_eq!(
segments.len(),
1,
"a fence nested in a loop is not a dispatch-level split point, so the program must stay \
one segment"
);
assert_eq!(
segments
.iter()
.map(|segment| grid_sync_fences(segment.entry()))
.sum::<usize>(),
1,
"the nested fence must survive the split; a splitter that swallowed it would produce a \
program that emits clean and runs unsynchronized"
);
}
#[test]
fn a_dispatch_level_grid_sync_fence_becomes_a_launch_boundary() {
let program = Program::wrapped(
vec![BufferDecl::read_write("state", 0, DataType::U32).with_count(256)],
[256, 1, 1],
vec![Node::Region {
generator: Ident::from("grid-sync-nested-fence-probe"),
source_region: None,
body: Arc::new(vec![
Node::store("state", Expr::gid_x(), Expr::u32(1)),
Node::barrier_with_ordering(MemoryOrdering::GridSync),
Node::store("state", Expr::gid_x(), Expr::u32(2)),
]),
}],
);
let segments = vyre_driver::grid_sync::split_on_grid_sync(&program);
assert_eq!(
segments.len(),
2,
"a dispatch-level fence must split the program into two launch segments"
);
assert_eq!(
segments
.iter()
.map(|segment| grid_sync_fences(segment.entry()))
.sum::<usize>(),
0,
"the launch boundary replaces the fence, so no segment may still carry it"
);
}