1use alloc::{boxed::Box, sync::Arc, vec::Vec};
2use core::borrow::{Borrow, BorrowMut};
3
4use itertools::Itertools;
5use miden_air::{
6 CoreCols, Felt, StackCols, SystemCols,
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::TraceGenerationContext,
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, ExecutionTrace, TraceBuildInputs, TraceLenSummary,
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;
59
60pub(crate) mod core_trace_fragment;
61
62mod processor;
63mod tracer;
64
65#[cfg(test)]
66mod tests;
67
68#[instrument(name = "build_trace", skip_all)]
92pub fn build_trace(inputs: TraceBuildInputs) -> Result<ExecutionTrace, ExecutionError> {
93 build_trace_with_max_len(inputs, MAX_TRACE_LEN)
94}
95
96pub fn build_trace_with_max_len(
101 inputs: TraceBuildInputs,
102 max_trace_len: usize,
103) -> Result<ExecutionTrace, ExecutionError> {
104 build_trace_inner(inputs, None, max_trace_len)
105}
106
107#[cfg(feature = "std")]
111pub(crate) fn build_trace_with_prebuilt_hasher(
112 inputs: TraceBuildInputs,
113 prebuilt_hasher: Hasher,
114) -> Result<ExecutionTrace, ExecutionError> {
115 build_trace_inner(inputs, Some(prebuilt_hasher), MAX_TRACE_LEN)
116}
117
118fn build_trace_inner(
119 inputs: TraceBuildInputs,
120 prebuilt_hasher: Option<Hasher>,
121 max_trace_len: usize,
122) -> Result<ExecutionTrace, ExecutionError> {
123 let TraceBuildInputs {
124 trace_output,
125 trace_generation_context,
126 program_info,
127 } = inputs;
128
129 let TraceGenerationContext {
130 core_trace_contexts,
131 mast_forest_store,
132 range_checker_replay,
133 memory_writes,
134 bitwise_replay: bitwise,
135 kernel_replay,
136 hasher_for_chiplet,
137 ace_replay,
138 fragment_size,
139 max_stack_depth,
140 } = trace_generation_context;
141
142 let total_core_trace_rows = core_trace_contexts
149 .len()
150 .checked_mul(fragment_size)
151 .and_then(|n| n.checked_add(1))
152 .ok_or(ExecutionError::TraceLenExceeded(max_trace_len))?;
153 if total_core_trace_rows > max_trace_len {
154 return Err(ExecutionError::TraceLenExceeded(max_trace_len));
155 }
156
157 if core_trace_contexts.is_empty() {
158 return Err(ExecutionError::Internal(
159 "no trace fragments provided in the trace generation context",
160 ));
161 }
162
163 let chiplets = info_span!("initialize_chiplets").in_scope(|| {
164 initialize_chiplets(
165 program_info.kernel().clone(),
166 &core_trace_contexts,
167 memory_writes,
168 bitwise,
169 kernel_replay,
170 hasher_for_chiplet,
171 prebuilt_hasher,
172 ace_replay,
173 &mast_forest_store,
174 max_trace_len,
175 )
176 })?;
177
178 let range_checker = info_span!("initialize_range_checker")
179 .in_scope(|| initialize_range_checker(range_checker_replay, &chiplets));
180
181 let mut core_trace_data = info_span!("generate_core_trace").in_scope(|| {
182 generate_core_trace_row_major(
183 core_trace_contexts,
184 program_info.kernel().clone(),
185 fragment_size,
186 &mast_forest_store,
187 max_stack_depth,
188 )
189 })?;
190
191 let core_trace_len = core_trace_data.len() / CORE_STORAGE_WIDTH;
192
193 let range_table_len = range_checker.get_number_range_checker_rows();
195
196 let core_height = pad_to_trace_length(core_trace_len.max(range_table_len));
197 let chiplets_height = pad_to_trace_length(chiplets.trace_len());
198 let poseidon2_permutation_trace_len = chiplets.poseidon2_permutation_trace_len();
199 let poseidon2_permutation_height = pad_to_trace_length(poseidon2_permutation_trace_len);
200 let padded_trace_len = core_height.max(chiplets_height).max(poseidon2_permutation_height);
201
202 if padded_trace_len > max_trace_len {
205 return Err(ExecutionError::TraceLenExceeded(max_trace_len));
206 }
207
208 let trace_len_summary = TraceLenSummary::new_with_padded(
209 core_trace_len,
210 range_table_len,
211 ChipletsLengths::new(&chiplets),
212 poseidon2_permutation_trace_len,
213 padded_trace_len,
214 );
215
216 let ((chiplets_trace, poseidon2_permutation_trace), ()) = info_span!("chiplet_traces_core_pad")
218 .in_scope(|| {
219 rayon::join(
220 || chiplets.into_traces(chiplets_height, poseidon2_permutation_height),
221 || pad_core_row_major(&mut core_trace_data, core_height),
222 )
223 });
224
225 info_span!("write_range_checker_columns").in_scope(|| {
227 range_checker.write_range_into_core(
228 &mut core_trace_data,
229 CORE_STORAGE_WIDTH,
230 CORE_TRACE_WIDTH,
231 CORE_TRACE_WIDTH + 1,
232 range_table_len,
233 core_height,
234 )
235 });
236
237 let main_trace = {
239 let last_program_row = RowIndex::from((core_trace_len as u32).saturating_sub(1));
240 MainTrace::from_parts(
241 core_trace_data,
242 chiplets_trace.trace,
243 poseidon2_permutation_trace.trace,
244 last_program_row,
245 )
246 };
247
248 Ok(ExecutionTrace::new_from_parts(
249 program_info,
250 trace_output,
251 main_trace,
252 trace_len_summary,
253 ))
254}
255
256fn pad_to_trace_length(logical_len: usize) -> usize {
261 logical_len.next_power_of_two().max(MIN_TRACE_LEN)
262}
263
264fn generate_core_trace_row_major(
266 core_trace_contexts: Vec<CoreTraceFragmentContext>,
267 kernel: KernelDescriptor,
268 fragment_size: usize,
269 mast_forest_store: &[Arc<SparseMastForest>],
270 max_stack_depth: usize,
271) -> Result<Vec<Felt>, ExecutionError> {
272 let num_fragments = core_trace_contexts.len();
273 let total_allocated_rows = num_fragments * fragment_size;
274
275 let mut core_trace_data = Felt::zero_vec(total_allocated_rows * CORE_STORAGE_WIDTH);
276
277 let first_stack_top = if let Some(first_context) = core_trace_contexts.first() {
279 first_context.state.stack.stack_top.to_vec()
280 } else {
281 vec![ZERO; MIN_STACK_DEPTH]
282 };
283
284 let writers: Vec<RowMajorTraceWriter<'_, Felt>> = core_trace_data
285 .chunks_exact_mut(fragment_size * CORE_STORAGE_WIDTH)
286 .map(|chunk| {
287 RowMajorTraceWriter::with_stride(chunk, CORE_STORAGE_WIDTH, CORE_STORAGE_WIDTH)
288 })
289 .collect();
290
291 let fragment_results: Result<Vec<_>, ExecutionError> = core_trace_contexts
293 .into_par_iter()
294 .zip(writers.into_par_iter())
295 .map(|(trace_state, writer)| {
296 let (mut processor, mut tracer, mut continuation_stack, mut current_forest) =
297 split_trace_fragment_context(
298 trace_state,
299 writer,
300 fragment_size,
301 mast_forest_store,
302 max_stack_depth,
303 )?;
304
305 processor.execute(
306 &mut continuation_stack,
307 &mut current_forest,
308 &kernel,
309 &mut tracer,
310 )?;
311
312 tracer.into_final_state()
313 })
314 .collect();
315 let fragment_results = fragment_results?;
316
317 let mut stack_rows = Vec::new();
318 let mut system_rows = Vec::new();
319 let mut total_core_trace_rows = 0;
320
321 for final_state in fragment_results {
322 stack_rows.push(final_state.last_stack_cols);
323 system_rows.push(final_state.last_system_cols);
324 total_core_trace_rows += final_state.num_rows_written;
325 }
326
327 fixup_stack_and_system_rows(
329 &mut core_trace_data,
330 fragment_size,
331 &stack_rows,
332 &system_rows,
333 &first_stack_top,
334 );
335
336 {
340 let w = CORE_STORAGE_WIDTH;
341 core_trace_data[..total_core_trace_rows * w]
342 .par_chunks_mut(fragment_size * w)
343 .for_each(|fragment_chunk| {
344 let num_rows = fragment_chunk.len() / w;
345 let mut h0_vals: Vec<Felt> = (0..num_rows)
346 .map(|r| {
347 let row: &CoreCols<Felt> = fragment_chunk[r * w..(r + 1) * w].borrow();
348 row.stack.h0
349 })
350 .collect();
351 batch_inversion_allow_zeros(&mut h0_vals);
352 for (r, &val) in h0_vals.iter().enumerate() {
353 let row: &mut CoreCols<Felt> = fragment_chunk[r * w..(r + 1) * w].borrow_mut();
354 row.stack.h0 = val;
355 }
356 });
357 }
358
359 core_trace_data.truncate(total_core_trace_rows * CORE_STORAGE_WIDTH);
361
362 push_halt_opcode_row(
363 &mut core_trace_data,
364 total_core_trace_rows,
365 system_rows.last().ok_or(ExecutionError::Internal(
366 "no trace fragments provided in the trace generation context",
367 ))?,
368 stack_rows.last().ok_or(ExecutionError::Internal(
369 "no trace fragments provided in the trace generation context",
370 ))?,
371 );
372
373 Ok(core_trace_data)
374}
375
376fn fixup_stack_and_system_rows(
383 core_trace_data: &mut [Felt],
384 fragment_size: usize,
385 stack_rows: &[StackCols<Felt>],
386 system_rows: &[SystemCols<Felt>],
387 first_stack_top: &[Felt],
388) {
389 const MIN_STACK_DEPTH_FELT: Felt = Felt::new_unchecked(MIN_STACK_DEPTH as u64);
390 let w = CORE_STORAGE_WIDTH;
391
392 {
393 let row: &mut CoreCols<Felt> = core_trace_data[..w].borrow_mut();
394
395 for (stack_col_idx, &value) in first_stack_top.iter().rev().enumerate() {
397 row.stack.top[stack_col_idx] = value;
398 }
399
400 row.stack.b0 = MIN_STACK_DEPTH_FELT;
401 row.stack.b1 = ZERO;
402 row.stack.h0 = ZERO;
403 }
404
405 let total_rows = core_trace_data.len() / w;
406 let num_fragments = total_rows / fragment_size;
407
408 for frag_idx in 1..num_fragments {
409 let row_idx = frag_idx * fragment_size;
410 let row_start = row_idx * w;
411 let row: &mut CoreCols<Felt> = core_trace_data[row_start..row_start + w].borrow_mut();
412 row.system = system_rows[frag_idx - 1].clone();
413 row.stack = stack_rows[frag_idx - 1].clone();
414 }
415}
416
417fn push_halt_opcode_row(
422 core_trace_data: &mut Vec<Felt>,
423 num_rows_before: usize,
424 last_system_state: &SystemCols<Felt>,
425 last_stack_state: &StackCols<Felt>,
426) {
427 let w = CORE_STORAGE_WIDTH;
428 let mut row_data = [ZERO; CORE_STORAGE_WIDTH];
429
430 let prev_hasher_state_first_half: [Felt; 4] = if num_rows_before > 0 {
433 let last_row_start = (num_rows_before - 1) * w;
434 let prev: &CoreCols<Felt> = core_trace_data[last_row_start..last_row_start + w].borrow();
435 let hs = &prev.decoder.hasher_state;
436 [hs[0], hs[1], hs[2], hs[3]]
437 } else {
438 [ZERO; 4]
439 };
440
441 {
442 let row: &mut CoreCols<Felt> = row_data.as_mut_slice().borrow_mut();
443
444 row.system = last_system_state.clone();
445 row.stack = last_stack_state.clone();
446
447 let halt_opcode = opcodes::HALT;
449 for bit_idx in 0..NUM_OP_BITS {
450 row.decoder.op_bits[bit_idx] = Felt::from_u8((halt_opcode >> bit_idx) & 1);
451 }
452
453 row.decoder.hasher_state[..4].copy_from_slice(&prev_hasher_state_first_half);
457
458 row.decoder.extra[1] = ONE;
462 }
463
464 core_trace_data.extend_from_slice(&row_data);
465}
466
467fn initialize_range_checker(
473 range_checker_replay: RangeCheckerReplay,
474 chiplets: &Chiplets,
475) -> RangeChecker {
476 let mut range_checker = RangeChecker::new();
477
478 for values in range_checker_replay {
480 range_checker.add_range_checks(&values);
481 }
482
483 chiplets.append_range_checks(&mut range_checker);
485
486 range_checker
487}
488
489fn initialize_chiplets(
499 kernel: KernelDescriptor,
500 core_trace_contexts: &[CoreTraceFragmentContext],
501 memory_writes: MemoryWritesReplay,
502 bitwise: BitwiseReplay,
503 kernel_replay: KernelReplay,
504 hasher_for_chiplet: HasherRequestReplay,
505 prebuilt_hasher: Option<Hasher>,
506 ace_replay: AceReplay,
507 mast_forest_store: &[Arc<SparseMastForest>],
508 max_trace_len: usize,
509) -> Result<Chiplets, ExecutionError> {
510 let non_hasher_trace_len = non_hasher_trace_len(
511 &kernel,
512 core_trace_contexts,
513 &memory_writes,
514 &bitwise,
515 &ace_replay,
516 max_trace_len,
517 )?;
518 let max_hasher_trace_len = max_trace_len
519 .checked_sub(non_hasher_trace_len)
520 .ok_or(ExecutionError::TraceLenExceeded(max_trace_len))?;
521
522 if prebuilt_hasher
523 .as_ref()
524 .is_some_and(|hasher| hasher.trace_len() > max_hasher_trace_len)
525 {
526 return Err(ExecutionError::TraceLenExceeded(max_trace_len));
527 }
528
529 let (hasher, (bitwise, (memory, (ace, kernel_rom)))) = rayon::join(
530 || match prebuilt_hasher {
531 Some(hasher) => Ok(hasher),
532 None => build_hasher_chiplet(
533 hasher_for_chiplet.into_resolved_ops(mast_forest_store),
534 max_hasher_trace_len,
535 )
536 .map_err(|err| match err {
537 ExecutionError::TraceLenExceeded(_) => {
540 ExecutionError::TraceLenExceeded(max_trace_len)
541 },
542 other => other,
543 }),
544 },
545 || {
546 rayon::join(
547 || build_bitwise_chiplet(bitwise, max_trace_len),
548 || {
549 rayon::join(
550 || build_memory_chiplet(memory_writes, core_trace_contexts, max_trace_len),
551 || {
552 rayon::join(
553 || build_ace_chiplet(ace_replay, max_trace_len),
554 || build_kernel_rom_chiplet(kernel, kernel_replay, max_trace_len),
555 )
556 },
557 )
558 },
559 )
560 },
561 );
562
563 let chiplets = Chiplets {
564 hasher: hasher?,
565 bitwise: bitwise?,
566 memory: memory?,
567 ace: ace?,
568 kernel_rom: kernel_rom?,
569 };
570 debug_assert_eq!(
571 non_hasher_trace_len,
572 chiplets.trace_len() - chiplets.hasher.trace_len(),
573 "chiplet preflight length differs from the materialized trace",
574 );
575 if chiplets.trace_len() > max_trace_len {
578 return Err(ExecutionError::TraceLenExceeded(max_trace_len));
579 }
580 Ok(chiplets)
581}
582
583fn non_hasher_trace_len(
584 kernel: &KernelDescriptor,
585 core_trace_contexts: &[CoreTraceFragmentContext],
586 memory_writes: &MemoryWritesReplay,
587 bitwise: &BitwiseReplay,
588 ace: &AceReplay,
589 max_trace_len: usize,
590) -> Result<usize, ExecutionError> {
591 let overflow = || ExecutionError::TraceLenExceeded(max_trace_len);
592 let bitwise_len = bitwise.num_operations().checked_mul(OP_CYCLE_LEN).ok_or_else(overflow)?;
593 let memory_reads_len = core_trace_contexts.iter().try_fold(0usize, |len, context| {
594 len.checked_add(context.replay.memory_reads.num_accesses()?)
595 });
596 let memory_len = memory_writes
597 .num_accesses()
598 .and_then(|writes| memory_reads_len.and_then(|reads| writes.checked_add(reads)))
599 .ok_or_else(overflow)?;
600 let ace_len = ace.trace_len().ok_or_else(overflow)?;
601
602 [1, kernel.proc_hashes().len(), bitwise_len, memory_len, ace_len]
603 .into_iter()
604 .try_fold(0usize, usize::checked_add)
605 .filter(|&total| total <= max_trace_len)
606 .ok_or_else(overflow)
607}
608
609pub(crate) fn build_hasher_chiplet<'a>(
615 ops: impl IntoIterator<Item = Result<ResolvedHasherOp<'a>, ExecutionError>>,
616 max_trace_len: usize,
617) -> Result<Hasher, ExecutionError> {
618 let mut hasher = Hasher::default();
619 for hasher_op in ops {
620 match hasher_op? {
621 ResolvedHasherOp::Permute(input_state) => {
622 let _ = hasher.permute(input_state);
623 },
624 ResolvedHasherOp::HashControlBlock((h1, h2, domain, expected_hash)) => {
625 let _ = hasher.hash_control_block(h1, h2, domain, expected_hash);
626 },
627 ResolvedHasherOp::HashBasicBlock((batch_groups, expected_hash)) => match batch_groups {
628 ResolvedBasicBlockGroups::Borrowed(op_batches) => {
629 let _ = hasher
630 .hash_basic_block(op_batches.iter().map(OpBatch::groups), expected_hash);
631 },
632 ResolvedBasicBlockGroups::Owned(batch_groups) => {
633 let _ = hasher.hash_basic_block(batch_groups.iter(), expected_hash);
634 },
635 },
636 ResolvedHasherOp::BuildMerkleRoot((value, path, index)) => {
637 let _ = hasher.build_merkle_root(value, &path, index);
638 },
639 ResolvedHasherOp::UpdateMerkleRoot((old_value, new_value, path, index)) => {
640 hasher.update_merkle_root(old_value, new_value, &path, index);
641 },
642 }
643 if hasher.trace_len() > max_trace_len {
644 return Err(ExecutionError::TraceLenExceeded(max_trace_len));
645 }
646 }
647 Ok(hasher)
648}
649
650fn build_bitwise_chiplet(
652 bitwise_replay: BitwiseReplay,
653 max_trace_len: usize,
654) -> Result<Bitwise, ExecutionError> {
655 let mut bitwise = Bitwise::default();
656 for (bitwise_op, a, b) in bitwise_replay {
657 match bitwise_op {
658 BitwiseOp::U32And => {
659 bitwise.u32and(a, b).map_exec_err_no_ctx()?;
660 },
661 BitwiseOp::U32Xor => {
662 bitwise.u32xor(a, b).map_exec_err_no_ctx()?;
663 },
664 }
665 if bitwise.trace_len() > max_trace_len {
666 return Err(ExecutionError::TraceLenExceeded(max_trace_len));
667 }
668 }
669 Ok(bitwise)
670}
671
672fn build_memory_chiplet(
674 memory_writes: MemoryWritesReplay,
675 core_trace_contexts: &[CoreTraceFragmentContext],
676 max_trace_len: usize,
677) -> Result<Memory, ExecutionError> {
678 enum MemoryAccess {
679 ReadElement(Felt, ContextId, RowIndex),
680 WriteElement(Felt, Felt, ContextId, RowIndex),
681 ReadWord(Felt, ContextId, RowIndex),
682 WriteWord(Felt, Word, ContextId, RowIndex),
683 }
684
685 impl MemoryAccess {
686 fn clk(&self) -> RowIndex {
687 match self {
688 MemoryAccess::ReadElement(_, _, clk) => *clk,
689 MemoryAccess::WriteElement(_, _, _, clk) => *clk,
690 MemoryAccess::ReadWord(_, _, clk) => *clk,
691 MemoryAccess::WriteWord(_, _, _, clk) => *clk,
692 }
693 }
694 }
695
696 let mut memory = Memory::default();
697
698 let elements_written: Box<dyn Iterator<Item = MemoryAccess>> =
702 Box::new(memory_writes.iter_elements_written().map(|(element, addr, ctx, clk)| {
703 MemoryAccess::WriteElement(*addr, *element, *ctx, *clk)
704 }));
705 let words_written: Box<dyn Iterator<Item = MemoryAccess>> = Box::new(
706 memory_writes
707 .iter_words_written()
708 .map(|(word, addr, ctx, clk)| MemoryAccess::WriteWord(*addr, *word, *ctx, *clk)),
709 );
710 let elements_read: Box<dyn Iterator<Item = MemoryAccess>> =
711 Box::new(core_trace_contexts.iter().flat_map(|ctx| {
712 ctx.replay
713 .memory_reads
714 .iter_read_elements()
715 .map(|(_, addr, ctx, clk)| MemoryAccess::ReadElement(addr, ctx, clk))
716 }));
717 let words_read: Box<dyn Iterator<Item = MemoryAccess>> =
718 Box::new(core_trace_contexts.iter().flat_map(|ctx| {
719 ctx.replay
720 .memory_reads
721 .iter_read_words()
722 .map(|(_, addr, ctx, clk)| MemoryAccess::ReadWord(addr, ctx, clk))
723 }));
724
725 [elements_written, words_written, elements_read, words_read]
726 .into_iter()
727 .kmerge_by(|a, b| a.clk() < b.clk())
728 .try_for_each(|mem_access| {
729 match mem_access {
730 MemoryAccess::ReadElement(addr, ctx, clk) => memory
731 .read(ctx, addr, clk)
732 .map(|_| ())
733 .map_err(ExecutionError::MemoryErrorNoCtx)?,
734 MemoryAccess::WriteElement(addr, element, ctx, clk) => memory
735 .write(ctx, addr, clk, element)
736 .map_err(ExecutionError::MemoryErrorNoCtx)?,
737 MemoryAccess::ReadWord(addr, ctx, clk) => memory
738 .read_word(ctx, addr, clk)
739 .map(|_| ())
740 .map_err(ExecutionError::MemoryErrorNoCtx)?,
741 MemoryAccess::WriteWord(addr, word, ctx, clk) => memory
742 .write_word(ctx, addr, clk, word)
743 .map_err(ExecutionError::MemoryErrorNoCtx)?,
744 }
745 if memory.trace_len() > max_trace_len {
746 return Err(ExecutionError::TraceLenExceeded(max_trace_len));
747 }
748 Ok(())
749 })?;
750
751 Ok(memory)
752}
753
754fn build_ace_chiplet(ace_replay: AceReplay, max_trace_len: usize) -> Result<Ace, ExecutionError> {
756 let mut ace = Ace::default();
757 for (clk, circuit_eval) in ace_replay.into_iter() {
758 ace.add_circuit_evaluation(clk, circuit_eval);
759 if ace.trace_len() > max_trace_len {
760 return Err(ExecutionError::TraceLenExceeded(max_trace_len));
761 }
762 }
763 Ok(ace)
764}
765
766fn build_kernel_rom_chiplet(
768 kernel: KernelDescriptor,
769 kernel_replay: KernelReplay,
770 max_trace_len: usize,
771) -> Result<KernelRom, ExecutionError> {
772 let mut kernel_rom = KernelRom::new(kernel);
773 for proc_hash in kernel_replay.into_iter() {
774 kernel_rom.access_proc(proc_hash).map_exec_err_no_ctx()?;
775 if kernel_rom.trace_len() > max_trace_len {
776 return Err(ExecutionError::TraceLenExceeded(max_trace_len));
777 }
778 }
779 Ok(kernel_rom)
780}
781
782fn pad_core_row_major(core_trace_data: &mut Vec<Felt>, core_height: usize) {
784 let w = CORE_STORAGE_WIDTH;
785 let total_program_rows = core_trace_data.len() / w;
786 assert!(total_program_rows <= core_height);
787 assert!(total_program_rows > 0);
788
789 let num_padding_rows = core_height - total_program_rows;
790 if num_padding_rows == 0 {
791 return;
792 }
793 let last_row_start = (total_program_rows - 1) * w;
794
795 let (last_hasher_first_half, last_stack): ([Felt; 4], StackCols<Felt>) = {
798 let last: &CoreCols<Felt> = core_trace_data[last_row_start..last_row_start + w].borrow();
799 let hs = &last.decoder.hasher_state;
800 let last_hasher: [Felt; 4] = [hs[0], hs[1], hs[2], hs[3]];
801 (last_hasher, last.stack.clone())
802 };
803
804 let mut template_data = [ZERO; CORE_STORAGE_WIDTH];
805 {
806 let template: &mut CoreCols<Felt> = template_data.as_mut_slice().borrow_mut();
807
808 let halt_opcode = opcodes::HALT;
813 for i in 0..NUM_OP_BITS {
814 template.decoder.op_bits[i] = Felt::from_u8((halt_opcode >> i) & 1);
815 }
816 template.decoder.hasher_state[..4].copy_from_slice(&last_hasher_first_half);
820
821 template.decoder.extra[1] = ONE;
825
826 template.stack = last_stack;
831 }
832
833 let pad_start = total_program_rows * w;
839 core_trace_data.resize(pad_start + num_padding_rows * w, ZERO);
840 core_trace_data[pad_start..]
841 .par_chunks_mut(w)
842 .enumerate()
843 .for_each(|(idx, row_buf)| {
844 row_buf.copy_from_slice(&template_data);
845 let row: &mut CoreCols<Felt> = row_buf.borrow_mut();
846 row.system.clk = Felt::from_u32((total_program_rows + idx) as u32);
847 });
848}
849
850type SplitFragmentContext<'a> = (
851 ReplayProcessor,
852 CoreTraceGenerationTracer<'a>,
853 ContinuationStack<Arc<SparseMastForest>>,
854 Arc<SparseMastForest>,
855);
856
857fn split_trace_fragment_context<'a>(
870 fragment_context: CoreTraceFragmentContext,
871 writer: RowMajorTraceWriter<'a, Felt>,
872 fragment_size: usize,
873 mast_forest_store: &[Arc<SparseMastForest>],
874 max_stack_depth: usize,
875) -> Result<SplitFragmentContext<'a>, ExecutionError> {
876 let CoreTraceFragmentContext {
877 state: CoreTraceState { system, decoder, stack },
878 replay:
879 ExecutionReplay {
880 block_stack: block_stack_replay,
881 execution_context: execution_context_replay,
882 stack_overflow: stack_overflow_replay,
883 memory_reads: memory_reads_replay,
884 advice: advice_replay,
885 hasher: hasher_response_replay,
886 block_address: block_address_replay,
887 mast_forest_resolution: mast_forest_resolution_replay,
888 },
889 continuation,
890 initial_mast_forest_id,
891 } = fragment_context;
892
893 let translated_continuation =
894 translate_snapshot_continuation_stack(continuation, mast_forest_store)?;
895
896 let initial_mast_forest =
897 lookup_mast_forest(mast_forest_store, initial_mast_forest_id)?.clone();
898
899 let processor = ReplayProcessor::new(
900 system,
901 stack,
902 stack_overflow_replay,
903 execution_context_replay,
904 advice_replay,
905 memory_reads_replay,
906 hasher_response_replay,
907 mast_forest_resolution_replay,
908 mast_forest_store.to_vec(),
909 max_stack_depth,
910 fragment_size.into(),
911 );
912 let tracer =
913 CoreTraceGenerationTracer::new(writer, decoder, block_address_replay, block_stack_replay);
914
915 Ok((processor, tracer, translated_continuation, initial_mast_forest))
916}
917
918fn translate_snapshot_continuation_stack(
924 snapshot: ContinuationStack<MastForestId>,
925 mast_forest_store: &[Arc<SparseMastForest>],
926) -> Result<ContinuationStack<Arc<SparseMastForest>>, ExecutionError> {
927 let mut out: ContinuationStack<Arc<SparseMastForest>> = ContinuationStack::default();
928 for cont in snapshot.into_inner() {
929 let translated = match cont {
930 Continuation::EnterForest { forest: id, package_debug_info } => {
931 Continuation::EnterForest {
932 forest: lookup_mast_forest(mast_forest_store, id)?.clone(),
933 package_debug_info,
934 }
935 },
936 Continuation::StartNode(id) => Continuation::StartNode(id),
937 Continuation::FinishJoin(id) => Continuation::FinishJoin(id),
938 Continuation::FinishSplit(id) => Continuation::FinishSplit(id),
939 Continuation::FinishLoop(node_id) => Continuation::FinishLoop(node_id),
940 Continuation::FinishCall(id) => Continuation::FinishCall(id),
941 Continuation::FinishDyn(id) => Continuation::FinishDyn(id),
942 Continuation::ResumeBasicBlock { node_id, batch_index, op_idx_in_batch } => {
943 Continuation::ResumeBasicBlock { node_id, batch_index, op_idx_in_batch }
944 },
945 Continuation::Respan { node_id, batch_index } => {
946 Continuation::Respan { node_id, batch_index }
947 },
948 Continuation::FinishBasicBlock(id) => Continuation::FinishBasicBlock(id),
949 };
950 out.push_continuation(translated);
951 }
952 Ok(out)
953}
954
955pub(super) fn lookup_mast_forest(
958 mast_forest_store: &[Arc<SparseMastForest>],
959 id: MastForestId,
960) -> Result<&Arc<SparseMastForest>, ExecutionError> {
961 mast_forest_store
962 .get(id.to_usize())
963 .ok_or(ExecutionError::Internal("MastForestId out of range of mast_forest_store"))
964}