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}