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, vec::Vec};
4use core::ops::ControlFlow;
5
6use miden_core::{mast::MastForest, program::KernelDescriptor};
7use miden_mast_package::debug_info::{DebugSourceNodeId, PackageDebugInfo};
8
9use crate::{
10    ExecutionError, FastProcessor, SourceInlineCallContext, 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: KernelDescriptor,
24    pub(crate) package_debug_info: Option<Arc<PackageDebugInfo>>,
25    pub(crate) inline_call_contexts: Vec<Option<SourceInlineCallContext>>,
26}
27
28impl ResumeContext {
29    /// Returns a reference to the continuation stack.
30    pub fn continuation_stack(&self) -> &ContinuationStack<Arc<MastForest>> {
31        &self.continuation_stack
32    }
33
34    /// Returns a reference to the MAST forest being currently executed.
35    pub fn current_forest(&self) -> &Arc<MastForest> {
36        &self.current_forest
37    }
38
39    /// Returns a reference to the debug info associated with the current forest, if available
40    pub fn debug_info(&self) -> Option<Arc<PackageDebugInfo>> {
41        self.package_debug_info.clone()
42    }
43
44    /// Returns the source/debug occurrence associated with the next continuation, if available.
45    pub fn next_source_node_id(&self) -> Option<DebugSourceNodeId> {
46        self.continuation_stack
47            .peek_continuation_with_source_node_id()
48            .and_then(|(_, source_node_id)| source_node_id)
49    }
50
51    /// Returns dynamic-boundary inline contexts active for the next operation, ordered from the
52    /// innermost boundary to the outermost.
53    pub fn inherited_inline_call_contexts(&self) -> impl Iterator<Item = &SourceInlineCallContext> {
54        let effective_depth = self.continuation_stack.iter_continuations_for_next_clock().fold(
55            self.inline_call_contexts.len(),
56            |depth, continuation| match continuation {
57                Continuation::EnterForest { inline_context_depth, .. } => *inline_context_depth,
58                Continuation::FinishDyn(_) => depth.saturating_sub(1),
59                _ => depth,
60            },
61        );
62        self.inline_call_contexts[..effective_depth.min(self.inline_call_contexts.len())]
63            .iter()
64            .rev()
65            .filter_map(Option::as_ref)
66    }
67
68    /// Returns a reference to the kernel being currently executed.
69    pub fn kernel(&self) -> &KernelDescriptor {
70        &self.kernel
71    }
72}
73
74// STOPPERS
75// ===============================================================================================
76
77/// A [`Stopper`] that never stops execution (except for returning an error when the maximum cycle
78/// count is exceeded).
79pub struct NeverStopper;
80
81impl Stopper for NeverStopper {
82    type Processor = FastProcessor;
83    type Forest = Arc<MastForest>;
84
85    #[inline(always)]
86    fn should_stop(
87        &self,
88        processor: &FastProcessor,
89        continuation_stack: &ContinuationStack<Arc<MastForest>>,
90        _continuation_after_stop: impl FnOnce() -> Option<(
91            Continuation<Arc<MastForest>>,
92            Option<DebugSourceNodeId>,
93        )>,
94    ) -> ControlFlow<BreakReason<Arc<MastForest>>> {
95        check_if_max_cycles_exceeded(processor)?;
96        check_if_continuation_stack_too_large(processor, continuation_stack)
97    }
98}
99
100/// A [`Stopper`] that always stops execution after each single step. An error is returned if the
101/// maximum cycle count is exceeded.
102pub struct StepStopper;
103
104impl Stopper for StepStopper {
105    type Processor = FastProcessor;
106    type Forest = Arc<MastForest>;
107
108    #[inline(always)]
109    fn should_stop(
110        &self,
111        processor: &FastProcessor,
112        continuation_stack: &ContinuationStack<Arc<MastForest>>,
113        continuation_after_stop: impl FnOnce() -> Option<(
114            Continuation<Arc<MastForest>>,
115            Option<DebugSourceNodeId>,
116        )>,
117    ) -> ControlFlow<BreakReason<Arc<MastForest>>> {
118        check_if_max_cycles_exceeded(processor)?;
119        check_if_continuation_stack_too_large(processor, continuation_stack)?;
120
121        ControlFlow::Break(BreakReason::Stopped(continuation_after_stop()))
122    }
123}
124
125/// Checks if the maximum cycle count has been exceeded, returning a `BreakReason::Err` if so.
126#[inline(always)]
127fn check_if_max_cycles_exceeded<F>(processor: &FastProcessor) -> ControlFlow<BreakReason<F>> {
128    if processor.clk > processor.options.max_cycles() as usize {
129        ControlFlow::Break(BreakReason::Err(ExecutionError::CycleLimitExceeded(
130            processor.options.max_cycles(),
131        )))
132    } else {
133        ControlFlow::Continue(())
134    }
135}
136
137/// Checks if the continuation stack size exceeds the maximum allowed, returning a
138/// `BreakReason::Err` if so.
139#[inline(always)]
140fn check_if_continuation_stack_too_large<F>(
141    processor: &FastProcessor,
142    continuation_stack: &ContinuationStack<F>,
143) -> ControlFlow<BreakReason<F>> {
144    if continuation_stack.len() > processor.options.max_num_continuations() {
145        ControlFlow::Break(BreakReason::Err(ExecutionError::Internal(
146            "continuation stack size exceeded the allowed maximum",
147        )))
148    } else {
149        ControlFlow::Continue(())
150    }
151}
152
153// BREAK REASON
154// ===============================================================================================
155
156/// The reason why execution was interrupted.
157#[derive(Debug)]
158pub enum BreakReason<F> {
159    /// An execution error occurred
160    Err(ExecutionError),
161    /// Execution was stopped by a [`Stopper`]. Provides the continuation to add to the continuation
162    /// stack before returning, if any. The mental model to have in mind when choosing the
163    /// continuation to add on a call to `FastProcessor::increment_clk()` is:
164    ///
165    /// "If execution is stopped here, does the current continuation stack properly encode the next
166    /// step of execution?"
167    ///
168    /// If yes, then `None` should be returned. If not, then the continuation that runs the next
169    /// step in `FastProcessor::execute_impl()` should be returned.
170    Stopped(Option<(Continuation<F>, Option<DebugSourceNodeId>)>),
171}