weirflow 0.1.0

GPU-first dataflow analysis primitives for Vyre and Santh compiler pipelines.
Documentation
#[allow(clippy::type_complexity)]
pub(super) fn live_reverse_control_csr(
    node_count: usize,
    edge_offsets: &[u32],
    edge_targets: &[u32],
    edge_kind_mask: &[u32],
) -> Result<(Vec<u32>, Vec<u32>, Vec<u32>), String> {
    let mut scratch = crate::fixed_point_scratch::FixedPointScratch::default();
    live_reverse_control_csr_into(
        node_count,
        edge_offsets,
        edge_targets,
        edge_kind_mask,
        &mut scratch,
    )?;
    Ok((
        std::mem::take(&mut scratch.reverse_offsets),
        std::mem::take(&mut scratch.reverse_targets),
        std::mem::take(&mut scratch.reverse_masks),
    ))
}

pub(super) fn live_reverse_control_csr_into(
    node_count: usize,
    edge_offsets: &[u32],
    edge_targets: &[u32],
    edge_kind_mask: &[u32],
    scratch: &mut crate::fixed_point_scratch::FixedPointScratch,
) -> Result<(), String> {
    let expected_offsets = node_count.checked_add(1).ok_or_else(|| {
        "live_closure CSR node_count overflows usize when adding sentinel offset".to_string()
    })?;
    if edge_offsets.len() != expected_offsets {
        return Err(format!(
            "live_closure CSR edge_offsets has {} entries, expected exactly node_count + 1 = {expected_offsets}. Fix: pass a complete CSR offset table.",
            edge_offsets.len()
        ));
    }
    let edge_count = live_u32_to_usize(edge_offsets[node_count], "final CSR offset")?;
    if edge_targets.len() != edge_count || edge_kind_mask.len() != edge_count {
        return Err(format!(
            "live_closure CSR declares {edge_count} edges but edge_targets has {} and edge_kind_mask has {}. Fix: pass edge arrays matching the final offset exactly.",
            edge_targets.len(),
            edge_kind_mask.len()
        ));
    }
    scratch.prepare_reverse_csr_scratch(node_count, edge_count)?;
    if edge_offsets[0] != 0 {
        return Err(format!(
            "live_closure CSR edge_offsets[0] is {}, expected 0. Fix: rebuild CSR offsets from zero; Weir GPU wrappers reject orphan prefix edges.",
            edge_offsets[0]
        ));
    }
    for src in 0..node_count {
        let start = live_u32_to_usize(edge_offsets[src], "row start offset")?;
        let end = live_u32_to_usize(edge_offsets[src + 1], "row end offset")?;
        if start > end {
            return Err(format!(
                "live_closure CSR edge_offsets is not monotonic at node {src}: start={start}, end={end}. Fix: sort and rebuild CSR offsets."
            ));
        }
        if end > edge_count {
            return Err(format!(
                "live_closure CSR edge_offsets[{}]={end} exceeds declared edge count {edge_count}. Fix: rebuild CSR offsets from the edge arrays.",
                src + 1
            ));
        }
        for edge in start..end {
            if edge_kind_mask[edge] & vyre_primitives::predicate::edge_kind::CONTROL == 0 {
                continue;
            }
            let dst = edge_targets[edge];
            let dst = live_u32_to_usize(dst, "edge target")?;
            if dst >= node_count {
                return Err(format!(
                    "live_closure CSR edge {edge} targets node {dst}, but node_count is {node_count}. Fix: reject or remap malformed graph edges before dispatch."
                ));
            }
            scratch.reverse_indegree[dst] = scratch.reverse_indegree[dst].checked_add(1).ok_or_else(|| {
                format!(
                    "live_closure reverse indegree overflowed u32 for node {dst}. Fix: shard the graph before live-closure dispatch."
                )
            })?;
        }
    }
    for node in 0..node_count {
        let indegree = live_usize_to_u32(scratch.reverse_indegree[node], "reverse indegree")?;
        scratch.reverse_offsets[node + 1] =
            scratch.reverse_offsets[node].checked_add(indegree).ok_or_else(|| {
                format!(
                    "live_closure reverse offset overflowed u32 at node {node}. Fix: shard the graph before live-closure dispatch."
                )
            })?;
    }
    for (dst, src) in scratch
        .reverse_cursor
        .iter_mut()
        .zip(scratch.reverse_offsets.iter().copied())
    {
        *dst = live_u32_to_usize(src, "reverse cursor")?;
    }
    let edge_total = live_u32_to_usize(scratch.reverse_offsets[node_count], "reverse edge count")?;
    crate::dispatch_decode::try_write_zero_words(
        &mut scratch.reverse_targets,
        edge_total,
        "live reverse CSR target",
    )?;
    crate::dispatch_decode::try_write_zero_words(
        &mut scratch.reverse_masks,
        edge_total,
        "live reverse CSR mask",
    )?;
    for src in 0..node_count {
        let start = live_u32_to_usize(edge_offsets[src], "row start offset")?;
        let end = live_u32_to_usize(edge_offsets[src + 1], "row end offset")?;
        for edge in start..end {
            if edge_kind_mask[edge] & vyre_primitives::predicate::edge_kind::CONTROL == 0 {
                continue;
            }
            let dst = edge_targets[edge];
            let dst = live_u32_to_usize(dst, "edge target")?;
            let out = scratch.reverse_cursor[dst];
            if out >= scratch.reverse_targets.len() {
                return Err(format!(
                    "live_closure reverse cursor for node {dst} points to {out}, outside {} reverse target slots. Fix: rebuild reverse CSR offsets before dispatch.",
                    scratch.reverse_targets.len()
                ));
            }
            scratch.reverse_cursor[dst] = scratch.reverse_cursor[dst].checked_add(1).ok_or_else(|| {
                format!(
                    "live_closure reverse cursor overflowed usize for node {dst}. Fix: shard the graph before live-closure dispatch."
                )
            })?;
            scratch.reverse_targets[out] = u32::try_from(src).map_err(|error| {
                format!(
                    "live_closure source node {src} cannot fit u32: {error}. Fix: shard the graph before live-closure dispatch."
                )
            })?;
            scratch.reverse_masks[out] = vyre_primitives::predicate::edge_kind::CONTROL;
        }
    }
    if scratch.reverse_targets.is_empty() {
        if let Some(last_offset) = scratch.reverse_offsets.last_mut() {
            *last_offset = 1;
        }
        scratch.reverse_targets.push(0);
        scratch.reverse_masks.push(0);
    }
    Ok(())
}

fn live_u32_to_usize(value: u32, label: &'static str) -> Result<usize, String> {
    usize::try_from(value).map_err(|error| {
        format!(
            "live_closure {label} value {value} cannot fit usize: {error}. Fix: shard the graph before live-closure dispatch."
        )
    })
}

fn live_usize_to_u32(value: usize, label: &'static str) -> Result<u32, String> {
    u32::try_from(value).map_err(|error| {
        format!(
            "live_closure {label} value {value} cannot fit u32: {error}. Fix: shard the graph before live-closure dispatch."
        )
    })
}