1use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
2
3use miden_air::trace::chiplets::hasher::{
4 CONTROLLER_ROWS_PER_PERM_FELT, CONTROLLER_ROWS_PER_PERMUTATION, STATE_WIDTH,
5};
6use miden_core::{FMP_ADDR, FMP_INIT_VALUE, operations::Operation};
7
8use super::{
9 block_stack::{BlockInfo, BlockStack, ExecutionContextInfo},
10 stack::OverflowTable,
11 trace_state::{
12 AceReplay, AdviceReplay, BitwiseReplay, BlockAddressReplay, BlockStackReplay,
13 CoreTraceFragmentContext, CoreTraceState, DecoderState, ExecutionContextReplay,
14 ExecutionContextSystemInfo, ExecutionReplay, HasherRequestReplay, HasherResponseReplay,
15 KernelReplay, MastForestResolutionReplay, MemoryReadsReplay, MemoryWritesReplay,
16 RangeCheckerReplay, StackOverflowReplay, StackState, SystemState,
17 },
18 utils::split_u32_into_u16,
19};
20use crate::{
21 ContextId, EMPTY_WORD, ExecutionError, FastProcessor, Felt, MIN_STACK_DEPTH, ONE, RowIndex,
22 Word, ZERO,
23 continuation_stack::{Continuation, ContinuationStack},
24 crypto::merkle::MerklePath,
25 mast::{
26 BasicBlockNode, JoinNode, LoopNode, MastForest, MastForestId, MastNode, MastNodeExt,
27 MastNodeId, SparseMastForest, SparseMastForestBuilder, SplitNode, VisitKind,
28 },
29 processor::{Processor, StackInterface, SystemInterface},
30 trace::chiplets::{CircuitEvaluation, PTR_OFFSET_ELEM, PTR_OFFSET_WORD},
31 tracer::{OperationHelperRegisters, Tracer},
32 utils::Idx,
33};
34
35#[derive(Debug)]
40struct StateSnapshot {
41 state: CoreTraceState,
42 continuation_stack: ContinuationStack<MastForestId>,
45 initial_mast_forest_id: MastForestId,
47}
48
49#[derive(Debug)]
53pub struct TraceGenerationContext {
54 pub core_trace_contexts: Vec<CoreTraceFragmentContext>,
56
57 pub mast_forest_store: Vec<Arc<SparseMastForest>>,
64
65 pub range_checker_replay: RangeCheckerReplay,
68 pub memory_writes: MemoryWritesReplay,
69 pub bitwise_replay: BitwiseReplay,
70 pub hasher_for_chiplet: HasherRequestReplay,
71 pub kernel_replay: KernelReplay,
72 pub ace_replay: AceReplay,
73
74 pub fragment_size: usize,
77
78 pub max_stack_depth: usize,
81}
82
83#[derive(Debug)]
96pub struct ExecutionTracer {
97 state_snapshot: Option<StateSnapshot>,
104
105 overflow_table: OverflowTable,
107 overflow_replay: StackOverflowReplay,
108
109 block_stack: BlockStack,
110 block_stack_replay: BlockStackReplay,
111 execution_context_replay: ExecutionContextReplay,
112
113 hasher_chiplet_shim: HasherChipletShim,
114 memory_reads: MemoryReadsReplay,
115 advice: AdviceReplay,
116 external: MastForestResolutionReplay,
117
118 range_checker: RangeCheckerReplay,
121 memory_writes: MemoryWritesReplay,
122 bitwise: BitwiseReplay,
123 kernel: KernelReplay,
124 hasher_for_chiplet: HasherRequestReplay,
125 ace: AceReplay,
126
127 fragment_contexts: Vec<CoreTraceFragmentContext>,
129
130 mast_forest_builders: Vec<SparseMastForestBuilder>,
136
137 mast_forest_ids: BTreeMap<*const MastForest, MastForestId>,
145
146 fragment_size: usize,
148
149 max_stack_depth: usize,
152
153 pending_restore_context: bool,
158
159 is_eval_circuit_op: bool,
162}
163
164impl ExecutionTracer {
165 #[cfg(feature = "std")]
169 pub(crate) fn new_with_streamed_hasher(
170 fragment_size: usize,
171 max_stack_depth: usize,
172 hasher_sender: std::sync::mpsc::Sender<crate::trace::ResolvedHasherOp<'static>>,
173 ) -> Self {
174 let mut tracer = Self::new(fragment_size, max_stack_depth);
175 tracer.hasher_for_chiplet = HasherRequestReplay::streamed(hasher_sender);
176 tracer
177 }
178
179 #[inline(always)]
181 pub fn new(fragment_size: usize, max_stack_depth: usize) -> Self {
182 Self {
183 state_snapshot: None,
184 overflow_table: OverflowTable::default(),
185 overflow_replay: StackOverflowReplay::default(),
186 block_stack: BlockStack::default(),
187 block_stack_replay: BlockStackReplay::default(),
188 execution_context_replay: ExecutionContextReplay::default(),
189 hasher_chiplet_shim: HasherChipletShim::default(),
190 memory_reads: MemoryReadsReplay::default(),
191 range_checker: RangeCheckerReplay::default(),
192 memory_writes: MemoryWritesReplay::default(),
193 advice: AdviceReplay::default(),
194 bitwise: BitwiseReplay::default(),
195 kernel: KernelReplay::default(),
196 hasher_for_chiplet: HasherRequestReplay::default(),
197 ace: AceReplay::default(),
198 external: MastForestResolutionReplay::default(),
199 fragment_contexts: Vec::new(),
200 mast_forest_builders: Vec::new(),
201 mast_forest_ids: BTreeMap::new(),
202 fragment_size,
203 max_stack_depth,
204 pending_restore_context: false,
205 is_eval_circuit_op: false,
206 }
207 }
208
209 #[inline]
212 fn forest_id(&mut self, forest: &Arc<MastForest>) -> MastForestId {
213 let key = Arc::as_ptr(forest);
214 if let Some(&id) = self.mast_forest_ids.get(&key) {
215 return id;
216 }
217
218 let id = MastForestId::from(self.mast_forest_builders.len() as u32);
219 self.mast_forest_builders.push(SparseMastForestBuilder::new(forest.clone()));
220 self.mast_forest_ids.insert(key, id);
221 id
222 }
223
224 #[inline]
237 fn record_visit(&mut self, forest: &Arc<MastForest>, node_id: MastNodeId) {
238 let id = self.forest_id(forest);
239 self.mast_forest_builders[id.to_usize()].record_visit(node_id, VisitKind::FullVisit);
240
241 if let Some(node) = forest.get_node_by_id(node_id) {
242 node.for_each_child(|child_id| {
243 self.mast_forest_builders[id.to_usize()]
244 .record_visit(child_id, VisitKind::DigestOnly);
245 });
246 }
247 }
248
249 fn translate_continuation_stack(
252 &mut self,
253 live: ContinuationStack<Arc<MastForest>>,
254 ) -> ContinuationStack<MastForestId> {
255 let mut translated: ContinuationStack<MastForestId> = ContinuationStack::default();
256 for cont in live.into_inner() {
257 let translated_cont = match cont {
258 Continuation::EnterForest { forest, package_debug_info } => {
259 Continuation::EnterForest {
260 forest: self.forest_id(&forest),
261 package_debug_info,
262 }
263 },
264 Continuation::StartNode(id) => Continuation::StartNode(id),
265 Continuation::FinishJoin(id) => Continuation::FinishJoin(id),
266 Continuation::FinishSplit(id) => Continuation::FinishSplit(id),
267 Continuation::FinishLoop(node_id) => Continuation::FinishLoop(node_id),
268 Continuation::FinishCall(id) => Continuation::FinishCall(id),
269 Continuation::FinishDyn(id) => Continuation::FinishDyn(id),
270 Continuation::ResumeBasicBlock { node_id, batch_index, op_idx_in_batch } => {
271 Continuation::ResumeBasicBlock { node_id, batch_index, op_idx_in_batch }
272 },
273 Continuation::Respan { node_id, batch_index } => {
274 Continuation::Respan { node_id, batch_index }
275 },
276 Continuation::FinishBasicBlock(id) => Continuation::FinishBasicBlock(id),
277 };
278 translated.push_continuation(translated_cont);
279 }
280 translated
281 }
282
283 #[inline(always)]
286 pub fn into_trace_generation_context(mut self) -> TraceGenerationContext {
287 self.finish_current_fragment_context();
289
290 let mast_forest_store = self
294 .mast_forest_builders
295 .into_iter()
296 .map(|builder| Arc::new(builder.finalize()))
297 .collect();
298
299 TraceGenerationContext {
300 core_trace_contexts: self.fragment_contexts,
301 mast_forest_store,
302 range_checker_replay: self.range_checker,
303 memory_writes: self.memory_writes,
304 bitwise_replay: self.bitwise,
305 kernel_replay: self.kernel,
306 hasher_for_chiplet: self.hasher_for_chiplet,
307 ace_replay: self.ace,
308 fragment_size: self.fragment_size,
309 max_stack_depth: self.max_stack_depth,
310 }
311 }
312
313 #[inline(always)]
324 fn start_new_fragment_context(
325 &mut self,
326 system_state: SystemState,
327 stack_top: [Felt; MIN_STACK_DEPTH],
328 mut continuation_stack: ContinuationStack<Arc<MastForest>>,
329 continuation: Continuation<Arc<MastForest>>,
330 current_forest: Arc<MastForest>,
331 ) {
332 self.finish_current_fragment_context();
334
335 let decoder_state = {
337 if self.block_stack.is_empty() {
338 DecoderState { current_addr: ZERO, parent_addr: ZERO }
339 } else {
340 let block_info = self.block_stack.peek();
341
342 DecoderState {
343 current_addr: block_info.addr,
344 parent_addr: block_info.parent_addr,
345 }
346 }
347 };
348 let stack = {
349 let stack_depth = MIN_STACK_DEPTH + self.overflow_table.num_elements_in_current_ctx();
350 let last_overflow_addr = self.overflow_table.last_update_clk_in_current_ctx();
351 StackState::new(stack_top, stack_depth, last_overflow_addr)
352 };
353
354 continuation_stack.push_continuation(continuation);
356
357 let initial_mast_forest_id = self.forest_id(¤t_forest);
361 let translated_stack = self.translate_continuation_stack(continuation_stack);
362
363 self.state_snapshot = Some(StateSnapshot {
364 state: CoreTraceState {
365 system: system_state,
366 decoder: decoder_state,
367 stack,
368 },
369 continuation_stack: translated_stack,
370 initial_mast_forest_id,
371 });
372 }
373
374 #[inline(always)]
375 fn record_control_node_start<P: Processor>(
376 &mut self,
377 node: &MastNode,
378 processor: &P,
379 current_forest: &MastForest,
380 ) {
381 let ctx_info = match node {
382 MastNode::Join(node) => {
383 let child1_hash = current_forest
384 .get_node_by_id(node.first())
385 .expect("join node's first child expected to be in the forest")
386 .digest();
387 let child2_hash = current_forest
388 .get_node_by_id(node.second())
389 .expect("join node's second child expected to be in the forest")
390 .digest();
391 self.hasher_for_chiplet.record_hash_control_block(
392 child1_hash,
393 child2_hash,
394 JoinNode::DOMAIN,
395 node.digest(),
396 );
397
398 None
399 },
400 MastNode::Split(node) => {
401 let child1_hash = current_forest
402 .get_node_by_id(node.on_true())
403 .expect("split node's true child expected to be in the forest")
404 .digest();
405 let child2_hash = current_forest
406 .get_node_by_id(node.on_false())
407 .expect("split node's false child expected to be in the forest")
408 .digest();
409 self.hasher_for_chiplet.record_hash_control_block(
410 child1_hash,
411 child2_hash,
412 SplitNode::DOMAIN,
413 node.digest(),
414 );
415
416 None
417 },
418 MastNode::Loop(node) => {
419 let body_hash = current_forest
420 .get_node_by_id(node.body())
421 .expect("loop node's body expected to be in the forest")
422 .digest();
423
424 self.hasher_for_chiplet.record_hash_control_block(
425 body_hash,
426 EMPTY_WORD,
427 LoopNode::DOMAIN,
428 node.digest(),
429 );
430
431 None
432 },
433 MastNode::Call(node) => {
434 let callee_hash = current_forest
435 .get_node_by_id(node.callee())
436 .expect("call node's callee expected to be in the forest")
437 .digest();
438
439 self.hasher_for_chiplet.record_hash_control_block(
440 callee_hash,
441 EMPTY_WORD,
442 node.domain(),
443 node.digest(),
444 );
445
446 let overflow_addr = self.overflow_table.last_update_clk_in_current_ctx();
447 Some(ExecutionContextInfo::new(
448 processor.system().ctx(),
449 processor.system().caller_hash(),
450 processor.stack().depth(),
451 overflow_addr,
452 ))
453 },
454 MastNode::Dyn(dyn_node) => {
455 self.hasher_for_chiplet.record_hash_control_block(
456 EMPTY_WORD,
457 EMPTY_WORD,
458 dyn_node.domain(),
459 dyn_node.digest(),
460 );
461
462 if dyn_node.is_dyncall() {
463 let (stack_depth_after_drop, overflow_addr) =
478 if processor.stack().depth() > MIN_STACK_DEPTH as u32 {
479 (
480 processor.stack().depth() - 1,
481 self.overflow_table.clk_after_pop_in_current_ctx(),
482 )
483 } else {
484 (processor.stack().depth(), ZERO)
485 };
486 Some(ExecutionContextInfo::new(
487 processor.system().ctx(),
488 processor.system().caller_hash(),
489 stack_depth_after_drop,
490 overflow_addr,
491 ))
492 } else {
493 None
494 }
495 },
496 MastNode::Block(_) => panic!(
497 "`ExecutionTracer::record_basic_block_start()` must be called instead for basic blocks"
498 ),
499 MastNode::External(_) => panic!(
500 "External nodes are guaranteed to be resolved before record_control_node_start is called"
501 ),
502 };
503
504 let block_addr = self.hasher_chiplet_shim.record_hash_control_block();
505 let parent_addr = self.block_stack.push(block_addr, ctx_info);
506 self.block_stack_replay.record_node_start_parent_addr(parent_addr);
507 }
508
509 #[inline(always)]
511 fn record_node_end(&mut self, block_info: &BlockInfo) {
512 let (prev_addr, prev_parent_addr) = if self.block_stack.is_empty() {
513 (ZERO, ZERO)
514 } else {
515 let prev_block = self.block_stack.peek();
516 (prev_block.addr, prev_block.parent_addr)
517 };
518 self.block_stack_replay
519 .record_node_end(block_info.addr, prev_addr, prev_parent_addr);
520 }
521
522 #[inline(always)]
524 fn record_execution_context(&mut self, ctx_info: ExecutionContextSystemInfo) {
525 self.execution_context_replay.record_execution_context(ctx_info);
526 }
527
528 #[inline(always)]
537 fn finish_current_fragment_context(&mut self) {
538 if let Some(snapshot) = self.state_snapshot.take() {
539 let (hasher_replay, block_addr_replay) = self.hasher_chiplet_shim.extract_replay();
541 let memory_reads_replay = core::mem::take(&mut self.memory_reads);
542 let advice_replay = core::mem::take(&mut self.advice);
543 let external_replay = core::mem::take(&mut self.external);
544 let stack_overflow_replay = core::mem::take(&mut self.overflow_replay);
545 let block_stack_replay = core::mem::take(&mut self.block_stack_replay);
546 let execution_context_replay = core::mem::take(&mut self.execution_context_replay);
547
548 let trace_state = CoreTraceFragmentContext {
549 state: snapshot.state,
550 replay: ExecutionReplay {
551 hasher: hasher_replay,
552 block_address: block_addr_replay,
553 memory_reads: memory_reads_replay,
554 advice: advice_replay,
555 mast_forest_resolution: external_replay,
556 stack_overflow: stack_overflow_replay,
557 block_stack: block_stack_replay,
558 execution_context: execution_context_replay,
559 },
560 continuation: snapshot.continuation_stack,
561 initial_mast_forest_id: snapshot.initial_mast_forest_id,
562 };
563
564 self.fragment_contexts.push(trace_state);
565 }
566 }
567
568 #[inline(always)]
572 fn increment_stack_size(&mut self, processor: &FastProcessor) {
573 let new_overflow_value = processor.stack_get(15);
574 self.overflow_table.push(new_overflow_value, processor.system().clock());
575 }
576
577 #[inline(always)]
579 fn decrement_stack_size(&mut self) {
580 if let Some(popped_value) = self.overflow_table.pop() {
581 let new_overflow_addr = self.overflow_table.last_update_clk_in_current_ctx();
582 self.overflow_replay.record_pop_overflow(popped_value, new_overflow_addr);
583 }
584 }
585}
586
587impl Tracer for ExecutionTracer {
588 type Processor = FastProcessor;
589 type Forest = Arc<MastForest>;
590
591 #[inline(always)]
594 fn start_clock_cycle(
595 &mut self,
596 processor: &FastProcessor,
597 continuation: Continuation<Arc<MastForest>>,
598 continuation_stack: &ContinuationStack<Arc<MastForest>>,
599 current_forest: &Arc<MastForest>,
600 ) {
601 if processor.system().clock().as_usize().is_multiple_of(self.fragment_size) {
603 self.start_new_fragment_context(
604 SystemState::from_processor(processor),
605 processor
606 .stack_top()
607 .try_into()
608 .expect("stack_top expected to be MIN_STACK_DEPTH elements"),
609 continuation_stack.clone(),
610 continuation.clone(),
611 current_forest.clone(),
612 );
613 }
614
615 if let Some(visited_node_id) = node_id_for_visit(&continuation) {
620 self.record_visit(current_forest, visited_node_id);
621 }
622
623 match continuation {
624 Continuation::ResumeBasicBlock { node_id, batch_index, op_idx_in_batch } => {
625 let basic_block = current_forest[node_id].unwrap_basic_block();
628 let op = &basic_block.op_batches()[batch_index].ops()[op_idx_in_batch];
629
630 if op.increments_stack_size() {
631 self.increment_stack_size(processor);
632 } else if op.decrements_stack_size() {
633 self.decrement_stack_size();
634 }
635
636 if matches!(op, Operation::EvalCircuit) {
637 self.is_eval_circuit_op = true;
638 }
639 },
640 Continuation::StartNode(mast_node_id) => match ¤t_forest[mast_node_id] {
641 MastNode::Join(_) | MastNode::Loop(_) => {
642 self.record_control_node_start(
643 ¤t_forest[mast_node_id],
644 processor,
645 current_forest,
646 );
647 },
648 MastNode::Split(_) => {
649 self.record_control_node_start(
650 ¤t_forest[mast_node_id],
651 processor,
652 current_forest,
653 );
654 self.decrement_stack_size();
655 },
656 MastNode::Call(_) => {
657 self.record_control_node_start(
658 ¤t_forest[mast_node_id],
659 processor,
660 current_forest,
661 );
662 self.overflow_table.start_context();
663 },
664 MastNode::Dyn(dyn_node) => {
665 self.record_control_node_start(
666 ¤t_forest[mast_node_id],
667 processor,
668 current_forest,
669 );
670 self.decrement_stack_size();
672
673 if dyn_node.is_dyncall() {
674 self.overflow_table.start_context();
678 }
679 },
680 MastNode::Block(basic_block_node) => {
681 let forest_id = self.forest_id(current_forest);
682 self.hasher_for_chiplet.record_hash_basic_block(
683 forest_id,
684 mast_node_id,
685 basic_block_node,
686 );
687 let block_addr =
688 self.hasher_chiplet_shim.record_hash_basic_block(basic_block_node);
689 let parent_addr = self.block_stack.push(block_addr, None);
690 self.block_stack_replay.record_node_start_parent_addr(parent_addr);
691 },
692 MastNode::External(_) => unreachable!(
693 "start_clock_cycle is guaranteed not to be called on external nodes"
694 ),
695 },
696 Continuation::Respan { node_id: _, batch_index: _ } => {
697 self.block_stack.peek_mut().addr += CONTROLLER_ROWS_PER_PERM_FELT;
698 },
699 Continuation::FinishLoop(_) if processor.stack_get(0) == ONE => {
700 self.decrement_stack_size();
702 },
703 Continuation::FinishJoin(_)
704 | Continuation::FinishSplit(_)
705 | Continuation::FinishCall(_)
706 | Continuation::FinishDyn(_)
707 | Continuation::FinishLoop(_) | Continuation::FinishBasicBlock(_) => {
709 if matches!(
711 &continuation,
712 Continuation::FinishLoop(_)
713 ) {
714 self.decrement_stack_size();
715 }
716
717 let block_info = self.block_stack.pop();
719 self.record_node_end(&block_info);
720
721 if let Some(ctx_info) = block_info.ctx_info {
722 self.record_execution_context(ExecutionContextSystemInfo {
723 parent_ctx: ctx_info.parent_ctx,
724 parent_fn_hash: ctx_info.parent_fn_hash,
725 });
726
727 self.pending_restore_context = true;
728 }
729 },
730 Continuation::EnterForest { .. } => {
731 panic!("EnterForest continuations are guaranteed not to be passed here")
732 },
733 }
734 }
735
736 #[inline(always)]
737 fn record_mast_forest_resolution(&mut self, node_id: MastNodeId, forest: &Arc<MastForest>) {
738 let forest_id = self.forest_id(forest);
739 self.external.record_resolution(node_id, forest_id);
740 }
741
742 #[inline(always)]
743 fn record_external_node_entered(
744 &mut self,
745 external_node_id: MastNodeId,
746 forest: &Arc<MastForest>,
747 ) {
748 self.record_visit(forest, external_node_id);
753 }
754
755 #[inline(always)]
756 fn record_hasher_permute(
757 &mut self,
758 input_state: [Felt; STATE_WIDTH],
759 output_state: [Felt; STATE_WIDTH],
760 ) {
761 self.hasher_for_chiplet.record_permute_input(input_state);
762 self.hasher_chiplet_shim.record_permute_output(output_state);
763 }
764
765 #[inline(always)]
766 fn record_hasher_build_merkle_root(
767 &mut self,
768 node: Word,
769 path: Option<&MerklePath>,
770 index: Felt,
771 output_root: Word,
772 ) {
773 let path = path.expect("execution tracer expects a valid Merkle path");
774 self.hasher_chiplet_shim.record_build_merkle_root(path, output_root);
775 self.hasher_for_chiplet.record_build_merkle_root(node, path.clone(), index);
776 }
777
778 #[inline(always)]
779 fn record_hasher_update_merkle_root(
780 &mut self,
781 old_value: Word,
782 new_value: Word,
783 path: Option<&MerklePath>,
784 index: Felt,
785 old_root: Word,
786 new_root: Word,
787 ) {
788 let path = path.expect("execution tracer expects a valid Merkle path");
789 self.hasher_chiplet_shim.record_update_merkle_root(path, old_root, new_root);
790 self.hasher_for_chiplet.record_update_merkle_root(
791 old_value,
792 new_value,
793 path.clone(),
794 index,
795 );
796 }
797
798 #[inline(always)]
799 fn record_memory_read_element(
800 &mut self,
801 element: Felt,
802 addr: Felt,
803 ctx: ContextId,
804 clk: RowIndex,
805 ) {
806 self.memory_reads.record_read_element(element, addr, ctx, clk);
807 }
808
809 #[inline(always)]
810 fn record_memory_read_word(&mut self, word: Word, addr: Felt, ctx: ContextId, clk: RowIndex) {
811 self.memory_reads.record_read_word(word, addr, ctx, clk);
812 }
813
814 #[inline(always)]
815 fn record_memory_write_element(
816 &mut self,
817 element: Felt,
818 addr: Felt,
819 ctx: ContextId,
820 clk: RowIndex,
821 ) {
822 self.memory_writes.record_write_element(element, addr, ctx, clk);
823 }
824
825 #[inline(always)]
826 fn record_memory_write_word(&mut self, word: Word, addr: Felt, ctx: ContextId, clk: RowIndex) {
827 self.memory_writes.record_write_word(word, addr, ctx, clk);
828 }
829
830 #[inline(always)]
831 fn record_memory_read_element_pair(
832 &mut self,
833 element_0: Felt,
834 addr_0: Felt,
835 element_1: Felt,
836 addr_1: Felt,
837 ctx: ContextId,
838 clk: RowIndex,
839 ) {
840 self.memory_reads.record_read_element(element_0, addr_0, ctx, clk);
841 self.memory_reads.record_read_element(element_1, addr_1, ctx, clk);
842 }
843
844 #[inline(always)]
845 fn record_memory_read_dword(
846 &mut self,
847 words: [Word; 2],
848 addr: Felt,
849 ctx: ContextId,
850 clk: RowIndex,
851 ) {
852 self.memory_reads.record_read_word(words[0], addr, ctx, clk);
853 self.memory_reads.record_read_word(words[1], addr + PTR_OFFSET_WORD, ctx, clk);
854 }
855
856 #[inline(always)]
857 fn record_dyncall_memory(
858 &mut self,
859 callee_hash: Word,
860 read_addr: Felt,
861 read_ctx: ContextId,
862 fmp_ctx: ContextId,
863 clk: RowIndex,
864 ) {
865 self.memory_reads.record_read_word(callee_hash, read_addr, read_ctx, clk);
866 self.memory_writes.record_write_element(FMP_INIT_VALUE, FMP_ADDR, fmp_ctx, clk);
867 }
868
869 #[inline(always)]
870 fn record_crypto_stream(
871 &mut self,
872 plaintext: [Word; 2],
873 src_addr: Felt,
874 ciphertext: [Word; 2],
875 dst_addr: Felt,
876 ctx: ContextId,
877 clk: RowIndex,
878 ) {
879 self.memory_reads.record_read_word(plaintext[0], src_addr, ctx, clk);
880 self.memory_reads
881 .record_read_word(plaintext[1], src_addr + PTR_OFFSET_WORD, ctx, clk);
882 self.memory_writes.record_write_word(ciphertext[0], dst_addr, ctx, clk);
883 self.memory_writes
884 .record_write_word(ciphertext[1], dst_addr + PTR_OFFSET_WORD, ctx, clk);
885 }
886
887 #[inline(always)]
888 fn record_pipe(&mut self, words: [Word; 2], addr: Felt, ctx: ContextId, clk: RowIndex) {
889 self.advice.record_pop_stack_dword(words);
890 self.memory_writes.record_write_word(words[0], addr, ctx, clk);
891 self.memory_writes.record_write_word(words[1], addr + PTR_OFFSET_WORD, ctx, clk);
892 }
893
894 #[inline(always)]
895 fn record_advice_pop_stack(&mut self, value: Felt) {
896 self.advice.record_pop_stack(value);
897 }
898
899 #[inline(always)]
900 fn record_advice_pop_stack_word(&mut self, word: Word) {
901 self.advice.record_pop_stack_word(word);
902 }
903
904 #[inline(always)]
905 fn record_u32and(&mut self, a: Felt, b: Felt) {
906 self.bitwise.record_u32and(a, b);
907 }
908
909 #[inline(always)]
910 fn record_u32xor(&mut self, a: Felt, b: Felt) {
911 self.bitwise.record_u32xor(a, b);
912 }
913
914 #[inline(always)]
915 fn record_u32_range_checks(&mut self, u32_lo: Felt, u32_hi: Felt) {
916 let (t1, t0) = split_u32_into_u16(u32_lo.as_canonical_u64());
917 let (t3, t2) = split_u32_into_u16(u32_hi.as_canonical_u64());
918
919 self.range_checker.record_range_check_u32([t0, t1, t2, t3]);
920 }
921
922 #[inline(always)]
923 fn record_kernel_proc_access(&mut self, proc_hash: Word) {
924 self.kernel.record_kernel_proc_access(proc_hash);
925 }
926
927 #[inline(always)]
928 fn record_circuit_evaluation(&mut self, circuit_evaluation: CircuitEvaluation) {
929 self.ace.record_circuit_evaluation(circuit_evaluation);
930 }
931
932 #[inline(always)]
933 fn finalize_clock_cycle(
934 &mut self,
935 processor: &FastProcessor,
936 _op_helper_registers: OperationHelperRegisters,
937 _current_forest: &Arc<MastForest>,
938 ) -> Result<(), ExecutionError> {
939 if self.pending_restore_context {
943 self.overflow_table.restore_context().map_err(|_| {
946 ExecutionError::Internal(
947 "overflow table restore_context failed during trace finalization",
948 )
949 })?;
950 self.overflow_replay.record_restore_context_overflow_addr(
951 MIN_STACK_DEPTH + self.overflow_table.num_elements_in_current_ctx(),
952 self.overflow_table.last_update_clk_in_current_ctx(),
953 );
954
955 self.pending_restore_context = false;
956 }
957
958 if self.is_eval_circuit_op {
962 let ptr = processor.stack_get(0);
963 let num_read = processor.stack_get(1).as_canonical_u64();
964 let num_eval = processor.stack_get(2).as_canonical_u64();
965 let ctx = processor.ctx();
966 let clk = processor.clock();
967
968 let num_read_rows = num_read / 2;
969
970 let mut addr = ptr;
971 for _ in 0..num_read_rows {
972 let word = processor
973 .memory()
974 .read_word(ctx, addr, clk)
975 .expect("EvalCircuit memory read should not fail after successful execution");
976 self.memory_reads.record_read_word(word, addr, ctx, clk);
977 addr += PTR_OFFSET_WORD;
978 }
979 for _ in 0..num_eval {
980 let element = processor
981 .memory()
982 .read_element(ctx, addr)
983 .expect("EvalCircuit memory read should not fail after successful execution");
984 self.memory_reads.record_read_element(element, addr, ctx, clk);
985 addr += PTR_OFFSET_ELEM;
986 }
987
988 self.is_eval_circuit_op = false;
989 }
990
991 Ok(())
992 }
993}
994
995#[inline]
1001fn node_id_for_visit<F>(continuation: &Continuation<F>) -> Option<MastNodeId> {
1002 match *continuation {
1003 Continuation::StartNode(id)
1004 | Continuation::FinishJoin(id)
1005 | Continuation::FinishSplit(id)
1006 | Continuation::FinishCall(id)
1007 | Continuation::FinishDyn(id)
1008 | Continuation::FinishBasicBlock(id) => Some(id),
1009 Continuation::FinishLoop(id) => Some(id),
1010 Continuation::ResumeBasicBlock { node_id, .. } | Continuation::Respan { node_id, .. } => {
1011 Some(node_id)
1012 },
1013 Continuation::EnterForest { .. } => None,
1014 }
1015}
1016
1017const NUM_HASHER_ROWS_PER_PERMUTATION: u32 = CONTROLLER_ROWS_PER_PERMUTATION as u32;
1022
1023#[derive(Debug)]
1030pub struct HasherChipletShim {
1031 addr: u32,
1035 hasher_replay: HasherResponseReplay,
1037 block_addr_replay: BlockAddressReplay,
1038}
1039
1040impl HasherChipletShim {
1041 pub fn new() -> Self {
1043 Self {
1044 addr: 1,
1045 hasher_replay: HasherResponseReplay::default(),
1046 block_addr_replay: BlockAddressReplay::default(),
1047 }
1048 }
1049
1050 pub fn record_hash_control_block(&mut self) -> Felt {
1052 let block_addr = Felt::from_u32(self.addr);
1053
1054 self.block_addr_replay.record_block_address(block_addr);
1055 self.addr += NUM_HASHER_ROWS_PER_PERMUTATION;
1056
1057 block_addr
1058 }
1059
1060 pub fn record_hash_basic_block(&mut self, basic_block_node: &BasicBlockNode) -> Felt {
1062 let block_addr = Felt::from_u32(self.addr);
1063
1064 self.block_addr_replay.record_block_address(block_addr);
1065 self.addr += NUM_HASHER_ROWS_PER_PERMUTATION * basic_block_node.num_op_batches() as u32;
1066
1067 block_addr
1068 }
1069 pub fn record_permute_output(&mut self, hashed_state: [Felt; 12]) {
1071 self.hasher_replay.record_permute(Felt::from_u32(self.addr), hashed_state);
1072 self.addr += NUM_HASHER_ROWS_PER_PERMUTATION;
1073 }
1074
1075 pub fn record_build_merkle_root(&mut self, path: &MerklePath, computed_root: Word) {
1077 self.hasher_replay
1078 .record_build_merkle_root(Felt::from_u32(self.addr), computed_root);
1079 self.addr += NUM_HASHER_ROWS_PER_PERMUTATION * path.depth() as u32;
1080 }
1081
1082 pub fn record_update_merkle_root(&mut self, path: &MerklePath, old_root: Word, new_root: Word) {
1084 self.hasher_replay
1085 .record_update_merkle_root(Felt::from_u32(self.addr), old_root, new_root);
1086
1087 self.addr += 2 * NUM_HASHER_ROWS_PER_PERMUTATION * path.depth() as u32;
1089 }
1090
1091 pub fn extract_replay(&mut self) -> (HasherResponseReplay, BlockAddressReplay) {
1092 (
1093 core::mem::take(&mut self.hasher_replay),
1094 core::mem::take(&mut self.block_addr_replay),
1095 )
1096 }
1097}
1098
1099impl Default for HasherChipletShim {
1100 fn default() -> Self {
1101 Self::new()
1102 }
1103}