weirflow 0.1.0

GPU-first dataflow analysis primitives for Vyre and Santh compiler pipelines.
Documentation
//! CPU + GPU parity for `weir::may_alias::may_alias`. Soundness:
//! `MayOver`. The Program emits a single-region intersect + atomic-any
//! kernel; the exact bit equation matches the reference oracle, so the parity
//! gate is still byte-exact (one scalar word per dispatch).

use vyre::DispatchConfig;
use vyre_driver_cuda::cuda_factory;
use weir::may_alias;

struct Lcg(u64);
impl Lcg {
    fn new(seed: u64) -> Self {
        Self(seed)
    }
    fn next_u32(&mut self) -> u32 {
        self.0 = self
            .0
            .wrapping_mul(6_364_136_223_846_793_005)
            .wrapping_add(1_442_695_040_888_963_407);
        (self.0 >> 32) as u32
    }
}

#[test]
fn may_alias_cpu_gpu_parity_1000_fuzz_batch() {
    let backend = cuda_factory().expect("CUDA backend unavailable on a GPU-required test host");
    let words: u32 = 256;
    let node_count = words * 32;
    let prog = may_alias::may_alias(node_count, "pts_p", "pts_q", "intersect", "out_scalar");
    let cfg = DispatchConfig::default();
    let mut rng = Lcg::new(0xA11A_5C0F_F0E5);

    for run in 0..1024 {
        // 50% chance of disjoint, 25% identical, 25% sparse  -  exercise both
        // branches of the any-reduce.
        let mode = run % 4;
        let p: Vec<u32> = (0..words).map(|_| rng.next_u32()).collect();
        let q: Vec<u32> = match mode {
            0 => p.iter().map(|w| !w).collect(), // disjoint
            1 => p.clone(),                      // identical
            2 => (0..words).map(|_| rng.next_u32()).collect(),
            _ => (0..words)
                .map(|i| if i == 0 { rng.next_u32() } else { 0 })
                .collect(),
        };
        let p_b: Vec<u8> = vyre_primitives::wire::pack_u32_slice(&p);
        let q_b: Vec<u8> = vyre_primitives::wire::pack_u32_slice(&q);
        let inter_b: Vec<u8> = vec![0u8; (words as usize) * 4];
        let inputs: [&[u8]; 3] = [&p_b, &q_b, &inter_b];

        let outputs = backend
            .dispatch_borrowed(&prog, &inputs, &cfg)
            .unwrap_or_else(|e| panic!("may_alias run {run} dispatch failed: {e}"));
        // Output shape: scratch buffer + scalar buffer. Last buffer is the
        // scalar; first 4 bytes encode the any-reduce result.
        let scalar_bytes = outputs.last().expect("dispatch returned no outputs");
        assert!(
            scalar_bytes.len() >= 4,
            "may_alias run {run}: scalar buffer too short ({} bytes)",
            scalar_bytes.len()
        );
        let actual = u32::from_le_bytes([
            scalar_bytes[0],
            scalar_bytes[1],
            scalar_bytes[2],
            scalar_bytes[3],
        ]);
        let expected = weir::oracle::summary::may_alias(&p, &q);
        assert_eq!(
            actual, expected,
            "may_alias run {run}: GPU={actual} CPU={expected}"
        );
    }
}