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 #[must_use]
18 #[inline]
19 pub fn from_raw_parts(
20 buffers: Vec<BufferDecl>,
21 workgroup_size: [u32; 3],
22 entry: Vec<Node>,
23 ) -> Self {
24 Self::new_raw(buffers, workgroup_size, entry)
25 }
26 };
27}
28
29impl Program {
30 pub const ROOT_REGION_GENERATOR: &'static str = "vyre.program.root";
33
34 #[must_use]
69 #[inline]
70 pub fn wrapped(buffers: Vec<BufferDecl>, workgroup_size: [u32; 3], entry: Vec<Node>) -> Self {
71 Self::new_raw(buffers, workgroup_size, Self::wrap_entry(entry))
72 }
73
74 define_raw_program_constructor!();
75
76 #[must_use]
77 #[inline]
78 pub(crate) fn new_raw(
79 buffers: Vec<BufferDecl>,
80 workgroup_size: [u32; 3],
81 entry: Vec<Node>,
82 ) -> Self {
83 let mut interner = FxHashMap::<Arc<str>, Arc<str>>::default();
84 interner.reserve(buffers.len());
85 let buffers: Vec<BufferDecl> = buffers
86 .into_iter()
87 .map(|mut b| {
88 let arc = interner
89 .entry(Arc::clone(&b.name))
90 .or_insert_with(|| Arc::clone(&b.name))
91 .clone();
92 b.name = arc;
93 b
94 })
95 .collect();
96 let buffer_index = Self::build_buffer_index(&buffers);
97 Self {
98 entry_op_id: None,
99 buffers: Arc::from(buffers),
100 buffer_index: Arc::new(buffer_index),
101 workgroup_size,
102 entry: Arc::new(entry),
103 hash: OnceLock::new(),
104 validation_set: OnceLock::new(),
105 structural_validated: std::sync::atomic::AtomicBool::new(false),
106 structural_validation_fingerprint: std::sync::atomic::AtomicU64::new(0),
107 mutation_provenance: std::sync::atomic::AtomicU8::new(0),
108 fingerprint: OnceLock::new(),
109 normalized_cache_digest: OnceLock::new(),
110 output_buffer_index: OnceLock::new(),
111 has_indirect_dispatch: OnceLock::new(),
112 stats: OnceLock::new(),
113 non_composable_with_self: false,
114 }
115 }
116
117 #[must_use]
124 #[inline]
125 pub fn with_rewritten_wrapped_entry(&self, entry: Vec<Node>) -> Self {
126 self.with_rewritten_entry(Self::wrap_entry(entry))
127 }
128
129 #[must_use]
142 #[inline]
143 pub fn map_entry<F: FnOnce(Vec<Node>) -> Vec<Node>>(self, f: F) -> Self {
144 let entry_op_id = self.entry_op_id.clone();
145 let buffers = Arc::clone(&self.buffers);
146 let buffer_index = Arc::clone(&self.buffer_index);
147 let workgroup_size = self.workgroup_size;
148 let non_composable_with_self = self.non_composable_with_self;
149 let entry = f(self.into_entry_vec());
150 Self {
151 entry_op_id,
152 buffers,
153 buffer_index,
154 workgroup_size,
155 entry: Arc::new(entry),
156 hash: OnceLock::new(),
157 validation_set: OnceLock::new(),
158 structural_validated: std::sync::atomic::AtomicBool::new(false),
159 structural_validation_fingerprint: std::sync::atomic::AtomicU64::new(0),
160 mutation_provenance: std::sync::atomic::AtomicU8::new(0),
161 fingerprint: OnceLock::new(),
162 normalized_cache_digest: OnceLock::new(),
163 output_buffer_index: OnceLock::new(),
164 has_indirect_dispatch: OnceLock::new(),
165 stats: OnceLock::new(),
166 non_composable_with_self,
167 }
168 }
169
170 #[must_use]
173 #[inline]
174 pub fn with_rewritten_entry(&self, entry: Vec<Node>) -> Self {
175 self.with_rewritten_workgroup_size_and_entry(self.workgroup_size, entry)
176 }
177
178 #[must_use]
181 #[inline]
182 pub fn with_rewritten_buffers(&self, buffers: Vec<BufferDecl>) -> Self {
183 let buffer_index = Self::build_buffer_index(&buffers);
184 Self {
185 entry_op_id: self.entry_op_id.clone(),
186 buffers: Arc::from(buffers),
187 buffer_index: Arc::new(buffer_index),
188 workgroup_size: self.workgroup_size,
189 entry: Arc::clone(&self.entry),
190 hash: OnceLock::new(),
191 validation_set: OnceLock::new(),
192 structural_validated: std::sync::atomic::AtomicBool::new(false),
193 structural_validation_fingerprint: std::sync::atomic::AtomicU64::new(0),
194 mutation_provenance: std::sync::atomic::AtomicU8::new(0),
195 fingerprint: OnceLock::new(),
196 normalized_cache_digest: OnceLock::new(),
197 output_buffer_index: OnceLock::new(),
198 has_indirect_dispatch: OnceLock::new(),
199 stats: OnceLock::new(),
200 non_composable_with_self: self.non_composable_with_self,
201 }
202 }
203
204 #[must_use]
207 #[inline]
208 pub fn with_rewritten_workgroup_size_and_entry(
209 &self,
210 workgroup_size: [u32; 3],
211 entry: Vec<Node>,
212 ) -> Self {
213 Self {
214 entry_op_id: self.entry_op_id.clone(),
215 buffers: Arc::clone(&self.buffers),
216 buffer_index: Arc::clone(&self.buffer_index),
217 workgroup_size,
218 entry: Arc::new(entry),
219 hash: OnceLock::new(),
220 validation_set: OnceLock::new(),
221 structural_validated: std::sync::atomic::AtomicBool::new(false),
222 structural_validation_fingerprint: std::sync::atomic::AtomicU64::new(0),
223 mutation_provenance: std::sync::atomic::AtomicU8::new(0),
224 fingerprint: OnceLock::new(),
225 normalized_cache_digest: OnceLock::new(),
226 output_buffer_index: OnceLock::new(),
227 has_indirect_dispatch: OnceLock::new(),
228 stats: OnceLock::new(),
229 non_composable_with_self: self.non_composable_with_self,
230 }
231 }
232
233 #[must_use]
236 #[inline]
237 pub fn into_entry_vec(self) -> Vec<Node> {
238 Arc::try_unwrap(self.entry).unwrap_or_else(|entry| entry.as_ref().clone())
239 }
240
241 #[must_use]
247 #[inline]
248 pub fn with_arena(
249 arena: &ExprArena,
250 buffers: Vec<BufferDecl>,
251 workgroup_size: [u32; 3],
252 ) -> ArenaProgram<'_> {
253 ArenaProgram::new(arena, buffers, workgroup_size)
254 }
255
256 #[must_use]
270 #[inline]
271 pub fn empty() -> Self {
272 Self::wrapped(Vec::new(), [1, 1, 1], Vec::new())
273 }
274
275 #[must_use]
278 #[inline]
279 pub fn with_entry_op_id(mut self, op_id: impl Into<String>) -> Self {
280 self.entry_op_id = Some(op_id.into());
281 self.invalidate_caches();
282 self
283 }
284
285 #[must_use]
287 #[inline]
288 pub fn entry_op_id(&self) -> Option<&str> {
289 self.entry_op_id.as_deref()
290 }
291
292 #[must_use]
294 #[inline]
295 pub(crate) fn with_optional_entry_op_id(mut self, op_id: Option<String>) -> Self {
296 self.entry_op_id = op_id;
297 self.invalidate_caches();
298 self
299 }
300}