Skip to main content

miden_processor/fast/
step.rs

1//! This module defines items relevant to controlling execution stopping conditions.
2
3use alloc::sync::Arc;
4use core::ops::ControlFlow;
5
6use miden_core::{mast::MastForest, program::Kernel};
7use miden_mast_package::debug_info::{DebugSourceNodeId, PackageDebugInfo};
8
9use crate::{
10    ExecutionError, FastProcessor, Stopper,
11    continuation_stack::{Continuation, ContinuationStack},
12};
13
14// RESUME CONTEXT
15// ===============================================================================================
16
17/// The context required to resume execution of a program from the last point at which it was
18/// stopped.
19#[derive(Debug)]
20pub struct ResumeContext {
21    pub(crate) current_forest: Arc<MastForest>,
22    pub(crate) continuation_stack: ContinuationStack<Arc<MastForest>>,
23    pub(crate) kernel: Kernel,
24    pub(crate) package_debug_info: Option<Arc<PackageDebugInfo>>,
25}
26
27impl ResumeContext {
28    /// Returns a reference to the continuation stack.
29    pub fn continuation_stack(&self) -> &ContinuationStack<Arc<MastForest>> {
30        &self.continuation_stack
31    }
32
33    /// Returns a reference to the MAST forest being currently executed.
34    pub fn current_forest(&self) -> &Arc<MastForest> {
35        &self.current_forest
36    }
37
38    /// Returns a reference to the debug info associated with the current forest, if available
39    pub fn debug_info(&self) -> Option<Arc<PackageDebugInfo>> {
40        self.package_debug_info.clone()
41    }
42
43    /// Returns a reference to the kernel being currently executed.
44    pub fn kernel(&self) -> &Kernel {
45        &self.kernel
46    }
47}
48
49// STOPPERS
50// ===============================================================================================
51
52/// A [`Stopper`] that never stops execution (except for returning an error when the maximum cycle
53/// count is exceeded).
54pub struct NeverStopper;
55
56impl Stopper for NeverStopper {
57    type Processor = FastProcessor;
58    type Forest = Arc<MastForest>;
59
60    #[inline(always)]
61    fn should_stop(
62        &self,
63        processor: &FastProcessor,
64        continuation_stack: &ContinuationStack<Arc<MastForest>>,
65        _continuation_after_stop: impl FnOnce() -> Option<(
66            Continuation<Arc<MastForest>>,
67            Option<DebugSourceNodeId>,
68        )>,
69    ) -> ControlFlow<BreakReason<Arc<MastForest>>> {
70        check_if_max_cycles_exceeded(processor)?;
71        check_if_continuation_stack_too_large(processor, continuation_stack)
72    }
73}
74
75/// A [`Stopper`] that always stops execution after each single step. An error is returned if the
76/// maximum cycle count is exceeded.
77pub struct StepStopper;
78
79impl Stopper for StepStopper {
80    type Processor = FastProcessor;
81    type Forest = Arc<MastForest>;
82
83    #[inline(always)]
84    fn should_stop(
85        &self,
86        processor: &FastProcessor,
87        continuation_stack: &ContinuationStack<Arc<MastForest>>,
88        continuation_after_stop: impl FnOnce() -> Option<(
89            Continuation<Arc<MastForest>>,
90            Option<DebugSourceNodeId>,
91        )>,
92    ) -> ControlFlow<BreakReason<Arc<MastForest>>> {
93        check_if_max_cycles_exceeded(processor)?;
94        check_if_continuation_stack_too_large(processor, continuation_stack)?;
95
96        ControlFlow::Break(BreakReason::Stopped(continuation_after_stop()))
97    }
98}
99
100/// Checks if the maximum cycle count has been exceeded, returning a `BreakReason::Err` if so.
101#[inline(always)]
102fn check_if_max_cycles_exceeded<F>(processor: &FastProcessor) -> ControlFlow<BreakReason<F>> {
103    if processor.clk > processor.options.max_cycles() as usize {
104        ControlFlow::Break(BreakReason::Err(ExecutionError::CycleLimitExceeded(
105            processor.options.max_cycles(),
106        )))
107    } else {
108        ControlFlow::Continue(())
109    }
110}
111
112/// Checks if the continuation stack size exceeds the maximum allowed, returning a
113/// `BreakReason::Err` if so.
114#[inline(always)]
115fn check_if_continuation_stack_too_large<F>(
116    processor: &FastProcessor,
117    continuation_stack: &ContinuationStack<F>,
118) -> ControlFlow<BreakReason<F>> {
119    if continuation_stack.len() > processor.options.max_num_continuations() {
120        ControlFlow::Break(BreakReason::Err(ExecutionError::Internal(
121            "continuation stack size exceeded the allowed maximum",
122        )))
123    } else {
124        ControlFlow::Continue(())
125    }
126}
127
128// BREAK REASON
129// ===============================================================================================
130
131/// The reason why execution was interrupted.
132#[derive(Debug)]
133pub enum BreakReason<F> {
134    /// An execution error occurred
135    Err(ExecutionError),
136    /// Execution was stopped by a [`Stopper`]. Provides the continuation to add to the continuation
137    /// stack before returning, if any. The mental model to have in mind when choosing the
138    /// continuation to add on a call to `FastProcessor::increment_clk()` is:
139    ///
140    /// "If execution is stopped here, does the current continuation stack properly encode the next
141    /// step of execution?"
142    ///
143    /// If yes, then `None` should be returned. If not, then the continuation that runs the next
144    /// step in `FastProcessor::execute_impl()` should be returned.
145    Stopped(Option<(Continuation<F>, Option<DebugSourceNodeId>)>),
146}