1pub mod analyzed;
2mod block;
3pub mod instruction;
4mod memory;
5mod opcode;
6mod program;
7mod public_values;
8mod record;
9pub mod shape;
10
11pub use analyzed::AnalyzedInstruction;
12pub use public_values::PV_DIGEST_NUM_WORDS;
13
14use backtrace::Backtrace as Trace;
16pub use block::Block;
17use cfg_if::cfg_if;
18pub use instruction::Instruction;
19use instruction::{
20 FieldEltType, HintAddCurveInstr, HintBitsInstr, HintExt2FeltsInstr, HintInstr, PrintInstr,
21};
22use itertools::Itertools;
23use memory::*;
24pub use opcode::*;
25pub use program::*;
26pub use public_values::{RecursionPublicValues, NUM_PV_ELMS_TO_HASH, RECURSIVE_PROOF_NUM_PV_ELTS};
27pub use record::*;
28use serde::{Deserialize, Serialize};
29use slop_algebra::{
30 AbstractExtensionField, AbstractField, ExtensionField, PrimeField32, PrimeField64,
31};
32use slop_maybe_rayon::prelude::*;
33use slop_poseidon2::{Poseidon2, Poseidon2ExternalMatrixGeneral};
34use slop_symmetric::{CryptographicPermutation, Permutation};
35use sp1_derive::AlignedBorrow;
36use sp1_hypercube::{
37 operations::poseidon2::air::{external_linear_layer_mut, internal_linear_layer_mut},
38 septic_curve::SepticCurve,
39 septic_extension::SepticExtension,
40 MachineRecord,
41};
42use std::{
43 array,
44 borrow::Borrow,
45 cell::UnsafeCell,
46 collections::VecDeque,
47 fmt::Debug,
48 io::{stdout, Write},
49 iter::zip,
50 marker::PhantomData,
51 sync::{Arc, Mutex},
52};
53use thiserror::Error;
54use tracing::debug_span;
55
56pub const PERMUTATION_WIDTH: usize = 16;
58pub const POSEIDON2_SBOX_DEGREE: u64 = 3;
59pub const HASH_RATE: usize = 8;
60
61pub const DIGEST_SIZE: usize = 8;
64
65pub const NUM_BITS: usize = 31;
66
67pub const D: usize = 4;
68
69#[derive(
70 AlignedBorrow, Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Default,
71)]
72#[repr(transparent)]
73pub struct Address<F>(pub F);
74
75impl<F: PrimeField64> Address<F> {
76 #[inline]
77 pub fn as_usize(&self) -> usize {
78 self.0.as_canonical_u64() as usize
79 }
80}
81
82#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
86#[repr(C)]
87pub struct BaseAluIo<V> {
88 pub out: V,
89 pub in1: V,
90 pub in2: V,
91}
92
93pub type BaseAluEvent<F> = BaseAluIo<F>;
94
95#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
97#[repr(C)]
98pub struct BaseAluInstr<F> {
99 pub opcode: BaseAluOpcode,
100 pub mult: F,
101 pub addrs: BaseAluIo<Address<F>>,
102}
103
104#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
108#[repr(C)]
109pub struct ExtAluIo<V> {
110 pub out: V,
111 pub in1: V,
112 pub in2: V,
113}
114
115pub type ExtAluEvent<F> = ExtAluIo<Block<F>>;
116
117#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
119#[repr(C)]
120pub struct ExtAluInstr<F> {
121 pub opcode: ExtAluOpcode,
122 pub mult: F,
123 pub addrs: ExtAluIo<Address<F>>,
124}
125
126#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
130#[repr(C)]
131pub struct MemIo<V> {
132 pub inner: V,
133}
134
135#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
136pub struct MemInstr<F> {
137 pub addrs: MemIo<Address<F>>,
138 pub vals: MemIo<Block<F>>,
139 pub mult: F,
140 pub kind: MemAccessKind,
141}
142
143pub type MemEvent<F> = MemIo<Block<F>>;
144
145#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
148pub enum MemAccessKind {
149 Read,
150 Write,
151}
152
153#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
155#[repr(C)]
156pub struct Poseidon2Io<V> {
157 pub input: [V; PERMUTATION_WIDTH],
158 pub output: [V; PERMUTATION_WIDTH],
159}
160
161#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
163#[repr(C)]
164pub struct Poseidon2Instr<F> {
165 pub addrs: Poseidon2Io<Address<F>>,
166 pub mults: [F; PERMUTATION_WIDTH],
167}
168
169pub type Poseidon2Event<F> = Poseidon2Io<F>;
170
171#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
174#[repr(C)]
175pub struct Poseidon2LinearLayerIo<V> {
176 pub input: [V; 4],
177 pub output: [V; 4],
178}
179
180#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
182#[repr(C)]
183pub struct Poseidon2LinearLayerInstr<F> {
184 pub addrs: Poseidon2LinearLayerIo<Address<F>>,
185 pub mults: [F; 4],
186 pub external: bool,
187}
188
189pub type Poseidon2LinearLayerEvent<F> = Poseidon2LinearLayerIo<Block<F>>;
190
191#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
193#[repr(C)]
194pub struct Poseidon2SBoxIo<V> {
195 pub input: V,
196 pub output: V,
197}
198
199#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
201#[repr(C)]
202pub struct Poseidon2SBoxInstr<F> {
203 pub addrs: Poseidon2SBoxIo<Address<F>>,
204 pub mults: F,
205 pub external: bool,
206}
207
208pub type Poseidon2SBoxEvent<F> = Poseidon2SBoxIo<Block<F>>;
209
210#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
213#[repr(C)]
214pub struct ExtFeltInstr<F> {
215 pub addrs: [Address<F>; 5],
216 pub mults: [F; 5],
217 pub ext2felt: bool,
218}
219
220#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
222#[repr(C)]
223pub struct ExtFeltEvent<F> {
224 pub input: Block<F>,
225}
226
227#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
229#[repr(C)]
230pub struct SelectIo<V> {
231 pub bit: V,
232 pub out1: V,
233 pub out2: V,
234 pub in1: V,
235 pub in2: V,
236}
237
238#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
240#[repr(C)]
241pub struct SelectInstr<F> {
242 pub addrs: SelectIo<Address<F>>,
243 pub mult1: F,
244 pub mult2: F,
245}
246
247pub type SelectEvent<F> = SelectIo<F>;
249
250#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
252pub struct PrefixSumChecksIo<V> {
253 pub zero: V,
254 pub one: V,
255 pub x1: Vec<V>,
256 pub x2: Vec<V>,
257 pub accs: Vec<V>,
258 pub field_accs: Vec<V>,
259}
260
261#[derive(Clone, Debug, Serialize, Deserialize)]
263pub struct PrefixSumChecksInstr<F> {
264 pub addrs: PrefixSumChecksIo<Address<F>>,
265 pub acc_mults: Vec<F>,
266 pub field_acc_mults: Vec<F>,
267}
268
269#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
271#[repr(C)]
272pub struct PrefixSumChecksEvent<F> {
273 pub x1: F,
274 pub x2: Block<F>,
275 pub zero: F,
276 pub one: Block<F>,
277 pub acc: Block<F>,
278 pub new_acc: Block<F>,
279 pub field_acc: F,
280 pub new_field_acc: F,
281}
282
283#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
286#[repr(C)]
287pub struct CommitPublicValuesInstr<F> {
288 pub pv_addrs: RecursionPublicValues<Address<F>>,
289}
290
291#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
293#[repr(C)]
294pub struct CommitPublicValuesEvent<F> {
295 pub public_values: RecursionPublicValues<F>,
296}
297
298type Perm<F, Diffusion> = Poseidon2<
299 F,
300 Poseidon2ExternalMatrixGeneral,
301 Diffusion,
302 PERMUTATION_WIDTH,
303 POSEIDON2_SBOX_DEGREE,
304>;
305
306pub struct Executor<'a, F: PrimeField32, EF: ExtensionField<F>, Diffusion> {
310 pub program: Arc<RecursionProgram<F>>,
312
313 pub memory: MemVec<F>,
315
316 pub record: ExecutionRecord<F>,
318
319 pub witness_stream: VecDeque<Block<F>>,
320
321 pub debug_stdout: Box<dyn Write + Send + 'a>,
323
324 perm: Option<Perm<F, Diffusion>>,
326
327 _marker_ef: PhantomData<EF>,
328
329 _marker_diffusion: PhantomData<Diffusion>,
330}
331
332#[derive(Error, Debug)]
333pub enum RuntimeError<F: Debug, EF: Debug> {
334 #[error(
335 "attempted to perform base field division {in1:?}/{in2:?}\n\
336 \tin instruction {instr:#?}\n\
337 \tnearest backtrace:\n{trace:#?}"
338 )]
339 DivFOutOfDomain { in1: F, in2: F, instr: BaseAluInstr<F>, trace: Option<Trace> },
340 #[error(
341 "attempted to perform extension field division {in1:?}/{in2:?}\n\
342 \tin instruction {instr:#?}\n\
343 \tnearest backtrace:\n{trace:#?}"
344 )]
345 DivEOutOfDomain { in1: EF, in2: EF, instr: ExtAluInstr<F>, trace: Option<Trace> },
346 #[error("failed to print to `debug_stdout`: {0}")]
347 DebugPrint(#[from] std::io::Error),
348 #[error("attempted to read from empty witness stream")]
349 EmptyWitnessStream,
350}
351
352impl<F: PrimeField32, EF: ExtensionField<F>, Diffusion> Executor<'_, F, EF, Diffusion>
353where
354 Poseidon2<
355 F,
356 Poseidon2ExternalMatrixGeneral,
357 Diffusion,
358 PERMUTATION_WIDTH,
359 POSEIDON2_SBOX_DEGREE,
360 >: CryptographicPermutation<[F; PERMUTATION_WIDTH]>,
361{
362 pub fn new(
363 program: Arc<RecursionProgram<F>>,
364 perm: Poseidon2<
365 F,
366 Poseidon2ExternalMatrixGeneral,
367 Diffusion,
368 PERMUTATION_WIDTH,
369 POSEIDON2_SBOX_DEGREE,
370 >,
371 ) -> Self {
372 let record = ExecutionRecord::<F> { program: program.clone(), ..Default::default() };
373 let memory = MemVec::with_capacity(program.total_memory);
374 Self {
375 program,
376 memory,
377 record,
378 witness_stream: VecDeque::new(),
379 debug_stdout: Box::new(stdout()),
380 perm: Some(perm),
381 _marker_ef: PhantomData,
382 _marker_diffusion: PhantomData,
383 }
384 }
385
386 pub fn print_stats(&self) {
387 if tracing::event_enabled!(tracing::Level::DEBUG) {
388 let mut stats = self.record.stats().into_iter().collect::<Vec<_>>();
389 stats.sort_unstable();
390 tracing::debug!("total events: {}", stats.iter().map(|(_, v)| *v).sum::<usize>());
391 for (k, v) in stats {
392 tracing::debug!(" {k}: {v}");
393 }
394 }
395 }
396
397 unsafe fn execute_one(
408 state: &mut ExecState<F, Diffusion>,
409 record: &UnsafeRecord<F>,
410 witness_stream: Option<&mut VecDeque<Block<F>>>,
411 analyzed_instruction: &AnalyzedInstruction<F>,
412 ) -> Result<(), RuntimeError<F, EF>> {
413 let ExecEnv { memory, perm, debug_stdout } = state.env;
414 let instruction = &analyzed_instruction.inner;
415 let offset = analyzed_instruction.offset;
416
417 match *instruction {
418 Instruction::BaseAlu(ref instr @ BaseAluInstr { opcode, mult: _, addrs }) => {
419 let in1 = memory.mr_unchecked(addrs.in1).val[0];
420 let in2 = memory.mr_unchecked(addrs.in2).val[0];
421 let out = match opcode {
423 BaseAluOpcode::AddF => in1 + in2,
424 BaseAluOpcode::SubF => in1 - in2,
425 BaseAluOpcode::MulF => in1 * in2,
426 BaseAluOpcode::DivF => match in1.try_div(in2) {
427 Some(x) => x,
428 None => {
429 if in1.is_zero() {
432 AbstractField::one()
433 } else {
434 return Err(RuntimeError::DivFOutOfDomain {
435 in1,
436 in2,
437 instr: *instr,
438 trace: state.resolve_trace().cloned(),
439 });
440 }
441 }
442 },
443 };
444 memory.mw_unchecked(addrs.out, Block::from(out));
445 UnsafeCell::raw_get(record.base_alu_events[offset].as_ptr()).write(BaseAluEvent {
447 out,
448 in1,
449 in2,
450 });
451 }
452 Instruction::ExtAlu(ref instr @ ExtAluInstr { opcode, mult: _, addrs }) => {
453 let in1 = memory.mr_unchecked(addrs.in1).val;
454 let in2 = memory.mr_unchecked(addrs.in2).val;
455 let in1_ef = EF::from_base_fn(|i| in1.0[i]);
457 let in2_ef = EF::from_base_fn(|i| in2.0[i]);
458 let out_ef = match opcode {
459 ExtAluOpcode::AddE => in1_ef + in2_ef,
460 ExtAluOpcode::SubE => in1_ef - in2_ef,
461 ExtAluOpcode::MulE => in1_ef * in2_ef,
462 ExtAluOpcode::DivE => match in1_ef.try_div(in2_ef) {
463 Some(x) => x,
464 None => {
465 if in1_ef.is_zero() {
468 AbstractField::one()
469 } else {
470 return Err(RuntimeError::DivEOutOfDomain {
471 in1: in1_ef,
472 in2: in2_ef,
473 instr: *instr,
474 trace: state.resolve_trace().cloned(),
475 });
476 }
477 }
478 },
479 };
480 let out = Block::from(out_ef.as_base_slice());
481 memory.mw_unchecked(addrs.out, out);
482
483 UnsafeCell::raw_get(record.ext_alu_events[offset].as_ptr()).write(ExtAluEvent {
485 out,
486 in1,
487 in2,
488 });
489 }
490 Instruction::Mem(MemInstr {
491 addrs: MemIo { inner: addr },
492 vals: MemIo { inner: val },
493 mult: _,
494 kind,
495 }) => match kind {
496 MemAccessKind::Read => {
497 let mem_entry = memory.mr_unchecked(addr);
498 assert_eq!(
499 mem_entry.val, val,
500 "stored memory value should be the specified value"
501 );
502 }
503 MemAccessKind::Write => memory.mw_unchecked(addr, val),
504 },
505 Instruction::ExtFelt(ExtFeltInstr { addrs, mults: _, ext2felt }) => {
506 if ext2felt {
507 let in_val = memory.mr_unchecked(addrs[0]).val;
508 for (addr, value) in addrs[1..].iter().zip_eq(in_val.0) {
509 memory.mw_unchecked(*addr, Block::from(value));
510 }
511 UnsafeCell::raw_get(record.ext_felt_conversion_events[offset].as_ptr())
513 .write(ExtFeltEvent { input: in_val });
514 } else {
515 let in_val = Block([
516 memory.mr_unchecked(addrs[1]).val.0[0],
517 memory.mr_unchecked(addrs[2]).val.0[0],
518 memory.mr_unchecked(addrs[3]).val.0[0],
519 memory.mr_unchecked(addrs[4]).val.0[0],
520 ]);
521 memory.mw_unchecked(addrs[0], in_val);
522 UnsafeCell::raw_get(record.ext_felt_conversion_events[offset].as_ptr())
524 .write(ExtFeltEvent { input: in_val });
525 }
526 }
527 Instruction::Poseidon2(ref instr) => {
528 let Poseidon2Instr { addrs: Poseidon2Io { input, output }, mults: _ } =
529 instr.as_ref();
530 let in_vals = std::array::from_fn(|i| memory.mr_unchecked(input[i]).val[0]);
531 let perm_output = perm.permute(in_vals);
532
533 perm_output.iter().zip(output).for_each(|(&val, addr)| {
534 memory.mw_unchecked(*addr, Block::from(val));
535 });
536
537 UnsafeCell::raw_get(record.poseidon2_events[offset].as_ptr())
539 .write(Poseidon2Event { input: in_vals, output: perm_output });
540 }
541 Instruction::Poseidon2LinearLayer(ref instr) => {
542 let Poseidon2LinearLayerInstr {
543 addrs: Poseidon2LinearLayerIo { input, output },
544 mults: _,
545 external,
546 } = instr.as_ref();
547 let mut state = [F::zero(); PERMUTATION_WIDTH];
548 let mut io_input = [Block::from(F::zero()); PERMUTATION_WIDTH / D];
549 let mut io_output = [Block::from(F::zero()); PERMUTATION_WIDTH / D];
550 for i in 0..PERMUTATION_WIDTH / D {
551 io_input[i] = memory.mr_unchecked(input[i]).val;
552 for j in 0..D {
553 state[i * D + j] = io_input[i].0[j];
554 }
555 }
556 if *external {
557 external_linear_layer_mut(&mut state);
558 } else {
559 internal_linear_layer_mut(&mut state);
560 }
561 for i in 0..PERMUTATION_WIDTH / D {
562 io_output[i] = Block(state[i * D..i * D + D].try_into().unwrap());
563 memory.mw_unchecked(output[i], io_output[i]);
564 }
565
566 UnsafeCell::raw_get(record.poseidon2_linear_layer_events[offset].as_ptr())
568 .write(Poseidon2LinearLayerEvent { input: io_input, output: io_output });
569 }
570 Instruction::Poseidon2SBox(Poseidon2SBoxInstr {
571 addrs: Poseidon2SBoxIo { input, output },
572 mults: _,
573 external,
574 }) => {
575 let io_input = memory.mr_unchecked(input).val;
576 let pow7 = |x: F| -> F { x * x * x };
577
578 let io_output = if external {
579 Block([
580 pow7(io_input.0[0]),
581 pow7(io_input.0[1]),
582 pow7(io_input.0[2]),
583 pow7(io_input.0[3]),
584 ])
585 } else {
586 Block([pow7(io_input.0[0]), io_input.0[1], io_input.0[2], io_input.0[3]])
587 };
588 memory.mw_unchecked(output, io_output);
589
590 UnsafeCell::raw_get(record.poseidon2_sbox_events[offset].as_ptr())
592 .write(Poseidon2SBoxEvent { input: io_input, output: io_output });
593 }
594 Instruction::Select(SelectInstr {
595 addrs: SelectIo { bit, out1, out2, in1, in2 },
596 mult1: _,
597 mult2: _,
598 }) => {
599 let bit = memory.mr_unchecked(bit).val[0];
600 let in1 = memory.mr_unchecked(in1).val[0];
601 let in2 = memory.mr_unchecked(in2).val[0];
602 let out1_val = bit * in2 + (F::one() - bit) * in1;
603 let out2_val = bit * in1 + (F::one() - bit) * in2;
604 memory.mw_unchecked(out1, Block::from(out1_val));
605 memory.mw_unchecked(out2, Block::from(out2_val));
606
607 UnsafeCell::raw_get(record.select_events[offset].as_ptr()).write(SelectEvent {
609 bit,
610 out1: out1_val,
611 out2: out2_val,
612 in1,
613 in2,
614 });
615 }
616 Instruction::HintBits(HintBitsInstr { ref output_addrs_mults, input_addr }) => {
617 let num = memory.mr_unchecked(input_addr).val[0].as_canonical_u32();
618 let bits = (0..output_addrs_mults.len())
620 .map(|i| Block::from(F::from_canonical_u32((num >> i) & 1)))
621 .collect::<Vec<_>>();
622
623 for (i, (bit, &(addr, _mult))) in
625 bits.into_iter().zip(output_addrs_mults).enumerate()
626 {
627 memory.mw_unchecked(addr, bit);
628
629 UnsafeCell::raw_get(record.mem_var_events[offset + i].as_ptr())
631 .write(MemEvent { inner: bit });
632 }
633 }
634 Instruction::HintAddCurve(ref instr) => {
635 let HintAddCurveInstr {
636 output_x_addrs_mults,
637 output_y_addrs_mults,
638 input1_x_addrs,
639 input1_y_addrs,
640 input2_x_addrs,
641 input2_y_addrs,
642 } = instr.as_ref();
643 let input1_x = SepticExtension::<F>::from_base_fn(|i| {
644 memory.mr_unchecked(input1_x_addrs[i]).val[0]
645 });
646 let input1_y = SepticExtension::<F>::from_base_fn(|i| {
647 memory.mr_unchecked(input1_y_addrs[i]).val[0]
648 });
649 let input2_x = SepticExtension::<F>::from_base_fn(|i| {
650 memory.mr_unchecked(input2_x_addrs[i]).val[0]
651 });
652 let input2_y = SepticExtension::<F>::from_base_fn(|i| {
653 memory.mr_unchecked(input2_y_addrs[i]).val[0]
654 });
655 let point1 = SepticCurve { x: input1_x, y: input1_y };
656 let point2 = SepticCurve { x: input2_x, y: input2_y };
657 let output = point1.add_incomplete(point2);
658
659 for (i, (val, &(addr, _mult))) in output
660 .x
661 .0
662 .into_iter()
663 .zip(output_x_addrs_mults.iter())
664 .chain(output.y.0.into_iter().zip(output_y_addrs_mults.iter()))
665 .enumerate()
666 {
667 memory.mw_unchecked(addr, Block::from(val));
668
669 UnsafeCell::raw_get(record.mem_var_events[offset + i].as_ptr())
670 .write(MemEvent { inner: Block::from(val) });
671 }
672 }
673 Instruction::PrefixSumChecks(ref instr) => {
674 let PrefixSumChecksInstr {
675 addrs: PrefixSumChecksIo { zero, one, x1, x2, accs, field_accs },
676 acc_mults: _,
677 field_acc_mults: _,
678 } = instr.as_ref();
679 let zero = memory.mr_unchecked(*zero).val[0];
680 let one = memory.mr_unchecked(*one).val.ext::<EF>();
681 let x1_f = x1.iter().map(|addr| memory.mr_unchecked(*addr).val[0]).collect_vec();
682 let x2_ef =
683 x2.iter().map(|addr| memory.mr_unchecked(*addr).val.ext::<EF>()).collect_vec();
684
685 let mut acc = EF::one();
686 let mut field_acc = F::zero();
687 for m in 0..x1_f.len() {
688 let product = EF::from_base(x1_f[m]) * x2_ef[m];
689 let lagrange_term = EF::one() - x1_f[m] - x2_ef[m] + product + product;
690 let new_field_acc = x1_f[m] + field_acc * F::from_canonical_u32(2);
691 let new_acc = acc * lagrange_term;
692
693 UnsafeCell::raw_get(record.prefix_sum_checks_events[offset + m].as_ptr())
694 .write(PrefixSumChecksEvent {
695 zero,
696 one: Block::from(one.as_base_slice()),
697 x1: x1_f[m],
698 x2: Block::from(x2_ef[m].as_base_slice()),
699 acc: Block::from(acc.as_base_slice()),
700 new_acc: Block::from(new_acc.as_base_slice()),
701 field_acc,
702 new_field_acc,
703 });
704
705 acc = new_acc;
706 field_acc = new_field_acc;
707 memory.mw_unchecked(accs[m], Block::from(acc.as_base_slice()));
708 memory.mw_unchecked(field_accs[m], Block::from(field_acc));
709 }
710 }
711 Instruction::CommitPublicValues(ref instr) => {
712 let pv_addrs = instr.pv_addrs.as_array();
713 let pv_values: [F; RECURSIVE_PROOF_NUM_PV_ELTS] =
714 array::from_fn(|i| memory.mr_unchecked(pv_addrs[i]).val[0]);
715
716 UnsafeCell::raw_get(record.public_values.as_ptr())
718 .write(*pv_values.as_slice().borrow());
719
720 UnsafeCell::raw_get(record.commit_pv_hash_events[offset].as_ptr()).write(
722 CommitPublicValuesEvent { public_values: *pv_values.as_slice().borrow() },
723 );
724 }
725 Instruction::Print(PrintInstr { ref field_elt_type, addr }) => match field_elt_type {
726 FieldEltType::Base => {
727 let f = memory.mr_unchecked(addr).val[0];
728 writeln!(debug_stdout.lock().unwrap(), "PRINTF={f}")
729 }
730 FieldEltType::Extension => {
731 let ef = memory.mr_unchecked(addr).val;
732 writeln!(debug_stdout.lock().unwrap(), "PRINTEF={ef:?}")
733 }
734 }
735 .map_err(RuntimeError::DebugPrint)?,
736 Instruction::HintExt2Felts(HintExt2FeltsInstr { output_addrs_mults, input_addr }) => {
737 let fs = memory.mr_unchecked(input_addr).val;
738 for (i, (f, (addr, _mult))) in fs.into_iter().zip(output_addrs_mults).enumerate() {
740 let felt = Block::from(f);
741 memory.mw_unchecked(addr, felt);
742
743 UnsafeCell::raw_get(record.mem_var_events[offset + i].as_ptr())
745 .write(MemEvent { inner: felt });
746 }
747 }
748 Instruction::Hint(HintInstr { ref output_addrs_mults }) => {
749 let witness_stream =
750 witness_stream.expect("hint should be called outside parallel contexts");
751 if witness_stream.len() < output_addrs_mults.len() {
753 return Err(RuntimeError::EmptyWitnessStream);
754 }
755
756 let witness = witness_stream.drain(0..output_addrs_mults.len());
757 for (i, (&(addr, _mult), val)) in zip(output_addrs_mults, witness).enumerate() {
758 memory.mw_unchecked(addr, val);
760
761 UnsafeCell::raw_get(record.mem_var_events[offset + i].as_ptr())
763 .write(MemEvent { inner: val });
764 }
765 }
766 Instruction::DebugBacktrace(ref backtrace) => {
767 cfg_if! {
768 if #[cfg(feature = "debug")] {
769 state.last_trace = Some(backtrace.clone());
770 } else {
771 let _ = backtrace;
773 }
774 }
775 }
776 }
777
778 Ok(())
779 }
780
781 unsafe fn execute_raw(
782 env: &ExecEnv<F, Diffusion>,
783 root_program: &Arc<RecursionProgram<F>>,
784 witness_stream: Option<&mut VecDeque<Block<F>>>,
785 ) -> Result<ExecutionRecord<F>, RuntimeError<F, EF>> {
786 let root_record = UnsafeRecord::<F>::new(root_program.event_counts);
787 debug_span!("root").in_scope(|| {
788 Self::execute_raw_inner(env, &root_program.inner, witness_stream, &root_record)
789 })?;
790
791 let record = root_record.into_record(Arc::clone(root_program), 0);
793 Ok(record)
794 }
795
796 unsafe fn execute_raw_inner(
800 env: &ExecEnv<F, Diffusion>,
801 program: &RawProgram<AnalyzedInstruction<F>>,
802 mut witness_stream: Option<&mut VecDeque<Block<F>>>,
803 record: &UnsafeRecord<F>,
804 ) -> Result<(), RuntimeError<F, EF>> {
805 let mut state = ExecState {
806 env: env.clone(),
807 #[cfg(feature = "debug")]
808 last_trace: None,
809 };
810
811 for block in &program.seq_blocks {
812 match block {
813 SeqBlock::Basic(basic_block) => {
814 for analyzed_instruction in &basic_block.instrs {
815 unsafe {
816 Self::execute_one(
817 &mut state,
818 record,
819 witness_stream.as_deref_mut(),
820 analyzed_instruction,
821 )
822 }?;
823 }
824 }
825 SeqBlock::Parallel(vec) => {
826 vec.par_iter().try_for_each(|subprogram| {
827 Self::execute_raw_inner(env, subprogram, None, record)
828 })?;
829 }
830 }
831 }
832
833 Ok(())
834 }
835
836 pub fn run(&mut self) -> Result<(), RuntimeError<F, EF>> {
838 let record = unsafe {
839 Self::execute_raw(
840 &ExecEnv {
841 memory: &self.memory,
842 perm: self.perm.as_ref().unwrap(),
843 debug_stdout: &Mutex::new(&mut self.debug_stdout),
844 },
845 &self.program,
846 Some(&mut self.witness_stream),
847 )
848 }?;
849
850 self.record = record;
851
852 Ok(())
853 }
854}
855
856struct ExecState<'a, 'b, F, Diffusion> {
857 pub env: ExecEnv<'a, 'b, F, Diffusion>,
858 #[cfg(feature = "debug")]
860 pub last_trace: Option<Trace>,
861}
862
863impl<F, Diffusion> ExecState<'_, '_, F, Diffusion> {
864 fn resolve_trace(&mut self) -> Option<&mut Trace> {
865 cfg_if::cfg_if! {
866 if #[cfg(feature = "debug")] {
867 #[allow(clippy::manual_inspect)]
869 self.last_trace.as_mut().map(|trace| {
870 trace.resolve();
871 trace
872 })
873 } else {
874 None
875 }
876 }
877 }
878}
879
880impl<'a, 'b, F, Diffusion> Clone for ExecState<'a, 'b, F, Diffusion>
881where
882 ExecEnv<'a, 'b, F, Diffusion>: Clone,
883 ExecutionRecord<F>: Clone,
884{
885 fn clone(&self) -> Self {
886 let Self {
887 env,
888 #[cfg(feature = "debug")]
890 last_trace,
891 } = self;
892 Self {
893 env: env.clone(),
894 #[cfg(feature = "debug")]
896 last_trace: last_trace.clone(),
897 }
898 }
899
900 fn clone_from(&mut self, source: &Self) {
901 let Self {
902 env,
903 #[cfg(feature = "debug")]
905 last_trace,
906 } = self;
907 env.clone_from(&source.env);
908 #[cfg(feature = "debug")]
910 last_trace.clone_from(&source.last_trace);
911 }
912}
913
914struct ExecEnv<'a, 'b, F, Diffusion> {
915 pub memory: &'a MemVec<F>,
916 pub perm: &'a Perm<F, Diffusion>,
917 pub debug_stdout: &'a Mutex<dyn Write + Send + 'b>,
918}
919
920impl<F, Diffusion> Clone for ExecEnv<'_, '_, F, Diffusion> {
921 fn clone(&self) -> Self {
922 let Self { memory, perm, debug_stdout } = self;
923 Self { memory, perm, debug_stdout }
924 }
925
926 fn clone_from(&mut self, source: &Self) {
927 let Self { memory, perm, debug_stdout } = self;
928 memory.clone_from(&source.memory);
929 perm.clone_from(&source.perm);
930 debug_stdout.clone_from(&source.debug_stdout);
931 }
932}