vyre 0.4.0

GPU compute intermediate representation with a standard operation library
Documentation
//! Specification and CPU reference for `workgroup.stack`.

use crate::ir::DataType;
use crate::ops::{AlgebraicLaw, Backend, IntrinsicDescriptor, OpSpec};

pub const INPUTS: &[DataType] = &[DataType::U32, DataType::U32];
pub const OUTPUTS: &[DataType] = &[DataType::U32, DataType::U32];
pub const LAWS: &[AlgebraicLaw] = &[];

pub const SPEC: OpSpec = OpSpec::intrinsic(
    "workgroup.stack",
    INPUTS,
    OUTPUTS,
    LAWS,
    wgsl_only,
    IntrinsicDescriptor::new(
        "workgroup_stack_push",
        "workgroup-sram-atomics",
        crate::ops::cpu_op::structured_intrinsic_cpu,
    ),
);
/// Stack command status word shared by the CPU oracle and WGSL lowering.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StackStatus {
    /// Operation completed.
    Ok = 0,
    /// The stack was full before a push could reserve a slot.
    Overflow = 1,
    /// The stack was empty before a pop or peek.
    Underflow = 2,
}
/// Error returned by fallible stack operations.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StackError {
    /// The bounded stack has no remaining capacity.
    Overflow,
    /// The stack contains no value to read.
    Underflow,
}
/// Bounded LIFO stack used as the CPU reference for `workgroup.stack`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WorkgroupStack<T> {
    capacity: usize,
    values: Vec<T>,
}
impl<T: Copy> WorkgroupStack<T> {
    /// Create an empty stack with a fixed capacity.
    #[must_use]
    pub fn new(capacity: usize) -> Self {
        Self {
            capacity,
            values: Vec::with_capacity(capacity),
        }
    }

    /// Push one value onto the stack.
    ///
    /// # Errors
    ///
    /// Returns [`StackError::Overflow`] when `len() == capacity`.
    pub fn push(&mut self, value: T) -> Result<StackStatus, StackError> {
        if self.values.len() >= self.capacity {
            return Err(StackError::Overflow);
        }
        self.values.push(value);
        Ok(StackStatus::Ok)
    }

    /// Pop the most recently pushed value.
    ///
    /// # Errors
    ///
    /// Returns [`StackError::Underflow`] when the stack is empty.
    pub fn pop(&mut self) -> Result<T, StackError> {
        self.values.pop().ok_or(StackError::Underflow)
    }

    /// Read the most recently pushed value without removing it.
    ///
    /// # Errors
    ///
    /// Returns [`StackError::Underflow`] when the stack is empty.
    pub fn peek(&self) -> Result<T, StackError> {
        self.values.last().copied().ok_or(StackError::Underflow)
    }

    /// Return the number of live elements.
    #[must_use]
    pub fn len(&self) -> usize {
        self.values.len()
    }

    /// Return true when the stack contains no values.
    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.values.is_empty()
    }

    /// Return the fixed capacity.
    #[must_use]
    pub fn capacity(&self) -> usize {
        self.capacity
    }
}
pub fn wgsl_only(backend: &Backend) -> bool {
    matches!(backend, Backend::Wgsl)
}