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