sp1_recursion_core/runtime/
mod.rs

1pub mod instruction;
2mod memory;
3mod opcode;
4mod program;
5mod record;
6
7// Avoid triggering annoying branch of thiserror derive macro.
8use backtrace::Backtrace as Trace;
9pub use instruction::Instruction;
10use instruction::{
11    FieldEltType, HintAddCurveInstr, HintBitsInstr, HintExt2FeltsInstr, HintInstr, PrintInstr,
12};
13use itertools::Itertools;
14use memory::*;
15pub use opcode::*;
16use p3_field::{AbstractExtensionField, AbstractField, ExtensionField, PrimeField32};
17use p3_maybe_rayon::prelude::*;
18use p3_poseidon2::{Poseidon2, Poseidon2ExternalMatrixGeneral};
19use p3_symmetric::{CryptographicPermutation, Permutation};
20use p3_util::reverse_bits_len;
21pub use program::*;
22pub use record::*;
23use sp1_stark::{septic_curve::SepticCurve, septic_extension::SepticExtension, MachineRecord};
24use std::{
25    array,
26    borrow::Borrow,
27    collections::VecDeque,
28    fmt::Debug,
29    io::{stdout, Write},
30    iter::zip,
31    marker::PhantomData,
32    sync::{Arc, Mutex},
33};
34use thiserror::Error;
35
36use crate::air::{Block, RECURSIVE_PROOF_NUM_PV_ELTS};
37
38/// TODO expand glob import once things are organized enough
39use crate::*;
40
41/// The heap pointer address.
42pub const HEAP_PTR: i32 = -4;
43pub const STACK_SIZE: usize = 1 << 24;
44pub const HEAP_START_ADDRESS: usize = STACK_SIZE + 4;
45pub const MEMORY_SIZE: usize = 1 << 28;
46
47/// The width of the Poseidon2 permutation.
48pub const PERMUTATION_WIDTH: usize = 16;
49pub const POSEIDON2_SBOX_DEGREE: u64 = 7;
50pub const HASH_RATE: usize = 8;
51
52/// The current verifier implementation assumes that we are using a 256-bit hash with 32-bit
53/// elements.
54pub const DIGEST_SIZE: usize = 8;
55
56pub const NUM_BITS: usize = 31;
57
58pub const D: usize = 4;
59
60type Perm<F, Diffusion> = Poseidon2<
61    F,
62    Poseidon2ExternalMatrixGeneral,
63    Diffusion,
64    PERMUTATION_WIDTH,
65    POSEIDON2_SBOX_DEGREE,
66>;
67
68/// TODO fully document.
69/// Taken from [`sp1_recursion_core::runtime::Runtime`].
70/// Many missing things (compared to the old `Runtime`) will need to be implemented.
71pub struct Runtime<'a, F: PrimeField32, EF: ExtensionField<F>, Diffusion> {
72    pub timestamp: usize,
73
74    pub nb_poseidons: usize,
75
76    pub nb_wide_poseidons: usize,
77
78    pub nb_bit_decompositions: usize,
79
80    pub nb_ext_ops: usize,
81
82    pub nb_base_ops: usize,
83
84    pub nb_memory_ops: usize,
85
86    pub nb_branch_ops: usize,
87
88    pub nb_select: usize,
89
90    pub nb_exp_reverse_bits: usize,
91
92    pub nb_fri_fold: usize,
93
94    pub nb_batch_fri: usize,
95
96    pub nb_print_f: usize,
97
98    pub nb_print_e: usize,
99
100    /// The program.
101    pub program: Arc<RecursionProgram<F>>,
102
103    /// Memory. From canonical usize of an Address to a MemoryEntry.
104    pub memory: MemVec<F>,
105
106    /// The execution record.
107    pub record: ExecutionRecord<F>,
108
109    pub witness_stream: VecDeque<Block<F>>,
110
111    /// The stream that print statements write to.
112    pub debug_stdout: Box<dyn Write + Send + 'a>,
113
114    /// Entries for dealing with the Poseidon2 hash state.
115    perm: Option<Perm<F, Diffusion>>,
116
117    _marker_ef: PhantomData<EF>,
118
119    _marker_diffusion: PhantomData<Diffusion>,
120}
121
122#[derive(Error, Debug)]
123pub enum RuntimeError<F: Debug, EF: Debug> {
124    #[error(
125        "attempted to perform base field division {in1:?}/{in2:?}\n\
126        \tin instruction {instr:#?}\n\
127        \tnearest backtrace:\n{trace:#?}"
128    )]
129    DivFOutOfDomain { in1: F, in2: F, instr: BaseAluInstr<F>, trace: Option<Trace> },
130    #[error(
131        "attempted to perform extension field division {in1:?}/{in2:?}\n\
132        \tin instruction {instr:#?}\n\
133        \tnearest backtrace:\n{trace:#?}"
134    )]
135    DivEOutOfDomain { in1: EF, in2: EF, instr: ExtAluInstr<F>, trace: Option<Trace> },
136    #[error("failed to print to `debug_stdout`: {0}")]
137    DebugPrint(#[from] std::io::Error),
138    #[error("attempted to read from empty witness stream")]
139    EmptyWitnessStream,
140}
141
142impl<F: PrimeField32, EF: ExtensionField<F>, Diffusion> Runtime<'_, F, EF, Diffusion>
143where
144    Poseidon2<
145        F,
146        Poseidon2ExternalMatrixGeneral,
147        Diffusion,
148        PERMUTATION_WIDTH,
149        POSEIDON2_SBOX_DEGREE,
150    >: CryptographicPermutation<[F; PERMUTATION_WIDTH]>,
151{
152    pub fn new(
153        program: Arc<RecursionProgram<F>>,
154        perm: Poseidon2<
155            F,
156            Poseidon2ExternalMatrixGeneral,
157            Diffusion,
158            PERMUTATION_WIDTH,
159            POSEIDON2_SBOX_DEGREE,
160        >,
161    ) -> Self {
162        let record = ExecutionRecord::<F> { program: program.clone(), ..Default::default() };
163        let memory = MemVec::with_capacity(program.total_memory);
164        Self {
165            timestamp: 0,
166            nb_poseidons: 0,
167            nb_wide_poseidons: 0,
168            nb_bit_decompositions: 0,
169            nb_select: 0,
170            nb_exp_reverse_bits: 0,
171            nb_ext_ops: 0,
172            nb_base_ops: 0,
173            nb_memory_ops: 0,
174            nb_branch_ops: 0,
175            nb_fri_fold: 0,
176            nb_batch_fri: 0,
177            nb_print_f: 0,
178            nb_print_e: 0,
179            program,
180            memory,
181            record,
182            witness_stream: VecDeque::new(),
183            debug_stdout: Box::new(stdout()),
184            perm: Some(perm),
185            _marker_ef: PhantomData,
186            _marker_diffusion: PhantomData,
187        }
188    }
189
190    pub fn print_stats(&self) {
191        if tracing::event_enabled!(tracing::Level::DEBUG) {
192            let mut stats = self.record.stats().into_iter().collect::<Vec<_>>();
193            stats.sort_unstable();
194            tracing::debug!("total events: {}", stats.iter().map(|(_, v)| *v).sum::<usize>());
195            for (k, v) in stats {
196                tracing::debug!("  {k}: {v}");
197            }
198        }
199    }
200
201    /// # Safety
202    ///
203    /// Safety is guaranteed if calls to this function (with the given `env` argument) obey the
204    /// happens-before relation defined in the documentation of [`RecursionProgram::new_unchecked`].
205    ///
206    /// This function makes use of interior mutability of `env` via `UnsafeCell`.
207    /// All of this function's unsafety stems from the `instruction` argument that indicates'
208    /// whether/how to read and write from the memory in `env`. There must be a strict
209    /// happens-before relation where reads happen before writes, and memory read from must be
210    /// initialized.
211    unsafe fn execute_one(
212        state: &mut ExecState<F, Diffusion>,
213        witness_stream: Option<&mut VecDeque<Block<F>>>,
214        instruction: Instruction<F>,
215    ) -> Result<(), RuntimeError<F, EF>> {
216        let ExecEnv { memory, perm, debug_stdout } = state.env;
217        let record = &mut state.record;
218        match instruction {
219            Instruction::BaseAlu(instr @ BaseAluInstr { opcode, mult: _, addrs }) => {
220                let in1 = memory.mr_unchecked(addrs.in1).val[0];
221                let in2 = memory.mr_unchecked(addrs.in2).val[0];
222                // Do the computation.
223                let out = match opcode {
224                    BaseAluOpcode::AddF => in1 + in2,
225                    BaseAluOpcode::SubF => in1 - in2,
226                    BaseAluOpcode::MulF => in1 * in2,
227                    BaseAluOpcode::DivF => match in1.try_div(in2) {
228                        Some(x) => x,
229                        None => {
230                            // Check for division exceptions and error. Note that 0/0 is defined
231                            // to be 1.
232                            if in1.is_zero() {
233                                AbstractField::one()
234                            } else {
235                                return Err(RuntimeError::DivFOutOfDomain {
236                                    in1,
237                                    in2,
238                                    instr,
239                                    trace: state.resolve_trace().cloned(),
240                                });
241                            }
242                        }
243                    },
244                };
245                memory.mw_unchecked(addrs.out, Block::from(out));
246                record.base_alu_events.push(BaseAluEvent { out, in1, in2 });
247            }
248            Instruction::ExtAlu(instr @ ExtAluInstr { opcode, mult: _, addrs }) => {
249                let in1 = memory.mr_unchecked(addrs.in1).val;
250                let in2 = memory.mr_unchecked(addrs.in2).val;
251                // Do the computation.
252                let in1_ef = EF::from_base_slice(&in1.0);
253                let in2_ef = EF::from_base_slice(&in2.0);
254                let out_ef = match opcode {
255                    ExtAluOpcode::AddE => in1_ef + in2_ef,
256                    ExtAluOpcode::SubE => in1_ef - in2_ef,
257                    ExtAluOpcode::MulE => in1_ef * in2_ef,
258                    ExtAluOpcode::DivE => match in1_ef.try_div(in2_ef) {
259                        Some(x) => x,
260                        None => {
261                            // Check for division exceptions and error. Note that 0/0 is defined
262                            // to be 1.
263                            if in1_ef.is_zero() {
264                                AbstractField::one()
265                            } else {
266                                return Err(RuntimeError::DivEOutOfDomain {
267                                    in1: in1_ef,
268                                    in2: in2_ef,
269                                    instr,
270                                    trace: state.resolve_trace().cloned(),
271                                });
272                            }
273                        }
274                    },
275                };
276                let out = Block::from(out_ef.as_base_slice());
277                memory.mw_unchecked(addrs.out, out);
278                record.ext_alu_events.push(ExtAluEvent { out, in1, in2 });
279            }
280            Instruction::Mem(MemInstr {
281                addrs: MemIo { inner: addr },
282                vals: MemIo { inner: val },
283                mult: _,
284                kind,
285            }) => {
286                match kind {
287                    MemAccessKind::Read => {
288                        let mem_entry = memory.mr_unchecked(addr);
289                        assert_eq!(
290                            mem_entry.val, val,
291                            "stored memory value should be the specified value"
292                        );
293                    }
294                    MemAccessKind::Write => memory.mw_unchecked(addr, val),
295                }
296                record.mem_const_count += 1;
297            }
298            Instruction::Poseidon2(instr) => {
299                let Poseidon2Instr { addrs: Poseidon2Io { input, output }, mults: _ } = *instr;
300                let in_vals = std::array::from_fn(|i| memory.mr_unchecked(input[i]).val[0]);
301                let perm_output = perm.permute(in_vals);
302
303                perm_output.iter().zip(output).for_each(|(&val, addr)| {
304                    memory.mw_unchecked(addr, Block::from(val));
305                });
306                record
307                    .poseidon2_events
308                    .push(Poseidon2Event { input: in_vals, output: perm_output });
309            }
310            Instruction::Select(SelectInstr {
311                addrs: SelectIo { bit, out1, out2, in1, in2 },
312                mult1: _,
313                mult2: _,
314            }) => {
315                let bit = memory.mr_unchecked(bit).val[0];
316                let in1 = memory.mr_unchecked(in1).val[0];
317                let in2 = memory.mr_unchecked(in2).val[0];
318                let out1_val = bit * in2 + (F::one() - bit) * in1;
319                let out2_val = bit * in1 + (F::one() - bit) * in2;
320                memory.mw_unchecked(out1, Block::from(out1_val));
321                memory.mw_unchecked(out2, Block::from(out2_val));
322                record.select_events.push(SelectEvent {
323                    bit,
324                    out1: out1_val,
325                    out2: out2_val,
326                    in1,
327                    in2,
328                })
329            }
330            Instruction::ExpReverseBitsLen(ExpReverseBitsInstr {
331                addrs: ExpReverseBitsIo { base, exp, result },
332                mult: _,
333            }) => {
334                let base_val = memory.mr_unchecked(base).val[0];
335                let exp_bits: Vec<_> =
336                    exp.iter().map(|bit| memory.mr_unchecked(*bit).val[0]).collect();
337                let exp_val = exp_bits
338                    .iter()
339                    .enumerate()
340                    .fold(0, |acc, (i, &val)| acc + val.as_canonical_u32() * (1 << i));
341                let out =
342                    base_val.exp_u64(reverse_bits_len(exp_val as usize, exp_bits.len()) as u64);
343                memory.mw_unchecked(result, Block::from(out));
344                record.exp_reverse_bits_len_events.push(ExpReverseBitsEvent {
345                    result: out,
346                    base: base_val,
347                    exp: exp_bits,
348                });
349            }
350            Instruction::HintBits(HintBitsInstr { output_addrs_mults, input_addr }) => {
351                let num = memory.mr_unchecked(input_addr).val[0].as_canonical_u32();
352                // Decompose the num into LE bits.
353                let bits = (0..output_addrs_mults.len())
354                    .map(|i| Block::from(F::from_canonical_u32((num >> i) & 1)))
355                    .collect::<Vec<_>>();
356                // Write the bits to the array at dst.
357                for (bit, (addr, _mult)) in bits.into_iter().zip(output_addrs_mults) {
358                    memory.mw_unchecked(addr, bit);
359                    record.mem_var_events.push(MemEvent { inner: bit });
360                }
361            }
362            Instruction::HintAddCurve(instr) => {
363                let HintAddCurveInstr {
364                    output_x_addrs_mults,
365                    output_y_addrs_mults,
366                    input1_x_addrs,
367                    input1_y_addrs,
368                    input2_x_addrs,
369                    input2_y_addrs,
370                } = *instr;
371                let input1_x = SepticExtension::<F>::from_base_fn(|i| {
372                    memory.mr_unchecked(input1_x_addrs[i]).val[0]
373                });
374                let input1_y = SepticExtension::<F>::from_base_fn(|i| {
375                    memory.mr_unchecked(input1_y_addrs[i]).val[0]
376                });
377                let input2_x = SepticExtension::<F>::from_base_fn(|i| {
378                    memory.mr_unchecked(input2_x_addrs[i]).val[0]
379                });
380                let input2_y = SepticExtension::<F>::from_base_fn(|i| {
381                    memory.mr_unchecked(input2_y_addrs[i]).val[0]
382                });
383                let point1 = SepticCurve { x: input1_x, y: input1_y };
384                let point2 = SepticCurve { x: input2_x, y: input2_y };
385                let output = point1.add_incomplete(point2);
386
387                for (val, (addr, _mult)) in
388                    output.x.0.into_iter().zip(output_x_addrs_mults.into_iter())
389                {
390                    memory.mw_unchecked(addr, Block::from(val));
391                    record.mem_var_events.push(MemEvent { inner: Block::from(val) });
392                }
393                for (val, (addr, _mult)) in
394                    output.y.0.into_iter().zip(output_y_addrs_mults.into_iter())
395                {
396                    memory.mw_unchecked(addr, Block::from(val));
397                    record.mem_var_events.push(MemEvent { inner: Block::from(val) });
398                }
399            }
400            Instruction::FriFold(instr) => {
401                let FriFoldInstr {
402                    base_single_addrs,
403                    ext_single_addrs,
404                    ext_vec_addrs,
405                    alpha_pow_mults: _,
406                    ro_mults: _,
407                } = *instr;
408                let x = memory.mr_unchecked(base_single_addrs.x).val[0];
409                let z = memory.mr_unchecked(ext_single_addrs.z).val;
410                let z: EF = z.ext();
411                let alpha = memory.mr_unchecked(ext_single_addrs.alpha).val;
412                let alpha: EF = alpha.ext();
413                let mat_opening = ext_vec_addrs
414                    .mat_opening
415                    .iter()
416                    .map(|addr| memory.mr_unchecked(*addr).val)
417                    .collect_vec();
418                let ps_at_z = ext_vec_addrs
419                    .ps_at_z
420                    .iter()
421                    .map(|addr| memory.mr_unchecked(*addr).val)
422                    .collect_vec();
423
424                for m in 0..ps_at_z.len() {
425                    // let m = F::from_canonical_u32(m);
426                    // Get the opening values.
427                    let p_at_x = mat_opening[m];
428                    let p_at_x: EF = p_at_x.ext();
429                    let p_at_z = ps_at_z[m];
430                    let p_at_z: EF = p_at_z.ext();
431
432                    // Calculate the quotient and update the values
433                    let quotient = (-p_at_z + p_at_x) / (-z + x);
434
435                    // First we peek to get the current value.
436                    let alpha_pow: EF =
437                        memory.mr_unchecked(ext_vec_addrs.alpha_pow_input[m]).val.ext();
438
439                    let ro: EF = memory.mr_unchecked(ext_vec_addrs.ro_input[m]).val.ext();
440
441                    let new_ro = ro + alpha_pow * quotient;
442                    let new_alpha_pow = alpha_pow * alpha;
443
444                    memory.mw_unchecked(
445                        ext_vec_addrs.ro_output[m],
446                        Block::from(new_ro.as_base_slice()),
447                    );
448
449                    memory.mw_unchecked(
450                        ext_vec_addrs.alpha_pow_output[m],
451                        Block::from(new_alpha_pow.as_base_slice()),
452                    );
453
454                    record.fri_fold_events.push(FriFoldEvent {
455                        base_single: FriFoldBaseIo { x },
456                        ext_single: FriFoldExtSingleIo {
457                            z: Block::from(z.as_base_slice()),
458                            alpha: Block::from(alpha.as_base_slice()),
459                        },
460                        ext_vec: FriFoldExtVecIo {
461                            mat_opening: Block::from(p_at_x.as_base_slice()),
462                            ps_at_z: Block::from(p_at_z.as_base_slice()),
463                            alpha_pow_input: Block::from(alpha_pow.as_base_slice()),
464                            ro_input: Block::from(ro.as_base_slice()),
465                            alpha_pow_output: Block::from(new_alpha_pow.as_base_slice()),
466                            ro_output: Block::from(new_ro.as_base_slice()),
467                        },
468                    });
469                }
470            }
471            Instruction::BatchFRI(instr) => {
472                let BatchFRIInstr { base_vec_addrs, ext_single_addrs, ext_vec_addrs, acc_mult: _ } =
473                    *instr;
474
475                let mut acc = EF::zero();
476                let p_at_xs = base_vec_addrs
477                    .p_at_x
478                    .iter()
479                    .map(|addr| memory.mr_unchecked(*addr).val[0])
480                    .collect_vec();
481                let p_at_zs = ext_vec_addrs
482                    .p_at_z
483                    .iter()
484                    .map(|addr| memory.mr_unchecked(*addr).val.ext::<EF>())
485                    .collect_vec();
486                let alpha_pows: Vec<_> = ext_vec_addrs
487                    .alpha_pow
488                    .iter()
489                    .map(|addr| memory.mr_unchecked(*addr).val.ext::<EF>())
490                    .collect_vec();
491
492                for m in 0..p_at_zs.len() {
493                    acc += alpha_pows[m] * (p_at_zs[m] - EF::from_base(p_at_xs[m]));
494                    record.batch_fri_events.push(BatchFRIEvent {
495                        base_vec: BatchFRIBaseVecIo { p_at_x: p_at_xs[m] },
496                        ext_single: BatchFRIExtSingleIo { acc: Block::from(acc.as_base_slice()) },
497                        ext_vec: BatchFRIExtVecIo {
498                            p_at_z: Block::from(p_at_zs[m].as_base_slice()),
499                            alpha_pow: Block::from(alpha_pows[m].as_base_slice()),
500                        },
501                    });
502                }
503
504                memory.mw_unchecked(ext_single_addrs.acc, Block::from(acc.as_base_slice()));
505            }
506            Instruction::CommitPublicValues(instr) => {
507                let pv_addrs = instr.pv_addrs.as_array();
508                let pv_values: [F; RECURSIVE_PROOF_NUM_PV_ELTS] =
509                    array::from_fn(|i| memory.mr_unchecked(pv_addrs[i]).val[0]);
510                record.public_values = *pv_values.as_slice().borrow();
511                record
512                    .commit_pv_hash_events
513                    .push(CommitPublicValuesEvent { public_values: record.public_values });
514            }
515
516            Instruction::Print(PrintInstr { field_elt_type, addr }) => match field_elt_type {
517                FieldEltType::Base => {
518                    let f = memory.mr_unchecked(addr).val[0];
519                    writeln!(debug_stdout.lock().unwrap(), "PRINTF={f}")
520                }
521                FieldEltType::Extension => {
522                    let ef = memory.mr_unchecked(addr).val;
523                    writeln!(debug_stdout.lock().unwrap(), "PRINTEF={ef:?}")
524                }
525            }
526            .map_err(RuntimeError::DebugPrint)?,
527            Instruction::HintExt2Felts(HintExt2FeltsInstr { output_addrs_mults, input_addr }) => {
528                let fs = memory.mr_unchecked(input_addr).val;
529                // Write the bits to the array at dst.
530                for (f, (addr, _mult)) in fs.into_iter().zip(output_addrs_mults) {
531                    let felt = Block::from(f);
532                    memory.mw_unchecked(addr, felt);
533                    record.mem_var_events.push(MemEvent { inner: felt });
534                }
535            }
536            Instruction::Hint(HintInstr { output_addrs_mults }) => {
537                let witness_stream =
538                    witness_stream.expect("hint should be called outside parallel contexts");
539                // Check that enough Blocks can be read, so `drain` does not panic.
540                if witness_stream.len() < output_addrs_mults.len() {
541                    return Err(RuntimeError::EmptyWitnessStream);
542                }
543                let witness = witness_stream.drain(0..output_addrs_mults.len());
544                for ((addr, _mult), val) in zip(output_addrs_mults, witness) {
545                    // Inline [`Self::mw`] to mutably borrow multiple fields of `self`.
546                    memory.mw_unchecked(addr, val);
547                    record.mem_var_events.push(MemEvent { inner: val });
548                }
549            }
550            #[cfg(feature = "debug")]
551            Instruction::DebugBacktrace(backtrace) => {
552                state.last_trace = Some(backtrace);
553            }
554        }
555
556        Ok(())
557    }
558
559    /// # Safety
560    ///
561    /// This function makes the same safety assumptions as [`RecursionProgram::new_unchecked`].
562    unsafe fn execute_raw(
563        env: &ExecEnv<F, Diffusion>,
564        program: &RawProgram<Instruction<F>>,
565        root_program: &Arc<RecursionProgram<F>>,
566        mut witness_stream: Option<&mut VecDeque<Block<F>>>,
567    ) -> Result<ExecutionRecord<F>, RuntimeError<F, EF>> {
568        let fresh_record =
569            || ExecutionRecord { program: Arc::clone(root_program), ..Default::default() };
570
571        let mut state = ExecState {
572            env: env.clone(),
573            record: fresh_record(),
574            #[cfg(feature = "debug")]
575            last_trace: None,
576        };
577
578        for block in &program.seq_blocks {
579            match block {
580                SeqBlock::Basic(basic_block) => {
581                    for instruction in &basic_block.instrs {
582                        unsafe {
583                            Self::execute_one(
584                                &mut state,
585                                witness_stream.as_deref_mut(),
586                                instruction.clone(),
587                            )
588                        }?;
589                    }
590                }
591                SeqBlock::Parallel(vec) => {
592                    state.record.append(
593                        &mut vec
594                            .par_iter()
595                            .map(|subprogram| {
596                                // Witness stream may not be called inside parallel contexts to
597                                // avoid nondeterminism.
598                                Self::execute_raw(env, subprogram, root_program, None)
599                            })
600                            .try_reduce(fresh_record, |mut record, mut res| {
601                                record.append(&mut res);
602                                Ok(record)
603                            })?,
604                    );
605                }
606            }
607        }
608        Ok(state.record)
609    }
610
611    /// Run the program.
612    pub fn run(&mut self) -> Result<(), RuntimeError<F, EF>> {
613        let record = unsafe {
614            Self::execute_raw(
615                &ExecEnv {
616                    memory: &self.memory,
617                    perm: self.perm.as_ref().unwrap(),
618                    debug_stdout: &Mutex::new(&mut self.debug_stdout),
619                },
620                &self.program.inner,
621                &self.program,
622                Some(&mut self.witness_stream),
623            )
624        }?;
625
626        self.record = record;
627
628        Ok(())
629    }
630}
631
632struct ExecState<'a, 'b, F, Diffusion> {
633    pub env: ExecEnv<'a, 'b, F, Diffusion>,
634    pub record: ExecutionRecord<F>,
635    #[cfg(feature = "debug")]
636    pub last_trace: Option<Trace>,
637}
638
639impl<F, Diffusion> ExecState<'_, '_, F, Diffusion> {
640    fn resolve_trace(&mut self) -> Option<&mut Trace> {
641        cfg_if::cfg_if! {
642            if #[cfg(feature = "debug")] {
643                // False positive.
644                #[allow(clippy::manual_inspect)]
645                self.last_trace.as_mut().map(|trace| {
646                    trace.resolve();
647                    trace
648                })
649            } else {
650                None
651            }
652        }
653    }
654}
655
656impl<'a, 'b, F, Diffusion> Clone for ExecState<'a, 'b, F, Diffusion>
657where
658    ExecEnv<'a, 'b, F, Diffusion>: Clone,
659    ExecutionRecord<F>: Clone,
660{
661    fn clone(&self) -> Self {
662        let Self {
663            env,
664            record,
665            #[cfg(feature = "debug")]
666            last_trace,
667        } = self;
668        Self {
669            env: env.clone(),
670            record: record.clone(),
671            #[cfg(feature = "debug")]
672            last_trace: last_trace.clone(),
673        }
674    }
675
676    fn clone_from(&mut self, source: &Self) {
677        let Self {
678            env,
679            record,
680            #[cfg(feature = "debug")]
681            last_trace,
682        } = self;
683        env.clone_from(&source.env);
684        record.clone_from(&source.record);
685        #[cfg(feature = "debug")]
686        last_trace.clone_from(&source.last_trace);
687    }
688}
689
690struct ExecEnv<'a, 'b, F, Diffusion> {
691    pub memory: &'a MemVec<F>,
692    pub perm: &'a Perm<F, Diffusion>,
693    pub debug_stdout: &'a Mutex<dyn Write + Send + 'b>,
694}
695
696impl<F, Diffusion> Clone for ExecEnv<'_, '_, F, Diffusion> {
697    fn clone(&self) -> Self {
698        let Self { memory, perm, debug_stdout } = self;
699        Self { memory, perm, debug_stdout }
700    }
701
702    fn clone_from(&mut self, source: &Self) {
703        let Self { memory, perm, debug_stdout } = self;
704        memory.clone_from(&source.memory);
705        perm.clone_from(&source.perm);
706        debug_stdout.clone_from(&source.debug_stdout);
707    }
708}