1use alloc::{sync::Arc, vec::Vec};
2
3use miden_core::{
4 mast::{MastForestId, MastNodeId},
5 program::Program,
6 serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
7};
8use miden_mast_package::debug_info::{DebugSourceInlineCall, DebugSourceNodeId, PackageDebugInfo};
9
10const CONTINUATION_STACK_SIZE_HINT: usize = 64;
12
13#[derive(Debug, Clone)]
15pub struct SourceInlineCallContext {
16 package_debug_info: Arc<PackageDebugInfo>,
17 source_node_id: DebugSourceNodeId,
18 op_idx: u32,
19}
20
21impl SourceInlineCallContext {
22 pub(crate) fn new(
23 package_debug_info: Arc<PackageDebugInfo>,
24 source_node_id: DebugSourceNodeId,
25 op_idx: u32,
26 ) -> Self {
27 Self {
28 package_debug_info,
29 source_node_id,
30 op_idx,
31 }
32 }
33
34 pub(crate) fn for_source_boundary(
35 package_debug_info: Arc<PackageDebugInfo>,
36 source_node_id: Option<DebugSourceNodeId>,
37 ) -> Option<Self> {
38 let source_node_id = source_node_id?;
39 let op_idx = package_debug_info.source_node(source_node_id)?.op_start;
40 package_debug_info.inline_calls_for_operation(source_node_id, op_idx).next()?;
41 Some(Self::new(package_debug_info, source_node_id, op_idx))
42 }
43
44 pub fn debug_info(&self) -> &Arc<PackageDebugInfo> {
46 &self.package_debug_info
47 }
48
49 pub fn source_node_id(&self) -> DebugSourceNodeId {
51 self.source_node_id
52 }
53
54 pub fn inline_calls(&self) -> impl Iterator<Item = &DebugSourceInlineCall> {
56 self.package_debug_info
57 .inline_calls_for_operation(self.source_node_id, self.op_idx)
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
74pub enum Continuation<F> {
75 StartNode(MastNodeId),
77 FinishJoin(MastNodeId),
79 FinishSplit(MastNodeId),
81 FinishLoop(MastNodeId),
88 FinishCall(MastNodeId),
90 FinishDyn(MastNodeId),
92 ResumeBasicBlock {
95 node_id: MastNodeId,
96 batch_index: usize,
97 op_idx_in_batch: usize,
98 },
99 Respan { node_id: MastNodeId, batch_index: usize },
102 FinishBasicBlock(MastNodeId),
106 EnterForest {
111 forest: F,
112 package_debug_info: Option<Arc<PackageDebugInfo>>,
113 inline_context_depth: usize,
115 },
116}
117
118impl<F> Continuation<F> {
119 pub fn increments_clk(&self) -> bool {
122 use Continuation::*;
123
124 match self {
128 StartNode(_)
129 | FinishJoin(_)
130 | FinishSplit(_)
131 | FinishLoop(_)
132 | FinishCall(_)
133 | FinishDyn(_)
134 | ResumeBasicBlock {
135 node_id: _,
136 batch_index: _,
137 op_idx_in_batch: _,
138 }
139 | Respan { node_id: _, batch_index: _ }
140 | FinishBasicBlock(_) => true,
141
142 EnterForest { .. } => false,
143 }
144 }
145
146 pub fn exec_node(&self) -> Option<MastNodeId> {
147 match self {
148 Self::StartNode(node_id)
149 | Self::FinishJoin(node_id)
150 | Self::FinishSplit(node_id)
151 | Self::FinishLoop(node_id)
152 | Self::FinishCall(node_id)
153 | Self::FinishDyn(node_id)
154 | Self::ResumeBasicBlock { node_id, .. }
155 | Self::Respan { node_id, .. }
156 | Self::FinishBasicBlock(node_id) => Some(*node_id),
157 Self::EnterForest { .. } => None,
158 }
159 }
160}
161
162#[derive(Debug, Clone, PartialEq, Eq)]
177pub struct ContinuationStack<F> {
178 stack: Vec<Continuation<F>>,
179 source_node_ids: Option<Vec<Option<DebugSourceNodeId>>>,
180}
181
182impl<F> Default for ContinuationStack<F> {
183 fn default() -> Self {
184 Self { stack: Vec::new(), source_node_ids: None }
185 }
186}
187
188impl<F> ContinuationStack<F> {
189 pub fn new(program: &Program) -> Self {
194 let mut stack = Vec::with_capacity(CONTINUATION_STACK_SIZE_HINT);
195 stack.push(Continuation::StartNode(program.entrypoint()));
196
197 Self { stack, source_node_ids: None }
198 }
199
200 pub(crate) fn new_with_source_node_id(
201 program: &Program,
202 source_node_id: DebugSourceNodeId,
203 ) -> Self {
204 Self::new_with_optional_source_node_id(program, Some(source_node_id))
205 }
206
207 pub(crate) fn new_with_optional_source_node_id(
208 program: &Program,
209 source_node_id: Option<DebugSourceNodeId>,
210 ) -> Self {
211 let mut stack = Vec::with_capacity(CONTINUATION_STACK_SIZE_HINT);
212 stack.push(Continuation::StartNode(program.entrypoint()));
213
214 let mut source_node_ids = Vec::with_capacity(CONTINUATION_STACK_SIZE_HINT);
215 source_node_ids.push(source_node_id);
216
217 Self {
218 stack,
219 source_node_ids: Some(source_node_ids),
220 }
221 }
222
223 pub fn push_continuation(&mut self, continuation: Continuation<F>) {
228 self.stack.push(continuation);
229 self.push_source_node_id(None);
230 }
231
232 pub(crate) fn push_with_source_node_id(
233 &mut self,
234 continuation: Continuation<F>,
235 source_node_id: Option<DebugSourceNodeId>,
236 ) {
237 self.stack.push(continuation);
238 self.push_source_node_id(source_node_id);
239 }
240
241 pub fn push_enter_forest(&mut self, forest: F) {
246 self.push_enter_forest_with_package_debug_info(forest, None, 0);
247 }
248
249 pub(crate) fn push_enter_forest_with_package_debug_info(
250 &mut self,
251 forest: F,
252 package_debug_info: Option<Arc<PackageDebugInfo>>,
253 inline_context_depth: usize,
254 ) {
255 self.stack.push(Continuation::EnterForest {
256 forest,
257 package_debug_info,
258 inline_context_depth,
259 });
260 self.push_source_node_id(None);
261 }
262
263 pub fn push_finish_join(&mut self, node_id: MastNodeId) {
265 self.stack.push(Continuation::FinishJoin(node_id));
266 self.push_source_node_id(None);
267 }
268
269 pub fn push_finish_split(&mut self, node_id: MastNodeId) {
271 self.stack.push(Continuation::FinishSplit(node_id));
272 self.push_source_node_id(None);
273 }
274
275 pub fn push_finish_loop(&mut self, node_id: MastNodeId) {
277 self.stack.push(Continuation::FinishLoop(node_id));
278 self.push_source_node_id(None);
279 }
280
281 pub fn push_finish_call(&mut self, node_id: MastNodeId) {
283 self.stack.push(Continuation::FinishCall(node_id));
284 self.push_source_node_id(None);
285 }
286
287 pub fn push_finish_dyn(&mut self, node_id: MastNodeId) {
289 self.stack.push(Continuation::FinishDyn(node_id));
290 self.push_source_node_id(None);
291 }
292
293 pub fn push_start_node(&mut self, node_id: MastNodeId) {
298 self.stack.push(Continuation::StartNode(node_id));
299 self.push_source_node_id(None);
300 }
301
302 pub fn pop_continuation(&mut self) -> Option<Continuation<F>> {
305 let continuation = self.stack.pop()?;
306 if let Some(source_node_ids) = &mut self.source_node_ids {
307 source_node_ids.pop();
308 }
309 Some(continuation)
310 }
311
312 pub(crate) fn pop_continuation_with_source_node_id(
313 &mut self,
314 ) -> Option<(Continuation<F>, Option<DebugSourceNodeId>)> {
315 let continuation = self.stack.pop()?;
316 let source_node_id = self.source_node_ids.as_mut().and_then(Vec::pop).flatten();
317 Some((continuation, source_node_id))
318 }
319
320 pub fn into_inner(self) -> Vec<Continuation<F>> {
323 self.stack
324 }
325
326 fn push_source_node_id(&mut self, source_node_id: Option<DebugSourceNodeId>) {
327 if let Some(source_node_ids) = &mut self.source_node_ids {
328 source_node_ids.push(source_node_id);
329 }
330 }
331
332 pub(crate) fn start_tracking_source_nodes(
333 &mut self,
334 next_source_node_id: Option<DebugSourceNodeId>,
335 ) {
336 let mut source_node_ids = Vec::with_capacity(self.stack.len());
337 source_node_ids.resize(self.stack.len(), None);
338 if let Some(source_node_id) = source_node_ids.last_mut() {
339 *source_node_id = next_source_node_id;
340 }
341 self.source_node_ids = Some(source_node_ids);
342 }
343
344 pub fn len(&self) -> usize {
349 self.stack.len()
350 }
351
352 pub fn peek_continuation(&self) -> Option<&Continuation<F>> {
358 self.stack.last()
359 }
360
361 pub(crate) fn peek_continuation_with_source_node_id(
362 &self,
363 ) -> Option<(&Continuation<F>, Option<DebugSourceNodeId>)> {
364 let continuation = self.stack.last()?;
365 let source_node_id = self
366 .source_node_ids
367 .as_ref()
368 .and_then(|source_node_ids| source_node_ids.last().copied().flatten());
369 Some((continuation, source_node_id))
370 }
371
372 pub(crate) fn tracks_source_nodes(&self) -> bool {
373 self.source_node_ids.is_some()
374 }
375
376 pub fn iter_continuations_for_next_clock(&self) -> impl Iterator<Item = &Continuation<F>> {
388 let mut found_incrementing_cont = false;
389
390 self.stack.iter().rev().take_while(move |continuation| {
391 if found_incrementing_cont {
392 false
394 } else if continuation.increments_clk() {
395 found_incrementing_cont = true;
397 true
398 } else {
399 true
401 }
402 })
403 }
404
405 pub fn iter_continuations_for_next_clock_with_source_node_ids(
408 &self,
409 ) -> impl Iterator<Item = (&Continuation<F>, Option<DebugSourceNodeId>)> {
410 let mut stack_index = self.stack.len().saturating_sub(1);
411
412 self.iter_continuations_for_next_clock().map(move |cont| {
413 let source_node_id = self
414 .source_node_ids
415 .as_deref()
416 .and_then(|ids| ids.get(stack_index).copied())
417 .flatten();
418 stack_index = stack_index.saturating_sub(1);
419 (cont, source_node_id)
420 })
421 }
422}
423
424impl ContinuationStack<MastForestId> {
425 pub(crate) fn iter_enter_forest_ids(&self) -> impl Iterator<Item = MastForestId> + '_ {
426 self.stack.iter().filter_map(|continuation| match continuation {
427 Continuation::EnterForest { forest, .. } => Some(*forest),
428 _ => None,
429 })
430 }
431}
432
433const TAG_START_NODE: u8 = 0;
437const TAG_FINISH_JOIN: u8 = 1;
438const TAG_FINISH_SPLIT: u8 = 2;
439const TAG_FINISH_LOOP: u8 = 3;
440const TAG_FINISH_CALL: u8 = 4;
441const TAG_FINISH_DYN: u8 = 5;
442const TAG_RESUME_BASIC_BLOCK: u8 = 6;
443const TAG_RESPAN: u8 = 7;
444const TAG_FINISH_BASIC_BLOCK: u8 = 8;
445const TAG_ENTER_FOREST: u8 = 9;
446
447impl Serializable for Continuation<MastForestId> {
453 fn write_into<W: ByteWriter>(&self, target: &mut W) {
454 match self {
455 Self::StartNode(node_id) => {
456 TAG_START_NODE.write_into(target);
457 node_id.write_into(target);
458 },
459 Self::FinishJoin(node_id) => {
460 TAG_FINISH_JOIN.write_into(target);
461 node_id.write_into(target);
462 },
463 Self::FinishSplit(node_id) => {
464 TAG_FINISH_SPLIT.write_into(target);
465 node_id.write_into(target);
466 },
467 Self::FinishLoop(node_id) => {
468 TAG_FINISH_LOOP.write_into(target);
469 node_id.write_into(target);
470 },
471 Self::FinishCall(node_id) => {
472 TAG_FINISH_CALL.write_into(target);
473 node_id.write_into(target);
474 },
475 Self::FinishDyn(node_id) => {
476 TAG_FINISH_DYN.write_into(target);
477 node_id.write_into(target);
478 },
479 Self::ResumeBasicBlock { node_id, batch_index, op_idx_in_batch } => {
480 TAG_RESUME_BASIC_BLOCK.write_into(target);
481 node_id.write_into(target);
482 batch_index.write_into(target);
483 op_idx_in_batch.write_into(target);
484 },
485 Self::Respan { node_id, batch_index } => {
486 TAG_RESPAN.write_into(target);
487 node_id.write_into(target);
488 batch_index.write_into(target);
489 },
490 Self::FinishBasicBlock(node_id) => {
491 TAG_FINISH_BASIC_BLOCK.write_into(target);
492 node_id.write_into(target);
493 },
494 Self::EnterForest {
495 forest,
496 package_debug_info: _,
497 inline_context_depth,
498 } => {
499 TAG_ENTER_FOREST.write_into(target);
500 forest.write_into(target);
501 inline_context_depth.write_into(target);
502 },
503 }
504 }
505}
506
507impl Deserializable for Continuation<MastForestId> {
508 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
509 match u8::read_from(source)? {
510 TAG_START_NODE => Ok(Self::StartNode(MastNodeId::read_from(source)?)),
511 TAG_FINISH_JOIN => Ok(Self::FinishJoin(MastNodeId::read_from(source)?)),
512 TAG_FINISH_SPLIT => Ok(Self::FinishSplit(MastNodeId::read_from(source)?)),
513 TAG_FINISH_LOOP => Ok(Self::FinishLoop(MastNodeId::read_from(source)?)),
514 TAG_FINISH_CALL => Ok(Self::FinishCall(MastNodeId::read_from(source)?)),
515 TAG_FINISH_DYN => Ok(Self::FinishDyn(MastNodeId::read_from(source)?)),
516 TAG_RESUME_BASIC_BLOCK => Ok(Self::ResumeBasicBlock {
517 node_id: MastNodeId::read_from(source)?,
518 batch_index: usize::read_from(source)?,
519 op_idx_in_batch: usize::read_from(source)?,
520 }),
521 TAG_RESPAN => Ok(Self::Respan {
522 node_id: MastNodeId::read_from(source)?,
523 batch_index: usize::read_from(source)?,
524 }),
525 TAG_FINISH_BASIC_BLOCK => Ok(Self::FinishBasicBlock(MastNodeId::read_from(source)?)),
526 TAG_ENTER_FOREST => Ok(Self::EnterForest {
527 forest: MastForestId::read_from(source)?,
528 package_debug_info: None,
529 inline_context_depth: usize::read_from(source)?,
530 }),
531 tag => {
532 Err(DeserializationError::InvalidValue(format!("invalid continuation tag {tag}")))
533 },
534 }
535 }
536}
537
538impl Serializable for ContinuationStack<MastForestId> {
543 fn write_into<W: ByteWriter>(&self, target: &mut W) {
544 self.stack.write_into(target);
545 }
546}
547
548impl Deserializable for ContinuationStack<MastForestId> {
549 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
550 let stack = Vec::<Continuation<MastForestId>>::read_from(source)?;
551 Ok(Self { stack, source_node_ids: None })
552 }
553}
554
555#[cfg(test)]
559mod tests {
560 use alloc::sync::Arc;
561
562 use miden_core::mast::MastForest;
563 use miden_mast_package::debug_info::{
564 DebugFunctionIdx, DebugLocIdx, DebugSourceInlineCall, DebugSourceNode,
565 PackageDebugInfoBuilder,
566 };
567
568 use super::*;
569
570 #[test]
571 fn get_next_clock_cycle_increment_empty_stack() {
572 let stack: ContinuationStack<Arc<MastForest>> = ContinuationStack::default();
573 assert!(stack.iter_continuations_for_next_clock().next().is_none());
574 }
575
576 #[test]
577 fn get_next_clock_cycle_increment_ends_with_incrementing() {
578 let mut stack: ContinuationStack<Arc<MastForest>> = ContinuationStack::default();
579 stack.push_continuation(Continuation::StartNode(MastNodeId::new_unchecked(0)));
581
582 let result: Vec<_> = stack.iter_continuations_for_next_clock().collect();
583 assert_eq!(result.len(), 1);
584 assert!(matches!(result[0], Continuation::StartNode(_)));
585 }
586
587 #[test]
588 fn get_next_clock_cycle_increment_enter_forest_after_incrementing() {
589 let mut stack: ContinuationStack<Arc<MastForest>> = ContinuationStack::default();
590 stack.push_continuation(Continuation::StartNode(MastNodeId::new_unchecked(0)));
592 stack.push_continuation(Continuation::EnterForest {
594 forest: Arc::new(MastForest::new()),
595 package_debug_info: None,
596 inline_context_depth: 0,
597 });
598
599 let result: Vec<_> = stack.iter_continuations_for_next_clock().collect();
600 assert_eq!(result.len(), 2);
602 assert!(matches!(result[0], Continuation::EnterForest { .. }));
603 assert!(matches!(result[1], Continuation::StartNode(_)));
604 }
605
606 #[test]
607 fn get_next_clock_cycle_increment_multiple_enter_forest_after_incrementing() {
608 let mut stack: ContinuationStack<Arc<MastForest>> = ContinuationStack::default();
609 stack.push_continuation(Continuation::StartNode(MastNodeId::new_unchecked(0)));
611 stack.push_continuation(Continuation::EnterForest {
613 forest: Arc::new(MastForest::new()),
614 package_debug_info: None,
615 inline_context_depth: 0,
616 });
617 stack.push_continuation(Continuation::EnterForest {
618 forest: Arc::new(MastForest::new()),
619 package_debug_info: None,
620 inline_context_depth: 0,
621 });
622
623 let result: Vec<_> = stack.iter_continuations_for_next_clock().collect();
624 assert_eq!(result.len(), 3);
626 assert!(matches!(result[0], Continuation::EnterForest { .. }));
627 assert!(matches!(result[1], Continuation::EnterForest { .. }));
628 assert!(matches!(result[2], Continuation::StartNode(_)));
629 }
630
631 #[test]
632 fn inline_call_context_uses_the_source_boundary_index() {
633 let mut builder = PackageDebugInfoBuilder::default();
634 let source_node_id = builder
635 .add_node(DebugSourceNode {
636 exec_node: MastNodeId::new_unchecked(0),
637 children: Vec::new(),
638 op_start: 7,
639 op_end: 7,
640 asm_ops: Vec::new(),
641 debug_vars: Vec::new(),
642 inline_calls: vec![DebugSourceInlineCall {
643 op_idx: 7,
644 callee_idx: DebugFunctionIdx::from(0),
645 loc_idx: DebugLocIdx::from(0),
646 }],
647 })
648 .unwrap();
649 let debug_info = Arc::from(builder.build());
650
651 let context =
652 SourceInlineCallContext::for_source_boundary(debug_info, Some(source_node_id))
653 .expect("boundary row should create inherited inline context");
654
655 assert_eq!(context.inline_calls().map(|row| row.op_idx).collect::<Vec<_>>(), [7]);
656 }
657
658 #[test]
659 fn continuation_stack_mast_forest_id_round_trip_omits_debug_metadata() {
660 let mut stack: ContinuationStack<MastForestId> = ContinuationStack::default();
661 stack.push_continuation(Continuation::StartNode(MastNodeId::from(1)));
662 stack.push_continuation(Continuation::EnterForest {
663 forest: MastForestId::from(2),
664 package_debug_info: None,
665 inline_context_depth: 0,
666 });
667 stack.push_continuation(Continuation::ResumeBasicBlock {
668 node_id: MastNodeId::from(3),
669 batch_index: 4,
670 op_idx_in_batch: 5,
671 });
672 stack.source_node_ids =
673 Some(vec![Some(DebugSourceNodeId::from(10)), None, Some(DebugSourceNodeId::from(11))]);
674
675 let bytes = stack.to_bytes();
676 let restored = ContinuationStack::<MastForestId>::read_from_bytes(&bytes).unwrap();
677
678 assert_eq!(restored.stack.len(), 3);
679 assert!(matches!(
680 restored.stack[0],
681 Continuation::StartNode(node_id) if node_id == MastNodeId::from(1)
682 ));
683 assert!(matches!(
684 restored.stack[1],
685 Continuation::EnterForest {
686 forest,
687 package_debug_info: None,
688 inline_context_depth: 0,
689 } if forest == MastForestId::from(2)
690 ));
691 assert!(matches!(
692 restored.stack[2],
693 Continuation::ResumeBasicBlock {
694 node_id,
695 batch_index: 4,
696 op_idx_in_batch: 5,
697 } if node_id == MastNodeId::from(3)
698 ));
699 assert_eq!(restored.source_node_ids, None);
702 }
703}