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