weirflow 0.1.0

GPU-first dataflow analysis primitives for Vyre and Santh compiler pipelines.
Documentation
use std::sync::Arc;

use super::OP_ID;
use vyre::ir::{BufferAccess, BufferDecl, DataType, Expr, Ident, Node, Program};

/// One widening step. `cfg_in` holds the previous-iteration
/// intervals `[prev_lo_v, prev_hi_v, …]`; `ranges_in` holds the
/// new iteration's intervals; `summary_out` receives the widened
/// result per variable.
#[must_use]
pub fn loop_summarize(cfg_in: &str, ranges_in: &str, summary_out: &str) -> Program {
    loop_summarize_with_slots(cfg_in, ranges_in, summary_out, 4, 8)
}

/// Checked version of [`loop_summarize_with_count`] for production GPU wrappers.
///
/// # Errors
///
/// Returns an actionable construction error when the variable domain cannot
/// fit the `lo/hi` slot layout used by the GPU program.
pub fn try_loop_summarize_with_count(
    cfg_in: &str,
    ranges_in: &str,
    summary_out: &str,
    var_count: u32,
) -> Result<Program, String> {
    let slots = var_count.checked_mul(2).ok_or_else(|| {
        format!(
            "loop_summarize var_count={var_count} overflows u32 buffer slots. Fix: shard loop summarization before building the GPU Program."
        )
    })?.max(1);
    Ok(loop_summarize_with_slots(
        cfg_in,
        ranges_in,
        summary_out,
        var_count,
        slots,
    ))
}

/// Version that takes `var_count` explicitly.
#[must_use]
#[cfg(any(test, feature = "legacy-infallible"))]
pub fn loop_summarize_with_count(
    cfg_in: &str,
    ranges_in: &str,
    summary_out: &str,
    var_count: u32,
) -> Program {
    let slots = var_count.checked_mul(2).unwrap_or_else(|| {
        panic!(
            "loop_summarize var_count={var_count} overflows u32 buffer slots. Fix: call try_loop_summarize_with_count and shard loop summarization before building the GPU Program."
        )
    }).max(1);
    loop_summarize_with_slots(cfg_in, ranges_in, summary_out, var_count, slots)
}

fn loop_summarize_with_slots(
    cfg_in: &str,
    ranges_in: &str,
    summary_out: &str,
    var_count: u32,
    slots: u32,
) -> Program {
    let v = Expr::InvocationId { axis: 0 };
    const NEG_INF: u32 = 0;
    const POS_INF: u32 = u32::MAX;

    let body = vec![
        Node::let_bind("lo_idx", Expr::mul(v.clone(), Expr::u32(2))),
        Node::let_bind("hi_idx", Expr::add(Expr::var("lo_idx"), Expr::u32(1))),
        Node::let_bind("prev_lo", Expr::load(cfg_in, Expr::var("lo_idx"))),
        Node::let_bind("prev_hi", Expr::load(cfg_in, Expr::var("hi_idx"))),
        Node::let_bind("new_lo", Expr::load(ranges_in, Expr::var("lo_idx"))),
        Node::let_bind("new_hi", Expr::load(ranges_in, Expr::var("hi_idx"))),
        // widen lo: if new_lo < prev_lo (lower bound decreasing),
        // jump to -∞ (NEG_INF); else keep prev_lo.
        Node::let_bind(
            "wide_lo",
            Expr::select(
                Expr::lt(Expr::var("new_lo"), Expr::var("prev_lo")),
                Expr::u32(NEG_INF),
                Expr::var("prev_lo"),
            ),
        ),
        // widen hi: if new_hi > prev_hi (upper bound increasing),
        // jump to +∞ (POS_INF); else keep prev_hi.
        Node::let_bind(
            "wide_hi",
            Expr::select(
                Expr::lt(Expr::var("prev_hi"), Expr::var("new_hi")),
                Expr::u32(POS_INF),
                Expr::var("prev_hi"),
            ),
        ),
        Node::store(summary_out, Expr::var("lo_idx"), Expr::var("wide_lo")),
        Node::store(summary_out, Expr::var("hi_idx"), Expr::var("wide_hi")),
    ];

    let buffers = vec![
        BufferDecl::storage(cfg_in, 0, BufferAccess::ReadOnly, DataType::U32).with_count(slots),
        BufferDecl::storage(ranges_in, 1, BufferAccess::ReadOnly, DataType::U32).with_count(slots),
        BufferDecl::storage(summary_out, 2, BufferAccess::ReadWrite, DataType::U32)
            .with_count(slots),
    ];

    Program::wrapped(
        buffers,
        [256, 1, 1],
        vec![Node::Region {
            generator: Ident::from(OP_ID),
            source_region: None,
            body: Arc::new(vec![Node::if_then(
                Expr::lt(v.clone(), Expr::u32(var_count)),
                body,
            )]),
        }],
    )
}