1use alloc::{sync::Arc, vec::Vec};
2
3use miden_air::trace::chiplets::hasher::{HASH_CYCLE_LEN, HASH_CYCLE_LEN_FELT, STATE_WIDTH};
4use miden_core::{FMP_ADDR, FMP_INIT_VALUE, operations::Operation};
5
6use super::{
7 decoder::block_stack::{BlockInfo, BlockStack, BlockType, ExecutionContextInfo},
8 stack::OverflowTable,
9 trace_state::{
10 AceReplay, AdviceReplay, BitwiseReplay, BlockAddressReplay, BlockStackReplay,
11 CoreTraceFragmentContext, CoreTraceState, DecoderState, ExecutionContextReplay,
12 ExecutionContextSystemInfo, ExecutionReplay, HasherRequestReplay, HasherResponseReplay,
13 KernelReplay, MastForestResolutionReplay, MemoryReadsReplay, MemoryWritesReplay, NodeFlags,
14 RangeCheckerReplay, StackOverflowReplay, StackState, SystemState,
15 },
16 utils::split_u32_into_u16,
17};
18use crate::{
19 ContextId, EMPTY_WORD, FastProcessor, Felt, MIN_STACK_DEPTH, ONE, RowIndex, Word, ZERO,
20 continuation_stack::{Continuation, ContinuationStack},
21 crypto::merkle::MerklePath,
22 mast::{
23 BasicBlockNode, JoinNode, LoopNode, MastForest, MastNode, MastNodeExt, MastNodeId,
24 SplitNode,
25 },
26 processor::{Processor, StackInterface, SystemInterface},
27 trace::chiplets::{CircuitEvaluation, PTR_OFFSET_ELEM, PTR_OFFSET_WORD},
28 tracer::{OperationHelperRegisters, Tracer},
29};
30
31#[derive(Debug)]
36struct StateSnapshot {
37 state: CoreTraceState,
38 continuation_stack: ContinuationStack,
39 initial_mast_forest: Arc<MastForest>,
40}
41
42pub struct TraceGenerationContext {
46 pub core_trace_contexts: Vec<CoreTraceFragmentContext>,
48
49 pub range_checker_replay: RangeCheckerReplay,
52 pub memory_writes: MemoryWritesReplay,
53 pub bitwise_replay: BitwiseReplay,
54 pub hasher_for_chiplet: HasherRequestReplay,
55 pub kernel_replay: KernelReplay,
56 pub ace_replay: AceReplay,
57
58 pub fragment_size: usize,
61}
62
63#[derive(Debug)]
76pub struct ExecutionTracer {
77 state_snapshot: Option<StateSnapshot>,
84
85 overflow_table: OverflowTable,
87 overflow_replay: StackOverflowReplay,
88
89 block_stack: BlockStack,
90 block_stack_replay: BlockStackReplay,
91 execution_context_replay: ExecutionContextReplay,
92
93 hasher_chiplet_shim: HasherChipletShim,
94 memory_reads: MemoryReadsReplay,
95 advice: AdviceReplay,
96 external: MastForestResolutionReplay,
97
98 range_checker: RangeCheckerReplay,
101 memory_writes: MemoryWritesReplay,
102 bitwise: BitwiseReplay,
103 kernel: KernelReplay,
104 hasher_for_chiplet: HasherRequestReplay,
105 ace: AceReplay,
106
107 fragment_contexts: Vec<CoreTraceFragmentContext>,
109
110 fragment_size: usize,
112
113 pending_restore_context: bool,
118
119 is_eval_circuit_op: bool,
122}
123
124impl ExecutionTracer {
125 #[inline(always)]
127 pub fn new(fragment_size: usize) -> Self {
128 Self {
129 state_snapshot: None,
130 overflow_table: OverflowTable::default(),
131 overflow_replay: StackOverflowReplay::default(),
132 block_stack: BlockStack::default(),
133 block_stack_replay: BlockStackReplay::default(),
134 execution_context_replay: ExecutionContextReplay::default(),
135 hasher_chiplet_shim: HasherChipletShim::default(),
136 memory_reads: MemoryReadsReplay::default(),
137 range_checker: RangeCheckerReplay::default(),
138 memory_writes: MemoryWritesReplay::default(),
139 advice: AdviceReplay::default(),
140 bitwise: BitwiseReplay::default(),
141 kernel: KernelReplay::default(),
142 hasher_for_chiplet: HasherRequestReplay::default(),
143 ace: AceReplay::default(),
144 external: MastForestResolutionReplay::default(),
145 fragment_contexts: Vec::new(),
146 fragment_size,
147 pending_restore_context: false,
148 is_eval_circuit_op: false,
149 }
150 }
151
152 #[inline(always)]
155 pub fn into_trace_generation_context(mut self) -> TraceGenerationContext {
156 self.finish_current_fragment_context();
158
159 TraceGenerationContext {
160 core_trace_contexts: self.fragment_contexts,
161 range_checker_replay: self.range_checker,
162 memory_writes: self.memory_writes,
163 bitwise_replay: self.bitwise,
164 kernel_replay: self.kernel,
165 hasher_for_chiplet: self.hasher_for_chiplet,
166 ace_replay: self.ace,
167 fragment_size: self.fragment_size,
168 }
169 }
170
171 #[inline(always)]
182 fn start_new_fragment_context(
183 &mut self,
184 system_state: SystemState,
185 stack_top: [Felt; MIN_STACK_DEPTH],
186 mut continuation_stack: ContinuationStack,
187 continuation: Continuation,
188 current_forest: Arc<MastForest>,
189 ) {
190 self.finish_current_fragment_context();
192
193 self.state_snapshot = {
195 let decoder_state = {
196 if self.block_stack.is_empty() {
197 DecoderState { current_addr: ZERO, parent_addr: ZERO }
198 } else {
199 let block_info = self.block_stack.peek();
200
201 DecoderState {
202 current_addr: block_info.addr,
203 parent_addr: block_info.parent_addr,
204 }
205 }
206 };
207 let stack = {
208 let stack_depth =
209 MIN_STACK_DEPTH + self.overflow_table.num_elements_in_current_ctx();
210 let last_overflow_addr = self.overflow_table.last_update_clk_in_current_ctx();
211 StackState::new(stack_top, stack_depth, last_overflow_addr)
212 };
213
214 continuation_stack.push_continuation(continuation);
216
217 Some(StateSnapshot {
218 state: CoreTraceState {
219 system: system_state,
220 decoder: decoder_state,
221 stack,
222 },
223 continuation_stack,
224 initial_mast_forest: current_forest,
225 })
226 };
227 }
228
229 #[inline(always)]
230 fn record_control_node_start<P: Processor>(
231 &mut self,
232 node: &MastNode,
233 processor: &P,
234 current_forest: &MastForest,
235 ) {
236 let (ctx_info, block_type) = match node {
237 MastNode::Join(node) => {
238 let child1_hash = current_forest
239 .get_node_by_id(node.first())
240 .expect("join node's first child expected to be in the forest")
241 .digest();
242 let child2_hash = current_forest
243 .get_node_by_id(node.second())
244 .expect("join node's second child expected to be in the forest")
245 .digest();
246 self.hasher_for_chiplet.record_hash_control_block(
247 child1_hash,
248 child2_hash,
249 JoinNode::DOMAIN,
250 node.digest(),
251 );
252
253 (None, BlockType::Join(false))
254 },
255 MastNode::Split(node) => {
256 let child1_hash = current_forest
257 .get_node_by_id(node.on_true())
258 .expect("split node's true child expected to be in the forest")
259 .digest();
260 let child2_hash = current_forest
261 .get_node_by_id(node.on_false())
262 .expect("split node's false child expected to be in the forest")
263 .digest();
264 self.hasher_for_chiplet.record_hash_control_block(
265 child1_hash,
266 child2_hash,
267 SplitNode::DOMAIN,
268 node.digest(),
269 );
270
271 (None, BlockType::Split)
272 },
273 MastNode::Loop(node) => {
274 let body_hash = current_forest
275 .get_node_by_id(node.body())
276 .expect("loop node's body expected to be in the forest")
277 .digest();
278
279 self.hasher_for_chiplet.record_hash_control_block(
280 body_hash,
281 EMPTY_WORD,
282 LoopNode::DOMAIN,
283 node.digest(),
284 );
285
286 let loop_entered = {
287 let condition = processor.stack().get(0);
288 condition == ONE
289 };
290
291 (None, BlockType::Loop(loop_entered))
292 },
293 MastNode::Call(node) => {
294 let callee_hash = current_forest
295 .get_node_by_id(node.callee())
296 .expect("call node's callee expected to be in the forest")
297 .digest();
298
299 self.hasher_for_chiplet.record_hash_control_block(
300 callee_hash,
301 EMPTY_WORD,
302 node.domain(),
303 node.digest(),
304 );
305
306 let exec_ctx = {
307 let overflow_addr = self.overflow_table.last_update_clk_in_current_ctx();
308 ExecutionContextInfo::new(
309 processor.system().ctx(),
310 processor.system().caller_hash(),
311 processor.stack().depth(),
312 overflow_addr,
313 )
314 };
315 let block_type = if node.is_syscall() {
316 BlockType::SysCall
317 } else {
318 BlockType::Call
319 };
320
321 (Some(exec_ctx), block_type)
322 },
323 MastNode::Dyn(dyn_node) => {
324 self.hasher_for_chiplet.record_hash_control_block(
325 EMPTY_WORD,
326 EMPTY_WORD,
327 dyn_node.domain(),
328 dyn_node.digest(),
329 );
330
331 if dyn_node.is_dyncall() {
332 let exec_ctx = {
333 let overflow_addr = self.overflow_table.last_update_clk_in_current_ctx();
334 let stack_depth_after_drop = processor.stack().depth() - 1;
343 ExecutionContextInfo::new(
344 processor.system().ctx(),
345 processor.system().caller_hash(),
346 stack_depth_after_drop,
347 overflow_addr,
348 )
349 };
350 (Some(exec_ctx), BlockType::Dyncall)
351 } else {
352 (None, BlockType::Dyn)
353 }
354 },
355 MastNode::Block(_) => panic!(
356 "`ExecutionTracer::record_basic_block_start()` must be called instead for basic blocks"
357 ),
358 MastNode::External(_) => panic!(
359 "External nodes are guaranteed to be resolved before record_control_node_start is called"
360 ),
361 };
362
363 let block_addr = self.hasher_chiplet_shim.record_hash_control_block();
364 let parent_addr = self.block_stack.push(block_addr, block_type, ctx_info);
365 self.block_stack_replay.record_node_start_parent_addr(parent_addr);
366 }
367
368 #[inline(always)]
370 fn record_node_end(&mut self, block_info: &BlockInfo) {
371 let flags = NodeFlags::new(
372 block_info.is_loop_body() == ONE,
373 block_info.is_entered_loop() == ONE,
374 block_info.is_call() == ONE,
375 block_info.is_syscall() == ONE,
376 );
377 let (prev_addr, prev_parent_addr) = if self.block_stack.is_empty() {
378 (ZERO, ZERO)
379 } else {
380 let prev_block = self.block_stack.peek();
381 (prev_block.addr, prev_block.parent_addr)
382 };
383 self.block_stack_replay.record_node_end(
384 block_info.addr,
385 flags,
386 prev_addr,
387 prev_parent_addr,
388 );
389 }
390
391 #[inline(always)]
393 fn record_execution_context(&mut self, ctx_info: ExecutionContextSystemInfo) {
394 self.execution_context_replay.record_execution_context(ctx_info);
395 }
396
397 #[inline(always)]
406 fn finish_current_fragment_context(&mut self) {
407 if let Some(snapshot) = self.state_snapshot.take() {
408 let (hasher_replay, block_addr_replay) = self.hasher_chiplet_shim.extract_replay();
410 let memory_reads_replay = core::mem::take(&mut self.memory_reads);
411 let advice_replay = core::mem::take(&mut self.advice);
412 let external_replay = core::mem::take(&mut self.external);
413 let stack_overflow_replay = core::mem::take(&mut self.overflow_replay);
414 let block_stack_replay = core::mem::take(&mut self.block_stack_replay);
415 let execution_context_replay = core::mem::take(&mut self.execution_context_replay);
416
417 let trace_state = CoreTraceFragmentContext {
418 state: snapshot.state,
419 replay: ExecutionReplay {
420 hasher: hasher_replay,
421 block_address: block_addr_replay,
422 memory_reads: memory_reads_replay,
423 advice: advice_replay,
424 mast_forest_resolution: external_replay,
425 stack_overflow: stack_overflow_replay,
426 block_stack: block_stack_replay,
427 execution_context: execution_context_replay,
428 },
429 continuation: snapshot.continuation_stack,
430 initial_mast_forest: snapshot.initial_mast_forest,
431 };
432
433 self.fragment_contexts.push(trace_state);
434 }
435 }
436
437 #[inline(always)]
441 fn increment_stack_size(&mut self, processor: &FastProcessor) {
442 let new_overflow_value = processor.stack_get(15);
443 self.overflow_table.push(new_overflow_value, processor.system().clock());
444 }
445
446 #[inline(always)]
448 fn decrement_stack_size(&mut self) {
449 if let Some(popped_value) = self.overflow_table.pop() {
450 let new_overflow_addr = self.overflow_table.last_update_clk_in_current_ctx();
451 self.overflow_replay.record_pop_overflow(popped_value, new_overflow_addr);
452 }
453 }
454}
455
456impl Tracer for ExecutionTracer {
457 type Processor = FastProcessor;
458
459 #[inline(always)]
462 fn start_clock_cycle(
463 &mut self,
464 processor: &FastProcessor,
465 continuation: Continuation,
466 continuation_stack: &ContinuationStack,
467 current_forest: &Arc<MastForest>,
468 ) {
469 if processor.system().clock().as_usize().is_multiple_of(self.fragment_size) {
471 self.start_new_fragment_context(
472 SystemState::from_processor(processor),
473 processor
474 .stack_top()
475 .try_into()
476 .expect("stack_top expected to be MIN_STACK_DEPTH elements"),
477 continuation_stack.clone(),
478 continuation.clone(),
479 current_forest.clone(),
480 );
481 }
482
483 match continuation {
484 Continuation::ResumeBasicBlock { node_id, batch_index, op_idx_in_batch } => {
485 let basic_block = current_forest[node_id].unwrap_basic_block();
488 let op = &basic_block.op_batches()[batch_index].ops()[op_idx_in_batch];
489
490 if op.increments_stack_size() {
491 self.increment_stack_size(processor);
492 } else if op.decrements_stack_size() {
493 self.decrement_stack_size();
494 }
495
496 if matches!(op, Operation::EvalCircuit) {
497 self.is_eval_circuit_op = true;
498 }
499 },
500 Continuation::StartNode(mast_node_id) => match ¤t_forest[mast_node_id] {
501 MastNode::Join(_) => {
502 self.record_control_node_start(
503 ¤t_forest[mast_node_id],
504 processor,
505 current_forest,
506 );
507 },
508 MastNode::Split(_) | MastNode::Loop(_) => {
509 self.record_control_node_start(
510 ¤t_forest[mast_node_id],
511 processor,
512 current_forest,
513 );
514 self.decrement_stack_size();
515 },
516 MastNode::Call(_) => {
517 self.record_control_node_start(
518 ¤t_forest[mast_node_id],
519 processor,
520 current_forest,
521 );
522 self.overflow_table.start_context();
523 },
524 MastNode::Dyn(dyn_node) => {
525 self.record_control_node_start(
526 ¤t_forest[mast_node_id],
527 processor,
528 current_forest,
529 );
530 self.decrement_stack_size();
532
533 if dyn_node.is_dyncall() {
534 self.overflow_table.start_context();
538 }
539 },
540 MastNode::Block(basic_block_node) => {
541 self.hasher_for_chiplet.record_hash_basic_block(
542 current_forest.clone(),
543 mast_node_id,
544 basic_block_node.digest(),
545 );
546 let block_addr =
547 self.hasher_chiplet_shim.record_hash_basic_block(basic_block_node);
548 let parent_addr =
549 self.block_stack.push(block_addr, BlockType::BasicBlock, None);
550 self.block_stack_replay.record_node_start_parent_addr(parent_addr);
551 },
552 MastNode::External(_) => unreachable!(
553 "start_clock_cycle is guaranteed not to be called on external nodes"
554 ),
555 },
556 Continuation::Respan { node_id: _, batch_index: _ } => {
557 self.block_stack.peek_mut().addr += HASH_CYCLE_LEN_FELT;
558 },
559 Continuation::FinishLoop { node_id: _, was_entered }
560 if was_entered && processor.stack_get(0) == ONE =>
561 {
562 self.decrement_stack_size();
564 },
565 Continuation::FinishJoin(_)
566 | Continuation::FinishSplit(_)
567 | Continuation::FinishCall(_)
568 | Continuation::FinishDyn(_)
569 | Continuation::FinishLoop { .. } | Continuation::FinishBasicBlock(_) => {
571 if matches!(
573 &continuation,
574 Continuation::FinishLoop { was_entered, .. } if *was_entered
575 ) {
576 self.decrement_stack_size();
577 }
578
579 let block_info = self.block_stack.pop();
581 self.record_node_end(&block_info);
582
583 if let Some(ctx_info) = block_info.ctx_info {
584 self.record_execution_context(ExecutionContextSystemInfo {
585 parent_ctx: ctx_info.parent_ctx,
586 parent_fn_hash: ctx_info.parent_fn_hash,
587 });
588
589 self.pending_restore_context = true;
590 }
591 },
592 Continuation::FinishExternal(_)
593 | Continuation::EnterForest(_)
594 | Continuation::AfterExitDecorators(_)
595 | Continuation::AfterExitDecoratorsBasicBlock(_) => {
596 panic!(
597 "FinishExternal, EnterForest, AfterExitDecorators and AfterExitDecoratorsBasicBlock continuations are guaranteed not to be passed here"
598 )
599 },
600 }
601 }
602
603 #[inline(always)]
604 fn record_mast_forest_resolution(&mut self, node_id: MastNodeId, forest: &Arc<MastForest>) {
605 self.external.record_resolution(node_id, forest.clone());
606 }
607
608 #[inline(always)]
609 fn record_hasher_permute(
610 &mut self,
611 input_state: [Felt; STATE_WIDTH],
612 output_state: [Felt; STATE_WIDTH],
613 ) {
614 self.hasher_for_chiplet.record_permute_input(input_state);
615 self.hasher_chiplet_shim.record_permute_output(output_state);
616 }
617
618 #[inline(always)]
619 fn record_hasher_build_merkle_root(
620 &mut self,
621 node: Word,
622 path: Option<&MerklePath>,
623 index: Felt,
624 output_root: Word,
625 ) {
626 let path = path.expect("execution tracer expects a valid Merkle path");
627 self.hasher_chiplet_shim.record_build_merkle_root(path, output_root);
628 self.hasher_for_chiplet.record_build_merkle_root(node, path.clone(), index);
629 }
630
631 #[inline(always)]
632 fn record_hasher_update_merkle_root(
633 &mut self,
634 old_value: Word,
635 new_value: Word,
636 path: Option<&MerklePath>,
637 index: Felt,
638 old_root: Word,
639 new_root: Word,
640 ) {
641 let path = path.expect("execution tracer expects a valid Merkle path");
642 self.hasher_chiplet_shim.record_update_merkle_root(path, old_root, new_root);
643 self.hasher_for_chiplet.record_update_merkle_root(
644 old_value,
645 new_value,
646 path.clone(),
647 index,
648 );
649 }
650
651 #[inline(always)]
652 fn record_memory_read_element(
653 &mut self,
654 element: Felt,
655 addr: Felt,
656 ctx: ContextId,
657 clk: RowIndex,
658 ) {
659 self.memory_reads.record_read_element(element, addr, ctx, clk);
660 }
661
662 #[inline(always)]
663 fn record_memory_read_word(&mut self, word: Word, addr: Felt, ctx: ContextId, clk: RowIndex) {
664 self.memory_reads.record_read_word(word, addr, ctx, clk);
665 }
666
667 #[inline(always)]
668 fn record_memory_write_element(
669 &mut self,
670 element: Felt,
671 addr: Felt,
672 ctx: ContextId,
673 clk: RowIndex,
674 ) {
675 self.memory_writes.record_write_element(element, addr, ctx, clk);
676 }
677
678 #[inline(always)]
679 fn record_memory_write_word(&mut self, word: Word, addr: Felt, ctx: ContextId, clk: RowIndex) {
680 self.memory_writes.record_write_word(word, addr, ctx, clk);
681 }
682
683 #[inline(always)]
684 fn record_memory_read_element_pair(
685 &mut self,
686 element_0: Felt,
687 addr_0: Felt,
688 element_1: Felt,
689 addr_1: Felt,
690 ctx: ContextId,
691 clk: RowIndex,
692 ) {
693 self.memory_reads.record_read_element(element_0, addr_0, ctx, clk);
694 self.memory_reads.record_read_element(element_1, addr_1, ctx, clk);
695 }
696
697 #[inline(always)]
698 fn record_memory_read_dword(
699 &mut self,
700 words: [Word; 2],
701 addr: Felt,
702 ctx: ContextId,
703 clk: RowIndex,
704 ) {
705 self.memory_reads.record_read_word(words[0], addr, ctx, clk);
706 self.memory_reads.record_read_word(words[1], addr + Felt::new(4), ctx, clk);
707 }
708
709 #[inline(always)]
710 fn record_dyncall_memory(
711 &mut self,
712 callee_hash: Word,
713 read_addr: Felt,
714 read_ctx: ContextId,
715 fmp_ctx: ContextId,
716 clk: RowIndex,
717 ) {
718 self.memory_reads.record_read_word(callee_hash, read_addr, read_ctx, clk);
719 self.memory_writes.record_write_element(FMP_INIT_VALUE, FMP_ADDR, fmp_ctx, clk);
720 }
721
722 #[inline(always)]
723 fn record_crypto_stream(
724 &mut self,
725 plaintext: [Word; 2],
726 src_addr: Felt,
727 ciphertext: [Word; 2],
728 dst_addr: Felt,
729 ctx: ContextId,
730 clk: RowIndex,
731 ) {
732 self.memory_reads.record_read_word(plaintext[0], src_addr, ctx, clk);
733 self.memory_reads
734 .record_read_word(plaintext[1], src_addr + Felt::new(4), ctx, clk);
735 self.memory_writes.record_write_word(ciphertext[0], dst_addr, ctx, clk);
736 self.memory_writes
737 .record_write_word(ciphertext[1], dst_addr + Felt::new(4), ctx, clk);
738 }
739
740 #[inline(always)]
741 fn record_pipe(&mut self, words: [Word; 2], addr: Felt, ctx: ContextId, clk: RowIndex) {
742 self.advice.record_pop_stack_dword(words);
743 self.memory_writes.record_write_word(words[0], addr, ctx, clk);
744 self.memory_writes.record_write_word(words[1], addr + Felt::new(4), ctx, clk);
745 }
746
747 #[inline(always)]
748 fn record_advice_pop_stack(&mut self, value: Felt) {
749 self.advice.record_pop_stack(value);
750 }
751
752 #[inline(always)]
753 fn record_advice_pop_stack_word(&mut self, word: Word) {
754 self.advice.record_pop_stack_word(word);
755 }
756
757 #[inline(always)]
758 fn record_u32and(&mut self, a: Felt, b: Felt) {
759 self.bitwise.record_u32and(a, b);
760 }
761
762 #[inline(always)]
763 fn record_u32xor(&mut self, a: Felt, b: Felt) {
764 self.bitwise.record_u32xor(a, b);
765 }
766
767 #[inline(always)]
768 fn record_u32_range_checks(&mut self, clk: RowIndex, u32_lo: Felt, u32_hi: Felt) {
769 let (t1, t0) = split_u32_into_u16(u32_lo.as_canonical_u64());
770 let (t3, t2) = split_u32_into_u16(u32_hi.as_canonical_u64());
771
772 self.range_checker.record_range_check_u32(clk, [t0, t1, t2, t3]);
773 }
774
775 #[inline(always)]
776 fn record_kernel_proc_access(&mut self, proc_hash: Word) {
777 self.kernel.record_kernel_proc_access(proc_hash);
778 }
779
780 #[inline(always)]
781 fn record_circuit_evaluation(&mut self, circuit_evaluation: CircuitEvaluation) {
782 self.ace.record_circuit_evaluation(circuit_evaluation);
783 }
784
785 #[inline(always)]
786 fn finalize_clock_cycle(
787 &mut self,
788 processor: &FastProcessor,
789 _op_helper_registers: OperationHelperRegisters,
790 _current_forest: &Arc<MastForest>,
791 ) {
792 if self.pending_restore_context {
796 self.overflow_table.restore_context();
799 self.overflow_replay.record_restore_context_overflow_addr(
800 MIN_STACK_DEPTH + self.overflow_table.num_elements_in_current_ctx(),
801 self.overflow_table.last_update_clk_in_current_ctx(),
802 );
803
804 self.pending_restore_context = false;
805 }
806
807 if self.is_eval_circuit_op {
811 let ptr = processor.stack_get(0);
812 let num_read = processor.stack_get(1).as_canonical_u64();
813 let num_eval = processor.stack_get(2).as_canonical_u64();
814 let ctx = processor.ctx();
815 let clk = processor.clock();
816
817 let num_read_rows = num_read / 2;
818
819 let mut addr = ptr;
820 for _ in 0..num_read_rows {
821 let word = processor
822 .memory()
823 .read_word(ctx, addr, clk)
824 .expect("EvalCircuit memory read should not fail after successful execution");
825 self.memory_reads.record_read_word(word, addr, ctx, clk);
826 addr += PTR_OFFSET_WORD;
827 }
828 for _ in 0..num_eval {
829 let element = processor
830 .memory()
831 .read_element(ctx, addr)
832 .expect("EvalCircuit memory read should not fail after successful execution");
833 self.memory_reads.record_read_element(element, addr, ctx, clk);
834 addr += PTR_OFFSET_ELEM;
835 }
836
837 self.is_eval_circuit_op = false;
838 }
839 }
840}
841
842const NUM_HASHER_ROWS_PER_PERMUTATION: u32 = HASH_CYCLE_LEN as u32;
848
849#[derive(Debug)]
856pub struct HasherChipletShim {
857 addr: u32,
861 hasher_replay: HasherResponseReplay,
863 block_addr_replay: BlockAddressReplay,
864}
865
866impl HasherChipletShim {
867 pub fn new() -> Self {
869 Self {
870 addr: 1,
871 hasher_replay: HasherResponseReplay::default(),
872 block_addr_replay: BlockAddressReplay::default(),
873 }
874 }
875
876 pub fn record_hash_control_block(&mut self) -> Felt {
878 let block_addr = Felt::from_u32(self.addr);
879
880 self.block_addr_replay.record_block_address(block_addr);
881 self.addr += NUM_HASHER_ROWS_PER_PERMUTATION;
882
883 block_addr
884 }
885
886 pub fn record_hash_basic_block(&mut self, basic_block_node: &BasicBlockNode) -> Felt {
888 let block_addr = Felt::from_u32(self.addr);
889
890 self.block_addr_replay.record_block_address(block_addr);
891 self.addr += NUM_HASHER_ROWS_PER_PERMUTATION * basic_block_node.num_op_batches() as u32;
892
893 block_addr
894 }
895 pub fn record_permute_output(&mut self, hashed_state: [Felt; 12]) {
897 self.hasher_replay.record_permute(Felt::from_u32(self.addr), hashed_state);
898 self.addr += NUM_HASHER_ROWS_PER_PERMUTATION;
899 }
900
901 pub fn record_build_merkle_root(&mut self, path: &MerklePath, computed_root: Word) {
903 self.hasher_replay
904 .record_build_merkle_root(Felt::from_u32(self.addr), computed_root);
905 self.addr += NUM_HASHER_ROWS_PER_PERMUTATION * path.depth() as u32;
906 }
907
908 pub fn record_update_merkle_root(&mut self, path: &MerklePath, old_root: Word, new_root: Word) {
910 self.hasher_replay
911 .record_update_merkle_root(Felt::from_u32(self.addr), old_root, new_root);
912
913 self.addr += 2 * NUM_HASHER_ROWS_PER_PERMUTATION * path.depth() as u32;
915 }
916
917 pub fn extract_replay(&mut self) -> (HasherResponseReplay, BlockAddressReplay) {
918 (
919 core::mem::take(&mut self.hasher_replay),
920 core::mem::take(&mut self.block_addr_replay),
921 )
922 }
923}
924
925impl Default for HasherChipletShim {
926 fn default() -> Self {
927 Self::new()
928 }
929}