Skip to main content

miden_processor/fast/
execution_api.rs

1use alloc::{sync::Arc, vec::Vec};
2use core::ops::ControlFlow;
3
4use miden_core::{
5    Word,
6    mast::{MastForest, MastNodeId},
7    program::{Kernel, MIN_STACK_DEPTH, Program, StackOutputs},
8};
9use miden_mast_package::debug_info::{
10    DebugSourceGraphLookupError, DebugSourceNodeId, PackageDebugInfo,
11};
12use tracing::instrument;
13
14use super::{
15    FastProcessor, NoopTracer,
16    external::maybe_use_caller_error_context,
17    step::{BreakReason, NeverStopper, ResumeContext, StepStopper},
18};
19use crate::{
20    ExecutionError, ExecutionOutput, Host, LoadedMastForest, Stopper, SyncHost, TraceBuildInputs,
21    continuation_stack::ContinuationStack,
22    errors::{
23        MapExecErr, MapExecErrNoCtx, PackageSourceDebugContext, malformed_mast_forest_with_context,
24    },
25    execution::{
26        InternalBreakReason, execute_impl, finish_emit_op_execution,
27        finish_load_mast_forest_from_dyn_start, finish_load_mast_forest_from_external,
28    },
29    trace::execution_tracer::ExecutionTracer,
30    tracer::Tracer,
31};
32
33impl FastProcessor {
34    // EXECUTE
35    // -------------------------------------------------------------------------------------------
36
37    /// Executes the given program synchronously and returns the execution output.
38    pub fn execute_sync(
39        self,
40        program: &Program,
41        host: &mut impl SyncHost,
42    ) -> Result<ExecutionOutput, ExecutionError> {
43        self.execute_with_tracer_sync(program, host, &mut NoopTracer)
44    }
45
46    /// Executes the given program synchronously with package-owned source/debug context.
47    ///
48    /// This derives the entrypoint source occurrence from [`PackageDebugInfo`], so the source graph
49    /// must contain at most one root for the executable entrypoint. When the package manifest names
50    /// the exact entrypoint source occurrence, use
51    /// [`Self::execute_with_package_debug_info_at_source_node_sync`] instead.
52    pub fn execute_with_package_debug_info_sync(
53        self,
54        program: &Program,
55        package_debug_info: &PackageDebugInfo,
56        host: &mut impl SyncHost,
57    ) -> Result<ExecutionOutput, ExecutionError> {
58        self.execute_with_package_debug_info_and_tracer_sync(
59            program,
60            package_debug_info,
61            None,
62            host,
63            &mut NoopTracer,
64        )
65    }
66
67    /// Executes the given program synchronously with package-owned source/debug context rooted at
68    /// `entrypoint_source_node_id`.
69    ///
70    /// Use this when the package manifest names the exact source/debug occurrence for the
71    /// executable entrypoint. This preserves source disambiguation when multiple source roots map
72    /// to the same executable MAST node.
73    pub fn execute_with_package_debug_info_at_source_node_sync(
74        self,
75        program: &Program,
76        package_debug_info: &PackageDebugInfo,
77        entrypoint_source_node_id: DebugSourceNodeId,
78        host: &mut impl SyncHost,
79    ) -> Result<ExecutionOutput, ExecutionError> {
80        self.execute_with_package_debug_info_and_tracer_sync(
81            program,
82            package_debug_info,
83            Some(entrypoint_source_node_id),
84            host,
85            &mut NoopTracer,
86        )
87    }
88
89    /// Async variant of [`Self::execute_sync`] for hosts that need async callbacks.
90    #[inline(always)]
91    pub async fn execute(
92        self,
93        program: &Program,
94        host: &mut impl Host,
95    ) -> Result<ExecutionOutput, ExecutionError> {
96        self.execute_with_tracer(program, host, &mut NoopTracer).await
97    }
98
99    /// Async variant of [`Self::execute_with_package_debug_info_sync`].
100    ///
101    /// When the package manifest names the exact entrypoint source occurrence, use
102    /// [`Self::execute_with_package_debug_info_at_source_node`] instead.
103    #[inline(always)]
104    pub async fn execute_with_package_debug_info(
105        self,
106        program: &Program,
107        package_debug_info: &PackageDebugInfo,
108        host: &mut impl Host,
109    ) -> Result<ExecutionOutput, ExecutionError> {
110        self.execute_with_package_debug_info_and_tracer(
111            program,
112            package_debug_info,
113            None,
114            host,
115            &mut NoopTracer,
116        )
117        .await
118    }
119
120    /// Async variant of [`Self::execute_with_package_debug_info_at_source_node_sync`].
121    #[inline(always)]
122    pub async fn execute_with_package_debug_info_at_source_node(
123        self,
124        program: &Program,
125        package_debug_info: &PackageDebugInfo,
126        entrypoint_source_node_id: DebugSourceNodeId,
127        host: &mut impl Host,
128    ) -> Result<ExecutionOutput, ExecutionError> {
129        self.execute_with_package_debug_info_and_tracer(
130            program,
131            package_debug_info,
132            Some(entrypoint_source_node_id),
133            host,
134            &mut NoopTracer,
135        )
136        .await
137    }
138
139    /// Executes the given program synchronously and returns the bundled trace inputs required by
140    /// [`crate::trace::build_trace`].
141    ///
142    /// # Example
143    /// ```
144    /// use miden_assembly::Assembler;
145    /// use miden_processor::{DefaultHost, FastProcessor, StackInputs};
146    ///
147    /// let program = Assembler::default()
148    ///     .assemble_program("prg", "begin push.1 drop end")
149    ///     .unwrap()
150    ///     .unwrap_program();
151    /// let mut host = DefaultHost::default();
152    ///
153    /// let trace_inputs = FastProcessor::new(StackInputs::default())
154    ///     .execute_trace_inputs_sync(&program, &mut host)
155    ///     .unwrap();
156    /// let trace = miden_processor::trace::build_trace(trace_inputs).unwrap();
157    ///
158    /// assert_eq!(*trace.program_hash(), program.hash());
159    /// ```
160    #[instrument(name = "execute_trace_inputs_sync", skip_all)]
161    pub fn execute_trace_inputs_sync(
162        self,
163        program: &Program,
164        host: &mut impl SyncHost,
165    ) -> Result<TraceBuildInputs, ExecutionError> {
166        let mut tracer = ExecutionTracer::new(
167            self.options.core_trace_fragment_size(),
168            self.options.max_stack_depth(),
169        );
170        let execution_output = self.execute_with_tracer_sync(program, host, &mut tracer)?;
171        Ok(Self::trace_build_inputs_from_parts(program, execution_output, tracer))
172    }
173
174    /// Executes the given program synchronously with package-owned source/debug context and returns
175    /// the bundled trace inputs required by [`crate::trace::build_trace`].
176    #[instrument(name = "execute_trace_inputs_with_package_debug_info_sync", skip_all)]
177    pub fn execute_trace_inputs_with_package_debug_info_sync(
178        self,
179        program: &Program,
180        package_debug_info: &PackageDebugInfo,
181        host: &mut impl SyncHost,
182    ) -> Result<TraceBuildInputs, ExecutionError> {
183        let mut tracer = ExecutionTracer::new(
184            self.options.core_trace_fragment_size(),
185            self.options.max_stack_depth(),
186        );
187        let execution_output = self.execute_with_package_debug_info_and_tracer_sync(
188            program,
189            package_debug_info,
190            None,
191            host,
192            &mut tracer,
193        )?;
194        Ok(Self::trace_build_inputs_from_parts(program, execution_output, tracer))
195    }
196
197    /// Executes the given program synchronously with package-owned source/debug context rooted at
198    /// `entrypoint_source_node_id` and returns the bundled trace inputs required by
199    /// [`crate::trace::build_trace`].
200    #[instrument(
201        name = "execute_trace_inputs_with_package_debug_info_at_source_node_sync",
202        skip_all
203    )]
204    pub fn execute_trace_inputs_with_package_debug_info_at_source_node_sync(
205        self,
206        program: &Program,
207        package_debug_info: &PackageDebugInfo,
208        entrypoint_source_node_id: DebugSourceNodeId,
209        host: &mut impl SyncHost,
210    ) -> Result<TraceBuildInputs, ExecutionError> {
211        let mut tracer = ExecutionTracer::new(
212            self.options.core_trace_fragment_size(),
213            self.options.max_stack_depth(),
214        );
215        let execution_output = self.execute_with_package_debug_info_and_tracer_sync(
216            program,
217            package_debug_info,
218            Some(entrypoint_source_node_id),
219            host,
220            &mut tracer,
221        )?;
222        Ok(Self::trace_build_inputs_from_parts(program, execution_output, tracer))
223    }
224
225    /// Async variant of [`Self::execute_trace_inputs_sync`] for async hosts.
226    #[inline(always)]
227    #[instrument(name = "execute_trace_inputs", skip_all)]
228    pub async fn execute_trace_inputs(
229        self,
230        program: &Program,
231        host: &mut impl Host,
232    ) -> Result<TraceBuildInputs, ExecutionError> {
233        let mut tracer = ExecutionTracer::new(
234            self.options.core_trace_fragment_size(),
235            self.options.max_stack_depth(),
236        );
237        let execution_output = self.execute_with_tracer(program, host, &mut tracer).await?;
238        Ok(Self::trace_build_inputs_from_parts(program, execution_output, tracer))
239    }
240
241    /// Async variant of [`Self::execute_trace_inputs_with_package_debug_info_sync`].
242    #[cfg(any(test, feature = "testing"))]
243    #[inline(always)]
244    #[instrument(name = "execute_trace_inputs_with_package_debug_info", skip_all)]
245    pub async fn execute_trace_inputs_with_package_debug_info(
246        self,
247        program: &Program,
248        package_debug_info: &PackageDebugInfo,
249        host: &mut impl Host,
250    ) -> Result<TraceBuildInputs, ExecutionError> {
251        let mut tracer = ExecutionTracer::new(
252            self.options.core_trace_fragment_size(),
253            self.options.max_stack_depth(),
254        );
255        let execution_output = self
256            .execute_with_package_debug_info_and_tracer(
257                program,
258                package_debug_info,
259                None,
260                host,
261                &mut tracer,
262            )
263            .await?;
264        Ok(Self::trace_build_inputs_from_parts(program, execution_output, tracer))
265    }
266
267    /// Async variant of
268    /// [`Self::execute_trace_inputs_with_package_debug_info_at_source_node_sync`].
269    #[cfg(any(test, feature = "testing"))]
270    #[inline(always)]
271    #[instrument(name = "execute_trace_inputs_with_package_debug_info_at_source_node", skip_all)]
272    pub async fn execute_trace_inputs_with_package_debug_info_at_source_node(
273        self,
274        program: &Program,
275        package_debug_info: &PackageDebugInfo,
276        entrypoint_source_node_id: DebugSourceNodeId,
277        host: &mut impl Host,
278    ) -> Result<TraceBuildInputs, ExecutionError> {
279        let mut tracer = ExecutionTracer::new(
280            self.options.core_trace_fragment_size(),
281            self.options.max_stack_depth(),
282        );
283        let execution_output = self
284            .execute_with_package_debug_info_and_tracer(
285                program,
286                package_debug_info,
287                Some(entrypoint_source_node_id),
288                host,
289                &mut tracer,
290            )
291            .await?;
292        Ok(Self::trace_build_inputs_from_parts(program, execution_output, tracer))
293    }
294
295    /// Executes the given program with the provided tracer using an async host.
296    pub async fn execute_with_tracer<T>(
297        mut self,
298        program: &Program,
299        host: &mut impl Host,
300        tracer: &mut T,
301    ) -> Result<ExecutionOutput, ExecutionError>
302    where
303        T: Tracer<Processor = Self, Forest = Arc<MastForest>>,
304    {
305        let mut continuation_stack = ContinuationStack::new(program);
306        let mut current_forest = program.mast_forest().clone();
307        let mut package_debug_info = None;
308
309        self.advice.extend_map(current_forest.advice_map()).map_exec_err_no_ctx()?;
310        let flow = self
311            .execute_impl_async(
312                &mut continuation_stack,
313                &mut current_forest,
314                program.kernel(),
315                host,
316                tracer,
317                &NeverStopper,
318                &mut package_debug_info,
319            )
320            .await;
321        Self::execution_result_from_flow(flow, self)
322    }
323
324    /// Executes the given program with package-owned source/debug context and the provided tracer
325    /// using an async host.
326    async fn execute_with_package_debug_info_and_tracer<T>(
327        mut self,
328        program: &Program,
329        package_debug_info: &PackageDebugInfo,
330        entrypoint_source_node_id: Option<DebugSourceNodeId>,
331        host: &mut impl Host,
332        tracer: &mut T,
333    ) -> Result<ExecutionOutput, ExecutionError>
334    where
335        T: Tracer<Processor = Self, Forest = Arc<MastForest>>,
336    {
337        let mut continuation_stack = Self::source_aware_continuation_stack(
338            program,
339            package_debug_info,
340            entrypoint_source_node_id,
341        )?;
342        let mut current_forest = program.mast_forest().clone();
343        let mut package_debug_info = Some(Arc::new(package_debug_info.clone()));
344
345        self.advice.extend_map(current_forest.advice_map()).map_exec_err_no_ctx()?;
346        let flow = self
347            .execute_impl_async(
348                &mut continuation_stack,
349                &mut current_forest,
350                program.kernel(),
351                host,
352                tracer,
353                &NeverStopper,
354                &mut package_debug_info,
355            )
356            .await;
357        Self::execution_result_from_flow(flow, self)
358    }
359
360    /// Executes the given program with the provided tracer using a sync host.
361    pub fn execute_with_tracer_sync<T>(
362        mut self,
363        program: &Program,
364        host: &mut impl SyncHost,
365        tracer: &mut T,
366    ) -> Result<ExecutionOutput, ExecutionError>
367    where
368        T: Tracer<Processor = Self, Forest = Arc<MastForest>>,
369    {
370        let mut continuation_stack = ContinuationStack::new(program);
371        let mut current_forest = program.mast_forest().clone();
372        let mut package_debug_info = None;
373
374        self.advice.extend_map(current_forest.advice_map()).map_exec_err_no_ctx()?;
375        let flow = self.execute_impl(
376            &mut continuation_stack,
377            &mut current_forest,
378            program.kernel(),
379            host,
380            tracer,
381            &NeverStopper,
382            &mut package_debug_info,
383        );
384        Self::execution_result_from_flow(flow, self)
385    }
386
387    /// Executes the given program with package-owned source/debug context and the provided tracer
388    /// using a sync host.
389    fn execute_with_package_debug_info_and_tracer_sync<T>(
390        mut self,
391        program: &Program,
392        package_debug_info: &PackageDebugInfo,
393        entrypoint_source_node_id: Option<DebugSourceNodeId>,
394        host: &mut impl SyncHost,
395        tracer: &mut T,
396    ) -> Result<ExecutionOutput, ExecutionError>
397    where
398        T: Tracer<Processor = Self, Forest = Arc<MastForest>>,
399    {
400        let mut continuation_stack = Self::source_aware_continuation_stack(
401            program,
402            package_debug_info,
403            entrypoint_source_node_id,
404        )?;
405        let mut current_forest = program.mast_forest().clone();
406        let mut package_debug_info = Some(Arc::new(package_debug_info.clone()));
407
408        self.advice.extend_map(current_forest.advice_map()).map_exec_err_no_ctx()?;
409        let flow = self.execute_impl(
410            &mut continuation_stack,
411            &mut current_forest,
412            program.kernel(),
413            host,
414            tracer,
415            &NeverStopper,
416            &mut package_debug_info,
417        );
418        Self::execution_result_from_flow(flow, self)
419    }
420
421    /// Executes a single clock cycle synchronously.
422    pub fn step_sync(
423        &mut self,
424        host: &mut impl SyncHost,
425        resume_ctx: ResumeContext,
426    ) -> Result<Option<ResumeContext>, ExecutionError> {
427        let ResumeContext {
428            mut current_forest,
429            mut continuation_stack,
430            kernel,
431            mut package_debug_info,
432        } = resume_ctx;
433
434        let flow = self.execute_impl(
435            &mut continuation_stack,
436            &mut current_forest,
437            &kernel,
438            host,
439            &mut NoopTracer,
440            &StepStopper,
441            &mut package_debug_info,
442        );
443        Self::resume_context_from_flow(
444            flow,
445            continuation_stack,
446            current_forest,
447            kernel,
448            package_debug_info,
449        )
450    }
451
452    /// Executes a single clock cycle synchronously with package-owned source/debug context.
453    pub fn step_with_package_debug_info_sync(
454        &mut self,
455        host: &mut impl SyncHost,
456        resume_ctx: ResumeContext,
457        package_debug_info: &PackageDebugInfo,
458    ) -> Result<Option<ResumeContext>, ExecutionError> {
459        let ResumeContext {
460            mut current_forest,
461            mut continuation_stack,
462            kernel,
463            package_debug_info: mut active_package_debug_info,
464        } = resume_ctx;
465        Self::ensure_source_aware_step_context(
466            &mut continuation_stack,
467            &mut active_package_debug_info,
468            package_debug_info,
469        )?;
470
471        let flow = self.execute_impl(
472            &mut continuation_stack,
473            &mut current_forest,
474            &kernel,
475            host,
476            &mut NoopTracer,
477            &StepStopper,
478            &mut active_package_debug_info,
479        );
480        Self::resume_context_from_flow(
481            flow,
482            continuation_stack,
483            current_forest,
484            kernel,
485            active_package_debug_info,
486        )
487    }
488
489    /// Async variant of [`Self::step_sync`].
490    #[inline(always)]
491    pub async fn step(
492        &mut self,
493        host: &mut impl Host,
494        resume_ctx: ResumeContext,
495    ) -> Result<Option<ResumeContext>, ExecutionError> {
496        let ResumeContext {
497            mut current_forest,
498            mut continuation_stack,
499            kernel,
500            mut package_debug_info,
501        } = resume_ctx;
502
503        let flow = self
504            .execute_impl_async(
505                &mut continuation_stack,
506                &mut current_forest,
507                &kernel,
508                host,
509                &mut NoopTracer,
510                &StepStopper,
511                &mut package_debug_info,
512            )
513            .await;
514        Self::resume_context_from_flow(
515            flow,
516            continuation_stack,
517            current_forest,
518            kernel,
519            package_debug_info,
520        )
521    }
522
523    /// Async variant of [`Self::step_with_package_debug_info_sync`].
524    #[inline(always)]
525    pub async fn step_with_package_debug_info(
526        &mut self,
527        host: &mut impl Host,
528        resume_ctx: ResumeContext,
529        package_debug_info: &PackageDebugInfo,
530    ) -> Result<Option<ResumeContext>, ExecutionError> {
531        let ResumeContext {
532            mut current_forest,
533            mut continuation_stack,
534            kernel,
535            package_debug_info: mut active_package_debug_info,
536        } = resume_ctx;
537        Self::ensure_source_aware_step_context(
538            &mut continuation_stack,
539            &mut active_package_debug_info,
540            package_debug_info,
541        )?;
542
543        let flow = self
544            .execute_impl_async(
545                &mut continuation_stack,
546                &mut current_forest,
547                &kernel,
548                host,
549                &mut NoopTracer,
550                &StepStopper,
551                &mut active_package_debug_info,
552            )
553            .await;
554        Self::resume_context_from_flow(
555            flow,
556            continuation_stack,
557            current_forest,
558            kernel,
559            active_package_debug_info,
560        )
561    }
562
563    /// Pairs execution output with the trace inputs captured by the tracer.
564    #[inline(always)]
565    fn trace_build_inputs_from_parts(
566        program: &Program,
567        execution_output: ExecutionOutput,
568        tracer: ExecutionTracer,
569    ) -> TraceBuildInputs {
570        TraceBuildInputs::from_execution(
571            program,
572            execution_output,
573            tracer.into_trace_generation_context(),
574        )
575    }
576
577    pub(super) fn source_aware_continuation_stack(
578        program: &Program,
579        package_debug_info: &PackageDebugInfo,
580        entrypoint_source_node_id: Option<DebugSourceNodeId>,
581    ) -> Result<ContinuationStack<Arc<MastForest>>, ExecutionError> {
582        if let Some(source_node_id) = entrypoint_source_node_id {
583            let Some(source_node) = package_debug_info.source_node(source_node_id) else {
584                return Err(ExecutionError::Internal(
585                    "package debug source graph is missing the entrypoint source node",
586                ));
587            };
588            if source_node.exec_node != program.entrypoint() {
589                return Err(ExecutionError::Internal(
590                    "package debug entrypoint source node does not match the program entrypoint",
591                ));
592            }
593
594            return Ok(ContinuationStack::new_with_source_node_id(program, source_node_id));
595        }
596
597        let Some(source_node_id) = package_debug_info
598            .unique_source_root_for_exec_node(program.entrypoint())
599            .map_err(|_| {
600                ExecutionError::Internal(
601                    "package debug source graph has ambiguous or malformed entrypoint roots",
602                )
603            })?
604        else {
605            return Ok(ContinuationStack::new_with_optional_source_node_id(program, None));
606        };
607
608        Ok(ContinuationStack::new_with_source_node_id(program, source_node_id))
609    }
610
611    #[cfg(any(test, feature = "testing"))]
612    fn source_aware_resume_context(
613        &mut self,
614        program: &Program,
615        package_debug_info: &PackageDebugInfo,
616        entrypoint_source_node_id: Option<DebugSourceNodeId>,
617    ) -> Result<ResumeContext, ExecutionError> {
618        self.advice
619            .extend_map(program.mast_forest().advice_map())
620            .map_exec_err_no_ctx()?;
621
622        Ok(ResumeContext {
623            current_forest: program.mast_forest().clone(),
624            continuation_stack: Self::source_aware_continuation_stack(
625                program,
626                package_debug_info,
627                entrypoint_source_node_id,
628            )?,
629            kernel: program.kernel().clone(),
630            package_debug_info: Some(Arc::new(package_debug_info.clone())),
631        })
632    }
633
634    fn ensure_source_aware_step_context(
635        continuation_stack: &mut ContinuationStack<Arc<MastForest>>,
636        package_debug_info: &mut Option<Arc<PackageDebugInfo>>,
637        supplied_package_debug_info: &PackageDebugInfo,
638    ) -> Result<(), ExecutionError> {
639        if package_debug_info.is_none() {
640            *package_debug_info = Some(Arc::new(supplied_package_debug_info.clone()));
641        }
642
643        if !continuation_stack.tracks_source_nodes() {
644            let source_node_id = Self::source_root_for_next_continuation(
645                continuation_stack,
646                package_debug_info.as_deref().expect("package debug info was just initialized"),
647            )?;
648            continuation_stack.start_tracking_source_nodes(source_node_id);
649        }
650
651        Ok(())
652    }
653
654    fn source_root_for_next_continuation(
655        continuation_stack: &ContinuationStack<Arc<MastForest>>,
656        package_debug_info: &PackageDebugInfo,
657    ) -> Result<Option<DebugSourceNodeId>, ExecutionError> {
658        let Some((continuation, _)) = continuation_stack.peek_continuation_with_source_node_id()
659        else {
660            return Ok(None);
661        };
662
663        let Some(exec_node) = continuation.exec_node() else {
664            return Ok(None);
665        };
666
667        package_debug_info.unique_source_root_for_exec_node(exec_node).map_err(|_| {
668            ExecutionError::Internal(
669                "package debug source graph has ambiguous or malformed continuation roots",
670            )
671        })
672    }
673
674    /// Converts a step-wise execution result into the next resume context, if execution stopped.
675    #[inline(always)]
676    fn resume_context_from_flow(
677        flow: ControlFlow<BreakReason<Arc<MastForest>>, StackOutputs>,
678        mut continuation_stack: ContinuationStack<Arc<MastForest>>,
679        current_forest: Arc<MastForest>,
680        kernel: Kernel,
681        package_debug_info: Option<Arc<PackageDebugInfo>>,
682    ) -> Result<Option<ResumeContext>, ExecutionError> {
683        match flow {
684            ControlFlow::Continue(_) => Ok(None),
685            ControlFlow::Break(break_reason) => match break_reason {
686                BreakReason::Err(err) => Err(err),
687                BreakReason::Stopped(maybe_continuation) => {
688                    if let Some((continuation, source_node_id)) = maybe_continuation {
689                        continuation_stack.push_with_source_node_id(continuation, source_node_id);
690                    }
691
692                    Ok(Some(ResumeContext {
693                        current_forest,
694                        continuation_stack,
695                        kernel,
696                        package_debug_info,
697                    }))
698                },
699            },
700        }
701    }
702
703    /// Materializes the current stack as public outputs without consuming the processor.
704    #[inline(always)]
705    fn current_stack_outputs(&self) -> StackOutputs {
706        StackOutputs::new(
707            &self.stack[self.stack_bot_idx..self.stack_top_idx]
708                .iter()
709                .rev()
710                .copied()
711                .collect::<Vec<_>>(),
712        )
713        .unwrap()
714    }
715
716    /// Executes the given program with the provided tracer and returns the stack outputs.
717    ///
718    /// This function takes a `&mut self` (compared to `self` for the public sync execution
719    /// methods) so that the processor state may be accessed after execution. Reusing the same
720    /// processor for a second program is incorrect. This is mainly meant to be used in tests.
721    fn execute_impl<S, T>(
722        &mut self,
723        continuation_stack: &mut ContinuationStack<Arc<MastForest>>,
724        current_forest: &mut Arc<MastForest>,
725        kernel: &Kernel,
726        host: &mut impl SyncHost,
727        tracer: &mut T,
728        stopper: &S,
729        package_debug_info: &mut Option<Arc<PackageDebugInfo>>,
730    ) -> ControlFlow<BreakReason<Arc<MastForest>>, StackOutputs>
731    where
732        S: Stopper<Processor = Self, Forest = Arc<MastForest>>,
733        T: Tracer<Processor = Self, Forest = Arc<MastForest>>,
734    {
735        while let ControlFlow::Break(internal_break_reason) = execute_impl(
736            self,
737            continuation_stack,
738            current_forest,
739            kernel,
740            host,
741            tracer,
742            stopper,
743            package_debug_info,
744        ) {
745            let current_package_debug_info = package_debug_info.as_deref();
746            let source_aware_execution =
747                current_package_debug_info.is_some() || continuation_stack.tracks_source_nodes();
748            match internal_break_reason {
749                InternalBreakReason::User(break_reason) => return ControlFlow::Break(break_reason),
750                InternalBreakReason::Emit { op_idx, continuation, source_node_id } => {
751                    self.op_emit_sync(host, op_idx, current_package_debug_info, source_node_id)?;
752
753                    finish_emit_op_execution(
754                        continuation,
755                        source_node_id,
756                        self,
757                        continuation_stack,
758                        current_forest,
759                        tracer,
760                        stopper,
761                    )?;
762                },
763                InternalBreakReason::LoadMastForestFromDyn { callee_hash, source_node_id } => {
764                    let (root_id, new_forest, new_package_debug_info, new_source_node_id) =
765                        match self.load_mast_forest_sync(
766                            callee_hash,
767                            host,
768                            current_package_debug_info,
769                            source_node_id,
770                            source_aware_execution,
771                        ) {
772                            Ok(result) => result,
773                            Err(err) => return ControlFlow::Break(BreakReason::Err(err)),
774                        };
775
776                    finish_load_mast_forest_from_dyn_start(
777                        root_id,
778                        new_forest,
779                        new_package_debug_info,
780                        new_source_node_id,
781                        self,
782                        current_forest,
783                        package_debug_info,
784                        continuation_stack,
785                        tracer,
786                        stopper,
787                    )?;
788                },
789                InternalBreakReason::LoadMastForestFromExternal {
790                    external_node_id,
791                    procedure_hash,
792                    source_node_id,
793                } => {
794                    let (root_id, new_forest, new_package_debug_info, new_source_node_id) =
795                        match self.load_mast_forest_sync(
796                            procedure_hash,
797                            host,
798                            current_package_debug_info,
799                            source_node_id,
800                            source_aware_execution,
801                        ) {
802                            Ok(result) => result,
803                            Err(err) => {
804                                let maybe_enriched_err = maybe_use_caller_error_context(
805                                    err,
806                                    continuation_stack,
807                                    current_package_debug_info,
808                                    host,
809                                );
810                                return ControlFlow::Break(BreakReason::Err(maybe_enriched_err));
811                            },
812                        };
813
814                    finish_load_mast_forest_from_external(
815                        root_id,
816                        new_forest,
817                        new_package_debug_info,
818                        new_source_node_id,
819                        external_node_id,
820                        current_forest,
821                        package_debug_info,
822                        continuation_stack,
823                        tracer,
824                    )?;
825                },
826            }
827        }
828
829        match StackOutputs::new(
830            &self.stack[self.stack_bot_idx..self.stack_top_idx]
831                .iter()
832                .rev()
833                .copied()
834                .collect::<Vec<_>>(),
835        ) {
836            Ok(stack_outputs) => ControlFlow::Continue(stack_outputs),
837            Err(_) => ControlFlow::Break(BreakReason::Err(ExecutionError::OutputStackOverflow(
838                self.stack_top_idx - self.stack_bot_idx - MIN_STACK_DEPTH,
839            ))),
840        }
841    }
842
843    async fn execute_impl_async<S, T>(
844        &mut self,
845        continuation_stack: &mut ContinuationStack<Arc<MastForest>>,
846        current_forest: &mut Arc<MastForest>,
847        kernel: &Kernel,
848        host: &mut impl Host,
849        tracer: &mut T,
850        stopper: &S,
851        package_debug_info: &mut Option<Arc<PackageDebugInfo>>,
852    ) -> ControlFlow<BreakReason<Arc<MastForest>>, StackOutputs>
853    where
854        S: Stopper<Processor = Self, Forest = Arc<MastForest>>,
855        T: Tracer<Processor = Self, Forest = Arc<MastForest>>,
856    {
857        while let ControlFlow::Break(internal_break_reason) = execute_impl(
858            self,
859            continuation_stack,
860            current_forest,
861            kernel,
862            host,
863            tracer,
864            stopper,
865            package_debug_info,
866        ) {
867            let current_package_debug_info = package_debug_info.as_deref();
868            let source_aware_execution =
869                current_package_debug_info.is_some() || continuation_stack.tracks_source_nodes();
870            match internal_break_reason {
871                InternalBreakReason::User(break_reason) => return ControlFlow::Break(break_reason),
872                InternalBreakReason::Emit { op_idx, continuation, source_node_id } => {
873                    self.op_emit(host, op_idx, current_package_debug_info, source_node_id).await?;
874
875                    finish_emit_op_execution(
876                        continuation,
877                        source_node_id,
878                        self,
879                        continuation_stack,
880                        current_forest,
881                        tracer,
882                        stopper,
883                    )?;
884                },
885                InternalBreakReason::LoadMastForestFromDyn { callee_hash, source_node_id } => {
886                    let (root_id, new_forest, new_package_debug_info, new_source_node_id) =
887                        match self
888                            .load_mast_forest(
889                                callee_hash,
890                                host,
891                                current_package_debug_info,
892                                source_node_id,
893                                source_aware_execution,
894                            )
895                            .await
896                        {
897                            Ok(result) => result,
898                            Err(err) => return ControlFlow::Break(BreakReason::Err(err)),
899                        };
900
901                    finish_load_mast_forest_from_dyn_start(
902                        root_id,
903                        new_forest,
904                        new_package_debug_info,
905                        new_source_node_id,
906                        self,
907                        current_forest,
908                        package_debug_info,
909                        continuation_stack,
910                        tracer,
911                        stopper,
912                    )?;
913                },
914                InternalBreakReason::LoadMastForestFromExternal {
915                    external_node_id,
916                    procedure_hash,
917                    source_node_id,
918                } => {
919                    let (root_id, new_forest, new_package_debug_info, new_source_node_id) =
920                        match self
921                            .load_mast_forest(
922                                procedure_hash,
923                                host,
924                                current_package_debug_info,
925                                source_node_id,
926                                source_aware_execution,
927                            )
928                            .await
929                        {
930                            Ok(result) => result,
931                            Err(err) => {
932                                let maybe_enriched_err = maybe_use_caller_error_context(
933                                    err,
934                                    continuation_stack,
935                                    current_package_debug_info,
936                                    host,
937                                );
938                                return ControlFlow::Break(BreakReason::Err(maybe_enriched_err));
939                            },
940                        };
941
942                    finish_load_mast_forest_from_external(
943                        root_id,
944                        new_forest,
945                        new_package_debug_info,
946                        new_source_node_id,
947                        external_node_id,
948                        current_forest,
949                        package_debug_info,
950                        continuation_stack,
951                        tracer,
952                    )?;
953                },
954            }
955        }
956
957        match StackOutputs::new(
958            &self.stack[self.stack_bot_idx..self.stack_top_idx]
959                .iter()
960                .rev()
961                .copied()
962                .collect::<Vec<_>>(),
963        ) {
964            Ok(stack_outputs) => ControlFlow::Continue(stack_outputs),
965            Err(_) => ControlFlow::Break(BreakReason::Err(ExecutionError::OutputStackOverflow(
966                self.stack_top_idx - self.stack_bot_idx - MIN_STACK_DEPTH,
967            ))),
968        }
969    }
970
971    // HELPERS
972    // ------------------------------------------------------------------------------------------
973
974    fn load_mast_forest_sync(
975        &mut self,
976        node_digest: Word,
977        host: &mut impl SyncHost,
978        package_debug_info: Option<&PackageDebugInfo>,
979        source_node_id: Option<DebugSourceNodeId>,
980        source_aware_execution: bool,
981    ) -> Result<
982        (
983            MastNodeId,
984            Arc<MastForest>,
985            Option<Arc<PackageDebugInfo>>,
986            Option<DebugSourceNodeId>,
987        ),
988        ExecutionError,
989    > {
990        let loaded_mast_forest = host.get_mast_forest(&node_digest).ok_or_else(|| {
991            match (package_debug_info, source_node_id) {
992                (Some(debug_info), Some(source_node_id)) => {
993                    crate::errors::procedure_not_found_with_package_source_context(
994                        node_digest,
995                        PackageSourceDebugContext::new(debug_info, source_node_id),
996                        host,
997                    )
998                },
999                _ => crate::errors::procedure_not_found_with_context(node_digest),
1000            }
1001        })?;
1002        let mast_forest = loaded_mast_forest.mast_forest().clone();
1003
1004        let root_id = mast_forest.find_procedure_root(node_digest).ok_or_else(|| {
1005            let context = match (package_debug_info, source_node_id) {
1006                (Some(debug_info), Some(source_node_id)) => {
1007                    Some(PackageSourceDebugContext::new(debug_info, source_node_id))
1008                },
1009                _ => None,
1010            };
1011            malformed_mast_forest_with_context(node_digest, context, host)
1012        })?;
1013
1014        self.advice.extend_map(mast_forest.advice_map()).map_exec_err()?;
1015        let (loaded_package_debug_info, loaded_source_node_id) =
1016            Self::loaded_package_source_context(
1017                &loaded_mast_forest,
1018                root_id,
1019                source_aware_execution,
1020            )?;
1021
1022        Ok((root_id, mast_forest, loaded_package_debug_info, loaded_source_node_id))
1023    }
1024
1025    async fn load_mast_forest(
1026        &mut self,
1027        node_digest: Word,
1028        host: &mut impl Host,
1029        package_debug_info: Option<&PackageDebugInfo>,
1030        source_node_id: Option<DebugSourceNodeId>,
1031        source_aware_execution: bool,
1032    ) -> Result<
1033        (
1034            MastNodeId,
1035            Arc<MastForest>,
1036            Option<Arc<PackageDebugInfo>>,
1037            Option<DebugSourceNodeId>,
1038        ),
1039        ExecutionError,
1040    > {
1041        let loaded_mast_forest = if let Some(mast_forest) = host.get_mast_forest(&node_digest).await
1042        {
1043            mast_forest
1044        } else {
1045            return Err(match (package_debug_info, source_node_id) {
1046                (Some(debug_info), Some(source_node_id)) => {
1047                    crate::errors::procedure_not_found_with_package_source_context(
1048                        node_digest,
1049                        PackageSourceDebugContext::new(debug_info, source_node_id),
1050                        host,
1051                    )
1052                },
1053                _ => crate::errors::procedure_not_found_with_context(node_digest),
1054            });
1055        };
1056        let mast_forest = loaded_mast_forest.mast_forest().clone();
1057
1058        let root_id = mast_forest.find_procedure_root(node_digest).ok_or_else(|| {
1059            let context = match (package_debug_info, source_node_id) {
1060                (Some(debug_info), Some(source_node_id)) => {
1061                    Some(PackageSourceDebugContext::new(debug_info, source_node_id))
1062                },
1063                _ => None,
1064            };
1065            malformed_mast_forest_with_context(node_digest, context, host)
1066        })?;
1067
1068        self.advice.extend_map(mast_forest.advice_map()).map_exec_err()?;
1069        let (loaded_package_debug_info, loaded_source_node_id) =
1070            Self::loaded_package_source_context(
1071                &loaded_mast_forest,
1072                root_id,
1073                source_aware_execution,
1074            )?;
1075
1076        Ok((root_id, mast_forest, loaded_package_debug_info, loaded_source_node_id))
1077    }
1078
1079    fn loaded_package_source_context(
1080        loaded_mast_forest: &LoadedMastForest,
1081        root_id: MastNodeId,
1082        source_aware_execution: bool,
1083    ) -> Result<(Option<Arc<PackageDebugInfo>>, Option<DebugSourceNodeId>), ExecutionError> {
1084        if !source_aware_execution {
1085            return Ok((None, None));
1086        }
1087
1088        let Some(package_debug_info) = loaded_mast_forest
1089            .package_debug_info()
1090            .map_err(|_| ExecutionError::Internal("loaded package debug info is malformed"))?
1091        else {
1092            return Ok((None, None));
1093        };
1094
1095        let source_node_id = match package_debug_info.unique_source_root_for_exec_node(root_id) {
1096            Ok(source_node_id) => source_node_id,
1097            Err(DebugSourceGraphLookupError::AmbiguousRoot { .. }) => None,
1098            Err(_) => {
1099                return Err(ExecutionError::Internal(
1100                    "loaded package debug source graph has malformed entrypoint roots",
1101                ));
1102            },
1103        };
1104
1105        Ok((Some(package_debug_info), source_node_id))
1106    }
1107
1108    /// Executes the given program synchronously one step at a time.
1109    pub fn execute_by_step_sync(
1110        mut self,
1111        program: &Program,
1112        host: &mut impl SyncHost,
1113    ) -> Result<StackOutputs, ExecutionError> {
1114        let mut current_resume_ctx = self.get_initial_resume_context(program)?;
1115
1116        loop {
1117            match self.step_sync(host, current_resume_ctx)? {
1118                Some(next_resume_ctx) => {
1119                    current_resume_ctx = next_resume_ctx;
1120                },
1121                None => break Ok(self.current_stack_outputs()),
1122            }
1123        }
1124    }
1125
1126    /// Executes the given program synchronously one step at a time with package-owned source/debug
1127    /// context.
1128    #[cfg(any(test, feature = "testing"))]
1129    pub fn execute_by_step_with_package_debug_info_sync(
1130        mut self,
1131        program: &Program,
1132        package_debug_info: &PackageDebugInfo,
1133        host: &mut impl SyncHost,
1134    ) -> Result<StackOutputs, ExecutionError> {
1135        let mut current_resume_ctx =
1136            self.source_aware_resume_context(program, package_debug_info, None)?;
1137
1138        loop {
1139            match self.step_with_package_debug_info_sync(
1140                host,
1141                current_resume_ctx,
1142                package_debug_info,
1143            )? {
1144                Some(next_resume_ctx) => {
1145                    current_resume_ctx = next_resume_ctx;
1146                },
1147                None => break Ok(self.current_stack_outputs()),
1148            }
1149        }
1150    }
1151
1152    /// Executes the given program synchronously one step at a time with package-owned source/debug
1153    /// context rooted at `entrypoint_source_node_id`.
1154    #[cfg(any(test, feature = "testing"))]
1155    pub fn execute_by_step_with_package_debug_info_at_source_node_sync(
1156        mut self,
1157        program: &Program,
1158        package_debug_info: &PackageDebugInfo,
1159        entrypoint_source_node_id: DebugSourceNodeId,
1160        host: &mut impl SyncHost,
1161    ) -> Result<StackOutputs, ExecutionError> {
1162        let mut current_resume_ctx = self.source_aware_resume_context(
1163            program,
1164            package_debug_info,
1165            Some(entrypoint_source_node_id),
1166        )?;
1167
1168        loop {
1169            match self.step_with_package_debug_info_sync(
1170                host,
1171                current_resume_ctx,
1172                package_debug_info,
1173            )? {
1174                Some(next_resume_ctx) => {
1175                    current_resume_ctx = next_resume_ctx;
1176                },
1177                None => break Ok(self.current_stack_outputs()),
1178            }
1179        }
1180    }
1181
1182    /// Async variant of [`Self::execute_by_step_sync`].
1183    #[inline(always)]
1184    pub async fn execute_by_step(
1185        mut self,
1186        program: &Program,
1187        host: &mut impl Host,
1188    ) -> Result<StackOutputs, ExecutionError> {
1189        let mut current_resume_ctx = self.get_initial_resume_context(program)?;
1190        let mut processor = self;
1191
1192        loop {
1193            match processor.step(host, current_resume_ctx).await? {
1194                Some(next_resume_ctx) => {
1195                    current_resume_ctx = next_resume_ctx;
1196                },
1197                None => break Ok(processor.current_stack_outputs()),
1198            }
1199        }
1200    }
1201
1202    /// Async variant of [`Self::execute_by_step_with_package_debug_info_sync`].
1203    #[cfg(any(test, feature = "testing"))]
1204    #[inline(always)]
1205    pub async fn execute_by_step_with_package_debug_info(
1206        mut self,
1207        program: &Program,
1208        package_debug_info: &PackageDebugInfo,
1209        host: &mut impl Host,
1210    ) -> Result<StackOutputs, ExecutionError> {
1211        let mut current_resume_ctx =
1212            self.source_aware_resume_context(program, package_debug_info, None)?;
1213        let mut processor = self;
1214
1215        loop {
1216            match processor
1217                .step_with_package_debug_info(host, current_resume_ctx, package_debug_info)
1218                .await?
1219            {
1220                Some(next_resume_ctx) => {
1221                    current_resume_ctx = next_resume_ctx;
1222                },
1223                None => break Ok(processor.current_stack_outputs()),
1224            }
1225        }
1226    }
1227
1228    /// Async variant of
1229    /// [`Self::execute_by_step_with_package_debug_info_at_source_node_sync`].
1230    #[cfg(any(test, feature = "testing"))]
1231    #[inline(always)]
1232    pub async fn execute_by_step_with_package_debug_info_at_source_node(
1233        mut self,
1234        program: &Program,
1235        package_debug_info: &PackageDebugInfo,
1236        entrypoint_source_node_id: DebugSourceNodeId,
1237        host: &mut impl Host,
1238    ) -> Result<StackOutputs, ExecutionError> {
1239        let mut current_resume_ctx = self.source_aware_resume_context(
1240            program,
1241            package_debug_info,
1242            Some(entrypoint_source_node_id),
1243        )?;
1244        let mut processor = self;
1245
1246        loop {
1247            match processor
1248                .step_with_package_debug_info(host, current_resume_ctx, package_debug_info)
1249                .await?
1250            {
1251                Some(next_resume_ctx) => {
1252                    current_resume_ctx = next_resume_ctx;
1253                },
1254                None => break Ok(processor.current_stack_outputs()),
1255            }
1256        }
1257    }
1258
1259    /// Similar to [`Self::execute_sync`], but allows mutable access to the processor.
1260    ///
1261    /// This is mainly meant to be used in tests.
1262    #[cfg(any(test, feature = "testing"))]
1263    pub fn execute_mut_sync(
1264        &mut self,
1265        program: &Program,
1266        host: &mut impl SyncHost,
1267    ) -> Result<StackOutputs, ExecutionError> {
1268        let mut continuation_stack = ContinuationStack::new(program);
1269        let mut current_forest = program.mast_forest().clone();
1270        let mut package_debug_info = None;
1271
1272        self.advice.extend_map(current_forest.advice_map()).map_exec_err_no_ctx()?;
1273
1274        let flow = self.execute_impl(
1275            &mut continuation_stack,
1276            &mut current_forest,
1277            program.kernel(),
1278            host,
1279            &mut NoopTracer,
1280            &NeverStopper,
1281            &mut package_debug_info,
1282        );
1283        Self::stack_result_from_flow(flow)
1284    }
1285
1286    /// Async variant of [`Self::execute_mut_sync`].
1287    #[cfg(any(test, feature = "testing"))]
1288    #[inline(always)]
1289    pub async fn execute_mut(
1290        &mut self,
1291        program: &Program,
1292        host: &mut impl Host,
1293    ) -> Result<StackOutputs, ExecutionError> {
1294        let mut continuation_stack = ContinuationStack::new(program);
1295        let mut current_forest = program.mast_forest().clone();
1296        let mut package_debug_info = None;
1297
1298        self.advice.extend_map(current_forest.advice_map()).map_exec_err_no_ctx()?;
1299
1300        let flow = self
1301            .execute_impl_async(
1302                &mut continuation_stack,
1303                &mut current_forest,
1304                program.kernel(),
1305                host,
1306                &mut NoopTracer,
1307                &NeverStopper,
1308                &mut package_debug_info,
1309            )
1310            .await;
1311        Self::stack_result_from_flow(flow)
1312    }
1313}