miden_processor/
continuation_stack.rs1use alloc::{sync::Arc, vec::Vec};
2
3use miden_core::{mast::MastNodeId, program::Program};
4use miden_mast_package::debug_info::{DebugSourceNodeId, PackageDebugInfo};
5
6const CONTINUATION_STACK_SIZE_HINT: usize = 64;
8
9#[derive(Debug, Clone)]
22pub enum Continuation<F> {
23 StartNode(MastNodeId),
25 FinishJoin(MastNodeId),
27 FinishSplit(MastNodeId),
29 FinishLoop(MastNodeId),
36 FinishCall(MastNodeId),
38 FinishDyn(MastNodeId),
40 ResumeBasicBlock {
43 node_id: MastNodeId,
44 batch_index: usize,
45 op_idx_in_batch: usize,
46 },
47 Respan { node_id: MastNodeId, batch_index: usize },
50 FinishBasicBlock(MastNodeId),
54 EnterForest {
59 forest: F,
60 package_debug_info: Option<Arc<PackageDebugInfo>>,
61 },
62}
63
64impl<F> Continuation<F> {
65 pub fn increments_clk(&self) -> bool {
68 use Continuation::*;
69
70 match self {
74 StartNode(_)
75 | FinishJoin(_)
76 | FinishSplit(_)
77 | FinishLoop(_)
78 | FinishCall(_)
79 | FinishDyn(_)
80 | ResumeBasicBlock {
81 node_id: _,
82 batch_index: _,
83 op_idx_in_batch: _,
84 }
85 | Respan { node_id: _, batch_index: _ }
86 | FinishBasicBlock(_) => true,
87
88 EnterForest { .. } => false,
89 }
90 }
91
92 #[cfg(any(test, feature = "testing"))]
93 pub(crate) fn exec_node(&self) -> Option<MastNodeId> {
94 match self {
95 Self::StartNode(node_id)
96 | Self::FinishJoin(node_id)
97 | Self::FinishSplit(node_id)
98 | Self::FinishLoop(node_id)
99 | Self::FinishCall(node_id)
100 | Self::FinishDyn(node_id)
101 | Self::ResumeBasicBlock { node_id, .. }
102 | Self::Respan { node_id, .. }
103 | Self::FinishBasicBlock(node_id) => Some(*node_id),
104 Self::EnterForest { .. } => None,
105 }
106 }
107}
108
109#[derive(Debug, Clone)]
119pub struct ContinuationStack<F> {
120 stack: Vec<Continuation<F>>,
121 source_node_ids: Option<Vec<Option<DebugSourceNodeId>>>,
122}
123
124impl<F> Default for ContinuationStack<F> {
125 fn default() -> Self {
126 Self { stack: Vec::new(), source_node_ids: None }
127 }
128}
129
130impl<F> ContinuationStack<F> {
131 pub fn new(program: &Program) -> Self {
136 let mut stack = Vec::with_capacity(CONTINUATION_STACK_SIZE_HINT);
137 stack.push(Continuation::StartNode(program.entrypoint()));
138
139 Self { stack, source_node_ids: None }
140 }
141
142 pub(crate) fn new_with_source_node_id(
143 program: &Program,
144 source_node_id: DebugSourceNodeId,
145 ) -> Self {
146 Self::new_with_optional_source_node_id(program, Some(source_node_id))
147 }
148
149 pub(crate) fn new_with_optional_source_node_id(
150 program: &Program,
151 source_node_id: Option<DebugSourceNodeId>,
152 ) -> Self {
153 let mut stack = Vec::with_capacity(CONTINUATION_STACK_SIZE_HINT);
154 stack.push(Continuation::StartNode(program.entrypoint()));
155
156 let mut source_node_ids = Vec::with_capacity(CONTINUATION_STACK_SIZE_HINT);
157 source_node_ids.push(source_node_id);
158
159 Self {
160 stack,
161 source_node_ids: Some(source_node_ids),
162 }
163 }
164
165 pub fn push_continuation(&mut self, continuation: Continuation<F>) {
170 self.stack.push(continuation);
171 self.push_source_node_id(None);
172 }
173
174 pub(crate) fn push_with_source_node_id(
175 &mut self,
176 continuation: Continuation<F>,
177 source_node_id: Option<DebugSourceNodeId>,
178 ) {
179 self.stack.push(continuation);
180 self.push_source_node_id(source_node_id);
181 }
182
183 pub fn push_enter_forest(&mut self, forest: F) {
188 self.push_enter_forest_with_package_debug_info(forest, None);
189 }
190
191 pub(crate) fn push_enter_forest_with_package_debug_info(
192 &mut self,
193 forest: F,
194 package_debug_info: Option<Arc<PackageDebugInfo>>,
195 ) {
196 self.stack.push(Continuation::EnterForest { forest, package_debug_info });
197 self.push_source_node_id(None);
198 }
199
200 pub fn push_finish_join(&mut self, node_id: MastNodeId) {
202 self.stack.push(Continuation::FinishJoin(node_id));
203 self.push_source_node_id(None);
204 }
205
206 pub fn push_finish_split(&mut self, node_id: MastNodeId) {
208 self.stack.push(Continuation::FinishSplit(node_id));
209 self.push_source_node_id(None);
210 }
211
212 pub fn push_finish_loop(&mut self, node_id: MastNodeId) {
214 self.stack.push(Continuation::FinishLoop(node_id));
215 self.push_source_node_id(None);
216 }
217
218 pub fn push_finish_call(&mut self, node_id: MastNodeId) {
220 self.stack.push(Continuation::FinishCall(node_id));
221 self.push_source_node_id(None);
222 }
223
224 pub fn push_finish_dyn(&mut self, node_id: MastNodeId) {
226 self.stack.push(Continuation::FinishDyn(node_id));
227 self.push_source_node_id(None);
228 }
229
230 pub fn push_start_node(&mut self, node_id: MastNodeId) {
235 self.stack.push(Continuation::StartNode(node_id));
236 self.push_source_node_id(None);
237 }
238
239 pub fn pop_continuation(&mut self) -> Option<Continuation<F>> {
242 let continuation = self.stack.pop()?;
243 if let Some(source_node_ids) = &mut self.source_node_ids {
244 source_node_ids.pop();
245 }
246 Some(continuation)
247 }
248
249 pub(crate) fn pop_continuation_with_source_node_id(
250 &mut self,
251 ) -> Option<(Continuation<F>, Option<DebugSourceNodeId>)> {
252 let continuation = self.stack.pop()?;
253 let source_node_id = self.source_node_ids.as_mut().and_then(Vec::pop).flatten();
254 Some((continuation, source_node_id))
255 }
256
257 pub fn into_inner(self) -> Vec<Continuation<F>> {
260 self.stack
261 }
262
263 fn push_source_node_id(&mut self, source_node_id: Option<DebugSourceNodeId>) {
264 if let Some(source_node_ids) = &mut self.source_node_ids {
265 source_node_ids.push(source_node_id);
266 }
267 }
268
269 #[cfg(any(test, feature = "testing"))]
270 pub(crate) fn start_tracking_source_nodes(
271 &mut self,
272 next_source_node_id: Option<DebugSourceNodeId>,
273 ) {
274 let mut source_node_ids = Vec::with_capacity(self.stack.len());
275 source_node_ids.resize(self.stack.len(), None);
276 if let Some(source_node_id) = source_node_ids.last_mut() {
277 *source_node_id = next_source_node_id;
278 }
279 self.source_node_ids = Some(source_node_ids);
280 }
281
282 pub fn len(&self) -> usize {
287 self.stack.len()
288 }
289
290 pub fn peek_continuation(&self) -> Option<&Continuation<F>> {
296 self.stack.last()
297 }
298
299 pub(crate) fn peek_continuation_with_source_node_id(
300 &self,
301 ) -> Option<(&Continuation<F>, Option<DebugSourceNodeId>)> {
302 let continuation = self.stack.last()?;
303 let source_node_id = self
304 .source_node_ids
305 .as_ref()
306 .and_then(|source_node_ids| source_node_ids.last().copied().flatten());
307 Some((continuation, source_node_id))
308 }
309
310 pub(crate) fn tracks_source_nodes(&self) -> bool {
311 self.source_node_ids.is_some()
312 }
313
314 pub fn iter_continuations_for_next_clock(&self) -> impl Iterator<Item = &Continuation<F>> {
326 let mut found_incrementing_cont = false;
327
328 self.stack.iter().rev().take_while(move |continuation| {
329 if found_incrementing_cont {
330 false
332 } else if continuation.increments_clk() {
333 found_incrementing_cont = true;
335 true
336 } else {
337 true
339 }
340 })
341 }
342}
343
344#[cfg(test)]
348mod tests {
349 use alloc::sync::Arc;
350
351 use miden_core::mast::MastForest;
352
353 use super::*;
354
355 #[test]
356 fn get_next_clock_cycle_increment_empty_stack() {
357 let stack: ContinuationStack<Arc<MastForest>> = ContinuationStack::default();
358 assert!(stack.iter_continuations_for_next_clock().next().is_none());
359 }
360
361 #[test]
362 fn get_next_clock_cycle_increment_ends_with_incrementing() {
363 let mut stack: ContinuationStack<Arc<MastForest>> = ContinuationStack::default();
364 stack.push_continuation(Continuation::StartNode(MastNodeId::new_unchecked(0)));
366
367 let result: Vec<_> = stack.iter_continuations_for_next_clock().collect();
368 assert_eq!(result.len(), 1);
369 assert!(matches!(result[0], Continuation::StartNode(_)));
370 }
371
372 #[test]
373 fn get_next_clock_cycle_increment_enter_forest_after_incrementing() {
374 let mut stack: ContinuationStack<Arc<MastForest>> = ContinuationStack::default();
375 stack.push_continuation(Continuation::StartNode(MastNodeId::new_unchecked(0)));
377 stack.push_continuation(Continuation::EnterForest {
379 forest: Arc::new(MastForest::new()),
380 package_debug_info: None,
381 });
382
383 let result: Vec<_> = stack.iter_continuations_for_next_clock().collect();
384 assert_eq!(result.len(), 2);
386 assert!(matches!(result[0], Continuation::EnterForest { .. }));
387 assert!(matches!(result[1], Continuation::StartNode(_)));
388 }
389
390 #[test]
391 fn get_next_clock_cycle_increment_multiple_enter_forest_after_incrementing() {
392 let mut stack: ContinuationStack<Arc<MastForest>> = ContinuationStack::default();
393 stack.push_continuation(Continuation::StartNode(MastNodeId::new_unchecked(0)));
395 stack.push_continuation(Continuation::EnterForest {
397 forest: Arc::new(MastForest::new()),
398 package_debug_info: None,
399 });
400 stack.push_continuation(Continuation::EnterForest {
401 forest: Arc::new(MastForest::new()),
402 package_debug_info: None,
403 });
404
405 let result: Vec<_> = stack.iter_continuations_for_next_clock().collect();
406 assert_eq!(result.len(), 3);
408 assert!(matches!(result[0], Continuation::EnterForest { .. }));
409 assert!(matches!(result[1], Continuation::EnterForest { .. }));
410 assert!(matches!(result[2], Continuation::StartNode(_)));
411 }
412}