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.visitor",
INPUTS,
OUTPUTS,
LAWS,
wgsl_only,
IntrinsicDescriptor::new(
"workgroup_visitor_visit",
"workgroup-sram-mark-and-emit",
crate::ops::cpu_op::structured_intrinsic_cpu,
),
);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VisitStatus {
FirstVisit = 0,
AlreadyVisited = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VisitError {
OutOfRange,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WorkgroupVisitor {
universe: usize,
emit_capacity: usize,
visited: Vec<bool>,
emit_buffer: Vec<u32>,
}
impl WorkgroupVisitor {
#[must_use]
pub fn new(universe: usize, emit_capacity: usize) -> Self {
Self {
universe,
emit_capacity,
visited: vec![false; universe],
emit_buffer: Vec::with_capacity(emit_capacity),
}
}
#[must_use]
pub fn universe(&self) -> usize {
self.universe
}
#[must_use]
pub fn emit_capacity(&self) -> usize {
self.emit_capacity
}
#[must_use]
pub fn emitted(&self) -> &[u32] {
&self.emit_buffer
}
#[must_use]
pub fn is_visited(&self, node: u32) -> bool {
let idx = node as usize;
idx < self.visited.len() && self.visited[idx]
}
pub fn visit(&mut self, node: u32) -> Result<VisitStatus, VisitError> {
let idx = node as usize;
if idx >= self.universe {
return Err(VisitError::OutOfRange);
}
if self.visited[idx] {
return Ok(VisitStatus::AlreadyVisited);
}
self.visited[idx] = true;
if self.emit_buffer.len() < self.emit_capacity {
self.emit_buffer.push(node);
}
Ok(VisitStatus::FirstVisit)
}
pub fn reset(&mut self) {
for slot in &mut self.visited {
*slot = false;
}
self.emit_buffer.clear();
}
}
pub fn wgsl_only(backend: &Backend) -> bool {
matches!(backend, Backend::Wgsl)
}