weirflow 0.1.0

GPU-first dataflow analysis primitives for Vyre and Santh compiler pipelines.
Documentation
#![allow(clippy::too_many_arguments)]

use super::sizing::graph_hash;

/// Opaque identity for a stable direct-resident IFDS graph.
///
/// Build this once for an invariant graph, then pass it to
/// [`DirectResidentIfdsBatch::solve_many_with_graph_key`] or
/// [`DirectResidentIfdsBatch::solve_with_graph_key`] to avoid re-hashing every
/// edge family at the call site. The keyed solve path still checks the
/// graph dimensions, edge-family lengths, and edge-family hash before touching resident cache
/// state.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct DirectResidentIfdsGraphKey {
    pub(crate) num_procs: u32,
    pub(crate) blocks_per_proc: u32,
    pub(crate) facts_per_proc: u32,
    pub(crate) intra_len: usize,
    pub(crate) inter_len: usize,
    pub(crate) gen_len: usize,
    pub(crate) kill_len: usize,
    pub(crate) graph_hash: u64,
}

/// Borrowed direct-resident IFDS graph identity plus edge families.
///
/// This is the repeated-solve hot-path handle: construction hashes invariant
/// edge families once, then the borrow prevents callers from mutating those
/// slices while repeated solves use the cached identity.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct DirectResidentIfdsGraph<'a> {
    pub(crate) key: DirectResidentIfdsGraphKey,
    pub(crate) intra_edges: &'a [(u32, u32, u32)],
    pub(crate) inter_edges: &'a [(u32, u32, u32, u32)],
    pub(crate) flow_gen: &'a [(u32, u32, u32)],
    pub(crate) flow_kill: &'a [(u32, u32, u32)],
}

impl DirectResidentIfdsGraphKey {
    /// Build a stable graph key from IFDS dimensions and invariant edge
    /// families.
    #[must_use]
    pub fn from_edges(
        num_procs: u32,
        blocks_per_proc: u32,
        facts_per_proc: u32,
        intra_edges: &[(u32, u32, u32)],
        inter_edges: &[(u32, u32, u32, u32)],
        flow_gen: &[(u32, u32, u32)],
        flow_kill: &[(u32, u32, u32)],
    ) -> Self {
        Self {
            num_procs,
            blocks_per_proc,
            facts_per_proc,
            intra_len: intra_edges.len(),
            inter_len: inter_edges.len(),
            gen_len: flow_gen.len(),
            kill_len: flow_kill.len(),
            graph_hash: graph_hash(intra_edges, inter_edges, flow_gen, flow_kill),
        }
    }

    pub(crate) fn validate_edges(
        self,
        num_procs: u32,
        blocks_per_proc: u32,
        facts_per_proc: u32,
        intra_edges: &[(u32, u32, u32)],
        inter_edges: &[(u32, u32, u32, u32)],
        flow_gen: &[(u32, u32, u32)],
        flow_kill: &[(u32, u32, u32)],
    ) -> Result<(), String> {
        if self.num_procs != num_procs
            || self.blocks_per_proc != blocks_per_proc
            || self.facts_per_proc != facts_per_proc
            || self.intra_len != intra_edges.len()
            || self.inter_len != inter_edges.len()
            || self.gen_len != flow_gen.len()
            || self.kill_len != flow_kill.len()
        {
            return Err(
                "weir direct resident IFDS graph key does not match supplied graph dimensions or edge-family lengths. Fix: rebuild DirectResidentIfdsGraphKey after changing the IFDS graph."
                    .to_string(),
            );
        }
        let supplied_hash = graph_hash(intra_edges, inter_edges, flow_gen, flow_kill);
        if self.graph_hash != supplied_hash {
            return Err(
                "weir direct resident IFDS graph key does not match supplied edge contents. Fix: rebuild DirectResidentIfdsGraphKey after changing any IFDS edge family."
                    .to_string(),
            );
        }
        Ok(())
    }
}

impl<'a> DirectResidentIfdsGraph<'a> {
    /// Build a borrowed graph view and stable key from IFDS dimensions and
    /// invariant edge families.
    #[must_use]
    pub fn from_edges(
        num_procs: u32,
        blocks_per_proc: u32,
        facts_per_proc: u32,
        intra_edges: &'a [(u32, u32, u32)],
        inter_edges: &'a [(u32, u32, u32, u32)],
        flow_gen: &'a [(u32, u32, u32)],
        flow_kill: &'a [(u32, u32, u32)],
    ) -> Self {
        Self {
            key: DirectResidentIfdsGraphKey::from_edges(
                num_procs,
                blocks_per_proc,
                facts_per_proc,
                intra_edges,
                inter_edges,
                flow_gen,
                flow_kill,
            ),
            intra_edges,
            inter_edges,
            flow_gen,
            flow_kill,
        }
    }

    /// Return the stable direct-resident graph cache key for this borrowed
    /// graph view.
    #[must_use]
    pub fn key(self) -> DirectResidentIfdsGraphKey {
        self.key
    }
}