weirflow 0.1.0

GPU-first dataflow analysis primitives for Vyre and Santh compiler pipelines.
Documentation
use super::*;
use std::{
    collections::HashSet,
    sync::{
        atomic::{AtomicU64, Ordering},
        Arc, Mutex,
    },
};
use vyre::backend::{private, BackendError, DispatchConfig, OutputBuffers, Resource};

struct FakeResidentBackend {
    next: AtomicU64,
    batch_uploads: AtomicU64,
    uploads: Mutex<Vec<(u64, usize)>>,
    freed: Mutex<Vec<u64>>,
    supported_ops: HashSet<vyre::ir::OpId>,
}

impl FakeResidentBackend {
    fn new() -> Self {
        Self {
            next: AtomicU64::new(1),
            batch_uploads: AtomicU64::new(0),
            uploads: Mutex::new(Vec::new()),
            freed: Mutex::new(Vec::new()),
            supported_ops: HashSet::new(),
        }
    }
}

impl private::Sealed for FakeResidentBackend {}

impl vyre::VyreBackend for FakeResidentBackend {
    fn id(&self) -> &'static str {
        "weir_test_fixed_point_resident_batch"
    }

    fn supported_ops(&self) -> &HashSet<vyre::ir::OpId> {
        &self.supported_ops
    }

    fn dispatch(
        &self,
        _program: &vyre::ir::Program,
        _inputs: &[Vec<u8>],
        _config: &DispatchConfig,
    ) -> Result<Vec<Vec<u8>>, BackendError> {
        unreachable!("resident fixed-point batch tests use persistent resources")
    }

    fn allocate_resident(&self, _byte_len: usize) -> Result<Resource, BackendError> {
        Ok(Resource::Resident(
            self.next.fetch_add(1, Ordering::Relaxed),
        ))
    }

    fn upload_resident(&self, resource: &Resource, bytes: &[u8]) -> Result<(), BackendError> {
        let Resource::Resident(handle) = resource else {
            panic!("fake backend only uploads resident resources");
        };
        self.uploads.lock().unwrap().push((*handle, bytes.len()));
        Ok(())
    }

    fn upload_resident_many(&self, uploads: &[(&Resource, &[u8])]) -> Result<(), BackendError> {
        self.batch_uploads.fetch_add(1, Ordering::Relaxed);
        for &(resource, bytes) in uploads {
            self.upload_resident(resource, bytes)?;
        }
        Ok(())
    }

    fn upload_resident_at_many(
        &self,
        uploads: &[(&Resource, usize, &[u8])],
    ) -> Result<(), BackendError> {
        self.batch_uploads.fetch_add(1, Ordering::Relaxed);
        for &(resource, offset, bytes) in uploads {
            assert_eq!(
                offset, 0,
                "resident fixed-point batch test backend only supports prefix uploads"
            );
            self.upload_resident(resource, bytes)?;
        }
        Ok(())
    }

    fn free_resident(&self, resource: Resource) -> Result<(), BackendError> {
        let Resource::Resident(handle) = resource else {
            panic!("fake backend only frees resident resources");
        };
        self.freed.lock().unwrap().push(handle);
        Ok(())
    }
}

struct FakeResidentPipeline {
    seen_resident_prefix: Mutex<Vec<usize>>,
}

impl FakeResidentPipeline {
    fn new() -> Self {
        Self {
            seen_resident_prefix: Mutex::new(Vec::new()),
        }
    }
}

impl private::Sealed for FakeResidentPipeline {}

impl vyre::CompiledPipeline for FakeResidentPipeline {
    fn id(&self) -> &str {
        "weir_test_fixed_point_resident_batch_pipeline"
    }

    fn dispatch(
        &self,
        _inputs: &[Vec<u8>],
        _config: &DispatchConfig,
    ) -> Result<Vec<Vec<u8>>, BackendError> {
        unreachable!("resident fixed-point batch tests use persistent resources")
    }

    fn dispatch_persistent_handles(
        &self,
        inputs: &[Resource],
        _config: &DispatchConfig,
    ) -> Result<OutputBuffers, BackendError> {
        let resident_prefix = inputs
            .iter()
            .take_while(|resource| matches!(resource, Resource::Resident(_)))
            .count();
        self.seen_resident_prefix
            .lock()
            .unwrap()
            .push(resident_prefix);
        Ok(vec![vec![1, 0, 0, 0]])
    }
}

struct FakeSequenceResidentPipeline {
    seen_resident_prefix: Mutex<Vec<usize>>,
    outputs: Mutex<std::collections::VecDeque<Vec<u8>>>,
}

impl FakeSequenceResidentPipeline {
    fn new(outputs: impl IntoIterator<Item = Vec<u8>>) -> Self {
        Self {
            seen_resident_prefix: Mutex::new(Vec::new()),
            outputs: Mutex::new(outputs.into_iter().collect()),
        }
    }
}

impl private::Sealed for FakeSequenceResidentPipeline {}

impl vyre::CompiledPipeline for FakeSequenceResidentPipeline {
    fn id(&self) -> &str {
        "weir_test_sized_resident_batch_pipeline"
    }

    fn dispatch(
        &self,
        _inputs: &[Vec<u8>],
        _config: &DispatchConfig,
    ) -> Result<Vec<Vec<u8>>, BackendError> {
        unreachable!("resident fixed-point batch tests use persistent resources")
    }

    fn dispatch_persistent_handles(
        &self,
        inputs: &[Resource],
        _config: &DispatchConfig,
    ) -> Result<OutputBuffers, BackendError> {
        let resident_prefix = inputs
            .iter()
            .take_while(|resource| matches!(resource, Resource::Resident(_)))
            .count();
        self.seen_resident_prefix
            .lock()
            .unwrap()
            .push(resident_prefix);
        let output = self
            .outputs
            .lock()
            .unwrap()
            .pop_front()
            .expect("fake sequence resident pipeline ran out of outputs");
        Ok(vec![output])
    }
}

fn graph_fixture() -> FixedPointForwardGraph {
    graph_fixture_for_kind(crate::fixed_point_graph::FixedPointAnalysisKind::Reaching)
}

fn graph_fixture_for_kind(
    kind: crate::fixed_point_graph::FixedPointAnalysisKind,
) -> FixedPointForwardGraph {
    FixedPointForwardGraph::new_for_kind(
        kind,
        "fixed_point_resident_batch_test",
        2,
        &[0, 1, 1],
        &[1],
        &[vyre_primitives::predicate::edge_kind::CONTROL],
    )
    .expect("valid graph must pack")
}

fn empty_graph_fixture(node_count: u32) -> FixedPointForwardGraph {
    empty_graph_fixture_for_kind(
        crate::fixed_point_graph::FixedPointAnalysisKind::Reaching,
        node_count,
    )
}

fn empty_graph_fixture_for_kind(
    kind: crate::fixed_point_graph::FixedPointAnalysisKind,
    node_count: u32,
) -> FixedPointForwardGraph {
    FixedPointForwardGraph::new_for_kind(
        kind,
        "fixed_point_resident_batch_empty_test",
        node_count,
        &vec![0; node_count as usize + 1],
        &[],
        &[],
    )
    .expect("valid empty graph must pack")
}

mod accounting_contracts;
mod cache_reuse_contracts;
mod result_buffer_contracts;
mod validation_contracts;