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}