miden_processor/fast/
trace_state.rs

1use alloc::{collections::VecDeque, sync::Arc, vec::Vec};
2
3use miden_air::{
4    RowIndex,
5    trace::chiplets::hasher::{HasherState, STATE_WIDTH},
6};
7use miden_core::{
8    Felt, ONE, Word, ZERO,
9    crypto::merkle::MerklePath,
10    mast::{MastForest, MastNodeId, OpBatch},
11    precompile::PrecompileTranscriptState,
12    stack::MIN_STACK_DEPTH,
13};
14
15use crate::{
16    AdviceError, ContextId, ErrorContext, ExecutionError,
17    chiplets::CircuitEvaluation,
18    continuation_stack::ContinuationStack,
19    fast::FastProcessor,
20    processor::{AdviceProviderInterface, HasherInterface, MemoryInterface},
21};
22
23// TRACE FRAGMENT CONTEXT
24// ================================================================================================
25
26/// Information required to build a core trace fragment (i.e. the system, decoder and stack
27/// columns).
28///
29/// This struct is meant to be built by the processor, and consumed mutably by a core trace fragment
30/// builder. That is, as core trace generation progresses, this struct can be mutated to represent
31/// the generation context at any clock cycle within the fragment.
32///
33/// This struct is conceptually divided into 4 main components:
34/// 1. core trace state: the state of the processor at any clock cycle in the fragment, initialized
35///    to the state at the first clock cycle in the fragment,
36/// 2. execution replay: information needed to replay the execution of the processor for the
37///    remainder of the fragment,
38/// 3. continuation: a stack of continuations for the processor representing the nodes in the MAST
39///    forest to execute when the current node is done executing,
40/// 4. initial state: some information about the state of the execution at the start of the
41///    fragment. This includes the [`MastForest`] that is being executed at the start of the
42///    fragment (which can change when encountering an [`miden_core::mast::ExternalNode`] or
43///    [`miden_core::mast::DynNode`]), and the current node's execution state, which contains
44///    additional information to pinpoint exactly where in the processing of the node we're at when
45///    this fragment begins.
46#[derive(Debug)]
47pub struct CoreTraceFragmentContext {
48    pub state: CoreTraceState,
49    pub replay: ExecutionReplay,
50    pub continuation: ContinuationStack,
51    pub initial_mast_forest: Arc<MastForest>,
52    pub initial_execution_state: NodeExecutionState,
53}
54
55// CORE TRACE STATE
56// ================================================================================================
57
58/// Subset of the processor state used to build the core trace (system, decoder and stack sets of
59/// columns).
60#[derive(Debug)]
61pub struct CoreTraceState {
62    pub system: SystemState,
63    pub decoder: DecoderState,
64    pub stack: StackState,
65}
66
67// SYSTEM STATE
68// ================================================================================================
69
70/// The `SystemState` represents all the information needed to build one row of the System trace.
71///
72/// This struct captures the complete state of the system at a specific clock cycle, allowing for
73/// reconstruction of the system trace during concurrent execution.
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub struct SystemState {
76    /// Current clock cycle (row index in the trace)
77    pub clk: RowIndex,
78
79    /// Execution context ID - starts at 0 (root context), changes on CALL/SYSCALL operations
80    pub ctx: ContextId,
81
82    /// Hash of the function that initiated the current execution context
83    /// - For root context: [ZERO; 4]
84    /// - For CALL/DYNCALL contexts: hash of the called function
85    /// - For SYSCALL contexts: hash remains from the calling function
86    pub fn_hash: Word,
87
88    /// Precompile transcript state (sponge capacity) used for recording `log_precompile` calls
89    /// - Initially [ZERO; 4]
90    /// - Updated with each `log_precompile` invocation
91    pub pc_transcript_state: PrecompileTranscriptState,
92}
93
94impl SystemState {
95    /// Convenience constructor that creates a new `SystemState` from a `FastProcessor`.
96    pub fn from_processor(processor: &FastProcessor) -> Self {
97        Self {
98            clk: processor.clk,
99            ctx: processor.ctx,
100            fn_hash: processor.caller_hash,
101            pc_transcript_state: processor.pc_transcript.state(),
102        }
103    }
104}
105
106// DECODER STATE
107// ================================================================================================
108
109/// The subset of the decoder state required to build the trace.
110#[derive(Debug)]
111pub struct DecoderState {
112    /// The value of the [miden_air::trace::decoder::ADDR_COL_IDX] column
113    pub current_addr: Felt,
114    /// The address of the current MAST node's parent.
115    pub parent_addr: Felt,
116}
117
118impl DecoderState {
119    /// This function is called when start executing a node (e.g. `JOIN`, `SPLIT`, etc). It emulates
120    /// pushing a new node onto the block stack, and updates the decoder state to point to the
121    /// current node in the block stack. Hence, the `current_addr` is set to the (replayed) address
122    /// of the current node, and the `parent_addr` is set to the (replayed) address of the parent
123    /// node (i.e. the node previously on top of the block stack).
124    pub fn replay_node_start(&mut self, replay: &mut ExecutionReplay) {
125        self.current_addr = replay.hasher.replay_block_address();
126        self.parent_addr = replay.block_stack.replay_node_start_parent_addr();
127    }
128
129    /// This function is called when we hit an `END` operation, signaling the end of execution for a
130    /// node. It updates the decoder state to point to the previous node in the block stack (which
131    /// could be renamed to "node stack"), and returns the address of the node that just ended,
132    /// along with any flags associated with it.
133    pub fn replay_node_end(&mut self, replay: &mut ExecutionReplay) -> (Felt, NodeFlags) {
134        let node_end_data = replay.block_stack.replay_node_end();
135
136        self.current_addr = node_end_data.prev_addr;
137        self.parent_addr = node_end_data.prev_parent_addr;
138
139        (node_end_data.ended_node_addr, node_end_data.flags)
140    }
141}
142
143// STACK STATE
144// ================================================================================================
145
146/// This struct captures the state of the top 16 elements of the stack at a specific clock cycle;
147/// that is, those elements that are written directly into the trace.
148///
149/// The stack trace consists of 19 columns total: 16 stack columns + 3 helper columns. The helper
150/// columns (stack_depth, overflow_addr, and overflow_helper) are computed from the stack_depth and
151/// last_overflow_addr fields.
152#[derive(Debug)]
153pub struct StackState {
154    /// Top 16 stack slots (s0 to s15). These represent the top elements of the stack that are
155    /// directly accessible.
156    pub stack_top: [Felt; MIN_STACK_DEPTH], // 16 columns
157
158    /// Current number of elements on the stack. It is guaranteed to be >= 16.
159    stack_depth: usize,
160
161    /// The last recorded overflow address for the stack - which is the clock cycle at which the
162    /// last item was pushed to the overflow
163    last_overflow_addr: Felt,
164}
165
166impl StackState {
167    /// Creates a new StackState with the provided parameters.
168    ///
169    /// `stack_top` should be the top 16 elements of the stack stored in reverse order, i.e.,
170    /// `stack_top[15]` is the topmost element (s0), and `stack_top[0]` is the bottommost element
171    /// (s15).
172    pub fn new(
173        stack_top: [Felt; MIN_STACK_DEPTH],
174        stack_depth: usize,
175        last_overflow_addr: Felt,
176    ) -> Self {
177        Self {
178            stack_top,
179            stack_depth,
180            last_overflow_addr,
181        }
182    }
183
184    /// Returns the value at the specified index in the stack top.
185    ///
186    /// # Panics
187    /// - if the index is greater than or equal to [MIN_STACK_DEPTH].
188    pub fn get(&self, index: usize) -> Felt {
189        self.stack_top[MIN_STACK_DEPTH - index - 1]
190    }
191
192    /// Returns the stack depth (b0 helper column)
193    pub fn stack_depth(&self) -> usize {
194        self.stack_depth
195    }
196
197    /// Returns the overflow address (b1 helper column) using the stack overflow replay
198    pub fn overflow_addr(&mut self) -> Felt {
199        self.last_overflow_addr
200    }
201
202    /// Returns the number of elements in the current context's overflow stack.
203    pub fn num_overflow_elements_in_current_ctx(&self) -> usize {
204        debug_assert!(self.stack_depth >= MIN_STACK_DEPTH);
205        self.stack_depth - MIN_STACK_DEPTH
206    }
207
208    /// Pushes the given element onto the overflow stack at the provided clock cycle.
209    pub fn push_overflow(&mut self, _element: Felt, clk: RowIndex) {
210        self.stack_depth += 1;
211        self.last_overflow_addr = clk.into();
212    }
213
214    /// Pops the top element from the overflow stack at the provided clock cycle, if any.
215    ///
216    /// If the overflow table is empty (i.e. stack depth is 16), the stack depth is unchanged, and
217    /// None is returned.
218    pub fn pop_overflow(
219        &mut self,
220        stack_overflow_replay: &mut StackOverflowReplay,
221    ) -> Option<Felt> {
222        debug_assert!(self.stack_depth >= MIN_STACK_DEPTH);
223
224        if self.stack_depth > MIN_STACK_DEPTH {
225            let (stack_value, new_overflow_addr) = stack_overflow_replay.replay_pop_overflow();
226            self.stack_depth -= 1;
227            self.last_overflow_addr = new_overflow_addr;
228            Some(stack_value)
229        } else {
230            self.last_overflow_addr = ZERO;
231            None
232        }
233    }
234
235    /// Derives the denominator of the overflow helper (h0 helper column) from the current stack
236    /// depth.
237    ///
238    /// It is expected that this values gets later inverted via batch inversion.
239    pub fn overflow_helper(&self) -> Felt {
240        let denominator = self.stack_depth() - MIN_STACK_DEPTH;
241        Felt::new(denominator as u64)
242    }
243
244    /// Starts a new execution context for this stack and returns a tuple consisting of the current
245    /// stack depth and the address of the overflow table row prior to starting the new context.
246    ///
247    /// This has the effect of hiding the contents of the overflow table such that it appears as
248    /// if the overflow table in the new context is empty.
249    pub fn start_context(&mut self) -> (usize, Felt) {
250        // Return the current stack depth and overflow address at the start of a new context
251        let current_depth = self.stack_depth;
252        let current_overflow_addr = self.last_overflow_addr;
253
254        // Reset stack depth to minimum (parallel to Process Stack behavior)
255        self.stack_depth = MIN_STACK_DEPTH;
256        self.last_overflow_addr = ZERO;
257
258        (current_depth, current_overflow_addr)
259    }
260
261    /// Restores the prior context for this stack.
262    ///
263    /// This has the effect bringing back items previously hidden from the overflow table.
264    pub fn restore_context(&mut self, stack_overflow_replay: &mut StackOverflowReplay) {
265        let (stack_depth, last_overflow_addr) =
266            stack_overflow_replay.replay_restore_context_overflow_addr();
267        // Restore stack depth to the value from before the context switch (parallel to Process
268        // Stack behavior)
269        self.stack_depth = stack_depth;
270        self.last_overflow_addr = last_overflow_addr;
271    }
272}
273
274/// Replay data necessary to build a trace fragment.
275///
276/// During execution, the processor records information to be replayed by the corresponding trace
277/// generator. This is done due to the fact that the trace generators don't have access to some
278/// components needed to produce those values, such as the memory chiplet, advice provider, etc. It
279/// also packages up all the necessary data for trace generators to generate trace fragments, which
280/// can be done on separate machines in parallel, for example.
281#[derive(Debug, Default)]
282pub struct ExecutionReplay {
283    pub block_stack: BlockStackReplay,
284    pub stack_overflow: StackOverflowReplay,
285    pub memory_reads: MemoryReadsReplay,
286    pub advice: AdviceReplay,
287    pub hasher: HasherResponseReplay,
288    pub mast_forest_resolution: MastForestResolutionReplay,
289}
290
291// BLOCK STACK REPLAY
292// ================================================================================================
293
294/// Replay data for the block stack.
295#[derive(Debug, Default)]
296pub struct BlockStackReplay {
297    /// The parent address, recorded when a new node is started (JOIN, SPLIT, etc).
298    node_start_parent_addr: VecDeque<Felt>,
299    /// The data needed to recover the state on an END operation.
300    node_end: VecDeque<NodeEndData>,
301    /// Extra data needed to recover the state on an END operation specifically for
302    /// CALL/SYSCALL/DYNCALL nodes (which start/end a new execution context).
303    execution_contexts: VecDeque<ExecutionContextSystemInfo>,
304}
305
306impl BlockStackReplay {
307    /// Creates a new instance of `BlockStackReplay`.
308    pub fn new() -> Self {
309        Self {
310            node_start_parent_addr: VecDeque::new(),
311            node_end: VecDeque::new(),
312            execution_contexts: VecDeque::new(),
313        }
314    }
315
316    /// Records the node's parent address
317    pub fn record_node_start_parent_addr(&mut self, parent_addr: Felt) {
318        self.node_start_parent_addr.push_back(parent_addr);
319    }
320
321    /// Records the necessary data needed to properly recover the state on an END operation.
322    ///
323    /// See [NodeEndData] for more details.
324    pub fn record_node_end(
325        &mut self,
326        ended_node_addr: Felt,
327        flags: NodeFlags,
328        prev_addr: Felt,
329        prev_parent_addr: Felt,
330    ) {
331        self.node_end.push_back(NodeEndData {
332            ended_node_addr,
333            flags,
334            prev_addr,
335            prev_parent_addr,
336        });
337    }
338
339    /// Records an execution context system info for a CALL/SYSCALL/DYNCALL operation.
340    pub fn record_execution_context(&mut self, ctx_info: ExecutionContextSystemInfo) {
341        self.execution_contexts.push_back(ctx_info);
342    }
343
344    /// Replays the node's parent address
345    pub fn replay_node_start_parent_addr(&mut self) -> Felt {
346        self.node_start_parent_addr
347            .pop_front()
348            .expect("No node start parent address recorded")
349    }
350
351    /// Replays the data needed to recover the state on an END operation.
352    pub fn replay_node_end(&mut self) -> NodeEndData {
353        self.node_end.pop_front().expect("No node address and flags recorded")
354    }
355
356    /// Replays the next recorded execution context system info.
357    pub fn replay_execution_context(&mut self) -> ExecutionContextSystemInfo {
358        self.execution_contexts.pop_front().expect("No execution context recorded")
359    }
360}
361
362/// The flags written in the second word of the hasher state for END operations.
363#[derive(Debug)]
364pub struct NodeFlags {
365    is_loop_body: bool,
366    loop_entered: bool,
367    is_call: bool,
368    is_syscall: bool,
369}
370
371impl NodeFlags {
372    /// Creates a new instance of `NodeFlags`.
373    pub fn new(is_loop_body: bool, loop_entered: bool, is_call: bool, is_syscall: bool) -> Self {
374        Self {
375            is_loop_body,
376            loop_entered,
377            is_call,
378            is_syscall,
379        }
380    }
381
382    /// Returns ONE if this node is a body of a LOOP node; otherwise returns ZERO.
383    pub fn is_loop_body(&self) -> Felt {
384        if self.is_loop_body { ONE } else { ZERO }
385    }
386
387    /// Returns ONE if this is a LOOP node and the body of the loop was executed at
388    /// least once; otherwise, returns ZERO.
389    pub fn loop_entered(&self) -> Felt {
390        if self.loop_entered { ONE } else { ZERO }
391    }
392
393    /// Returns ONE if this node is a CALL or DYNCALL; otherwise returns ZERO.
394    pub fn is_call(&self) -> Felt {
395        if self.is_call { ONE } else { ZERO }
396    }
397
398    /// Returns ONE if this node is a SYSCALL; otherwise returns ZERO.
399    pub fn is_syscall(&self) -> Felt {
400        if self.is_syscall { ONE } else { ZERO }
401    }
402
403    /// Convenience method that writes the flags in the proper order to be written to the second
404    /// word of the hasher state for the trace row of an END operation.
405    pub fn to_hasher_state_second_word(&self) -> Word {
406        [self.is_loop_body(), self.loop_entered(), self.is_call(), self.is_syscall()].into()
407    }
408}
409
410/// The data needed to fully recover the state on an END operation.
411///
412/// We record `ended_node_addr` and `flags` in order to be able to properly populate the trace
413/// row for the node operation. Additionally, we record `prev_addr` and `prev_parent_addr` to
414/// allow emulating peeking into the block stack, which is needed when processing REPEAT or RESPAN
415/// nodes.
416#[derive(Debug)]
417pub struct NodeEndData {
418    /// the address of the node that is ending
419    pub ended_node_addr: Felt,
420    /// the flags associated with the node that is ending
421    pub flags: NodeFlags,
422    /// the address of the node sitting on top of the block stack after the END operation (or 0 if
423    /// the block stack is empty)
424    pub prev_addr: Felt,
425    /// the parent address of the node sitting on top of the block stack after the END operation
426    /// (or 0 if the block stack is empty)
427    pub prev_parent_addr: Felt,
428}
429
430/// Data required to recover the state of an execution context when restoring it during an END
431/// operation.
432#[derive(Debug)]
433pub struct ExecutionContextSystemInfo {
434    pub parent_ctx: ContextId,
435    pub parent_fn_hash: Word,
436}
437
438// MAST FOREST RESOLUTION REPLAY
439// ================================================================================================
440
441/// Records and replays the resolutions of [`crate::host::AsyncHost::get_mast_forest`] or
442/// [`crate::host::SyncHost::get_mast_forest`].
443///
444/// These calls are made when encountering an [`miden_core::mast::ExternalNode`], or when
445/// encountering a [`miden_core::mast::DynNode`] where the procedure hash on the stack refers to
446/// a procedure not present in the current forest.
447#[derive(Debug, Default)]
448pub struct MastForestResolutionReplay {
449    mast_forest_resolutions: VecDeque<(MastNodeId, Arc<MastForest>)>,
450}
451
452impl MastForestResolutionReplay {
453    /// Records a resolution of a MastNodeId with its associated MastForest when encountering an
454    /// External node, or `DYN`/`DYNCALL` node with an external procedure hash on the stack.
455    pub fn record_resolution(&mut self, node_id: MastNodeId, forest: Arc<MastForest>) {
456        self.mast_forest_resolutions.push_back((node_id, forest));
457    }
458
459    /// Replays the next recorded MastForest resolution, returning both the node ID and forest
460    pub fn replay_resolution(&mut self) -> (MastNodeId, Arc<MastForest>) {
461        self.mast_forest_resolutions
462            .pop_front()
463            .expect("No MastForest resolutions recorded")
464    }
465}
466
467// MEMORY REPLAY
468// ================================================================================================
469
470/// Records and replays all the reads made to memory, in which all elements and words read from
471/// memory during a given fragment are recorded by the fast processor, and replayed by the main
472/// trace fragment generators.
473///
474/// This is used to simulate memory reads in parallel trace generation without needing to actually
475/// access the memory chiplet.
476///
477/// Elements/words read are stored with their addresses and are assumed to be read from the same
478/// addresses that they were recorded at. This works naturally since the fast processor has exactly
479/// the same access patterns as the main trace generators (which re-executes part of the program).
480/// The read methods include debug assertions to verify address consistency.
481#[derive(Debug, Default)]
482pub struct MemoryReadsReplay {
483    elements_read: VecDeque<(Felt, Felt, ContextId, RowIndex)>,
484    words_read: VecDeque<(Word, Felt, ContextId, RowIndex)>,
485}
486
487impl MemoryReadsReplay {
488    // MUTATIONS (populated by the fast processor)
489    // --------------------------------------------------------------------------------
490
491    /// Records a read element from memory
492    pub fn record_read_element(
493        &mut self,
494        element: Felt,
495        addr: Felt,
496        ctx: ContextId,
497        clk: RowIndex,
498    ) {
499        self.elements_read.push_back((element, addr, ctx, clk));
500    }
501
502    /// Records a read word from memory
503    pub fn record_read_word(&mut self, word: Word, addr: Felt, ctx: ContextId, clk: RowIndex) {
504        self.words_read.push_back((word, addr, ctx, clk));
505    }
506
507    // ACCESSORS
508    // --------------------------------------------------------------------------------
509
510    pub fn replay_read_element(&mut self, addr: Felt) -> Felt {
511        let (element, stored_addr, _ctx, _clk) =
512            self.elements_read.pop_front().expect("No elements read from memory");
513        debug_assert_eq!(stored_addr, addr, "Address mismatch: expected {addr}, got {stored_addr}");
514        element
515    }
516
517    pub fn replay_read_word(&mut self, addr: Felt) -> Word {
518        let (word, stored_addr, _ctx, _clk) =
519            self.words_read.pop_front().expect("No words read from memory");
520        debug_assert_eq!(stored_addr, addr, "Address mismatch: expected {addr}, got {stored_addr}");
521        word
522    }
523
524    /// Returns an iterator over all recorded memory element reads, yielding tuples of
525    /// (element, address, context ID, clock cycle).
526    pub fn iter_read_elements(&self) -> impl Iterator<Item = (Felt, Felt, ContextId, RowIndex)> {
527        self.elements_read.iter().copied()
528    }
529
530    /// Returns an iterator over all recorded memory word reads, yielding tuples of
531    /// (word, address, context ID, clock cycle).
532    pub fn iter_read_words(&self) -> impl Iterator<Item = (Word, Felt, ContextId, RowIndex)> {
533        self.words_read.iter().copied()
534    }
535}
536
537/// Records and replays all the writes made to memory, in which all elements written to memory
538/// throughout a program's execution are recorded by the fast processor.
539///
540/// This is separated from [MemoryReadsReplay] since writes are not needed for core trace generation
541/// (as reads are), but only to be able to fully build the memory chiplet trace.
542#[derive(Debug, Default)]
543pub struct MemoryWritesReplay {
544    elements_written: VecDeque<(Felt, Felt, ContextId, RowIndex)>,
545    words_written: VecDeque<(Word, Felt, ContextId, RowIndex)>,
546}
547
548impl MemoryWritesReplay {
549    /// Records a write element to memory
550    pub fn record_write_element(
551        &mut self,
552        element: Felt,
553        addr: Felt,
554        ctx: ContextId,
555        clk: RowIndex,
556    ) {
557        self.elements_written.push_back((element, addr, ctx, clk));
558    }
559
560    /// Records a write word to memory
561    pub fn record_write_word(&mut self, word: Word, addr: Felt, ctx: ContextId, clk: RowIndex) {
562        self.words_written.push_back((word, addr, ctx, clk));
563    }
564
565    /// Returns an iterator over all recorded memory element writes, yielding tuples of
566    /// (element, address, context ID, clock cycle).
567    pub fn iter_elements_written(
568        &self,
569    ) -> impl Iterator<Item = &(Felt, Felt, ContextId, RowIndex)> {
570        self.elements_written.iter()
571    }
572
573    /// Returns an iterator over all recorded memory word writes, yielding tuples of
574    /// (word, address, context ID, clock cycle).
575    pub fn iter_words_written(&self) -> impl Iterator<Item = &(Word, Felt, ContextId, RowIndex)> {
576        self.words_written.iter()
577    }
578}
579
580impl MemoryInterface for MemoryReadsReplay {
581    fn read_element(
582        &mut self,
583        _ctx: ContextId,
584        addr: Felt,
585        _err_ctx: &impl ErrorContext,
586    ) -> Result<Felt, crate::MemoryError> {
587        Ok(self.replay_read_element(addr))
588    }
589
590    fn read_word(
591        &mut self,
592        _ctx: ContextId,
593        addr: Felt,
594        _clk: RowIndex,
595        _err_ctx: &impl ErrorContext,
596    ) -> Result<Word, crate::MemoryError> {
597        Ok(self.replay_read_word(addr))
598    }
599
600    fn write_element(
601        &mut self,
602        _ctx: ContextId,
603        _addr: Felt,
604        _element: Felt,
605        _err_ctx: &impl ErrorContext,
606    ) -> Result<(), crate::MemoryError> {
607        Ok(())
608    }
609
610    fn write_word(
611        &mut self,
612        _ctx: ContextId,
613        _addr: Felt,
614        _clk: RowIndex,
615        _word: Word,
616        _err_ctx: &impl ErrorContext,
617    ) -> Result<(), crate::MemoryError> {
618        Ok(())
619    }
620}
621
622// ADVICE REPLAY
623// ================================================================================================
624
625/// Implements a shim for the advice provider, in which all advice provider operations during a
626/// given fragment are pre-recorded by the fast processor.
627///
628/// This is used to simulate advice provider interactions in parallel trace generation without
629/// needing to actually access the advice provider. All advice provider operations are recorded
630/// during fast execution and then replayed during parallel trace generation.
631///
632/// The shim records all operations with their parameters and results, and provides replay methods
633/// that return the pre-recorded results. This works naturally since the fast processor has exactly
634/// the same access patterns as the main trace generators (which re-executes part of the program).
635/// The read methods include debug assertions to verify parameter consistency.
636#[derive(Debug, Default)]
637pub struct AdviceReplay {
638    // Stack operations
639    stack_pops: VecDeque<Felt>,
640    stack_word_pops: VecDeque<Word>,
641    stack_dword_pops: VecDeque<[Word; 2]>,
642}
643
644impl AdviceReplay {
645    // MUTATIONS (populated by the fast processor)
646    // --------------------------------------------------------------------------------
647
648    /// Records the value returned by a pop_stack operation
649    pub fn record_pop_stack(&mut self, value: Felt) {
650        self.stack_pops.push_back(value);
651    }
652
653    /// Records the word returned by a pop_stack_word operation
654    pub fn record_pop_stack_word(&mut self, word: Word) {
655        self.stack_word_pops.push_back(word);
656    }
657
658    /// Records the double word returned by a pop_stack_dword operation
659    pub fn record_pop_stack_dword(&mut self, dword: [Word; 2]) {
660        self.stack_dword_pops.push_back(dword);
661    }
662
663    // ACCESSORS (used during parallel trace generation)
664    // --------------------------------------------------------------------------------
665
666    /// Replays a pop_stack operation, returning the previously recorded value
667    pub fn replay_pop_stack(&mut self) -> Felt {
668        self.stack_pops.pop_front().expect("No stack pop operations recorded")
669    }
670
671    /// Replays a pop_stack_word operation, returning the previously recorded word
672    pub fn replay_pop_stack_word(&mut self) -> Word {
673        self.stack_word_pops.pop_front().expect("No stack word pop operations recorded")
674    }
675
676    /// Replays a pop_stack_dword operation, returning the previously recorded double word
677    pub fn replay_pop_stack_dword(&mut self) -> [Word; 2] {
678        self.stack_dword_pops
679            .pop_front()
680            .expect("No stack dword pop operations recorded")
681    }
682}
683
684impl AdviceProviderInterface for AdviceReplay {
685    fn pop_stack(&mut self) -> Result<Felt, AdviceError> {
686        Ok(self.replay_pop_stack())
687    }
688
689    fn pop_stack_word(&mut self) -> Result<Word, AdviceError> {
690        Ok(self.replay_pop_stack_word())
691    }
692
693    fn pop_stack_dword(&mut self) -> Result<[Word; 2], AdviceError> {
694        Ok(self.replay_pop_stack_dword())
695    }
696
697    /// Returns an empty Merkle path, as Merkle paths are ignored in parallel trace generation.
698    fn get_merkle_path(
699        &self,
700        _root: Word,
701        _depth: Felt,
702        _index: Felt,
703    ) -> Result<Option<MerklePath>, AdviceError> {
704        Ok(None)
705    }
706
707    /// Returns an empty Merkle path and root, as they are ignored in parallel trace generation.
708    fn update_merkle_node(
709        &mut self,
710        _root: Word,
711        _depth: Felt,
712        _index: Felt,
713        _value: Word,
714    ) -> Result<Option<MerklePath>, AdviceError> {
715        Ok(None)
716    }
717}
718
719// BITWISE REPLAY
720// ================================================================================================
721
722/// Enum representing the different bitwise operations that can be recorded.
723#[derive(Debug)]
724pub enum BitwiseOp {
725    U32And,
726    U32Xor,
727}
728
729/// Replay data for bitwise operations.
730#[derive(Debug, Default)]
731pub struct BitwiseReplay {
732    u32op_with_operands: VecDeque<(BitwiseOp, Felt, Felt)>,
733}
734
735impl BitwiseReplay {
736    // MUTATIONS (populated by the fast processor)
737    // --------------------------------------------------------------------------------
738
739    /// Records the operands of a u32and operation.
740    pub fn record_u32and(&mut self, a: Felt, b: Felt) {
741        self.u32op_with_operands.push_back((BitwiseOp::U32And, a, b));
742    }
743
744    /// Records the operands of a u32xor operation.
745    pub fn record_u32xor(&mut self, a: Felt, b: Felt) {
746        self.u32op_with_operands.push_back((BitwiseOp::U32Xor, a, b));
747    }
748}
749
750impl IntoIterator for BitwiseReplay {
751    type Item = (BitwiseOp, Felt, Felt);
752    type IntoIter = <VecDeque<(BitwiseOp, Felt, Felt)> as IntoIterator>::IntoIter;
753
754    /// Returns an iterator over all recorded u32 operations with their operands.
755    fn into_iter(self) -> Self::IntoIter {
756        self.u32op_with_operands.into_iter()
757    }
758}
759
760// KERNEL REPLAY
761// ================================================================================================
762
763/// Replay data for kernel operations.
764#[derive(Debug, Default)]
765pub struct KernelReplay {
766    kernel_proc_accesses: VecDeque<Word>,
767}
768
769impl KernelReplay {
770    // MUTATIONS (populated by the fast processor)
771    // --------------------------------------------------------------------------------
772
773    /// Records the procedure hash of a syscall.
774    pub fn record_kernel_proc_access(&mut self, proc_hash: Word) {
775        self.kernel_proc_accesses.push_back(proc_hash);
776    }
777}
778
779impl IntoIterator for KernelReplay {
780    type Item = Word;
781    type IntoIter = <VecDeque<Word> as IntoIterator>::IntoIter;
782
783    /// Returns an iterator over all recorded kernel procedure accesses.
784    fn into_iter(self) -> Self::IntoIter {
785        self.kernel_proc_accesses.into_iter()
786    }
787}
788
789// ACE REPLAY
790// ================================================================================================
791
792/// Replay data for ACE operations.
793#[derive(Debug, Default)]
794pub struct AceReplay {
795    circuit_evaluations: VecDeque<(RowIndex, CircuitEvaluation)>,
796}
797
798impl AceReplay {
799    // MUTATIONS (populated by the fast processor)
800    // --------------------------------------------------------------------------------
801
802    /// Records the procedure hash of a syscall.
803    pub fn record_circuit_evaluation(&mut self, clk: RowIndex, circuit_eval: CircuitEvaluation) {
804        self.circuit_evaluations.push_back((clk, circuit_eval));
805    }
806}
807
808impl IntoIterator for AceReplay {
809    type Item = (RowIndex, CircuitEvaluation);
810    type IntoIter = <VecDeque<(RowIndex, CircuitEvaluation)> as IntoIterator>::IntoIter;
811
812    /// Returns an iterator over all recorded circuit evaluations.
813    fn into_iter(self) -> Self::IntoIter {
814        self.circuit_evaluations.into_iter()
815    }
816}
817
818// RANGE CHECKER REPLAY
819// ================================================================================================
820
821/// Replay data for range checking operations.
822///
823/// This currently only records
824#[derive(Debug, Default)]
825pub struct RangeCheckerReplay {
826    range_checks_u32_ops: VecDeque<(RowIndex, [u16; 4])>,
827}
828
829impl RangeCheckerReplay {
830    // MUTATIONS (populated by the fast processor)
831    // --------------------------------------------------------------------------------
832
833    /// Records the set of range checks which result from a u32 operation.
834    pub fn record_range_check_u32(&mut self, row_index: RowIndex, u16_limbs: [u16; 4]) {
835        self.range_checks_u32_ops.push_back((row_index, u16_limbs));
836    }
837}
838
839impl IntoIterator for RangeCheckerReplay {
840    type Item = (RowIndex, [u16; 4]);
841    type IntoIter = <VecDeque<(RowIndex, [u16; 4])> as IntoIterator>::IntoIter;
842
843    /// Returns an iterator over all recorded range checks resulting from u32 operations.
844    fn into_iter(self) -> Self::IntoIter {
845        self.range_checks_u32_ops.into_iter()
846    }
847}
848
849// HASHER RESPONSE REPLAY
850// ================================================================================================
851
852/// Records and replays the response of requests made to the hasher chiplet during the execution of
853/// a program.
854///
855/// The hasher responses are recorded during fast processor execution and then replayed during core
856/// trace generation.
857#[derive(Debug, Default)]
858pub struct HasherResponseReplay {
859    /// Recorded hasher addresses from operations like hash_control_block, hash_basic_block, etc.
860    block_addresses: VecDeque<Felt>,
861
862    /// Recorded hasher operations from permutation operations (HPerm).
863    ///
864    /// Each entry contains (address, output_state)
865    permutation_operations: VecDeque<(Felt, [Felt; 12])>,
866
867    /// Recorded hasher operations from Merkle path verification operations.
868    ///
869    /// Each entry contains (address, computed_root)
870    build_merkle_root_operations: VecDeque<(Felt, Word)>,
871
872    /// Recorded hasher operations from Merkle root update operations.
873    ///
874    /// Each entry contains (address, old_root, new_root)
875    mrupdate_operations: VecDeque<(Felt, Word, Word)>,
876}
877
878impl HasherResponseReplay {
879    // MUTATIONS (populated by the fast processor)
880    // --------------------------------------------------------------------------------------------
881
882    /// Records the address associated with a `Hasher::hash_control_block` or
883    /// `Hasher::hash_basic_block` operation.
884    pub fn record_block_address(&mut self, addr: Felt) {
885        self.block_addresses.push_back(addr);
886    }
887
888    /// Records a `Hasher::permute` operation with its address and result (after applying the
889    /// permutation)
890    pub fn record_permute(&mut self, addr: Felt, hashed_state: [Felt; 12]) {
891        self.permutation_operations.push_back((addr, hashed_state));
892    }
893
894    /// Records a Merkle path verification with its address and computed root
895    pub fn record_build_merkle_root(&mut self, addr: Felt, computed_root: Word) {
896        self.build_merkle_root_operations.push_back((addr, computed_root));
897    }
898
899    /// Records a Merkle root update with its address, old root, and new root
900    pub fn record_update_merkle_root(&mut self, addr: Felt, old_root: Word, new_root: Word) {
901        self.mrupdate_operations.push_back((addr, old_root, new_root));
902    }
903
904    // ACCESSORS (used by parallel trace generators)
905    // --------------------------------------------------------------------------------------------
906
907    /// Replays a `Hasher::hash_control_block` or `Hasher::hash_basic_block` operation, returning
908    /// the pre-recorded address
909    pub fn replay_block_address(&mut self) -> Felt {
910        self.block_addresses.pop_front().expect("No block address operations recorded")
911    }
912
913    /// Replays a `Hasher::permute` operation, returning its address and result
914    pub fn replay_permute(&mut self) -> (Felt, [Felt; 12]) {
915        self.permutation_operations
916            .pop_front()
917            .expect("No permutation operations recorded")
918    }
919
920    /// Replays a Merkle path verification, returning the pre-recorded address and computed root
921    pub fn replay_build_merkle_root(&mut self) -> (Felt, Word) {
922        self.build_merkle_root_operations
923            .pop_front()
924            .expect("No build merkle root operations recorded")
925    }
926
927    /// Replays a Merkle root update, returning the pre-recorded address, old root, and new root
928    pub fn replay_update_merkle_root(&mut self) -> (Felt, Word, Word) {
929        self.mrupdate_operations.pop_front().expect("No mrupdate operations recorded")
930    }
931}
932
933impl HasherInterface for HasherResponseReplay {
934    fn permute(&mut self, _state: HasherState) -> (Felt, HasherState) {
935        self.replay_permute()
936    }
937
938    fn verify_merkle_root(
939        &mut self,
940        claimed_root: Word,
941        _value: Word,
942        _path: Option<&MerklePath>,
943        _index: Felt,
944        on_err: impl FnOnce() -> ExecutionError,
945    ) -> Result<Felt, ExecutionError> {
946        let (addr, computed_root) = self.replay_build_merkle_root();
947        if claimed_root == computed_root {
948            Ok(addr)
949        } else {
950            // If the hasher doesn't compute the same root (using the same path),
951            // then it means that `node` is not the value currently in the tree at `index`
952            Err(on_err())
953        }
954    }
955
956    fn update_merkle_root(
957        &mut self,
958        claimed_old_root: Word,
959        _old_value: Word,
960        _new_value: Word,
961        _path: Option<&MerklePath>,
962        _index: Felt,
963        on_err: impl FnOnce() -> ExecutionError,
964    ) -> Result<(Felt, Word), ExecutionError> {
965        let (address, old_root, new_root) = self.replay_update_merkle_root();
966
967        if claimed_old_root == old_root {
968            Ok((address, new_root))
969        } else {
970            Err(on_err())
971        }
972    }
973}
974
975/// Enum representing the different hasher operations that can be recorded, along with their
976/// operands.
977#[derive(Debug)]
978pub enum HasherOp {
979    Permute([Felt; STATE_WIDTH]),
980    HashControlBlock((Word, Word, Felt, Word)),
981    HashBasicBlock((Vec<OpBatch>, Word)),
982    BuildMerkleRoot((Word, MerklePath, Felt)),
983    UpdateMerkleRoot((Word, Word, MerklePath, Felt)),
984}
985
986/// Records and replays all the requests made to the hasher chiplet during the execution of a
987/// program, for the purposes of generating the hasher chiplet's trace.
988///
989/// The hasher requests are recorded during fast processor execution and then replayed during hasher
990/// chiplet trace generation.
991#[derive(Debug, Default)]
992pub struct HasherRequestReplay {
993    hasher_ops: VecDeque<HasherOp>,
994}
995
996impl HasherRequestReplay {
997    /// Records a `Hasher::permute()` request.
998    pub fn record_permute_input(&mut self, state: [Felt; STATE_WIDTH]) {
999        self.hasher_ops.push_back(HasherOp::Permute(state));
1000    }
1001
1002    /// Records a `Hasher::hash_control_block()` request.
1003    pub fn record_hash_control_block(
1004        &mut self,
1005        h1: Word,
1006        h2: Word,
1007        domain: Felt,
1008        expected_hash: Word,
1009    ) {
1010        self.hasher_ops
1011            .push_back(HasherOp::HashControlBlock((h1, h2, domain, expected_hash)));
1012    }
1013
1014    /// Records a `Hasher::hash_basic_block()` request.
1015    pub fn record_hash_basic_block(&mut self, op_batches: Vec<OpBatch>, expected_hash: Word) {
1016        self.hasher_ops.push_back(HasherOp::HashBasicBlock((op_batches, expected_hash)));
1017    }
1018
1019    /// Records a `Hasher::build_merkle_root()` request.
1020    pub fn record_build_merkle_root(&mut self, leaf: Word, path: MerklePath, index: Felt) {
1021        self.hasher_ops.push_back(HasherOp::BuildMerkleRoot((leaf, path, index)));
1022    }
1023
1024    /// Records a `Hasher::update_merkle_root()` request.
1025    pub fn record_update_merkle_root(
1026        &mut self,
1027        old_value: Word,
1028        new_value: Word,
1029        path: MerklePath,
1030        index: Felt,
1031    ) {
1032        self.hasher_ops
1033            .push_back(HasherOp::UpdateMerkleRoot((old_value, new_value, path, index)));
1034    }
1035}
1036
1037impl IntoIterator for HasherRequestReplay {
1038    type Item = HasherOp;
1039    type IntoIter = <VecDeque<HasherOp> as IntoIterator>::IntoIter;
1040
1041    fn into_iter(self) -> Self::IntoIter {
1042        self.hasher_ops.into_iter()
1043    }
1044}
1045
1046// STACK OVERFLOW REPLAY
1047// ================================================================================================
1048
1049/// Implements a shim for stack overflow operations, in which all overflow values and addresses
1050/// during a given fragment are pre-recorded by the fast processor and replayed by the main trace
1051/// fragment generators.
1052///
1053/// This is used to simulate stack overflow functionality in parallel trace generation without
1054/// needing to maintain the actual overflow table. All overflow operations are recorded during
1055/// fast execution and then replayed during parallel trace generation.
1056///
1057/// The shim records overflow values (from pop operations) and overflow addresses (representing
1058/// the clock cycle of the last overflow update) and provides replay methods that return the
1059/// pre-recorded values. This works naturally since the fast processor has exactly the same
1060/// access patterns as the main trace generators.
1061#[derive(Debug)]
1062pub struct StackOverflowReplay {
1063    /// Recorded overflow values and overflow addresses from pop_overflow operations. Each entry
1064    /// represents a value that was popped from the overflow stack, and the overflow address of the
1065    /// entry at the top of the overflow stack *after* the pop operation.
1066    ///
1067    /// For example, given the following table:
1068    ///
1069    /// | Overflow Value | Overflow Address |
1070    /// |----------------|------------------|
1071    /// |      8         |         14       |
1072    /// |      2         |         16       |
1073    /// |      7         |         18       |
1074    ///
1075    /// a `pop_overflow()` operation would return (popped_value, prev_addr) = (7, 16).
1076    overflow_values: VecDeque<(Felt, Felt)>,
1077
1078    /// Recorded (stack depth, overflow address) returned when restoring a context
1079    restore_context_info: VecDeque<(usize, Felt)>,
1080}
1081
1082impl Default for StackOverflowReplay {
1083    fn default() -> Self {
1084        Self::new()
1085    }
1086}
1087
1088impl StackOverflowReplay {
1089    /// Creates a new StackOverflowReplay with empty operation vectors
1090    pub fn new() -> Self {
1091        Self {
1092            overflow_values: VecDeque::new(),
1093            restore_context_info: VecDeque::new(),
1094        }
1095    }
1096
1097    // MUTATORS
1098    // --------------------------------------------------------------------------------
1099
1100    /// Records the value returned by a pop_overflow operation, along with the overflow address
1101    /// stored in the overflow table *after* the pop. That is, `new_overflow_addr` represents the
1102    /// clock cycle at which the value *before* `value` was added to the overflow table. See the
1103    /// docstring for the `overflow_values` field for more information.
1104    ///
1105    /// This *must* only be called if there is an actual value in the overflow table to pop; that
1106    /// is, don't call if the stack depth is 16.
1107    pub fn record_pop_overflow(&mut self, value: Felt, new_overflow_addr: Felt) {
1108        self.overflow_values.push_back((value, new_overflow_addr));
1109    }
1110
1111    /// Records the overflow address when restoring a context
1112    pub fn record_restore_context_overflow_addr(&mut self, stack_depth: usize, addr: Felt) {
1113        self.restore_context_info.push_back((stack_depth, addr));
1114    }
1115
1116    // ACCESSORS
1117    // --------------------------------------------------------------------------------
1118
1119    /// Replays a pop_overflow operation, returning the previously recorded value and
1120    /// `new_overflow_addr`.
1121    ///
1122    /// This *must* only be called if there is an actual value in the overflow table to pop; that
1123    /// is, don't call if the stack depth is 16.
1124    ///
1125    /// See [Self::record_pop_overflow] for more details.
1126    pub fn replay_pop_overflow(&mut self) -> (Felt, Felt) {
1127        self.overflow_values.pop_front().expect("No overflow pop operations recorded")
1128    }
1129
1130    /// Replays the overflow address when restoring a context
1131    pub fn replay_restore_context_overflow_addr(&mut self) -> (usize, Felt) {
1132        self.restore_context_info
1133            .pop_front()
1134            .expect("No overflow address operations recorded")
1135    }
1136}
1137
1138// NODE EXECUTION STATE
1139// ================================================================================================
1140
1141/// Specifies the execution state of a node.
1142///
1143/// Each MAST node has at least 2 different states associated with it: processing the START and END
1144/// nodes (e.g. JOIN and END in the case of [miden_core::mast::JoinNode]). Some have more; for
1145/// example, [miden_core::mast::BasicBlockNode] has SPAN and END, in addition to one state for each
1146/// operation in the basic block. Since a trace fragment can begin at any clock cycle (determined by
1147/// the configured fragment size), specifying which MAST node we're executing is
1148/// insufficient; we also have to specify *at what point* during the execution of this node we are
1149/// at. This is the information that this type is meant to encode.
1150#[derive(Debug, Clone, PartialEq, Eq)]
1151pub enum NodeExecutionState {
1152    /// Resume execution within a basic block at a specific batch and operation index.
1153    /// This is used when continuing execution mid-way through a basic block.
1154    BasicBlock {
1155        /// Node ID of the basic block being executed
1156        node_id: MastNodeId,
1157        /// Index of the operation batch within the basic block
1158        batch_index: usize,
1159        /// Index of the operation within the batch
1160        op_idx_in_batch: usize,
1161    },
1162    /// Execute a control flow node (JOIN, SPLIT, LOOP, etc.) from the start. This is used when
1163    /// beginning execution of a control flow construct.
1164    Start(MastNodeId),
1165    /// Execute a RESPAN for the specified batch within the specified basic block.
1166    Respan {
1167        /// Node ID of the basic block being executed
1168        node_id: MastNodeId,
1169        /// Index of the operation batch within the basic block
1170        batch_index: usize,
1171    },
1172    /// Execute a Loop node, starting at a REPEAT operation.
1173    LoopRepeat(MastNodeId),
1174    /// Execute the END phase of a control flow node (JOIN, SPLIT, LOOP, etc.).
1175    /// This is used when completing execution of a control flow construct.
1176    End(MastNodeId),
1177}