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};
7
8use crate::{
9    ExecutionError, FastProcessor, Stopper,
10    continuation_stack::{Continuation, ContinuationStack},
11};
12
13// RESUME CONTEXT
14// ===============================================================================================
15
16/// The context required to resume execution of a program from the last point at which it was
17/// stopped.
18#[derive(Debug)]
19pub struct ResumeContext {
20    pub(crate) current_forest: Arc<MastForest>,
21    pub(crate) continuation_stack: ContinuationStack,
22    pub(crate) kernel: Kernel,
23}
24
25impl ResumeContext {
26    /// Returns a reference to the continuation stack.
27    pub fn continuation_stack(&self) -> &ContinuationStack {
28        &self.continuation_stack
29    }
30
31    /// Returns a reference to the MAST forest being currently executed.
32    pub fn current_forest(&self) -> &Arc<MastForest> {
33        &self.current_forest
34    }
35
36    /// Returns a reference to the kernel being currently executed.
37    pub fn kernel(&self) -> &Kernel {
38        &self.kernel
39    }
40}
41
42// STOPPERS
43// ===============================================================================================
44
45/// A [`Stopper`] that never stops execution (except for returning an error when the maximum cycle
46/// count is exceeded).
47pub struct NeverStopper;
48
49impl Stopper for NeverStopper {
50    type Processor = FastProcessor;
51
52    #[inline(always)]
53    fn should_stop(
54        &self,
55        processor: &FastProcessor,
56        _continuation_after_stop: impl FnOnce() -> Option<Continuation>,
57    ) -> ControlFlow<BreakReason> {
58        check_if_max_cycles_exceeded(processor)
59    }
60}
61
62/// A [`Stopper`] that always stops execution after each single step. An error is returned if the
63/// maximum cycle count is exceeded.
64pub struct StepStopper;
65
66impl Stopper for StepStopper {
67    type Processor = FastProcessor;
68
69    #[inline(always)]
70    fn should_stop(
71        &self,
72        processor: &FastProcessor,
73        continuation_after_stop: impl FnOnce() -> Option<Continuation>,
74    ) -> ControlFlow<BreakReason> {
75        check_if_max_cycles_exceeded(processor)?;
76
77        ControlFlow::Break(BreakReason::Stopped(continuation_after_stop()))
78    }
79}
80
81/// Checks if the maximum cycle count has been exceeded, returning a `BreakReason::Err` if so.
82#[inline(always)]
83fn check_if_max_cycles_exceeded(processor: &FastProcessor) -> ControlFlow<BreakReason> {
84    if processor.clk > processor.options.max_cycles() as usize {
85        ControlFlow::Break(BreakReason::Err(ExecutionError::CycleLimitExceeded(
86            processor.options.max_cycles(),
87        )))
88    } else {
89        ControlFlow::Continue(())
90    }
91}
92
93// BREAK REASON
94// ===============================================================================================
95
96/// The reason why execution was interrupted.
97#[derive(Debug)]
98pub enum BreakReason {
99    /// An execution error occurred
100    Err(ExecutionError),
101    /// Execution was stopped by a [`Stopper`]. Provides the continuation to add to the continuation
102    /// stack before returning, if any. The mental model to have in mind when choosing the
103    /// continuation to add on a call to `FastProcessor::increment_clk()` is:
104    ///
105    /// "If execution is stopped here, does the current continuation stack properly encode the next
106    /// step of execution?"
107    ///
108    /// If yes, then `None` should be returned. If not, then the continuation that runs the next
109    /// step in `FastProcessor::execute_impl()` should be returned.
110    Stopped(Option<Continuation>),
111}