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