vyre_foundation/ir_inner/model/program/
builder.rs1use std::sync::{Arc, OnceLock};
2
3use rustc_hash::FxHashMap;
4
5use crate::ir_inner::model::arena::{ArenaProgram, ExprArena};
6use crate::ir_inner::model::node::Node;
7
8use super::{BufferDecl, Program};
9
10macro_rules! define_raw_program_constructor {
11 () => {
12 #[deprecated(
17 note = "Program::new preserves raw top-level entry nodes. Use Program::wrapped for runnable programs; reserve Program::new for wire decode and negative tests."
18 )]
19 #[must_use]
20 #[inline]
21 pub fn new(
22 buffers: Vec<BufferDecl>,
23 workgroup_size: [u32; 3],
24 entry: Vec<Node>,
25 ) -> Self {
26 Self::new_raw(buffers, workgroup_size, entry)
27 }
28 };
29}
30
31impl Program {
32 pub const ROOT_REGION_GENERATOR: &'static str = "vyre.program.root";
35
36 #[must_use]
71 #[inline]
72 pub fn wrapped(buffers: Vec<BufferDecl>, workgroup_size: [u32; 3], entry: Vec<Node>) -> Self {
73 Self::new_raw(buffers, workgroup_size, Self::wrap_entry(entry))
74 }
75
76 define_raw_program_constructor!();
77
78 #[must_use]
79 #[inline]
80 pub(crate) fn new_raw(
81 buffers: Vec<BufferDecl>,
82 workgroup_size: [u32; 3],
83 entry: Vec<Node>,
84 ) -> Self {
85 let mut interner = FxHashMap::<Arc<str>, Arc<str>>::default();
86 interner.reserve(buffers.len());
87 let buffers: Vec<BufferDecl> = buffers
88 .into_iter()
89 .map(|mut b| {
90 let arc = interner
91 .entry(Arc::clone(&b.name))
92 .or_insert_with(|| Arc::clone(&b.name))
93 .clone();
94 b.name = arc;
95 b
96 })
97 .collect();
98 let buffer_index = Self::build_buffer_index(&buffers);
99 Self {
100 entry_op_id: None,
101 buffers: Arc::from(buffers),
102 buffer_index: Arc::new(buffer_index),
103 workgroup_size,
104 entry: Arc::new(entry),
105 hash: OnceLock::new(),
106 validation_set: OnceLock::new(),
107 structural_validated: std::sync::atomic::AtomicBool::new(false),
108 structural_validation_fingerprint: std::sync::atomic::AtomicU64::new(0),
109 mutation_provenance: std::sync::atomic::AtomicU8::new(0),
110 fingerprint: OnceLock::new(),
111 normalized_cache_digest: OnceLock::new(),
112 output_buffer_index: OnceLock::new(),
113 has_indirect_dispatch: OnceLock::new(),
114 stats: OnceLock::new(),
115 non_composable_with_self: false,
116 }
117 }
118
119 #[must_use]
126 #[inline]
127 pub fn with_rewritten_wrapped_entry(&self, entry: Vec<Node>) -> Self {
128 self.with_rewritten_entry(Self::wrap_entry(entry))
129 }
130
131 #[must_use]
144 #[inline]
145 pub fn map_entry<F: FnOnce(Vec<Node>) -> Vec<Node>>(self, f: F) -> Self {
146 let entry_op_id = self.entry_op_id.clone();
147 let buffers = Arc::clone(&self.buffers);
148 let buffer_index = Arc::clone(&self.buffer_index);
149 let workgroup_size = self.workgroup_size;
150 let non_composable_with_self = self.non_composable_with_self;
151 let entry = f(self.into_entry_vec());
152 Self {
153 entry_op_id,
154 buffers,
155 buffer_index,
156 workgroup_size,
157 entry: Arc::new(entry),
158 hash: OnceLock::new(),
159 validation_set: OnceLock::new(),
160 structural_validated: std::sync::atomic::AtomicBool::new(false),
161 structural_validation_fingerprint: std::sync::atomic::AtomicU64::new(0),
162 mutation_provenance: std::sync::atomic::AtomicU8::new(0),
163 fingerprint: OnceLock::new(),
164 normalized_cache_digest: OnceLock::new(),
165 output_buffer_index: OnceLock::new(),
166 has_indirect_dispatch: OnceLock::new(),
167 stats: OnceLock::new(),
168 non_composable_with_self,
169 }
170 }
171
172 #[must_use]
175 #[inline]
176 pub fn with_rewritten_entry(&self, entry: Vec<Node>) -> Self {
177 self.with_rewritten_workgroup_size_and_entry(self.workgroup_size, entry)
178 }
179
180 #[must_use]
183 #[inline]
184 pub fn with_rewritten_buffers(&self, buffers: Vec<BufferDecl>) -> Self {
185 let buffer_index = Self::build_buffer_index(&buffers);
186 Self {
187 entry_op_id: self.entry_op_id.clone(),
188 buffers: Arc::from(buffers),
189 buffer_index: Arc::new(buffer_index),
190 workgroup_size: self.workgroup_size,
191 entry: Arc::clone(&self.entry),
192 hash: OnceLock::new(),
193 validation_set: OnceLock::new(),
194 structural_validated: std::sync::atomic::AtomicBool::new(false),
195 structural_validation_fingerprint: std::sync::atomic::AtomicU64::new(0),
196 mutation_provenance: std::sync::atomic::AtomicU8::new(0),
197 fingerprint: OnceLock::new(),
198 normalized_cache_digest: OnceLock::new(),
199 output_buffer_index: OnceLock::new(),
200 has_indirect_dispatch: OnceLock::new(),
201 stats: OnceLock::new(),
202 non_composable_with_self: self.non_composable_with_self,
203 }
204 }
205
206 #[must_use]
209 #[inline]
210 pub fn with_rewritten_workgroup_size_and_entry(
211 &self,
212 workgroup_size: [u32; 3],
213 entry: Vec<Node>,
214 ) -> Self {
215 Self {
216 entry_op_id: self.entry_op_id.clone(),
217 buffers: Arc::clone(&self.buffers),
218 buffer_index: Arc::clone(&self.buffer_index),
219 workgroup_size,
220 entry: Arc::new(entry),
221 hash: OnceLock::new(),
222 validation_set: OnceLock::new(),
223 structural_validated: std::sync::atomic::AtomicBool::new(false),
224 structural_validation_fingerprint: std::sync::atomic::AtomicU64::new(0),
225 mutation_provenance: std::sync::atomic::AtomicU8::new(0),
226 fingerprint: OnceLock::new(),
227 normalized_cache_digest: OnceLock::new(),
228 output_buffer_index: OnceLock::new(),
229 has_indirect_dispatch: OnceLock::new(),
230 stats: OnceLock::new(),
231 non_composable_with_self: self.non_composable_with_self,
232 }
233 }
234
235 #[must_use]
238 #[inline]
239 pub fn into_entry_vec(self) -> Vec<Node> {
240 Arc::try_unwrap(self.entry).unwrap_or_else(|entry| entry.as_ref().clone())
241 }
242
243 #[must_use]
249 #[inline]
250 pub fn with_arena(
251 arena: &ExprArena,
252 buffers: Vec<BufferDecl>,
253 workgroup_size: [u32; 3],
254 ) -> ArenaProgram<'_> {
255 ArenaProgram::new(arena, buffers, workgroup_size)
256 }
257
258 #[must_use]
272 #[inline]
273 pub fn empty() -> Self {
274 Self::wrapped(Vec::new(), [1, 1, 1], Vec::new())
275 }
276
277 #[must_use]
280 #[inline]
281 pub fn with_entry_op_id(mut self, op_id: impl Into<String>) -> Self {
282 self.entry_op_id = Some(op_id.into());
283 self.invalidate_caches();
284 self
285 }
286
287 #[must_use]
289 #[inline]
290 pub fn entry_op_id(&self) -> Option<&str> {
291 self.entry_op_id.as_deref()
292 }
293
294 #[must_use]
296 #[inline]
297 pub(crate) fn with_optional_entry_op_id(mut self, op_id: Option<String>) -> Self {
298 self.entry_op_id = op_id;
299 self.invalidate_caches();
300 self
301 }
302}