use std::sync::Arc;
use vyre::ir::{BufferAccess, BufferDecl, DataType, Expr, Ident, Node, Program};
use vyre::DispatchConfig;
use vyre_driver::grid_sync::dispatch_with_grid_sync_split;
use vyre_driver_cuda::cuda_factory;
use vyre_foundation::execution_plan::fusion::fuse_programs;
const NODE_COUNT: u32 = 65536;
fn arm0_atomic_or() -> Program {
let t = Expr::InvocationId { axis: 0 };
let body = vec![
Node::let_bind("tag_val", Expr::load("tag", t.clone())),
Node::if_then(
Expr::ne(Expr::var("tag_val"), Expr::u32(0)),
vec![
Node::let_bind("word_idx", Expr::shr(t.clone(), Expr::u32(5))),
Node::let_bind(
"bit",
Expr::shl(Expr::u32(1), Expr::bitand(t.clone(), Expr::u32(31))),
),
Node::let_bind(
"_",
Expr::atomic_or("gets", Expr::var("word_idx"), Expr::var("bit")),
),
],
),
];
Program::wrapped(
vec![
BufferDecl::storage("tag", 0, BufferAccess::ReadOnly, DataType::U32)
.with_count(NODE_COUNT),
BufferDecl::storage("gets", 1, BufferAccess::ReadWrite, DataType::U32)
.with_count(NODE_COUNT.div_ceil(32)),
],
[256, 1, 1],
vec![Node::Region {
generator: Ident::from("weir::tests::df::three_arm_fusion::arm0"),
source_region: None,
body: Arc::new(vec![Node::if_then(
Expr::lt(t.clone(), Expr::u32(NODE_COUNT)),
body,
)]),
}],
)
}
fn arm1_bitset_any() -> Program {
let words = NODE_COUNT.div_ceil(32);
let body = vec![
Node::let_bind("acc", Expr::u32(0)),
Node::let_bind("found", Expr::u32(0)),
Node::loop_for(
"w",
Expr::u32(0),
Expr::u32(words),
vec![Node::if_then(
Expr::eq(Expr::var("found"), Expr::u32(0)),
vec![
Node::assign(
"acc",
Expr::bitor(Expr::var("acc"), Expr::load("gets", Expr::var("w"))),
),
Node::if_then(
Expr::ne(Expr::var("acc"), Expr::u32(0)),
vec![Node::assign("found", Expr::u32(1))],
),
],
)],
),
Node::store(
"scalar",
Expr::u32(0),
Expr::select(
Expr::ne(Expr::var("acc"), Expr::u32(0)),
Expr::u32(1),
Expr::u32(0),
),
),
];
Program::wrapped(
vec![
BufferDecl::storage("gets", 0, BufferAccess::ReadOnly, DataType::U32).with_count(words),
BufferDecl::storage("scalar", 1, BufferAccess::ReadWrite, DataType::U32).with_count(1),
],
[1, 1, 1],
vec![Node::Region {
generator: Ident::from("weir::tests::df::three_arm_fusion::arm1"),
source_region: None,
body: Arc::new(vec![Node::if_then(
Expr::eq(Expr::InvocationId { axis: 0 }, Expr::u32(0)),
body,
)]),
}],
)
}
fn arm2_wrapper() -> Program {
let t = Expr::InvocationId { axis: 0 };
let body = vec![
Node::let_bind("gid", t.clone()),
Node::let_bind("scalar_val", Expr::load("scalar", Expr::u32(0))),
Node::let_bind(
"scalar_local",
Expr::select(
Expr::ne(Expr::var("scalar_val"), Expr::u32(0)),
Expr::u32(1),
Expr::u32(0),
),
),
Node::let_bind("word_idx", Expr::shr(Expr::var("gid"), Expr::u32(5))),
Node::let_bind(
"bit_mask",
Expr::shl(Expr::u32(1), Expr::bitand(Expr::var("gid"), Expr::u32(31))),
),
Node::let_bind("anchor_word", Expr::load("gets", Expr::var("word_idx"))),
Node::let_bind(
"anchor_bit",
Expr::bitand(Expr::var("anchor_word"), Expr::var("bit_mask")),
),
Node::store(
"result",
Expr::var("gid"),
Expr::select(
Expr::and(
Expr::ne(Expr::var("anchor_bit"), Expr::u32(0)),
Expr::ne(Expr::var("scalar_local"), Expr::u32(0)),
),
Expr::u32(1),
Expr::u32(0),
),
),
];
Program::wrapped(
vec![
BufferDecl::storage("gets", 0, BufferAccess::ReadOnly, DataType::U32)
.with_count(NODE_COUNT.div_ceil(32)),
BufferDecl::storage("scalar", 1, BufferAccess::ReadOnly, DataType::U32).with_count(1),
BufferDecl::storage("result", 2, BufferAccess::ReadWrite, DataType::U32)
.with_count(NODE_COUNT),
],
[256, 1, 1],
vec![Node::Region {
generator: Ident::from("weir::tests::df::three_arm_fusion::arm2"),
source_region: None,
body: Arc::new(vec![Node::if_then(
Expr::lt(t.clone(), Expr::u32(NODE_COUNT)),
body,
)]),
}],
)
}
#[test]
fn three_arm_fused_rule_fires_at_every_gid() {
let backend = cuda_factory().unwrap_or_else(|error| {
panic!(
"CUDA adapter acquisition failed for three_arm_fusion probe: {error}. \
Fix: repair the CUDA driver/runtime probe; Weir GPU fusion tests must not skip."
)
});
let program = fuse_programs(&[arm0_atomic_or(), arm1_bitset_any(), arm2_wrapper()])
.expect("3-arm fuse cleanly");
let cfg = DispatchConfig::default();
let probe_nodes: &[u32] = &[
0, 1, 24, 30, 31, 32, 33, 39, 42, 52, 70, 100, 200, 255, 256, 257, 300, 511, 512, 513, 768,
1000, 1024, 4096, 65535,
];
for &node_id in probe_nodes {
let mut tag = vec![0u32; NODE_COUNT as usize];
tag[node_id as usize] = 1;
let tag_b: Vec<u8> = vyre_primitives::wire::pack_u32_slice(&tag);
let gets_b: Vec<u8> = vec![0u8; (NODE_COUNT.div_ceil(32) as usize) * 4];
let scalar_b: Vec<u8> = vec![0u8; 4];
let result_b: Vec<u8> = vec![0u8; (NODE_COUNT as usize) * 4];
let inputs: [&[u8]; 4] = [&tag_b, &gets_b, &scalar_b, &result_b];
let outputs = dispatch_with_grid_sync_split(backend.as_ref(), &program, &inputs, &cfg)
.unwrap_or_else(|e| panic!("dispatch failed at node {node_id}: {e}"));
let read_words = |idx: usize| -> Vec<u32> {
vyre_primitives::wire::decode_u32_le_bytes_all(&outputs[idx])
};
let gets = read_words(0);
let scalar = read_words(1);
let result = read_words(2);
let target_word = (node_id / 32) as usize;
let bit = 1u32 << (node_id % 32);
eprintln!(
"node_id={node_id:>5} gets[{target_word}]={:#010x} scalar={} result[{node_id}]={}",
gets[target_word], scalar[0], result[node_id as usize]
);
assert_eq!(
gets[target_word] & bit,
bit,
"arm0 atomic_or missed gid {node_id} (gets[{target_word}]={:#010x})",
gets[target_word]
);
assert_eq!(scalar[0], 1, "arm1 bitset_any missed gid {node_id}");
assert_eq!(
result[node_id as usize],
1,
"arm2 wrapper failed at gid {node_id} block={} \
(gets[{target_word}]={:#010x}, scalar={}). \
Cross-grid sync regression: the fusion barrier between \
arm 1 (gid==0-gated store) and arm 2 (per-gid read) did \
not propagate the write.",
node_id / 256,
gets[target_word],
scalar[0],
);
}
}