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
18mod continuation_stack;
19mod debug;
20mod errors;
21mod execution;
22mod execution_options;
23mod fast;
24mod host;
25mod processor;
26mod tracer;
27
28use crate::{
29    advice::{AdviceInputs, AdviceProvider},
30    continuation_stack::ContinuationStack,
31    errors::MapExecErr,
32    processor::{Processor, SystemInterface},
33    trace::RowIndex,
34};
35
36#[cfg(any(test, feature = "testing"))]
37mod test_utils;
38#[cfg(any(test, feature = "testing"))]
39pub use test_utils::{ProcessorStateSnapshot, TestHost, TraceCollector};
40
41#[cfg(test)]
42mod tests;
43
44// RE-EXPORTS
45// ================================================================================================
46
47pub use continuation_stack::Continuation;
48pub use errors::{AceError, ExecutionError, HostError, MemoryError};
49pub use execution_options::{ExecutionOptions, ExecutionOptionsError};
50pub use fast::{BreakReason, ExecutionOutput, FastProcessor, ResumeContext};
51pub use host::{
52    BaseHost, FutureMaybeSend, Host, MastForestStore, MemMastForestStore, SyncHost,
53    debug::DefaultDebugHandler,
54    default::{DefaultHost, HostLibrary},
55    handlers::{DebugError, DebugHandler, TraceError},
56};
57pub use miden_core::{
58    EMPTY_WORD, Felt, ONE, WORD_SIZE, Word, ZERO, crypto, field, mast, precompile,
59    program::{
60        InputError, Kernel, MIN_STACK_DEPTH, Program, ProgramInfo, StackInputs, StackOutputs,
61    },
62    serde, utils,
63};
64pub use trace::{TraceBuildInputs, TraceGenerationContext};
65
66pub mod advice {
67    pub use miden_core::advice::{AdviceInputs, AdviceMap, AdviceStackBuilder};
68
69    pub use super::host::{
70        AdviceMutation,
71        advice::{AdviceError, AdviceProvider},
72    };
73}
74
75pub mod event {
76    pub use miden_core::events::*;
77
78    pub use crate::host::handlers::{
79        EventError, EventHandler, EventHandlerRegistry, NoopEventHandler,
80    };
81}
82
83pub mod operation {
84    pub use miden_core::operations::*;
85
86    pub use crate::errors::OperationError;
87}
88
89pub mod trace;
90
91// EXECUTORS
92// ================================================================================================
93
94/// Executes the provided program against the provided inputs and returns the resulting execution
95/// output.
96///
97/// The `host` parameter is used to provide the external environment to the program being executed,
98/// such as access to the advice provider and libraries that the program depends on.
99///
100/// # Errors
101/// Returns an error if program execution fails for any reason.
102#[tracing::instrument("execute_program", skip_all)]
103pub async fn execute(
104    program: &Program,
105    stack_inputs: StackInputs,
106    advice_inputs: AdviceInputs,
107    host: &mut impl Host,
108    options: ExecutionOptions,
109) -> Result<ExecutionOutput, ExecutionError> {
110    let processor = FastProcessor::new_with_options(stack_inputs, advice_inputs, options);
111    processor.execute(program, host).await
112}
113
114/// Synchronous wrapper for the async `execute()` function.
115#[tracing::instrument("execute_program_sync", skip_all)]
116pub fn execute_sync(
117    program: &Program,
118    stack_inputs: StackInputs,
119    advice_inputs: AdviceInputs,
120    host: &mut impl SyncHost,
121    options: ExecutionOptions,
122) -> Result<ExecutionOutput, ExecutionError> {
123    let processor = FastProcessor::new_with_options(stack_inputs, advice_inputs, options);
124    processor.execute_sync(program, host)
125}
126
127// PROCESSOR STATE
128// ===============================================================================================
129
130/// A view into the current state of the processor.
131///
132/// This struct provides read access to the processor's state, including the stack, memory,
133/// advice provider, and execution context information.
134#[derive(Debug)]
135pub struct ProcessorState<'a> {
136    processor: &'a FastProcessor,
137}
138
139impl<'a> ProcessorState<'a> {
140    /// Returns a reference to the advice provider.
141    #[inline(always)]
142    pub fn advice_provider(&self) -> &AdviceProvider {
143        self.processor.advice_provider()
144    }
145
146    /// Returns the execution options.
147    #[inline(always)]
148    pub fn execution_options(&self) -> &ExecutionOptions {
149        self.processor.execution_options()
150    }
151
152    /// Returns the current clock cycle of a process.
153    #[inline(always)]
154    pub fn clock(&self) -> RowIndex {
155        self.processor.clock()
156    }
157
158    /// Returns the current execution context ID.
159    #[inline(always)]
160    pub fn ctx(&self) -> ContextId {
161        self.processor.ctx()
162    }
163
164    /// Returns the value located at the specified position on the stack at the current clock cycle.
165    ///
166    /// This method can access elements beyond the top 16 positions by using the overflow table.
167    #[inline(always)]
168    pub fn get_stack_item(&self, pos: usize) -> Felt {
169        self.processor.stack_get_safe(pos)
170    }
171
172    /// Returns a word starting at the specified element index on the stack.
173    ///
174    /// The word is formed by taking 4 consecutive elements starting from the specified index.
175    /// For example, start_idx=0 creates a word from stack elements 0-3, start_idx=1 creates
176    /// a word from elements 1-4, etc.
177    ///
178    /// Stack element N will be at position 0 of the word, N+1 at position 1, N+2 at position 2,
179    /// and N+3 at position 3. `word[0]` corresponds to the top of the stack.
180    ///
181    /// This method can access elements beyond the top 16 positions by using the overflow table.
182    /// Creating a word does not change the state of the stack.
183    #[inline(always)]
184    pub fn get_stack_word(&self, start_idx: usize) -> Word {
185        self.processor.stack_get_word_safe(start_idx)
186    }
187
188    /// Returns stack state at the current clock cycle. This includes the top 16 items of the
189    /// stack + overflow entries.
190    #[inline(always)]
191    pub fn get_stack_state(&self) -> Vec<Felt> {
192        self.processor.stack().iter().rev().copied().collect()
193    }
194
195    /// Returns the element located at the specified context/address, or None if the address hasn't
196    /// been accessed previously.
197    #[inline(always)]
198    pub fn get_mem_value(&self, ctx: ContextId, addr: u32) -> Option<Felt> {
199        self.processor.memory().read_element_impl(ctx, addr)
200    }
201
202    /// Returns the batch of elements starting at the specified context/address.
203    ///
204    /// # Errors
205    /// - If the address is not word aligned.
206    #[inline(always)]
207    pub fn get_mem_word(&self, ctx: ContextId, addr: u32) -> Result<Option<Word>, MemoryError> {
208        self.processor.memory().read_word_impl(ctx, addr)
209    }
210
211    /// Reads (start_addr, end_addr) tuple from the specified elements of the operand stack (
212    /// without modifying the state of the stack), and verifies that memory range is valid.
213    pub fn get_mem_addr_range(
214        &self,
215        start_idx: usize,
216        end_idx: usize,
217    ) -> Result<core::ops::Range<u32>, MemoryError> {
218        let start_addr = self.get_stack_item(start_idx).as_canonical_u64();
219        let end_addr = self.get_stack_item(end_idx).as_canonical_u64();
220
221        if start_addr > u32::MAX as u64 {
222            return Err(MemoryError::AddressOutOfBounds { addr: start_addr });
223        }
224        if end_addr > u32::MAX as u64 {
225            return Err(MemoryError::AddressOutOfBounds { addr: end_addr });
226        }
227
228        if start_addr > end_addr {
229            return Err(MemoryError::InvalidMemoryRange { start_addr, end_addr });
230        }
231
232        Ok(start_addr as u32..end_addr as u32)
233    }
234
235    /// Returns the entire memory state for the specified execution context at the current clock
236    /// cycle.
237    ///
238    /// The state is returned as a vector of (address, value) tuples, and includes addresses which
239    /// have been accessed at least once.
240    #[inline(always)]
241    pub fn get_mem_state(&self, ctx: ContextId) -> Vec<(MemoryAddress, Felt)> {
242        self.processor.memory().get_memory_state(ctx)
243    }
244}
245
246// STOPPER
247// ===============================================================================================
248
249/// A trait for types that determine whether execution should be stopped after each clock cycle.
250///
251/// This allows for flexible control over the execution process, enabling features such as stepping
252/// through execution (see [`crate::FastProcessor::step`]) or limiting execution to a certain number
253/// of clock cycles (used in parallel trace generation to fill the trace for a predetermined trace
254/// fragment).
255pub trait Stopper {
256    type Processor;
257
258    /// Determines whether execution should be stopped at the end of each clock cycle.
259    ///
260    /// This method is guaranteed to be called at the end of each clock cycle, *after* the processor
261    /// state has been updated to reflect the effects of the operations executed during that cycle
262    /// (*including* the processor clock). Hence, a processor clock of `N` indicates that clock
263    /// cycle `N - 1` has just completed.
264    ///
265    /// The `continuation_after_stop` is provided in cases where simply resuming execution from the
266    /// top of the continuation stack is not sufficient to continue execution correctly. For
267    /// example, when stopping execution in the middle of a basic block, we need to provide a
268    /// `ResumeBasicBlock` continuation to ensure that execution resumes at the correct operation
269    /// within the basic block (i.e. the operation right after the one that was last executed before
270    /// being stopped). No continuation is provided in case of error, since it is expected that
271    /// execution will not be resumed.
272    fn should_stop(
273        &self,
274        processor: &Self::Processor,
275        continuation_stack: &ContinuationStack,
276        continuation_after_stop: impl FnOnce() -> Option<continuation_stack::Continuation>,
277    ) -> ControlFlow<BreakReason>;
278}
279
280// EXECUTION CONTEXT
281// ================================================================================================
282
283/// Represents the ID of an execution context
284#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
285pub struct ContextId(u32);
286
287impl ContextId {
288    /// Returns the root context ID
289    pub fn root() -> Self {
290        Self(0)
291    }
292
293    /// Returns true if the context ID represents the root context
294    pub fn is_root(&self) -> bool {
295        self.0 == 0
296    }
297}
298
299impl From<RowIndex> for ContextId {
300    fn from(value: RowIndex) -> Self {
301        Self(value.as_u32())
302    }
303}
304
305impl From<u32> for ContextId {
306    fn from(value: u32) -> Self {
307        Self(value)
308    }
309}
310
311impl From<ContextId> for u32 {
312    fn from(context_id: ContextId) -> Self {
313        context_id.0
314    }
315}
316
317impl From<ContextId> for u64 {
318    fn from(context_id: ContextId) -> Self {
319        context_id.0.into()
320    }
321}
322
323impl From<ContextId> for Felt {
324    fn from(context_id: ContextId) -> Self {
325        Felt::from_u32(context_id.0)
326    }
327}
328
329impl Display for ContextId {
330    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331        write!(f, "{}", self.0)
332    }
333}
334
335// MEMORY ADDRESS
336// ================================================================================================
337
338#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
339pub struct MemoryAddress(u32);
340
341impl From<u32> for MemoryAddress {
342    fn from(addr: u32) -> Self {
343        MemoryAddress(addr)
344    }
345}
346
347impl From<MemoryAddress> for u32 {
348    fn from(value: MemoryAddress) -> Self {
349        value.0
350    }
351}
352
353impl Display for MemoryAddress {
354    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
355        Display::fmt(&self.0, f)
356    }
357}
358
359impl LowerHex for MemoryAddress {
360    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
361        LowerHex::fmt(&self.0, f)
362    }
363}
364
365impl core::ops::Add<MemoryAddress> for MemoryAddress {
366    type Output = Self;
367
368    fn add(self, rhs: MemoryAddress) -> Self::Output {
369        MemoryAddress(self.0 + rhs.0)
370    }
371}
372
373impl core::ops::Add<u32> for MemoryAddress {
374    type Output = Self;
375
376    fn add(self, rhs: u32) -> Self::Output {
377        MemoryAddress(self.0 + rhs)
378    }
379}