weirflow 0.1.0

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

#[test]
fn fixed_point_batch_reuses_prepared_graph_buffers() {
    let dispatch = |_: &vyre::ir::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 frontier = u32::from_le_bytes(inputs[5][..4].try_into().unwrap());
        let next = frontier | 0b11;
        if outputs.is_empty() {
            outputs.push(Vec::new());
        }
        outputs[0].clear();
        outputs[0].extend_from_slice(&next.to_le_bytes());
        Ok(())
    };
    let graph = CsrGraph::new(
        2,
        &[0, 1, 1],
        &[1],
        &[vyre_primitives::predicate::edge_kind::CONTROL],
    );
    let mut batch = FixedPointBatch::new(&dispatch);
    let prepared = batch
        .prepare_reaching(graph)
        .expect("batch must prepare reaching graph once");
    let retained = prepared.retained_bytes();
    let mut into = Vec::with_capacity(1);
    let into_ptr = into.as_ptr();

    let first = batch
        .reaching_prepared(&prepared, &[0b01], 4)
        .expect("first prepared reaching run must converge");
    batch
        .reaching_prepared_into(&prepared, &[0b01], 4, &mut into)
        .expect("prepared reaching_into run must converge");
    let second = batch
        .reaching_prepared(&prepared, &[0b10], 4)
        .expect("second prepared reaching run must converge");

    assert_eq!(first, vec![0b11]);
    assert_eq!(into, vec![0b11]);
    assert_eq!(
        into.as_ptr(),
        into_ptr,
        "reaching_prepared_into must reuse caller result allocation"
    );
    assert_eq!(second, vec![0b11]);
    assert_eq!(prepared.retained_bytes(), retained);
}

#[test]
fn fixed_point_batch_repacks_prepared_graph_in_place() {
    let dispatch = |_: &vyre::ir::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);
        }
        outputs.clear();
        outputs.push(inputs[5].to_vec());
        Ok(())
    };
    let first_graph = CsrGraph::new(
        4,
        &[0, 2, 3, 3, 3],
        &[1, 2, 3],
        &[
            vyre_primitives::predicate::edge_kind::CONTROL,
            vyre_primitives::predicate::edge_kind::ASSIGNMENT,
            vyre_primitives::predicate::edge_kind::CONTROL,
        ],
    );
    let second_graph = CsrGraph::new(
        4,
        &[0, 1, 1, 2, 2],
        &[2, 3],
        &[
            vyre_primitives::predicate::edge_kind::CONTROL,
            vyre_primitives::predicate::edge_kind::CONTROL,
        ],
    );
    let mut batch = FixedPointBatch::new(&dispatch);
    let mut prepared = batch
        .prepare_reaching(first_graph)
        .expect("initial reaching graph must prepare");
    let pg_nodes_ptr = prepared.pg_nodes_bytes.as_ptr() as usize;
    let edge_offsets_ptr = prepared.edge_offsets_bytes.as_ptr() as usize;
    let edge_targets_ptr = prepared.edge_targets_bytes.as_ptr() as usize;
    let edge_kind_ptr = prepared.edge_kind_mask_bytes.as_ptr() as usize;
    let pg_tags_ptr = prepared.pg_node_tags_bytes.as_ptr() as usize;
    let retained = prepared.retained_bytes();
    let mask_scratch_ptr = batch.scratch().edge_kind_masks.as_ptr() as usize;
    let mask_scratch_capacity = batch.scratch().edge_kind_mask_capacity();

    batch
        .prepare_reaching_into(second_graph, &mut prepared)
        .expect("batch must repack reaching graph in place");

    assert_eq!(prepared.node_count(), 4);
    assert_eq!(prepared.edge_count(), 2);
    assert_eq!(prepared.pg_nodes_bytes.as_ptr() as usize, pg_nodes_ptr);
    assert_eq!(
        prepared.edge_offsets_bytes.as_ptr() as usize,
        edge_offsets_ptr
    );
    assert_eq!(
        prepared.edge_targets_bytes.as_ptr() as usize,
        edge_targets_ptr
    );
    assert_eq!(
        prepared.edge_kind_mask_bytes.as_ptr() as usize,
        edge_kind_ptr
    );
    assert_eq!(prepared.pg_node_tags_bytes.as_ptr() as usize, pg_tags_ptr);
    assert_eq!(prepared.retained_bytes(), retained);
    assert_eq!(
        batch.scratch().edge_kind_masks.as_ptr() as usize,
        mask_scratch_ptr
    );
    assert!(batch.scratch().edge_kind_mask_capacity() >= mask_scratch_capacity);
}

#[test]
fn fixed_point_batch_reuses_prepared_live_graph_buffers() {
    let dispatch = |_: &vyre::ir::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 frontier = u32::from_le_bytes(inputs[5][..4].try_into().unwrap());
        let next = frontier | 0b11;
        if outputs.is_empty() {
            outputs.push(Vec::new());
        }
        outputs[0].clear();
        outputs[0].extend_from_slice(&next.to_le_bytes());
        Ok(())
    };
    let graph = CsrGraph::new(
        2,
        &[0, 1, 1],
        &[1],
        &[vyre_primitives::predicate::edge_kind::CONTROL],
    );
    let mut batch = FixedPointBatch::new(&dispatch);
    let prepared = batch
        .prepare_live(graph)
        .expect("batch must prepare live graph once");
    let retained = prepared.retained_bytes();
    let reverse_offsets_ptr = batch.scratch().reverse_offsets.as_ptr() as usize;
    let reverse_targets_ptr = batch.scratch().reverse_targets.as_ptr() as usize;
    let reverse_offsets_capacity = batch.scratch().reverse_offset_capacity();
    let reverse_targets_capacity = batch.scratch().reverse_target_capacity();
    let mut into = Vec::with_capacity(1);
    let into_ptr = into.as_ptr();

    let first = batch
        .live_prepared(&prepared, &[0b10], 4)
        .expect("first prepared live run must converge");
    batch
        .live_prepared_into(&prepared, &[0b10], 4, &mut into)
        .expect("prepared live_into run must converge");
    let second = batch
        .live_prepared(&prepared, &[0b01], 4)
        .expect("second prepared live run must converge");

    assert_eq!(first, vec![0b11]);
    assert_eq!(into, vec![0b11]);
    assert_eq!(
        into.as_ptr(),
        into_ptr,
        "live_prepared_into must reuse caller result allocation"
    );
    assert_eq!(second, vec![0b11]);
    assert_eq!(prepared.retained_bytes(), retained);
    assert_eq!(
        batch.scratch().reverse_offsets.as_ptr() as usize,
        reverse_offsets_ptr
    );
    assert_eq!(
        batch.scratch().reverse_targets.as_ptr() as usize,
        reverse_targets_ptr
    );
    assert!(batch.scratch().reverse_offset_capacity() >= reverse_offsets_capacity);
    assert!(batch.scratch().reverse_target_capacity() >= reverse_targets_capacity);
}