weirflow 0.1.0

GPU-first dataflow analysis primitives for Vyre and Santh compiler pipelines.
Documentation
use super::*;

#[test]
fn live_step_emits_program() {
    let shape = ProgramGraphShape::new(64, 64);
    let p = live_step(shape, "fin", "fout");
    assert!(
        !p.entry().is_empty(),
        "weir::live: backward-step Program body must not be empty"
    );
}

#[test]
fn live_step_is_deterministic() {
    let shape = ProgramGraphShape::new(64, 64);
    let p1 = live_step(shape, "fin", "fout");
    let p2 = live_step(shape, "fin", "fout");
    assert_eq!(
        p1.entry().len(),
        p2.entry().len(),
        "weir::live: live_step must be deterministic"
    );
}

#[test]
fn live_soundness_is_exact() {
    use super::super::soundness::SoundnessTagged;
    assert_eq!(
        Liveness.soundness(),
        super::super::soundness::Soundness::Exact,
        "weir::live: backward dataflow over reversed CFG is Exact"
    );
}

#[test]
fn live_reverse_csr_rejects_orphan_prefix_edges() {
    let err = live_reverse_control_csr(
        2,
        &[1, 1, 1],
        &[0],
        &[vyre_primitives::predicate::edge_kind::CONTROL],
    )
    .expect_err("nonzero first CSR offset must be rejected");
    assert!(
        err.contains("edge_offsets[0]"),
        "unexpected diagnostic: {err}"
    );
}

#[test]
fn live_closure_rejects_out_of_domain_seed_tail_bits() {
    let err = live_closure_borrowed_via(
        &|_, _, _| unreachable!("validation must reject before dispatch"),
        3,
        &[0, 0, 0, 0],
        &[],
        &[],
        &[0b1000],
        1,
    )
    .expect_err("tail bit outside node_count must be rejected");
    assert!(
        err.contains("outside the declared domain"),
        "unexpected diagnostic: {err}"
    );
}

#[test]
fn live_closure_into_reuses_output_storage_across_iterations() {
    let calls = std::cell::Cell::new(0_u32);
    let saw_reused_slot = std::cell::Cell::new(false);
    let dispatch =
        |_: &Program, inputs: &[&[u8]], _: Option<[u32; 3]>, outputs: &mut Vec<Vec<u8>>| {
            if inputs.len() < 6 {
                return crate::fixed_point_closure::fallback_bitset_equal_dispatch(inputs, outputs);
            }
            let call = calls.get();
            if call > 0 && outputs.len() == 1 {
                saw_reused_slot.set(true);
            }
            calls.set(call + 1);
            let frontier = u32::from_le_bytes(inputs[5][..4].try_into().unwrap());
            let next = if frontier & 0b10 != 0 {
                frontier | 0b01
            } else {
                frontier
            };
            if outputs.is_empty() {
                outputs.push(Vec::new());
            }
            outputs[0].clear();
            outputs[0].extend_from_slice(&next.to_le_bytes());
            Ok(())
        };

    let result = live_closure_borrowed_into_via(
        &dispatch,
        2,
        &[0, 1, 1],
        &[1],
        &[vyre_primitives::predicate::edge_kind::CONTROL],
        &[0b10],
        4,
    )
    .expect("live closure into dispatch must converge");

    assert_eq!(result, vec![0b11]);
    assert_eq!(calls.get(), 2);
    assert!(saw_reused_slot.get());
}

#[test]
fn live_closure_with_scratch_reuses_reverse_csr_buffers() {
    let dispatch =
        |_: &Program, inputs: &[&[u8]], _: Option<[u32; 3]>, outputs: &mut Vec<Vec<u8>>| {
            if inputs.len() < 6 {
                return crate::fixed_point_closure::fallback_bitset_equal_dispatch(inputs, outputs);
            }
            if outputs.is_empty() {
                outputs.push(Vec::new());
            }
            outputs[0].clear();
            outputs[0].extend_from_slice(inputs[5]);
            Ok(())
        };
    let mut scratch = crate::fixed_point_scratch::FixedPointScratch::default();

    live_closure_borrowed_into_with_scratch_via(
        &dispatch,
        3,
        &[0, 1, 2, 2],
        &[1, 2],
        &[
            vyre_primitives::predicate::edge_kind::CONTROL,
            vyre_primitives::predicate::edge_kind::CONTROL,
        ],
        &[0b100],
        1,
        &mut scratch,
    )
    .expect("first live closure must converge");
    let offset_ptr = scratch.reverse_offsets.as_ptr() as usize;
    let target_ptr = scratch.reverse_targets.as_ptr() as usize;
    let offset_capacity = scratch.reverse_offset_capacity();
    let target_capacity = scratch.reverse_target_capacity();

    live_closure_borrowed_into_with_scratch_via(
        &dispatch,
        3,
        &[0, 1, 2, 2],
        &[1, 2],
        &[
            vyre_primitives::predicate::edge_kind::CONTROL,
            vyre_primitives::predicate::edge_kind::CONTROL,
        ],
        &[0b100],
        1,
        &mut scratch,
    )
    .expect("second live closure must converge");

    assert_eq!(scratch.reverse_offsets.as_ptr() as usize, offset_ptr);
    assert_eq!(scratch.reverse_targets.as_ptr() as usize, target_ptr);
    assert!(scratch.reverse_offset_capacity() >= offset_capacity);
    assert!(scratch.reverse_target_capacity() >= target_capacity);
}