1pub mod instruction;
2mod memory;
3mod opcode;
4mod program;
5mod record;
6
7use 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
38use crate::*;
40
41pub 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
47pub const PERMUTATION_WIDTH: usize = 16;
49pub const POSEIDON2_SBOX_DEGREE: u64 = 7;
50pub const HASH_RATE: usize = 8;
51
52pub 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
68pub 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 pub program: Arc<RecursionProgram<F>>,
102
103 pub memory: MemVec<F>,
105
106 pub record: ExecutionRecord<F>,
108
109 pub witness_stream: VecDeque<Block<F>>,
110
111 pub debug_stdout: Box<dyn Write + Send + 'a>,
113
114 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 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 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 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 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 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 let bits = (0..output_addrs_mults.len())
354 .map(|i| Block::from(F::from_canonical_u32((num >> i) & 1)))
355 .collect::<Vec<_>>();
356 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 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 let quotient = (-p_at_z + p_at_x) / (-z + x);
434
435 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 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 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 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 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 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 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 #[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}