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