1use alloc::{boxed::Box, vec::Vec};
2use core::{mem::MaybeUninit, ops::ControlFlow};
3
4use itertools::Itertools;
5use miden_air::{
6 FieldElement, RowIndex,
7 trace::{
8 CLK_COL_IDX, CTX_COL_IDX, DECODER_TRACE_OFFSET, DECODER_TRACE_WIDTH, FN_HASH_RANGE,
9 MIN_TRACE_LEN, PADDED_TRACE_WIDTH, STACK_TRACE_OFFSET, STACK_TRACE_WIDTH, SYS_TRACE_WIDTH,
10 TRACE_WIDTH,
11 decoder::{
12 ADDR_COL_IDX, GROUP_COUNT_COL_IDX, HASHER_STATE_OFFSET, IN_SPAN_COL_IDX,
13 NUM_HASHER_COLUMNS, NUM_OP_BATCH_FLAGS, NUM_OP_BITS, NUM_USER_OP_HELPERS,
14 OP_BATCH_FLAGS_OFFSET, OP_BITS_EXTRA_COLS_OFFSET, OP_BITS_OFFSET, OP_INDEX_COL_IDX,
15 },
16 main_trace::MainTrace,
17 stack::{B0_COL_IDX, B1_COL_IDX, H0_COL_IDX, STACK_TOP_OFFSET},
18 },
19};
20use miden_core::{
21 Felt, Kernel, ONE, OPCODE_PUSH, Operation, QuadFelt, StarkField, WORD_SIZE, Word, ZERO,
22 mast::{BasicBlockNode, MastForest, MastNode, MastNodeExt, MastNodeId, OpBatch},
23 precompile::PrecompileTranscriptState,
24 stack::MIN_STACK_DEPTH,
25 utils::{range, uninit_vector},
26};
27use rayon::prelude::*;
28use winter_prover::{crypto::RandomCoin, math::batch_inversion};
29
30use crate::{
31 ChipletsLengths, ColMatrix, ContextId, ErrorContext, ExecutionError, ExecutionTrace,
32 ProcessState, TraceLenSummary,
33 chiplets::{Chiplets, CircuitEvaluation, MAX_NUM_ACE_WIRES, PTR_OFFSET_ELEM, PTR_OFFSET_WORD},
34 continuation_stack::Continuation,
35 crypto::RpoRandomCoin,
36 decoder::{
37 AuxTraceBuilder as DecoderAuxTraceBuilder, BasicBlockContext,
38 block_stack::ExecutionContextInfo,
39 },
40 errors::AceError,
41 fast::{
42 ExecutionOutput, NoopTracer, Tracer,
43 execution_tracer::TraceGenerationContext,
44 trace_state::{
45 AceReplay, AdviceReplay, BitwiseOp, BitwiseReplay, CoreTraceFragmentContext,
46 DecoderState, ExecutionContextSystemInfo, HasherOp, HasherRequestReplay,
47 HasherResponseReplay, KernelReplay, MemoryReadsReplay, MemoryWritesReplay,
48 NodeExecutionState, NodeFlags,
49 },
50 },
51 host::default::NoopHost,
52 processor::{
53 MemoryInterface, OperationHelperRegisters, Processor, StackInterface, SystemInterface,
54 },
55 range::RangeChecker,
56 stack::AuxTraceBuilder as StackAuxTraceBuilder,
57 trace::{AuxTraceBuilders, NUM_RAND_ROWS},
58 utils::split_u32_into_u16,
59};
60
61pub const CORE_TRACE_WIDTH: usize = SYS_TRACE_WIDTH + DECODER_TRACE_WIDTH + STACK_TRACE_WIDTH;
62
63mod basic_block;
64mod call;
65mod r#dyn;
66mod join;
67mod r#loop;
68
69mod split;
70mod trace_builder;
71
72#[cfg(test)]
73mod tests;
74
75pub fn build_trace(
80 execution_output: ExecutionOutput,
81 trace_generation_context: TraceGenerationContext,
82 program_hash: Word,
83 kernel: Kernel,
84) -> ExecutionTrace {
85 let TraceGenerationContext {
86 core_trace_contexts,
87 range_checker_replay,
88 memory_writes,
89 bitwise_replay: bitwise,
90 kernel_replay,
91 hasher_for_chiplet,
92 ace_replay,
93 final_pc_transcript,
94 fragment_size,
95 } = trace_generation_context;
96
97 let chiplets = initialize_chiplets(
98 kernel.clone(),
99 &core_trace_contexts,
100 memory_writes,
101 bitwise,
102 kernel_replay,
103 hasher_for_chiplet,
104 ace_replay,
105 );
106
107 let range_checker = initialize_range_checker(range_checker_replay, &chiplets);
108
109 let fragments = generate_core_trace_fragments(core_trace_contexts, fragment_size);
110
111 let core_trace_len = {
113 let core_trace_len: usize = fragments.iter().map(|f| f.row_count()).sum();
114
115 core_trace_len - 1
121 };
122
123 let range_table_len = range_checker.get_number_range_checker_rows();
125
126 let trace_len_summary =
127 TraceLenSummary::new(core_trace_len, range_table_len, ChipletsLengths::new(&chiplets));
128
129 let main_trace_len =
131 compute_main_trace_length(core_trace_len, range_table_len, chiplets.trace_len());
132
133 let (core_trace_columns, (range_checker_trace, chiplets_trace)) = rayon::join(
134 || combine_fragments(fragments, main_trace_len),
135 || {
136 rayon::join(
137 || {
138 range_checker.into_trace_with_table(
139 range_table_len,
140 main_trace_len,
141 NUM_RAND_ROWS,
142 )
143 },
144 || chiplets.into_trace(main_trace_len, NUM_RAND_ROWS, final_pc_transcript.state()),
145 )
146 },
147 );
148
149 let padding = vec![vec![ZERO; main_trace_len]; PADDED_TRACE_WIDTH - TRACE_WIDTH];
151
152 let mut trace_columns: Vec<Vec<Felt>> = core_trace_columns
154 .into_iter()
155 .chain(range_checker_trace.trace)
156 .chain(chiplets_trace.trace)
157 .chain(padding)
158 .collect();
159
160 let mut rng = RpoRandomCoin::new(program_hash);
162
163 for i in main_trace_len - NUM_RAND_ROWS..main_trace_len {
165 for column in trace_columns.iter_mut() {
166 column[i] = rng.draw().expect("failed to draw a random value");
167 }
168 }
169
170 let main_trace = {
172 let last_program_row = RowIndex::from((core_trace_len as u32).saturating_sub(1));
173 let col_matrix = ColMatrix::new(trace_columns);
174 MainTrace::new(col_matrix, last_program_row)
175 };
176
177 let aux_trace_builders = AuxTraceBuilders {
179 decoder: DecoderAuxTraceBuilder::default(),
180 range: range_checker_trace.aux_builder,
181 chiplets: chiplets_trace.aux_builder,
182 stack: StackAuxTraceBuilder,
183 };
184
185 ExecutionTrace::new_from_parts(
186 program_hash,
187 kernel,
188 execution_output,
189 main_trace,
190 aux_trace_builders,
191 trace_len_summary,
192 )
193}
194
195fn compute_main_trace_length(
196 core_trace_len: usize,
197 range_table_len: usize,
198 chiplets_trace_len: usize,
199) -> usize {
200 let max_len = range_table_len.max(core_trace_len).max(chiplets_trace_len);
202
203 let trace_len = (max_len + NUM_RAND_ROWS).next_power_of_two();
206 core::cmp::max(trace_len, MIN_TRACE_LEN)
207}
208
209fn generate_core_trace_fragments(
211 core_trace_contexts: Vec<CoreTraceFragmentContext>,
212 fragment_size: usize,
213) -> Vec<CoreTraceFragment> {
214 let first_stack_top = if let Some(first_context) = core_trace_contexts.first() {
216 first_context.state.stack.stack_top.to_vec()
217 } else {
218 vec![ZERO; MIN_STACK_DEPTH]
219 };
220
221 let first_system_state = [
223 ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ];
230
231 let fragment_results: Vec<(
233 CoreTraceFragment,
234 [Felt; STACK_TRACE_WIDTH],
235 [Felt; SYS_TRACE_WIDTH],
236 )> = core_trace_contexts
237 .into_par_iter()
238 .map(|trace_state| {
239 let main_trace_generator = CoreTraceFragmentGenerator::new(trace_state, fragment_size);
240 main_trace_generator.generate_fragment()
241 })
242 .collect();
243
244 let mut fragments = Vec::new();
246 let mut stack_rows = Vec::new();
247 let mut system_rows = Vec::new();
248
249 for (fragment, stack_row, system_row) in fragment_results {
250 fragments.push(fragment);
251 stack_rows.push(stack_row);
252 system_rows.push(system_row);
253 }
254
255 fixup_stack_and_system_rows(
259 &mut fragments,
260 &stack_rows,
261 &system_rows,
262 &first_stack_top,
263 &first_system_state,
264 );
265
266 append_halt_opcode_row(
267 fragments.last_mut().expect("expected at least one trace fragment"),
268 system_rows.last().expect(
269 "system_rows should not be empty, which indicates that there are no trace fragments",
270 ),
271 stack_rows.last().expect(
272 "stack_rows should not be empty, which indicates that there are no trace fragments",
273 ),
274 );
275
276 fragments
277}
278
279fn fixup_stack_and_system_rows(
282 fragments: &mut [CoreTraceFragment],
283 stack_rows: &[[Felt; STACK_TRACE_WIDTH]],
284 system_rows: &[[Felt; SYS_TRACE_WIDTH]],
285 first_stack_top: &[Felt],
286 first_system_state: &[Felt; SYS_TRACE_WIDTH],
287) {
288 const MIN_STACK_DEPTH_FELT: Felt = Felt::new(MIN_STACK_DEPTH as u64);
289
290 if fragments.is_empty() {
291 return;
292 }
293
294 if let Some(first_fragment) = fragments.first_mut() {
296 for (col_idx, &value) in first_system_state.iter().enumerate() {
298 first_fragment.columns[col_idx][0] = value;
299 }
300
301 for (stack_col_idx, &value) in first_stack_top.iter().rev().enumerate() {
305 first_fragment.columns[STACK_TRACE_OFFSET + STACK_TOP_OFFSET + stack_col_idx][0] =
306 value;
307 }
308
309 first_fragment.columns[STACK_TRACE_OFFSET + B0_COL_IDX][0] = MIN_STACK_DEPTH_FELT;
311 first_fragment.columns[STACK_TRACE_OFFSET + B1_COL_IDX][0] = ZERO;
312 first_fragment.columns[STACK_TRACE_OFFSET + H0_COL_IDX][0] = ZERO;
313 }
314
315 for (i, fragment) in fragments.iter_mut().enumerate().skip(1) {
318 if fragment.row_count() > 0 && i - 1 < stack_rows.len() && i - 1 < system_rows.len() {
319 let system_row = &system_rows[i - 1];
321 for (col_idx, &value) in system_row.iter().enumerate() {
322 fragment.columns[col_idx][0] = value;
323 }
324
325 let stack_row = &stack_rows[i - 1];
327 for (col_idx, &value) in stack_row.iter().enumerate() {
328 fragment.columns[STACK_TRACE_OFFSET + col_idx][0] = value;
329 }
330 }
331 }
332}
333
334fn append_halt_opcode_row(
339 last_fragment: &mut CoreTraceFragment,
340 last_system_state: &[Felt; SYS_TRACE_WIDTH],
341 last_stack_state: &[Felt; STACK_TRACE_WIDTH],
342) {
343 for (col_idx, &value) in last_system_state.iter().enumerate() {
346 last_fragment.columns[col_idx].push(value);
347 }
348
349 for (col_idx, &value) in last_stack_state.iter().enumerate() {
352 last_fragment.columns[STACK_TRACE_OFFSET + col_idx].push(value);
353 }
354
355 last_fragment.columns[DECODER_TRACE_OFFSET + ADDR_COL_IDX].push(ZERO);
359
360 let halt_opcode = Operation::Halt.op_code();
362 for bit_idx in 0..NUM_OP_BITS {
363 let bit_value = Felt::from((halt_opcode >> bit_idx) & 1);
364 last_fragment.columns[DECODER_TRACE_OFFSET + OP_BITS_OFFSET + bit_idx].push(bit_value);
365 }
366
367 for hasher_col_idx in 0..NUM_HASHER_COLUMNS {
371 let col_idx = DECODER_TRACE_OFFSET + HASHER_STATE_OFFSET + hasher_col_idx;
372 if hasher_col_idx < 4 {
373 let last_row_idx = last_fragment.columns[col_idx].len() - 1;
375 let last_hasher_value = last_fragment.columns[col_idx][last_row_idx];
376 last_fragment.columns[col_idx].push(last_hasher_value);
377 } else {
378 last_fragment.columns[col_idx].push(ZERO);
380 }
381 }
382
383 last_fragment.columns[DECODER_TRACE_OFFSET + IN_SPAN_COL_IDX].push(ZERO);
385
386 last_fragment.columns[DECODER_TRACE_OFFSET + GROUP_COUNT_COL_IDX].push(ZERO);
388
389 last_fragment.columns[DECODER_TRACE_OFFSET + OP_INDEX_COL_IDX].push(ZERO);
391
392 for batch_flag_idx in 0..NUM_OP_BATCH_FLAGS {
394 let col_idx = DECODER_TRACE_OFFSET + OP_BATCH_FLAGS_OFFSET + batch_flag_idx;
395 last_fragment.columns[col_idx].push(ZERO);
396 }
397
398 last_fragment.columns[DECODER_TRACE_OFFSET + OP_BITS_EXTRA_COLS_OFFSET].push(ZERO);
402 last_fragment.columns[DECODER_TRACE_OFFSET + OP_BITS_EXTRA_COLS_OFFSET + 1].push(ONE);
403}
404
405fn initialize_range_checker(
406 range_checker_replay: crate::fast::trace_state::RangeCheckerReplay,
407 chiplets: &Chiplets,
408) -> RangeChecker {
409 let mut range_checker = RangeChecker::new();
410
411 for (clk, values) in range_checker_replay.into_iter() {
413 range_checker.add_range_checks(clk, &values);
414 }
415
416 chiplets.append_range_checks(&mut range_checker);
418
419 range_checker
420}
421
422fn initialize_chiplets(
423 kernel: Kernel,
424 core_trace_contexts: &[CoreTraceFragmentContext],
425 memory_writes: MemoryWritesReplay,
426 bitwise: BitwiseReplay,
427 kernel_replay: KernelReplay,
428 hasher_for_chiplet: HasherRequestReplay,
429 ace_replay: AceReplay,
430) -> Chiplets {
431 let mut chiplets = Chiplets::new(kernel);
432
433 for hasher_op in hasher_for_chiplet.into_iter() {
435 match hasher_op {
436 HasherOp::Permute(input_state) => {
437 chiplets.hasher.permute(input_state);
438 },
439 HasherOp::HashControlBlock((h1, h2, domain, expected_hash)) => {
440 chiplets.hasher.hash_control_block(h1, h2, domain, expected_hash);
441 },
442 HasherOp::HashBasicBlock((op_batches, expected_hash)) => {
443 chiplets.hasher.hash_basic_block(&op_batches, expected_hash);
444 },
445 HasherOp::BuildMerkleRoot((value, path, index)) => {
446 chiplets.hasher.build_merkle_root(value, &path, index);
447 },
448 HasherOp::UpdateMerkleRoot((old_value, new_value, path, index)) => {
449 chiplets.hasher.update_merkle_root(old_value, new_value, &path, index);
450 },
451 }
452 }
453
454 for (bitwise_op, a, b) in bitwise {
456 match bitwise_op {
457 BitwiseOp::U32And => {
458 chiplets
459 .bitwise
460 .u32and(a, b, &())
461 .expect("bitwise AND operation failed when populating chiplet");
462 },
463 BitwiseOp::U32Xor => {
464 chiplets
465 .bitwise
466 .u32xor(a, b, &())
467 .expect("bitwise XOR operation failed when populating chiplet");
468 },
469 }
470 }
471
472 {
478 let elements_written: Box<dyn Iterator<Item = MemoryAccess>> =
479 Box::new(memory_writes.iter_elements_written().map(|(element, addr, ctx, clk)| {
480 MemoryAccess::WriteElement(*addr, *element, *ctx, *clk)
481 }));
482 let words_written: Box<dyn Iterator<Item = MemoryAccess>> = Box::new(
483 memory_writes
484 .iter_words_written()
485 .map(|(word, addr, ctx, clk)| MemoryAccess::WriteWord(*addr, *word, *ctx, *clk)),
486 );
487 let elements_read: Box<dyn Iterator<Item = MemoryAccess>> =
488 Box::new(core_trace_contexts.iter().flat_map(|ctx| {
489 ctx.replay
490 .memory_reads
491 .iter_read_elements()
492 .map(|(_, addr, ctx, clk)| MemoryAccess::ReadElement(addr, ctx, clk))
493 }));
494 let words_read: Box<dyn Iterator<Item = MemoryAccess>> =
495 Box::new(core_trace_contexts.iter().flat_map(|ctx| {
496 ctx.replay
497 .memory_reads
498 .iter_read_words()
499 .map(|(_, addr, ctx, clk)| MemoryAccess::ReadWord(addr, ctx, clk))
500 }));
501
502 [elements_written, words_written, elements_read, words_read]
503 .into_iter()
504 .kmerge_by(|a, b| a.clk() < b.clk())
505 .for_each(|mem_access| match mem_access {
506 MemoryAccess::ReadElement(addr, ctx, clk) => {
507 chiplets
508 .memory
509 .read(ctx, addr, clk, &())
510 .expect("memory read element failed when populating chiplet");
511 },
512 MemoryAccess::WriteElement(addr, element, ctx, clk) => {
513 chiplets
514 .memory
515 .write(ctx, addr, clk, element, &())
516 .expect("memory write element failed when populating chiplet");
517 },
518 MemoryAccess::ReadWord(addr, ctx, clk) => {
519 chiplets
520 .memory
521 .read_word(ctx, addr, clk, &())
522 .expect("memory read word failed when populating chiplet");
523 },
524 MemoryAccess::WriteWord(addr, word, ctx, clk) => {
525 chiplets
526 .memory
527 .write_word(ctx, addr, clk, word, &())
528 .expect("memory write word failed when populating chiplet");
529 },
530 });
531
532 enum MemoryAccess {
533 ReadElement(Felt, ContextId, RowIndex),
534 WriteElement(Felt, Felt, ContextId, RowIndex),
535 ReadWord(Felt, ContextId, RowIndex),
536 WriteWord(Felt, Word, ContextId, RowIndex),
537 }
538
539 impl MemoryAccess {
540 fn clk(&self) -> RowIndex {
541 match self {
542 MemoryAccess::ReadElement(_, _, clk) => *clk,
543 MemoryAccess::WriteElement(_, _, _, clk) => *clk,
544 MemoryAccess::ReadWord(_, _, clk) => *clk,
545 MemoryAccess::WriteWord(_, _, _, clk) => *clk,
546 }
547 }
548 }
549 }
550
551 for (clk, circuit_eval) in ace_replay.into_iter() {
553 chiplets.ace.add_circuit_evaluation(clk, circuit_eval);
554 }
555
556 for proc_hash in kernel_replay.into_iter() {
558 chiplets
559 .kernel_rom
560 .access_proc(proc_hash, &())
561 .expect("kernel proc access failed when populating chiplet");
562 }
563
564 chiplets
565}
566
567fn combine_fragments(fragments: Vec<CoreTraceFragment>, trace_len: usize) -> Vec<Vec<Felt>> {
569 if fragments.is_empty() {
570 panic!("Cannot combine empty fragments vector");
571 }
572
573 let total_program_rows: usize = fragments.iter().map(|f| f.row_count()).sum();
575
576 let mut trace_columns: Vec<Box<[MaybeUninit<Felt>]>> =
578 (0..CORE_TRACE_WIDTH).map(|_| Box::new_uninit_slice(trace_len)).collect();
579
580 let mut current_row_idx = 0;
582 for fragment in fragments {
583 let fragment_rows = fragment.row_count();
584
585 for local_row_idx in 0..fragment_rows {
586 let global_row_idx = current_row_idx + local_row_idx;
587
588 for (col_idx, trace_column) in trace_columns.iter_mut().enumerate() {
590 trace_column[global_row_idx].write(fragment.columns[col_idx][local_row_idx]);
591 }
592 }
593
594 current_row_idx += fragment_rows;
595 }
596
597 pad_trace_columns(&mut trace_columns, total_program_rows, trace_len);
599
600 let mut core_trace_columns: Vec<Vec<Felt>> = trace_columns
602 .into_iter()
603 .map(|uninit_column| {
604 let init_column = unsafe { uninit_column.assume_init() };
606 Vec::from(init_column)
607 })
608 .collect();
609
610 core_trace_columns[STACK_TRACE_OFFSET + H0_COL_IDX] =
612 batch_inversion(&core_trace_columns[STACK_TRACE_OFFSET + H0_COL_IDX]);
613
614 core_trace_columns
616}
617
618fn pad_trace_columns(
628 trace_columns: &mut [Box<[MaybeUninit<Felt>]>],
629 total_program_rows: usize,
630 trace_len: usize,
631) {
632 assert_ne!(total_program_rows, 0);
634 assert!(total_program_rows + NUM_RAND_ROWS - 1 <= trace_len);
635
636 for (clk_val, clk_row) in
641 trace_columns[CLK_COL_IDX].iter_mut().enumerate().skip(total_program_rows)
642 {
643 clk_row.write(Felt::from(clk_val as u32));
644 }
645
646 for ctx_row in trace_columns[CTX_COL_IDX].iter_mut().skip(total_program_rows) {
648 ctx_row.write(ZERO);
649 }
650
651 for fn_hash_col_idx in FN_HASH_RANGE {
654 for fn_hash_row in trace_columns[fn_hash_col_idx].iter_mut().skip(total_program_rows) {
655 fn_hash_row.write(ZERO);
656 }
657 }
658
659 for addr_row in trace_columns[DECODER_TRACE_OFFSET + ADDR_COL_IDX]
664 .iter_mut()
665 .skip(total_program_rows)
666 {
667 addr_row.write(ZERO);
668 }
669
670 let halt_opcode = Operation::Halt.op_code();
672 for i in 0..NUM_OP_BITS {
673 let bit_value = Felt::from((halt_opcode >> i) & 1);
674 for op_bit_row in trace_columns[DECODER_TRACE_OFFSET + OP_BITS_OFFSET + i]
675 .iter_mut()
676 .skip(total_program_rows)
677 {
678 op_bit_row.write(bit_value);
679 }
680 }
681
682 for i in 0..NUM_HASHER_COLUMNS {
686 let col_idx = DECODER_TRACE_OFFSET + HASHER_STATE_OFFSET + i;
687 if i < 4 {
688 let last_hasher_value =
692 unsafe { trace_columns[col_idx][total_program_rows - 1].assume_init() };
693 for hasher_row in trace_columns[col_idx].iter_mut().skip(total_program_rows) {
694 hasher_row.write(last_hasher_value);
695 }
696 } else {
697 for hasher_row in trace_columns[col_idx].iter_mut().skip(total_program_rows) {
699 hasher_row.write(ZERO);
700 }
701 }
702 }
703
704 for in_span_row in trace_columns[DECODER_TRACE_OFFSET + IN_SPAN_COL_IDX]
706 .iter_mut()
707 .skip(total_program_rows)
708 {
709 in_span_row.write(ZERO);
710 }
711
712 for group_count_row in trace_columns[DECODER_TRACE_OFFSET + GROUP_COUNT_COL_IDX]
714 .iter_mut()
715 .skip(total_program_rows)
716 {
717 group_count_row.write(ZERO);
718 }
719
720 for op_idx_row in trace_columns[DECODER_TRACE_OFFSET + OP_INDEX_COL_IDX]
722 .iter_mut()
723 .skip(total_program_rows)
724 {
725 op_idx_row.write(ZERO);
726 }
727
728 for i in 0..NUM_OP_BATCH_FLAGS {
730 let col_idx = DECODER_TRACE_OFFSET + OP_BATCH_FLAGS_OFFSET + i;
731 for op_batch_flag_row in trace_columns[col_idx].iter_mut().skip(total_program_rows) {
732 op_batch_flag_row.write(ZERO);
733 }
734 }
735
736 for op_bit_extra_row in trace_columns[DECODER_TRACE_OFFSET + OP_BITS_EXTRA_COLS_OFFSET]
740 .iter_mut()
741 .skip(total_program_rows)
742 {
743 op_bit_extra_row.write(ZERO);
744 }
745 for op_bit_extra_row in trace_columns[DECODER_TRACE_OFFSET + OP_BITS_EXTRA_COLS_OFFSET + 1]
746 .iter_mut()
747 .skip(total_program_rows)
748 {
749 op_bit_extra_row.write(ONE);
750 }
751
752 for i in 0..STACK_TRACE_WIDTH {
757 let col_idx = STACK_TRACE_OFFSET + i;
758 let last_stack_value =
761 unsafe { trace_columns[col_idx][total_program_rows - 1].assume_init() };
762 for stack_row in trace_columns[col_idx].iter_mut().skip(total_program_rows) {
763 stack_row.write(last_stack_value);
764 }
765 }
766}
767
768struct CoreTraceFragment {
777 pub columns: [Vec<Felt>; CORE_TRACE_WIDTH],
778}
779
780impl CoreTraceFragment {
781 pub unsafe fn new_uninit(num_rows: usize) -> Self {
787 Self {
788 columns: core::array::from_fn(|_| unsafe { uninit_vector(num_rows) }),
790 }
791 }
792
793 pub fn row_count(&self) -> usize {
795 self.columns[0].len()
796 }
797}
798
799struct CoreTraceFragmentGenerator {
800 fragment_start_clk: RowIndex,
801 fragment: CoreTraceFragment,
802 context: CoreTraceFragmentContext,
803 span_context: Option<BasicBlockContext>,
804 stack_rows: Option<[Felt; STACK_TRACE_WIDTH]>,
805 system_rows: Option<[Felt; SYS_TRACE_WIDTH]>,
806 fragment_size: usize,
807}
808
809impl CoreTraceFragmentGenerator {
810 pub fn new(context: CoreTraceFragmentContext, fragment_size: usize) -> Self {
812 Self {
813 fragment_start_clk: context.state.system.clk,
814 fragment: unsafe { CoreTraceFragment::new_uninit(fragment_size) },
818 context,
819 span_context: None,
820 stack_rows: None,
821 system_rows: None,
822 fragment_size,
823 }
824 }
825
826 pub fn generate_fragment(
828 mut self,
829 ) -> (CoreTraceFragment, [Felt; STACK_TRACE_WIDTH], [Felt; SYS_TRACE_WIDTH]) {
830 let execution_state = self.context.initial_execution_state.clone();
831 let _ = self.execute_fragment_generation(execution_state);
833 let final_stack_rows = self.stack_rows.unwrap_or([ZERO; STACK_TRACE_WIDTH]);
834 let final_system_rows = self.system_rows.unwrap_or([ZERO; SYS_TRACE_WIDTH]);
835 let fragment = self.finalize_fragment();
836 (fragment, final_stack_rows, final_system_rows)
837 }
838
839 fn execute_fragment_generation(
841 &mut self,
842 execution_state: NodeExecutionState,
843 ) -> ControlFlow<()> {
844 let initial_mast_forest = self.context.initial_mast_forest.clone();
845
846 match execution_state {
848 NodeExecutionState::BasicBlock { node_id, batch_index, op_idx_in_batch } => {
849 let basic_block_node = {
850 let mast_node =
851 initial_mast_forest.get_node_by_id(node_id).expect("node should exist");
852 mast_node.get_basic_block().expect("Expected a basic block node")
853 };
854
855 let op_batches = basic_block_node.op_batches();
856 assert!(
857 batch_index < op_batches.len(),
858 "Batch index out of bounds: {batch_index} >= {}",
859 op_batches.len()
860 );
861
862 self.span_context =
864 Some(initialize_span_context(basic_block_node, batch_index, op_idx_in_batch));
865
866 {
868 let current_batch = &op_batches[batch_index];
869 self.execute_op_batch(
870 current_batch,
871 Some(op_idx_in_batch),
872 &initial_mast_forest,
873 )?;
874 }
875
876 for op_batch in op_batches.iter().skip(batch_index + 1) {
878 self.respan(op_batch)?;
879
880 self.execute_op_batch(op_batch, None, &initial_mast_forest)?;
881 }
882
883 self.add_span_end_trace_row(basic_block_node)?;
885 },
886 NodeExecutionState::Start(node_id) => {
887 self.execute_mast_node(node_id, &initial_mast_forest)?;
888 },
889 NodeExecutionState::Respan { node_id, batch_index } => {
890 let basic_block_node = {
891 let mast_node =
892 initial_mast_forest.get_node_by_id(node_id).expect("node should exist");
893 mast_node.get_basic_block().expect("Expected a basic block node")
894 };
895 let current_batch = &basic_block_node.op_batches()[batch_index];
896
897 self.span_context = {
898 let current_op_group = current_batch.groups()[0];
899 let num_groups_left: usize = basic_block_node
900 .op_batches()
901 .iter()
902 .skip(batch_index)
903 .map(|batch| batch.num_groups())
904 .sum();
905
906 Some(BasicBlockContext {
907 group_ops_left: current_op_group,
908 num_groups_left: Felt::new(num_groups_left as u64),
909 })
910 };
911
912 for op_batch in basic_block_node.op_batches().iter().skip(batch_index) {
914 self.respan(op_batch)?;
915
916 self.execute_op_batch(op_batch, None, &initial_mast_forest)?;
917 }
918
919 self.add_span_end_trace_row(basic_block_node)?;
921 },
922 NodeExecutionState::LoopRepeat(node_id) => {
923 let loop_node = initial_mast_forest
925 .get_node_by_id(node_id)
926 .expect("node should exist")
927 .unwrap_loop();
928
929 let mut condition = self.get(0);
930 self.decrement_size(&mut NoopTracer);
931
932 while condition == ONE {
933 self.add_loop_repeat_trace_row(
934 loop_node,
935 &initial_mast_forest,
936 self.context.state.decoder.current_addr,
937 )?;
938
939 self.execute_mast_node(loop_node.body(), &initial_mast_forest)?;
940
941 condition = self.get(0);
942 self.decrement_size(&mut NoopTracer);
943 }
944
945 self.add_end_trace_row(loop_node.digest())?;
950 },
951 NodeExecutionState::End(node_id) => {
954 let mast_node =
955 initial_mast_forest.get_node_by_id(node_id).expect("node should exist");
956
957 match mast_node {
958 MastNode::Join(join_node) => {
959 self.add_end_trace_row(join_node.digest())?;
960 },
961 MastNode::Split(split_node) => {
962 self.add_end_trace_row(split_node.digest())?;
963 },
964 MastNode::Loop(loop_node) => {
965 let (ended_node_addr, flags) = self.update_decoder_state_on_node_end();
966
967 if flags.loop_entered() == ONE {
969 self.decrement_size(&mut NoopTracer);
970 }
971
972 self.add_end_trace_row_impl(loop_node.digest(), flags, ended_node_addr)?;
973 },
974 MastNode::Call(call_node) => {
975 let ctx_info = self.context.replay.block_stack.replay_execution_context();
976 self.restore_context_from_replay(&ctx_info);
977 self.add_end_trace_row(call_node.digest())?;
978 },
979 MastNode::Dyn(dyn_node) => {
980 if dyn_node.is_dyncall() {
981 let ctx_info =
982 self.context.replay.block_stack.replay_execution_context();
983 self.restore_context_from_replay(&ctx_info);
984 }
985 self.add_end_trace_row(dyn_node.digest())?;
986 },
987 MastNode::Block(basic_block_node) => {
988 self.add_span_end_trace_row(basic_block_node)?;
989 },
990 MastNode::External(_external_node) => {
991 panic!("Unexpected external node in END execution state")
994 },
995 }
996 },
997 }
998
999 let mut current_forest = self.context.initial_mast_forest.clone();
1001
1002 while let Some(continuation) = self.context.continuation.pop_continuation() {
1003 match continuation {
1004 Continuation::StartNode(node_id) => {
1005 self.execute_mast_node(node_id, ¤t_forest)?;
1006 },
1007 Continuation::FinishJoin(node_id) => {
1008 let mast_node =
1009 current_forest.get_node_by_id(node_id).expect("node should exist");
1010 self.add_end_trace_row(mast_node.digest())?;
1011 },
1012 Continuation::FinishSplit(node_id) => {
1013 let mast_node =
1014 current_forest.get_node_by_id(node_id).expect("node should exist");
1015 self.add_end_trace_row(mast_node.digest())?;
1016 },
1017 Continuation::FinishLoop(node_id) => {
1018 let loop_node = current_forest
1019 .get_node_by_id(node_id)
1020 .expect("node should exist")
1021 .unwrap_loop();
1022
1023 let mut condition = self.get(0);
1024 self.decrement_size(&mut NoopTracer);
1025
1026 while condition == ONE {
1027 self.add_loop_repeat_trace_row(
1028 loop_node,
1029 ¤t_forest,
1030 self.context.state.decoder.current_addr,
1031 )?;
1032
1033 self.execute_mast_node(loop_node.body(), ¤t_forest)?;
1034
1035 condition = self.get(0);
1036 self.decrement_size(&mut NoopTracer);
1037 }
1038
1039 self.add_end_trace_row(loop_node.digest())?;
1044 },
1045 Continuation::FinishCall(node_id) => {
1046 let mast_node =
1047 current_forest.get_node_by_id(node_id).expect("node should exist");
1048
1049 let ctx_info = self.context.replay.block_stack.replay_execution_context();
1051 self.restore_context_from_replay(&ctx_info);
1052
1053 self.add_end_trace_row(mast_node.digest())?;
1055 },
1056 Continuation::FinishDyn(node_id) => {
1057 let dyn_node = current_forest
1058 .get_node_by_id(node_id)
1059 .expect("node should exist")
1060 .unwrap_dyn();
1061 if dyn_node.is_dyncall() {
1062 let ctx_info = self.context.replay.block_stack.replay_execution_context();
1063 self.restore_context_from_replay(&ctx_info);
1064 }
1065
1066 self.add_end_trace_row(dyn_node.digest())?;
1067 },
1068 Continuation::FinishExternal(_node_id) => {
1069 },
1074 Continuation::EnterForest(previous_forest) => {
1075 current_forest = previous_forest;
1077 },
1078 }
1079 }
1080
1081 ControlFlow::Continue(())
1083 }
1084
1085 fn execute_mast_node(
1086 &mut self,
1087 node_id: MastNodeId,
1088 current_forest: &MastForest,
1089 ) -> ControlFlow<()> {
1090 let mast_node = current_forest.get_node_by_id(node_id).expect("node should exist");
1091
1092 match mast_node {
1093 MastNode::Block(basic_block_node) => {
1094 self.update_decoder_state_on_node_start();
1095
1096 let num_groups_left_in_block = Felt::from(basic_block_node.num_op_groups() as u32);
1097 let first_op_batch = basic_block_node
1098 .op_batches()
1099 .first()
1100 .expect("Basic block should have at least one op batch");
1101
1102 self.add_span_start_trace_row(
1104 first_op_batch,
1105 num_groups_left_in_block,
1106 self.context.state.decoder.parent_addr,
1108 )?;
1109
1110 self.span_context = Some(BasicBlockContext {
1116 group_ops_left: first_op_batch.groups()[0],
1117 num_groups_left: num_groups_left_in_block - ONE,
1118 });
1119
1120 let op_batches = basic_block_node.op_batches();
1122
1123 {
1125 let first_op_batch =
1126 op_batches.first().expect("Basic block should have at least one op batch");
1127 self.execute_op_batch(first_op_batch, None, current_forest)?;
1128 }
1129
1130 for op_batch in op_batches.iter().skip(1) {
1132 self.respan(op_batch)?;
1134
1135 self.execute_op_batch(op_batch, None, current_forest)?;
1136 }
1137
1138 self.add_span_end_trace_row(basic_block_node)?;
1140
1141 ControlFlow::Continue(())
1142 },
1143 MastNode::Join(join_node) => {
1144 self.update_decoder_state_on_node_start();
1145
1146 self.add_join_start_trace_row(
1148 join_node,
1149 current_forest,
1150 self.context.state.decoder.parent_addr,
1151 )?;
1152
1153 self.execute_mast_node(join_node.first(), current_forest)?;
1155
1156 self.execute_mast_node(join_node.second(), current_forest)?;
1158
1159 self.add_end_trace_row(join_node.digest())
1161 },
1162 MastNode::Split(split_node) => {
1163 self.update_decoder_state_on_node_start();
1164
1165 let condition = self.get(0);
1166 self.decrement_size(&mut NoopTracer);
1167
1168 self.add_split_start_trace_row(
1170 split_node,
1171 current_forest,
1172 self.context.state.decoder.parent_addr,
1173 )?;
1174
1175 if condition == ONE {
1177 self.execute_mast_node(split_node.on_true(), current_forest)?;
1178 } else {
1179 self.execute_mast_node(split_node.on_false(), current_forest)?;
1180 }
1181
1182 self.add_end_trace_row(split_node.digest())
1184 },
1185 MastNode::Loop(loop_node) => {
1186 self.update_decoder_state_on_node_start();
1187
1188 let mut condition = self.get(0);
1191 self.decrement_size(&mut NoopTracer);
1192
1193 self.add_loop_start_trace_row(
1195 loop_node,
1196 current_forest,
1197 self.context.state.decoder.parent_addr,
1198 )?;
1199
1200 if condition == ONE {
1205 self.execute_mast_node(loop_node.body(), current_forest)?;
1206
1207 condition = self.get(0);
1208 self.decrement_size(&mut NoopTracer);
1209 }
1210
1211 while condition == ONE {
1212 self.add_loop_repeat_trace_row(
1213 loop_node,
1214 current_forest,
1215 self.context.state.decoder.current_addr,
1216 )?;
1217
1218 self.execute_mast_node(loop_node.body(), current_forest)?;
1219
1220 condition = self.get(0);
1221 self.decrement_size(&mut NoopTracer);
1222 }
1223
1224 self.add_end_trace_row(loop_node.digest())
1229 },
1230 MastNode::Call(call_node) => {
1231 self.update_decoder_state_on_node_start();
1232
1233 self.context.state.stack.start_context();
1234
1235 if call_node.is_syscall() {
1237 self.context.state.system.ctx = ContextId::root(); } else {
1239 self.context.state.system.ctx =
1240 ContextId::from(self.context.state.system.clk + 1); self.context.state.system.fn_hash = current_forest[call_node.callee()].digest();
1242 }
1243
1244 self.add_call_start_trace_row(
1246 call_node,
1247 current_forest,
1248 self.context.state.decoder.parent_addr,
1249 )?;
1250
1251 self.execute_mast_node(call_node.callee(), current_forest)?;
1253
1254 let ctx_info = self.context.replay.block_stack.replay_execution_context();
1256 self.restore_context_from_replay(&ctx_info);
1257
1258 self.add_end_trace_row(call_node.digest())
1260 },
1261 MastNode::Dyn(dyn_node) => {
1262 self.update_decoder_state_on_node_start();
1263
1264 let callee_hash = {
1265 let mem_addr = self.context.state.stack.get(0);
1266 self.context.replay.memory_reads.replay_read_word(mem_addr)
1267 };
1268
1269 if dyn_node.is_dyncall() {
1271 let (stack_depth, next_overflow_addr) =
1272 self.shift_stack_left_and_start_context();
1273 let ctx_info = ExecutionContextInfo::new(
1276 self.context.state.system.ctx,
1277 self.context.state.system.fn_hash,
1278 stack_depth as u32,
1279 next_overflow_addr,
1280 );
1281
1282 self.context.state.system.ctx =
1283 ContextId::from(self.context.state.system.clk + 1); self.context.state.system.fn_hash = callee_hash;
1285
1286 self.add_dyncall_start_trace_row(
1287 self.context.state.decoder.parent_addr,
1288 callee_hash,
1289 ctx_info,
1290 )?;
1291 } else {
1292 self.decrement_size(&mut NoopTracer);
1294 self.add_dyn_start_trace_row(
1295 self.context.state.decoder.parent_addr,
1296 callee_hash,
1297 )?;
1298 };
1299
1300 match current_forest.find_procedure_root(callee_hash) {
1302 Some(callee_id) => self.execute_mast_node(callee_id, current_forest)?,
1303 None => {
1304 let (resolved_node_id, resolved_forest) =
1305 self.context.replay.mast_forest_resolution.replay_resolution();
1306
1307 self.execute_mast_node(resolved_node_id, &resolved_forest)?
1308 },
1309 };
1310
1311 if dyn_node.is_dyncall() {
1313 let ctx_info = self.context.replay.block_stack.replay_execution_context();
1314 self.restore_context_from_replay(&ctx_info);
1315 }
1316
1317 self.add_end_trace_row(dyn_node.digest())
1319 },
1320 MastNode::External(_) => {
1321 let (resolved_node_id, resolved_forest) =
1322 self.context.replay.mast_forest_resolution.replay_resolution();
1323
1324 self.execute_mast_node(resolved_node_id, &resolved_forest)
1325 },
1326 }
1327 }
1328
1329 fn update_decoder_state_on_node_start(&mut self) {
1339 let DecoderState { current_addr, parent_addr } = &mut self.context.state.decoder;
1340
1341 *current_addr = self.context.replay.hasher.replay_block_address();
1342 *parent_addr = self.context.replay.block_stack.replay_node_start();
1343 }
1344
1345 fn update_decoder_state_on_node_end(&mut self) -> (Felt, NodeFlags) {
1350 let DecoderState { current_addr, parent_addr } = &mut self.context.state.decoder;
1351
1352 let node_end_data = self.context.replay.block_stack.replay_node_end();
1353
1354 *current_addr = node_end_data.prev_addr;
1355 *parent_addr = node_end_data.prev_parent_addr;
1356
1357 (node_end_data.ended_node_addr, node_end_data.flags)
1358 }
1359
1360 fn restore_context_from_replay(&mut self, ctx_info: &ExecutionContextSystemInfo) {
1365 self.context.state.system.ctx = ctx_info.parent_ctx;
1366 self.context.state.system.fn_hash = ctx_info.parent_fn_hash;
1367
1368 self.context
1369 .state
1370 .stack
1371 .restore_context(&mut self.context.replay.stack_overflow);
1372 }
1373
1374 fn execute_op_batch(
1378 &mut self,
1379 batch: &OpBatch,
1380 start_op_idx: Option<usize>,
1381 current_forest: &MastForest,
1382 ) -> ControlFlow<()> {
1383 let start_op_idx = start_op_idx.unwrap_or(0);
1384 let end_indices = batch.end_indices();
1385
1386 for (op_idx_in_batch, (op_group_idx, op_idx_in_group, op)) in
1388 batch.iter_with_groups().enumerate().skip(start_op_idx)
1389 {
1390 {
1391 let user_op_helpers = if let Operation::Emit = op {
1394 None
1395 } else {
1396 self.execute_sync_op(
1400 op,
1401 0,
1402 current_forest,
1403 &mut NoopHost,
1404 &(),
1405 &mut NoopTracer,
1406 )
1407 .expect("operation should execute successfully")
1411 };
1412
1413 self.add_operation_trace_row(*op, op_idx_in_group, user_op_helpers)?;
1415 }
1416
1417 if op_idx_in_batch + 1 == end_indices[op_group_idx]
1420 && let Some(next_op_group_idx) = batch.next_op_group_index(op_group_idx)
1421 {
1422 self.start_op_group(batch.groups()[next_op_group_idx]);
1423 }
1424 }
1425
1426 ControlFlow::Continue(())
1427 }
1428
1429 pub fn start_op_group(&mut self, op_group: Felt) {
1431 let ctx = self.span_context.as_mut().expect("not in span");
1432
1433 debug_assert_eq!(ZERO, ctx.group_ops_left, "not all ops executed in current group");
1435 ctx.group_ops_left = op_group;
1436 ctx.num_groups_left -= ONE;
1437 }
1438
1439 fn finalize_fragment(mut self) -> CoreTraceFragment {
1441 debug_assert!(
1442 self.num_rows_built() <= self.fragment_size,
1443 "built too many rows: {} > {}",
1444 self.num_rows_built(),
1445 self.fragment_size
1446 );
1447
1448 let num_rows = core::cmp::min(self.num_rows_built(), self.fragment_size);
1452 for column in &mut self.fragment.columns {
1453 column.truncate(num_rows);
1454 }
1455
1456 self.fragment
1457 }
1458
1459 pub fn shift_stack_left_and_start_context(&mut self) -> (usize, Felt) {
1464 self.decrement_size(&mut NoopTracer);
1465 self.context.state.stack.start_context()
1466 }
1467
1468 fn append_opcode(&mut self, opcode: u8, row_idx: usize) {
1469 use miden_air::trace::{
1470 DECODER_TRACE_OFFSET,
1471 decoder::{NUM_OP_BITS, OP_BITS_OFFSET},
1472 };
1473
1474 let bits: [Felt; NUM_OP_BITS] = core::array::from_fn(|i| Felt::from((opcode >> i) & 1));
1476 for (i, bit) in bits.iter().enumerate() {
1477 self.fragment.columns[DECODER_TRACE_OFFSET + OP_BITS_OFFSET + i][row_idx] = *bit;
1478 }
1479
1480 self.fragment.columns[DECODER_TRACE_OFFSET + OP_BITS_EXTRA_COLS_OFFSET][row_idx] =
1482 bits[6] * (ONE - bits[5]) * bits[4];
1483 self.fragment.columns[DECODER_TRACE_OFFSET + OP_BITS_EXTRA_COLS_OFFSET + 1][row_idx] =
1484 bits[6] * bits[5];
1485 }
1486
1487 fn done_generating(&mut self) -> bool {
1488 self.num_rows_built() >= self.fragment_size
1490 }
1491
1492 fn num_rows_built(&self) -> usize {
1493 self.context.state.system.clk - self.fragment_start_clk
1495 }
1496
1497 fn increment_clk(&mut self) -> ControlFlow<()> {
1498 self.context.state.system.clk += 1u32;
1499
1500 if self.done_generating() {
1502 ControlFlow::Break(())
1504 } else {
1505 ControlFlow::Continue(())
1507 }
1508 }
1509}
1510
1511fn initialize_span_context(
1526 basic_block_node: &BasicBlockNode,
1527 batch_index: usize,
1528 op_idx_in_batch: usize,
1529) -> BasicBlockContext {
1530 let op_batches = basic_block_node.op_batches();
1531 let (current_op_group_idx, op_idx_in_group) = op_batches[batch_index]
1532 .op_idx_in_batch_to_group(op_idx_in_batch)
1533 .expect("invalid batch");
1534
1535 let group_ops_left = {
1536 let current_op_group = op_batches[batch_index].groups()[current_op_group_idx];
1541
1542 Felt::new(current_op_group.as_int() >> (NUM_OP_BITS * op_idx_in_group))
1549 };
1550
1551 let num_groups_left = {
1552 let total_groups = basic_block_node.num_op_groups();
1555
1556 let mut groups_consumed = 0;
1558 for op_batch in op_batches.iter().take(batch_index) {
1559 groups_consumed += op_batch.num_groups().next_power_of_two();
1560 }
1561
1562 {
1565 let mut current_op_group =
1568 op_batches[batch_index].groups()[current_op_group_idx].as_int();
1569 for _ in 0..op_idx_in_group {
1570 let current_op = (current_op_group & 0b1111111) as u8;
1571 if current_op == OPCODE_PUSH {
1572 groups_consumed += 1;
1573 }
1574
1575 current_op_group >>= NUM_OP_BITS; }
1577 }
1578
1579 groups_consumed += current_op_group_idx + 1;
1583
1584 Felt::from((total_groups - groups_consumed) as u32)
1585 };
1586
1587 BasicBlockContext { group_ops_left, num_groups_left }
1588}
1589
1590impl StackInterface for CoreTraceFragmentGenerator {
1594 fn top(&self) -> &[Felt] {
1595 &self.context.state.stack.stack_top
1596 }
1597
1598 fn top_mut(&mut self) -> &mut [Felt] {
1599 &mut self.context.state.stack.stack_top
1600 }
1601
1602 fn get(&self, idx: usize) -> Felt {
1603 debug_assert!(idx < MIN_STACK_DEPTH);
1604 self.context.state.stack.stack_top[MIN_STACK_DEPTH - idx - 1]
1605 }
1606
1607 fn get_mut(&mut self, idx: usize) -> &mut Felt {
1608 debug_assert!(idx < MIN_STACK_DEPTH);
1609
1610 &mut self.context.state.stack.stack_top[MIN_STACK_DEPTH - idx - 1]
1611 }
1612
1613 fn get_word(&self, start_idx: usize) -> Word {
1614 debug_assert!(start_idx < MIN_STACK_DEPTH - 4);
1615
1616 let word_start_idx = MIN_STACK_DEPTH - start_idx - 4;
1617 self.top()[range(word_start_idx, WORD_SIZE)].try_into().unwrap()
1618 }
1619
1620 fn depth(&self) -> u32 {
1621 (MIN_STACK_DEPTH + self.context.state.stack.num_overflow_elements_in_current_ctx()) as u32
1622 }
1623
1624 fn set(&mut self, idx: usize, element: Felt) {
1625 *self.get_mut(idx) = element;
1626 }
1627
1628 fn set_word(&mut self, start_idx: usize, word: &Word) {
1629 debug_assert!(start_idx < MIN_STACK_DEPTH - 4);
1630 let word_start_idx = MIN_STACK_DEPTH - start_idx - 4;
1631
1632 let word_on_stack =
1633 &mut self.context.state.stack.stack_top[range(word_start_idx, WORD_SIZE)];
1634 word_on_stack.copy_from_slice(word.as_slice());
1635 }
1636
1637 fn swap(&mut self, idx1: usize, idx2: usize) {
1638 let a = self.get(idx1);
1639 let b = self.get(idx2);
1640 self.set(idx1, b);
1641 self.set(idx2, a);
1642 }
1643
1644 fn swapw_nth(&mut self, n: usize) {
1645 let (rest_of_stack, top_word) =
1651 self.context.state.stack.stack_top.split_at_mut(MIN_STACK_DEPTH - WORD_SIZE);
1652 let (_, nth_word) = rest_of_stack.split_at_mut(rest_of_stack.len() - n * WORD_SIZE);
1653
1654 nth_word[0..WORD_SIZE].swap_with_slice(&mut top_word[0..WORD_SIZE]);
1655 }
1656
1657 fn rotate_left(&mut self, n: usize) {
1658 let rotation_bot_index = MIN_STACK_DEPTH - n;
1659 let new_stack_top_element = self.context.state.stack.stack_top[rotation_bot_index];
1660
1661 for i in 0..n - 1 {
1663 self.context.state.stack.stack_top[rotation_bot_index + i] =
1664 self.context.state.stack.stack_top[rotation_bot_index + i + 1];
1665 }
1666
1667 self.set(0, new_stack_top_element);
1669 }
1670
1671 fn rotate_right(&mut self, n: usize) {
1672 let rotation_bot_index = MIN_STACK_DEPTH - n;
1673 let new_stack_bot_element = self.context.state.stack.stack_top[MIN_STACK_DEPTH - 1];
1674
1675 for i in 1..n {
1677 self.context.state.stack.stack_top[MIN_STACK_DEPTH - i] =
1678 self.context.state.stack.stack_top[MIN_STACK_DEPTH - i - 1];
1679 }
1680
1681 self.context.state.stack.stack_top[rotation_bot_index] = new_stack_bot_element;
1683 }
1684
1685 fn increment_size(&mut self, _tracer: &mut impl Tracer) -> Result<(), ExecutionError> {
1686 const SENTINEL_VALUE: Felt = Felt::new(Felt::MODULUS - 1);
1687
1688 {
1690 let last_element = self.get(MIN_STACK_DEPTH - 1);
1691 self.context.state.stack.push_overflow(last_element, self.clk());
1692 }
1693
1694 for write_idx in (1..MIN_STACK_DEPTH).rev() {
1696 let read_idx = write_idx - 1;
1697 self.set(write_idx, self.get(read_idx));
1698 }
1699
1700 self.set(0, SENTINEL_VALUE);
1703
1704 Ok(())
1705 }
1706
1707 fn decrement_size(&mut self, _tracer: &mut impl Tracer) {
1708 for write_idx in 0..(MIN_STACK_DEPTH - 1) {
1710 let read_idx = write_idx + 1;
1711 self.set(write_idx, self.get(read_idx));
1712 }
1713
1714 if let Some(last_element) =
1716 self.context.state.stack.pop_overflow(&mut self.context.replay.stack_overflow)
1717 {
1718 self.set(MIN_STACK_DEPTH - 1, last_element);
1720 } else {
1721 self.set(MIN_STACK_DEPTH - 1, ZERO);
1723 }
1724 }
1725}
1726
1727impl Processor for CoreTraceFragmentGenerator {
1728 type HelperRegisters = TraceGenerationHelpers;
1729 type System = Self;
1730 type Stack = Self;
1731 type AdviceProvider = AdviceReplay;
1732 type Memory = MemoryReadsReplay;
1733 type Hasher = HasherResponseReplay;
1734
1735 fn stack(&mut self) -> &mut Self::Stack {
1736 self
1737 }
1738
1739 fn system(&mut self) -> &mut Self::System {
1740 self
1741 }
1742
1743 fn state(&mut self) -> ProcessState<'_> {
1744 ProcessState::Noop(())
1745 }
1746
1747 fn advice_provider(&mut self) -> &mut Self::AdviceProvider {
1748 &mut self.context.replay.advice
1749 }
1750
1751 fn memory(&mut self) -> &mut Self::Memory {
1752 &mut self.context.replay.memory_reads
1753 }
1754
1755 fn hasher(&mut self) -> &mut Self::Hasher {
1756 &mut self.context.replay.hasher
1757 }
1758
1759 fn precompile_transcript_state(&self) -> PrecompileTranscriptState {
1760 self.context.state.system.pc_transcript_state
1761 }
1762
1763 fn set_precompile_transcript_state(&mut self, state: PrecompileTranscriptState) {
1764 self.context.state.system.pc_transcript_state = state;
1765 }
1766
1767 fn op_eval_circuit(
1768 &mut self,
1769 err_ctx: &impl ErrorContext,
1770 tracer: &mut impl Tracer,
1771 ) -> Result<(), ExecutionError> {
1772 let num_eval = self.stack().get(2);
1773 let num_read = self.stack().get(1);
1774 let ptr = self.stack().get(0);
1775 let ctx = self.system().ctx();
1776
1777 let _circuit_evaluation = eval_circuit_fast_(
1778 ctx,
1779 ptr,
1780 self.system().clk(),
1781 num_read,
1782 num_eval,
1783 self,
1784 err_ctx,
1785 tracer,
1786 )?;
1787
1788 Ok(())
1789 }
1790}
1791
1792impl SystemInterface for CoreTraceFragmentGenerator {
1793 fn caller_hash(&self) -> Word {
1794 self.context.state.system.fn_hash
1795 }
1796
1797 fn clk(&self) -> RowIndex {
1798 self.context.state.system.clk
1799 }
1800
1801 fn ctx(&self) -> ContextId {
1802 self.context.state.system.ctx
1803 }
1804}
1805
1806struct TraceGenerationHelpers;
1809
1810impl OperationHelperRegisters for TraceGenerationHelpers {
1811 #[inline(always)]
1812 fn op_eq_registers(stack_second: Felt, stack_first: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
1813 let h0 = if stack_second == stack_first {
1814 ZERO
1815 } else {
1816 (stack_first - stack_second).inv()
1817 };
1818
1819 [h0, ZERO, ZERO, ZERO, ZERO, ZERO]
1820 }
1821
1822 #[inline(always)]
1823 fn op_u32split_registers(hi: Felt, lo: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
1824 let (t1, t0) = split_u32_into_u16(lo.as_int());
1825 let (t3, t2) = split_u32_into_u16(hi.as_int());
1826 let m = (Felt::from(u32::MAX) - hi).inv();
1827
1828 [Felt::from(t0), Felt::from(t1), Felt::from(t2), Felt::from(t3), m, ZERO]
1829 }
1830
1831 #[inline(always)]
1832 fn op_eqz_registers(top: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
1833 let h0 = if top == ZERO { ZERO } else { top.inv() };
1836
1837 [h0, ZERO, ZERO, ZERO, ZERO, ZERO]
1838 }
1839
1840 #[inline(always)]
1841 fn op_expacc_registers(acc_update_val: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
1842 [acc_update_val, ZERO, ZERO, ZERO, ZERO, ZERO]
1843 }
1844
1845 #[inline(always)]
1846 fn op_fri_ext2fold4_registers(
1847 ev: QuadFelt,
1848 es: QuadFelt,
1849 x: Felt,
1850 x_inv: Felt,
1851 ) -> [Felt; NUM_USER_OP_HELPERS] {
1852 let ev_arr = [ev];
1853 let ev_felts = QuadFelt::slice_as_base_elements(&ev_arr);
1854
1855 let es_arr = [es];
1856 let es_felts = QuadFelt::slice_as_base_elements(&es_arr);
1857
1858 [ev_felts[0], ev_felts[1], es_felts[0], es_felts[1], x, x_inv]
1859 }
1860
1861 #[inline(always)]
1862 fn op_u32add_registers(hi: Felt, lo: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
1863 let (t1, t0) = split_u32_into_u16(lo.as_int());
1865 let (t3, t2) = split_u32_into_u16(hi.as_int());
1866
1867 [Felt::from(t0), Felt::from(t1), Felt::from(t2), Felt::from(t3), ZERO, ZERO]
1869 }
1870
1871 #[inline(always)]
1872 fn op_u32add3_registers(hi: Felt, lo: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
1873 let (t1, t0) = split_u32_into_u16(lo.as_int());
1875 let (t3, t2) = split_u32_into_u16(hi.as_int());
1876
1877 [Felt::from(t0), Felt::from(t1), Felt::from(t2), Felt::from(t3), ZERO, ZERO]
1878 }
1879
1880 #[inline(always)]
1881 fn op_u32sub_registers(second_new: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
1882 let (t1, t0) = split_u32_into_u16(second_new.as_int());
1884
1885 [Felt::from(t0), Felt::from(t1), ZERO, ZERO, ZERO, ZERO]
1886 }
1887
1888 #[inline(always)]
1889 fn op_u32mul_registers(hi: Felt, lo: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
1890 let (t1, t0) = split_u32_into_u16(lo.as_int());
1892 let (t3, t2) = split_u32_into_u16(hi.as_int());
1893 let m = (Felt::from(u32::MAX) - hi).inv();
1894
1895 [Felt::from(t0), Felt::from(t1), Felt::from(t2), Felt::from(t3), m, ZERO]
1896 }
1897
1898 #[inline(always)]
1899 fn op_u32madd_registers(hi: Felt, lo: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
1900 let (t1, t0) = split_u32_into_u16(lo.as_int());
1902 let (t3, t2) = split_u32_into_u16(hi.as_int());
1903 let m = (Felt::from(u32::MAX) - hi).inv();
1904
1905 [Felt::from(t0), Felt::from(t1), Felt::from(t2), Felt::from(t3), m, ZERO]
1906 }
1907
1908 #[inline(always)]
1909 fn op_u32div_registers(hi: Felt, lo: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
1910 let (t1, t0) = split_u32_into_u16(lo.as_int());
1912 let (t3, t2) = split_u32_into_u16(hi.as_int());
1913
1914 [Felt::from(t0), Felt::from(t1), Felt::from(t2), Felt::from(t3), ZERO, ZERO]
1915 }
1916
1917 #[inline(always)]
1918 fn op_u32assert2_registers(first: Felt, second: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
1919 let (t1, t0) = split_u32_into_u16(second.as_int());
1921 let (t3, t2) = split_u32_into_u16(first.as_int());
1922
1923 [Felt::from(t0), Felt::from(t1), Felt::from(t2), Felt::from(t3), ZERO, ZERO]
1924 }
1925
1926 #[inline(always)]
1927 fn op_hperm_registers(addr: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
1928 [addr, ZERO, ZERO, ZERO, ZERO, ZERO]
1929 }
1930
1931 #[inline(always)]
1932 fn op_merkle_path_registers(addr: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
1933 [addr, ZERO, ZERO, ZERO, ZERO, ZERO]
1934 }
1935
1936 #[inline(always)]
1937 fn op_horner_eval_registers(
1938 alpha: QuadFelt,
1939 k0: Felt,
1940 k1: Felt,
1941 acc_tmp: QuadFelt,
1942 ) -> [Felt; NUM_USER_OP_HELPERS] {
1943 [
1944 alpha.base_element(0),
1945 alpha.base_element(1),
1946 k0,
1947 k1,
1948 acc_tmp.base_element(0),
1949 acc_tmp.base_element(1),
1950 ]
1951 }
1952
1953 #[inline(always)]
1954 fn op_log_precompile_registers(addr: Felt, cap_prev: Word) -> [Felt; NUM_USER_OP_HELPERS] {
1955 [addr, cap_prev[0], cap_prev[1], cap_prev[2], cap_prev[3], ZERO]
1958 }
1959}
1960
1961#[allow(clippy::too_many_arguments)]
1969fn eval_circuit_fast_(
1970 ctx: ContextId,
1971 ptr: Felt,
1972 clk: RowIndex,
1973 num_vars: Felt,
1974 num_eval: Felt,
1975 processor: &mut CoreTraceFragmentGenerator,
1976 err_ctx: &impl ErrorContext,
1977 tracer: &mut impl Tracer,
1978) -> Result<CircuitEvaluation, ExecutionError> {
1979 let num_vars = num_vars.as_int();
1980 let num_eval = num_eval.as_int();
1981
1982 let num_wires = num_vars + num_eval;
1983 if num_wires > MAX_NUM_ACE_WIRES as u64 {
1984 return Err(ExecutionError::failed_arithmetic_evaluation(
1985 err_ctx,
1986 AceError::TooManyWires(num_wires),
1987 ));
1988 }
1989
1990 if !num_vars.is_multiple_of(2) || num_vars == 0 {
1994 return Err(ExecutionError::failed_arithmetic_evaluation(
1995 err_ctx,
1996 AceError::NumVarIsNotWordAlignedOrIsEmpty(num_vars),
1997 ));
1998 }
1999 if !num_eval.is_multiple_of(4) || num_eval == 0 {
2000 return Err(ExecutionError::failed_arithmetic_evaluation(
2001 err_ctx,
2002 AceError::NumEvalIsNotWordAlignedOrIsEmpty(num_eval),
2003 ));
2004 }
2005
2006 let num_read_rows = num_vars as u32 / 2;
2008 let num_eval_rows = num_eval as u32;
2009
2010 let mut evaluation_context = CircuitEvaluation::new(ctx, clk, num_read_rows, num_eval_rows);
2011
2012 let mut ptr = ptr;
2013 for _ in 0..num_read_rows {
2017 let word = processor
2018 .memory()
2019 .read_word(ctx, ptr, clk, err_ctx)
2020 .map_err(ExecutionError::MemoryError)?;
2021 tracer.record_memory_read_word(word, ptr, ctx, clk);
2022 evaluation_context.do_read(ptr, word)?;
2023 ptr += PTR_OFFSET_WORD;
2024 }
2025 for _ in 0..num_eval_rows {
2027 let instruction = processor
2028 .memory()
2029 .read_element(ctx, ptr, err_ctx)
2030 .map_err(ExecutionError::MemoryError)?;
2031 tracer.record_memory_read_element(instruction, ptr, ctx, clk);
2032 evaluation_context.do_eval(ptr, instruction, err_ctx)?;
2033 ptr += PTR_OFFSET_ELEM;
2034 }
2035
2036 if !evaluation_context.output_value().is_some_and(|eval| eval == QuadFelt::ZERO) {
2038 return Err(ExecutionError::failed_arithmetic_evaluation(
2039 err_ctx,
2040 AceError::CircuitNotEvaluateZero,
2041 ));
2042 }
2043
2044 Ok(evaluation_context)
2045}