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    #[cfg(any(test, feature = "testing"))]
454    pub fn step_with_package_debug_info_sync(
455        &mut self,
456        host: &mut impl SyncHost,
457        resume_ctx: ResumeContext,
458        package_debug_info: &PackageDebugInfo,
459    ) -> Result<Option<ResumeContext>, ExecutionError> {
460        let ResumeContext {
461            mut current_forest,
462            mut continuation_stack,
463            kernel,
464            package_debug_info: mut active_package_debug_info,
465        } = resume_ctx;
466        Self::ensure_source_aware_step_context(
467            &mut continuation_stack,
468            &mut active_package_debug_info,
469            package_debug_info,
470        )?;
471
472        let flow = self.execute_impl(
473            &mut continuation_stack,
474            &mut current_forest,
475            &kernel,
476            host,
477            &mut NoopTracer,
478            &StepStopper,
479            &mut active_package_debug_info,
480        );
481        Self::resume_context_from_flow(
482            flow,
483            continuation_stack,
484            current_forest,
485            kernel,
486            active_package_debug_info,
487        )
488    }
489
490    /// Async variant of [`Self::step_sync`].
491    #[inline(always)]
492    pub async fn step(
493        &mut self,
494        host: &mut impl Host,
495        resume_ctx: ResumeContext,
496    ) -> Result<Option<ResumeContext>, ExecutionError> {
497        let ResumeContext {
498            mut current_forest,
499            mut continuation_stack,
500            kernel,
501            mut package_debug_info,
502        } = resume_ctx;
503
504        let flow = self
505            .execute_impl_async(
506                &mut continuation_stack,
507                &mut current_forest,
508                &kernel,
509                host,
510                &mut NoopTracer,
511                &StepStopper,
512                &mut package_debug_info,
513            )
514            .await;
515        Self::resume_context_from_flow(
516            flow,
517            continuation_stack,
518            current_forest,
519            kernel,
520            package_debug_info,
521        )
522    }
523
524    /// Async variant of [`Self::step_with_package_debug_info_sync`].
525    #[cfg(any(test, feature = "testing"))]
526    #[inline(always)]
527    pub async fn step_with_package_debug_info(
528        &mut self,
529        host: &mut impl Host,
530        resume_ctx: ResumeContext,
531        package_debug_info: &PackageDebugInfo,
532    ) -> Result<Option<ResumeContext>, ExecutionError> {
533        let ResumeContext {
534            mut current_forest,
535            mut continuation_stack,
536            kernel,
537            package_debug_info: mut active_package_debug_info,
538        } = resume_ctx;
539        Self::ensure_source_aware_step_context(
540            &mut continuation_stack,
541            &mut active_package_debug_info,
542            package_debug_info,
543        )?;
544
545        let flow = self
546            .execute_impl_async(
547                &mut continuation_stack,
548                &mut current_forest,
549                &kernel,
550                host,
551                &mut NoopTracer,
552                &StepStopper,
553                &mut active_package_debug_info,
554            )
555            .await;
556        Self::resume_context_from_flow(
557            flow,
558            continuation_stack,
559            current_forest,
560            kernel,
561            active_package_debug_info,
562        )
563    }
564
565    /// Pairs execution output with the trace inputs captured by the tracer.
566    #[inline(always)]
567    fn trace_build_inputs_from_parts(
568        program: &Program,
569        execution_output: ExecutionOutput,
570        tracer: ExecutionTracer,
571    ) -> TraceBuildInputs {
572        TraceBuildInputs::from_execution(
573            program,
574            execution_output,
575            tracer.into_trace_generation_context(),
576        )
577    }
578
579    fn source_aware_continuation_stack(
580        program: &Program,
581        package_debug_info: &PackageDebugInfo,
582        entrypoint_source_node_id: Option<DebugSourceNodeId>,
583    ) -> Result<ContinuationStack<Arc<MastForest>>, ExecutionError> {
584        if let Some(source_node_id) = entrypoint_source_node_id {
585            let Some(source_node) = package_debug_info.source_node(source_node_id) else {
586                return Err(ExecutionError::Internal(
587                    "package debug source graph is missing the entrypoint source node",
588                ));
589            };
590            if source_node.exec_node != program.entrypoint() {
591                return Err(ExecutionError::Internal(
592                    "package debug entrypoint source node does not match the program entrypoint",
593                ));
594            }
595
596            return Ok(ContinuationStack::new_with_source_node_id(program, source_node_id));
597        }
598
599        let Some(source_node_id) = package_debug_info
600            .unique_source_root_for_exec_node(program.entrypoint())
601            .map_err(|_| {
602                ExecutionError::Internal(
603                    "package debug source graph has ambiguous or malformed entrypoint roots",
604                )
605            })?
606        else {
607            return Ok(ContinuationStack::new_with_optional_source_node_id(program, None));
608        };
609
610        Ok(ContinuationStack::new_with_source_node_id(program, source_node_id))
611    }
612
613    #[cfg(any(test, feature = "testing"))]
614    fn source_aware_resume_context(
615        &mut self,
616        program: &Program,
617        package_debug_info: &PackageDebugInfo,
618        entrypoint_source_node_id: Option<DebugSourceNodeId>,
619    ) -> Result<ResumeContext, ExecutionError> {
620        self.advice
621            .extend_map(program.mast_forest().advice_map())
622            .map_exec_err_no_ctx()?;
623
624        Ok(ResumeContext {
625            current_forest: program.mast_forest().clone(),
626            continuation_stack: Self::source_aware_continuation_stack(
627                program,
628                package_debug_info,
629                entrypoint_source_node_id,
630            )?,
631            kernel: program.kernel().clone(),
632            package_debug_info: Some(Arc::new(package_debug_info.clone())),
633        })
634    }
635
636    #[cfg(any(test, feature = "testing"))]
637    fn ensure_source_aware_step_context(
638        continuation_stack: &mut ContinuationStack<Arc<MastForest>>,
639        package_debug_info: &mut Option<Arc<PackageDebugInfo>>,
640        supplied_package_debug_info: &PackageDebugInfo,
641    ) -> Result<(), ExecutionError> {
642        if package_debug_info.is_none() {
643            *package_debug_info = Some(Arc::new(supplied_package_debug_info.clone()));
644        }
645
646        if !continuation_stack.tracks_source_nodes() {
647            let source_node_id = Self::source_root_for_next_continuation(
648                continuation_stack,
649                package_debug_info.as_deref().expect("package debug info was just initialized"),
650            )?;
651            continuation_stack.start_tracking_source_nodes(source_node_id);
652        }
653
654        Ok(())
655    }
656
657    #[cfg(any(test, feature = "testing"))]
658    fn source_root_for_next_continuation(
659        continuation_stack: &ContinuationStack<Arc<MastForest>>,
660        package_debug_info: &PackageDebugInfo,
661    ) -> Result<Option<DebugSourceNodeId>, ExecutionError> {
662        let Some((continuation, _)) = continuation_stack.peek_continuation_with_source_node_id()
663        else {
664            return Ok(None);
665        };
666
667        let Some(exec_node) = continuation.exec_node() else {
668            return Ok(None);
669        };
670
671        package_debug_info.unique_source_root_for_exec_node(exec_node).map_err(|_| {
672            ExecutionError::Internal(
673                "package debug source graph has ambiguous or malformed continuation roots",
674            )
675        })
676    }
677
678    /// Converts a step-wise execution result into the next resume context, if execution stopped.
679    #[inline(always)]
680    fn resume_context_from_flow(
681        flow: ControlFlow<BreakReason<Arc<MastForest>>, StackOutputs>,
682        mut continuation_stack: ContinuationStack<Arc<MastForest>>,
683        current_forest: Arc<MastForest>,
684        kernel: Kernel,
685        package_debug_info: Option<Arc<PackageDebugInfo>>,
686    ) -> Result<Option<ResumeContext>, ExecutionError> {
687        match flow {
688            ControlFlow::Continue(_) => Ok(None),
689            ControlFlow::Break(break_reason) => match break_reason {
690                BreakReason::Err(err) => Err(err),
691                BreakReason::Stopped(maybe_continuation) => {
692                    if let Some((continuation, source_node_id)) = maybe_continuation {
693                        continuation_stack.push_with_source_node_id(continuation, source_node_id);
694                    }
695
696                    Ok(Some(ResumeContext {
697                        current_forest,
698                        continuation_stack,
699                        kernel,
700                        package_debug_info,
701                    }))
702                },
703            },
704        }
705    }
706
707    /// Materializes the current stack as public outputs without consuming the processor.
708    #[inline(always)]
709    fn current_stack_outputs(&self) -> StackOutputs {
710        StackOutputs::new(
711            &self.stack[self.stack_bot_idx..self.stack_top_idx]
712                .iter()
713                .rev()
714                .copied()
715                .collect::<Vec<_>>(),
716        )
717        .unwrap()
718    }
719
720    /// Executes the given program with the provided tracer and returns the stack outputs.
721    ///
722    /// This function takes a `&mut self` (compared to `self` for the public sync execution
723    /// methods) so that the processor state may be accessed after execution. Reusing the same
724    /// processor for a second program is incorrect. This is mainly meant to be used in tests.
725    fn execute_impl<S, T>(
726        &mut self,
727        continuation_stack: &mut ContinuationStack<Arc<MastForest>>,
728        current_forest: &mut Arc<MastForest>,
729        kernel: &Kernel,
730        host: &mut impl SyncHost,
731        tracer: &mut T,
732        stopper: &S,
733        package_debug_info: &mut Option<Arc<PackageDebugInfo>>,
734    ) -> ControlFlow<BreakReason<Arc<MastForest>>, StackOutputs>
735    where
736        S: Stopper<Processor = Self, Forest = Arc<MastForest>>,
737        T: Tracer<Processor = Self, Forest = Arc<MastForest>>,
738    {
739        while let ControlFlow::Break(internal_break_reason) = execute_impl(
740            self,
741            continuation_stack,
742            current_forest,
743            kernel,
744            host,
745            tracer,
746            stopper,
747            package_debug_info,
748        ) {
749            let current_package_debug_info = package_debug_info.as_deref();
750            let source_aware_execution =
751                current_package_debug_info.is_some() || continuation_stack.tracks_source_nodes();
752            match internal_break_reason {
753                InternalBreakReason::User(break_reason) => return ControlFlow::Break(break_reason),
754                InternalBreakReason::Emit { op_idx, continuation, source_node_id } => {
755                    self.op_emit_sync(host, op_idx, current_package_debug_info, source_node_id)?;
756
757                    finish_emit_op_execution(
758                        continuation,
759                        source_node_id,
760                        self,
761                        continuation_stack,
762                        current_forest,
763                        tracer,
764                        stopper,
765                    )?;
766                },
767                InternalBreakReason::LoadMastForestFromDyn { callee_hash, source_node_id } => {
768                    let (root_id, new_forest, new_package_debug_info, new_source_node_id) =
769                        match self.load_mast_forest_sync(
770                            callee_hash,
771                            host,
772                            current_package_debug_info,
773                            source_node_id,
774                            source_aware_execution,
775                        ) {
776                            Ok(result) => result,
777                            Err(err) => return ControlFlow::Break(BreakReason::Err(err)),
778                        };
779
780                    finish_load_mast_forest_from_dyn_start(
781                        root_id,
782                        new_forest,
783                        new_package_debug_info,
784                        new_source_node_id,
785                        self,
786                        current_forest,
787                        package_debug_info,
788                        continuation_stack,
789                        tracer,
790                        stopper,
791                    )?;
792                },
793                InternalBreakReason::LoadMastForestFromExternal {
794                    external_node_id,
795                    procedure_hash,
796                    source_node_id,
797                } => {
798                    let (root_id, new_forest, new_package_debug_info, new_source_node_id) =
799                        match self.load_mast_forest_sync(
800                            procedure_hash,
801                            host,
802                            current_package_debug_info,
803                            source_node_id,
804                            source_aware_execution,
805                        ) {
806                            Ok(result) => result,
807                            Err(err) => {
808                                let maybe_enriched_err = maybe_use_caller_error_context(
809                                    err,
810                                    continuation_stack,
811                                    current_package_debug_info,
812                                    host,
813                                );
814                                return ControlFlow::Break(BreakReason::Err(maybe_enriched_err));
815                            },
816                        };
817
818                    finish_load_mast_forest_from_external(
819                        root_id,
820                        new_forest,
821                        new_package_debug_info,
822                        new_source_node_id,
823                        external_node_id,
824                        current_forest,
825                        package_debug_info,
826                        continuation_stack,
827                        tracer,
828                    )?;
829                },
830            }
831        }
832
833        match StackOutputs::new(
834            &self.stack[self.stack_bot_idx..self.stack_top_idx]
835                .iter()
836                .rev()
837                .copied()
838                .collect::<Vec<_>>(),
839        ) {
840            Ok(stack_outputs) => ControlFlow::Continue(stack_outputs),
841            Err(_) => ControlFlow::Break(BreakReason::Err(ExecutionError::OutputStackOverflow(
842                self.stack_top_idx - self.stack_bot_idx - MIN_STACK_DEPTH,
843            ))),
844        }
845    }
846
847    async fn execute_impl_async<S, T>(
848        &mut self,
849        continuation_stack: &mut ContinuationStack<Arc<MastForest>>,
850        current_forest: &mut Arc<MastForest>,
851        kernel: &Kernel,
852        host: &mut impl Host,
853        tracer: &mut T,
854        stopper: &S,
855        package_debug_info: &mut Option<Arc<PackageDebugInfo>>,
856    ) -> ControlFlow<BreakReason<Arc<MastForest>>, StackOutputs>
857    where
858        S: Stopper<Processor = Self, Forest = Arc<MastForest>>,
859        T: Tracer<Processor = Self, Forest = Arc<MastForest>>,
860    {
861        while let ControlFlow::Break(internal_break_reason) = execute_impl(
862            self,
863            continuation_stack,
864            current_forest,
865            kernel,
866            host,
867            tracer,
868            stopper,
869            package_debug_info,
870        ) {
871            let current_package_debug_info = package_debug_info.as_deref();
872            let source_aware_execution =
873                current_package_debug_info.is_some() || continuation_stack.tracks_source_nodes();
874            match internal_break_reason {
875                InternalBreakReason::User(break_reason) => return ControlFlow::Break(break_reason),
876                InternalBreakReason::Emit { op_idx, continuation, source_node_id } => {
877                    self.op_emit(host, op_idx, current_package_debug_info, source_node_id).await?;
878
879                    finish_emit_op_execution(
880                        continuation,
881                        source_node_id,
882                        self,
883                        continuation_stack,
884                        current_forest,
885                        tracer,
886                        stopper,
887                    )?;
888                },
889                InternalBreakReason::LoadMastForestFromDyn { callee_hash, source_node_id } => {
890                    let (root_id, new_forest, new_package_debug_info, new_source_node_id) =
891                        match self
892                            .load_mast_forest(
893                                callee_hash,
894                                host,
895                                current_package_debug_info,
896                                source_node_id,
897                                source_aware_execution,
898                            )
899                            .await
900                        {
901                            Ok(result) => result,
902                            Err(err) => return ControlFlow::Break(BreakReason::Err(err)),
903                        };
904
905                    finish_load_mast_forest_from_dyn_start(
906                        root_id,
907                        new_forest,
908                        new_package_debug_info,
909                        new_source_node_id,
910                        self,
911                        current_forest,
912                        package_debug_info,
913                        continuation_stack,
914                        tracer,
915                        stopper,
916                    )?;
917                },
918                InternalBreakReason::LoadMastForestFromExternal {
919                    external_node_id,
920                    procedure_hash,
921                    source_node_id,
922                } => {
923                    let (root_id, new_forest, new_package_debug_info, new_source_node_id) =
924                        match self
925                            .load_mast_forest(
926                                procedure_hash,
927                                host,
928                                current_package_debug_info,
929                                source_node_id,
930                                source_aware_execution,
931                            )
932                            .await
933                        {
934                            Ok(result) => result,
935                            Err(err) => {
936                                let maybe_enriched_err = maybe_use_caller_error_context(
937                                    err,
938                                    continuation_stack,
939                                    current_package_debug_info,
940                                    host,
941                                );
942                                return ControlFlow::Break(BreakReason::Err(maybe_enriched_err));
943                            },
944                        };
945
946                    finish_load_mast_forest_from_external(
947                        root_id,
948                        new_forest,
949                        new_package_debug_info,
950                        new_source_node_id,
951                        external_node_id,
952                        current_forest,
953                        package_debug_info,
954                        continuation_stack,
955                        tracer,
956                    )?;
957                },
958            }
959        }
960
961        match StackOutputs::new(
962            &self.stack[self.stack_bot_idx..self.stack_top_idx]
963                .iter()
964                .rev()
965                .copied()
966                .collect::<Vec<_>>(),
967        ) {
968            Ok(stack_outputs) => ControlFlow::Continue(stack_outputs),
969            Err(_) => ControlFlow::Break(BreakReason::Err(ExecutionError::OutputStackOverflow(
970                self.stack_top_idx - self.stack_bot_idx - MIN_STACK_DEPTH,
971            ))),
972        }
973    }
974
975    // HELPERS
976    // ------------------------------------------------------------------------------------------
977
978    fn load_mast_forest_sync(
979        &mut self,
980        node_digest: Word,
981        host: &mut impl SyncHost,
982        package_debug_info: Option<&PackageDebugInfo>,
983        source_node_id: Option<DebugSourceNodeId>,
984        source_aware_execution: bool,
985    ) -> Result<
986        (
987            MastNodeId,
988            Arc<MastForest>,
989            Option<Arc<PackageDebugInfo>>,
990            Option<DebugSourceNodeId>,
991        ),
992        ExecutionError,
993    > {
994        let loaded_mast_forest = host.get_mast_forest(&node_digest).ok_or_else(|| {
995            match (package_debug_info, source_node_id) {
996                (Some(debug_info), Some(source_node_id)) => {
997                    crate::errors::procedure_not_found_with_package_source_context(
998                        node_digest,
999                        PackageSourceDebugContext::new(debug_info, source_node_id),
1000                        host,
1001                    )
1002                },
1003                _ => crate::errors::procedure_not_found_with_context(node_digest),
1004            }
1005        })?;
1006        let mast_forest = loaded_mast_forest.mast_forest().clone();
1007
1008        let root_id = mast_forest.find_procedure_root(node_digest).ok_or_else(|| {
1009            let context = match (package_debug_info, source_node_id) {
1010                (Some(debug_info), Some(source_node_id)) => {
1011                    Some(PackageSourceDebugContext::new(debug_info, source_node_id))
1012                },
1013                _ => None,
1014            };
1015            malformed_mast_forest_with_context(node_digest, context, host)
1016        })?;
1017
1018        self.advice.extend_map(mast_forest.advice_map()).map_exec_err()?;
1019        let (loaded_package_debug_info, loaded_source_node_id) =
1020            Self::loaded_package_source_context(
1021                &loaded_mast_forest,
1022                root_id,
1023                source_aware_execution,
1024            )?;
1025
1026        Ok((root_id, mast_forest, loaded_package_debug_info, loaded_source_node_id))
1027    }
1028
1029    async fn load_mast_forest(
1030        &mut self,
1031        node_digest: Word,
1032        host: &mut impl Host,
1033        package_debug_info: Option<&PackageDebugInfo>,
1034        source_node_id: Option<DebugSourceNodeId>,
1035        source_aware_execution: bool,
1036    ) -> Result<
1037        (
1038            MastNodeId,
1039            Arc<MastForest>,
1040            Option<Arc<PackageDebugInfo>>,
1041            Option<DebugSourceNodeId>,
1042        ),
1043        ExecutionError,
1044    > {
1045        let loaded_mast_forest = if let Some(mast_forest) = host.get_mast_forest(&node_digest).await
1046        {
1047            mast_forest
1048        } else {
1049            return Err(match (package_debug_info, source_node_id) {
1050                (Some(debug_info), Some(source_node_id)) => {
1051                    crate::errors::procedure_not_found_with_package_source_context(
1052                        node_digest,
1053                        PackageSourceDebugContext::new(debug_info, source_node_id),
1054                        host,
1055                    )
1056                },
1057                _ => crate::errors::procedure_not_found_with_context(node_digest),
1058            });
1059        };
1060        let mast_forest = loaded_mast_forest.mast_forest().clone();
1061
1062        let root_id = mast_forest.find_procedure_root(node_digest).ok_or_else(|| {
1063            let context = match (package_debug_info, source_node_id) {
1064                (Some(debug_info), Some(source_node_id)) => {
1065                    Some(PackageSourceDebugContext::new(debug_info, source_node_id))
1066                },
1067                _ => None,
1068            };
1069            malformed_mast_forest_with_context(node_digest, context, host)
1070        })?;
1071
1072        self.advice.extend_map(mast_forest.advice_map()).map_exec_err()?;
1073        let (loaded_package_debug_info, loaded_source_node_id) =
1074            Self::loaded_package_source_context(
1075                &loaded_mast_forest,
1076                root_id,
1077                source_aware_execution,
1078            )?;
1079
1080        Ok((root_id, mast_forest, loaded_package_debug_info, loaded_source_node_id))
1081    }
1082
1083    fn loaded_package_source_context(
1084        loaded_mast_forest: &LoadedMastForest,
1085        root_id: MastNodeId,
1086        source_aware_execution: bool,
1087    ) -> Result<(Option<Arc<PackageDebugInfo>>, Option<DebugSourceNodeId>), ExecutionError> {
1088        if !source_aware_execution {
1089            return Ok((None, None));
1090        }
1091
1092        let Some(package_debug_info) = loaded_mast_forest
1093            .package_debug_info()
1094            .map_err(|_| ExecutionError::Internal("loaded package debug info is malformed"))?
1095        else {
1096            return Ok((None, None));
1097        };
1098
1099        let source_node_id = match package_debug_info.unique_source_root_for_exec_node(root_id) {
1100            Ok(source_node_id) => source_node_id,
1101            Err(DebugSourceGraphLookupError::AmbiguousRoot { .. }) => None,
1102            Err(_) => {
1103                return Err(ExecutionError::Internal(
1104                    "loaded package debug source graph has malformed entrypoint roots",
1105                ));
1106            },
1107        };
1108
1109        Ok((Some(package_debug_info), source_node_id))
1110    }
1111
1112    /// Executes the given program synchronously one step at a time.
1113    pub fn execute_by_step_sync(
1114        mut self,
1115        program: &Program,
1116        host: &mut impl SyncHost,
1117    ) -> Result<StackOutputs, ExecutionError> {
1118        let mut current_resume_ctx = self.get_initial_resume_context(program)?;
1119
1120        loop {
1121            match self.step_sync(host, current_resume_ctx)? {
1122                Some(next_resume_ctx) => {
1123                    current_resume_ctx = next_resume_ctx;
1124                },
1125                None => break Ok(self.current_stack_outputs()),
1126            }
1127        }
1128    }
1129
1130    /// Executes the given program synchronously one step at a time with package-owned source/debug
1131    /// context.
1132    #[cfg(any(test, feature = "testing"))]
1133    pub fn execute_by_step_with_package_debug_info_sync(
1134        mut self,
1135        program: &Program,
1136        package_debug_info: &PackageDebugInfo,
1137        host: &mut impl SyncHost,
1138    ) -> Result<StackOutputs, ExecutionError> {
1139        let mut current_resume_ctx =
1140            self.source_aware_resume_context(program, package_debug_info, None)?;
1141
1142        loop {
1143            match self.step_with_package_debug_info_sync(
1144                host,
1145                current_resume_ctx,
1146                package_debug_info,
1147            )? {
1148                Some(next_resume_ctx) => {
1149                    current_resume_ctx = next_resume_ctx;
1150                },
1151                None => break Ok(self.current_stack_outputs()),
1152            }
1153        }
1154    }
1155
1156    /// Executes the given program synchronously one step at a time with package-owned source/debug
1157    /// context rooted at `entrypoint_source_node_id`.
1158    #[cfg(any(test, feature = "testing"))]
1159    pub fn execute_by_step_with_package_debug_info_at_source_node_sync(
1160        mut self,
1161        program: &Program,
1162        package_debug_info: &PackageDebugInfo,
1163        entrypoint_source_node_id: DebugSourceNodeId,
1164        host: &mut impl SyncHost,
1165    ) -> Result<StackOutputs, ExecutionError> {
1166        let mut current_resume_ctx = self.source_aware_resume_context(
1167            program,
1168            package_debug_info,
1169            Some(entrypoint_source_node_id),
1170        )?;
1171
1172        loop {
1173            match self.step_with_package_debug_info_sync(
1174                host,
1175                current_resume_ctx,
1176                package_debug_info,
1177            )? {
1178                Some(next_resume_ctx) => {
1179                    current_resume_ctx = next_resume_ctx;
1180                },
1181                None => break Ok(self.current_stack_outputs()),
1182            }
1183        }
1184    }
1185
1186    /// Async variant of [`Self::execute_by_step_sync`].
1187    #[inline(always)]
1188    pub async fn execute_by_step(
1189        mut self,
1190        program: &Program,
1191        host: &mut impl Host,
1192    ) -> Result<StackOutputs, ExecutionError> {
1193        let mut current_resume_ctx = self.get_initial_resume_context(program)?;
1194        let mut processor = self;
1195
1196        loop {
1197            match processor.step(host, current_resume_ctx).await? {
1198                Some(next_resume_ctx) => {
1199                    current_resume_ctx = next_resume_ctx;
1200                },
1201                None => break Ok(processor.current_stack_outputs()),
1202            }
1203        }
1204    }
1205
1206    /// Async variant of [`Self::execute_by_step_with_package_debug_info_sync`].
1207    #[cfg(any(test, feature = "testing"))]
1208    #[inline(always)]
1209    pub async fn execute_by_step_with_package_debug_info(
1210        mut self,
1211        program: &Program,
1212        package_debug_info: &PackageDebugInfo,
1213        host: &mut impl Host,
1214    ) -> Result<StackOutputs, ExecutionError> {
1215        let mut current_resume_ctx =
1216            self.source_aware_resume_context(program, package_debug_info, None)?;
1217        let mut processor = self;
1218
1219        loop {
1220            match processor
1221                .step_with_package_debug_info(host, current_resume_ctx, package_debug_info)
1222                .await?
1223            {
1224                Some(next_resume_ctx) => {
1225                    current_resume_ctx = next_resume_ctx;
1226                },
1227                None => break Ok(processor.current_stack_outputs()),
1228            }
1229        }
1230    }
1231
1232    /// Async variant of
1233    /// [`Self::execute_by_step_with_package_debug_info_at_source_node_sync`].
1234    #[cfg(any(test, feature = "testing"))]
1235    #[inline(always)]
1236    pub async fn execute_by_step_with_package_debug_info_at_source_node(
1237        mut self,
1238        program: &Program,
1239        package_debug_info: &PackageDebugInfo,
1240        entrypoint_source_node_id: DebugSourceNodeId,
1241        host: &mut impl Host,
1242    ) -> Result<StackOutputs, ExecutionError> {
1243        let mut current_resume_ctx = self.source_aware_resume_context(
1244            program,
1245            package_debug_info,
1246            Some(entrypoint_source_node_id),
1247        )?;
1248        let mut processor = self;
1249
1250        loop {
1251            match processor
1252                .step_with_package_debug_info(host, current_resume_ctx, package_debug_info)
1253                .await?
1254            {
1255                Some(next_resume_ctx) => {
1256                    current_resume_ctx = next_resume_ctx;
1257                },
1258                None => break Ok(processor.current_stack_outputs()),
1259            }
1260        }
1261    }
1262
1263    /// Similar to [`Self::execute_sync`], but allows mutable access to the processor.
1264    ///
1265    /// This is mainly meant to be used in tests.
1266    #[cfg(any(test, feature = "testing"))]
1267    pub fn execute_mut_sync(
1268        &mut self,
1269        program: &Program,
1270        host: &mut impl SyncHost,
1271    ) -> Result<StackOutputs, ExecutionError> {
1272        let mut continuation_stack = ContinuationStack::new(program);
1273        let mut current_forest = program.mast_forest().clone();
1274        let mut package_debug_info = None;
1275
1276        self.advice.extend_map(current_forest.advice_map()).map_exec_err_no_ctx()?;
1277
1278        let flow = self.execute_impl(
1279            &mut continuation_stack,
1280            &mut current_forest,
1281            program.kernel(),
1282            host,
1283            &mut NoopTracer,
1284            &NeverStopper,
1285            &mut package_debug_info,
1286        );
1287        Self::stack_result_from_flow(flow)
1288    }
1289
1290    /// Async variant of [`Self::execute_mut_sync`].
1291    #[cfg(any(test, feature = "testing"))]
1292    #[inline(always)]
1293    pub async fn execute_mut(
1294        &mut self,
1295        program: &Program,
1296        host: &mut impl Host,
1297    ) -> Result<StackOutputs, ExecutionError> {
1298        let mut continuation_stack = ContinuationStack::new(program);
1299        let mut current_forest = program.mast_forest().clone();
1300        let mut package_debug_info = None;
1301
1302        self.advice.extend_map(current_forest.advice_map()).map_exec_err_no_ctx()?;
1303
1304        let flow = self
1305            .execute_impl_async(
1306                &mut continuation_stack,
1307                &mut current_forest,
1308                program.kernel(),
1309                host,
1310                &mut NoopTracer,
1311                &NeverStopper,
1312                &mut package_debug_info,
1313            )
1314            .await;
1315        Self::stack_result_from_flow(flow)
1316    }
1317}