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)]
309#[cfg_attr(
310    all(feature = "arbitrary", test),
311    miden_test_serialization_macros::serialization_test
312)]
313pub struct ContextId(u32);
314
315impl ContextId {
316    /// Returns the root context ID
317    pub fn root() -> Self {
318        Self(0)
319    }
320
321    /// Returns true if the context ID represents the root context
322    pub fn is_root(&self) -> bool {
323        self.0 == 0
324    }
325}
326
327impl From<RowIndex> for ContextId {
328    fn from(value: RowIndex) -> Self {
329        Self(value.as_u32())
330    }
331}
332
333impl From<u32> for ContextId {
334    fn from(value: u32) -> Self {
335        Self(value)
336    }
337}
338
339impl From<ContextId> for u32 {
340    fn from(context_id: ContextId) -> Self {
341        context_id.0
342    }
343}
344
345impl From<ContextId> for u64 {
346    fn from(context_id: ContextId) -> Self {
347        context_id.0.into()
348    }
349}
350
351impl From<ContextId> for Felt {
352    fn from(context_id: ContextId) -> Self {
353        Felt::from_u32(context_id.0)
354    }
355}
356
357#[cfg(any(test, feature = "arbitrary"))]
358mod context_id_arbitrary {
359    use proptest::prelude::*;
360
361    use super::ContextId;
362
363    impl Arbitrary for ContextId {
364        type Parameters = ();
365        type Strategy = BoxedStrategy<Self>;
366
367        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
368            // Plain u32 newtype; edge-biased to the 0 and u32::MAX bounds.
369            prop_oneof![Just(0u32), Just(u32::MAX), any::<u32>()].prop_map(Self).boxed()
370        }
371    }
372}
373
374impl Serializable for ContextId {
375    fn write_into<W: serde::ByteWriter>(&self, target: &mut W) {
376        Serializable::write_into(&self.0, target);
377    }
378}
379
380impl Deserializable for ContextId {
381    fn read_from<R: serde::ByteReader>(
382        source: &mut R,
383    ) -> Result<Self, serde::DeserializationError> {
384        Ok(Self(<u32 as Deserializable>::read_from(source)?))
385    }
386
387    fn min_serialized_size() -> usize {
388        <u32 as Deserializable>::min_serialized_size()
389    }
390}
391
392impl Display for ContextId {
393    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
394        write!(f, "{}", self.0)
395    }
396}
397
398// MEMORY ADDRESS
399// ================================================================================================
400
401#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
402pub struct MemoryAddress(u32);
403
404impl From<u32> for MemoryAddress {
405    fn from(addr: u32) -> Self {
406        MemoryAddress(addr)
407    }
408}
409
410impl From<MemoryAddress> for u32 {
411    fn from(value: MemoryAddress) -> Self {
412        value.0
413    }
414}
415
416impl Display for MemoryAddress {
417    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
418        Display::fmt(&self.0, f)
419    }
420}
421
422impl LowerHex for MemoryAddress {
423    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
424        LowerHex::fmt(&self.0, f)
425    }
426}
427
428impl core::ops::Add<MemoryAddress> for MemoryAddress {
429    type Output = Self;
430
431    fn add(self, rhs: MemoryAddress) -> Self::Output {
432        MemoryAddress(self.0 + rhs.0)
433    }
434}
435
436impl core::ops::Add<u32> for MemoryAddress {
437    type Output = Self;
438
439    fn add(self, rhs: u32) -> Self::Output {
440        MemoryAddress(self.0 + rhs)
441    }
442}
443
444// HELPERS
445// ===============================================================================================
446
447/// Lifts an [`Option<T>`] into a [`ControlFlow`] suitable for the execution loop, mapping `None`
448/// to a break carrying an [`ExecutionError::Internal`] with `err_msg`.
449///
450/// Intended for use with `?` at sites where a `None` represents a violated internal invariant —
451/// most commonly a missing node returned by
452/// [`ExecutableMastForest::get_node_by_id`](miden_core::mast::ExecutableMastForest::get_node_by_id).
453/// For functions returning `ControlFlow<InternalBreakReason<F>>`, chain
454/// `.map_break(InternalBreakReason::from)` before `?`.
455#[track_caller]
456fn option_map_break_reason<F, T>(
457    opt: Option<T>,
458    err_msg: &'static str,
459) -> ControlFlow<BreakReason<F>, T> {
460    match opt {
461        Some(value) => ControlFlow::Continue(value),
462        None => ControlFlow::Break(BreakReason::Err(ExecutionError::Internal(err_msg))),
463    }
464}