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 tracing::instrument;
10
11use super::{
12    FastProcessor, NoopTracer,
13    external::maybe_use_caller_error_context,
14    step::{BreakReason, NeverStopper, ResumeContext, StepStopper},
15};
16use crate::{
17    ExecutionError, ExecutionOutput, Host, Stopper, SyncHost, TraceBuildInputs,
18    continuation_stack::ContinuationStack,
19    errors::{MapExecErr, MapExecErrNoCtx, OperationError},
20    execution::{
21        InternalBreakReason, execute_impl, finish_emit_op_execution,
22        finish_load_mast_forest_from_dyn_start, finish_load_mast_forest_from_external,
23    },
24    trace::execution_tracer::ExecutionTracer,
25    tracer::Tracer,
26};
27
28impl FastProcessor {
29    // EXECUTE
30    // -------------------------------------------------------------------------------------------
31
32    /// Executes the given program synchronously and returns the execution output.
33    pub fn execute_sync(
34        self,
35        program: &Program,
36        host: &mut impl SyncHost,
37    ) -> Result<ExecutionOutput, ExecutionError> {
38        self.execute_with_tracer_sync(program, host, &mut NoopTracer)
39    }
40
41    /// Async variant of [`Self::execute_sync`] for hosts that need async callbacks.
42    #[inline(always)]
43    pub async fn execute(
44        self,
45        program: &Program,
46        host: &mut impl Host,
47    ) -> Result<ExecutionOutput, ExecutionError> {
48        self.execute_with_tracer(program, host, &mut NoopTracer).await
49    }
50
51    /// Executes the given program synchronously and returns the bundled trace inputs required by
52    /// [`crate::trace::build_trace`].
53    ///
54    /// # Example
55    /// ```
56    /// use miden_assembly::Assembler;
57    /// use miden_processor::{DefaultHost, FastProcessor, StackInputs};
58    ///
59    /// let program = Assembler::default().assemble_program("begin push.1 drop end").unwrap();
60    /// let mut host = DefaultHost::default();
61    ///
62    /// let trace_inputs = FastProcessor::new(StackInputs::default())
63    ///     .execute_trace_inputs_sync(&program, &mut host)
64    ///     .unwrap();
65    /// let trace = miden_processor::trace::build_trace(trace_inputs).unwrap();
66    ///
67    /// assert_eq!(*trace.program_hash(), program.hash());
68    /// ```
69    #[instrument(name = "execute_trace_inputs_sync", skip_all)]
70    pub fn execute_trace_inputs_sync(
71        self,
72        program: &Program,
73        host: &mut impl SyncHost,
74    ) -> Result<TraceBuildInputs, ExecutionError> {
75        let mut tracer = ExecutionTracer::new(self.options.core_trace_fragment_size());
76        let execution_output = self.execute_with_tracer_sync(program, host, &mut tracer)?;
77        Ok(Self::trace_build_inputs_from_parts(program, execution_output, tracer))
78    }
79
80    /// Async variant of [`Self::execute_trace_inputs_sync`] for async hosts.
81    #[inline(always)]
82    #[instrument(name = "execute_trace_inputs", skip_all)]
83    pub async fn execute_trace_inputs(
84        self,
85        program: &Program,
86        host: &mut impl Host,
87    ) -> Result<TraceBuildInputs, ExecutionError> {
88        let mut tracer = ExecutionTracer::new(self.options.core_trace_fragment_size());
89        let execution_output = self.execute_with_tracer(program, host, &mut tracer).await?;
90        Ok(Self::trace_build_inputs_from_parts(program, execution_output, tracer))
91    }
92
93    /// Executes the given program with the provided tracer using an async host.
94    pub async fn execute_with_tracer<T>(
95        mut self,
96        program: &Program,
97        host: &mut impl Host,
98        tracer: &mut T,
99    ) -> Result<ExecutionOutput, ExecutionError>
100    where
101        T: Tracer<Processor = Self>,
102    {
103        let mut continuation_stack = ContinuationStack::new(program);
104        let mut current_forest = program.mast_forest().clone();
105
106        self.advice.extend_map(current_forest.advice_map()).map_exec_err_no_ctx()?;
107        let flow = self
108            .execute_impl_async(
109                &mut continuation_stack,
110                &mut current_forest,
111                program.kernel(),
112                host,
113                tracer,
114                &NeverStopper,
115            )
116            .await;
117        Self::execution_result_from_flow(flow, self)
118    }
119
120    /// Executes the given program with the provided tracer using a sync host.
121    pub fn execute_with_tracer_sync<T>(
122        mut self,
123        program: &Program,
124        host: &mut impl SyncHost,
125        tracer: &mut T,
126    ) -> Result<ExecutionOutput, ExecutionError>
127    where
128        T: Tracer<Processor = Self>,
129    {
130        let mut continuation_stack = ContinuationStack::new(program);
131        let mut current_forest = program.mast_forest().clone();
132
133        self.advice.extend_map(current_forest.advice_map()).map_exec_err_no_ctx()?;
134        let flow = self.execute_impl(
135            &mut continuation_stack,
136            &mut current_forest,
137            program.kernel(),
138            host,
139            tracer,
140            &NeverStopper,
141        );
142        Self::execution_result_from_flow(flow, self)
143    }
144
145    /// Executes a single clock cycle synchronously.
146    pub fn step_sync(
147        &mut self,
148        host: &mut impl SyncHost,
149        resume_ctx: ResumeContext,
150    ) -> Result<Option<ResumeContext>, ExecutionError> {
151        let ResumeContext {
152            mut current_forest,
153            mut continuation_stack,
154            kernel,
155        } = resume_ctx;
156
157        let flow = self.execute_impl(
158            &mut continuation_stack,
159            &mut current_forest,
160            &kernel,
161            host,
162            &mut NoopTracer,
163            &StepStopper,
164        );
165        Self::resume_context_from_flow(flow, continuation_stack, current_forest, kernel)
166    }
167
168    /// Async variant of [`Self::step_sync`].
169    #[inline(always)]
170    pub async fn step(
171        &mut self,
172        host: &mut impl Host,
173        resume_ctx: ResumeContext,
174    ) -> Result<Option<ResumeContext>, ExecutionError> {
175        let ResumeContext {
176            mut current_forest,
177            mut continuation_stack,
178            kernel,
179        } = resume_ctx;
180
181        let flow = self
182            .execute_impl_async(
183                &mut continuation_stack,
184                &mut current_forest,
185                &kernel,
186                host,
187                &mut NoopTracer,
188                &StepStopper,
189            )
190            .await;
191        Self::resume_context_from_flow(flow, continuation_stack, current_forest, kernel)
192    }
193
194    /// Pairs execution output with the trace inputs captured by the tracer.
195    #[inline(always)]
196    fn trace_build_inputs_from_parts(
197        program: &Program,
198        execution_output: ExecutionOutput,
199        tracer: ExecutionTracer,
200    ) -> TraceBuildInputs {
201        TraceBuildInputs::from_execution(
202            program,
203            execution_output,
204            tracer.into_trace_generation_context(),
205        )
206    }
207
208    /// Converts a step-wise execution result into the next resume context, if execution stopped.
209    #[inline(always)]
210    fn resume_context_from_flow(
211        flow: ControlFlow<BreakReason, StackOutputs>,
212        mut continuation_stack: ContinuationStack,
213        current_forest: Arc<MastForest>,
214        kernel: Kernel,
215    ) -> Result<Option<ResumeContext>, ExecutionError> {
216        match flow {
217            ControlFlow::Continue(_) => Ok(None),
218            ControlFlow::Break(break_reason) => match break_reason {
219                BreakReason::Err(err) => Err(err),
220                BreakReason::Stopped(maybe_continuation) => {
221                    if let Some(continuation) = maybe_continuation {
222                        continuation_stack.push_continuation(continuation);
223                    }
224
225                    Ok(Some(ResumeContext {
226                        current_forest,
227                        continuation_stack,
228                        kernel,
229                    }))
230                },
231            },
232        }
233    }
234
235    /// Materializes the current stack as public outputs without consuming the processor.
236    #[inline(always)]
237    fn current_stack_outputs(&self) -> StackOutputs {
238        StackOutputs::new(
239            &self.stack[self.stack_bot_idx..self.stack_top_idx]
240                .iter()
241                .rev()
242                .copied()
243                .collect::<Vec<_>>(),
244        )
245        .unwrap()
246    }
247
248    /// Executes the given program with the provided tracer and returns the stack outputs.
249    ///
250    /// This function takes a `&mut self` (compared to `self` for the public sync execution
251    /// methods) so that the processor state may be accessed after execution. Reusing the same
252    /// processor for a second program is incorrect. This is mainly meant to be used in tests.
253    fn execute_impl<S, T>(
254        &mut self,
255        continuation_stack: &mut ContinuationStack,
256        current_forest: &mut Arc<MastForest>,
257        kernel: &Kernel,
258        host: &mut impl SyncHost,
259        tracer: &mut T,
260        stopper: &S,
261    ) -> ControlFlow<BreakReason, StackOutputs>
262    where
263        S: Stopper<Processor = Self>,
264        T: Tracer<Processor = Self>,
265    {
266        while let ControlFlow::Break(internal_break_reason) =
267            execute_impl(self, continuation_stack, current_forest, kernel, host, tracer, stopper)
268        {
269            match internal_break_reason {
270                InternalBreakReason::User(break_reason) => return ControlFlow::Break(break_reason),
271                InternalBreakReason::Emit {
272                    basic_block_node_id,
273                    op_idx,
274                    continuation,
275                } => {
276                    self.op_emit_sync(host, current_forest, basic_block_node_id, op_idx)?;
277
278                    finish_emit_op_execution(
279                        continuation,
280                        self,
281                        continuation_stack,
282                        current_forest,
283                        tracer,
284                        stopper,
285                    )?;
286                },
287                InternalBreakReason::LoadMastForestFromDyn { dyn_node_id, callee_hash } => {
288                    let (root_id, new_forest) = match self.load_mast_forest_sync(
289                        callee_hash,
290                        host,
291                        current_forest,
292                        dyn_node_id,
293                    ) {
294                        Ok(result) => result,
295                        Err(err) => return ControlFlow::Break(BreakReason::Err(err)),
296                    };
297
298                    finish_load_mast_forest_from_dyn_start(
299                        root_id,
300                        new_forest,
301                        self,
302                        current_forest,
303                        continuation_stack,
304                        tracer,
305                        stopper,
306                    )?;
307                },
308                InternalBreakReason::LoadMastForestFromExternal {
309                    external_node_id,
310                    procedure_hash,
311                } => {
312                    let (root_id, new_forest) = match self.load_mast_forest_sync(
313                        procedure_hash,
314                        host,
315                        current_forest,
316                        external_node_id,
317                    ) {
318                        Ok(result) => result,
319                        Err(err) => {
320                            let maybe_enriched_err = maybe_use_caller_error_context(
321                                err,
322                                current_forest,
323                                continuation_stack,
324                                host,
325                            );
326
327                            return ControlFlow::Break(BreakReason::Err(maybe_enriched_err));
328                        },
329                    };
330
331                    finish_load_mast_forest_from_external(
332                        root_id,
333                        new_forest,
334                        external_node_id,
335                        current_forest,
336                        continuation_stack,
337                        host,
338                        tracer,
339                    )?;
340                },
341            }
342        }
343
344        match StackOutputs::new(
345            &self.stack[self.stack_bot_idx..self.stack_top_idx]
346                .iter()
347                .rev()
348                .copied()
349                .collect::<Vec<_>>(),
350        ) {
351            Ok(stack_outputs) => ControlFlow::Continue(stack_outputs),
352            Err(_) => ControlFlow::Break(BreakReason::Err(ExecutionError::OutputStackOverflow(
353                self.stack_top_idx - self.stack_bot_idx - MIN_STACK_DEPTH,
354            ))),
355        }
356    }
357
358    async fn execute_impl_async<S, T>(
359        &mut self,
360        continuation_stack: &mut ContinuationStack,
361        current_forest: &mut Arc<MastForest>,
362        kernel: &Kernel,
363        host: &mut impl Host,
364        tracer: &mut T,
365        stopper: &S,
366    ) -> ControlFlow<BreakReason, StackOutputs>
367    where
368        S: Stopper<Processor = Self>,
369        T: Tracer<Processor = Self>,
370    {
371        while let ControlFlow::Break(internal_break_reason) =
372            execute_impl(self, continuation_stack, current_forest, kernel, host, tracer, stopper)
373        {
374            match internal_break_reason {
375                InternalBreakReason::User(break_reason) => return ControlFlow::Break(break_reason),
376                InternalBreakReason::Emit {
377                    basic_block_node_id,
378                    op_idx,
379                    continuation,
380                } => {
381                    self.op_emit(host, current_forest, basic_block_node_id, op_idx).await?;
382
383                    finish_emit_op_execution(
384                        continuation,
385                        self,
386                        continuation_stack,
387                        current_forest,
388                        tracer,
389                        stopper,
390                    )?;
391                },
392                InternalBreakReason::LoadMastForestFromDyn { dyn_node_id, callee_hash } => {
393                    let (root_id, new_forest) = match self
394                        .load_mast_forest(callee_hash, host, current_forest, dyn_node_id)
395                        .await
396                    {
397                        Ok(result) => result,
398                        Err(err) => return ControlFlow::Break(BreakReason::Err(err)),
399                    };
400
401                    finish_load_mast_forest_from_dyn_start(
402                        root_id,
403                        new_forest,
404                        self,
405                        current_forest,
406                        continuation_stack,
407                        tracer,
408                        stopper,
409                    )?;
410                },
411                InternalBreakReason::LoadMastForestFromExternal {
412                    external_node_id,
413                    procedure_hash,
414                } => {
415                    let (root_id, new_forest) = match self
416                        .load_mast_forest(procedure_hash, host, current_forest, external_node_id)
417                        .await
418                    {
419                        Ok(result) => result,
420                        Err(err) => {
421                            let maybe_enriched_err = maybe_use_caller_error_context(
422                                err,
423                                current_forest,
424                                continuation_stack,
425                                host,
426                            );
427
428                            return ControlFlow::Break(BreakReason::Err(maybe_enriched_err));
429                        },
430                    };
431
432                    finish_load_mast_forest_from_external(
433                        root_id,
434                        new_forest,
435                        external_node_id,
436                        current_forest,
437                        continuation_stack,
438                        host,
439                        tracer,
440                    )?;
441                },
442            }
443        }
444
445        match StackOutputs::new(
446            &self.stack[self.stack_bot_idx..self.stack_top_idx]
447                .iter()
448                .rev()
449                .copied()
450                .collect::<Vec<_>>(),
451        ) {
452            Ok(stack_outputs) => ControlFlow::Continue(stack_outputs),
453            Err(_) => ControlFlow::Break(BreakReason::Err(ExecutionError::OutputStackOverflow(
454                self.stack_top_idx - self.stack_bot_idx - MIN_STACK_DEPTH,
455            ))),
456        }
457    }
458
459    // HELPERS
460    // ------------------------------------------------------------------------------------------
461
462    fn load_mast_forest_sync(
463        &mut self,
464        node_digest: Word,
465        host: &mut impl SyncHost,
466        current_forest: &MastForest,
467        node_id: MastNodeId,
468    ) -> Result<(MastNodeId, Arc<MastForest>), ExecutionError> {
469        let mast_forest = host.get_mast_forest(&node_digest).ok_or_else(|| {
470            crate::errors::procedure_not_found_with_context(
471                node_digest,
472                current_forest,
473                node_id,
474                host,
475            )
476        })?;
477
478        let root_id = mast_forest.find_procedure_root(node_digest).ok_or_else(|| {
479            Err::<(), _>(OperationError::MalformedMastForestInHost { root_digest: node_digest })
480                .map_exec_err(current_forest, node_id, host)
481                .unwrap_err()
482        })?;
483
484        self.advice.extend_map(mast_forest.advice_map()).map_exec_err(
485            current_forest,
486            node_id,
487            host,
488        )?;
489
490        Ok((root_id, mast_forest))
491    }
492
493    async fn load_mast_forest(
494        &mut self,
495        node_digest: Word,
496        host: &mut impl Host,
497        current_forest: &MastForest,
498        node_id: MastNodeId,
499    ) -> Result<(MastNodeId, Arc<MastForest>), ExecutionError> {
500        let mast_forest = if let Some(mast_forest) = host.get_mast_forest(&node_digest).await {
501            mast_forest
502        } else {
503            return Err(crate::errors::procedure_not_found_with_context(
504                node_digest,
505                current_forest,
506                node_id,
507                host,
508            ));
509        };
510
511        let root_id = mast_forest.find_procedure_root(node_digest).ok_or_else(|| {
512            Err::<(), _>(OperationError::MalformedMastForestInHost { root_digest: node_digest })
513                .map_exec_err(current_forest, node_id, host)
514                .unwrap_err()
515        })?;
516
517        self.advice.extend_map(mast_forest.advice_map()).map_exec_err(
518            current_forest,
519            node_id,
520            host,
521        )?;
522
523        Ok((root_id, mast_forest))
524    }
525
526    /// Executes the given program synchronously one step at a time.
527    pub fn execute_by_step_sync(
528        mut self,
529        program: &Program,
530        host: &mut impl SyncHost,
531    ) -> Result<StackOutputs, ExecutionError> {
532        let mut current_resume_ctx = self.get_initial_resume_context(program).unwrap();
533
534        loop {
535            match self.step_sync(host, current_resume_ctx)? {
536                Some(next_resume_ctx) => {
537                    current_resume_ctx = next_resume_ctx;
538                },
539                None => break Ok(self.current_stack_outputs()),
540            }
541        }
542    }
543
544    /// Async variant of [`Self::execute_by_step_sync`].
545    #[inline(always)]
546    pub async fn execute_by_step(
547        mut self,
548        program: &Program,
549        host: &mut impl Host,
550    ) -> Result<StackOutputs, ExecutionError> {
551        let mut current_resume_ctx = self.get_initial_resume_context(program).unwrap();
552        let mut processor = self;
553
554        loop {
555            match processor.step(host, current_resume_ctx).await? {
556                Some(next_resume_ctx) => {
557                    current_resume_ctx = next_resume_ctx;
558                },
559                None => break Ok(processor.current_stack_outputs()),
560            }
561        }
562    }
563
564    /// Similar to [`Self::execute_sync`], but allows mutable access to the processor.
565    ///
566    /// This is mainly meant to be used in tests.
567    #[cfg(any(test, feature = "testing"))]
568    pub fn execute_mut_sync(
569        &mut self,
570        program: &Program,
571        host: &mut impl SyncHost,
572    ) -> Result<StackOutputs, ExecutionError> {
573        let mut continuation_stack = ContinuationStack::new(program);
574        let mut current_forest = program.mast_forest().clone();
575
576        self.advice.extend_map(current_forest.advice_map()).map_exec_err_no_ctx()?;
577
578        let flow = self.execute_impl(
579            &mut continuation_stack,
580            &mut current_forest,
581            program.kernel(),
582            host,
583            &mut NoopTracer,
584            &NeverStopper,
585        );
586        Self::stack_result_from_flow(flow)
587    }
588
589    /// Async variant of [`Self::execute_mut_sync`].
590    #[cfg(any(test, feature = "testing"))]
591    #[inline(always)]
592    pub async fn execute_mut(
593        &mut self,
594        program: &Program,
595        host: &mut impl Host,
596    ) -> Result<StackOutputs, ExecutionError> {
597        let mut continuation_stack = ContinuationStack::new(program);
598        let mut current_forest = program.mast_forest().clone();
599
600        self.advice.extend_map(current_forest.advice_map()).map_exec_err_no_ctx()?;
601
602        let flow = self
603            .execute_impl_async(
604                &mut continuation_stack,
605                &mut current_forest,
606                program.kernel(),
607                host,
608                &mut NoopTracer,
609                &NeverStopper,
610            )
611            .await;
612        Self::stack_result_from_flow(flow)
613    }
614}