weirflow 0.1.0

GPU-first dataflow analysis primitives for Vyre and Santh compiler pipelines.
Documentation
pub(crate) fn subset_closure_resident_plan_with_scratch(
    pipeline: &dyn vyre::CompiledPipeline,
    graph: &crate::fixed_point_resident::FixedPointResidentGraph,
    seed_bits: &[u32],
    max_iterations: u32,
    config: &vyre::DispatchConfig,
    scratch: &mut crate::fixed_point_scratch::FixedPointScratch,
) -> Result<Vec<u32>, String> {
    let mut result = crate::staging_reserve::reserved_vec(
        crate::dispatch_decode::bitset_word_capacity(
            "points_to subset_closure resident result",
            graph.node_count(),
        )?,
        "fixed-point result word",
    )?;
    subset_closure_resident_plan_with_scratch_into(
        pipeline,
        graph,
        seed_bits,
        max_iterations,
        config,
        scratch,
        &mut result,
    )?;
    Ok(result)
}

pub(crate) fn subset_closure_resident_plan_with_scratch_into(
    pipeline: &dyn vyre::CompiledPipeline,
    graph: &crate::fixed_point_resident::FixedPointResidentGraph,
    seed_bits: &[u32],
    max_iterations: u32,
    config: &vyre::DispatchConfig,
    scratch: &mut crate::fixed_point_scratch::FixedPointScratch,
    result: &mut Vec<u32>,
) -> Result<(), String> {
    graph.require_kind(
        crate::fixed_point_graph::FixedPointAnalysisKind::PointsToSubset,
        "subset_closure_resident_plan_with_scratch_into",
    )?;
    crate::dispatch_decode::require_positive_iterations(
        "points_to subset_closure_via",
        max_iterations,
    )?;
    let words = crate::dispatch_decode::bitset_word_capacity(
        "points_to subset_closure resident",
        graph.node_count(),
    )?;
    crate::dispatch_decode::require_bitset_words("points_to subset_closure_via", seed_bits, words)?;
    crate::dispatch_decode::require_bitset_tail_clear(
        "points_to subset_closure_via",
        seed_bits,
        graph.node_count(),
    )?;
    scratch.prepare_frontier_words(words, seed_bits)?;
    scratch.begin_frontier_density(graph.node_count());
    scratch.prepare_grid_dispatch_config(config, [graph.node_count().max(1), 1, 1]);
    scratch.prepare_next_words(words)?;
    for _ in 0..max_iterations {
        crate::dispatch_decode::try_pack_u32_into(&scratch.frontier_words, &mut scratch.frontier_bytes, "weir u32 byte staging")?;
        graph.resources_with_frontier_into(&scratch.frontier_bytes, &mut scratch.resources)?;
        pipeline
            .dispatch_persistent_handles_into(
                &scratch.resources,
                &scratch.dispatch_config,
                &mut scratch.outputs,
            )
            .map_err(|error| error.to_string())?;
        crate::dispatch_decode::unpack_only_exact_u32_into(
            &scratch.outputs,
            "points_to subset_closure_via",
            "points-to frontier",
            words,
            &mut scratch.next,
        )?;
        crate::dispatch_decode::require_bitset_tail_clear(
            "points_to subset_closure_via output",
            &scratch.next,
            graph.node_count(),
        )?;
        scratch.record_decoded_frontier_transition();
        if crate::fixed_point_scratch::copy_if_converged(
            &scratch.next,
            &scratch.frontier_words,
            result,
        )? {
            return Ok(());
        }
        std::mem::swap(&mut scratch.frontier_words, &mut scratch.next);
    }
    Err(format!(
        "points_to subset_closure_via did not converge within {max_iterations} iterations"
    ))
}

pub fn subset_closure_resident_plan_with_backend_scratch(
    backend: &dyn vyre::VyreBackend,
    pipeline: &dyn vyre::CompiledPipeline,
    graph: &crate::fixed_point_resident::FixedPointResidentGraph,
    seed_bits: &[u32],
    max_iterations: u32,
    config: &vyre::DispatchConfig,
    scratch: &mut crate::fixed_point_scratch::FixedPointScratch,
) -> Result<Vec<u32>, String> {
    let mut result = crate::staging_reserve::reserved_vec(
        crate::dispatch_decode::bitset_word_capacity(
            "points_to subset_closure resident backend result",
            graph.node_count(),
        )?,
        "fixed-point result word",
    )?;
    subset_closure_resident_plan_with_backend_scratch_into(
        backend,
        pipeline,
        graph,
        seed_bits,
        max_iterations,
        config,
        scratch,
        &mut result,
    )?;
    Ok(result)
}

pub fn subset_closure_resident_plan_with_backend_scratch_into(
    backend: &dyn vyre::VyreBackend,
    pipeline: &dyn vyre::CompiledPipeline,
    graph: &crate::fixed_point_resident::FixedPointResidentGraph,
    seed_bits: &[u32],
    max_iterations: u32,
    config: &vyre::DispatchConfig,
    scratch: &mut crate::fixed_point_scratch::FixedPointScratch,
    result: &mut Vec<u32>,
) -> Result<(), String> {
    graph.require_kind(
        crate::fixed_point_graph::FixedPointAnalysisKind::PointsToSubset,
        "subset_closure_resident_plan_with_backend_scratch_into",
    )?;
    crate::dispatch_decode::require_positive_iterations(
        "points_to subset_closure_via",
        max_iterations,
    )?;
    let words = crate::dispatch_decode::bitset_word_capacity(
        "points_to subset_closure resident",
        graph.node_count(),
    )?;
    crate::dispatch_decode::require_bitset_words("points_to subset_closure_via", seed_bits, words)?;
    crate::dispatch_decode::require_bitset_tail_clear(
        "points_to subset_closure_via",
        seed_bits,
        graph.node_count(),
    )?;
    scratch.prepare_frontier_words(words, seed_bits)?;
    let frontier_byte_len = words.checked_mul(4).ok_or_else(|| {
        "points_to subset_closure_via resident frontier byte length overflow. Fix: shard the graph before GPU dispatch.".to_string()
    })?;
    let frontier_in = backend
        .allocate_resident(frontier_byte_len)
        .map_err(|error| error.to_string())?;
    let frontier_out = match backend.allocate_resident(frontier_byte_len) {
        Ok(resource) => resource,
        Err(error) => {
            let _ = backend.free_resident(frontier_in);
            return Err(error.to_string());
        }
    };
    let run_result = subset_closure_resident_plan_with_frontier_resources_into(
        backend,
        pipeline,
        graph,
        &frontier_in,
        &frontier_out,
        max_iterations,
        config,
        scratch,
        result,
    );
    let free_in = backend
        .free_resident(frontier_in)
        .map_err(|error| error.to_string());
    let free_out = backend
        .free_resident(frontier_out)
        .map_err(|error| error.to_string());
    match (run_result, free_in, free_out) {
        (Err(error), _, _) => Err(error),
        (Ok(_), Err(error), _) | (Ok(_), _, Err(error)) => Err(error),
        (Ok(()), Ok(()), Ok(())) => Ok(()),
    }
}

pub fn subset_closure_resident_plan_with_reusable_frontier_scratch(
    backend: &dyn vyre::VyreBackend,
    pipeline: &dyn vyre::CompiledPipeline,
    graph: &crate::fixed_point_resident::FixedPointResidentGraph,
    seed_bits: &[u32],
    max_iterations: u32,
    config: &vyre::DispatchConfig,
    scratch: &mut crate::fixed_point_scratch::FixedPointScratch,
    resident_frontier: &mut crate::fixed_point_resident::FixedPointResidentFrontierScratch,
) -> Result<Vec<u32>, String> {
    let mut result = crate::staging_reserve::reserved_vec(
        crate::dispatch_decode::bitset_word_capacity(
            "points_to subset_closure resident reusable result",
            graph.node_count(),
        )?,
        "fixed-point result word",
    )?;
    subset_closure_resident_plan_with_reusable_frontier_scratch_into(
        backend,
        pipeline,
        graph,
        seed_bits,
        max_iterations,
        config,
        scratch,
        resident_frontier,
        &mut result,
    )?;
    Ok(result)
}

pub fn subset_closure_resident_plan_with_reusable_frontier_scratch_sequence_window(
    backend: &dyn vyre::VyreBackend,
    program: &vyre::ir::Program,
    graph: &crate::fixed_point_resident::FixedPointResidentGraph,
    seed_bits: &[u32],
    max_iterations: u32,
    scratch: &mut crate::fixed_point_scratch::FixedPointScratch,
    resident_frontier: &mut crate::fixed_point_resident::FixedPointResidentFrontierScratch,
) -> Result<Vec<u32>, String> {
    let mut result = crate::staging_reserve::reserved_vec(
        crate::dispatch_decode::bitset_word_capacity(
            "points_to subset_closure resident sequence result",
            graph.node_count(),
        )?,
        "fixed-point result word",
    )?;
    subset_closure_resident_plan_with_reusable_frontier_scratch_sequence_window_into(
        backend,
        program,
        graph,
        seed_bits,
        max_iterations,
        scratch,
        resident_frontier,
        &mut result,
    )?;
    Ok(result)
}

pub fn subset_closure_resident_plan_with_reusable_frontier_scratch_into(
    backend: &dyn vyre::VyreBackend,
    pipeline: &dyn vyre::CompiledPipeline,
    graph: &crate::fixed_point_resident::FixedPointResidentGraph,
    seed_bits: &[u32],
    max_iterations: u32,
    config: &vyre::DispatchConfig,
    scratch: &mut crate::fixed_point_scratch::FixedPointScratch,
    resident_frontier: &mut crate::fixed_point_resident::FixedPointResidentFrontierScratch,
    result: &mut Vec<u32>,
) -> Result<(), String> {
    graph.require_kind(
        crate::fixed_point_graph::FixedPointAnalysisKind::PointsToSubset,
        "subset_closure_resident_plan_with_reusable_frontier_scratch_into",
    )?;
    crate::dispatch_decode::require_positive_iterations(
        "points_to subset_closure_via",
        max_iterations,
    )?;
    let words = crate::dispatch_decode::bitset_word_capacity(
        "points_to subset_closure resident",
        graph.node_count(),
    )?;
    crate::dispatch_decode::require_bitset_words("points_to subset_closure_via", seed_bits, words)?;
    crate::dispatch_decode::require_bitset_tail_clear(
        "points_to subset_closure_via",
        seed_bits,
        graph.node_count(),
    )?;
    scratch.prepare_frontier_words(words, seed_bits)?;
    let frontier_byte_len = words.checked_mul(4).ok_or_else(|| {
        "points_to subset_closure_via resident frontier byte length overflow. Fix: shard the graph before GPU dispatch.".to_string()
    })?;
    let (frontier_in, frontier_out) =
        resident_frontier.ensure_pair_refs(backend, frontier_byte_len)?;
    subset_closure_resident_plan_with_frontier_resources_into(
        backend,
        pipeline,
        graph,
        frontier_in,
        frontier_out,
        max_iterations,
        config,
        scratch,
        result,
    )
}

pub fn subset_closure_resident_plan_with_reusable_frontier_scratch_sequence_window_into(
    backend: &dyn vyre::VyreBackend,
    program: &vyre::ir::Program,
    graph: &crate::fixed_point_resident::FixedPointResidentGraph,
    seed_bits: &[u32],
    max_iterations: u32,
    scratch: &mut crate::fixed_point_scratch::FixedPointScratch,
    resident_frontier: &mut crate::fixed_point_resident::FixedPointResidentFrontierScratch,
    result: &mut Vec<u32>,
) -> Result<(), String> {
    crate::fixed_point_resident_sequence::run_resident_sequence_window_into(
        backend,
        program,
        graph,
        seed_bits,
        max_iterations,
        scratch,
        resident_frontier,
        result,
        crate::fixed_point_graph::FixedPointAnalysisKind::PointsToSubset,
        "points_to subset_closure_via sequence-window",
    )
}

fn subset_closure_resident_plan_with_frontier_resources_into(
    backend: &dyn vyre::VyreBackend,
    pipeline: &dyn vyre::CompiledPipeline,
    graph: &crate::fixed_point_resident::FixedPointResidentGraph,
    frontier_in: &vyre::backend::Resource,
    frontier_out: &vyre::backend::Resource,
    max_iterations: u32,
    config: &vyre::DispatchConfig,
    scratch: &mut crate::fixed_point_scratch::FixedPointScratch,
    result: &mut Vec<u32>,
) -> Result<(), String> {
    let words = scratch.frontier_words.len();
    scratch.begin_frontier_density(graph.node_count());
    scratch.prepare_grid_dispatch_config(config, [graph.node_count().max(1), 1, 1]);
    scratch.prepare_next_words(words)?;
    crate::dispatch_decode::try_pack_u32_into(&scratch.frontier_words, &mut scratch.frontier_bytes, "weir u32 byte staging")?;
    backend
        .upload_resident_many(&[
            (frontier_in, scratch.frontier_bytes.as_slice()),
            (frontier_out, scratch.frontier_bytes.as_slice()),
        ])
        .map_err(|error| error.to_string())?;
    let mut current_frontier = frontier_in;
    let mut next_frontier = frontier_out;
    for _ in 0..max_iterations {
        graph.resources_with_resident_frontier_into(
            current_frontier,
            next_frontier,
            &mut scratch.resources,
        )?;
        pipeline
            .dispatch_persistent_handles_into(
                &scratch.resources,
                &scratch.dispatch_config,
                &mut scratch.outputs,
            )
            .map_err(|error| error.to_string())?;
        crate::dispatch_decode::unpack_only_exact_u32_into(
            &scratch.outputs,
            "points_to subset_closure_via",
            "points-to frontier",
            words,
            &mut scratch.next,
        )?;
        crate::dispatch_decode::require_bitset_tail_clear(
            "points_to subset_closure_via output",
            &scratch.next,
            graph.node_count(),
        )?;
        scratch.record_decoded_frontier_transition();
        if crate::fixed_point_scratch::copy_if_converged(
            &scratch.next,
            &scratch.frontier_words,
            result,
        )? {
            return Ok(());
        }
        std::mem::swap(&mut scratch.frontier_words, &mut scratch.next);
        std::mem::swap(&mut current_frontier, &mut next_frontier);
    }
    Err(format!(
        "points_to subset_closure_via did not converge within {max_iterations} iterations"
    ))
}