1use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, vec::Vec};
2use core::borrow::{Borrow, BorrowMut};
3
4use itertools::Itertools;
5use miden_air::{
6 AIRS, CoreCols, Felt, MIDEN_AIR_COUNT, MidenAir, StackCols, SystemCols, config, memory,
7 trace::{
8 DECODER_TRACE_WIDTH, MIN_TRACE_LEN, MainTrace, RANGE_CHECK_TRACE_WIDTH, RowIndex,
9 STACK_TRACE_WIDTH, SYS_TRACE_WIDTH, chiplets::bitwise::OP_CYCLE_LEN, decoder::NUM_OP_BITS,
10 },
11};
12use miden_core::{
13 ONE, Word, ZERO,
14 field::{PrimeCharacteristicRing, batch_inversion_allow_zeros},
15 mast::{MastForestId, OpBatch, SparseMastForest},
16 operations::opcodes,
17 program::{KernelDescriptor, MIN_STACK_DEPTH},
18 utils::Idx,
19};
20use rayon::prelude::*;
21use tracing::{info_span, instrument};
22
23use super::{
24 chiplets::Chiplets,
25 execution_tracer::TraceReplay,
26 trace_state::{
27 AceReplay, BitwiseOp, BitwiseReplay, CoreTraceFragmentContext, CoreTraceState,
28 ExecutionReplay, HasherRequestReplay, KernelReplay, MemoryWritesReplay, RangeCheckerReplay,
29 ResolvedBasicBlockGroups, ResolvedHasherOp,
30 },
31};
32use crate::{
33 ContextId, ExecutionError,
34 continuation_stack::{Continuation, ContinuationStack},
35 errors::MapExecErrNoCtx,
36 trace::{
37 ChipletsLengths, TraceLenSummary, VmTrace, VmWitness,
38 chiplets::{Ace, Bitwise, Hasher, KernelRom, Memory},
39 parallel::{processor::ReplayProcessor, tracer::CoreTraceGenerationTracer},
40 range::RangeChecker,
41 utils::RowMajorTraceWriter,
42 },
43};
44
45pub const CORE_TRACE_WIDTH: usize = SYS_TRACE_WIDTH + DECODER_TRACE_WIDTH + STACK_TRACE_WIDTH;
47
48pub const CORE_STORAGE_WIDTH: usize = CORE_TRACE_WIDTH + RANGE_CHECK_TRACE_WIDTH;
53
54pub(crate) const MAX_TRACE_LEN: usize = 1 << 29;
63
64pub const DEFAULT_MAX_PROVER_MEMORY_BYTES: u64 = 64 << 30;
69
70pub(crate) mod core_trace_fragment;
71
72mod processor;
73mod tracer;
74
75#[cfg(test)]
76mod tests;
77
78#[instrument(name = "build_trace", skip_all)]
103pub fn build_trace(witness: VmWitness) -> Result<VmTrace, ExecutionError> {
104 build_trace_inner(witness, None, DEFAULT_MAX_PROVER_MEMORY_BYTES)
105}
106
107pub fn build_trace_with_budget(
109 witness: VmWitness,
110 max_prover_memory_bytes: u64,
111) -> Result<VmTrace, ExecutionError> {
112 build_trace_inner(witness, None, max_prover_memory_bytes)
113}
114
115#[cfg(feature = "std")]
119pub(crate) fn build_trace_with_prebuilt_hasher(
120 witness: VmWitness,
121 prebuilt_hasher: Hasher,
122 max_prover_memory_bytes: u64,
123) -> Result<VmTrace, ExecutionError> {
124 build_trace_inner(witness, Some(prebuilt_hasher), max_prover_memory_bytes)
125}
126
127fn build_trace_inner(
128 witness: VmWitness,
129 prebuilt_hasher: Option<Hasher>,
130 max_prover_memory_bytes: u64,
131) -> Result<VmTrace, ExecutionError> {
132 let VmWitness {
133 program_info,
134 stack_inputs,
135 stack_outputs,
136 trace,
137 precompile_root,
138 } = witness;
139
140 let TraceReplay {
141 core_trace_contexts,
142 mast_forest_store,
143 range_checker_replay,
144 memory_writes,
145 bitwise_replay: bitwise,
146 kernel_replay,
147 hasher_for_chiplet,
148 ace_replay,
149 fragment_size,
150 max_stack_depth,
151 } = trace;
152
153 let pcs_params = config::pcs_params();
154
155 let max_trace_len =
159 MAX_TRACE_LEN.min(memory::max_any_height_for_budget(max_prover_memory_bytes, &pcs_params));
160
161 let max_core_alloc_len = MAX_TRACE_LEN.min(max_core_alloc_rows(max_prover_memory_bytes));
172 let total_core_trace_rows = core_trace_contexts
173 .len()
174 .checked_mul(fragment_size)
175 .and_then(|n| n.checked_add(1))
176 .ok_or(ExecutionError::TraceLenExceeded(max_core_alloc_len))?;
177 if total_core_trace_rows > max_core_alloc_len {
178 return Err(ExecutionError::TraceLenExceeded(max_core_alloc_len));
179 }
180
181 if core_trace_contexts.is_empty() {
182 return Err(ExecutionError::Internal("no trace fragments provided in the trace witness"));
183 }
184
185 let chiplets = info_span!("initialize_chiplets").in_scope(|| {
186 initialize_chiplets(
187 program_info.kernel().clone(),
188 &core_trace_contexts,
189 memory_writes,
190 bitwise,
191 kernel_replay,
192 hasher_for_chiplet,
193 prebuilt_hasher,
194 ace_replay,
195 &mast_forest_store,
196 max_trace_len,
197 )
198 })?;
199
200 let range_checker = info_span!("initialize_range_checker")
201 .in_scope(|| initialize_range_checker(range_checker_replay, &chiplets));
202
203 let mut core_trace_data = info_span!("generate_core_trace").in_scope(|| {
204 generate_core_trace_row_major(
205 core_trace_contexts,
206 program_info.kernel().clone(),
207 fragment_size,
208 &mast_forest_store,
209 max_stack_depth,
210 )
211 })?;
212
213 let core_trace_len = core_trace_data.len() / CORE_STORAGE_WIDTH;
214
215 let range_table_len = range_checker.get_number_range_checker_rows();
217
218 let core_height = pad_to_trace_length(core_trace_len.max(range_table_len));
219 let chiplets_height = pad_to_trace_length(chiplets.trace_len());
220 let poseidon2_permutation_trace_len = chiplets.poseidon2_permutation_trace_len();
221 let poseidon2_permutation_height = pad_to_trace_length(poseidon2_permutation_trace_len);
222
223 debug_assert_eq!(
226 AIRS,
227 [MidenAir::Core, MidenAir::Chiplets, MidenAir::Poseidon2Permutation],
228 "heights below must be listed in AIRS order",
229 );
230 let heights: [usize; MIDEN_AIR_COUNT] =
231 [core_height, chiplets_height, poseidon2_permutation_height];
232 validate_heights_within_max_trace_len(&heights)?;
233 let estimated_bytes = memory::prover_peak_bytes(&heights, &pcs_params).ok_or(
234 ExecutionError::ProverMemoryExceeded {
235 estimated_bytes: u64::MAX,
236 budget_bytes: max_prover_memory_bytes,
237 },
238 )?;
239 if estimated_bytes > max_prover_memory_bytes {
240 return Err(ExecutionError::ProverMemoryExceeded {
241 estimated_bytes,
242 budget_bytes: max_prover_memory_bytes,
243 });
244 }
245
246 let trace_len_summary = TraceLenSummary::new_with_padded(
247 core_trace_len,
248 range_table_len,
249 ChipletsLengths::new(&chiplets),
250 poseidon2_permutation_trace_len,
251 heights,
252 );
253
254 let ((chiplets_trace, poseidon2_permutation_trace), ()) = info_span!("chiplet_traces_core_pad")
256 .in_scope(|| {
257 rayon::join(
258 || chiplets.into_traces(chiplets_height, poseidon2_permutation_height),
259 || pad_core_row_major(&mut core_trace_data, core_height),
260 )
261 });
262
263 info_span!("write_range_checker_columns").in_scope(|| {
265 range_checker.write_range_into_core(
266 &mut core_trace_data,
267 CORE_STORAGE_WIDTH,
268 CORE_TRACE_WIDTH,
269 CORE_TRACE_WIDTH + 1,
270 range_table_len,
271 core_height,
272 )
273 });
274
275 let main_trace = {
277 let last_program_row = RowIndex::from((core_trace_len as u32).saturating_sub(1));
278 MainTrace::from_parts(
279 core_trace_data,
280 chiplets_trace.trace,
281 poseidon2_permutation_trace.trace,
282 last_program_row,
283 )
284 };
285
286 Ok(VmTrace::new_from_parts(
287 program_info,
288 stack_inputs,
289 stack_outputs,
290 precompile_root,
291 main_trace,
292 trace_len_summary,
293 ))
294}
295
296fn pad_to_trace_length(logical_len: usize) -> usize {
301 logical_len.next_power_of_two().max(MIN_TRACE_LEN)
302}
303
304fn max_core_alloc_rows(max_prover_memory_bytes: u64) -> usize {
311 let bytes_per_row = (CORE_STORAGE_WIDTH * size_of::<Felt>()) as u64;
312 usize::try_from(max_prover_memory_bytes / bytes_per_row).unwrap_or(usize::MAX)
313}
314
315fn validate_heights_within_max_trace_len(
319 heights: &[usize; MIDEN_AIR_COUNT],
320) -> Result<(), ExecutionError> {
321 if heights.iter().any(|&height| height > MAX_TRACE_LEN) {
322 return Err(ExecutionError::TraceLenExceeded(MAX_TRACE_LEN));
323 }
324 Ok(())
325}
326
327fn generate_core_trace_row_major(
329 core_trace_contexts: Vec<CoreTraceFragmentContext>,
330 kernel: KernelDescriptor,
331 fragment_size: usize,
332 mast_forest_store: &[Arc<SparseMastForest>],
333 max_stack_depth: usize,
334) -> Result<Vec<Felt>, ExecutionError> {
335 let num_fragments = core_trace_contexts.len();
336 let total_allocated_rows = num_fragments * fragment_size;
337
338 let mut core_trace_data = Felt::zero_vec(total_allocated_rows * CORE_STORAGE_WIDTH);
339
340 let first_stack_top = if let Some(first_context) = core_trace_contexts.first() {
342 first_context.state.stack.stack_top.to_vec()
343 } else {
344 vec![ZERO; MIN_STACK_DEPTH]
345 };
346
347 let writers: Vec<RowMajorTraceWriter<'_, Felt>> = core_trace_data
348 .chunks_exact_mut(fragment_size * CORE_STORAGE_WIDTH)
349 .map(|chunk| {
350 RowMajorTraceWriter::with_stride(chunk, CORE_STORAGE_WIDTH, CORE_STORAGE_WIDTH)
351 })
352 .collect();
353
354 let fragment_results: Result<Vec<_>, ExecutionError> = core_trace_contexts
356 .into_par_iter()
357 .zip(writers.into_par_iter())
358 .map(|(trace_state, writer)| {
359 let (mut processor, mut tracer, mut continuation_stack, mut current_forest) =
360 split_trace_fragment_context(
361 trace_state,
362 writer,
363 fragment_size,
364 mast_forest_store,
365 max_stack_depth,
366 )?;
367
368 processor.execute(
369 &mut continuation_stack,
370 &mut current_forest,
371 &kernel,
372 &mut tracer,
373 )?;
374
375 tracer.into_final_state()
376 })
377 .collect();
378 let fragment_results = fragment_results?;
379
380 let mut stack_rows = Vec::new();
381 let mut system_rows = Vec::new();
382 let mut total_core_trace_rows = 0;
383
384 for final_state in fragment_results {
385 stack_rows.push(final_state.last_stack_cols);
386 system_rows.push(final_state.last_system_cols);
387 total_core_trace_rows += final_state.num_rows_written;
388 }
389
390 fixup_stack_and_system_rows(
392 &mut core_trace_data,
393 fragment_size,
394 &stack_rows,
395 &system_rows,
396 &first_stack_top,
397 );
398 set_loop_body_multiplicities(&mut core_trace_data, total_core_trace_rows)?;
399
400 {
404 let w = CORE_STORAGE_WIDTH;
405 core_trace_data[..total_core_trace_rows * w]
406 .par_chunks_mut(fragment_size * w)
407 .for_each(|fragment_chunk| {
408 let num_rows = fragment_chunk.len() / w;
409 let mut h0_vals: Vec<Felt> = (0..num_rows)
410 .map(|r| {
411 let row: &CoreCols<Felt> = fragment_chunk[r * w..(r + 1) * w].borrow();
412 row.stack.h0
413 })
414 .collect();
415 batch_inversion_allow_zeros(&mut h0_vals);
416 for (r, &val) in h0_vals.iter().enumerate() {
417 let row: &mut CoreCols<Felt> = fragment_chunk[r * w..(r + 1) * w].borrow_mut();
418 row.stack.h0 = val;
419 }
420 });
421 }
422
423 core_trace_data.truncate(total_core_trace_rows * CORE_STORAGE_WIDTH);
425
426 push_halt_opcode_row(
427 &mut core_trace_data,
428 total_core_trace_rows,
429 system_rows
430 .last()
431 .ok_or(ExecutionError::Internal("no trace fragments provided in the trace witness"))?,
432 stack_rows
433 .last()
434 .ok_or(ExecutionError::Internal("no trace fragments provided in the trace witness"))?,
435 );
436
437 Ok(core_trace_data)
438}
439
440fn set_loop_body_multiplicities(
449 core_trace_data: &mut [Felt],
450 num_rows: usize,
451) -> Result<(), ExecutionError> {
452 if num_rows < 2 {
453 return Ok(());
454 }
455
456 let mut loop_body_counts = BTreeMap::<u64, u64>::new();
457 let width = CORE_STORAGE_WIDTH;
458
459 for row_idx in 0..num_rows - 1 {
460 let row: &CoreCols<Felt> = core_trace_data[row_idx * width..(row_idx + 1) * width].borrow();
461
462 if decode_opcode(&row.decoder.op_bits) != opcodes::END
463 || row.decoder.end_block_flags().is_loop_body != ONE
464 {
465 continue;
466 }
467
468 let next: &CoreCols<Felt> =
469 core_trace_data[(row_idx + 1) * width..(row_idx + 2) * width].borrow();
470 let loop_addr = next.decoder.addr.as_canonical_u64();
471 *loop_body_counts.entry(loop_addr).or_insert(0) += 1;
472 }
473
474 for row_idx in 0..num_rows - 1 {
475 let is_loop = {
476 let row: &CoreCols<Felt> =
477 core_trace_data[row_idx * width..(row_idx + 1) * width].borrow();
478 decode_opcode(&row.decoder.op_bits) == opcodes::LOOP
479 };
480 if !is_loop {
481 continue;
482 }
483
484 let loop_addr = {
485 let next: &CoreCols<Felt> =
486 core_trace_data[(row_idx + 1) * width..(row_idx + 2) * width].borrow();
487 next.decoder.addr.as_canonical_u64()
488 };
489 let body_count = loop_body_counts
490 .get(&loop_addr)
491 .copied()
492 .ok_or(ExecutionError::Internal("dynamic LOOP has no matching body END"))?;
493
494 let row: &mut CoreCols<Felt> =
495 core_trace_data[row_idx * width..(row_idx + 1) * width].borrow_mut();
496 row.decoder.group_count = Felt::new_unchecked(body_count);
497 }
498
499 Ok(())
500}
501
502fn decode_opcode(op_bits: &[Felt; NUM_OP_BITS]) -> u8 {
503 let mut opcode = 0u8;
504 for (idx, bit) in op_bits.iter().enumerate() {
505 opcode |= ((bit.as_canonical_u64() & 1) as u8) << idx;
506 }
507 opcode
508}
509
510fn fixup_stack_and_system_rows(
517 core_trace_data: &mut [Felt],
518 fragment_size: usize,
519 stack_rows: &[StackCols<Felt>],
520 system_rows: &[SystemCols<Felt>],
521 first_stack_top: &[Felt],
522) {
523 const MIN_STACK_DEPTH_FELT: Felt = Felt::new_unchecked(MIN_STACK_DEPTH as u64);
524 let w = CORE_STORAGE_WIDTH;
525
526 {
527 let row: &mut CoreCols<Felt> = core_trace_data[..w].borrow_mut();
528
529 for (stack_col_idx, &value) in first_stack_top.iter().rev().enumerate() {
531 row.stack.top[stack_col_idx] = value;
532 }
533
534 row.stack.b0 = MIN_STACK_DEPTH_FELT;
535 row.stack.b1 = ZERO;
536 row.stack.h0 = ZERO;
537 }
538
539 let total_rows = core_trace_data.len() / w;
540 let num_fragments = total_rows / fragment_size;
541
542 for frag_idx in 1..num_fragments {
543 let row_idx = frag_idx * fragment_size;
544 let row_start = row_idx * w;
545 let row: &mut CoreCols<Felt> = core_trace_data[row_start..row_start + w].borrow_mut();
546 row.system = system_rows[frag_idx - 1].clone();
547 row.stack = stack_rows[frag_idx - 1].clone();
548 }
549}
550
551fn push_halt_opcode_row(
556 core_trace_data: &mut Vec<Felt>,
557 num_rows_before: usize,
558 last_system_state: &SystemCols<Felt>,
559 last_stack_state: &StackCols<Felt>,
560) {
561 let w = CORE_STORAGE_WIDTH;
562 let mut row_data = [ZERO; CORE_STORAGE_WIDTH];
563
564 let prev_hasher_state_first_half: [Felt; 4] = if num_rows_before > 0 {
567 let last_row_start = (num_rows_before - 1) * w;
568 let prev: &CoreCols<Felt> = core_trace_data[last_row_start..last_row_start + w].borrow();
569 let hs = &prev.decoder.hasher_state;
570 [hs[0], hs[1], hs[2], hs[3]]
571 } else {
572 [ZERO; 4]
573 };
574
575 {
576 let row: &mut CoreCols<Felt> = row_data.as_mut_slice().borrow_mut();
577
578 row.system = last_system_state.clone();
579 row.stack = last_stack_state.clone();
580
581 let halt_opcode = opcodes::HALT;
583 for bit_idx in 0..NUM_OP_BITS {
584 row.decoder.op_bits[bit_idx] = Felt::from_u8((halt_opcode >> bit_idx) & 1);
585 }
586
587 row.decoder.hasher_state[..4].copy_from_slice(&prev_hasher_state_first_half);
591
592 row.decoder.extra[1] = ONE;
596 }
597
598 core_trace_data.extend_from_slice(&row_data);
599}
600
601fn initialize_range_checker(
607 range_checker_replay: RangeCheckerReplay,
608 chiplets: &Chiplets,
609) -> RangeChecker {
610 let mut range_checker = RangeChecker::new();
611
612 for values in range_checker_replay {
614 range_checker.add_range_checks(values.as_ref());
615 }
616
617 chiplets.append_range_checks(&mut range_checker);
619
620 range_checker
621}
622
623fn initialize_chiplets(
633 kernel: KernelDescriptor,
634 core_trace_contexts: &[CoreTraceFragmentContext],
635 memory_writes: MemoryWritesReplay,
636 bitwise: BitwiseReplay,
637 kernel_replay: KernelReplay,
638 hasher_for_chiplet: HasherRequestReplay,
639 prebuilt_hasher: Option<Hasher>,
640 ace_replay: AceReplay,
641 mast_forest_store: &[Arc<SparseMastForest>],
642 max_trace_len: usize,
643) -> Result<Chiplets, ExecutionError> {
644 let non_hasher_trace_len = non_hasher_trace_len(
645 &kernel,
646 core_trace_contexts,
647 &memory_writes,
648 &bitwise,
649 &ace_replay,
650 max_trace_len,
651 )?;
652 let max_hasher_trace_len = max_trace_len
653 .checked_sub(non_hasher_trace_len)
654 .ok_or(ExecutionError::TraceLenExceeded(max_trace_len))?;
655
656 if prebuilt_hasher
657 .as_ref()
658 .is_some_and(|hasher| hasher.trace_len() > max_hasher_trace_len)
659 {
660 return Err(ExecutionError::TraceLenExceeded(max_trace_len));
661 }
662
663 let (hasher, (bitwise, (memory, (ace, kernel_rom)))) = rayon::join(
664 || match prebuilt_hasher {
665 Some(hasher) => Ok(hasher),
666 None => build_hasher_chiplet(
667 hasher_for_chiplet.into_resolved_ops(mast_forest_store),
668 max_hasher_trace_len,
669 )
670 .map_err(|err| match err {
671 ExecutionError::TraceLenExceeded(_) => {
674 ExecutionError::TraceLenExceeded(max_trace_len)
675 },
676 other => other,
677 }),
678 },
679 || {
680 rayon::join(
681 || build_bitwise_chiplet(bitwise, max_trace_len),
682 || {
683 rayon::join(
684 || build_memory_chiplet(memory_writes, core_trace_contexts, max_trace_len),
685 || {
686 rayon::join(
687 || build_ace_chiplet(ace_replay, max_trace_len),
688 || build_kernel_rom_chiplet(kernel, kernel_replay, max_trace_len),
689 )
690 },
691 )
692 },
693 )
694 },
695 );
696
697 let chiplets = Chiplets {
698 hasher: hasher?,
699 bitwise: bitwise?,
700 memory: memory?,
701 ace: ace?,
702 kernel_rom: kernel_rom?,
703 };
704 debug_assert_eq!(
705 non_hasher_trace_len,
706 chiplets.trace_len() - chiplets.hasher.trace_len(),
707 "chiplet preflight length differs from the materialized trace",
708 );
709 if chiplets.trace_len() > max_trace_len {
712 return Err(ExecutionError::TraceLenExceeded(max_trace_len));
713 }
714 Ok(chiplets)
715}
716
717fn non_hasher_trace_len(
718 kernel: &KernelDescriptor,
719 core_trace_contexts: &[CoreTraceFragmentContext],
720 memory_writes: &MemoryWritesReplay,
721 bitwise: &BitwiseReplay,
722 ace: &AceReplay,
723 max_trace_len: usize,
724) -> Result<usize, ExecutionError> {
725 let overflow = || ExecutionError::TraceLenExceeded(max_trace_len);
726 let bitwise_len = bitwise.num_operations().checked_mul(OP_CYCLE_LEN).ok_or_else(overflow)?;
727 let memory_reads_len = core_trace_contexts.iter().try_fold(0usize, |len, context| {
728 len.checked_add(context.replay.memory_reads.num_accesses()?)
729 });
730 let memory_len = memory_writes
731 .num_accesses()
732 .and_then(|writes| memory_reads_len.and_then(|reads| writes.checked_add(reads)))
733 .ok_or_else(overflow)?;
734 let ace_len = ace.trace_len().ok_or_else(overflow)?;
735
736 [1, kernel.proc_hashes().len(), bitwise_len, memory_len, ace_len]
737 .into_iter()
738 .try_fold(0usize, usize::checked_add)
739 .filter(|&total| total <= max_trace_len)
740 .ok_or_else(overflow)
741}
742
743pub(crate) fn build_hasher_chiplet<'a>(
749 ops: impl IntoIterator<Item = Result<ResolvedHasherOp<'a>, ExecutionError>>,
750 max_trace_len: usize,
751) -> Result<Hasher, ExecutionError> {
752 let mut hasher = Hasher::default();
753 for hasher_op in ops {
754 match hasher_op? {
755 ResolvedHasherOp::Permute(input_state) => {
756 let _ = hasher.permute(input_state);
757 },
758 ResolvedHasherOp::HashControlBlock((h1, h2, domain, expected_hash)) => {
759 let _ = hasher.hash_control_block(h1, h2, domain, expected_hash);
760 },
761 ResolvedHasherOp::HashBasicBlock((batch_groups, expected_hash)) => match batch_groups {
762 ResolvedBasicBlockGroups::Borrowed(op_batches) => {
763 let _ = hasher
764 .hash_basic_block(op_batches.iter().map(OpBatch::groups), expected_hash);
765 },
766 ResolvedBasicBlockGroups::Owned(batch_groups) => {
767 let _ = hasher.hash_basic_block(batch_groups.iter(), expected_hash);
768 },
769 },
770 ResolvedHasherOp::BuildMerkleRoot((value, path, index)) => {
771 let _ = hasher.build_merkle_root(value, &path, index);
772 },
773 ResolvedHasherOp::UpdateMerkleRoot((old_value, new_value, path, index)) => {
774 hasher.update_merkle_root(old_value, new_value, &path, index);
775 },
776 }
777 if hasher.trace_len() > max_trace_len {
778 return Err(ExecutionError::TraceLenExceeded(max_trace_len));
779 }
780 }
781 Ok(hasher)
782}
783
784fn build_bitwise_chiplet(
786 bitwise_replay: BitwiseReplay,
787 max_trace_len: usize,
788) -> Result<Bitwise, ExecutionError> {
789 let mut bitwise = Bitwise::default();
790 for (bitwise_op, a, b) in bitwise_replay {
791 match bitwise_op {
792 BitwiseOp::U32And => {
793 bitwise.u32and(a, b).map_exec_err_no_ctx()?;
794 },
795 BitwiseOp::U32Xor => {
796 bitwise.u32xor(a, b).map_exec_err_no_ctx()?;
797 },
798 }
799 if bitwise.trace_len() > max_trace_len {
800 return Err(ExecutionError::TraceLenExceeded(max_trace_len));
801 }
802 }
803 Ok(bitwise)
804}
805
806fn build_memory_chiplet(
808 memory_writes: MemoryWritesReplay,
809 core_trace_contexts: &[CoreTraceFragmentContext],
810 max_trace_len: usize,
811) -> Result<Memory, ExecutionError> {
812 enum MemoryAccess {
813 ReadElement(Felt, ContextId, RowIndex),
814 WriteElement(Felt, Felt, ContextId, RowIndex),
815 ReadWord(Felt, ContextId, RowIndex),
816 WriteWord(Felt, Word, ContextId, RowIndex),
817 }
818
819 impl MemoryAccess {
820 fn clk(&self) -> RowIndex {
821 match self {
822 MemoryAccess::ReadElement(_, _, clk) => *clk,
823 MemoryAccess::WriteElement(_, _, _, clk) => *clk,
824 MemoryAccess::ReadWord(_, _, clk) => *clk,
825 MemoryAccess::WriteWord(_, _, _, clk) => *clk,
826 }
827 }
828 }
829
830 let mut memory = Memory::default();
831
832 let elements_written: Box<dyn Iterator<Item = MemoryAccess>> =
836 Box::new(memory_writes.iter_elements_written().map(|(element, addr, ctx, clk)| {
837 MemoryAccess::WriteElement(*addr, *element, *ctx, *clk)
838 }));
839 let words_written: Box<dyn Iterator<Item = MemoryAccess>> = Box::new(
840 memory_writes
841 .iter_words_written()
842 .map(|(word, addr, ctx, clk)| MemoryAccess::WriteWord(*addr, *word, *ctx, *clk)),
843 );
844 let elements_read: Box<dyn Iterator<Item = MemoryAccess>> =
845 Box::new(core_trace_contexts.iter().flat_map(|ctx| {
846 ctx.replay
847 .memory_reads
848 .iter_read_elements()
849 .map(|(_, addr, ctx, clk)| MemoryAccess::ReadElement(addr, ctx, clk))
850 }));
851 let words_read: Box<dyn Iterator<Item = MemoryAccess>> =
852 Box::new(core_trace_contexts.iter().flat_map(|ctx| {
853 ctx.replay
854 .memory_reads
855 .iter_read_words()
856 .map(|(_, addr, ctx, clk)| MemoryAccess::ReadWord(addr, ctx, clk))
857 }));
858
859 [elements_written, words_written, elements_read, words_read]
860 .into_iter()
861 .kmerge_by(|a, b| a.clk() < b.clk())
862 .try_for_each(|mem_access| {
863 match mem_access {
864 MemoryAccess::ReadElement(addr, ctx, clk) => memory
865 .read(ctx, addr, clk)
866 .map(|_| ())
867 .map_err(ExecutionError::MemoryErrorNoCtx)?,
868 MemoryAccess::WriteElement(addr, element, ctx, clk) => memory
869 .write(ctx, addr, clk, element)
870 .map_err(ExecutionError::MemoryErrorNoCtx)?,
871 MemoryAccess::ReadWord(addr, ctx, clk) => memory
872 .read_word(ctx, addr, clk)
873 .map(|_| ())
874 .map_err(ExecutionError::MemoryErrorNoCtx)?,
875 MemoryAccess::WriteWord(addr, word, ctx, clk) => memory
876 .write_word(ctx, addr, clk, word)
877 .map_err(ExecutionError::MemoryErrorNoCtx)?,
878 }
879 if memory.trace_len() > max_trace_len {
880 return Err(ExecutionError::TraceLenExceeded(max_trace_len));
881 }
882 Ok(())
883 })?;
884
885 Ok(memory)
886}
887
888fn build_ace_chiplet(ace_replay: AceReplay, max_trace_len: usize) -> Result<Ace, ExecutionError> {
890 let mut ace = Ace::default();
891 for (clk, circuit_eval) in ace_replay.into_iter() {
892 ace.add_circuit_evaluation(clk, circuit_eval);
893 if ace.trace_len() > max_trace_len {
894 return Err(ExecutionError::TraceLenExceeded(max_trace_len));
895 }
896 }
897 Ok(ace)
898}
899
900fn build_kernel_rom_chiplet(
902 kernel: KernelDescriptor,
903 kernel_replay: KernelReplay,
904 max_trace_len: usize,
905) -> Result<KernelRom, ExecutionError> {
906 let mut kernel_rom = KernelRom::new(kernel);
907 for proc_hash in kernel_replay.into_iter() {
908 kernel_rom.access_proc(proc_hash).map_exec_err_no_ctx()?;
909 if kernel_rom.trace_len() > max_trace_len {
910 return Err(ExecutionError::TraceLenExceeded(max_trace_len));
911 }
912 }
913 Ok(kernel_rom)
914}
915
916fn pad_core_row_major(core_trace_data: &mut Vec<Felt>, core_height: usize) {
918 let w = CORE_STORAGE_WIDTH;
919 let total_program_rows = core_trace_data.len() / w;
920 assert!(total_program_rows <= core_height);
921 assert!(total_program_rows > 0);
922
923 let num_padding_rows = core_height - total_program_rows;
924 if num_padding_rows == 0 {
925 return;
926 }
927 let last_row_start = (total_program_rows - 1) * w;
928
929 let (last_hasher_first_half, last_stack): ([Felt; 4], StackCols<Felt>) = {
932 let last: &CoreCols<Felt> = core_trace_data[last_row_start..last_row_start + w].borrow();
933 let hs = &last.decoder.hasher_state;
934 let last_hasher: [Felt; 4] = [hs[0], hs[1], hs[2], hs[3]];
935 (last_hasher, last.stack.clone())
936 };
937
938 let mut template_data = [ZERO; CORE_STORAGE_WIDTH];
939 {
940 let template: &mut CoreCols<Felt> = template_data.as_mut_slice().borrow_mut();
941
942 let halt_opcode = opcodes::HALT;
947 for i in 0..NUM_OP_BITS {
948 template.decoder.op_bits[i] = Felt::from_u8((halt_opcode >> i) & 1);
949 }
950 template.decoder.hasher_state[..4].copy_from_slice(&last_hasher_first_half);
954
955 template.decoder.extra[1] = ONE;
959
960 template.stack = last_stack;
965 }
966
967 let pad_start = total_program_rows * w;
973 core_trace_data.resize(pad_start + num_padding_rows * w, ZERO);
974 core_trace_data[pad_start..]
975 .par_chunks_mut(w)
976 .enumerate()
977 .for_each(|(idx, row_buf)| {
978 row_buf.copy_from_slice(&template_data);
979 let row: &mut CoreCols<Felt> = row_buf.borrow_mut();
980 row.system.clk = Felt::from_u32((total_program_rows + idx) as u32);
981 });
982}
983
984type SplitFragmentContext<'a> = (
985 ReplayProcessor,
986 CoreTraceGenerationTracer<'a>,
987 ContinuationStack<Arc<SparseMastForest>>,
988 Arc<SparseMastForest>,
989);
990
991fn split_trace_fragment_context<'a>(
1004 fragment_context: CoreTraceFragmentContext,
1005 writer: RowMajorTraceWriter<'a, Felt>,
1006 fragment_size: usize,
1007 mast_forest_store: &[Arc<SparseMastForest>],
1008 max_stack_depth: usize,
1009) -> Result<SplitFragmentContext<'a>, ExecutionError> {
1010 let CoreTraceFragmentContext {
1011 state: CoreTraceState { system, decoder, stack },
1012 replay:
1013 ExecutionReplay {
1014 block_stack: block_stack_replay,
1015 execution_context: execution_context_replay,
1016 stack_overflow: stack_overflow_replay,
1017 memory_reads: memory_reads_replay,
1018 advice: advice_replay,
1019 hasher: hasher_response_replay,
1020 block_address: block_address_replay,
1021 mast_forest_resolution: mast_forest_resolution_replay,
1022 },
1023 continuation,
1024 initial_mast_forest_id,
1025 } = fragment_context;
1026
1027 let translated_continuation =
1028 translate_snapshot_continuation_stack(continuation, mast_forest_store)?;
1029
1030 let initial_mast_forest =
1031 lookup_mast_forest(mast_forest_store, initial_mast_forest_id)?.clone();
1032
1033 let processor = ReplayProcessor::new(
1034 system,
1035 stack,
1036 stack_overflow_replay,
1037 execution_context_replay,
1038 advice_replay,
1039 memory_reads_replay,
1040 hasher_response_replay,
1041 mast_forest_resolution_replay,
1042 mast_forest_store.to_vec(),
1043 max_stack_depth,
1044 fragment_size.into(),
1045 );
1046 let tracer =
1047 CoreTraceGenerationTracer::new(writer, decoder, block_address_replay, block_stack_replay);
1048
1049 Ok((processor, tracer, translated_continuation, initial_mast_forest))
1050}
1051
1052fn translate_snapshot_continuation_stack(
1058 snapshot: ContinuationStack<MastForestId>,
1059 mast_forest_store: &[Arc<SparseMastForest>],
1060) -> Result<ContinuationStack<Arc<SparseMastForest>>, ExecutionError> {
1061 let mut out: ContinuationStack<Arc<SparseMastForest>> = ContinuationStack::default();
1062 for cont in snapshot.into_inner() {
1063 let translated = match cont {
1064 Continuation::EnterForest {
1065 forest: id,
1066 package_debug_info,
1067 inline_context_depth,
1068 } => Continuation::EnterForest {
1069 forest: lookup_mast_forest(mast_forest_store, id)?.clone(),
1070 package_debug_info,
1071 inline_context_depth,
1072 },
1073 Continuation::StartNode(id) => Continuation::StartNode(id),
1074 Continuation::FinishJoin(id) => Continuation::FinishJoin(id),
1075 Continuation::FinishSplit(id) => Continuation::FinishSplit(id),
1076 Continuation::FinishLoop(node_id) => Continuation::FinishLoop(node_id),
1077 Continuation::FinishCall(id) => Continuation::FinishCall(id),
1078 Continuation::FinishDyn(id) => Continuation::FinishDyn(id),
1079 Continuation::ResumeBasicBlock { node_id, batch_index, op_idx_in_batch } => {
1080 Continuation::ResumeBasicBlock { node_id, batch_index, op_idx_in_batch }
1081 },
1082 Continuation::Respan { node_id, batch_index } => {
1083 Continuation::Respan { node_id, batch_index }
1084 },
1085 Continuation::FinishBasicBlock(id) => Continuation::FinishBasicBlock(id),
1086 };
1087 out.push_continuation(translated);
1088 }
1089 Ok(out)
1090}
1091
1092pub(super) fn lookup_mast_forest(
1095 mast_forest_store: &[Arc<SparseMastForest>],
1096 id: MastForestId,
1097) -> Result<&Arc<SparseMastForest>, ExecutionError> {
1098 mast_forest_store
1099 .get(id.to_usize())
1100 .ok_or(ExecutionError::Internal("MastForestId out of range of mast_forest_store"))
1101}