Skip to main content

miden_processor/
lib.rs

1#![no_std]
2// Trace tests intentionally use index-based `for i in a..b` over column slices; clippy's iterator
3// suggestion is noisier than helpful there.
4#![cfg_attr(test, allow(clippy::needless_range_loop))]
5
6#[macro_use]
7extern crate alloc;
8
9#[cfg(feature = "std")]
10extern crate std;
11
12use alloc::vec::Vec;
13use core::{
14    fmt::{self, Display, LowerHex},
15    ops::ControlFlow,
16};
17
18use miden_mast_package::debug_info::DebugSourceNodeId;
19
20mod continuation_stack;
21mod errors;
22mod execution;
23mod execution_options;
24mod executor;
25mod fast;
26mod host;
27mod processor;
28mod tracer;
29
30use miden_core::{
31    deferred::{Digest, Node, PrecompileError},
32    mast::ExecutableMastForest,
33    serde::{Deserializable, Serializable},
34};
35
36use crate::{
37    advice::{AdviceInputs, AdviceProvider},
38    continuation_stack::ContinuationStack,
39    errors::MapExecErr,
40    processor::{Processor, SystemInterface},
41    trace::RowIndex,
42};
43
44#[cfg(any(test, feature = "testing"))]
45mod test_utils;
46#[cfg(any(test, feature = "testing"))]
47pub use test_utils::{ProcessorStateSnapshot, TestHost};
48
49#[cfg(test)]
50mod tests;
51
52// RE-EXPORTS
53// ================================================================================================
54
55pub use continuation_stack::{Continuation, SourceInlineCallContext};
56pub use errors::{
57    AceError, ExecutionError, HostError, MemoryError, PackageSourceDebugContext,
58    advice_error_with_package_source_context, event_error_with_package_source_context,
59    procedure_not_found_with_package_source_context,
60};
61pub use execution_options::{ExecutionOptions, ExecutionOptionsError};
62pub use executor::ProgramExecutor;
63pub use fast::{BreakReason, ExecutionOutput, FastProcessor, ResumeContext};
64pub use host::{
65    BaseHost, FutureMaybeSend, Host, LoadedMastForest, MastForestStore, MemMastForestStore,
66    SyncHost,
67    debug::{StdoutWriter, format_value, write_interval, write_stack},
68    default::{DefaultHost, HostLibrary},
69};
70pub use miden_core::{
71    EMPTY_WORD, Felt, ONE, WORD_SIZE, Word, ZERO, crypto, field, mast,
72    program::{
73        ExecutionClaim, InputError, KernelDescriptor, MIN_STACK_DEPTH, Program, ProgramInfo,
74        StackInputs, StackOutputs,
75    },
76    serde, utils,
77};
78pub use trace::{ExecutionWitness, PrecompileWitness, VmWitness};
79
80pub mod advice {
81    pub use miden_core::advice::{AdviceInputs, AdviceMap, AdviceStack};
82
83    pub use super::host::{
84        AdviceMutation,
85        advice::{AdviceError, AdviceProvider},
86    };
87}
88
89pub mod event {
90    pub use miden_core::events::*;
91
92    pub use crate::host::handlers::{
93        EventError, EventHandler, EventHandlerRegistry, NoopEventHandler, TraceError, TraceHandler,
94        TraceHandlerRegistry,
95    };
96}
97
98pub mod operation {
99    pub use miden_core::operations::*;
100
101    pub use crate::errors::{BinaryValueErrorContext, OperationError};
102}
103
104pub mod trace;
105
106// PROCESSOR STATE
107// ===============================================================================================
108
109/// A view into the current state of the processor.
110///
111/// This struct provides read access to the processor's state, including the stack, memory,
112/// advice provider, and execution context information.
113#[derive(Debug)]
114pub struct ProcessorState<'a> {
115    processor: &'a FastProcessor,
116}
117
118impl<'a> ProcessorState<'a> {
119    /// Returns a reference to the advice provider.
120    #[inline(always)]
121    pub fn advice_provider(&self) -> &AdviceProvider {
122        self.processor.advice_provider()
123    }
124
125    /// Returns the execution options.
126    #[inline(always)]
127    pub fn execution_options(&self) -> &ExecutionOptions {
128        self.processor.execution_options()
129    }
130
131    /// Returns the current clock cycle of a process.
132    #[inline(always)]
133    pub fn clock(&self) -> RowIndex {
134        self.processor.clock()
135    }
136
137    /// Returns the current execution context ID.
138    #[inline(always)]
139    pub fn ctx(&self) -> ContextId {
140        self.processor.ctx()
141    }
142
143    /// Returns the value located at the specified position on the stack at the current clock cycle.
144    ///
145    /// This method can access elements beyond the top 16 positions by using the overflow table.
146    #[inline(always)]
147    pub fn get_stack_item(&self, pos: usize) -> Felt {
148        self.processor.stack_get_safe(pos)
149    }
150
151    /// Returns a word starting at the specified element index on the stack.
152    ///
153    /// The word is formed by taking 4 consecutive elements starting from the specified index.
154    /// For example, start_idx=0 creates a word from stack elements 0-3, start_idx=1 creates
155    /// a word from elements 1-4, etc.
156    ///
157    /// Stack element N will be at position 0 of the word, N+1 at position 1, N+2 at position 2,
158    /// and N+3 at position 3. `word[0]` corresponds to the top of the stack.
159    ///
160    /// This method can access elements beyond the top 16 positions by using the overflow table.
161    /// Creating a word does not change the state of the stack.
162    #[inline(always)]
163    pub fn get_stack_word(&self, start_idx: usize) -> Word {
164        self.processor.stack_get_word_safe(start_idx)
165    }
166
167    /// Returns stack state at the current clock cycle. This includes the top 16 items of the
168    /// stack + overflow entries.
169    #[inline(always)]
170    pub fn get_stack_state(&self) -> Vec<Felt> {
171        self.processor.stack().iter().rev().copied().collect()
172    }
173
174    /// Returns the element located at the specified context/address, or None if the address hasn't
175    /// been accessed previously.
176    #[inline(always)]
177    pub fn get_mem_value(&self, ctx: ContextId, addr: u32) -> Option<Felt> {
178        self.processor.memory().read_element_impl(ctx, addr)
179    }
180
181    /// Returns the batch of elements starting at the specified context/address.
182    ///
183    /// # Errors
184    /// - If the address is not word aligned.
185    #[inline(always)]
186    pub fn get_mem_word(&self, ctx: ContextId, addr: u32) -> Result<Option<Word>, MemoryError> {
187        self.processor.memory().read_word_impl(ctx, addr)
188    }
189
190    /// Reads (start_addr, end_addr) tuple from the specified elements of the operand stack (
191    /// without modifying the state of the stack), and verifies that memory range is valid.
192    ///
193    /// The range is half-open `[start, end)`; both `start` and `end` must be `<= u32::MAX`.
194    pub fn get_mem_addr_range(
195        &self,
196        start_idx: usize,
197        end_idx: usize,
198    ) -> Result<core::ops::Range<u32>, MemoryError> {
199        let start_addr = self.get_stack_item(start_idx).as_canonical_u64();
200        let end_addr = self.get_stack_item(end_idx).as_canonical_u64();
201
202        if start_addr > u32::MAX as u64 {
203            return Err(MemoryError::AddressOutOfBounds { addr: start_addr });
204        }
205        if end_addr > u32::MAX as u64 {
206            return Err(MemoryError::AddressOutOfBounds { addr: end_addr });
207        }
208
209        if start_addr > end_addr {
210            return Err(MemoryError::InvalidMemoryRange { start_addr, end_addr });
211        }
212
213        Ok(start_addr as u32..end_addr as u32)
214    }
215
216    /// Returns the entire memory state for the specified execution context at the current clock
217    /// cycle.
218    ///
219    /// The state is returned as a vector of (address, value) tuples, and includes addresses which
220    /// have been accessed at least once.
221    #[inline(always)]
222    pub fn get_mem_state(&self, ctx: ContextId) -> Vec<(MemoryAddress, Felt)> {
223        self.processor.memory().get_memory_state(ctx)
224    }
225
226    /// Returns the already-memoized canonical deferred digest for `digest`, if present.
227    ///
228    /// This is a read-only lookup: it does not evaluate `digest`, register helper nodes, or mutate
229    /// deferred state.
230    #[inline(always)]
231    pub fn get_canonical_deferred_digest(&self, digest: Digest) -> Option<Digest> {
232        self.processor.deferred_state().get_canonical_digest(digest)
233    }
234
235    /// Returns the already-memoized canonical deferred node for `digest`, if present.
236    ///
237    /// This is a read-only lookup and returns only canonical results that were already memoized in
238    /// deferred state; it never evaluates or mutates deferred state.
239    #[inline(always)]
240    pub fn get_canonical_deferred_node(&self, digest: Digest) -> Option<(Digest, &Node)> {
241        self.processor.deferred_state().get_canonical_node(digest)
242    }
243
244    /// Returns the already-memoized canonical deferred node for `digest`.
245    ///
246    /// This is a read-only lookup and never evaluates or mutates deferred state.
247    ///
248    /// # Errors
249    /// Returns [`PrecompileError::MissingNode`] if no memoized canonical node is available.
250    #[inline(always)]
251    pub fn require_canonical_deferred_node(
252        &self,
253        digest: Digest,
254    ) -> Result<(Digest, &Node), PrecompileError> {
255        self.processor.deferred_state().require_canonical_node(digest)
256    }
257}
258
259// STOPPER
260// ===============================================================================================
261
262/// A trait for types that determine whether execution should be stopped after each clock cycle.
263///
264/// This allows for flexible control over the execution process, enabling features such as stepping
265/// through execution (see [`crate::FastProcessor::step`]) or limiting execution to a certain number
266/// of clock cycles (used in parallel trace generation to fill the trace for a predetermined trace
267/// fragment).
268pub trait Stopper {
269    type Processor;
270
271    /// The forest representation used by the executor this stopper is paired with.
272    ///
273    /// For live execution this is `Arc<MastForest>`; for replay it is `Arc<SparseMastForest>`.
274    type Forest: ExecutableMastForest + Clone;
275
276    /// Determines whether execution should be stopped at the end of each clock cycle.
277    ///
278    /// This method is guaranteed to be called at the end of each clock cycle, *after* the processor
279    /// state has been updated to reflect the effects of the operations executed during that cycle
280    /// (*including* the processor clock). Hence, a processor clock of `N` indicates that clock
281    /// cycle `N - 1` has just completed.
282    ///
283    /// The `continuation_after_stop` is provided in cases where simply resuming execution from the
284    /// top of the continuation stack is not sufficient to continue execution correctly. For
285    /// example, when stopping execution in the middle of a basic block, we need to provide a
286    /// `ResumeBasicBlock` continuation to ensure that execution resumes at the correct operation
287    /// within the basic block (i.e. the operation right after the one that was last executed before
288    /// being stopped). No continuation is provided in case of error, since it is expected that
289    /// execution will not be resumed.
290    fn should_stop(
291        &self,
292        processor: &Self::Processor,
293        continuation_stack: &ContinuationStack<Self::Forest>,
294        continuation_after_stop: impl FnOnce() -> Option<(
295            Continuation<Self::Forest>,
296            Option<DebugSourceNodeId>,
297        )>,
298    ) -> ControlFlow<BreakReason<Self::Forest>>;
299}
300
301// EXECUTION CONTEXT
302// ================================================================================================
303
304/// Represents the ID of an execution context
305#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
306pub struct ContextId(u32);
307
308impl ContextId {
309    /// Returns the root context ID
310    pub fn root() -> Self {
311        Self(0)
312    }
313
314    /// Returns true if the context ID represents the root context
315    pub fn is_root(&self) -> bool {
316        self.0 == 0
317    }
318}
319
320impl From<RowIndex> for ContextId {
321    fn from(value: RowIndex) -> Self {
322        Self(value.as_u32())
323    }
324}
325
326impl From<u32> for ContextId {
327    fn from(value: u32) -> Self {
328        Self(value)
329    }
330}
331
332impl From<ContextId> for u32 {
333    fn from(context_id: ContextId) -> Self {
334        context_id.0
335    }
336}
337
338impl From<ContextId> for u64 {
339    fn from(context_id: ContextId) -> Self {
340        context_id.0.into()
341    }
342}
343
344impl From<ContextId> for Felt {
345    fn from(context_id: ContextId) -> Self {
346        Felt::from_u32(context_id.0)
347    }
348}
349
350impl Serializable for ContextId {
351    fn write_into<W: serde::ByteWriter>(&self, target: &mut W) {
352        Serializable::write_into(&self.0, target);
353    }
354}
355
356impl Deserializable for ContextId {
357    fn read_from<R: serde::ByteReader>(
358        source: &mut R,
359    ) -> Result<Self, serde::DeserializationError> {
360        Ok(Self(<u32 as Deserializable>::read_from(source)?))
361    }
362
363    fn min_serialized_size() -> usize {
364        <u32 as Deserializable>::min_serialized_size()
365    }
366}
367
368impl Display for ContextId {
369    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370        write!(f, "{}", self.0)
371    }
372}
373
374// MEMORY ADDRESS
375// ================================================================================================
376
377#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
378pub struct MemoryAddress(u32);
379
380impl From<u32> for MemoryAddress {
381    fn from(addr: u32) -> Self {
382        MemoryAddress(addr)
383    }
384}
385
386impl From<MemoryAddress> for u32 {
387    fn from(value: MemoryAddress) -> Self {
388        value.0
389    }
390}
391
392impl Display for MemoryAddress {
393    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
394        Display::fmt(&self.0, f)
395    }
396}
397
398impl LowerHex for MemoryAddress {
399    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400        LowerHex::fmt(&self.0, f)
401    }
402}
403
404impl core::ops::Add<MemoryAddress> for MemoryAddress {
405    type Output = Self;
406
407    fn add(self, rhs: MemoryAddress) -> Self::Output {
408        MemoryAddress(self.0 + rhs.0)
409    }
410}
411
412impl core::ops::Add<u32> for MemoryAddress {
413    type Output = Self;
414
415    fn add(self, rhs: u32) -> Self::Output {
416        MemoryAddress(self.0 + rhs)
417    }
418}
419
420// HELPERS
421// ===============================================================================================
422
423/// Lifts an [`Option<T>`] into a [`ControlFlow`] suitable for the execution loop, mapping `None`
424/// to a break carrying an [`ExecutionError::Internal`] with `err_msg`.
425///
426/// Intended for use with `?` at sites where a `None` represents a violated internal invariant —
427/// most commonly a missing node returned by
428/// [`ExecutableMastForest::get_node_by_id`](miden_core::mast::ExecutableMastForest::get_node_by_id).
429/// For functions returning `ControlFlow<InternalBreakReason<F>>`, chain
430/// `.map_break(InternalBreakReason::from)` before `?`.
431#[track_caller]
432fn option_map_break_reason<F, T>(
433    opt: Option<T>,
434    err_msg: &'static str,
435) -> ControlFlow<BreakReason<F>, T> {
436    match opt {
437        Some(value) => ControlFlow::Continue(value),
438        None => ControlFlow::Break(BreakReason::Err(ExecutionError::Internal(err_msg))),
439    }
440}