From 2086f2a026c05bde0884c8715b7f09eb70dece14 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Fri, 6 Dec 2024 15:40:14 -0300 Subject: [PATCH 01/35] save events --- riscv-executor/src/lib.rs | 268 +++++++++++++++++++++++++++++++++++--- 1 file changed, 251 insertions(+), 17 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 3a887d2f0d..a84090b473 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -42,6 +42,70 @@ use memory::*; use crate::profiler::Profiler; +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +/// Used to identify operations in the event log +#[allow(non_camel_case_types)] +enum MainInstruction { + set_reg, + get_reg, + affine, + mstore, + mstore_bootloader, + mload, + load_bootloader_input, + assert_bootloader_input, + load_label, + jump, + jump_dyn, + jump_to_bootloader_input, + branch_if_diff_nonzero, + branch_if_diff_equal, + skip_if_equal, + branch_if_diff_greater_than, + is_diff_greater_than, + is_equal_zero, + is_not_equal, + add_wrap, + wrap16, + sub_wrap_with_offset, + sign_extend_byte, + sign_extend_16_bits, + to_signed, + divremu, + mul, + and, + or, + xor, + shl, + shr, + invert_gl, + split_gl, + poseidon_gl, + poseidon2_gl, + affine_256, + mod_256, + ec_add, + ec_double, + commit_public, +} + +struct MainEvent(MainInstruction, u32, Vec); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[allow(non_camel_case_types)] +enum MachineInstance { + main_memory, + main_regs, + main_publics, + main_binary, + main_shift, + main_split_gl, + main_poseidon_gl, + main_poseidon2_gl, + // main_keccakf, + // main_arith, +} + /// Initial value of the PC. /// /// To match the ZK proof witness, the PC must start after some offset used for @@ -266,6 +330,13 @@ pub struct ExecutionTrace { /// The length of the trace, after applying the reg_writes. len: usize, + /// Main machine instructions + main_events: Vec>, + + /// Calls into submachines. Each is a sequence of field elemements: the RHS values of the lookup followed by the selector idx (if the machine has it). + /// TODO: keep a flat Vec instead? individial events can be identified from the machine asm definition. + submachine_events: HashMap>>, + /// witness columns cols: HashMap>>, } @@ -288,6 +359,8 @@ impl ExecutionTrace { reg_writes, mem_ops: Vec::new(), len: pc, + main_events: Vec::new(), + submachine_events: HashMap::new(), cols, } } @@ -389,9 +462,10 @@ mod builder { use powdr_number::FieldElement; use crate::{ - BinaryMachine, Elem, ExecMode, Execution, ExecutionTrace, MemOperation, MemOperationKind, - MemoryMachine, MemoryState, PoseidonGlMachine, PublicsMachine, RegWrite, RegisterMemory, - ShiftMachine, SplitGlMachine, Submachine, SubmachineBoxed, PC_INITIAL_VAL, + BinaryMachine, Elem, ExecMode, Execution, ExecutionTrace, MachineInstance, MainEvent, + MainInstruction, MemOperation, MemOperationKind, MemoryMachine, MemoryState, + PoseidonGlMachine, PublicsMachine, RegWrite, RegisterMemory, ShiftMachine, SplitGlMachine, + Submachine, SubmachineBoxed, PC_INITIAL_VAL, }; fn namespace_degree_range( @@ -551,6 +625,22 @@ mod builder { } } + pub(crate) fn main_event(&mut self, ev: MainInstruction, pc: u32, args: Vec) { + if let ExecMode::Trace = self.mode { + self.trace.main_events.push(MainEvent(ev, pc, args)); + } + } + + pub(crate) fn submachine_event(&mut self, m: MachineInstance, args: &[F]) { + if let ExecMode::Trace = self.mode { + self.trace + .submachine_events + .entry(m) + .or_default() + .push(args.into()); + } + } + pub(crate) fn main_columns_len(&self) -> usize { let cols_len = self .trace @@ -700,6 +790,16 @@ mod builder { } pub(crate) fn set_mem(&mut self, addr: u32, val: u32, step: u32, selector: u32) { + self.submachine_event( + MachineInstance::main_memory, + &[ + 1.into(), + addr.into(), + step.into(), + val.into(), + selector.into(), + ], + ); if let ExecMode::Trace = self.mode { self.trace.mem_ops.push(MemOperation { row: self.trace.len, @@ -714,6 +814,16 @@ mod builder { pub(crate) fn get_mem(&mut self, addr: u32, step: u32, selector: u32) -> u32 { let val = *self.mem.get(&addr).unwrap_or(&0); + self.submachine_event( + MachineInstance::main_memory, + &[ + 0.into(), + addr.into(), + step.into(), + val.into(), + selector.into(), + ], + ); if let ExecMode::Trace = self.mode { self.trace.mem_ops.push(MemOperation { row: self.trace.len, @@ -759,6 +869,15 @@ mod builder { }; } + for MainEvent(i, pc, args) in &self.trace.main_events { + println!("main_event {i:?} {pc} {args:?}"); + } + for (m, ev) in &self.trace.submachine_events { + for e in ev { + println!("submachine_event {m:?} {e:?}"); + } + } + let pil = opt_pil.unwrap(); let main_degree = { @@ -1021,22 +1140,30 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } /// read register value, updating the register memory machine - fn reg_read(&mut self, step_offset: u32, reg: u32, selector_idx: u32) -> Elem { + fn reg_read(&mut self, step_offset: u32, reg: u32, selector: u32) -> Elem { let val = self.proc.get_reg_mem(reg); + self.proc.submachine_event( + MachineInstance::main_regs, + &[0.into(), reg.into(), val.into_fe(), selector.into()], + ); if let ExecMode::Trace = self.mode { self.proc .regs_machine - .read(self.step + step_offset, reg, val, selector_idx); + .read(self.step + step_offset, reg, val, selector); } val } /// write value to register, updating the register memory machine - fn reg_write(&mut self, step_offset: u32, reg: u32, val: Elem, selector_idx: u32) { + fn reg_write(&mut self, step_offset: u32, reg: u32, val: Elem, selector: u32) { + self.proc.submachine_event( + MachineInstance::main_regs, + &[1.into(), reg.into(), val.into_fe(), selector.into()], + ); if let ExecMode::Trace = self.mode { self.proc .regs_machine - .write(self.step + step_offset, reg, val, selector_idx); + .write(self.step + step_offset, reg, val, selector); } self.proc.set_reg_mem(reg, val); } @@ -1072,6 +1199,19 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { }; } + macro_rules! main_event { + ($insn:ident, $($args:expr),*) => { + self.proc + .main_event(MainInstruction::$insn, self.proc.get_pc().u(), vec![$($args, )*]) + }; + } + + macro_rules! submachine_event { + ($machine:ident, $($args:expr),*) => { + self.proc.submachine_event(MachineInstance::$machine, &[$($args, )*]) + }; + } + let args = args .iter() .map(|expr| self.eval_expression(expr)[0]) @@ -1098,12 +1238,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(Y_free_value, val); } + main_event!(set_reg,); Vec::new() } "get_reg" => { let addr = args[0].u(); let val = self.reg_read(0, addr, 0); + main_event!(get_reg,); vec![val] } "affine" => { @@ -1118,6 +1260,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.reg_write(1, write_reg, res, 3); set_col!(tmp1_col, val1); + main_event!(affine,); Vec::new() } @@ -1157,6 +1300,11 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(X_b3, Elem::from_u32_as_fe(b3.into())); set_col!(X_b4, Elem::from_u32_as_fe(b4.into())); + if name == "mstore" { + main_event!(mstore,); + } else { + main_event!(mstore_bootloader,); + } Vec::new() } "mload" => { @@ -1191,6 +1339,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Elem::from_u32_as_fe(((v as u64 >> 32) & 1) as u32) ); + main_event!(mload,); Vec::new() } // TODO: update to witness generation for continuations @@ -1205,6 +1354,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.reg_write(2, write_addr, val, 3); + main_event!(load_bootloader_input,); Vec::new() } // TODO: update to witness generation for continuations @@ -1219,6 +1369,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { assert_eq!(val, actual_val); + main_event!(assert_bootloader_input,); Vec::new() } "load_label" => { @@ -1230,6 +1381,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc.set_col("main::instr_load_label_param_l", label); + main_event!(load_label,); Vec::new() } "jump" => { @@ -1243,6 +1395,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc.set_col("main::instr_jump_param_l", label); + main_event!(jump,); Vec::new() } "jump_dyn" => { @@ -1257,6 +1410,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp1_col, addr); + main_event!(jump_dyn,); Vec::new() } // TODO: update to witness generation for continuations @@ -1265,6 +1419,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let addr = self.bootloader_inputs[bootloader_input_idx]; self.proc.set_pc(addr); + main_event!(jump_to_bootloader_input,); Vec::new() } "branch_if_diff_nonzero" => { @@ -1290,6 +1445,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc .set_col("main::instr_branch_if_diff_nonzero_param_l", label); + main_event!(branch_if_diff_nonzero,); Vec::new() } "branch_if_diff_equal" => { @@ -1316,6 +1472,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc .set_col("main::instr_branch_if_diff_equal_param_l", label); + main_event!(branch_if_diff_equal,); Vec::new() } "skip_if_equal" => { @@ -1340,6 +1497,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(XX_inv, Elem::Field(F::one() / get_col!(XX).into_fe())); } + main_event!(skip_if_equal,); Vec::new() } "branch_if_diff_greater_than" => { @@ -1381,6 +1539,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); + main_event!(branch_if_diff_greater_than,); Vec::new() } "is_diff_greater_than" => { @@ -1409,6 +1568,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(X_b4, Elem::from_u32_as_fe(b4.into())); set_col!(wrap_bit, Elem::from_u32_as_fe(r)); + main_event!(is_diff_greater_than,); Vec::new() } "is_equal_zero" => { @@ -1426,6 +1586,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(XX_inv, Elem::Field(F::one() / get_col!(XX).into_fe())); } + main_event!(is_equal_zero,); Vec::new() } "is_not_equal" => { @@ -1448,6 +1609,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(XX_inv, Elem::Field(F::one() / get_col!(XX).into_fe())); } + main_event!(is_not_equal,); Vec::new() } "add_wrap" => { @@ -1485,6 +1647,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); + main_event!(add_wrap,); Vec::new() } "wrap16" => { @@ -1513,6 +1676,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(Y_b5, Elem::from_u32_as_fe(b5.into())); set_col!(Y_b6, Elem::from_u32_as_fe(b6.into())); + main_event!(wrap16,); Vec::new() } "sub_wrap_with_offset" => { @@ -1546,6 +1710,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); + main_event!(sub_wrap_with_offset,); Vec::new() } "sign_extend_byte" => { @@ -1578,6 +1743,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); + main_event!(sign_extend_byte,); Vec::new() } "sign_extend_16_bits" => { @@ -1614,6 +1780,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); + main_event!(sign_extend_16_bits,); Vec::new() } "to_signed" => { @@ -1642,6 +1809,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(Y_7bit, Elem::from_u32_as_fe(b4 as u32 & 0x7f)); + main_event!(to_signed,); Vec::new() } "fail" => { @@ -1703,6 +1871,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(Y_b8, Elem::from_u32_as_fe(b8.into())); } + main_event!(divremu,); Vec::new() } "mul" => { @@ -1734,6 +1903,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); } + submachine_event!(main_split_gl, r.into(), lo.into(), hi.into()); + main_event!(mul,); Vec::new() } "and" | "or" | "xor" => { @@ -1748,13 +1919,30 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp1_col, val1); set_col!(tmp2_col, val2); - let (r, sel) = match name { - "and" => (val1.u() & val2_offset.u(), 0), - "or" => (val1.u() | val2_offset.u(), 1), - "xor" => (val1.u() ^ val2_offset.u(), 2), + let (r, op_id, sel) = match name { + "and" => { + main_event!(and,); + (val1.u() & val2_offset.u(), 0, 0) + } + "or" => { + main_event!(or,); + (val1.u() | val2_offset.u(), 1, 1) + } + "xor" => { + main_event!(xor,); + (val1.u() ^ val2_offset.u(), 2, 2) + } _ => unreachable!(), }; + submachine_event!( + main_binary, + op_id.into(), + val1.into_fe(), + val2_offset.into_fe(), + r.into() + ); + if let ExecMode::Trace = self.mode { self.proc.submachine("binary").add_operation( name, @@ -1779,12 +1967,26 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let write_reg = args[3].u(); let val2_offset: Elem = (val2.bin() + offset).into(); - let (r, sel) = match name { - "shl" => (val1.u() << val2_offset.u(), 0), - "shr" => (val1.u() >> val2_offset.u(), 1), + let (r, op_id, sel) = match name { + "shl" => { + main_event!(shl,); + (val1.u() << val2_offset.u(), 0, 0) + } + "shr" => { + main_event!(shr,); + (val1.u() >> val2_offset.u(), 1, 1) + } _ => unreachable!(), }; + submachine_event!( + main_shift, + op_id.into(), + val1.into_fe(), + val2_offset.into_fe(), + r.into() + ); + self.reg_write(3, write_reg, r.into(), 3); set_col!(tmp1_col, val1); @@ -1828,6 +2030,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); } + submachine_event!(main_split_gl, inv, low_inv.into(), high_inv.into()); + main_event!(invert_gl,); Vec::new() } "split_gl" => { @@ -1859,6 +2063,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); } + submachine_event!(main_split_gl, value.into(), lo.into(), hi.into()); + main_event!(split_gl,); Vec::new() } "poseidon_gl" => { @@ -1899,14 +2105,15 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { outputs.iter().enumerate().rev().for_each(|(i, v)| { // the .rev() is not necessary, but makes the split_gl // operations in the same "order" as automatic witgen - let v = v.to_integer().try_into_u64().unwrap(); - let hi = (v >> 32) as u32; - let lo = (v & 0xffffffff) as u32; + let val = v.to_integer().try_into_u64().unwrap(); + let hi = (val >> 32) as u32; + let lo = (val & 0xffffffff) as u32; // step/selector of memory writes from the poseidon machine self.proc .set_mem(output_ptr.u() + 8 * i as u32, lo, self.step + 1, 4); self.proc .set_mem(output_ptr.u() + 8 * i as u32 + 4, hi, self.step + 1, 5); + submachine_event!(main_split_gl, *v, lo.into(), hi.into()); if let ExecMode::Trace = self.mode { // split gl of the poseidon machine self.proc.submachine("split_gl").add_operation( @@ -1947,6 +2154,13 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); } + submachine_event!( + main_poseidon_gl, + input_ptr.into_fe(), + output_ptr.into_fe(), + self.step.into() + ); + main_event!(poseidon_gl,); vec![] } "poseidon2_gl" => { @@ -1976,6 +2190,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc.set_mem(output_ptr + i as u32 * 4, v, 0, 0); // TODO: step/selector for poseidon2 }); + // TODO: need to add memory read/write events for poseidon2 to work + submachine_event!( + main_poseidon2_gl, + input_ptr.into(), + output_ptr.into(), + self.step.into() + ); + main_event!(poseidon2_gl,); vec![] } "affine_256" => { @@ -2001,6 +2223,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { .set_reg(®ister_by_idx(i + 8), Elem::Field(result.1[i])) }); + // TODO: main_arith event + main_event!(affine_256,); vec![] } "mod_256" => { @@ -2021,6 +2245,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc .set_reg(®ister_by_idx(i), Elem::Field(result[i])) }); + + // TODO: main_arith event + main_event!(mod_256,); vec![] } "ec_add" => { @@ -2049,6 +2276,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { .set_reg(®ister_by_idx(i + 8), Elem::Field(result.1[i])) }); + // TODO: main_arith event + main_event!(ec_add,); vec![] } "ec_double" => { @@ -2071,6 +2300,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { .set_reg(®ister_by_idx(i + 8), Elem::Field(result.1[i])) }); + // TODO: main_arith event + main_event!(ec_double,); vec![] } "commit_public" => { @@ -2087,6 +2318,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { &[], ); } + + submachine_event!(main_publics, idx.into_fe(), limb.into_fe()); + main_event!(commit_public,); vec![] } instr => { From 3c28b65b0e5cad9d686c672771b814b10972e6b4 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Fri, 6 Dec 2024 16:00:35 -0300 Subject: [PATCH 02/35] placeholder --- riscv-executor/src/lib.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index a84090b473..2d8faa4025 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -895,6 +895,22 @@ mod builder { // fill up main trace to degree self.extend_rows(main_degree); + // generate witness for submachines + // ---------------------------- + for (m, events) in self.trace.submachine_events { + match m { + // MachineInstance::main_binary => {} + // MachineInstance::main_shift => {} + // MachineInstance::main_memory => todo!(), + // MachineInstance::main_regs => todo!(), + // MachineInstance::main_publics => todo!(), + // MachineInstance::main_split_gl => todo!(), + // MachineInstance::main_poseidon_gl => todo!(), + // MachineInstance::main_poseidon2_gl => todo!(), + _ => {} + } + } + // add submachine traces to main trace // ---------------------------- for mut machine in self.submachines.into_values().map(|m| m.into_inner()) { From eb124a936acb7c015fae48e9d432f8692590244b Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Fri, 6 Dec 2024 16:33:23 -0300 Subject: [PATCH 03/35] change submachine.add_operation to just take `&[F]` --- riscv-executor/src/lib.rs | 206 ++++++++++------------- riscv-executor/src/memory.rs | 18 +- riscv-executor/src/submachines.rs | 266 ++++++++++++------------------ 3 files changed, 197 insertions(+), 293 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 2d8faa4025..57537d1790 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -141,10 +141,6 @@ impl Elem { Self::Field(F::from(value)) } - pub fn from_u64_as_fe_unchecked(value: u64) -> Self { - Self::Field(F::from(value)) - } - pub fn from_bool_as_fe(value: bool) -> Self { if value { Self::Field(F::one()) @@ -188,13 +184,6 @@ impl Elem { self.bin().try_into().unwrap() } - fn fe(&self) -> F { - match self { - Self::Binary(_) => panic!(), - Self::Field(f) => *f, - } - } - fn is_zero(&self) -> bool { match self { Self::Binary(b) => *b == 0, @@ -338,7 +327,7 @@ pub struct ExecutionTrace { submachine_events: HashMap>>, /// witness columns - cols: HashMap>>, + cols: HashMap>, } impl ExecutionTrace { @@ -351,7 +340,7 @@ impl ExecutionTrace { let cols: HashMap = witness_cols .into_iter() .filter(|n| n.starts_with("main::")) - .map(|n| (n, vec![Elem::Field(F::zero()), Elem::Field(F::zero())])) + .map(|n| (n, vec![F::zero(), F::zero()])) .collect(); ExecutionTrace { @@ -377,9 +366,8 @@ impl ExecutionTrace { } /// transpose the register write operations into value columns - fn generate_registers_trace(&self) -> Vec<(String, Vec>)> { - let mut reg_values: HashMap<&str, Vec>> = - HashMap::with_capacity(self.reg_map.len()); + fn generate_registers_trace(&self) -> Vec<(String, Vec)> { + let mut reg_values: HashMap<&str, Vec> = HashMap::with_capacity(self.reg_map.len()); let mut rows = self.replay(); while let Some(row) = rows.next_row() { @@ -387,7 +375,7 @@ impl ExecutionTrace { reg_values .entry(reg_name) .or_default() - .push(Elem::Field(row[index as usize])); + .push(row[index as usize]); } } @@ -721,7 +709,7 @@ mod builder { .cols .get_mut(name) .unwrap_or_else(|| panic!("col not found: {name}")); - *col.get_mut(idx).unwrap() = value; + *col.get_mut(idx).unwrap() = value.into_fe(); } } @@ -732,7 +720,7 @@ mod builder { .cols .get_mut(name) .unwrap_or_else(|| panic!("col not found: {name}")); - *col.last_mut().unwrap() = value; + *col.last_mut().unwrap() = value.into_fe(); } } @@ -743,7 +731,7 @@ mod builder { .cols .get(name) .unwrap_or_else(|| panic!("col not found: {name}")); - *col.last().unwrap() + Elem::Field(*col.last().unwrap()) } else { Elem::Field(F::zero()) } @@ -751,10 +739,7 @@ mod builder { pub fn push_row(&mut self) { if let ExecMode::Trace = self.mode { - self.trace - .cols - .values_mut() - .for_each(|v| v.push(Elem::Field(0.into()))); + self.trace.cols.values_mut().for_each(|v| v.push(F::zero())); } } @@ -897,19 +882,19 @@ mod builder { // generate witness for submachines // ---------------------------- - for (m, events) in self.trace.submachine_events { - match m { - // MachineInstance::main_binary => {} - // MachineInstance::main_shift => {} - // MachineInstance::main_memory => todo!(), - // MachineInstance::main_regs => todo!(), - // MachineInstance::main_publics => todo!(), - // MachineInstance::main_split_gl => todo!(), - // MachineInstance::main_poseidon_gl => todo!(), - // MachineInstance::main_poseidon2_gl => todo!(), - _ => {} - } - } + // for (m, _events) in self.trace.submachine_events { + // match m { + // // MachineInstance::main_binary => {} + // // MachineInstance::main_shift => {} + // // MachineInstance::main_memory => todo!(), + // // MachineInstance::main_regs => todo!(), + // // MachineInstance::main_publics => todo!(), + // // MachineInstance::main_split_gl => todo!(), + // // MachineInstance::main_poseidon_gl => todo!(), + // // MachineInstance::main_poseidon2_gl => todo!(), + // _ => {} + // } + // } // add submachine traces to main trace // ---------------------------- @@ -960,16 +945,7 @@ mod builder { trace_len: self.trace.len, memory: self.mem, memory_accesses: std::mem::take(&mut self.trace.mem_ops), - trace: self - .trace - .cols - .into_iter() - // convert Elem into F - .map(|(name, elems)| { - let fes = elems.into_iter().map(|e| e.into_fe()).collect(); - (name, fes) - }) - .collect(), + trace: self.trace.cols, register_memory: self.reg_mem.for_bootloader(), } } @@ -1165,7 +1141,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { if let ExecMode::Trace = self.mode { self.proc .regs_machine - .read(self.step + step_offset, reg, val, selector); + .read(self.step + step_offset, reg, val.into_fe(), selector); } val } @@ -1179,7 +1155,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { if let ExecMode::Trace = self.mode { self.proc .regs_machine - .write(self.step + step_offset, reg, val, selector); + .write(self.step + step_offset, reg, val.into_fe(), selector); } self.proc.set_reg_mem(reg, val); } @@ -1911,12 +1887,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp4_col, Elem::from_u32_as_fe(hi)); if let ExecMode::Trace = self.mode { - self.proc.submachine("split_gl").add_operation( - "split_gl", - &[hi.into(), lo.into()], - Some(0), - &[], - ); + let selector = 0; + self.proc.submachine("split_gl").add_operation(&[ + lo.into(), + hi.into(), + selector.into(), + ]); } submachine_event!(main_split_gl, r.into(), lo.into(), hi.into()); @@ -1960,12 +1936,13 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); if let ExecMode::Trace = self.mode { - self.proc.submachine("binary").add_operation( - name, - &[val1, val2_offset, r.into()], - Some(sel), - &[], - ); + self.proc.submachine("binary").add_operation(&[ + op_id.into(), + val1.into_fe(), + val2_offset.into_fe(), + r.into(), + sel.into(), + ]); } self.reg_write(3, write_reg, r.into(), 3); @@ -2010,12 +1987,13 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp3_col, Elem::from_u32_as_fe(r)); if let ExecMode::Trace = self.mode { - self.proc.submachine("shift").add_operation( - name, - &[val1, val2_offset, r.into()], - Some(sel), - &[], - ); + self.proc.submachine("shift").add_operation(&[ + op_id.into(), + val1.into_fe(), + val2_offset.into_fe(), + r.into(), + sel.into(), + ]); } Vec::new() @@ -2038,12 +2016,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(XX_inv, Elem::Field(inv)); if let ExecMode::Trace = self.mode { - self.proc.submachine("split_gl").add_operation( - "split_gl", - &[high_inv.into(), low_inv.into()], - Some(0), - &[], - ); + let sel = 0; + self.proc.submachine("split_gl").add_operation(&[ + low_inv.into(), + high_inv.into(), + sel.into(), + ]); } submachine_event!(main_split_gl, inv, low_inv.into(), high_inv.into()); @@ -2071,12 +2049,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp4_col, Elem::from_u32_as_fe(hi)); if let ExecMode::Trace = self.mode { - self.proc.submachine("split_gl").add_operation( - "split_gl", - &[hi.into(), lo.into()], - Some(0), - &[], - ); + let sel = 0; + self.proc.submachine("split_gl").add_operation(&[ + lo.into(), + hi.into(), + sel.into(), + ]); } submachine_event!(main_split_gl, value.into(), lo.into(), hi.into()); @@ -2132,42 +2110,39 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { submachine_event!(main_split_gl, *v, lo.into(), hi.into()); if let ExecMode::Trace = self.mode { // split gl of the poseidon machine - self.proc.submachine("split_gl").add_operation( - "split_gl", - &[hi.into(), lo.into()], - Some(1), - &[], - ); + let sel = 1; + self.proc.submachine("split_gl").add_operation(&[ + lo.into(), + hi.into(), + sel.into(), + ]); } }); if let ExecMode::Trace = self.mode { - self.proc.submachine("poseidon_gl").add_operation( - "poseidon_gl", - &[ - input_ptr, - output_ptr, - self.step.into(), - Elem::Field(inputs[0]), - Elem::Field(inputs[1]), - Elem::Field(inputs[2]), - Elem::Field(inputs[3]), - Elem::Field(inputs[4]), - Elem::Field(inputs[5]), - Elem::Field(inputs[6]), - Elem::Field(inputs[7]), - Elem::Field(inputs[8]), - Elem::Field(inputs[9]), - Elem::Field(inputs[10]), - Elem::Field(inputs[11]), - Elem::Field(outputs[0]), - Elem::Field(outputs[1]), - Elem::Field(outputs[2]), - Elem::Field(outputs[3]), - ], - Some(0), - &[], - ); + let sel = 0; + self.proc.submachine("poseidon_gl").add_operation(&[ + input_ptr.into_fe(), + output_ptr.into_fe(), + self.step.into(), + inputs[0], + inputs[1], + inputs[2], + inputs[3], + inputs[4], + inputs[5], + inputs[6], + inputs[7], + inputs[8], + inputs[9], + inputs[10], + inputs[11], + outputs[0], + outputs[1], + outputs[2], + outputs[3], + sel.into(), + ]); } submachine_event!( @@ -2327,12 +2302,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp2_col, limb); log::debug!("Committing public: idx={idx}, limb={limb}"); if let ExecMode::Trace = self.mode { - self.proc.submachine("publics").add_operation( - "access", - &[idx, limb], - None, - &[], - ); + self.proc + .submachine("publics") + .add_operation(&[idx.into_fe(), limb.into_fe()]); } submachine_event!(main_publics, idx.into_fe(), limb.into_fe()); diff --git a/riscv-executor/src/memory.rs b/riscv-executor/src/memory.rs index c29fc5cf64..8a1b2ccd5d 100644 --- a/riscv-executor/src/memory.rs +++ b/riscv-executor/src/memory.rs @@ -1,13 +1,11 @@ use powdr_number::FieldElement; -use crate::Elem; - #[derive(Debug, Eq, PartialEq)] /// Order of fields matter: will be ordered by addr then step. struct Op { addr: u32, step: u32, - value: Elem, + value: F, write: bool, // each machine that's called via permutation has a selector array, with one entry per incoming permutation. // This is the idx assigned to the `link` triggering the memory operation. @@ -37,7 +35,7 @@ impl MemoryMachine { } } - pub fn write(&mut self, step: u32, addr: u32, val: Elem, selector_idx: u32) { + pub fn write(&mut self, step: u32, addr: u32, val: F, selector_idx: u32) { self.ops.push(Op { addr, step, @@ -47,7 +45,7 @@ impl MemoryMachine { }); } - pub fn read(&mut self, step: u32, addr: u32, val: Elem, selector_idx: u32) { + pub fn read(&mut self, step: u32, addr: u32, val: F, selector_idx: u32) { self.ops.push(Op { addr, step, @@ -61,7 +59,7 @@ impl MemoryMachine { self.ops.len() as u32 } - pub fn take_cols(mut self, len: u32) -> Vec<(String, Vec>)> { + pub fn take_cols(mut self, len: u32) -> Vec<(String, Vec)> { assert!( len >= self.len(), "trying to take less rows than memory ops" @@ -132,11 +130,7 @@ impl MemoryMachine { // extend rows if needed let last_step = self.ops.last().map(|op| op.step).unwrap_or(0); let last_addr = self.ops.last().map(|op| op.addr).unwrap_or(0); - let last_value = self - .ops - .last() - .map(|op| op.value) - .unwrap_or(Elem::Field(0.into())); + let last_value = self.ops.last().map(|op| op.value).unwrap_or(0.into()); if self.len() < len { // addr and value are repeated cols[Addr as usize].1.resize(len as usize, last_addr.into()); @@ -145,7 +139,7 @@ impl MemoryMachine { cols[Step as usize].1.extend( (last_step + 1..) .take((len - self.len()) as usize) - .map(|x| Elem::from_u32_as_fe(x)), + .map(|x| F::from(x)), ); // rest are zero cols[Change as usize].1.resize(len as usize, 0.into()); diff --git a/riscv-executor/src/submachines.rs b/riscv-executor/src/submachines.rs index a5aa96d85b..2ab008fb01 100644 --- a/riscv-executor/src/submachines.rs +++ b/riscv-executor/src/submachines.rs @@ -1,6 +1,5 @@ use super::decompose_lower32; use super::poseidon_gl; -use super::Elem; use powdr_number::{FieldElement, LargeInt}; use std::collections::HashMap; @@ -12,13 +11,7 @@ trait SubmachineKind { /// Row block size const BLOCK_SIZE: u32; /// Add an operation to the submachine trace - fn add_operation( - trace: &mut SubmachineTrace, - name: &str, - args: &[Elem], - selector: Option, - submachines: &[&mut dyn Submachine], - ); + fn add_operation(trace: &mut SubmachineTrace, args: &[F]); /// Some machines need more than simply copying first block fn dummy_block_fix(_trace: &mut SubmachineTrace, _rows: u32) {} } @@ -44,20 +37,14 @@ pub trait Submachine { /// current number of rows fn len(&self) -> u32; /// add a new operation to the trace - fn add_operation( - &mut self, - name: &str, - args: &[Elem], - selector: Option, - submachines: &[&mut dyn Submachine], - ); + fn add_operation(&mut self, args: &[F]); /// apply final row overrides (needed because the trace is circular) fn final_row_override(&mut self); /// push a dummy block to the trace fn push_dummy_block(&mut self, machine_max_degree: usize); /// Consume the trace returning a list of columns. /// Should be called only once. - fn take_cols(&mut self) -> Vec<(String, Vec>)>; + fn take_cols(&mut self) -> Vec<(String, Vec)>; } /// Concrete implementation of the Submachine trait @@ -93,14 +80,8 @@ impl Submachine for SubmachineImpl self.trace.len() } - fn add_operation( - &mut self, - name: &str, - args: &[Elem], - selector: Option, - submachines: &[&mut dyn Submachine], - ) { - M::add_operation(&mut self.trace, name, args, selector, submachines); + fn add_operation(&mut self, args: &[F]) { + M::add_operation(&mut self.trace, args); } fn final_row_override(&mut self) { @@ -115,7 +96,7 @@ impl Submachine for SubmachineImpl M::dummy_block_fix(&mut self.trace, dummy_size); } - fn take_cols(&mut self) -> Vec<(String, Vec>)> { + fn take_cols(&mut self) -> Vec<(String, Vec)> { self.trace .take_cols() .map(|(k, v)| (format!("{}::{}", self.namespace, k), v)) @@ -125,14 +106,14 @@ impl Submachine for SubmachineImpl /// Holds the submachine trace as a list of columns and a last row override struct SubmachineTrace { - cols: HashMap>>, + cols: HashMap>, // the trace is circular, so for the first block, we can only set the // previous row after the whole trace is built - last_row_overrides: HashMap>>, + last_row_overrides: HashMap>, } impl SubmachineTrace { - fn new(cols: HashMap>>) -> Self { + fn new(cols: HashMap>) -> Self { assert!(!cols.is_empty(), "machine with no witness columns"); SubmachineTrace { last_row_overrides: cols.keys().map(|n| (n.clone(), None)).collect(), @@ -145,7 +126,7 @@ impl SubmachineTrace { } /// set the value of a column in all rows of the current block - fn set_current_block(&mut self, size: u32, col: &str, value: Elem) { + fn set_current_block(&mut self, size: u32, col: &str, value: F) { for i in 0..size { let idx = self.len() - i - 1; *self @@ -158,12 +139,12 @@ impl SubmachineTrace { } /// set the value of a column in the current - fn set_current_row(&mut self, col: &str, value: Elem) { + fn set_current_row(&mut self, col: &str, value: F) { *self.cols.get_mut(col).unwrap().last_mut().unwrap() = value; } /// set the value of a column in the last row of the complete trace - fn set_final_row(&mut self, col: &str, value: Elem) { + fn set_final_row(&mut self, col: &str, value: F) { *self.last_row_overrides.get_mut(col).unwrap() = Some(value); } @@ -178,9 +159,7 @@ impl SubmachineTrace { /// add new row of zeroes to the trce fn push_row(&mut self) { - self.cols - .values_mut() - .for_each(|v| v.push(Elem::Field(0.into()))); + self.cols.values_mut().for_each(|v| v.push(0.into())); } /// Push a dummy block to the trace. @@ -204,29 +183,9 @@ impl SubmachineTrace { } /// consume the trace, returning the columns - fn take_cols(&mut self) -> impl Iterator>)> { + fn take_cols(&mut self) -> impl Iterator)> { std::mem::take(&mut self.cols).into_iter() } - - /// helper for debugging purposes only - #[allow(dead_code)] - fn print_row_idx(&self, idx: i64) { - let idx: usize = if idx < 0 { - (self.len() as i64 + idx) as usize - } else { - idx as usize - }; - - print!("Row {idx} - "); - for (col, values) in &self.cols { - let val = format!( - "{col}: {:x}", - values[idx].into_fe().to_integer().try_into_u64().unwrap() - ); - print!("{val:>15}, "); - } - println!(); - } } pub struct BinaryMachine; @@ -235,31 +194,20 @@ impl SubmachineKind for BinaryMachine { const SELECTORS: &'static str = "sel"; const BLOCK_SIZE: u32 = 4; - fn add_operation( - trace: &mut SubmachineTrace, - op: &str, - args: &[Elem], - selector: Option, - _submachines: &[&mut dyn Submachine], - ) { - let [a, b, c] = args[..] else { + fn add_operation(trace: &mut SubmachineTrace, args: &[F]) { + let [op_id, a, b, c, selector] = args[..] else { panic!(); }; - let op_id: Elem = match op { - "and" => 0, - "or" => 1, - "xor" => 2, - _ => unreachable!(), - } - .into(); - // decompose A - let (a1, a2, a3, a4, _sign) = decompose_lower32(a.u().into()); + let (a1, a2, a3, a4, _sign) = + decompose_lower32(a.to_integer().try_into_u32().unwrap().into()); // decompose B - let (b1, b2, b3, b4, _sign) = decompose_lower32(b.u().into()); + let (b1, b2, b3, b4, _sign) = + decompose_lower32(b.to_integer().try_into_u32().unwrap().into()); // decompose C - let (c1, c2, c3, c4, _sign) = decompose_lower32(c.u().into()); + let (c1, c2, c3, c4, _sign) = + decompose_lower32(c.to_integer().try_into_u32().unwrap().into()); // set last row of the previous block if trace.len() > 0 { @@ -281,9 +229,9 @@ impl SubmachineKind for BinaryMachine { trace.set_current_row("A_byte", (a2 as u32).into()); trace.set_current_row("B_byte", (b2 as u32).into()); trace.set_current_row("C_byte", (c2 as u32).into()); - trace.set_current_row("A", (a.u() & 0xff).into()); - trace.set_current_row("B", (b.u() & 0xff).into()); - trace.set_current_row("C", (c.u() & 0xff).into()); + trace.set_current_row("A", (a.to_integer().try_into_u32().unwrap() & 0xff).into()); + trace.set_current_row("B", (b.to_integer().try_into_u32().unwrap() & 0xff).into()); + trace.set_current_row("C", (c.to_integer().try_into_u32().unwrap() & 0xff).into()); trace.push_row(); trace.set_current_row("operation_id", op_id); @@ -291,9 +239,18 @@ impl SubmachineKind for BinaryMachine { trace.set_current_row("A_byte", (a3 as u32).into()); trace.set_current_row("B_byte", (b3 as u32).into()); trace.set_current_row("C_byte", (c3 as u32).into()); - trace.set_current_row("A", (a.u() & 0xffff).into()); - trace.set_current_row("B", (b.u() & 0xffff).into()); - trace.set_current_row("C", (c.u() & 0xffff).into()); + trace.set_current_row( + "A", + (a.to_integer().try_into_u32().unwrap() & 0xffff).into(), + ); + trace.set_current_row( + "B", + (b.to_integer().try_into_u32().unwrap() & 0xffff).into(), + ); + trace.set_current_row( + "C", + (c.to_integer().try_into_u32().unwrap() & 0xffff).into(), + ); trace.push_row(); trace.set_current_row("operation_id", op_id); @@ -301,9 +258,18 @@ impl SubmachineKind for BinaryMachine { trace.set_current_row("A_byte", (a4 as u32).into()); trace.set_current_row("B_byte", (b4 as u32).into()); trace.set_current_row("C_byte", (c4 as u32).into()); - trace.set_current_row("A", (a.u() & 0xffffff).into()); - trace.set_current_row("B", (b.u() & 0xffffff).into()); - trace.set_current_row("C", (c.u() & 0xffffff).into()); + trace.set_current_row( + "A", + (a.to_integer().try_into_u32().unwrap() & 0xffffff).into(), + ); + trace.set_current_row( + "B", + (b.to_integer().try_into_u32().unwrap() & 0xffffff).into(), + ); + trace.set_current_row( + "C", + (c.to_integer().try_into_u32().unwrap() & 0xffffff).into(), + ); trace.push_row(); trace.set_current_row("operation_id", op_id); @@ -312,7 +278,7 @@ impl SubmachineKind for BinaryMachine { trace.set_current_row("C", c); // latch row: set selector trace.set_current_row( - &format!("{}[{}]", Self::SELECTORS, selector.unwrap()), + &format!("{}[{}]", Self::SELECTORS, selector.try_into_i32().unwrap()), 1.into(), ); } @@ -324,34 +290,26 @@ impl SubmachineKind for ShiftMachine { const SELECTORS: &'static str = "sel"; const BLOCK_SIZE: u32 = 4; - fn add_operation( - trace: &mut SubmachineTrace, - op: &str, - args: &[Elem], - selector: Option, - _submachines: &[&mut dyn Submachine], - ) { - let [a, b, c] = args[..] else { + fn add_operation(trace: &mut SubmachineTrace, args: &[F]) { + let [op_id, a, b, c, selector] = args[..] else { panic!(); }; - let op_id: Elem; let mut shl = 0; let mut shr = 0; - match op { - "shl" => { - op_id = 0.into(); - shl = b.u(); + match op_id.try_into_i32().unwrap() { + 0 => { + shl = b.to_integer().try_into_u32().unwrap(); } - "shr" => { - op_id = 1.into(); - shr = b.u(); + 1 => { + shr = b.to_integer().try_into_u32().unwrap(); } _ => unreachable!(), }; // decompose A - let (b1, b2, b3, b4, _sign) = decompose_lower32(a.u().into()); + let (b1, b2, b3, b4, _sign) = + decompose_lower32(a.to_integer().try_into_u32().unwrap().into()); // set last row of the previous block if trace.len() > 0 { @@ -374,7 +332,7 @@ impl SubmachineKind for ShiftMachine { let c_part_factor = (b2 as u32) << 8; let c_part = ((c_part_factor << shl) >> shr).into(); trace.set_current_row("C_part", c_part); - let a_row = a.u() & 0xff; + let a_row = a.to_integer().try_into_u32().unwrap() & 0xff; trace.set_current_row("A", a_row.into()); trace.set_current_row("B", b); trace.set_current_row("B_next", b); @@ -387,7 +345,7 @@ impl SubmachineKind for ShiftMachine { let c_part_factor = (b3 as u32) << 16; let c_part = ((c_part_factor << shl) >> shr).into(); trace.set_current_row("C_part", c_part); - let a_row = a.u() & 0xffff; + let a_row = a.to_integer().try_into_u32().unwrap() & 0xffff; trace.set_current_row("A", a_row.into()); trace.set_current_row("B", b); trace.set_current_row("B_next", b); @@ -400,7 +358,7 @@ impl SubmachineKind for ShiftMachine { let c_part_factor = (b4 as u32) << 24; let c_part = ((c_part_factor << shl) >> shr).into(); trace.set_current_row("C_part", c_part); - let a_row = a.u() & 0xffffff; + let a_row = a.to_integer().try_into_u32().unwrap() & 0xffffff; trace.set_current_row("A", a_row.into()); trace.set_current_row("B", b); trace.set_current_row("B_next", b); @@ -412,7 +370,7 @@ impl SubmachineKind for ShiftMachine { trace.set_current_row("B", b); trace.set_current_row("C", c); trace.set_current_row( - &format!("{}[{}]", Self::SELECTORS, selector.unwrap()), + &format!("{}[{}]", Self::SELECTORS, selector.try_into_i32().unwrap()), 1.into(), ); } @@ -424,22 +382,16 @@ impl SubmachineKind for SplitGlMachine { const SELECTORS: &'static str = "sel"; const BLOCK_SIZE: u32 = 8; - fn add_operation( - trace: &mut SubmachineTrace, - _op: &str, - args: &[Elem], - selector: Option, - _submachines: &[&mut dyn Submachine], - ) { - let [output_hi, output_low] = args[..] else { + fn add_operation(trace: &mut SubmachineTrace, args: &[F]) { + let [output_lo, output_hi, selector] = args[..] else { panic!(); }; - let lo = output_low.u() as u64; - let hi = output_hi.u() as u64; + let lo = output_lo.to_integer().try_into_u32().unwrap() as u64; + let hi = output_hi.to_integer().try_into_u32().unwrap() as u64; - fn hi_and_lo(hi: u64, lo: u64) -> Elem { - Elem::from_u64_as_fe_unchecked((hi << 32) | lo) + fn hi_and_lo(hi: u64, lo: u64) -> F { + ((hi << 32) | lo).into() } let (b0, b1, b2, b3, _) = decompose_lower32(lo as i64); @@ -481,9 +433,9 @@ impl SubmachineKind for SplitGlMachine { // split_gl needs 8 rows: // 0 trace.push_row(); - trace.set_current_row("output_low", Elem::from_u64_as_fe_unchecked(lo & 0xff)); + trace.set_current_row("output_low", (lo & 0xff).into()); trace.set_current_row("output_high", 0.into()); - trace.set_current_row("in_acc", Elem::from_u64_as_fe_unchecked(lo & 0xff)); + trace.set_current_row("in_acc", (lo & 0xff).into()); trace.set_current_row("bytes", b[1].into()); trace.set_current_row("lt", lt[1].into()); trace.set_current_row("gt", gt[1].into()); @@ -491,9 +443,9 @@ impl SubmachineKind for SplitGlMachine { // 1 trace.push_row(); - trace.set_current_row("output_low", Elem::from_u64_as_fe_unchecked(lo & 0xffff)); + trace.set_current_row("output_low", (lo & 0xffff).into()); trace.set_current_row("output_high", 0.into()); - trace.set_current_row("in_acc", Elem::from_u64_as_fe_unchecked(lo & 0xffff)); + trace.set_current_row("in_acc", (lo & 0xffff).into()); trace.set_current_row("bytes", b[2].into()); trace.set_current_row("lt", lt[2].into()); trace.set_current_row("gt", gt[2].into()); @@ -501,9 +453,9 @@ impl SubmachineKind for SplitGlMachine { // 2 trace.push_row(); - trace.set_current_row("output_low", Elem::from_u64_as_fe_unchecked(lo & 0xffffff)); + trace.set_current_row("output_low", (lo & 0xffffff).into()); trace.set_current_row("output_high", 0.into()); - trace.set_current_row("in_acc", Elem::from_u64_as_fe_unchecked(lo & 0xffffff)); + trace.set_current_row("in_acc", (lo & 0xffffff).into()); trace.set_current_row("bytes", b[3].into()); trace.set_current_row("lt", lt[3].into()); trace.set_current_row("gt", gt[3].into()); @@ -511,9 +463,9 @@ impl SubmachineKind for SplitGlMachine { // 3 trace.push_row(); - trace.set_current_row("output_low", Elem::from_u64_as_fe_unchecked(lo)); + trace.set_current_row("output_low", lo.into()); trace.set_current_row("output_high", 0.into()); - trace.set_current_row("in_acc", Elem::from_u64_as_fe_unchecked(lo)); + trace.set_current_row("in_acc", lo.into()); trace.set_current_row("bytes", b[4].into()); trace.set_current_row("lt", lt[4].into()); trace.set_current_row("gt", gt[4].into()); @@ -521,8 +473,8 @@ impl SubmachineKind for SplitGlMachine { // 4 trace.push_row(); - trace.set_current_row("output_low", Elem::from_u64_as_fe_unchecked(lo)); - trace.set_current_row("output_high", Elem::from_u64_as_fe_unchecked(hi & 0xff)); + trace.set_current_row("output_low", lo.into()); + trace.set_current_row("output_high", (hi & 0xff).into()); trace.set_current_row("in_acc", hi_and_lo(hi & 0xff, lo)); trace.set_current_row("bytes", b[5].into()); trace.set_current_row("lt", lt[5].into()); @@ -531,8 +483,8 @@ impl SubmachineKind for SplitGlMachine { // 5 trace.push_row(); - trace.set_current_row("output_low", Elem::from_u64_as_fe_unchecked(lo)); - trace.set_current_row("output_high", Elem::from_u64_as_fe_unchecked(hi & 0xffff)); + trace.set_current_row("output_low", lo.into()); + trace.set_current_row("output_high", (hi & 0xffff).into()); trace.set_current_row("in_acc", hi_and_lo(hi & 0xffff, lo)); trace.set_current_row("bytes", b[6].into()); trace.set_current_row("lt", lt[6].into()); @@ -541,8 +493,8 @@ impl SubmachineKind for SplitGlMachine { // 6 trace.push_row(); - trace.set_current_row("output_low", Elem::from_u64_as_fe_unchecked(lo)); - trace.set_current_row("output_high", Elem::from_u64_as_fe_unchecked(hi & 0xffffff)); + trace.set_current_row("output_low", (lo).into()); + trace.set_current_row("output_high", (hi & 0xffffff).into()); trace.set_current_row("in_acc", hi_and_lo(hi & 0xffffff, lo)); trace.set_current_row("bytes", b[7].into()); trace.set_current_row("lt", lt[7].into()); @@ -552,11 +504,11 @@ impl SubmachineKind for SplitGlMachine { // 7: bytes/lt/gt/was_lt are set by the next row trace.push_row(); trace.set_current_row( - &format!("{}[{}]", Self::SELECTORS, selector.unwrap()), + &format!("{}[{}]", Self::SELECTORS, selector.try_into_i32().unwrap()), 1.into(), ); - trace.set_current_row("output_low", Elem::from_u64_as_fe_unchecked(lo)); - trace.set_current_row("output_high", Elem::from_u64_as_fe_unchecked(hi)); + trace.set_current_row("output_low", lo.into()); + trace.set_current_row("output_high", hi.into()); trace.set_current_row("in_acc", hi_and_lo(hi, lo)); } } @@ -565,29 +517,22 @@ pub struct PublicsMachine; impl SubmachineKind for PublicsMachine { const SELECTORS: &'static str = ""; const BLOCK_SIZE: u32 = 1; - fn add_operation( - trace: &mut SubmachineTrace, - _op: &str, - args: &[Elem], - selector: Option, - _submachines: &[&mut dyn Submachine], - ) { - assert!(selector.is_none()); + fn add_operation(trace: &mut SubmachineTrace, args: &[F]) { let [addr, value] = args[..] else { panic!(); }; assert!( - addr.u() < 8, + addr.to_integer().try_into_u32().unwrap() < 8, "publics machine only supports 8 public values" ); - while addr.u() >= trace.len() { + while addr.to_integer().try_into_u32().unwrap() >= trace.len() { trace.push_row(); } *trace .cols .get_mut("value") .unwrap() - .get_mut(addr.u() as usize) + .get_mut(addr.to_integer().try_into_u32().unwrap() as usize) .unwrap() = value; } } @@ -598,13 +543,7 @@ impl SubmachineKind for PoseidonGlMachine { const SELECTORS: &'static str = "sel"; const BLOCK_SIZE: u32 = 31; // full rounds + partial rounds + 1 - fn add_operation( - trace: &mut SubmachineTrace, - _op: &str, - args: &[Elem], - selector: Option, - _submachines: &[&mut dyn Submachine], - ) { + fn add_operation(trace: &mut SubmachineTrace, args: &[F]) { const STATE_SIZE: usize = 12; const OUTPUT_SIZE: usize = 4; @@ -651,22 +590,22 @@ impl SubmachineKind for PoseidonGlMachine { "x7[9]", "x7[10]", "x7[11]", ]; - let [input_addr, output_addr, time_step] = args[0..3] else { + let [input_addr, output_addr, time_step, selector] = args[0..3] else { panic!() }; let input = args[3..3 + STATE_SIZE].to_vec(); let output = args[3 + STATE_SIZE..3 + STATE_SIZE + OUTPUT_SIZE].to_vec(); - let mut state: Vec = input.iter().map(|e| e.into_fe()).collect(); + let mut state: Vec = input.clone(); for row in 0..(Self::BLOCK_SIZE - 1) as usize { trace.push_row(); for i in 0..STATE_SIZE { - trace.set_current_row(STATE_COLS[i], Elem::Field(state[i])); + trace.set_current_row(STATE_COLS[i], state[i]); } // memory read/write columns if row < STATE_SIZE { - let v = input[row].fe().to_integer().try_into_u64().unwrap(); + let v = input[row].to_integer().try_into_u64().unwrap(); let hi = (v >> 32) as u32; let lo = (v & 0xffffffff) as u32; trace.set_current_row("do_mload", 1.into()); @@ -674,7 +613,6 @@ impl SubmachineKind for PoseidonGlMachine { trace.set_current_row("word_high", hi.into()); } else if row < STATE_SIZE + OUTPUT_SIZE { let v = output[row - STATE_SIZE] - .fe() .to_integer() .try_into_u64() .unwrap(); @@ -695,8 +633,8 @@ impl SubmachineKind for PoseidonGlMachine { let a = state[i] + F::from(poseidon_gl::ROUND_CONSTANTS[i][row]); let x3 = a.pow(3.into()); let x7 = x3.pow(2.into()) * a; - trace.set_current_row(X3_COLS[i], Elem::Field(x3)); - trace.set_current_row(X7_COLS[i], Elem::Field(x7)); + trace.set_current_row(X3_COLS[i], x3); + trace.set_current_row(X7_COLS[i], x7); if i == 0 || is_full { state[i] = x7; } else { @@ -720,10 +658,10 @@ impl SubmachineKind for PoseidonGlMachine { let a = state[i]; let x3 = a.pow(3.into()); let x7 = x3.pow(2.into()) * a; - trace.set_current_row(X3_COLS[i], Elem::Field(x3)); - trace.set_current_row(X7_COLS[i], Elem::Field(x7)); + trace.set_current_row(X3_COLS[i], x3); + trace.set_current_row(X7_COLS[i], x7); // set output - trace.set_current_row(STATE_COLS[i], Elem::Field(state[i])); + trace.set_current_row(STATE_COLS[i], state[i]); } // these are the same in the whole block trace.set_current_block(Self::BLOCK_SIZE, "operation_id", 0.into()); @@ -733,14 +671,14 @@ impl SubmachineKind for PoseidonGlMachine { // set selector trace.set_current_block( Self::BLOCK_SIZE, - &format!("{}[{}]", Self::SELECTORS, selector.unwrap()), + &format!("{}[{}]", Self::SELECTORS, selector.try_into_i32().unwrap()), 1.into(), ); for i in 0..STATE_SIZE { trace.set_current_block(Self::BLOCK_SIZE, INPUT_COLS[i], input[i]); } for i in 0..OUTPUT_SIZE { - trace.set_current_block(Self::BLOCK_SIZE, OUTPUT_COLS[i], Elem::Field(state[i])); + trace.set_current_block(Self::BLOCK_SIZE, OUTPUT_COLS[i], state[i]); } } From e16ed82f38fcee5c7fb959bd4c1216f79c8ad0b0 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Mon, 9 Dec 2024 10:34:04 -0300 Subject: [PATCH 04/35] fix --- riscv-executor/src/lib.rs | 2 +- riscv-executor/src/submachines.rs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 57537d1790..7f7637ec79 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -2125,6 +2125,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { input_ptr.into_fe(), output_ptr.into_fe(), self.step.into(), + sel.into(), inputs[0], inputs[1], inputs[2], @@ -2141,7 +2142,6 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { outputs[1], outputs[2], outputs[3], - sel.into(), ]); } diff --git a/riscv-executor/src/submachines.rs b/riscv-executor/src/submachines.rs index 2ab008fb01..00281c7319 100644 --- a/riscv-executor/src/submachines.rs +++ b/riscv-executor/src/submachines.rs @@ -590,9 +590,10 @@ impl SubmachineKind for PoseidonGlMachine { "x7[9]", "x7[10]", "x7[11]", ]; - let [input_addr, output_addr, time_step, selector] = args[0..3] else { - panic!() + let [input_addr, output_addr, time_step, selector] = args[0..4] else { + panic!(); }; + let input = args[3..3 + STATE_SIZE].to_vec(); let output = args[3 + STATE_SIZE..3 + STATE_SIZE + OUTPUT_SIZE].to_vec(); From 1a616fcd31c411eba07f4fa30afcff792e03ff9c Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Mon, 9 Dec 2024 10:40:19 -0300 Subject: [PATCH 05/35] selector first arg --- riscv-executor/src/lib.rs | 16 ++++++++-------- riscv-executor/src/submachines.rs | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 7f7637ec79..c7a0b864bb 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -323,7 +323,7 @@ pub struct ExecutionTrace { main_events: Vec>, /// Calls into submachines. Each is a sequence of field elemements: the RHS values of the lookup followed by the selector idx (if the machine has it). - /// TODO: keep a flat Vec instead? individial events can be identified from the machine asm definition. + /// TODO: keep a flat Vec instead? individial events could be identified from the machine asm definition and operation id. submachine_events: HashMap>>, /// witness columns @@ -1889,9 +1889,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { if let ExecMode::Trace = self.mode { let selector = 0; self.proc.submachine("split_gl").add_operation(&[ + selector.into(), lo.into(), hi.into(), - selector.into(), ]); } @@ -1937,11 +1937,11 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { if let ExecMode::Trace = self.mode { self.proc.submachine("binary").add_operation(&[ + sel.into(), op_id.into(), val1.into_fe(), val2_offset.into_fe(), r.into(), - sel.into(), ]); } @@ -1988,11 +1988,11 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { if let ExecMode::Trace = self.mode { self.proc.submachine("shift").add_operation(&[ + sel.into(), op_id.into(), val1.into_fe(), val2_offset.into_fe(), r.into(), - sel.into(), ]); } @@ -2018,9 +2018,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { if let ExecMode::Trace = self.mode { let sel = 0; self.proc.submachine("split_gl").add_operation(&[ + sel.into(), low_inv.into(), high_inv.into(), - sel.into(), ]); } @@ -2051,9 +2051,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { if let ExecMode::Trace = self.mode { let sel = 0; self.proc.submachine("split_gl").add_operation(&[ + sel.into(), lo.into(), hi.into(), - sel.into(), ]); } @@ -2112,9 +2112,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { // split gl of the poseidon machine let sel = 1; self.proc.submachine("split_gl").add_operation(&[ + sel.into(), lo.into(), hi.into(), - sel.into(), ]); } }); @@ -2122,10 +2122,10 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { if let ExecMode::Trace = self.mode { let sel = 0; self.proc.submachine("poseidon_gl").add_operation(&[ + sel.into(), input_ptr.into_fe(), output_ptr.into_fe(), self.step.into(), - sel.into(), inputs[0], inputs[1], inputs[2], diff --git a/riscv-executor/src/submachines.rs b/riscv-executor/src/submachines.rs index 00281c7319..198f9aa17d 100644 --- a/riscv-executor/src/submachines.rs +++ b/riscv-executor/src/submachines.rs @@ -195,7 +195,7 @@ impl SubmachineKind for BinaryMachine { const BLOCK_SIZE: u32 = 4; fn add_operation(trace: &mut SubmachineTrace, args: &[F]) { - let [op_id, a, b, c, selector] = args[..] else { + let [selector, op_id, a, b, c] = args[..] else { panic!(); }; @@ -291,7 +291,7 @@ impl SubmachineKind for ShiftMachine { const BLOCK_SIZE: u32 = 4; fn add_operation(trace: &mut SubmachineTrace, args: &[F]) { - let [op_id, a, b, c, selector] = args[..] else { + let [selector, op_id, a, b, c] = args[..] else { panic!(); }; @@ -383,7 +383,7 @@ impl SubmachineKind for SplitGlMachine { const BLOCK_SIZE: u32 = 8; fn add_operation(trace: &mut SubmachineTrace, args: &[F]) { - let [output_lo, output_hi, selector] = args[..] else { + let [selector, output_lo, output_hi] = args[..] else { panic!(); }; @@ -590,7 +590,7 @@ impl SubmachineKind for PoseidonGlMachine { "x7[9]", "x7[10]", "x7[11]", ]; - let [input_addr, output_addr, time_step, selector] = args[0..4] else { + let [selector, input_addr, output_addr, time_step] = args[0..4] else { panic!(); }; From 9f65c6c3b0bead8a89311946c50f0bbd32f03613 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Tue, 10 Dec 2024 10:10:05 -0300 Subject: [PATCH 06/35] change interface a bit --- riscv-executor/src/lib.rs | 129 ++++++++++++++---------------- riscv-executor/src/submachines.rs | 99 +++++++++++++++-------- 2 files changed, 128 insertions(+), 100 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index c7a0b864bb..e47f36fa7c 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -1888,11 +1888,11 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { if let ExecMode::Trace = self.mode { let selector = 0; - self.proc.submachine("split_gl").add_operation(&[ - selector.into(), - lo.into(), - hi.into(), - ]); + self.proc.submachine("split_gl").add_operation( + Some(selector), + &[r.into(), lo.into(), hi.into()], + &[], + ); } submachine_event!(main_split_gl, r.into(), lo.into(), hi.into()); @@ -1911,7 +1911,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp1_col, val1); set_col!(tmp2_col, val2); - let (r, op_id, sel) = match name { + let (r, op_id, selector) = match name { "and" => { main_event!(and,); (val1.u() & val2_offset.u(), 0, 0) @@ -1936,13 +1936,16 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); if let ExecMode::Trace = self.mode { - self.proc.submachine("binary").add_operation(&[ - sel.into(), - op_id.into(), - val1.into_fe(), - val2_offset.into_fe(), - r.into(), - ]); + self.proc.submachine("binary").add_operation( + Some(selector), + &[ + op_id.into(), + val1.into_fe(), + val2_offset.into_fe(), + r.into(), + ], + &[], + ); } self.reg_write(3, write_reg, r.into(), 3); @@ -1960,7 +1963,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let write_reg = args[3].u(); let val2_offset: Elem = (val2.bin() + offset).into(); - let (r, op_id, sel) = match name { + let (r, op_id, selector) = match name { "shl" => { main_event!(shl,); (val1.u() << val2_offset.u(), 0, 0) @@ -1987,13 +1990,16 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp3_col, Elem::from_u32_as_fe(r)); if let ExecMode::Trace = self.mode { - self.proc.submachine("shift").add_operation(&[ - sel.into(), - op_id.into(), - val1.into_fe(), - val2_offset.into_fe(), - r.into(), - ]); + self.proc.submachine("shift").add_operation( + Some(selector), + &[ + op_id.into(), + val1.into_fe(), + val2_offset.into_fe(), + r.into(), + ], + &[], + ); } Vec::new() @@ -2016,12 +2022,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(XX_inv, Elem::Field(inv)); if let ExecMode::Trace = self.mode { - let sel = 0; - self.proc.submachine("split_gl").add_operation(&[ - sel.into(), - low_inv.into(), - high_inv.into(), - ]); + let selector = 0; + self.proc.submachine("split_gl").add_operation( + Some(selector), + &[inv, low_inv.into(), high_inv.into()], + &[], + ); } submachine_event!(main_split_gl, inv, low_inv.into(), high_inv.into()); @@ -2034,10 +2040,10 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let write_reg1 = args[1].u(); let write_reg2 = args[2].u(); - let value = val1.into_fe().to_integer(); + let value_fe = val1.into_fe(); // This instruction is only for Goldilocks, so the value must // fit into a u64. - let value = value.try_into_u64().unwrap(); + let value = value_fe.to_integer().try_into_u64().unwrap(); let lo = (value & 0xffffffff) as u32; let hi = (value >> 32) as u32; @@ -2049,12 +2055,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp4_col, Elem::from_u32_as_fe(hi)); if let ExecMode::Trace = self.mode { - let sel = 0; - self.proc.submachine("split_gl").add_operation(&[ - sel.into(), - lo.into(), - hi.into(), - ]); + let selector = 0; + self.proc.submachine("split_gl").add_operation( + Some(selector), + &[value_fe.into(), lo.into(), hi.into()], + &[], + ); } submachine_event!(main_split_gl, value.into(), lo.into(), hi.into()); @@ -2110,39 +2116,26 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { submachine_event!(main_split_gl, *v, lo.into(), hi.into()); if let ExecMode::Trace = self.mode { // split gl of the poseidon machine - let sel = 1; - self.proc.submachine("split_gl").add_operation(&[ - sel.into(), - lo.into(), - hi.into(), - ]); + let selector = 1; + self.proc.submachine("split_gl").add_operation( + Some(selector), + &[*v, lo.into(), hi.into()], + &[], + ); } }); if let ExecMode::Trace = self.mode { - let sel = 0; - self.proc.submachine("poseidon_gl").add_operation(&[ - sel.into(), - input_ptr.into_fe(), - output_ptr.into_fe(), - self.step.into(), - inputs[0], - inputs[1], - inputs[2], - inputs[3], - inputs[4], - inputs[5], - inputs[6], - inputs[7], - inputs[8], - inputs[9], - inputs[10], - inputs[11], - outputs[0], - outputs[1], - outputs[2], - outputs[3], - ]); + let selector = 0; + self.proc.submachine("poseidon_gl").add_operation( + Some(selector), + &[input_ptr.into_fe(), output_ptr.into_fe(), self.step.into()], + &[ + inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], + inputs[6], inputs[7], inputs[8], inputs[9], inputs[10], inputs[11], + outputs[0], outputs[1], outputs[2], outputs[3], + ], + ); } submachine_event!( @@ -2302,9 +2295,11 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp2_col, limb); log::debug!("Committing public: idx={idx}, limb={limb}"); if let ExecMode::Trace = self.mode { - self.proc - .submachine("publics") - .add_operation(&[idx.into_fe(), limb.into_fe()]); + self.proc.submachine("publics").add_operation( + None, + &[idx.into_fe(), limb.into_fe()], + &[], + ); } submachine_event!(main_publics, idx.into_fe(), limb.into_fe()); diff --git a/riscv-executor/src/submachines.rs b/riscv-executor/src/submachines.rs index 198f9aa17d..d0397acdf0 100644 --- a/riscv-executor/src/submachines.rs +++ b/riscv-executor/src/submachines.rs @@ -11,7 +11,14 @@ trait SubmachineKind { /// Row block size const BLOCK_SIZE: u32; /// Add an operation to the submachine trace - fn add_operation(trace: &mut SubmachineTrace, args: &[F]); + fn add_operation( + trace: &mut SubmachineTrace, + // pil lookup RHS values (including selector idx, if present) + selector_idx: Option, + lookup_args: &[F], + // extra info provided by the executor + extra: &[F], + ); /// Some machines need more than simply copying first block fn dummy_block_fix(_trace: &mut SubmachineTrace, _rows: u32) {} } @@ -37,7 +44,7 @@ pub trait Submachine { /// current number of rows fn len(&self) -> u32; /// add a new operation to the trace - fn add_operation(&mut self, args: &[F]); + fn add_operation(&mut self, selector_idx: Option, lookup_args: &[F], extra: &[F]); /// apply final row overrides (needed because the trace is circular) fn final_row_override(&mut self); /// push a dummy block to the trace @@ -80,8 +87,8 @@ impl Submachine for SubmachineImpl self.trace.len() } - fn add_operation(&mut self, args: &[F]) { - M::add_operation(&mut self.trace, args); + fn add_operation(&mut self, selector_idx: Option, lookup_args: &[F], extra: &[F]) { + M::add_operation(&mut self.trace, selector_idx, lookup_args, extra); } fn final_row_override(&mut self) { @@ -165,6 +172,7 @@ impl SubmachineTrace { /// Push a dummy block to the trace. /// A dummy block is a copy of the first block, with the final row updates applied to it, and selectors set to 0. fn push_dummy_block(&mut self, machine_max_degree: usize, size: u32, selectors: &'static str) { + // TODO: use Optional selector argument let selector_pat = format!("{selectors}["); for i in 0..size { @@ -194,8 +202,15 @@ impl SubmachineKind for BinaryMachine { const SELECTORS: &'static str = "sel"; const BLOCK_SIZE: u32 = 4; - fn add_operation(trace: &mut SubmachineTrace, args: &[F]) { - let [selector, op_id, a, b, c] = args[..] else { + fn add_operation( + trace: &mut SubmachineTrace, + selector_idx: Option, + lookup_args: &[F], + extra: &[F], + ) { + assert!(extra.is_empty()); + let selector_idx = selector_idx.unwrap(); + let [op_id, a, b, c] = lookup_args[..] else { panic!(); }; @@ -277,10 +292,7 @@ impl SubmachineKind for BinaryMachine { trace.set_current_row("B", b); trace.set_current_row("C", c); // latch row: set selector - trace.set_current_row( - &format!("{}[{}]", Self::SELECTORS, selector.try_into_i32().unwrap()), - 1.into(), - ); + trace.set_current_row(&format!("{}[{}]", Self::SELECTORS, selector_idx), 1.into()); } } @@ -290,8 +302,15 @@ impl SubmachineKind for ShiftMachine { const SELECTORS: &'static str = "sel"; const BLOCK_SIZE: u32 = 4; - fn add_operation(trace: &mut SubmachineTrace, args: &[F]) { - let [selector, op_id, a, b, c] = args[..] else { + fn add_operation( + trace: &mut SubmachineTrace, + selector_idx: Option, + lookup_args: &[F], + extra: &[F], + ) { + assert!(extra.is_empty()); + let selector_idx = selector_idx.unwrap(); + let [op_id, a, b, c] = lookup_args[..] else { panic!(); }; @@ -369,10 +388,7 @@ impl SubmachineKind for ShiftMachine { trace.set_current_row("A", a); trace.set_current_row("B", b); trace.set_current_row("C", c); - trace.set_current_row( - &format!("{}[{}]", Self::SELECTORS, selector.try_into_i32().unwrap()), - 1.into(), - ); + trace.set_current_row(&format!("{}[{}]", Self::SELECTORS, selector_idx), 1.into()); } } @@ -382,8 +398,15 @@ impl SubmachineKind for SplitGlMachine { const SELECTORS: &'static str = "sel"; const BLOCK_SIZE: u32 = 8; - fn add_operation(trace: &mut SubmachineTrace, args: &[F]) { - let [selector, output_lo, output_hi] = args[..] else { + fn add_operation( + trace: &mut SubmachineTrace, + selector_idx: Option, + lookup_args: &[F], + extra: &[F], + ) { + assert!(extra.is_empty()); + let selector_idx = selector_idx.unwrap(); + let [_input, output_lo, output_hi] = lookup_args[..] else { panic!(); }; @@ -503,10 +526,7 @@ impl SubmachineKind for SplitGlMachine { // 7: bytes/lt/gt/was_lt are set by the next row trace.push_row(); - trace.set_current_row( - &format!("{}[{}]", Self::SELECTORS, selector.try_into_i32().unwrap()), - 1.into(), - ); + trace.set_current_row(&format!("{}[{}]", Self::SELECTORS, selector_idx), 1.into()); trace.set_current_row("output_low", lo.into()); trace.set_current_row("output_high", hi.into()); trace.set_current_row("in_acc", hi_and_lo(hi, lo)); @@ -517,8 +537,15 @@ pub struct PublicsMachine; impl SubmachineKind for PublicsMachine { const SELECTORS: &'static str = ""; const BLOCK_SIZE: u32 = 1; - fn add_operation(trace: &mut SubmachineTrace, args: &[F]) { - let [addr, value] = args[..] else { + fn add_operation( + trace: &mut SubmachineTrace, + selector_idx: Option, + lookup_args: &[F], + extra: &[F], + ) { + assert!(selector_idx.is_none()); + assert!(extra.is_empty()); + let [addr, value] = lookup_args[..] else { panic!(); }; assert!( @@ -543,10 +570,23 @@ impl SubmachineKind for PoseidonGlMachine { const SELECTORS: &'static str = "sel"; const BLOCK_SIZE: u32 = 31; // full rounds + partial rounds + 1 - fn add_operation(trace: &mut SubmachineTrace, args: &[F]) { + fn add_operation( + trace: &mut SubmachineTrace, + selector_idx: Option, + lookup_args: &[F], + extra: &[F], + ) { const STATE_SIZE: usize = 12; const OUTPUT_SIZE: usize = 4; + let selector_idx = selector_idx.unwrap(); + let [input_addr, output_addr, time_step] = lookup_args[..] else { + panic!(); + }; + + let input = extra[0..STATE_SIZE].to_vec(); + let output = extra[STATE_SIZE..STATE_SIZE + OUTPUT_SIZE].to_vec(); + const INPUT_COLS: [&str; STATE_SIZE] = [ "input[0]", "input[1]", @@ -590,13 +630,6 @@ impl SubmachineKind for PoseidonGlMachine { "x7[9]", "x7[10]", "x7[11]", ]; - let [selector, input_addr, output_addr, time_step] = args[0..4] else { - panic!(); - }; - - let input = args[3..3 + STATE_SIZE].to_vec(); - let output = args[3 + STATE_SIZE..3 + STATE_SIZE + OUTPUT_SIZE].to_vec(); - let mut state: Vec = input.clone(); for row in 0..(Self::BLOCK_SIZE - 1) as usize { @@ -672,7 +705,7 @@ impl SubmachineKind for PoseidonGlMachine { // set selector trace.set_current_block( Self::BLOCK_SIZE, - &format!("{}[{}]", Self::SELECTORS, selector.try_into_i32().unwrap()), + &format!("{}[{}]", Self::SELECTORS, selector_idx), 1.into(), ); for i in 0..STATE_SIZE { From 3bebba6b391b3be5531fce140cbbe6abd72ad378 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Tue, 10 Dec 2024 10:11:24 -0300 Subject: [PATCH 07/35] fix & better panic --- riscv-executor/src/lib.rs | 2 +- riscv-executor/src/submachines.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index e47f36fa7c..131d161d4a 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -2058,7 +2058,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let selector = 0; self.proc.submachine("split_gl").add_operation( Some(selector), - &[value_fe.into(), lo.into(), hi.into()], + &[value_fe, lo.into(), hi.into()], &[], ); } diff --git a/riscv-executor/src/submachines.rs b/riscv-executor/src/submachines.rs index d0397acdf0..ddc73a8f16 100644 --- a/riscv-executor/src/submachines.rs +++ b/riscv-executor/src/submachines.rs @@ -65,11 +65,12 @@ impl SubmachineImpl { pub fn new(namespace: &str, witness_cols: &[String]) -> Self { // filter machine columns let prefix = format!("{namespace}::"); - let witness_cols = witness_cols + let witness_cols: HashMap<_, _> = witness_cols .iter() .filter(|c| c.starts_with(namespace)) .map(|c| (c.strip_prefix(&prefix).unwrap().to_string(), vec![])) .collect(); + assert!(!witness_cols.is_empty(), "machine with no witness columns"); SubmachineImpl { namespace: namespace.to_string(), trace: SubmachineTrace::new(witness_cols), @@ -121,7 +122,6 @@ struct SubmachineTrace { impl SubmachineTrace { fn new(cols: HashMap>) -> Self { - assert!(!cols.is_empty(), "machine with no witness columns"); SubmachineTrace { last_row_overrides: cols.keys().map(|n| (n.clone(), None)).collect(), cols, From 4d69bc8fee995f5962da56772309b0bd7ff16a90 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Tue, 10 Dec 2024 10:46:03 -0300 Subject: [PATCH 08/35] stuff --- riscv-executor/src/lib.rs | 185 +++++++++++++++++++----------- riscv-executor/src/memory.rs | 8 +- riscv-executor/src/submachines.rs | 16 +-- 3 files changed, 127 insertions(+), 82 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 131d161d4a..889b7e2bee 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -89,7 +89,14 @@ enum MainInstruction { commit_public, } -struct MainEvent(MainInstruction, u32, Vec); +struct MainOp(MainInstruction, u32, Vec); +#[derive(Debug)] +struct SubmachineOp { + selector: Option, + lookup_args: Vec, + // TODO: make submachines produce everything and remove this + extra: Vec, +} #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[allow(non_camel_case_types)] @@ -320,11 +327,10 @@ pub struct ExecutionTrace { len: usize, /// Main machine instructions - main_events: Vec>, + main_ops: Vec>, /// Calls into submachines. Each is a sequence of field elemements: the RHS values of the lookup followed by the selector idx (if the machine has it). - /// TODO: keep a flat Vec instead? individial events could be identified from the machine asm definition and operation id. - submachine_events: HashMap>>, + submachine_ops: HashMap>>, /// witness columns cols: HashMap>, @@ -348,8 +354,8 @@ impl ExecutionTrace { reg_writes, mem_ops: Vec::new(), len: pc, - main_events: Vec::new(), - submachine_events: HashMap::new(), + main_ops: Vec::new(), + submachine_ops: HashMap::new(), cols, } } @@ -450,10 +456,10 @@ mod builder { use powdr_number::FieldElement; use crate::{ - BinaryMachine, Elem, ExecMode, Execution, ExecutionTrace, MachineInstance, MainEvent, - MainInstruction, MemOperation, MemOperationKind, MemoryMachine, MemoryState, - PoseidonGlMachine, PublicsMachine, RegWrite, RegisterMemory, ShiftMachine, SplitGlMachine, - Submachine, SubmachineBoxed, PC_INITIAL_VAL, + BinaryMachine, Elem, ExecMode, Execution, ExecutionTrace, MachineInstance, MainInstruction, + MainOp, MemOperation, MemOperationKind, MemoryMachine, MemoryState, PoseidonGlMachine, + PublicsMachine, RegWrite, RegisterMemory, ShiftMachine, SplitGlMachine, Submachine, + SubmachineBoxed, SubmachineOp, PC_INITIAL_VAL, }; fn namespace_degree_range( @@ -613,19 +619,29 @@ mod builder { } } - pub(crate) fn main_event(&mut self, ev: MainInstruction, pc: u32, args: Vec) { + pub(crate) fn main_op(&mut self, ev: MainInstruction, pc: u32, args: Vec) { if let ExecMode::Trace = self.mode { - self.trace.main_events.push(MainEvent(ev, pc, args)); + self.trace.main_ops.push(MainOp(ev, pc, args)); } } - pub(crate) fn submachine_event(&mut self, m: MachineInstance, args: &[F]) { + pub(crate) fn submachine_op( + &mut self, + m: MachineInstance, + selector: Option, + lookup_args: &[F], + extra: &[F], + ) { if let ExecMode::Trace = self.mode { self.trace - .submachine_events + .submachine_ops .entry(m) .or_default() - .push(args.into()); + .push(SubmachineOp { + selector, + lookup_args: lookup_args.to_vec(), + extra: extra.to_vec(), + }); } } @@ -774,16 +790,12 @@ mod builder { self.set_next_pc().and(Some(st_line)) } - pub(crate) fn set_mem(&mut self, addr: u32, val: u32, step: u32, selector: u32) { - self.submachine_event( + pub(crate) fn set_mem(&mut self, addr: u32, val: u32, step: u32, selector: u8) { + self.submachine_op( MachineInstance::main_memory, - &[ - 1.into(), - addr.into(), - step.into(), - val.into(), - selector.into(), - ], + Some(selector), + &[1.into(), addr.into(), step.into(), val.into()], + &[], ); if let ExecMode::Trace = self.mode { self.trace.mem_ops.push(MemOperation { @@ -797,17 +809,13 @@ mod builder { self.mem.insert(addr, val); } - pub(crate) fn get_mem(&mut self, addr: u32, step: u32, selector: u32) -> u32 { + pub(crate) fn get_mem(&mut self, addr: u32, step: u32, selector: u8) -> u32 { let val = *self.mem.get(&addr).unwrap_or(&0); - self.submachine_event( + self.submachine_op( MachineInstance::main_memory, - &[ - 0.into(), - addr.into(), - step.into(), - val.into(), - selector.into(), - ], + Some(selector), + &[0.into(), addr.into(), step.into(), val.into()], + &[], ); if let ExecMode::Trace = self.mode { self.trace.mem_ops.push(MemOperation { @@ -854,12 +862,12 @@ mod builder { }; } - for MainEvent(i, pc, args) in &self.trace.main_events { + for MainOp(i, pc, args) in &self.trace.main_ops { println!("main_event {i:?} {pc} {args:?}"); } - for (m, ev) in &self.trace.submachine_events { - for e in ev { - println!("submachine_event {m:?} {e:?}"); + for (m, ops) in &self.trace.submachine_ops { + for o in ops { + println!("submachine_event {m:?} {o:?}"); } } @@ -1132,11 +1140,13 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } /// read register value, updating the register memory machine - fn reg_read(&mut self, step_offset: u32, reg: u32, selector: u32) -> Elem { + fn reg_read(&mut self, step_offset: u32, reg: u32, selector: u8) -> Elem { let val = self.proc.get_reg_mem(reg); - self.proc.submachine_event( + self.proc.submachine_op( MachineInstance::main_regs, - &[0.into(), reg.into(), val.into_fe(), selector.into()], + Some(selector), + &[0.into(), reg.into(), val.into_fe()], + &[], ); if let ExecMode::Trace = self.mode { self.proc @@ -1147,10 +1157,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } /// write value to register, updating the register memory machine - fn reg_write(&mut self, step_offset: u32, reg: u32, val: Elem, selector: u32) { - self.proc.submachine_event( + fn reg_write(&mut self, step_offset: u32, reg: u32, val: Elem, selector: u8) { + self.proc.submachine_op( MachineInstance::main_regs, - &[1.into(), reg.into(), val.into_fe(), selector.into()], + Some(selector), + &[1.into(), reg.into(), val.into_fe()], + &[], ); if let ExecMode::Trace = self.mode { self.proc @@ -1194,13 +1206,13 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { macro_rules! main_event { ($insn:ident, $($args:expr),*) => { self.proc - .main_event(MainInstruction::$insn, self.proc.get_pc().u(), vec![$($args, )*]) + .main_op(MainInstruction::$insn, self.proc.get_pc().u(), vec![$($args, )*]) }; } macro_rules! submachine_event { - ($machine:ident, $($args:expr),*) => { - self.proc.submachine_event(MachineInstance::$machine, &[$($args, )*]) + ($machine:ident, $selector:expr, $args:expr, $($extra:expr),*) => { + self.proc.submachine_op(MachineInstance::$machine, $selector, $args, &[$($extra, )*]) }; } @@ -1886,8 +1898,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp3_col, Elem::from_u32_as_fe(lo)); set_col!(tmp4_col, Elem::from_u32_as_fe(hi)); + let selector = 0; if let ExecMode::Trace = self.mode { - let selector = 0; self.proc.submachine("split_gl").add_operation( Some(selector), &[r.into(), lo.into(), hi.into()], @@ -1895,7 +1907,11 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); } - submachine_event!(main_split_gl, r.into(), lo.into(), hi.into()); + submachine_event!( + main_split_gl, + Some(selector), + &[r.into(), lo.into(), hi.into()], + ); main_event!(mul,); Vec::new() } @@ -1929,10 +1945,13 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { submachine_event!( main_binary, - op_id.into(), - val1.into_fe(), - val2_offset.into_fe(), - r.into() + Some(selector), + &[ + op_id.into(), + val1.into_fe(), + val2_offset.into_fe(), + r.into() + ], ); if let ExecMode::Trace = self.mode { @@ -1977,10 +1996,13 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { submachine_event!( main_shift, - op_id.into(), - val1.into_fe(), - val2_offset.into_fe(), - r.into() + Some(selector), + &[ + op_id.into(), + val1.into_fe(), + val2_offset.into_fe(), + r.into() + ], ); self.reg_write(3, write_reg, r.into(), 3); @@ -2021,8 +2043,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp4_col, Elem::from_u32_as_fe(high_inv)); set_col!(XX_inv, Elem::Field(inv)); + let selector = 0; if let ExecMode::Trace = self.mode { - let selector = 0; self.proc.submachine("split_gl").add_operation( Some(selector), &[inv, low_inv.into(), high_inv.into()], @@ -2030,7 +2052,11 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); } - submachine_event!(main_split_gl, inv, low_inv.into(), high_inv.into()); + submachine_event!( + main_split_gl, + Some(selector), + &[inv, low_inv.into(), high_inv.into()], + ); main_event!(invert_gl,); Vec::new() } @@ -2054,8 +2080,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp3_col, Elem::from_u32_as_fe(lo)); set_col!(tmp4_col, Elem::from_u32_as_fe(hi)); + let selector = 0; if let ExecMode::Trace = self.mode { - let selector = 0; self.proc.submachine("split_gl").add_operation( Some(selector), &[value_fe, lo.into(), hi.into()], @@ -2063,7 +2089,11 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); } - submachine_event!(main_split_gl, value.into(), lo.into(), hi.into()); + submachine_event!( + main_split_gl, + Some(selector), + &[value.into(), lo.into(), hi.into()], + ); main_event!(split_gl,); Vec::new() } @@ -2113,10 +2143,10 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { .set_mem(output_ptr.u() + 8 * i as u32, lo, self.step + 1, 4); self.proc .set_mem(output_ptr.u() + 8 * i as u32 + 4, hi, self.step + 1, 5); - submachine_event!(main_split_gl, *v, lo.into(), hi.into()); + let selector = 1; + submachine_event!(main_split_gl, Some(selector), &[*v, lo.into(), hi.into()],); if let ExecMode::Trace = self.mode { // split gl of the poseidon machine - let selector = 1; self.proc.submachine("split_gl").add_operation( Some(selector), &[*v, lo.into(), hi.into()], @@ -2125,8 +2155,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } }); + let selector = 0; if let ExecMode::Trace = self.mode { - let selector = 0; self.proc.submachine("poseidon_gl").add_operation( Some(selector), &[input_ptr.into_fe(), output_ptr.into_fe(), self.step.into()], @@ -2140,9 +2170,24 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { submachine_event!( main_poseidon_gl, - input_ptr.into_fe(), - output_ptr.into_fe(), - self.step.into() + Some(selector), + &[input_ptr.into_fe(), output_ptr.into_fe(), self.step.into()], + inputs[0], + inputs[1], + inputs[2], + inputs[3], + inputs[4], + inputs[5], + inputs[6], + inputs[7], + inputs[8], + inputs[9], + inputs[10], + inputs[11], + outputs[0], + outputs[1], + outputs[2], + outputs[3] ); main_event!(poseidon_gl,); vec![] @@ -2175,11 +2220,11 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { }); // TODO: need to add memory read/write events for poseidon2 to work + let selector = 0; submachine_event!( main_poseidon2_gl, - input_ptr.into(), - output_ptr.into(), - self.step.into() + Some(selector), + &[input_ptr.into(), output_ptr.into(), self.step.into()], ); main_event!(poseidon2_gl,); vec![] @@ -2302,7 +2347,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); } - submachine_event!(main_publics, idx.into_fe(), limb.into_fe()); + submachine_event!(main_publics, None, &[idx.into_fe(), limb.into_fe()],); main_event!(commit_public,); vec![] } diff --git a/riscv-executor/src/memory.rs b/riscv-executor/src/memory.rs index 8a1b2ccd5d..5cd3c8d1b5 100644 --- a/riscv-executor/src/memory.rs +++ b/riscv-executor/src/memory.rs @@ -9,7 +9,7 @@ struct Op { write: bool, // each machine that's called via permutation has a selector array, with one entry per incoming permutation. // This is the idx assigned to the `link` triggering the memory operation. - selector_idx: u32, + selector_idx: u8, } pub struct MemoryMachine { @@ -35,7 +35,7 @@ impl MemoryMachine { } } - pub fn write(&mut self, step: u32, addr: u32, val: F, selector_idx: u32) { + pub fn write(&mut self, step: u32, addr: u32, val: F, selector_idx: u8) { self.ops.push(Op { addr, step, @@ -45,7 +45,7 @@ impl MemoryMachine { }); } - pub fn read(&mut self, step: u32, addr: u32, val: F, selector_idx: u32) { + pub fn read(&mut self, step: u32, addr: u32, val: F, selector_idx: u8) { self.ops.push(Op { addr, step, @@ -116,7 +116,7 @@ impl MemoryMachine { cols[Addr as usize].1.push(op.addr.into()); cols[Value as usize].1.push(op.value); - for i in 0..selector_count as u32 { + for i in 0..selector_count as u8 { cols[Selectors as usize + i as usize] .1 .push(if i == op.selector_idx { diff --git a/riscv-executor/src/submachines.rs b/riscv-executor/src/submachines.rs index ddc73a8f16..65fbf19100 100644 --- a/riscv-executor/src/submachines.rs +++ b/riscv-executor/src/submachines.rs @@ -14,7 +14,7 @@ trait SubmachineKind { fn add_operation( trace: &mut SubmachineTrace, // pil lookup RHS values (including selector idx, if present) - selector_idx: Option, + selector_idx: Option, lookup_args: &[F], // extra info provided by the executor extra: &[F], @@ -44,7 +44,7 @@ pub trait Submachine { /// current number of rows fn len(&self) -> u32; /// add a new operation to the trace - fn add_operation(&mut self, selector_idx: Option, lookup_args: &[F], extra: &[F]); + fn add_operation(&mut self, selector_idx: Option, lookup_args: &[F], extra: &[F]); /// apply final row overrides (needed because the trace is circular) fn final_row_override(&mut self); /// push a dummy block to the trace @@ -88,7 +88,7 @@ impl Submachine for SubmachineImpl self.trace.len() } - fn add_operation(&mut self, selector_idx: Option, lookup_args: &[F], extra: &[F]) { + fn add_operation(&mut self, selector_idx: Option, lookup_args: &[F], extra: &[F]) { M::add_operation(&mut self.trace, selector_idx, lookup_args, extra); } @@ -204,7 +204,7 @@ impl SubmachineKind for BinaryMachine { fn add_operation( trace: &mut SubmachineTrace, - selector_idx: Option, + selector_idx: Option, lookup_args: &[F], extra: &[F], ) { @@ -304,7 +304,7 @@ impl SubmachineKind for ShiftMachine { fn add_operation( trace: &mut SubmachineTrace, - selector_idx: Option, + selector_idx: Option, lookup_args: &[F], extra: &[F], ) { @@ -400,7 +400,7 @@ impl SubmachineKind for SplitGlMachine { fn add_operation( trace: &mut SubmachineTrace, - selector_idx: Option, + selector_idx: Option, lookup_args: &[F], extra: &[F], ) { @@ -539,7 +539,7 @@ impl SubmachineKind for PublicsMachine { const BLOCK_SIZE: u32 = 1; fn add_operation( trace: &mut SubmachineTrace, - selector_idx: Option, + selector_idx: Option, lookup_args: &[F], extra: &[F], ) { @@ -572,7 +572,7 @@ impl SubmachineKind for PoseidonGlMachine { fn add_operation( trace: &mut SubmachineTrace, - selector_idx: Option, + selector_idx: Option, lookup_args: &[F], extra: &[F], ) { From e6f2cc14fab16ced331499ed9b196a8136f32f7c Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Wed, 11 Dec 2024 09:33:06 -0300 Subject: [PATCH 09/35] comments --- riscv-executor/src/lib.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 889b7e2bee..01e59a5cf8 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -92,9 +92,13 @@ enum MainInstruction { struct MainOp(MainInstruction, u32, Vec); #[derive(Debug)] struct SubmachineOp { + // TODO: if we move to using witgen here, this will probably be an `identity_id` instead selector: Option, + // these are the RHS values of the lookup (i.e., inside brackets in the PIL lookup) lookup_args: Vec, - // TODO: make submachines produce everything and remove this + // TODO: this is just for the hand-written poseidon_gl submachine, + // instead of accessing memory from the submachine we give it the input + // values extra: Vec, } @@ -108,7 +112,7 @@ enum MachineInstance { main_shift, main_split_gl, main_poseidon_gl, - main_poseidon2_gl, + // main_poseidon2_gl, // main_keccakf, // main_arith, } @@ -2219,13 +2223,6 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc.set_mem(output_ptr + i as u32 * 4, v, 0, 0); // TODO: step/selector for poseidon2 }); - // TODO: need to add memory read/write events for poseidon2 to work - let selector = 0; - submachine_event!( - main_poseidon2_gl, - Some(selector), - &[input_ptr.into(), output_ptr.into(), self.step.into()], - ); main_event!(poseidon2_gl,); vec![] } From 46420d3c1216cc2b5494b86ad20408577c893383 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Wed, 11 Dec 2024 10:15:57 -0300 Subject: [PATCH 10/35] generate submachines at the end --- riscv-executor/src/lib.rs | 113 ++++++-------------------------------- 1 file changed, 18 insertions(+), 95 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 01e59a5cf8..24bd151483 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -851,10 +851,6 @@ mod builder { self.reg_mem.second_last = self.reg_mem.last.clone(); } - pub(crate) fn submachine(&self, name: &str) -> RefMut<'_, Box>> { - self.submachines[name].borrow_mut() - } - pub fn finish(mut self, opt_pil: Option<&Analyzed>) -> Execution { if let ExecMode::Fast = self.mode { return Execution { @@ -894,19 +890,24 @@ mod builder { // generate witness for submachines // ---------------------------- - // for (m, _events) in self.trace.submachine_events { - // match m { - // // MachineInstance::main_binary => {} - // // MachineInstance::main_shift => {} - // // MachineInstance::main_memory => todo!(), - // // MachineInstance::main_regs => todo!(), - // // MachineInstance::main_publics => todo!(), - // // MachineInstance::main_split_gl => todo!(), - // // MachineInstance::main_poseidon_gl => todo!(), - // // MachineInstance::main_poseidon2_gl => todo!(), - // _ => {} - // } - // } + for (m, ops) in self.trace.submachine_ops { + let m = match m { + MachineInstance::main_memory => continue, // "memory", + MachineInstance::main_regs => continue, // "regs", + MachineInstance::main_binary => "binary", + MachineInstance::main_shift => "shift", + MachineInstance::main_publics => "publics", + MachineInstance::main_split_gl => "split_gl", + MachineInstance::main_poseidon_gl => "poseidon_gl", + }; + for op in ops { + self.submachines[m].borrow_mut().add_operation( + op.selector, + &op.lookup_args, + &op.extra, + ); + } + } // add submachine traces to main trace // ---------------------------- @@ -1903,14 +1904,6 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp4_col, Elem::from_u32_as_fe(hi)); let selector = 0; - if let ExecMode::Trace = self.mode { - self.proc.submachine("split_gl").add_operation( - Some(selector), - &[r.into(), lo.into(), hi.into()], - &[], - ); - } - submachine_event!( main_split_gl, Some(selector), @@ -1958,19 +1951,6 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ], ); - if let ExecMode::Trace = self.mode { - self.proc.submachine("binary").add_operation( - Some(selector), - &[ - op_id.into(), - val1.into_fe(), - val2_offset.into_fe(), - r.into(), - ], - &[], - ); - } - self.reg_write(3, write_reg, r.into(), 3); set_col!(tmp3_col, Elem::from_u32_as_fe(r)); @@ -2015,19 +1995,6 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp2_col, val2); set_col!(tmp3_col, Elem::from_u32_as_fe(r)); - if let ExecMode::Trace = self.mode { - self.proc.submachine("shift").add_operation( - Some(selector), - &[ - op_id.into(), - val1.into_fe(), - val2_offset.into_fe(), - r.into(), - ], - &[], - ); - } - Vec::new() } "invert_gl" => { @@ -2048,14 +2015,6 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(XX_inv, Elem::Field(inv)); let selector = 0; - if let ExecMode::Trace = self.mode { - self.proc.submachine("split_gl").add_operation( - Some(selector), - &[inv, low_inv.into(), high_inv.into()], - &[], - ); - } - submachine_event!( main_split_gl, Some(selector), @@ -2085,14 +2044,6 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp4_col, Elem::from_u32_as_fe(hi)); let selector = 0; - if let ExecMode::Trace = self.mode { - self.proc.submachine("split_gl").add_operation( - Some(selector), - &[value_fe, lo.into(), hi.into()], - &[], - ); - } - submachine_event!( main_split_gl, Some(selector), @@ -2149,29 +2100,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { .set_mem(output_ptr.u() + 8 * i as u32 + 4, hi, self.step + 1, 5); let selector = 1; submachine_event!(main_split_gl, Some(selector), &[*v, lo.into(), hi.into()],); - if let ExecMode::Trace = self.mode { - // split gl of the poseidon machine - self.proc.submachine("split_gl").add_operation( - Some(selector), - &[*v, lo.into(), hi.into()], - &[], - ); - } }); let selector = 0; - if let ExecMode::Trace = self.mode { - self.proc.submachine("poseidon_gl").add_operation( - Some(selector), - &[input_ptr.into_fe(), output_ptr.into_fe(), self.step.into()], - &[ - inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], - inputs[6], inputs[7], inputs[8], inputs[9], inputs[10], inputs[11], - outputs[0], outputs[1], outputs[2], outputs[3], - ], - ); - } - submachine_event!( main_poseidon_gl, Some(selector), @@ -2336,14 +2267,6 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp1_col, idx); set_col!(tmp2_col, limb); log::debug!("Committing public: idx={idx}, limb={limb}"); - if let ExecMode::Trace = self.mode { - self.proc.submachine("publics").add_operation( - None, - &[idx.into_fe(), limb.into_fe()], - &[], - ); - } - submachine_event!(main_publics, None, &[idx.into_fe(), limb.into_fe()],); main_event!(commit_public,); vec![] From 2b4d6b688e03a314526ced48cd277fbed91c319c Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Wed, 11 Dec 2024 10:33:09 -0300 Subject: [PATCH 11/35] generate submachine traces at the end of execution --- riscv-executor/src/lib.rs | 27 +++++--------------- riscv-executor/src/submachines.rs | 41 ++++++++++++++----------------- 2 files changed, 25 insertions(+), 43 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 24bd151483..5d679f8b4e 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -89,7 +89,9 @@ enum MainInstruction { commit_public, } +#[allow(unused)] struct MainOp(MainInstruction, u32, Vec); + #[derive(Debug)] struct SubmachineOp { // TODO: if we move to using witgen here, this will probably be an `identity_id` instead @@ -447,11 +449,7 @@ impl RegisterMemory { } mod builder { - use std::{ - cell::{RefCell, RefMut}, - cmp, - collections::HashMap, - }; + use std::{cell::RefCell, cmp, collections::HashMap}; use powdr_ast::{ analyzed::{Analyzed, DegreeRange}, @@ -862,15 +860,6 @@ mod builder { }; } - for MainOp(i, pc, args) in &self.trace.main_ops { - println!("main_event {i:?} {pc} {args:?}"); - } - for (m, ops) in &self.trace.submachine_ops { - for o in ops { - println!("submachine_event {m:?} {o:?}"); - } - } - let pil = opt_pil.unwrap(); let main_degree = { @@ -912,20 +901,16 @@ mod builder { // add submachine traces to main trace // ---------------------------- for mut machine in self.submachines.into_values().map(|m| m.into_inner()) { - // if the machine is not empty, we need to fill it up to the degree + // finalize and extend the submachine traces and add to full trace if machine.len() > 0 { - machine.final_row_override(); let range = namespace_degree_range(pil, machine.namespace()); // extend with dummy blocks up to the required machine degree let machine_degree = std::cmp::max(machine.len().next_power_of_two(), range.min as u32); - while machine.len() < machine_degree { - machine.push_dummy_block(machine_degree as usize); + for (col_name, col) in machine.finish(machine_degree) { + assert!(self.trace.cols.insert(col_name, col).is_none()); } } - for (col_name, col) in machine.take_cols() { - assert!(self.trace.cols.insert(col_name, col).is_none()); - } } // add regs memory trace diff --git a/riscv-executor/src/submachines.rs b/riscv-executor/src/submachines.rs index 65fbf19100..26c85f5683 100644 --- a/riscv-executor/src/submachines.rs +++ b/riscv-executor/src/submachines.rs @@ -45,13 +45,9 @@ pub trait Submachine { fn len(&self) -> u32; /// add a new operation to the trace fn add_operation(&mut self, selector_idx: Option, lookup_args: &[F], extra: &[F]); - /// apply final row overrides (needed because the trace is circular) - fn final_row_override(&mut self); - /// push a dummy block to the trace - fn push_dummy_block(&mut self, machine_max_degree: usize); - /// Consume the trace returning a list of columns. - /// Should be called only once. - fn take_cols(&mut self) -> Vec<(String, Vec)>; + /// finish the trace, padding to the given degree and returning the machine columns. + /// Ideally we'd take `self` here, but this is called from a `dyn Trait`... + fn finish(&mut self, degree: u32) -> Vec<(String, Vec)>; } /// Concrete implementation of the Submachine trait @@ -59,6 +55,7 @@ struct SubmachineImpl { namespace: String, trace: SubmachineTrace, m: std::marker::PhantomData, + finished: bool, } impl SubmachineImpl { @@ -75,6 +72,7 @@ impl SubmachineImpl { namespace: namespace.to_string(), trace: SubmachineTrace::new(witness_cols), m: std::marker::PhantomData, + finished: false, } } } @@ -92,19 +90,18 @@ impl Submachine for SubmachineImpl M::add_operation(&mut self.trace, selector_idx, lookup_args, extra); } - fn final_row_override(&mut self) { - self.trace.final_row_override() - } - - fn push_dummy_block(&mut self, machine_max_degree: usize) { - let prev_len = self.len(); - self.trace - .push_dummy_block(machine_max_degree, M::BLOCK_SIZE, M::SELECTORS); - let dummy_size = self.len() - prev_len; - M::dummy_block_fix(&mut self.trace, dummy_size); - } - - fn take_cols(&mut self) -> Vec<(String, Vec)> { + fn finish(&mut self, degree: u32) -> Vec<(String, Vec)> { + assert!(self.len() > 0); + assert!(!self.finished, "submachine finish called twice"); + self.finished = true; + self.trace.final_row_override(); + while self.len() < degree { + let prev_len = self.len(); + self.trace + .push_dummy_block(degree, M::BLOCK_SIZE, M::SELECTORS); + let dummy_size = self.len() - prev_len; + M::dummy_block_fix(&mut self.trace, dummy_size); + } self.trace .take_cols() .map(|(k, v)| (format!("{}::{}", self.namespace, k), v)) @@ -171,12 +168,12 @@ impl SubmachineTrace { /// Push a dummy block to the trace. /// A dummy block is a copy of the first block, with the final row updates applied to it, and selectors set to 0. - fn push_dummy_block(&mut self, machine_max_degree: usize, size: u32, selectors: &'static str) { + fn push_dummy_block(&mut self, machine_max_degree: u32, size: u32, selectors: &'static str) { // TODO: use Optional selector argument let selector_pat = format!("{selectors}["); for i in 0..size { - if self.cols.values().next().unwrap().len() == machine_max_degree { + if self.cols.values().next().unwrap().len() as u32 == machine_max_degree { break; } self.cols.iter_mut().for_each(|(col, values)| { From a1a8718c3629dfabfa1bd1552d64f95a7716198a Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Wed, 11 Dec 2024 14:37:09 -0300 Subject: [PATCH 12/35] make memory implement Submachine --- riscv-executor/src/memory.rs | 135 +++++++++++++++++++++++++++++++++-- 1 file changed, 128 insertions(+), 7 deletions(-) diff --git a/riscv-executor/src/memory.rs b/riscv-executor/src/memory.rs index 5cd3c8d1b5..99ec949c49 100644 --- a/riscv-executor/src/memory.rs +++ b/riscv-executor/src/memory.rs @@ -1,4 +1,6 @@ -use powdr_number::FieldElement; +use powdr_number::{FieldElement, LargeInt}; + +use crate::Submachine; #[derive(Debug, Eq, PartialEq)] /// Order of fields matter: will be ordered by addr then step. @@ -6,7 +8,7 @@ struct Op { addr: u32, step: u32, value: F, - write: bool, + write: F, // each machine that's called via permutation has a selector array, with one entry per incoming permutation. // This is the idx assigned to the `link` triggering the memory operation. selector_idx: u8, @@ -40,7 +42,7 @@ impl MemoryMachine { addr, step, value: val, - write: true, + write: 1.into(), selector_idx, }); } @@ -50,7 +52,7 @@ impl MemoryMachine { addr, step, value: val, - write: false, + write: 0.into(), selector_idx, }); } @@ -109,9 +111,7 @@ impl MemoryMachine { cols[DiffLower as usize].1.push(0.into()); cols[Change as usize].1.push(0.into()); } - cols[IsWrite as usize] - .1 - .push(if op.write { 1.into() } else { 0.into() }); + cols[IsWrite as usize].1.push(op.write); cols[Step as usize].1.push(op.step.into()); cols[Addr as usize].1.push(op.addr.into()); cols[Value as usize].1.push(op.value); @@ -157,3 +157,124 @@ impl MemoryMachine { cols } } + +impl Submachine for MemoryMachine { + fn len(&self) -> u32 { + self.ops.len() as u32 + } + + fn namespace(&self) -> &str { + &self.namespace + } + + fn add_operation(&mut self, selector_idx: Option, lookup_args: &[F], _extra: &[F]) { + let [op_id, addr, step, value] = lookup_args[..] else { + panic!() + }; + self.ops.push(Op { + addr: addr.to_integer().try_into_u32().unwrap(), + step: step.to_integer().try_into_u32().unwrap(), + value, + write: op_id, + selector_idx: selector_idx.unwrap(), + }); + } + + fn finish(&mut self, degree: u32) -> Vec<(String, Vec)> { + assert!( + degree >= self.len(), + "trying to take less rows than memory ops" + ); + + // order here matters (pil defines the order of witness cols)! we use this to index into the columns + #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] + #[repr(usize)] + enum Cols { + Addr = 0, + Step, + Change, + Value, + IsWrite, + DiffLower, + DiffUpper, + Selectors, // this is last and will be used as a base for selectors + } + use Cols::*; + + // sort ops by (addr, step) + self.ops.sort_by_key(|op| (op.addr, op.step)); + + let mut cols: Vec<_> = std::mem::take(&mut self.witness_cols) + .into_iter() + .map(|n| (n, vec![])) + .collect(); + let selector_count = cols.len() - Cols::Selectors as usize; + + // generate rows from ops + for (idx, op) in self.ops.iter().enumerate() { + if let Some(next_addr) = self.ops.get(idx + 1).map(|op| op.addr) { + assert!(next_addr >= op.addr); + let diff = if next_addr > op.addr { + cols[Change as usize].1.push(1.into()); + (next_addr - op.addr).wrapping_sub(1) + } else { + cols[Change as usize].1.push(0.into()); + let next_step = self.ops.get(idx + 1).map(|op| op.step).unwrap(); + (next_step - op.step).wrapping_sub(1) + }; + cols[DiffLower as usize].1.push((diff & 0xffff).into()); + cols[DiffUpper as usize].1.push((diff >> 16).into()); + } else { + // last row + cols[DiffUpper as usize].1.push(0.into()); + cols[DiffLower as usize].1.push(0.into()); + cols[Change as usize].1.push(0.into()); + } + cols[IsWrite as usize].1.push(op.write); + cols[Step as usize].1.push(op.step.into()); + cols[Addr as usize].1.push(op.addr.into()); + cols[Value as usize].1.push(op.value); + + for i in 0..selector_count as u8 { + cols[Selectors as usize + i as usize] + .1 + .push(if i == op.selector_idx { + 1.into() + } else { + 0.into() + }); + } + } + + // extend rows if needed + let last_step = self.ops.last().map(|op| op.step).unwrap_or(0); + let last_addr = self.ops.last().map(|op| op.addr).unwrap_or(0); + let last_value = self.ops.last().map(|op| op.value).unwrap_or(0.into()); + if self.len() < degree { + // addr and value are repeated + cols[Addr as usize] + .1 + .resize(degree as usize, last_addr.into()); + cols[Value as usize].1.resize(degree as usize, last_value); + // step increases + cols[Step as usize].1.extend( + (last_step + 1..) + .take((degree - self.len()) as usize) + .map(|x| F::from(x)), + ); + // rest are zero + cols[Change as usize].1.resize(degree as usize, 0.into()); + cols[IsWrite as usize].1.resize(degree as usize, 0.into()); + cols[DiffLower as usize].1.resize(degree as usize, 0.into()); + cols[DiffUpper as usize].1.resize(degree as usize, 0.into()); + for i in 0..selector_count as u32 { + cols[Selectors as usize + i as usize] + .1 + .resize(degree as usize, 0.into()); + } + } + // m_change is 1 in last row + *cols[Change as usize].1.last_mut().unwrap() = 1.into(); + cols + } +} From d8154a864b6b936866b903299659979699f03bf0 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Wed, 11 Dec 2024 15:04:47 -0300 Subject: [PATCH 13/35] memory machine works the same as other machines --- riscv-executor/src/lib.rs | 203 ++++++++++++++++------------------- riscv-executor/src/memory.rs | 116 -------------------- 2 files changed, 92 insertions(+), 227 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 5d679f8b4e..c58092c452 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -89,6 +89,7 @@ enum MainInstruction { commit_public, } +// TODO: we don't do anything with these yet. Idea is to keep info that is to be given to witgen #[allow(unused)] struct MainOp(MainInstruction, u32, Vec); @@ -99,8 +100,7 @@ struct SubmachineOp { // these are the RHS values of the lookup (i.e., inside brackets in the PIL lookup) lookup_args: Vec, // TODO: this is just for the hand-written poseidon_gl submachine, - // instead of accessing memory from the submachine we give it the input - // values + // we give it the input values because it doesn't have access to memory extra: Vec, } @@ -335,7 +335,9 @@ pub struct ExecutionTrace { /// Main machine instructions main_ops: Vec>, - /// Calls into submachines. Each is a sequence of field elemements: the RHS values of the lookup followed by the selector idx (if the machine has it). + /// Calls into submachines. Each is a sequence of field elemements: the RHS + /// values of the lookup followed by the selector idx (if the machine has + /// it). submachine_ops: HashMap>>, /// witness columns @@ -493,8 +495,6 @@ mod builder { trace: ExecutionTrace, submachines: HashMap>>>, - pub regs_machine: MemoryMachine, - pub memory_machine: MemoryMachine, /// Maximum rows we can run before we stop the execution. max_rows: usize, @@ -571,28 +571,39 @@ mod builder { if let ExecMode::Trace = mode { [ ( - "binary", - BinaryMachine::new_boxed("main_binary", &witness_cols).into(), + "memory".to_string(), + RefCell::new(Box::new(MemoryMachine::new("main_memory", &witness_cols))) + as RefCell>>, // this first `as` is needed to coerce the type of the array ), ( - "shift", - ShiftMachine::new_boxed("main_shift", &witness_cols).into(), + "regs".to_string(), + RefCell::new(Box::new(MemoryMachine::new("main_regs", &witness_cols))), ), ( - "split_gl", - SplitGlMachine::new_boxed("main_split_gl", &witness_cols).into(), + "binary".to_string(), + RefCell::new(BinaryMachine::new_boxed("main_binary", &witness_cols)), ), ( - "publics", - PublicsMachine::new_boxed("main_publics", &witness_cols).into(), + "shift".to_string(), + RefCell::new(ShiftMachine::new_boxed("main_shift", &witness_cols)), ), ( - "poseidon_gl", - PoseidonGlMachine::new_boxed("main_poseidon_gl", &witness_cols).into(), + "split_gl".to_string(), + RefCell::new(SplitGlMachine::new_boxed("main_split_gl", &witness_cols)), + ), + ( + "publics".to_string(), + RefCell::new(PublicsMachine::new_boxed("main_publics", &witness_cols)), + ), + ( + "poseidon_gl".to_string(), + RefCell::new(PoseidonGlMachine::new_boxed( + "main_poseidon_gl", + &witness_cols, + )), ), ] .into_iter() - .map(|(name, m)| (name.to_string(), m)) .collect() } else { Default::default() @@ -601,8 +612,6 @@ mod builder { let mut ret = Self { pc_idx, curr_pc: PC_INITIAL_VAL.into(), - regs_machine: MemoryMachine::new("main_regs", &witness_cols), - memory_machine: MemoryMachine::new("main_memory", &witness_cols), trace: ExecutionTrace::new(witness_cols, reg_map, reg_writes, PC_INITIAL_VAL + 1), submachines, next_statement_line: 1, @@ -805,7 +814,6 @@ mod builder { kind: MemOperationKind::Write, address: addr, }); - self.memory_machine.write(step, addr, val.into(), selector); } self.mem.insert(addr, val); @@ -825,7 +833,6 @@ mod builder { kind: MemOperationKind::Read, address: addr, }); - self.memory_machine.read(step, addr, val.into(), selector); } val } @@ -881,8 +888,8 @@ mod builder { // ---------------------------- for (m, ops) in self.trace.submachine_ops { let m = match m { - MachineInstance::main_memory => continue, // "memory", - MachineInstance::main_regs => continue, // "regs", + MachineInstance::main_memory => "memory", + MachineInstance::main_regs => "regs", MachineInstance::main_binary => "binary", MachineInstance::main_shift => "shift", MachineInstance::main_publics => "publics", @@ -913,32 +920,6 @@ mod builder { } } - // add regs memory trace - // ---------------------------- - let regs_degree = { - let range = namespace_degree_range(pil, &self.regs_machine.namespace); - std::cmp::max( - self.regs_machine.len().next_power_of_two(), - range.min as u32, - ) - }; - for (col_name, col) in self.regs_machine.take_cols(regs_degree) { - assert!(self.trace.cols.insert(col_name, col).is_none()); - } - - // add main memory trace - // ---------------------------- - let mem_degree = { - let range = namespace_degree_range(pil, &self.memory_machine.namespace); - std::cmp::max( - self.memory_machine.len().next_power_of_two(), - range.min as u32, - ) - }; - for (col_name, col) in self.memory_machine.take_cols(mem_degree) { - assert!(self.trace.cols.insert(col_name, col).is_none()); - } - Execution { trace_len: self.trace.len, memory: self.mem, @@ -1135,14 +1116,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc.submachine_op( MachineInstance::main_regs, Some(selector), - &[0.into(), reg.into(), val.into_fe()], + &[ + 0.into(), + reg.into(), + (self.step + step_offset).into(), + val.into_fe(), + ], &[], ); - if let ExecMode::Trace = self.mode { - self.proc - .regs_machine - .read(self.step + step_offset, reg, val.into_fe(), selector); - } val } @@ -1151,14 +1132,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc.submachine_op( MachineInstance::main_regs, Some(selector), - &[1.into(), reg.into(), val.into_fe()], + &[ + 1.into(), + reg.into(), + (self.step + step_offset).into(), + val.into_fe(), + ], &[], ); - if let ExecMode::Trace = self.mode { - self.proc - .regs_machine - .write(self.step + step_offset, reg, val.into_fe(), selector); - } self.proc.set_reg_mem(reg, val); } @@ -1193,14 +1174,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { }; } - macro_rules! main_event { + macro_rules! main_op { ($insn:ident, $($args:expr),*) => { self.proc .main_op(MainInstruction::$insn, self.proc.get_pc().u(), vec![$($args, )*]) }; } - macro_rules! submachine_event { + macro_rules! submachine_op { ($machine:ident, $selector:expr, $args:expr, $($extra:expr),*) => { self.proc.submachine_op(MachineInstance::$machine, $selector, $args, &[$($extra, )*]) }; @@ -1232,14 +1213,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(Y_free_value, val); } - main_event!(set_reg,); + main_op!(set_reg,); Vec::new() } "get_reg" => { let addr = args[0].u(); let val = self.reg_read(0, addr, 0); - main_event!(get_reg,); + main_op!(get_reg,); vec![val] } "affine" => { @@ -1254,7 +1235,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.reg_write(1, write_reg, res, 3); set_col!(tmp1_col, val1); - main_event!(affine,); + main_op!(affine,); Vec::new() } @@ -1295,9 +1276,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(X_b4, Elem::from_u32_as_fe(b4.into())); if name == "mstore" { - main_event!(mstore,); + main_op!(mstore,); } else { - main_event!(mstore_bootloader,); + main_op!(mstore_bootloader,); } Vec::new() } @@ -1333,7 +1314,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Elem::from_u32_as_fe(((v as u64 >> 32) & 1) as u32) ); - main_event!(mload,); + main_op!(mload,); Vec::new() } // TODO: update to witness generation for continuations @@ -1348,7 +1329,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.reg_write(2, write_addr, val, 3); - main_event!(load_bootloader_input,); + main_op!(load_bootloader_input,); Vec::new() } // TODO: update to witness generation for continuations @@ -1363,7 +1344,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { assert_eq!(val, actual_val); - main_event!(assert_bootloader_input,); + main_op!(assert_bootloader_input,); Vec::new() } "load_label" => { @@ -1375,7 +1356,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc.set_col("main::instr_load_label_param_l", label); - main_event!(load_label,); + main_op!(load_label,); Vec::new() } "jump" => { @@ -1389,7 +1370,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc.set_col("main::instr_jump_param_l", label); - main_event!(jump,); + main_op!(jump,); Vec::new() } "jump_dyn" => { @@ -1404,7 +1385,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp1_col, addr); - main_event!(jump_dyn,); + main_op!(jump_dyn,); Vec::new() } // TODO: update to witness generation for continuations @@ -1413,7 +1394,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let addr = self.bootloader_inputs[bootloader_input_idx]; self.proc.set_pc(addr); - main_event!(jump_to_bootloader_input,); + main_op!(jump_to_bootloader_input,); Vec::new() } "branch_if_diff_nonzero" => { @@ -1439,7 +1420,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc .set_col("main::instr_branch_if_diff_nonzero_param_l", label); - main_event!(branch_if_diff_nonzero,); + main_op!(branch_if_diff_nonzero,); Vec::new() } "branch_if_diff_equal" => { @@ -1466,7 +1447,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc .set_col("main::instr_branch_if_diff_equal_param_l", label); - main_event!(branch_if_diff_equal,); + main_op!(branch_if_diff_equal,); Vec::new() } "skip_if_equal" => { @@ -1491,7 +1472,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(XX_inv, Elem::Field(F::one() / get_col!(XX).into_fe())); } - main_event!(skip_if_equal,); + main_op!(skip_if_equal,); Vec::new() } "branch_if_diff_greater_than" => { @@ -1533,7 +1514,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); - main_event!(branch_if_diff_greater_than,); + main_op!(branch_if_diff_greater_than,); Vec::new() } "is_diff_greater_than" => { @@ -1562,7 +1543,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(X_b4, Elem::from_u32_as_fe(b4.into())); set_col!(wrap_bit, Elem::from_u32_as_fe(r)); - main_event!(is_diff_greater_than,); + main_op!(is_diff_greater_than,); Vec::new() } "is_equal_zero" => { @@ -1580,7 +1561,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(XX_inv, Elem::Field(F::one() / get_col!(XX).into_fe())); } - main_event!(is_equal_zero,); + main_op!(is_equal_zero,); Vec::new() } "is_not_equal" => { @@ -1603,7 +1584,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(XX_inv, Elem::Field(F::one() / get_col!(XX).into_fe())); } - main_event!(is_not_equal,); + main_op!(is_not_equal,); Vec::new() } "add_wrap" => { @@ -1641,7 +1622,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); - main_event!(add_wrap,); + main_op!(add_wrap,); Vec::new() } "wrap16" => { @@ -1670,7 +1651,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(Y_b5, Elem::from_u32_as_fe(b5.into())); set_col!(Y_b6, Elem::from_u32_as_fe(b6.into())); - main_event!(wrap16,); + main_op!(wrap16,); Vec::new() } "sub_wrap_with_offset" => { @@ -1704,7 +1685,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); - main_event!(sub_wrap_with_offset,); + main_op!(sub_wrap_with_offset,); Vec::new() } "sign_extend_byte" => { @@ -1737,7 +1718,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); - main_event!(sign_extend_byte,); + main_op!(sign_extend_byte,); Vec::new() } "sign_extend_16_bits" => { @@ -1774,7 +1755,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); - main_event!(sign_extend_16_bits,); + main_op!(sign_extend_16_bits,); Vec::new() } "to_signed" => { @@ -1803,7 +1784,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(Y_7bit, Elem::from_u32_as_fe(b4 as u32 & 0x7f)); - main_event!(to_signed,); + main_op!(to_signed,); Vec::new() } "fail" => { @@ -1865,7 +1846,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(Y_b8, Elem::from_u32_as_fe(b8.into())); } - main_event!(divremu,); + main_op!(divremu,); Vec::new() } "mul" => { @@ -1889,12 +1870,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp4_col, Elem::from_u32_as_fe(hi)); let selector = 0; - submachine_event!( + submachine_op!( main_split_gl, Some(selector), &[r.into(), lo.into(), hi.into()], ); - main_event!(mul,); + main_op!(mul,); Vec::new() } "and" | "or" | "xor" => { @@ -1911,21 +1892,21 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let (r, op_id, selector) = match name { "and" => { - main_event!(and,); + main_op!(and,); (val1.u() & val2_offset.u(), 0, 0) } "or" => { - main_event!(or,); + main_op!(or,); (val1.u() | val2_offset.u(), 1, 1) } "xor" => { - main_event!(xor,); + main_op!(xor,); (val1.u() ^ val2_offset.u(), 2, 2) } _ => unreachable!(), }; - submachine_event!( + submachine_op!( main_binary, Some(selector), &[ @@ -1953,17 +1934,17 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let (r, op_id, selector) = match name { "shl" => { - main_event!(shl,); + main_op!(shl,); (val1.u() << val2_offset.u(), 0, 0) } "shr" => { - main_event!(shr,); + main_op!(shr,); (val1.u() >> val2_offset.u(), 1, 1) } _ => unreachable!(), }; - submachine_event!( + submachine_op!( main_shift, Some(selector), &[ @@ -2000,12 +1981,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(XX_inv, Elem::Field(inv)); let selector = 0; - submachine_event!( + submachine_op!( main_split_gl, Some(selector), &[inv, low_inv.into(), high_inv.into()], ); - main_event!(invert_gl,); + main_op!(invert_gl,); Vec::new() } "split_gl" => { @@ -2029,12 +2010,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp4_col, Elem::from_u32_as_fe(hi)); let selector = 0; - submachine_event!( + submachine_op!( main_split_gl, Some(selector), &[value.into(), lo.into(), hi.into()], ); - main_event!(split_gl,); + main_op!(split_gl,); Vec::new() } "poseidon_gl" => { @@ -2084,11 +2065,11 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc .set_mem(output_ptr.u() + 8 * i as u32 + 4, hi, self.step + 1, 5); let selector = 1; - submachine_event!(main_split_gl, Some(selector), &[*v, lo.into(), hi.into()],); + submachine_op!(main_split_gl, Some(selector), &[*v, lo.into(), hi.into()],); }); let selector = 0; - submachine_event!( + submachine_op!( main_poseidon_gl, Some(selector), &[input_ptr.into_fe(), output_ptr.into_fe(), self.step.into()], @@ -2109,7 +2090,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { outputs[2], outputs[3] ); - main_event!(poseidon_gl,); + main_op!(poseidon_gl,); vec![] } "poseidon2_gl" => { @@ -2139,7 +2120,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc.set_mem(output_ptr + i as u32 * 4, v, 0, 0); // TODO: step/selector for poseidon2 }); - main_event!(poseidon2_gl,); + main_op!(poseidon2_gl,); vec![] } "affine_256" => { @@ -2166,7 +2147,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { }); // TODO: main_arith event - main_event!(affine_256,); + main_op!(affine_256,); vec![] } "mod_256" => { @@ -2189,7 +2170,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { }); // TODO: main_arith event - main_event!(mod_256,); + main_op!(mod_256,); vec![] } "ec_add" => { @@ -2219,7 +2200,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { }); // TODO: main_arith event - main_event!(ec_add,); + main_op!(ec_add,); vec![] } "ec_double" => { @@ -2243,7 +2224,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { }); // TODO: main_arith event - main_event!(ec_double,); + main_op!(ec_double,); vec![] } "commit_public" => { @@ -2252,8 +2233,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp1_col, idx); set_col!(tmp2_col, limb); log::debug!("Committing public: idx={idx}, limb={limb}"); - submachine_event!(main_publics, None, &[idx.into_fe(), limb.into_fe()],); - main_event!(commit_public,); + submachine_op!(main_publics, None, &[idx.into_fe(), limb.into_fe()],); + main_op!(commit_public,); vec![] } instr => { diff --git a/riscv-executor/src/memory.rs b/riscv-executor/src/memory.rs index 99ec949c49..c1373e22dc 100644 --- a/riscv-executor/src/memory.rs +++ b/riscv-executor/src/memory.rs @@ -37,125 +37,9 @@ impl MemoryMachine { } } - pub fn write(&mut self, step: u32, addr: u32, val: F, selector_idx: u8) { - self.ops.push(Op { - addr, - step, - value: val, - write: 1.into(), - selector_idx, - }); - } - - pub fn read(&mut self, step: u32, addr: u32, val: F, selector_idx: u8) { - self.ops.push(Op { - addr, - step, - value: val, - write: 0.into(), - selector_idx, - }); - } - pub fn len(&self) -> u32 { self.ops.len() as u32 } - - pub fn take_cols(mut self, len: u32) -> Vec<(String, Vec)> { - assert!( - len >= self.len(), - "trying to take less rows than memory ops" - ); - - // order here matters (pil defines the order of witness cols)! we use this to index into the columns - #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] - #[repr(usize)] - enum Cols { - Addr = 0, - Step, - Change, - Value, - IsWrite, - DiffLower, - DiffUpper, - Selectors, // this is last and will be used as a base for selectors - } - use Cols::*; - - // sort ops by (addr, step) - self.ops.sort_by_key(|op| (op.addr, op.step)); - - let mut cols: Vec<_> = std::mem::take(&mut self.witness_cols) - .into_iter() - .map(|n| (n, vec![])) - .collect(); - let selector_count = cols.len() - Cols::Selectors as usize; - - // generate rows from ops - for (idx, op) in self.ops.iter().enumerate() { - if let Some(next_addr) = self.ops.get(idx + 1).map(|op| op.addr) { - assert!(next_addr >= op.addr); - let diff = if next_addr > op.addr { - cols[Change as usize].1.push(1.into()); - (next_addr - op.addr).wrapping_sub(1) - } else { - cols[Change as usize].1.push(0.into()); - let next_step = self.ops.get(idx + 1).map(|op| op.step).unwrap(); - (next_step - op.step).wrapping_sub(1) - }; - cols[DiffLower as usize].1.push((diff & 0xffff).into()); - cols[DiffUpper as usize].1.push((diff >> 16).into()); - } else { - // last row - cols[DiffUpper as usize].1.push(0.into()); - cols[DiffLower as usize].1.push(0.into()); - cols[Change as usize].1.push(0.into()); - } - cols[IsWrite as usize].1.push(op.write); - cols[Step as usize].1.push(op.step.into()); - cols[Addr as usize].1.push(op.addr.into()); - cols[Value as usize].1.push(op.value); - - for i in 0..selector_count as u8 { - cols[Selectors as usize + i as usize] - .1 - .push(if i == op.selector_idx { - 1.into() - } else { - 0.into() - }); - } - } - - // extend rows if needed - let last_step = self.ops.last().map(|op| op.step).unwrap_or(0); - let last_addr = self.ops.last().map(|op| op.addr).unwrap_or(0); - let last_value = self.ops.last().map(|op| op.value).unwrap_or(0.into()); - if self.len() < len { - // addr and value are repeated - cols[Addr as usize].1.resize(len as usize, last_addr.into()); - cols[Value as usize].1.resize(len as usize, last_value); - // step increases - cols[Step as usize].1.extend( - (last_step + 1..) - .take((len - self.len()) as usize) - .map(|x| F::from(x)), - ); - // rest are zero - cols[Change as usize].1.resize(len as usize, 0.into()); - cols[IsWrite as usize].1.resize(len as usize, 0.into()); - cols[DiffLower as usize].1.resize(len as usize, 0.into()); - cols[DiffUpper as usize].1.resize(len as usize, 0.into()); - for i in 0..selector_count as u32 { - cols[Selectors as usize + i as usize] - .1 - .resize(len as usize, 0.into()); - } - } - // m_change is 1 in last row - *cols[Change as usize].1.last_mut().unwrap() = 1.into(); - cols - } } impl Submachine for MemoryMachine { From 6d9cc8e04db2315f5fbd925f50cfe07cd98669b2 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Wed, 11 Dec 2024 15:27:27 -0300 Subject: [PATCH 14/35] comment --- riscv-executor/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 2f98493d17..f3de237bc3 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -335,9 +335,7 @@ pub struct ExecutionTrace { /// Main machine instructions main_ops: Vec>, - /// Calls into submachines. Each is a sequence of field elemements: the RHS - /// values of the lookup followed by the selector idx (if the machine has - /// it). + /// Calls into submachines submachine_ops: HashMap>>, /// witness columns From d31f8153b235b86e7a03b38fd87390d242349ff1 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Tue, 10 Dec 2024 14:23:30 -0300 Subject: [PATCH 15/35] handle machines that have been optimized away --- riscv-executor/src/submachines.rs | 44 ++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/riscv-executor/src/submachines.rs b/riscv-executor/src/submachines.rs index 84162358dd..5a9d96e408 100644 --- a/riscv-executor/src/submachines.rs +++ b/riscv-executor/src/submachines.rs @@ -30,6 +30,19 @@ pub trait SubmachineBoxed { impl SubmachineBoxed for M { fn new_boxed(namespace: &str, witness_cols: &[String]) -> Box> { + // filter machine columns + let prefix = format!("{namespace}::"); + let witness_cols: HashMap<_, _> = witness_cols + .iter() + .filter(|c| c.starts_with(namespace)) + .map(|c| (c.strip_prefix(&prefix).unwrap().to_string(), vec![])) + .collect(); + // if we end up with no columns, the machine is not present or has been optimized away + if witness_cols.is_empty() { + return Box::new(DummySubmachine { + namespace: namespace.to_string(), + }); + } Box::new(SubmachineImpl::::new(namespace, witness_cols)) } } @@ -59,15 +72,7 @@ struct SubmachineImpl { } impl SubmachineImpl { - pub fn new(namespace: &str, witness_cols: &[String]) -> Self { - // filter machine columns - let prefix = format!("{namespace}::"); - let witness_cols: HashMap<_, _> = witness_cols - .iter() - .filter(|c| c.starts_with(namespace)) - .map(|c| (c.strip_prefix(&prefix).unwrap().to_string(), vec![])) - .collect(); - assert!(!witness_cols.is_empty(), "machine with no witness columns"); + pub fn new(namespace: &str, witness_cols: HashMap>) -> Self { SubmachineImpl { namespace: namespace.to_string(), trace: SubmachineTrace::new(witness_cols), @@ -109,6 +114,27 @@ impl Submachine for SubmachineImpl } } +/// Submachine that does nothing. Used in place of machines that are not present in the optimized pil. +struct DummySubmachine { + namespace: String, +} + +impl Submachine for DummySubmachine { + fn namespace(&self) -> &str { + self.namespace.as_str() + } + + fn len(&self) -> u32 { + 0 + } + + fn add_operation(&mut self, _selector_idx: Option, _lookup_args: &[F], _extra: &[F]) {} + + fn finish(&mut self, _degree: u32) -> Vec<(String, Vec)> { + vec![] + } +} + /// Holds the submachine trace as a list of columns and a last row override struct SubmachineTrace { cols: HashMap>, From 0f6b23e0238b77b00d52d2e04fea980134c39852 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Thu, 12 Dec 2024 08:25:06 -0300 Subject: [PATCH 16/35] Revert "handle machines that have been optimized away" This reverts commit d31f8153b235b86e7a03b38fd87390d242349ff1. --- riscv-executor/src/submachines.rs | 44 +++++++------------------------ 1 file changed, 9 insertions(+), 35 deletions(-) diff --git a/riscv-executor/src/submachines.rs b/riscv-executor/src/submachines.rs index 5a9d96e408..84162358dd 100644 --- a/riscv-executor/src/submachines.rs +++ b/riscv-executor/src/submachines.rs @@ -30,19 +30,6 @@ pub trait SubmachineBoxed { impl SubmachineBoxed for M { fn new_boxed(namespace: &str, witness_cols: &[String]) -> Box> { - // filter machine columns - let prefix = format!("{namespace}::"); - let witness_cols: HashMap<_, _> = witness_cols - .iter() - .filter(|c| c.starts_with(namespace)) - .map(|c| (c.strip_prefix(&prefix).unwrap().to_string(), vec![])) - .collect(); - // if we end up with no columns, the machine is not present or has been optimized away - if witness_cols.is_empty() { - return Box::new(DummySubmachine { - namespace: namespace.to_string(), - }); - } Box::new(SubmachineImpl::::new(namespace, witness_cols)) } } @@ -72,7 +59,15 @@ struct SubmachineImpl { } impl SubmachineImpl { - pub fn new(namespace: &str, witness_cols: HashMap>) -> Self { + pub fn new(namespace: &str, witness_cols: &[String]) -> Self { + // filter machine columns + let prefix = format!("{namespace}::"); + let witness_cols: HashMap<_, _> = witness_cols + .iter() + .filter(|c| c.starts_with(namespace)) + .map(|c| (c.strip_prefix(&prefix).unwrap().to_string(), vec![])) + .collect(); + assert!(!witness_cols.is_empty(), "machine with no witness columns"); SubmachineImpl { namespace: namespace.to_string(), trace: SubmachineTrace::new(witness_cols), @@ -114,27 +109,6 @@ impl Submachine for SubmachineImpl } } -/// Submachine that does nothing. Used in place of machines that are not present in the optimized pil. -struct DummySubmachine { - namespace: String, -} - -impl Submachine for DummySubmachine { - fn namespace(&self) -> &str { - self.namespace.as_str() - } - - fn len(&self) -> u32 { - 0 - } - - fn add_operation(&mut self, _selector_idx: Option, _lookup_args: &[F], _extra: &[F]) {} - - fn finish(&mut self, _degree: u32) -> Vec<(String, Vec)> { - vec![] - } -} - /// Holds the submachine trace as a list of columns and a last row override struct SubmachineTrace { cols: HashMap>, From cc815d9e081d798d250587338ff7bfd1851309c7 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Thu, 12 Dec 2024 09:42:39 -0300 Subject: [PATCH 17/35] fix for publics, empty machines and better panics --- riscv-executor/src/lib.rs | 23 +++++++++++++++- riscv-executor/src/submachines.rs | 45 ++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index f3de237bc3..bc97c43d87 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -905,7 +905,11 @@ mod builder { // add submachine traces to main trace // ---------------------------- - for mut machine in self.submachines.into_values().map(|m| m.into_inner()) { + for (name, mut machine) in self + .submachines + .into_iter() + .map(|(n, m)| (n, m.into_inner())) + { // finalize and extend the submachine traces and add to full trace if machine.len() > 0 { let range = namespace_degree_range(pil, machine.namespace()); @@ -915,6 +919,23 @@ mod builder { for (col_name, col) in machine.finish(machine_degree) { assert!(self.trace.cols.insert(col_name, col).is_none()); } + } else if name == "publics" { + // for the publics machine, even with no operations being + // issued, the declared "publics" force the cells to be + // filled. We add operations here to emulate that. + if machine.len() == 0 { + for i in 0..8 { + machine.add_operation(None, &[i.into(), 0.into()], &[]); + } + } + for (col_name, col) in machine.finish(8) { + assert!(self.trace.cols.insert(col_name, col).is_none()); + } + } else { + // keep machine columns empty + for (col_name, col) in machine.finish(0) { + assert!(self.trace.cols.insert(col_name, col).is_none()); + } } } diff --git a/riscv-executor/src/submachines.rs b/riscv-executor/src/submachines.rs index 84162358dd..32b3cf921b 100644 --- a/riscv-executor/src/submachines.rs +++ b/riscv-executor/src/submachines.rs @@ -52,7 +52,6 @@ pub trait Submachine { /// Concrete implementation of the Submachine trait struct SubmachineImpl { - namespace: String, trace: SubmachineTrace, m: std::marker::PhantomData, finished: bool, @@ -67,10 +66,14 @@ impl SubmachineImpl { .filter(|c| c.starts_with(namespace)) .map(|c| (c.strip_prefix(&prefix).unwrap().to_string(), vec![])) .collect(); - assert!(!witness_cols.is_empty(), "machine with no witness columns"); + if witness_cols.is_empty() { + log::info!( + "namespace {} has no witness columns in the optimized pil", + namespace + ); + } SubmachineImpl { - namespace: namespace.to_string(), - trace: SubmachineTrace::new(witness_cols), + trace: SubmachineTrace::new(namespace, witness_cols), m: std::marker::PhantomData, finished: false, } @@ -79,7 +82,7 @@ impl SubmachineImpl { impl Submachine for SubmachineImpl { fn namespace(&self) -> &str { - self.namespace.as_str() + self.trace.namespace.as_str() } fn len(&self) -> u32 { @@ -91,7 +94,7 @@ impl Submachine for SubmachineImpl } fn finish(&mut self, degree: u32) -> Vec<(String, Vec)> { - assert!(self.len() > 0); + assert!(self.len() <= degree); assert!(!self.finished, "submachine finish called twice"); self.finished = true; self.trace.final_row_override(); @@ -104,13 +107,14 @@ impl Submachine for SubmachineImpl } self.trace .take_cols() - .map(|(k, v)| (format!("{}::{}", self.namespace, k), v)) + .map(|(k, v)| (format!("{}::{}", self.trace.namespace, k), v)) .collect() } } /// Holds the submachine trace as a list of columns and a last row override struct SubmachineTrace { + namespace: String, cols: HashMap>, // the trace is circular, so for the first block, we can only set the // previous row after the whole trace is built @@ -118,14 +122,18 @@ struct SubmachineTrace { } impl SubmachineTrace { - fn new(cols: HashMap>) -> Self { + fn new(namespace: &str, cols: HashMap>) -> Self { SubmachineTrace { + namespace: namespace.to_string(), last_row_overrides: cols.keys().map(|n| (n.clone(), None)).collect(), cols, } } fn len(&self) -> u32 { + if self.cols.is_empty() { + return 0; + } self.cols.values().next().unwrap().len().try_into().unwrap() } @@ -136,7 +144,7 @@ impl SubmachineTrace { *self .cols .get_mut(col) - .unwrap() + .unwrap_or_else(|| panic!("{} has no column {col}", self.namespace)) .get_mut(idx as usize) .unwrap() = value; } @@ -144,19 +152,32 @@ impl SubmachineTrace { /// set the value of a column in the current fn set_current_row(&mut self, col: &str, value: F) { - *self.cols.get_mut(col).unwrap().last_mut().unwrap() = value; + *self + .cols + .get_mut(col) + .unwrap_or_else(|| panic!("{} has no column {col}", self.namespace)) + .last_mut() + .unwrap() = value; } /// set the value of a column in the last row of the complete trace fn set_final_row(&mut self, col: &str, value: F) { - *self.last_row_overrides.get_mut(col).unwrap() = Some(value); + *self + .last_row_overrides + .get_mut(col) + .unwrap_or_else(|| panic!("{} has no column {col}", self.namespace)) = Some(value); } /// apply saved updates to the last row of the trace fn final_row_override(&mut self) { for (col, value) in self.last_row_overrides.iter() { if let Some(value) = value { - *self.cols.get_mut(col).unwrap().last_mut().unwrap() = *value; + *self + .cols + .get_mut(col) + .unwrap_or_else(|| panic!("{} has no column {col}", self.namespace)) + .last_mut() + .unwrap() = *value; } } } From c37f8cd50aaeaa1db4e8c397f18f006014007868 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Thu, 12 Dec 2024 11:25:06 -0300 Subject: [PATCH 18/35] memory machine fix --- riscv-executor/src/memory.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/riscv-executor/src/memory.rs b/riscv-executor/src/memory.rs index c1373e22dc..3949e5d996 100644 --- a/riscv-executor/src/memory.rs +++ b/riscv-executor/src/memory.rs @@ -156,9 +156,9 @@ impl Submachine for MemoryMachine { .1 .resize(degree as usize, 0.into()); } + // m_change is 1 in last row + *cols[Change as usize].1.last_mut().unwrap() = 1.into(); } - // m_change is 1 in last row - *cols[Change as usize].1.last_mut().unwrap() = 1.into(); cols } } From cd35a60c39ea54ea0256ec9a74b295e143ce6c6a Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Thu, 12 Dec 2024 11:40:44 -0300 Subject: [PATCH 19/35] drop the enum --- riscv-executor/src/lib.rs | 57 ++++----------------------------------- 1 file changed, 5 insertions(+), 52 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index bc97c43d87..ea27585757 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -42,56 +42,9 @@ use memory::*; use crate::profiler::Profiler; -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -/// Used to identify operations in the event log -#[allow(non_camel_case_types)] -enum MainInstruction { - set_reg, - get_reg, - affine, - mstore, - mstore_bootloader, - mload, - load_bootloader_input, - assert_bootloader_input, - load_label, - jump, - jump_dyn, - jump_to_bootloader_input, - branch_if_diff_nonzero, - branch_if_diff_equal, - skip_if_equal, - branch_if_diff_greater_than, - is_diff_greater_than, - is_equal_zero, - is_not_equal, - add_wrap, - wrap16, - sub_wrap_with_offset, - sign_extend_byte, - sign_extend_16_bits, - to_signed, - divremu, - mul, - and, - or, - xor, - shl, - shr, - invert_gl, - split_gl, - poseidon_gl, - poseidon2_gl, - affine_256, - mod_256, - ec_add, - ec_double, - commit_public, -} - // TODO: we don't do anything with these yet. Idea is to keep info that is to be given to witgen #[allow(unused)] -struct MainOp(MainInstruction, u32, Vec); +struct MainOp(&'static str, u32, Vec); #[derive(Debug)] struct SubmachineOp { @@ -458,8 +411,8 @@ mod builder { use powdr_number::FieldElement; use crate::{ - BinaryMachine, Elem, ExecMode, Execution, ExecutionTrace, MachineInstance, MainInstruction, - MainOp, MemOperation, MemOperationKind, MemoryMachine, MemoryState, PoseidonGlMachine, + BinaryMachine, Elem, ExecMode, Execution, ExecutionTrace, MachineInstance, MainOp, + MemOperation, MemOperationKind, MemoryMachine, MemoryState, PoseidonGlMachine, PublicsMachine, RegWrite, RegisterMemory, ShiftMachine, SplitGlMachine, Submachine, SubmachineBoxed, SubmachineOp, PC_INITIAL_VAL, }; @@ -628,7 +581,7 @@ mod builder { } } - pub(crate) fn main_op(&mut self, ev: MainInstruction, pc: u32, args: Vec) { + pub(crate) fn main_op(&mut self, ev: &'static str, pc: u32, args: Vec) { if let ExecMode::Trace = self.mode { self.trace.main_ops.push(MainOp(ev, pc, args)); } @@ -1196,7 +1149,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { macro_rules! main_op { ($insn:ident, $($args:expr),*) => { self.proc - .main_op(MainInstruction::$insn, self.proc.get_pc().u(), vec![$($args, )*]) + .main_op(stringify!($insn), self.proc.get_pc().u(), vec![$($args, )*]) }; } From a127cba8d364d9860d7b9af32fa83c54e9a914b0 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Fri, 13 Dec 2024 14:26:47 -0300 Subject: [PATCH 20/35] almost working --- riscv-executor/src/lib.rs | 354 +++++++++++++++++++----------- riscv-executor/src/memory.rs | 14 +- riscv-executor/src/pil.rs | 95 ++++++++ riscv-executor/src/submachines.rs | 45 ++-- 4 files changed, 351 insertions(+), 157 deletions(-) create mode 100644 riscv-executor/src/pil.rs diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index ea27585757..0d6227001b 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -20,7 +20,7 @@ use builder::TraceBuilder; use itertools::Itertools; use powdr_ast::{ - analyzed::Analyzed, + analyzed::{Analyzed, Identity}, asm_analysis::{AnalysisASMFile, CallableSymbol, FunctionStatement, LabelStatement, Machine}, parsed::{ asm::{parse_absolute_path, AssignmentRegister, DebugDirective}, @@ -39,6 +39,7 @@ mod submachines; use submachines::*; mod memory; use memory::*; +mod pil; use crate::profiler::Profiler; @@ -48,8 +49,8 @@ struct MainOp(&'static str, u32, Vec); #[derive(Debug)] struct SubmachineOp { - // TODO: if we move to using witgen here, this will probably be an `identity_id` instead - selector: Option, + // pil identity id of the link + identity_id: u64, // these are the RHS values of the lookup (i.e., inside brackets in the PIL lookup) lookup_args: Vec, // TODO: this is just for the hand-written poseidon_gl submachine, @@ -411,7 +412,7 @@ mod builder { use powdr_number::FieldElement; use crate::{ - BinaryMachine, Elem, ExecMode, Execution, ExecutionTrace, MachineInstance, MainOp, + pil, BinaryMachine, Elem, ExecMode, Execution, ExecutionTrace, MachineInstance, MainOp, MemOperation, MemOperationKind, MemoryMachine, MemoryState, PoseidonGlMachine, PublicsMachine, RegWrite, RegisterMemory, ShiftMachine, SplitGlMachine, Submachine, SubmachineBoxed, SubmachineOp, PC_INITIAL_VAL, @@ -590,7 +591,7 @@ mod builder { pub(crate) fn submachine_op( &mut self, m: MachineInstance, - selector: Option, + identity_id: u64, lookup_args: &[F], extra: &[F], ) { @@ -600,7 +601,7 @@ mod builder { .entry(m) .or_default() .push(SubmachineOp { - selector, + identity_id, lookup_args: lookup_args.to_vec(), extra: extra.to_vec(), }); @@ -752,10 +753,10 @@ mod builder { self.set_next_pc().and(Some(st_line)) } - pub(crate) fn set_mem(&mut self, addr: u32, val: u32, step: u32, selector: u8) { + pub(crate) fn set_mem(&mut self, addr: u32, val: u32, step: u32, identity_id: u64) { self.submachine_op( MachineInstance::main_memory, - Some(selector), + identity_id, &[1.into(), addr.into(), step.into(), val.into()], &[], ); @@ -770,11 +771,11 @@ mod builder { self.mem.insert(addr, val); } - pub(crate) fn get_mem(&mut self, addr: u32, step: u32, selector: u8) -> u32 { + pub(crate) fn get_mem(&mut self, addr: u32, step: u32, identity_id: u64) -> u32 { let val = *self.mem.get(&addr).unwrap_or(&0); self.submachine_op( MachineInstance::main_memory, - Some(selector), + identity_id, &[0.into(), addr.into(), step.into(), val.into()], &[], ); @@ -837,6 +838,8 @@ mod builder { // generate witness for submachines // ---------------------------- + let links = pil::links_from_pil(pil); + let mut link_selector = HashMap::new(); for (m, ops) in self.trace.submachine_ops { let m = match m { MachineInstance::main_memory => "memory", @@ -848,8 +851,11 @@ mod builder { MachineInstance::main_poseidon_gl => "poseidon_gl", }; for op in ops { + let selector = link_selector + .entry(op.identity_id) + .or_insert_with(|| pil::selector_for_link(&links, op.identity_id)); self.submachines[m].borrow_mut().add_operation( - op.selector, + selector.as_deref(), &op.lookup_args, &op.extra, ); @@ -1041,6 +1047,9 @@ struct Executor<'a, 'b, F: FieldElement> { program_cols: HashMap, step: u32, mode: ExecMode, + + pil_links: Vec>, + pil_instruction_links: HashMap<(String, String), Vec>>, } impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { @@ -1083,11 +1092,11 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } /// read register value, updating the register memory machine - fn reg_read(&mut self, step_offset: u32, reg: u32, selector: u8) -> Elem { + fn reg_read(&mut self, step_offset: u32, reg: u32, identity_id: u64) -> Elem { let val = self.proc.get_reg_mem(reg); self.proc.submachine_op( MachineInstance::main_regs, - Some(selector), + identity_id, &[ 0.into(), reg.into(), @@ -1100,10 +1109,10 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } /// write value to register, updating the register memory machine - fn reg_write(&mut self, step_offset: u32, reg: u32, val: Elem, selector: u8) { + fn reg_write(&mut self, step_offset: u32, reg: u32, val: Elem, identity_id: u64) { self.proc.submachine_op( MachineInstance::main_regs, - Some(selector), + identity_id, &[ 1.into(), reg.into(), @@ -1132,6 +1141,16 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } } + /// Gets the identity id for a link associated with a given instruction. Idx + /// is based on the order of links in the optimized pil. + fn link_id(&mut self, from: &str, target: &str, idx: usize) -> u64 { + let entries = self + .pil_instruction_links + .entry((from.to_string(), target.to_string())) + .or_insert_with(|| pil::find_links(&self.pil_links, from, target)); + entries.get(idx).unwrap().id() + } + fn exec_instruction(&mut self, name: &str, args: &[Expression]) -> Vec> { // shorthand macros for setting/getting main machine witness values in the current row macro_rules! set_col { @@ -1177,7 +1196,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { "set_reg" => { let addr = args[0].u(); let val = args[1]; - self.reg_write(0, addr, val, 3); + + let lid = self.link_id(name, "main_regs", 0); + self.reg_write(0, addr, val, lid); set_col!(Y, val); @@ -1190,21 +1211,23 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } "get_reg" => { let addr = args[0].u(); - let val = self.reg_read(0, addr, 0); + let lid = self.link_id(name, "main_regs", 0); + let val = self.reg_read(0, addr, lid); main_op!(get_reg,); vec![val] } "affine" => { let read_reg = args[0].u(); - let val1 = self.reg_read(0, read_reg, 0); + let lid = self.link_id(name, "main_regs", 0); + let val1 = self.reg_read(0, read_reg, lid); let write_reg = args[1].u(); let factor = args[2]; let offset = args[3]; let res = val1.mul(&factor).add(&offset); - self.reg_write(1, write_reg, res, 3); + self.reg_write(1, write_reg, res, lid); set_col!(tmp1_col, val1); main_op!(affine,); @@ -1214,11 +1237,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { "mstore" | "mstore_bootloader" => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let addr1 = self.reg_read(0, read_reg1, 0); - let addr2 = self.reg_read(1, read_reg2, 1); + let lid = self.link_id(name, "main_regs", 0); + let addr1 = self.reg_read(0, read_reg1, lid); + let lid = self.link_id(name, "main_regs", 1); + let addr2 = self.reg_read(1, read_reg2, lid); let offset = args[2].bin(); let read_reg3 = args[3].u(); - let value = self.reg_read(2, read_reg3, 2); + let lid = self.link_id(name, "main_regs", 2); + let value = self.reg_read(2, read_reg3, lid); let addr = addr1.bin() - addr2.bin() + offset; // assumptions from the asm machine @@ -1235,7 +1261,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); let addr = addr as u32; - self.proc.set_mem(addr, value.u(), self.step + 3, 1); + let lid = self.link_id(name, "main_memory", 0); + self.proc.set_mem(addr, value.u(), self.step + 3, lid); set_col!(tmp1_col, addr1); set_col!(tmp2_col, addr2); @@ -1256,20 +1283,24 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } "mload" => { let read_reg = args[0].u(); - let addr1 = self.reg_read(0, read_reg, 0); + let lid = self.link_id(name, "main_regs", 0); + let addr1 = self.reg_read(0, read_reg, lid); let offset = args[1].bin(); let write_addr1 = args[2].u(); let write_addr2 = args[3].u(); let addr = addr1.bin() + offset; + let lid = self.link_id(name, "main_memory", 0); let val = self .proc - .get_mem(addr as u32 & 0xfffffffc, self.step + 1, 0); + .get_mem(addr as u32 & 0xfffffffc, self.step + 1, lid); let rem = addr % 4; - self.reg_write(2, write_addr1, val.into(), 3); - self.reg_write(3, write_addr2, rem.into(), 4); + let lid = self.link_id(name, "main_regs", 1); + self.reg_write(2, write_addr1, val.into(), lid); + let lid = self.link_id(name, "main_regs", 2); + self.reg_write(3, write_addr2, rem.into(), lid); set_col!(tmp1_col, addr1); set_col!(tmp3_col, Elem::from_u32_as_fe(val)); @@ -1291,7 +1322,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } // TODO: update to witness generation for continuations "load_bootloader_input" => { - let addr = self.reg_read(0, args[0].u(), 0); + let lid = self.link_id(name, "main_regs", 0); + let addr = self.reg_read(0, args[0].u(), lid); let write_addr = args[1].u(); let factor = args[2].bin(); let offset = args[3].bin(); @@ -1299,15 +1331,18 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let addr = addr.bin() * factor + offset; let val = self.bootloader_inputs[addr as usize]; - self.reg_write(2, write_addr, val, 3); + let lid = self.link_id(name, "main_regs", 1); + self.reg_write(2, write_addr, val, lid); main_op!(load_bootloader_input,); Vec::new() } // TODO: update to witness generation for continuations "assert_bootloader_input" => { - let addr = self.reg_read(0, args[0].u(), 0); - let val = self.reg_read(1, args[1].u(), 1); + let lid = self.link_id(name, "main_regs", 0); + let addr = self.reg_read(0, args[0].u(), lid); + let lid = self.link_id(name, "main_regs", 1); + let val = self.reg_read(1, args[1].u(), lid); let factor = args[2].bin(); let offset = args[3].bin(); @@ -1322,7 +1357,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { "load_label" => { let write_reg = args[0].u(); let label = args[1]; - self.reg_write(0, write_reg, label, 3); + let lid = self.link_id(name, "main_regs", 0); + self.reg_write(0, write_reg, label, lid); set_col!(tmp1_col, label); @@ -1336,7 +1372,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let next_pc = self.proc.get_pc().u() + 1; let write_reg = args[1].u(); - self.reg_write(0, write_reg, next_pc.into(), 3); + let lid = self.link_id(name, "main_regs", 0); + self.reg_write(0, write_reg, next_pc.into(), lid); self.proc.set_pc(label); @@ -1347,11 +1384,13 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } "jump_dyn" => { let read_reg = args[0].u(); - let addr = self.reg_read(0, read_reg, 0); + let lid = self.link_id(name, "main_regs", 0); + let addr = self.reg_read(0, read_reg, lid); let next_pc = self.proc.get_pc().u() + 1; let write_reg = args[1].u(); - self.reg_write(0, write_reg, next_pc.into(), 3); + let lid = self.link_id(name, "main_regs", 1); + self.reg_write(0, write_reg, next_pc.into(), lid); self.proc.set_pc(addr); @@ -1372,8 +1411,10 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { "branch_if_diff_nonzero" => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let val1 = self.reg_read(0, read_reg1, 0); - let val2 = self.reg_read(1, read_reg2, 1); + let lid = self.link_id(name, "main_regs", 0); + let val1 = self.reg_read(0, read_reg1, lid); + let lid = self.link_id(name, "main_regs", 1); + let val2 = self.reg_read(1, read_reg2, lid); let val: Elem = val1.sub(&val2); let label = args[2]; @@ -1398,8 +1439,10 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { "branch_if_diff_equal" => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let val1 = self.reg_read(0, read_reg1, 0); - let val2 = self.reg_read(1, read_reg2, 1); + let lid = self.link_id(name, "main_regs", 0); + let val1 = self.reg_read(0, read_reg1, lid); + let lid = self.link_id(name, "main_regs", 1); + let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2]; let val: Elem = val1.sub(&val2).sub(&offset); let label = args[3]; @@ -1425,8 +1468,10 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { "skip_if_equal" => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let val1 = self.reg_read(0, read_reg1, 0); - let val2 = self.reg_read(1, read_reg2, 1); + let lid = self.link_id(name, "main_regs", 0); + let val1 = self.reg_read(0, read_reg1, lid); + let lid = self.link_id(name, "main_regs", 1); + let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2]; let cond = args[3]; let val: Elem = val1.sub(&val2).add(&offset); @@ -1452,8 +1497,10 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let read_reg2 = args[1].u(); // We can't call u() because input registers may have come from // a call to `to_signed`, which stores a signed integer. - let val1 = self.reg_read(0, read_reg1, 0); - let val2 = self.reg_read(1, read_reg2, 1); + let lid = self.link_id(name, "main_regs", 0); + let val1 = self.reg_read(0, read_reg1, lid); + let lid = self.link_id(name, "main_regs", 1); + let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2]; let val: Elem = val1.sub(&val2).sub(&offset); let label = args[3]; @@ -1492,15 +1539,18 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { "is_diff_greater_than" => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let val1 = self.reg_read(0, read_reg1, 0); - let val2 = self.reg_read(1, read_reg2, 1); + let lid = self.link_id(name, "main_regs", 0); + let val1 = self.reg_read(0, read_reg1, lid); + let lid = self.link_id(name, "main_regs", 1); + let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2]; let write_reg = args[3].u(); let val = val1.sub(&val2).sub(&offset); let r = if val.bin() > 0 { 1 } else { 0 }; - self.reg_write(2, write_reg, r.into(), 3); + let lid = self.link_id(name, "main_regs", 2); + self.reg_write(2, write_reg, r.into(), lid); set_col!(tmp1_col, val1); set_col!(tmp2_col, val2); @@ -1520,11 +1570,13 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } "is_equal_zero" => { let read_reg = args[0].u(); - let val = self.reg_read(0, read_reg, 0); + let lid = self.link_id(name, "main_regs", 0); + let val = self.reg_read(0, read_reg, lid); let write_reg = args[1].u(); let r = if val.is_zero() { 1 } else { 0 }; - self.reg_write(2, write_reg, r.into(), 3); + let lid = self.link_id(name, "main_regs", 1); + self.reg_write(2, write_reg, r.into(), lid); set_col!(tmp1_col, val); set_col!(XX, val); @@ -1539,13 +1591,16 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { "is_not_equal" => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let val1 = self.reg_read(0, read_reg1, 0); - let val2 = self.reg_read(1, read_reg2, 1); + let lid = self.link_id(name, "main_regs", 0); + let val1 = self.reg_read(0, read_reg1, lid); + let lid = self.link_id(name, "main_regs", 1); + let val2 = self.reg_read(1, read_reg2, lid); let write_reg = args[2].u(); let val: Elem = (val1.bin() - val2.bin()).into(); let r = if !val.is_zero() { 1 } else { 0 }; - self.reg_write(2, write_reg, r.into(), 3); + let lid = self.link_id(name, "main_regs", 2); + self.reg_write(2, write_reg, r.into(), lid); set_col!(tmp1_col, val1); set_col!(tmp2_col, val2); @@ -1562,8 +1617,10 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { "add_wrap" => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let val1 = self.reg_read(0, read_reg1, 0); - let val2 = self.reg_read(1, read_reg2, 1); + let lid = self.link_id(name, "main_regs", 0); + let val1 = self.reg_read(0, read_reg1, lid); + let lid = self.link_id(name, "main_regs", 1); + let val2 = self.reg_read(1, read_reg2, lid); set_col!(tmp1_col, val1); set_col!(tmp2_col, val2); let offset = args[2]; @@ -1576,7 +1633,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { // don't use .u() here: we are deliberately discarding the // higher bits let r = val.bin() as u32; - self.reg_write(2, write_reg, r.into(), 3); + let lid = self.link_id(name, "main_regs", 2); + self.reg_write(2, write_reg, r.into(), lid); set_col!(tmp3_col, Elem::from_u32_as_fe(r)); let v = val.as_i64_from_lower_bytes(); @@ -1599,7 +1657,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } "wrap16" => { let read_reg = args[0].u(); - let val = self.reg_read(0, read_reg, 0); + let lid = self.link_id(name, "main_regs", 0); + let val = self.reg_read(0, read_reg, lid); let factor = args[1].bin(); let write_reg = args[2].u(); let val_offset: Elem = (val.bin() * factor).into(); @@ -1607,7 +1666,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { // don't use .u() here: we are deliberately discarding the // higher bits let r = val_offset.bin() as u32; - self.reg_write(3, write_reg, r.into(), 3); + let lid = self.link_id(name, "main_regs", 1); + self.reg_write(3, write_reg, r.into(), lid); set_col!(tmp1_col, val); set_col!(tmp3_col, Elem::from_u32_as_fe(r)); @@ -1629,15 +1689,18 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { "sub_wrap_with_offset" => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let val1 = self.reg_read(0, read_reg1, 0); - let val2 = self.reg_read(1, read_reg2, 1); + let lid = self.link_id(name, "main_regs", 0); + let val1 = self.reg_read(0, read_reg1, lid); + let lid = self.link_id(name, "main_regs", 1); + let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2]; let write_reg = args[3].u(); let val = val1.sub(&val2).add(&offset); let r_i64: i64 = val.bin() + 0x100000000; let r = r_i64 as u32; - self.reg_write(2, write_reg, r.into(), 3); + let lid = self.link_id(name, "main_regs", 2); + self.reg_write(2, write_reg, r.into(), lid); set_col!(tmp1_col, val1); set_col!(tmp2_col, val2); @@ -1662,13 +1725,15 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } "sign_extend_byte" => { let read_reg = args[0].u(); - let val = self.reg_read(0, read_reg, 0); + let lid = self.link_id(name, "main_regs", 0); + let val = self.reg_read(0, read_reg, lid); let write_reg = args[1].u(); // Sign extend the byte let byte_val = (val.u() as u8) as i8; let extended_val = byte_val as i32 as u32; - self.reg_write(3, write_reg, extended_val.into(), 3); + let lid = self.link_id(name, "main_regs", 1); + self.reg_write(3, write_reg, extended_val.into(), lid); set_col!(tmp1_col, val); set_col!(tmp3_col, Elem::from_u32_as_fe(extended_val)); @@ -1695,7 +1760,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } "sign_extend_16_bits" => { let read_reg = args[0].u(); - let val = self.reg_read(0, read_reg, 0); + let lid = self.link_id(name, "main_regs", 0); + let val = self.reg_read(0, read_reg, lid); let write_reg = args[1].u(); // Perform sign extension on the 16-bit value @@ -1705,7 +1771,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } else { val.u() & 0x0000FFFF }; - self.reg_write(3, write_reg, extended_val.into(), 3); + let lid = self.link_id(name, "main_regs", 1); + self.reg_write(3, write_reg, extended_val.into(), lid); set_col!(tmp1_col, val); set_col!(tmp3_col, Elem::from_u32_as_fe(extended_val)); @@ -1732,11 +1799,13 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } "to_signed" => { let read_reg = args[0].u(); - let val = self.reg_read(0, read_reg, 0); + let lid = self.link_id(name, "main_regs", 0); + let val = self.reg_read(0, read_reg, lid); let write_reg = args[1].u(); let r = val.u() as i32; - self.reg_write(1, write_reg, r.into(), 3); + let lid = self.link_id(name, "main_regs", 1); + self.reg_write(1, write_reg, r.into(), lid); set_col!(tmp1_col, val); set_col!(tmp3_col, Elem::from_i32_as_fe(r)); @@ -1766,8 +1835,10 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { "divremu" => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let val1 = self.reg_read(0, read_reg1, 0); - let val2 = self.reg_read(1, read_reg2, 1); + let lid = self.link_id(name, "main_regs", 0); + let val1 = self.reg_read(0, read_reg1, lid); + let lid = self.link_id(name, "main_regs", 1); + let val2 = self.reg_read(1, read_reg2, lid); let write_reg1 = args[2].u(); let write_reg2 = args[3].u(); @@ -1783,8 +1854,10 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { rem = y; } - self.reg_write(2, write_reg1, div.into(), 3); - self.reg_write(3, write_reg2, rem.into(), 4); + let lid = self.link_id(name, "main_regs", 2); + self.reg_write(2, write_reg1, div.into(), lid); + let lid = self.link_id(name, "main_regs", 3); + self.reg_write(3, write_reg2, rem.into(), lid); set_col!(tmp1_col, val1); set_col!(tmp2_col, val2); @@ -1824,8 +1897,10 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { "mul" => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let val1 = self.reg_read(0, read_reg1, 0); - let val2 = self.reg_read(1, read_reg2, 1); + let lid = self.link_id(name, "main_regs", 0); + let val1 = self.reg_read(0, read_reg1, lid); + let lid = self.link_id(name, "main_regs", 1); + let val2 = self.reg_read(1, read_reg2, lid); let write_reg1 = args[2].u(); let write_reg2 = args[3].u(); @@ -1833,28 +1908,28 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let lo = r as u32; let hi = (r >> 32) as u32; - self.reg_write(2, write_reg1, lo.into(), 3); - self.reg_write(3, write_reg2, hi.into(), 4); + let lid = self.link_id(name, "main_regs", 2); + self.reg_write(2, write_reg1, lo.into(), lid); + let lid = self.link_id(name, "main_regs", 3); + self.reg_write(3, write_reg2, hi.into(), lid); set_col!(tmp1_col, val1); set_col!(tmp2_col, val2); set_col!(tmp3_col, Elem::from_u32_as_fe(lo)); set_col!(tmp4_col, Elem::from_u32_as_fe(hi)); - let selector = 0; - submachine_op!( - main_split_gl, - Some(selector), - &[r.into(), lo.into(), hi.into()], - ); + let lid = self.link_id(name, "main_split_gl", 0); + submachine_op!(main_split_gl, lid, &[r.into(), lo.into(), hi.into()],); main_op!(mul,); Vec::new() } "and" | "or" | "xor" => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let val1 = self.reg_read(0, read_reg1, 0); - let val2 = self.reg_read(1, read_reg2, 1); + let lid = self.link_id(name, "main_regs", 0); + let val1 = self.reg_read(0, read_reg1, lid); + let lid = self.link_id(name, "main_regs", 1); + let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2].bin(); let write_reg = args[3].u(); let val2_offset: Elem = (val2.bin() + offset).into(); @@ -1862,25 +1937,26 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp1_col, val1); set_col!(tmp2_col, val2); - let (r, op_id, selector) = match name { + let (r, op_id) = match name { "and" => { main_op!(and,); - (val1.u() & val2_offset.u(), 0, 0) + (val1.u() & val2_offset.u(), 0) } "or" => { main_op!(or,); - (val1.u() | val2_offset.u(), 1, 1) + (val1.u() | val2_offset.u(), 1) } "xor" => { main_op!(xor,); - (val1.u() ^ val2_offset.u(), 2, 2) + (val1.u() ^ val2_offset.u(), 2) } _ => unreachable!(), }; + let lid = self.link_id(name, "main_binary", 0); submachine_op!( main_binary, - Some(selector), + lid, &[ op_id.into(), val1.into_fe(), @@ -1889,7 +1965,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ], ); - self.reg_write(3, write_reg, r.into(), 3); + let lid = self.link_id(name, "main_regs", 2); + self.reg_write(3, write_reg, r.into(), lid); set_col!(tmp3_col, Elem::from_u32_as_fe(r)); @@ -1898,27 +1975,30 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { "shl" | "shr" => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let val1 = self.reg_read(0, read_reg1, 0); - let val2 = self.reg_read(1, read_reg2, 1); + let lid = self.link_id(name, "main_regs", 0); + let val1 = self.reg_read(0, read_reg1, lid); + let lid = self.link_id(name, "main_regs", 1); + let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2].bin(); let write_reg = args[3].u(); let val2_offset: Elem = (val2.bin() + offset).into(); - let (r, op_id, selector) = match name { + let (r, op_id) = match name { "shl" => { main_op!(shl,); - (val1.u() << val2_offset.u(), 0, 0) + (val1.u() << val2_offset.u(), 0) } "shr" => { main_op!(shr,); - (val1.u() >> val2_offset.u(), 1, 1) + (val1.u() >> val2_offset.u(), 1) } _ => unreachable!(), }; + let lid = self.link_id(name, "main_shift", 0); submachine_op!( main_shift, - Some(selector), + lid, &[ op_id.into(), val1.into_fe(), @@ -1927,7 +2007,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ], ); - self.reg_write(3, write_reg, r.into(), 3); + let lid = self.link_id(name, "main_regs", 2); + self.reg_write(3, write_reg, r.into(), lid); set_col!(tmp1_col, val1); set_col!(tmp2_col, val2); @@ -1938,13 +2019,17 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { "invert_gl" => { let low_addr = args[0].u(); let high_addr = args[1].u(); - let low = self.reg_read(0, low_addr, 0); - let high = self.reg_read(1, high_addr, 1); + let lid = self.link_id(name, "main_regs", 0); + let low = self.reg_read(0, low_addr, lid); + let lid = self.link_id(name, "main_regs", 1); + let high = self.reg_read(1, high_addr, lid); let inv = F::one() / F::from((high.u() as u64) << 32 | low.u() as u64); let inv_u64 = inv.to_integer().try_into_u64().unwrap(); let (low_inv, high_inv) = (inv_u64 as u32, (inv_u64 >> 32) as u32); - self.reg_write(2, low_addr, low_inv.into(), 3); - self.reg_write(3, high_addr, high_inv.into(), 4); + let lid = self.link_id(name, "main_regs", 2); + self.reg_write(2, low_addr, low_inv.into(), lid); + let lid = self.link_id(name, "main_regs", 3); + self.reg_write(3, high_addr, high_inv.into(), lid); set_col!(tmp1_col, low); set_col!(tmp2_col, high); @@ -1952,18 +2037,15 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp4_col, Elem::from_u32_as_fe(high_inv)); set_col!(XX_inv, Elem::Field(inv)); - let selector = 0; - submachine_op!( - main_split_gl, - Some(selector), - &[inv, low_inv.into(), high_inv.into()], - ); + let lid = self.link_id(name, "main_split_gl", 0); + submachine_op!(main_split_gl, lid, &[inv, low_inv.into(), high_inv.into()],); main_op!(invert_gl,); Vec::new() } "split_gl" => { let read_reg = args[0].u(); - let val1 = self.reg_read(0, read_reg, 0); + let lid = self.link_id(name, "main_regs", 0); + let val1 = self.reg_read(0, read_reg, lid); let write_reg1 = args[1].u(); let write_reg2 = args[2].u(); @@ -1974,28 +2056,28 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let lo = (value & 0xffffffff) as u32; let hi = (value >> 32) as u32; - self.reg_write(2, write_reg1, lo.into(), 3); - self.reg_write(3, write_reg2, hi.into(), 4); + let lid = self.link_id(name, "main_regs", 1); + self.reg_write(2, write_reg1, lo.into(), lid); + let lid = self.link_id(name, "main_regs", 2); + self.reg_write(3, write_reg2, hi.into(), lid); set_col!(tmp1_col, val1); set_col!(tmp3_col, Elem::from_u32_as_fe(lo)); set_col!(tmp4_col, Elem::from_u32_as_fe(hi)); - let selector = 0; - submachine_op!( - main_split_gl, - Some(selector), - &[value.into(), lo.into(), hi.into()], - ); + let lid = self.link_id(name, "main_split_gl", 0); + submachine_op!(main_split_gl, lid, &[value.into(), lo.into(), hi.into()],); main_op!(split_gl,); Vec::new() } "poseidon_gl" => { let reg1 = args[0].u(); let reg2 = args[1].u(); - let input_ptr = self.reg_read(0, reg1, 0); + let lid = self.link_id(name, "main_regs", 0); + let input_ptr = self.reg_read(0, reg1, lid); assert!(is_multiple_of_4(input_ptr.u())); - let output_ptr = self.reg_read(1, reg2, 1); + let lid = self.link_id(name, "main_regs", 1); + let output_ptr = self.reg_read(1, reg2, lid); assert!(is_multiple_of_4(output_ptr.u())); set_col!(tmp1_col, input_ptr); @@ -2017,9 +2099,11 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let inputs = (0..12) .map(|i| { - // step/selector of memory reads from the poseidon machine - let lo = self.proc.get_mem(input_ptr.u() + 8 * i, self.step, 2); - let hi = self.proc.get_mem(input_ptr.u() + 8 * i + 4, self.step, 3); + // memory reads from the poseidon machine + let lid = self.link_id("poseidon_gl", "memory", 0); + let lo = self.proc.get_mem(input_ptr.u() + 8 * i, self.step, lid); + let lid = self.link_id("poseidon_gl", "memory", 1); + let hi = self.proc.get_mem(input_ptr.u() + 8 * i + 4, self.step, lid); F::from(((hi as u64) << 32) | lo as u64) }) .collect::>(); @@ -2031,19 +2115,21 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let val = v.to_integer().try_into_u64().unwrap(); let hi = (val >> 32) as u32; let lo = (val & 0xffffffff) as u32; - // step/selector of memory writes from the poseidon machine + // memory writes from the poseidon machine + let lid = self.link_id("poseidon_gl", "main_memory", 2); self.proc - .set_mem(output_ptr.u() + 8 * i as u32, lo, self.step + 1, 4); + .set_mem(output_ptr.u() + 8 * i as u32, lo, self.step + 1, lid); + let lid = self.link_id("poseidon_gl", "main_memory", 3); self.proc - .set_mem(output_ptr.u() + 8 * i as u32 + 4, hi, self.step + 1, 5); - let selector = 1; - submachine_op!(main_split_gl, Some(selector), &[*v, lo.into(), hi.into()],); + .set_mem(output_ptr.u() + 8 * i as u32 + 4, hi, self.step + 1, lid); + let lid = self.link_id("poseidon_gl", "main_split_gl", 0); + submachine_op!(main_split_gl, lid, &[*v, lo.into(), hi.into()],); }); - let selector = 0; + let lid = self.link_id(name, "main_poseidon_gl", 0); submachine_op!( main_poseidon_gl, - Some(selector), + lid, &[input_ptr.into_fe(), output_ptr.into_fe(), self.step.into()], inputs[0], inputs[1], @@ -2070,7 +2156,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { assert!(is_multiple_of_4(input_ptr)); let inputs: [u64; 8] = (0..16) - .map(|i| self.proc.get_mem(input_ptr + i * 4, 0, 0)) // TODO: step/selector for poseidon2 + .map(|i| self.proc.get_mem(input_ptr + i * 4, 0, 0)) .chunks(2) .into_iter() .map(|mut chunk| { @@ -2089,7 +2175,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let output_ptr = self.proc.get_reg_mem(args[1].u()).u(); assert!(is_multiple_of_4(output_ptr)); result.enumerate().for_each(|(i, v)| { - self.proc.set_mem(output_ptr + i as u32 * 4, v, 0, 0); // TODO: step/selector for poseidon2 + self.proc.set_mem(output_ptr + i as u32 * 4, v, 0, 0); }); main_op!(poseidon2_gl,); @@ -2252,12 +2338,15 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { vec![] } "commit_public" => { - let idx = self.reg_read(0, args[0].u(), 0); - let limb = self.reg_read(0, args[1].u(), 1); + let lid = self.link_id(name, "main_regs", 0); + let idx = self.reg_read(0, args[0].u(), lid); + let lid = self.link_id(name, "main_regs", 1); + let limb = self.reg_read(0, args[1].u(), lid); set_col!(tmp1_col, idx); set_col!(tmp2_col, limb); log::debug!("Committing public: idx={idx}, limb={limb}"); - submachine_op!(main_publics, None, &[idx.into_fe(), limb.into_fe()],); + let lid = self.link_id(name, "main_publics", 0); + submachine_op!(main_publics, lid, &[idx.into_fe(), limb.into_fe()],); main_op!(commit_public,); vec![] } @@ -2493,6 +2582,7 @@ pub fn execute( profiling: Option, ) -> Execution { log::info!("Executing..."); + execute_inner( asm, Some(opt_pil), @@ -2574,6 +2664,8 @@ fn execute_inner( .map(|v| Elem::try_from_fe_as_bin(v).unwrap_or(Elem::Field(*v))) .collect(); + let pil_links = opt_pil.map(pil::links_from_pil).unwrap_or_default(); + // We clear the QueryCallback's virtual FS before the execution. (prover_ctx)("Clear").unwrap(); let mut e = Executor { @@ -2585,6 +2677,8 @@ fn execute_inner( program_cols, step: 0, mode, + pil_links, + pil_instruction_links: Default::default(), }; e.init(); diff --git a/riscv-executor/src/memory.rs b/riscv-executor/src/memory.rs index 3949e5d996..c5e247ce23 100644 --- a/riscv-executor/src/memory.rs +++ b/riscv-executor/src/memory.rs @@ -9,8 +9,6 @@ struct Op { step: u32, value: F, write: F, - // each machine that's called via permutation has a selector array, with one entry per incoming permutation. - // This is the idx assigned to the `link` triggering the memory operation. selector_idx: u8, } @@ -51,16 +49,24 @@ impl Submachine for MemoryMachine { &self.namespace } - fn add_operation(&mut self, selector_idx: Option, lookup_args: &[F], _extra: &[F]) { + fn add_operation(&mut self, selector: Option<&str>, lookup_args: &[F], _extra: &[F]) { let [op_id, addr, step, value] = lookup_args[..] else { panic!() }; + // get the idx from the selector + let selector_idx = selector + .map(|s| { + let start = s.find('[').unwrap() + 1; + let end = s.find(']').unwrap(); + s[start..end].parse::().unwrap() + }) + .unwrap(); self.ops.push(Op { addr: addr.to_integer().try_into_u32().unwrap(), step: step.to_integer().try_into_u32().unwrap(), value, write: op_id, - selector_idx: selector_idx.unwrap(), + selector_idx, }); } diff --git a/riscv-executor/src/pil.rs b/riscv-executor/src/pil.rs new file mode 100644 index 0000000000..a6d8da6d06 --- /dev/null +++ b/riscv-executor/src/pil.rs @@ -0,0 +1,95 @@ +use std::ops::ControlFlow; + +use powdr_ast::{ + analyzed::{AlgebraicExpression, Analyzed, Identity, PermutationIdentity}, + parsed::visitor::ExpressionVisitable, +}; +use powdr_number::FieldElement; + +pub fn links_from_pil(pil: &Analyzed) -> Vec> { + pil.identities + .iter() + .filter(|id| matches!(id, Identity::Permutation(_) | Identity::Lookup(_))) + .cloned() + .collect() +} + +fn extract_selector(permutation: &PermutationIdentity) -> String { + match permutation + .right + .selector + .pre_visit_expressions_return(&mut |e| { + if let AlgebraicExpression::Reference(r) = e { + // this makes the assumption that selector names always start + // with "sel" and there is no other array with a name starting + // with "sel" defined in the machine/namespace + if r.name.contains("::sel") && r.name.contains("[") { + return ControlFlow::Break(r.name.clone()); + } + } + ControlFlow::Continue(()) + }) { + ControlFlow::Break(s) => s, + ControlFlow::Continue(_) => panic!("couldn't find selector"), + } +} + +pub fn selector_for_link(links: &[Identity], id: u64) -> Option { + for l in links.iter() { + if let Identity::Permutation(perm) = l { + if perm.id == id { + return Some(extract_selector(perm)); + } + } + if let Identity::Lookup(lookup) = l { + if lookup.id == id { + return None; + } + } + } + panic!("identity {id} doesnt exist") +} + +pub fn find_links( + links: &[Identity], + from: &str, + to: &str, +) -> Vec> { + links + .iter() + .filter(|id| match id { + Identity::Permutation(id) => { + let left = id.left.selector.expr_any(|e| { + if let AlgebraicExpression::Reference(r) = e { + return r.name.contains(from); + } + false + }); + let right = id.right.selector.expr_any(|e| { + if let AlgebraicExpression::Reference(r) = e { + return r.name.contains(to); + } + false + }); + left && right + } + Identity::Lookup(id) => { + let left = id.left.selector.expr_any(|e| { + if let AlgebraicExpression::Reference(r) = e { + return r.name.contains(from); + } + false + }); + let right = id.right.expr_any(|e| { + if let AlgebraicExpression::Reference(r) = e { + return r.name.contains(to); + } + false + }); + left && right + } + _ => false, + }) + .cloned() + .collect() +} diff --git a/riscv-executor/src/submachines.rs b/riscv-executor/src/submachines.rs index 32b3cf921b..f434acd91a 100644 --- a/riscv-executor/src/submachines.rs +++ b/riscv-executor/src/submachines.rs @@ -4,6 +4,10 @@ use powdr_number::{FieldElement, LargeInt}; use std::collections::HashMap; +fn only_column_name(name: &str) -> &str { + name.rfind("::").map(|i| &name[i + 2..]).unwrap_or(name) +} + /// Each submachine kind (i.e., binary, shift) must implement this trait trait SubmachineKind { /// Which of the witness columns are selectors, if any @@ -13,8 +17,7 @@ trait SubmachineKind { /// Add an operation to the submachine trace fn add_operation( trace: &mut SubmachineTrace, - // pil lookup RHS values (including selector idx, if present) - selector_idx: Option, + selector: Option<&str>, lookup_args: &[F], // extra info provided by the executor extra: &[F], @@ -44,7 +47,7 @@ pub trait Submachine { /// current number of rows fn len(&self) -> u32; /// add a new operation to the trace - fn add_operation(&mut self, selector_idx: Option, lookup_args: &[F], extra: &[F]); + fn add_operation(&mut self, selector: Option<&str>, lookup_args: &[F], extra: &[F]); /// finish the trace, padding to the given degree and returning the machine columns. /// Ideally we'd take `self` here, but this is called from a `dyn Trait`... fn finish(&mut self, degree: u32) -> Vec<(String, Vec)>; @@ -89,8 +92,8 @@ impl Submachine for SubmachineImpl self.trace.len() } - fn add_operation(&mut self, selector_idx: Option, lookup_args: &[F], extra: &[F]) { - M::add_operation(&mut self.trace, selector_idx, lookup_args, extra); + fn add_operation(&mut self, selector: Option<&str>, lookup_args: &[F], extra: &[F]) { + M::add_operation(&mut self.trace, selector, lookup_args, extra); } fn finish(&mut self, degree: u32) -> Vec<(String, Vec)> { @@ -222,12 +225,12 @@ impl SubmachineKind for BinaryMachine { fn add_operation( trace: &mut SubmachineTrace, - selector_idx: Option, + selector: Option<&str>, lookup_args: &[F], extra: &[F], ) { assert!(extra.is_empty()); - let selector_idx = selector_idx.unwrap(); + let selector = only_column_name(selector.unwrap()); let [op_id, a, b, c] = lookup_args[..] else { panic!(); }; @@ -310,7 +313,7 @@ impl SubmachineKind for BinaryMachine { trace.set_current_row("B", b); trace.set_current_row("C", c); // latch row: set selector - trace.set_current_row(&format!("{}[{}]", Self::SELECTORS, selector_idx), 1.into()); + trace.set_current_row(selector, 1.into()); } } @@ -322,12 +325,12 @@ impl SubmachineKind for ShiftMachine { fn add_operation( trace: &mut SubmachineTrace, - selector_idx: Option, + selector: Option<&str>, lookup_args: &[F], extra: &[F], ) { assert!(extra.is_empty()); - let selector_idx = selector_idx.unwrap(); + let selector = only_column_name(selector.unwrap()); let [op_id, a, b, c] = lookup_args[..] else { panic!(); }; @@ -406,7 +409,7 @@ impl SubmachineKind for ShiftMachine { trace.set_current_row("A", a); trace.set_current_row("B", b); trace.set_current_row("C", c); - trace.set_current_row(&format!("{}[{}]", Self::SELECTORS, selector_idx), 1.into()); + trace.set_current_row(selector, 1.into()); } } @@ -418,12 +421,12 @@ impl SubmachineKind for SplitGlMachine { fn add_operation( trace: &mut SubmachineTrace, - selector_idx: Option, + selector: Option<&str>, lookup_args: &[F], extra: &[F], ) { assert!(extra.is_empty()); - let selector_idx = selector_idx.unwrap(); + let selector = only_column_name(selector.unwrap()); let [_input, output_lo, output_hi] = lookup_args[..] else { panic!(); }; @@ -544,7 +547,7 @@ impl SubmachineKind for SplitGlMachine { // 7: bytes/lt/gt/was_lt are set by the next row trace.push_row(); - trace.set_current_row(&format!("{}[{}]", Self::SELECTORS, selector_idx), 1.into()); + trace.set_current_row(selector, 1.into()); trace.set_current_row("output_low", lo.into()); trace.set_current_row("output_high", hi.into()); trace.set_current_row("in_acc", hi_and_lo(hi, lo)); @@ -557,11 +560,11 @@ impl SubmachineKind for PublicsMachine { const BLOCK_SIZE: u32 = 1; fn add_operation( trace: &mut SubmachineTrace, - selector_idx: Option, + selector: Option<&str>, lookup_args: &[F], extra: &[F], ) { - assert!(selector_idx.is_none()); + assert!(selector.is_none()); assert!(extra.is_empty()); let [addr, value] = lookup_args[..] else { panic!(); @@ -590,14 +593,14 @@ impl SubmachineKind for PoseidonGlMachine { fn add_operation( trace: &mut SubmachineTrace, - selector_idx: Option, + selector: Option<&str>, lookup_args: &[F], extra: &[F], ) { const STATE_SIZE: usize = 12; const OUTPUT_SIZE: usize = 4; - let selector_idx = selector_idx.unwrap(); + let selector = only_column_name(selector.unwrap()); let [input_addr, output_addr, time_step] = lookup_args[..] else { panic!(); }; @@ -720,11 +723,7 @@ impl SubmachineKind for PoseidonGlMachine { trace.set_current_block(Self::BLOCK_SIZE, "input_addr", input_addr); trace.set_current_block(Self::BLOCK_SIZE, "output_addr", output_addr); // set selector - trace.set_current_block( - Self::BLOCK_SIZE, - &format!("{}[{}]", Self::SELECTORS, selector_idx), - 1.into(), - ); + trace.set_current_block(Self::BLOCK_SIZE, selector, 1.into()); for i in 0..STATE_SIZE { trace.set_current_block(Self::BLOCK_SIZE, INPUT_COLS[i], input[i]); } From f29255ff6b9336ae8c9973ed5e1136e06172cf8f Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Fri, 13 Dec 2024 14:52:37 -0300 Subject: [PATCH 21/35] use enums --- riscv-executor/src/lib.rs | 428 +++++++++++++++++++++++--------------- 1 file changed, 263 insertions(+), 165 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 0d6227001b..3808443243 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -58,19 +58,119 @@ struct SubmachineOp { extra: Vec, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[allow(non_camel_case_types)] -enum MachineInstance { - main_memory, - main_regs, - main_publics, - main_binary, - main_shift, - main_split_gl, - main_poseidon_gl, - // main_poseidon2_gl, - // main_keccakf, - // main_arith, +/// This enum helps us avoid raw strings for instruction names/columns +macro_rules! instructions { + ($($name:ident),*) => { + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[allow(non_camel_case_types)] + enum Instruction { + $($name,)* + } + + impl Instruction { + fn from_name(s: &str) -> Option { + match s { + $(stringify!($name) => Some(Self::$name),)* + _ => None + } + } + + fn column(&self) -> &'static str { + match *self { + $(Self::$name => concat!("main::instr_", stringify!($name)),)* + } + } + } + }; +} + +instructions! { + set_reg, + get_reg, + affine, + mstore, + mstore_bootloader, + mload, + load_bootloader_input, + assert_bootloader_input, + load_label, + jump, + jump_dyn, + jump_to_bootloader_input, + branch_if_diff_nonzero, + branch_if_diff_equal, + skip_if_equal, + branch_if_diff_greater_than, + is_diff_greater_than, + is_equal_zero, + is_not_equal, + add_wrap, + wrap16, + sub_wrap_with_offset, + sign_extend_byte, + sign_extend_16_bits, + to_signed, + divremu, + mul, + and, + or, + xor, + shl, + shr, + invert_gl, + split_gl, + poseidon_gl, + poseidon2_gl, + affine_256, + mod_256, + ec_add, + ec_double, + commit_public, + fail +} + +macro_rules! machine_instances { + ($($name:ident),*) => { + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[allow(non_camel_case_types)] + enum MachineInstance { + $($name,)* + } + + impl MachineInstance { + // fn from_name(s: &str) -> Option { + // match s { + // $(stringify!($name) => Some(Self::$name),)* + // _ => None + // } + // } + + fn name(&self) -> &'static str { + match *self { + $(Self::$name => stringify!($name),)* + } + } + + // fn namespace(&self) -> &'static str { + // match *self { + // $(Self::$name => concat!("main_", stringify!($name)),)* + // } + // } + } + }; +} + +machine_instances! { + memory, + regs, + publics, + binary, + shift, + split_gl, + poseidon_gl + // poseidon2_gl, + // keccakf, + // arith, } /// Initial value of the PC. @@ -755,7 +855,7 @@ mod builder { pub(crate) fn set_mem(&mut self, addr: u32, val: u32, step: u32, identity_id: u64) { self.submachine_op( - MachineInstance::main_memory, + MachineInstance::memory, identity_id, &[1.into(), addr.into(), step.into(), val.into()], &[], @@ -774,7 +874,7 @@ mod builder { pub(crate) fn get_mem(&mut self, addr: u32, step: u32, identity_id: u64) -> u32 { let val = *self.mem.get(&addr).unwrap_or(&0); self.submachine_op( - MachineInstance::main_memory, + MachineInstance::memory, identity_id, &[0.into(), addr.into(), step.into(), val.into()], &[], @@ -841,15 +941,7 @@ mod builder { let links = pil::links_from_pil(pil); let mut link_selector = HashMap::new(); for (m, ops) in self.trace.submachine_ops { - let m = match m { - MachineInstance::main_memory => "memory", - MachineInstance::main_regs => "regs", - MachineInstance::main_binary => "binary", - MachineInstance::main_shift => "shift", - MachineInstance::main_publics => "publics", - MachineInstance::main_split_gl => "split_gl", - MachineInstance::main_poseidon_gl => "poseidon_gl", - }; + let m = m.name(); for op in ops { let selector = link_selector .entry(op.identity_id) @@ -1049,7 +1141,7 @@ struct Executor<'a, 'b, F: FieldElement> { mode: ExecMode, pil_links: Vec>, - pil_instruction_links: HashMap<(String, String), Vec>>, + pil_instruction_links: HashMap<(&'static str, &'static str), Vec>>, } impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { @@ -1095,7 +1187,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { fn reg_read(&mut self, step_offset: u32, reg: u32, identity_id: u64) -> Elem { let val = self.proc.get_reg_mem(reg); self.proc.submachine_op( - MachineInstance::main_regs, + MachineInstance::regs, identity_id, &[ 0.into(), @@ -1111,7 +1203,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { /// write value to register, updating the register memory machine fn reg_write(&mut self, step_offset: u32, reg: u32, val: Elem, identity_id: u64) { self.proc.submachine_op( - MachineInstance::main_regs, + MachineInstance::regs, identity_id, &[ 1.into(), @@ -1141,13 +1233,20 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } } - /// Gets the identity id for a link associated with a given instruction. Idx - /// is based on the order of links in the optimized pil. - fn link_id(&mut self, from: &str, target: &str, idx: usize) -> u64 { + /// Gets the identity id for a link associated with a given instruction. + /// idx is based on the order link appear in the assembly (assumed to be the same in the optimized pil). + fn link_id(&mut self, from: &'static str, target: &'static str, idx: usize) -> u64 { let entries = self .pil_instruction_links - .entry((from.to_string(), target.to_string())) + .entry((from, target)) .or_insert_with(|| pil::find_links(&self.pil_links, from, target)); + println!("{from}->{target} entries: {:?}", entries.len()); + for s in entries + .iter() + .map(|e| pil::selector_for_link(&self.pil_links, e.id())) + { + println!("\t{s:?}"); + } entries.get(idx).unwrap().id() } @@ -1192,12 +1291,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc .set_col(&format!("main::instr_{name}"), Elem::from_u32_as_fe(1)); - let r = match name { - "set_reg" => { + let instr = Instruction::from_name(name).expect("unknown instruction"); + + let r = match instr { + Instruction::set_reg => { let addr = args[0].u(); let val = args[1]; - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); self.reg_write(0, addr, val, lid); set_col!(Y, val); @@ -1209,17 +1310,17 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(set_reg,); Vec::new() } - "get_reg" => { + Instruction::get_reg => { let addr = args[0].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val = self.reg_read(0, addr, lid); main_op!(get_reg,); vec![val] } - "affine" => { + Instruction::affine => { let read_reg = args[0].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val1 = self.reg_read(0, read_reg, lid); let write_reg = args[1].u(); let factor = args[2]; @@ -1234,16 +1335,16 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Vec::new() } - "mstore" | "mstore_bootloader" => { + Instruction::mstore | Instruction::mstore_bootloader => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let addr1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let addr2 = self.reg_read(1, read_reg2, lid); let offset = args[2].bin(); let read_reg3 = args[3].u(); - let lid = self.link_id(name, "main_regs", 2); + let lid = self.link_id(instr.column(), "main_regs", 2); let value = self.reg_read(2, read_reg3, lid); let addr = addr1.bin() - addr2.bin() + offset; @@ -1261,7 +1362,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); let addr = addr as u32; - let lid = self.link_id(name, "main_memory", 0); + let lid = self.link_id(instr.column(), "main_memory", 0); self.proc.set_mem(addr, value.u(), self.step + 3, lid); set_col!(tmp1_col, addr1); @@ -1281,9 +1382,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } Vec::new() } - "mload" => { + Instruction::mload => { let read_reg = args[0].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let addr1 = self.reg_read(0, read_reg, lid); let offset = args[1].bin(); let write_addr1 = args[2].u(); @@ -1291,15 +1392,15 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let addr = addr1.bin() + offset; - let lid = self.link_id(name, "main_memory", 0); + let lid = self.link_id(instr.column(), "main_memory", 0); let val = self .proc .get_mem(addr as u32 & 0xfffffffc, self.step + 1, lid); let rem = addr % 4; - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); self.reg_write(2, write_addr1, val.into(), lid); - let lid = self.link_id(name, "main_regs", 2); + let lid = self.link_id(instr.column(), "main_regs", 2); self.reg_write(3, write_addr2, rem.into(), lid); set_col!(tmp1_col, addr1); @@ -1321,8 +1422,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Vec::new() } // TODO: update to witness generation for continuations - "load_bootloader_input" => { - let lid = self.link_id(name, "main_regs", 0); + Instruction::load_bootloader_input => { + let lid = self.link_id(instr.column(), "main_regs", 0); let addr = self.reg_read(0, args[0].u(), lid); let write_addr = args[1].u(); let factor = args[2].bin(); @@ -1331,17 +1432,17 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let addr = addr.bin() * factor + offset; let val = self.bootloader_inputs[addr as usize]; - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); self.reg_write(2, write_addr, val, lid); main_op!(load_bootloader_input,); Vec::new() } // TODO: update to witness generation for continuations - "assert_bootloader_input" => { - let lid = self.link_id(name, "main_regs", 0); + Instruction::assert_bootloader_input => { + let lid = self.link_id(instr.column(), "main_regs", 0); let addr = self.reg_read(0, args[0].u(), lid); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let val = self.reg_read(1, args[1].u(), lid); let factor = args[2].bin(); let offset = args[3].bin(); @@ -1354,10 +1455,10 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(assert_bootloader_input,); Vec::new() } - "load_label" => { + Instruction::load_label => { let write_reg = args[0].u(); let label = args[1]; - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); self.reg_write(0, write_reg, label, lid); set_col!(tmp1_col, label); @@ -1367,12 +1468,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(load_label,); Vec::new() } - "jump" => { + Instruction::jump => { let label = args[0]; let next_pc = self.proc.get_pc().u() + 1; let write_reg = args[1].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); self.reg_write(0, write_reg, next_pc.into(), lid); self.proc.set_pc(label); @@ -1382,14 +1483,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(jump,); Vec::new() } - "jump_dyn" => { + Instruction::jump_dyn => { let read_reg = args[0].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let addr = self.reg_read(0, read_reg, lid); let next_pc = self.proc.get_pc().u() + 1; let write_reg = args[1].u(); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); self.reg_write(0, write_reg, next_pc.into(), lid); self.proc.set_pc(addr); @@ -1400,7 +1501,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Vec::new() } // TODO: update to witness generation for continuations - "jump_to_bootloader_input" => { + Instruction::jump_to_bootloader_input => { let bootloader_input_idx = args[0].bin() as usize; let addr = self.bootloader_inputs[bootloader_input_idx]; self.proc.set_pc(addr); @@ -1408,12 +1509,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(jump_to_bootloader_input,); Vec::new() } - "branch_if_diff_nonzero" => { + Instruction::branch_if_diff_nonzero => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let val: Elem = val1.sub(&val2); @@ -1436,12 +1537,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(branch_if_diff_nonzero,); Vec::new() } - "branch_if_diff_equal" => { + Instruction::branch_if_diff_equal => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2]; let val: Elem = val1.sub(&val2).sub(&offset); @@ -1465,12 +1566,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(branch_if_diff_equal,); Vec::new() } - "skip_if_equal" => { + Instruction::skip_if_equal => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2]; let cond = args[3]; @@ -1492,14 +1593,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(skip_if_equal,); Vec::new() } - "branch_if_diff_greater_than" => { + Instruction::branch_if_diff_greater_than => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); // We can't call u() because input registers may have come from // a call to `to_signed`, which stores a signed integer. - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2]; let val: Elem = val1.sub(&val2).sub(&offset); @@ -1536,12 +1637,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(branch_if_diff_greater_than,); Vec::new() } - "is_diff_greater_than" => { + Instruction::is_diff_greater_than => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2]; @@ -1549,7 +1650,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let val = val1.sub(&val2).sub(&offset); let r = if val.bin() > 0 { 1 } else { 0 }; - let lid = self.link_id(name, "main_regs", 2); + let lid = self.link_id(instr.column(), "main_regs", 2); self.reg_write(2, write_reg, r.into(), lid); set_col!(tmp1_col, val1); @@ -1568,14 +1669,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(is_diff_greater_than,); Vec::new() } - "is_equal_zero" => { + Instruction::is_equal_zero => { let read_reg = args[0].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val = self.reg_read(0, read_reg, lid); let write_reg = args[1].u(); let r = if val.is_zero() { 1 } else { 0 }; - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); self.reg_write(2, write_reg, r.into(), lid); set_col!(tmp1_col, val); @@ -1588,18 +1689,18 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(is_equal_zero,); Vec::new() } - "is_not_equal" => { + Instruction::is_not_equal => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let write_reg = args[2].u(); let val: Elem = (val1.bin() - val2.bin()).into(); let r = if !val.is_zero() { 1 } else { 0 }; - let lid = self.link_id(name, "main_regs", 2); + let lid = self.link_id(instr.column(), "main_regs", 2); self.reg_write(2, write_reg, r.into(), lid); set_col!(tmp1_col, val1); @@ -1614,12 +1715,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(is_not_equal,); Vec::new() } - "add_wrap" => { + Instruction::add_wrap => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); set_col!(tmp1_col, val1); set_col!(tmp2_col, val2); @@ -1633,7 +1734,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { // don't use .u() here: we are deliberately discarding the // higher bits let r = val.bin() as u32; - let lid = self.link_id(name, "main_regs", 2); + let lid = self.link_id(instr.column(), "main_regs", 2); self.reg_write(2, write_reg, r.into(), lid); set_col!(tmp3_col, Elem::from_u32_as_fe(r)); @@ -1655,9 +1756,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(add_wrap,); Vec::new() } - "wrap16" => { + Instruction::wrap16 => { let read_reg = args[0].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val = self.reg_read(0, read_reg, lid); let factor = args[1].bin(); let write_reg = args[2].u(); @@ -1666,7 +1767,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { // don't use .u() here: we are deliberately discarding the // higher bits let r = val_offset.bin() as u32; - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); self.reg_write(3, write_reg, r.into(), lid); set_col!(tmp1_col, val); @@ -1686,12 +1787,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(wrap16,); Vec::new() } - "sub_wrap_with_offset" => { + Instruction::sub_wrap_with_offset => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2]; let write_reg = args[3].u(); @@ -1699,7 +1800,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let r_i64: i64 = val.bin() + 0x100000000; let r = r_i64 as u32; - let lid = self.link_id(name, "main_regs", 2); + let lid = self.link_id(instr.column(), "main_regs", 2); self.reg_write(2, write_reg, r.into(), lid); set_col!(tmp1_col, val1); @@ -1723,16 +1824,16 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(sub_wrap_with_offset,); Vec::new() } - "sign_extend_byte" => { + Instruction::sign_extend_byte => { let read_reg = args[0].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val = self.reg_read(0, read_reg, lid); let write_reg = args[1].u(); // Sign extend the byte let byte_val = (val.u() as u8) as i8; let extended_val = byte_val as i32 as u32; - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); self.reg_write(3, write_reg, extended_val.into(), lid); set_col!(tmp1_col, val); @@ -1758,9 +1859,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(sign_extend_byte,); Vec::new() } - "sign_extend_16_bits" => { + Instruction::sign_extend_16_bits => { let read_reg = args[0].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val = self.reg_read(0, read_reg, lid); let write_reg = args[1].u(); @@ -1771,7 +1872,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } else { val.u() & 0x0000FFFF }; - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); self.reg_write(3, write_reg, extended_val.into(), lid); set_col!(tmp1_col, val); @@ -1797,14 +1898,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(sign_extend_16_bits,); Vec::new() } - "to_signed" => { + Instruction::to_signed => { let read_reg = args[0].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val = self.reg_read(0, read_reg, lid); let write_reg = args[1].u(); let r = val.u() as i32; - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); self.reg_write(1, write_reg, r.into(), lid); set_col!(tmp1_col, val); @@ -1828,16 +1929,16 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(to_signed,); Vec::new() } - "fail" => { + Instruction::fail => { // TODO: handle it better panic!("reached a fail instruction") } - "divremu" => { + Instruction::divremu => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let write_reg1 = args[2].u(); let write_reg2 = args[3].u(); @@ -1854,9 +1955,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { rem = y; } - let lid = self.link_id(name, "main_regs", 2); + let lid = self.link_id(instr.column(), "main_regs", 2); self.reg_write(2, write_reg1, div.into(), lid); - let lid = self.link_id(name, "main_regs", 3); + let lid = self.link_id(instr.column(), "main_regs", 3); self.reg_write(3, write_reg2, rem.into(), lid); set_col!(tmp1_col, val1); @@ -1894,12 +1995,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(divremu,); Vec::new() } - "mul" => { + Instruction::mul => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let write_reg1 = args[2].u(); let write_reg2 = args[3].u(); @@ -1908,9 +2009,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let lo = r as u32; let hi = (r >> 32) as u32; - let lid = self.link_id(name, "main_regs", 2); + let lid = self.link_id(instr.column(), "main_regs", 2); self.reg_write(2, write_reg1, lo.into(), lid); - let lid = self.link_id(name, "main_regs", 3); + let lid = self.link_id(instr.column(), "main_regs", 3); self.reg_write(3, write_reg2, hi.into(), lid); set_col!(tmp1_col, val1); @@ -1918,17 +2019,17 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp3_col, Elem::from_u32_as_fe(lo)); set_col!(tmp4_col, Elem::from_u32_as_fe(hi)); - let lid = self.link_id(name, "main_split_gl", 0); - submachine_op!(main_split_gl, lid, &[r.into(), lo.into(), hi.into()],); + let lid = self.link_id(instr.column(), "main_split_gl", 0); + submachine_op!(split_gl, lid, &[r.into(), lo.into(), hi.into()],); main_op!(mul,); Vec::new() } - "and" | "or" | "xor" => { + Instruction::and | Instruction::or | Instruction::xor => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2].bin(); let write_reg = args[3].u(); @@ -1953,9 +2054,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { _ => unreachable!(), }; - let lid = self.link_id(name, "main_binary", 0); + let lid = self.link_id(instr.column(), "main_binary", 0); submachine_op!( - main_binary, + binary, lid, &[ op_id.into(), @@ -1965,19 +2066,19 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ], ); - let lid = self.link_id(name, "main_regs", 2); + let lid = self.link_id(instr.column(), "main_regs", 2); self.reg_write(3, write_reg, r.into(), lid); set_col!(tmp3_col, Elem::from_u32_as_fe(r)); Vec::new() } - "shl" | "shr" => { + Instruction::shl | Instruction::shr => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2].bin(); let write_reg = args[3].u(); @@ -1995,9 +2096,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { _ => unreachable!(), }; - let lid = self.link_id(name, "main_shift", 0); + let lid = self.link_id(instr.column(), "main_shift", 0); submachine_op!( - main_shift, + shift, lid, &[ op_id.into(), @@ -2007,7 +2108,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ], ); - let lid = self.link_id(name, "main_regs", 2); + let lid = self.link_id(instr.column(), "main_regs", 2); self.reg_write(3, write_reg, r.into(), lid); set_col!(tmp1_col, val1); @@ -2016,19 +2117,19 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Vec::new() } - "invert_gl" => { + Instruction::invert_gl => { let low_addr = args[0].u(); let high_addr = args[1].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let low = self.reg_read(0, low_addr, lid); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let high = self.reg_read(1, high_addr, lid); let inv = F::one() / F::from((high.u() as u64) << 32 | low.u() as u64); let inv_u64 = inv.to_integer().try_into_u64().unwrap(); let (low_inv, high_inv) = (inv_u64 as u32, (inv_u64 >> 32) as u32); - let lid = self.link_id(name, "main_regs", 2); + let lid = self.link_id(instr.column(), "main_regs", 2); self.reg_write(2, low_addr, low_inv.into(), lid); - let lid = self.link_id(name, "main_regs", 3); + let lid = self.link_id(instr.column(), "main_regs", 3); self.reg_write(3, high_addr, high_inv.into(), lid); set_col!(tmp1_col, low); @@ -2037,14 +2138,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp4_col, Elem::from_u32_as_fe(high_inv)); set_col!(XX_inv, Elem::Field(inv)); - let lid = self.link_id(name, "main_split_gl", 0); - submachine_op!(main_split_gl, lid, &[inv, low_inv.into(), high_inv.into()],); + let lid = self.link_id(instr.column(), "main_split_gl", 0); + submachine_op!(split_gl, lid, &[inv, low_inv.into(), high_inv.into()],); main_op!(invert_gl,); Vec::new() } - "split_gl" => { + Instruction::split_gl => { let read_reg = args[0].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let val1 = self.reg_read(0, read_reg, lid); let write_reg1 = args[1].u(); let write_reg2 = args[2].u(); @@ -2056,27 +2157,27 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let lo = (value & 0xffffffff) as u32; let hi = (value >> 32) as u32; - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); self.reg_write(2, write_reg1, lo.into(), lid); - let lid = self.link_id(name, "main_regs", 2); + let lid = self.link_id(instr.column(), "main_regs", 2); self.reg_write(3, write_reg2, hi.into(), lid); set_col!(tmp1_col, val1); set_col!(tmp3_col, Elem::from_u32_as_fe(lo)); set_col!(tmp4_col, Elem::from_u32_as_fe(hi)); - let lid = self.link_id(name, "main_split_gl", 0); - submachine_op!(main_split_gl, lid, &[value.into(), lo.into(), hi.into()],); + let lid = self.link_id(instr.column(), "main_split_gl", 0); + submachine_op!(split_gl, lid, &[value.into(), lo.into(), hi.into()],); main_op!(split_gl,); Vec::new() } - "poseidon_gl" => { + Instruction::poseidon_gl => { let reg1 = args[0].u(); let reg2 = args[1].u(); - let lid = self.link_id(name, "main_regs", 0); + let lid = self.link_id(instr.column(), "main_regs", 0); let input_ptr = self.reg_read(0, reg1, lid); assert!(is_multiple_of_4(input_ptr.u())); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let output_ptr = self.reg_read(1, reg2, lid); assert!(is_multiple_of_4(output_ptr.u())); @@ -2116,19 +2217,19 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let hi = (val >> 32) as u32; let lo = (val & 0xffffffff) as u32; // memory writes from the poseidon machine - let lid = self.link_id("poseidon_gl", "main_memory", 2); + let lid = self.link_id("main_poseidon_gl", "main_memory", 2); self.proc .set_mem(output_ptr.u() + 8 * i as u32, lo, self.step + 1, lid); - let lid = self.link_id("poseidon_gl", "main_memory", 3); + let lid = self.link_id("main_poseidon_gl", "main_memory", 3); self.proc .set_mem(output_ptr.u() + 8 * i as u32 + 4, hi, self.step + 1, lid); - let lid = self.link_id("poseidon_gl", "main_split_gl", 0); - submachine_op!(main_split_gl, lid, &[*v, lo.into(), hi.into()],); + let lid = self.link_id("main_poseidon_gl", "main_split_gl", 0); + submachine_op!(split_gl, lid, &[*v, lo.into(), hi.into()],); }); - let lid = self.link_id(name, "main_poseidon_gl", 0); + let lid = self.link_id(instr.column(), "main_poseidon_gl", 0); submachine_op!( - main_poseidon_gl, + poseidon_gl, lid, &[input_ptr.into_fe(), output_ptr.into_fe(), self.step.into()], inputs[0], @@ -2151,7 +2252,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(poseidon_gl,); vec![] } - "poseidon2_gl" => { + Instruction::poseidon2_gl => { let input_ptr = self.proc.get_reg_mem(args[0].u()).u(); assert!(is_multiple_of_4(input_ptr)); @@ -2181,7 +2282,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(poseidon2_gl,); vec![] } - "affine_256" => { + Instruction::affine_256 => { // a * b + c = d let input_ptr_a = self.proc.get_reg_mem(args[0].u()).u(); assert!(is_multiple_of_4(input_ptr_a)); @@ -2224,7 +2325,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(affine_256,); vec![] } - "mod_256" => { + Instruction::mod_256 => { // a mod b = c let input_ptr_a = self.proc.get_reg_mem(args[0].u()).u(); assert!(is_multiple_of_4(input_ptr_a)); @@ -2257,7 +2358,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(mod_256,); vec![] } - "ec_add" => { + Instruction::ec_add => { // a + b = c let input_ptr_a = self.proc.get_reg_mem(args[0].u()).u(); assert!(is_multiple_of_4(input_ptr_a)); @@ -2301,7 +2402,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(ec_add,); vec![] } - "ec_double" => { + Instruction::ec_double => { // a * 2 = b let input_ptr_a = self.proc.get_reg_mem(args[0].u()).u(); assert!(is_multiple_of_4(input_ptr_a)); @@ -2337,22 +2438,19 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { main_op!(ec_double,); vec![] } - "commit_public" => { - let lid = self.link_id(name, "main_regs", 0); + Instruction::commit_public => { + let lid = self.link_id(instr.column(), "main_regs", 0); let idx = self.reg_read(0, args[0].u(), lid); - let lid = self.link_id(name, "main_regs", 1); + let lid = self.link_id(instr.column(), "main_regs", 1); let limb = self.reg_read(0, args[1].u(), lid); set_col!(tmp1_col, idx); set_col!(tmp2_col, limb); log::debug!("Committing public: idx={idx}, limb={limb}"); - let lid = self.link_id(name, "main_publics", 0); - submachine_op!(main_publics, lid, &[idx.into_fe(), limb.into_fe()],); + let lid = self.link_id(instr.column(), "main_publics", 0); + submachine_op!(publics, lid, &[idx.into_fe(), limb.into_fe()],); main_op!(commit_public,); vec![] } - instr => { - panic!("unknown instruction: {instr}"); - } }; r From ad1920d23923f2b1128e594f1cc47d0dfe6e2ad8 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Fri, 13 Dec 2024 15:08:18 -0300 Subject: [PATCH 22/35] fix --- riscv-executor/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 3808443243..6806097d83 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -1328,6 +1328,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let res = val1.mul(&factor).add(&offset); + let lid = self.link_id(instr.column(), "main_regs", 1); self.reg_write(1, write_reg, res, lid); set_col!(tmp1_col, val1); From d4fabc2493578aab7b5c80406772066868b3088a Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Fri, 13 Dec 2024 15:42:44 -0300 Subject: [PATCH 23/35] fix --- riscv-executor/src/lib.rs | 182 +++++++++++++++++++------------------- riscv-executor/src/pil.rs | 49 ++++++++++ 2 files changed, 141 insertions(+), 90 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 6806097d83..8d32004d95 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -75,7 +75,7 @@ macro_rules! instructions { } } - fn column(&self) -> &'static str { + fn flag(&self) -> &'static str { match *self { $(Self::$name => concat!("main::instr_", stringify!($name)),)* } @@ -1235,18 +1235,20 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { /// Gets the identity id for a link associated with a given instruction. /// idx is based on the order link appear in the assembly (assumed to be the same in the optimized pil). + fn instr_link_id(&mut self, instr: Instruction, target: &'static str, idx: usize) -> u64 { + let entries = self + .pil_instruction_links + .entry((instr.flag(), target)) + .or_insert_with(|| pil::find_instruction_links(&self.pil_links, instr.flag(), target)); + entries.get(idx).unwrap().id() + } + + /// Find the identity id of a link. fn link_id(&mut self, from: &'static str, target: &'static str, idx: usize) -> u64 { let entries = self .pil_instruction_links .entry((from, target)) .or_insert_with(|| pil::find_links(&self.pil_links, from, target)); - println!("{from}->{target} entries: {:?}", entries.len()); - for s in entries - .iter() - .map(|e| pil::selector_for_link(&self.pil_links, e.id())) - { - println!("\t{s:?}"); - } entries.get(idx).unwrap().id() } @@ -1298,7 +1300,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let addr = args[0].u(); let val = args[1]; - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); self.reg_write(0, addr, val, lid); set_col!(Y, val); @@ -1312,7 +1314,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } Instruction::get_reg => { let addr = args[0].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val = self.reg_read(0, addr, lid); main_op!(get_reg,); @@ -1320,7 +1322,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } Instruction::affine => { let read_reg = args[0].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val1 = self.reg_read(0, read_reg, lid); let write_reg = args[1].u(); let factor = args[2]; @@ -1328,7 +1330,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let res = val1.mul(&factor).add(&offset); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); self.reg_write(1, write_reg, res, lid); set_col!(tmp1_col, val1); @@ -1339,13 +1341,13 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Instruction::mstore | Instruction::mstore_bootloader => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let addr1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let addr2 = self.reg_read(1, read_reg2, lid); let offset = args[2].bin(); let read_reg3 = args[3].u(); - let lid = self.link_id(instr.column(), "main_regs", 2); + let lid = self.instr_link_id(instr, "main_regs", 2); let value = self.reg_read(2, read_reg3, lid); let addr = addr1.bin() - addr2.bin() + offset; @@ -1363,7 +1365,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); let addr = addr as u32; - let lid = self.link_id(instr.column(), "main_memory", 0); + let lid = self.instr_link_id(instr, "main_memory", 0); self.proc.set_mem(addr, value.u(), self.step + 3, lid); set_col!(tmp1_col, addr1); @@ -1385,7 +1387,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } Instruction::mload => { let read_reg = args[0].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let addr1 = self.reg_read(0, read_reg, lid); let offset = args[1].bin(); let write_addr1 = args[2].u(); @@ -1393,15 +1395,15 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let addr = addr1.bin() + offset; - let lid = self.link_id(instr.column(), "main_memory", 0); + let lid = self.instr_link_id(instr, "main_memory", 0); let val = self .proc .get_mem(addr as u32 & 0xfffffffc, self.step + 1, lid); let rem = addr % 4; - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); self.reg_write(2, write_addr1, val.into(), lid); - let lid = self.link_id(instr.column(), "main_regs", 2); + let lid = self.instr_link_id(instr, "main_regs", 2); self.reg_write(3, write_addr2, rem.into(), lid); set_col!(tmp1_col, addr1); @@ -1424,7 +1426,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } // TODO: update to witness generation for continuations Instruction::load_bootloader_input => { - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let addr = self.reg_read(0, args[0].u(), lid); let write_addr = args[1].u(); let factor = args[2].bin(); @@ -1433,7 +1435,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let addr = addr.bin() * factor + offset; let val = self.bootloader_inputs[addr as usize]; - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); self.reg_write(2, write_addr, val, lid); main_op!(load_bootloader_input,); @@ -1441,9 +1443,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } // TODO: update to witness generation for continuations Instruction::assert_bootloader_input => { - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let addr = self.reg_read(0, args[0].u(), lid); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let val = self.reg_read(1, args[1].u(), lid); let factor = args[2].bin(); let offset = args[3].bin(); @@ -1459,7 +1461,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Instruction::load_label => { let write_reg = args[0].u(); let label = args[1]; - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); self.reg_write(0, write_reg, label, lid); set_col!(tmp1_col, label); @@ -1474,7 +1476,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let next_pc = self.proc.get_pc().u() + 1; let write_reg = args[1].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); self.reg_write(0, write_reg, next_pc.into(), lid); self.proc.set_pc(label); @@ -1486,12 +1488,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } Instruction::jump_dyn => { let read_reg = args[0].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let addr = self.reg_read(0, read_reg, lid); let next_pc = self.proc.get_pc().u() + 1; let write_reg = args[1].u(); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); self.reg_write(0, write_reg, next_pc.into(), lid); self.proc.set_pc(addr); @@ -1513,9 +1515,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Instruction::branch_if_diff_nonzero => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let val: Elem = val1.sub(&val2); @@ -1541,9 +1543,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Instruction::branch_if_diff_equal => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2]; let val: Elem = val1.sub(&val2).sub(&offset); @@ -1570,9 +1572,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Instruction::skip_if_equal => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2]; let cond = args[3]; @@ -1599,9 +1601,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let read_reg2 = args[1].u(); // We can't call u() because input registers may have come from // a call to `to_signed`, which stores a signed integer. - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2]; let val: Elem = val1.sub(&val2).sub(&offset); @@ -1641,9 +1643,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Instruction::is_diff_greater_than => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2]; @@ -1651,7 +1653,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let val = val1.sub(&val2).sub(&offset); let r = if val.bin() > 0 { 1 } else { 0 }; - let lid = self.link_id(instr.column(), "main_regs", 2); + let lid = self.instr_link_id(instr, "main_regs", 2); self.reg_write(2, write_reg, r.into(), lid); set_col!(tmp1_col, val1); @@ -1672,12 +1674,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } Instruction::is_equal_zero => { let read_reg = args[0].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val = self.reg_read(0, read_reg, lid); let write_reg = args[1].u(); let r = if val.is_zero() { 1 } else { 0 }; - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); self.reg_write(2, write_reg, r.into(), lid); set_col!(tmp1_col, val); @@ -1693,15 +1695,15 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Instruction::is_not_equal => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let write_reg = args[2].u(); let val: Elem = (val1.bin() - val2.bin()).into(); let r = if !val.is_zero() { 1 } else { 0 }; - let lid = self.link_id(instr.column(), "main_regs", 2); + let lid = self.instr_link_id(instr, "main_regs", 2); self.reg_write(2, write_reg, r.into(), lid); set_col!(tmp1_col, val1); @@ -1719,9 +1721,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Instruction::add_wrap => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); set_col!(tmp1_col, val1); set_col!(tmp2_col, val2); @@ -1735,7 +1737,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { // don't use .u() here: we are deliberately discarding the // higher bits let r = val.bin() as u32; - let lid = self.link_id(instr.column(), "main_regs", 2); + let lid = self.instr_link_id(instr, "main_regs", 2); self.reg_write(2, write_reg, r.into(), lid); set_col!(tmp3_col, Elem::from_u32_as_fe(r)); @@ -1759,7 +1761,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } Instruction::wrap16 => { let read_reg = args[0].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val = self.reg_read(0, read_reg, lid); let factor = args[1].bin(); let write_reg = args[2].u(); @@ -1768,7 +1770,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { // don't use .u() here: we are deliberately discarding the // higher bits let r = val_offset.bin() as u32; - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); self.reg_write(3, write_reg, r.into(), lid); set_col!(tmp1_col, val); @@ -1791,9 +1793,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Instruction::sub_wrap_with_offset => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2]; let write_reg = args[3].u(); @@ -1801,7 +1803,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let r_i64: i64 = val.bin() + 0x100000000; let r = r_i64 as u32; - let lid = self.link_id(instr.column(), "main_regs", 2); + let lid = self.instr_link_id(instr, "main_regs", 2); self.reg_write(2, write_reg, r.into(), lid); set_col!(tmp1_col, val1); @@ -1827,14 +1829,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } Instruction::sign_extend_byte => { let read_reg = args[0].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val = self.reg_read(0, read_reg, lid); let write_reg = args[1].u(); // Sign extend the byte let byte_val = (val.u() as u8) as i8; let extended_val = byte_val as i32 as u32; - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); self.reg_write(3, write_reg, extended_val.into(), lid); set_col!(tmp1_col, val); @@ -1862,7 +1864,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } Instruction::sign_extend_16_bits => { let read_reg = args[0].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val = self.reg_read(0, read_reg, lid); let write_reg = args[1].u(); @@ -1873,7 +1875,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } else { val.u() & 0x0000FFFF }; - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); self.reg_write(3, write_reg, extended_val.into(), lid); set_col!(tmp1_col, val); @@ -1901,12 +1903,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } Instruction::to_signed => { let read_reg = args[0].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val = self.reg_read(0, read_reg, lid); let write_reg = args[1].u(); let r = val.u() as i32; - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); self.reg_write(1, write_reg, r.into(), lid); set_col!(tmp1_col, val); @@ -1937,9 +1939,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Instruction::divremu => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let write_reg1 = args[2].u(); let write_reg2 = args[3].u(); @@ -1956,9 +1958,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { rem = y; } - let lid = self.link_id(instr.column(), "main_regs", 2); + let lid = self.instr_link_id(instr, "main_regs", 2); self.reg_write(2, write_reg1, div.into(), lid); - let lid = self.link_id(instr.column(), "main_regs", 3); + let lid = self.instr_link_id(instr, "main_regs", 3); self.reg_write(3, write_reg2, rem.into(), lid); set_col!(tmp1_col, val1); @@ -1999,9 +2001,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Instruction::mul => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let write_reg1 = args[2].u(); let write_reg2 = args[3].u(); @@ -2010,9 +2012,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let lo = r as u32; let hi = (r >> 32) as u32; - let lid = self.link_id(instr.column(), "main_regs", 2); + let lid = self.instr_link_id(instr, "main_regs", 2); self.reg_write(2, write_reg1, lo.into(), lid); - let lid = self.link_id(instr.column(), "main_regs", 3); + let lid = self.instr_link_id(instr, "main_regs", 3); self.reg_write(3, write_reg2, hi.into(), lid); set_col!(tmp1_col, val1); @@ -2020,7 +2022,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp3_col, Elem::from_u32_as_fe(lo)); set_col!(tmp4_col, Elem::from_u32_as_fe(hi)); - let lid = self.link_id(instr.column(), "main_split_gl", 0); + let lid = self.instr_link_id(instr, "main_split_gl", 0); submachine_op!(split_gl, lid, &[r.into(), lo.into(), hi.into()],); main_op!(mul,); Vec::new() @@ -2028,9 +2030,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Instruction::and | Instruction::or | Instruction::xor => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2].bin(); let write_reg = args[3].u(); @@ -2055,7 +2057,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { _ => unreachable!(), }; - let lid = self.link_id(instr.column(), "main_binary", 0); + let lid = self.instr_link_id(instr, "main_binary", 0); submachine_op!( binary, lid, @@ -2067,7 +2069,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ], ); - let lid = self.link_id(instr.column(), "main_regs", 2); + let lid = self.instr_link_id(instr, "main_regs", 2); self.reg_write(3, write_reg, r.into(), lid); set_col!(tmp3_col, Elem::from_u32_as_fe(r)); @@ -2077,9 +2079,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Instruction::shl | Instruction::shr => { let read_reg1 = args[0].u(); let read_reg2 = args[1].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val1 = self.reg_read(0, read_reg1, lid); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let val2 = self.reg_read(1, read_reg2, lid); let offset = args[2].bin(); let write_reg = args[3].u(); @@ -2097,7 +2099,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { _ => unreachable!(), }; - let lid = self.link_id(instr.column(), "main_shift", 0); + let lid = self.instr_link_id(instr, "main_shift", 0); submachine_op!( shift, lid, @@ -2109,7 +2111,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ], ); - let lid = self.link_id(instr.column(), "main_regs", 2); + let lid = self.instr_link_id(instr, "main_regs", 2); self.reg_write(3, write_reg, r.into(), lid); set_col!(tmp1_col, val1); @@ -2121,16 +2123,16 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Instruction::invert_gl => { let low_addr = args[0].u(); let high_addr = args[1].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let low = self.reg_read(0, low_addr, lid); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let high = self.reg_read(1, high_addr, lid); let inv = F::one() / F::from((high.u() as u64) << 32 | low.u() as u64); let inv_u64 = inv.to_integer().try_into_u64().unwrap(); let (low_inv, high_inv) = (inv_u64 as u32, (inv_u64 >> 32) as u32); - let lid = self.link_id(instr.column(), "main_regs", 2); + let lid = self.instr_link_id(instr, "main_regs", 2); self.reg_write(2, low_addr, low_inv.into(), lid); - let lid = self.link_id(instr.column(), "main_regs", 3); + let lid = self.instr_link_id(instr, "main_regs", 3); self.reg_write(3, high_addr, high_inv.into(), lid); set_col!(tmp1_col, low); @@ -2139,14 +2141,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp4_col, Elem::from_u32_as_fe(high_inv)); set_col!(XX_inv, Elem::Field(inv)); - let lid = self.link_id(instr.column(), "main_split_gl", 0); + let lid = self.instr_link_id(instr, "main_split_gl", 0); submachine_op!(split_gl, lid, &[inv, low_inv.into(), high_inv.into()],); main_op!(invert_gl,); Vec::new() } Instruction::split_gl => { let read_reg = args[0].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let val1 = self.reg_read(0, read_reg, lid); let write_reg1 = args[1].u(); let write_reg2 = args[2].u(); @@ -2158,16 +2160,16 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let lo = (value & 0xffffffff) as u32; let hi = (value >> 32) as u32; - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); self.reg_write(2, write_reg1, lo.into(), lid); - let lid = self.link_id(instr.column(), "main_regs", 2); + let lid = self.instr_link_id(instr, "main_regs", 2); self.reg_write(3, write_reg2, hi.into(), lid); set_col!(tmp1_col, val1); set_col!(tmp3_col, Elem::from_u32_as_fe(lo)); set_col!(tmp4_col, Elem::from_u32_as_fe(hi)); - let lid = self.link_id(instr.column(), "main_split_gl", 0); + let lid = self.instr_link_id(instr, "main_split_gl", 0); submachine_op!(split_gl, lid, &[value.into(), lo.into(), hi.into()],); main_op!(split_gl,); Vec::new() @@ -2175,10 +2177,10 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Instruction::poseidon_gl => { let reg1 = args[0].u(); let reg2 = args[1].u(); - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let input_ptr = self.reg_read(0, reg1, lid); assert!(is_multiple_of_4(input_ptr.u())); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let output_ptr = self.reg_read(1, reg2, lid); assert!(is_multiple_of_4(output_ptr.u())); @@ -2228,7 +2230,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { submachine_op!(split_gl, lid, &[*v, lo.into(), hi.into()],); }); - let lid = self.link_id(instr.column(), "main_poseidon_gl", 0); + let lid = self.instr_link_id(instr, "main_poseidon_gl", 0); submachine_op!( poseidon_gl, lid, @@ -2440,14 +2442,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { vec![] } Instruction::commit_public => { - let lid = self.link_id(instr.column(), "main_regs", 0); + let lid = self.instr_link_id(instr, "main_regs", 0); let idx = self.reg_read(0, args[0].u(), lid); - let lid = self.link_id(instr.column(), "main_regs", 1); + let lid = self.instr_link_id(instr, "main_regs", 1); let limb = self.reg_read(0, args[1].u(), lid); set_col!(tmp1_col, idx); set_col!(tmp2_col, limb); log::debug!("Committing public: idx={idx}, limb={limb}"); - let lid = self.link_id(instr.column(), "main_publics", 0); + let lid = self.instr_link_id(instr, "main_publics", 0); submachine_op!(publics, lid, &[idx.into_fe(), limb.into_fe()],); main_op!(commit_public,); vec![] diff --git a/riscv-executor/src/pil.rs b/riscv-executor/src/pil.rs index a6d8da6d06..39c8b4f92f 100644 --- a/riscv-executor/src/pil.rs +++ b/riscv-executor/src/pil.rs @@ -50,6 +50,9 @@ pub fn selector_for_link(links: &[Identity], id: u64) -> Opt panic!("identity {id} doesnt exist") } +/// Find links referencing columns containing the string `from` on the LHS and `to` on the RHS. +/// For permutations, only looks at the identity selectors. +/// For lookups, looks at the left selector and the right expression. pub fn find_links( links: &[Identity], from: &str, @@ -93,3 +96,49 @@ pub fn find_links( .cloned() .collect() } + +/// Find links referencing the exact `instruction_flag` on the LHS. +/// On the RHS it looks for columns containing the string `to`. +pub fn find_instruction_links( + links: &[Identity], + instruction_flag: &str, + to: &str, +) -> Vec> { + links + .iter() + .filter(|id| match id { + Identity::Permutation(id) => { + let left = id.left.selector.expr_any(|e| { + if let AlgebraicExpression::Reference(r) = e { + return r.name == instruction_flag; + } + false + }); + let right = id.right.selector.expr_any(|e| { + if let AlgebraicExpression::Reference(r) = e { + return r.name.contains(to); + } + false + }); + left && right + } + Identity::Lookup(id) => { + let left = id.left.selector.expr_any(|e| { + if let AlgebraicExpression::Reference(r) = e { + return r.name == instruction_flag; + } + false + }); + let right = id.right.expr_any(|e| { + if let AlgebraicExpression::Reference(r) = e { + return r.name.contains(to); + } + false + }); + left && right + } + _ => false, + }) + .cloned() + .collect() +} From 3bb99a22b99cf8d89ef41fa44b81d4bce87f7f6d Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Fri, 13 Dec 2024 16:23:05 -0300 Subject: [PATCH 24/35] ignore identity ids when not in trace mode --- riscv-executor/src/lib.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 8d32004d95..3b5de7a747 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -1236,11 +1236,17 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { /// Gets the identity id for a link associated with a given instruction. /// idx is based on the order link appear in the assembly (assumed to be the same in the optimized pil). fn instr_link_id(&mut self, instr: Instruction, target: &'static str, idx: usize) -> u64 { - let entries = self - .pil_instruction_links - .entry((instr.flag(), target)) - .or_insert_with(|| pil::find_instruction_links(&self.pil_links, instr.flag(), target)); - entries.get(idx).unwrap().id() + if let ExecMode::Trace = self.mode { + let entries = self + .pil_instruction_links + .entry((instr.flag(), target)) + .or_insert_with(|| { + pil::find_instruction_links(&self.pil_links, instr.flag(), target) + }); + entries.get(idx).unwrap().id() + } else { + 0 // we don't care about identity ids outside trace mode + } } /// Find the identity id of a link. From b7a6327a1368c150c26c8994def51966a8a9bf82 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Mon, 16 Dec 2024 08:27:02 -0300 Subject: [PATCH 25/35] remove TODO --- riscv-executor/src/submachines.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/riscv-executor/src/submachines.rs b/riscv-executor/src/submachines.rs index 32b3cf921b..94b92c878d 100644 --- a/riscv-executor/src/submachines.rs +++ b/riscv-executor/src/submachines.rs @@ -190,7 +190,6 @@ impl SubmachineTrace { /// Push a dummy block to the trace. /// A dummy block is a copy of the first block, with the final row updates applied to it, and selectors set to 0. fn push_dummy_block(&mut self, machine_max_degree: u32, size: u32, selectors: &'static str) { - // TODO: use Optional selector argument let selector_pat = format!("{selectors}["); for i in 0..size { @@ -198,10 +197,10 @@ impl SubmachineTrace { break; } self.cols.iter_mut().for_each(|(col, values)| { - if !col.starts_with(&selector_pat) { - values.push(values[i as usize]) + if !selectors.is_empty() && col.starts_with(&selector_pat) { + values.push(0.into()) // selectors always 0 in dummy blocks } else { - values.push(0.into()) + values.push(values[i as usize]) } }); } From 7cda443e11d21014404ff3b3d868fc87054799e0 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Mon, 16 Dec 2024 08:40:45 -0300 Subject: [PATCH 26/35] fixes --- riscv-executor/src/lib.rs | 110 ++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 53 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index ea27585757..8208018c05 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -753,13 +753,13 @@ mod builder { } pub(crate) fn set_mem(&mut self, addr: u32, val: u32, step: u32, selector: u8) { - self.submachine_op( - MachineInstance::main_memory, - Some(selector), - &[1.into(), addr.into(), step.into(), val.into()], - &[], - ); if let ExecMode::Trace = self.mode { + self.submachine_op( + MachineInstance::main_memory, + Some(selector), + &[1.into(), addr.into(), step.into(), val.into()], + &[], + ); self.trace.mem_ops.push(MemOperation { row: self.trace.len, kind: MemOperationKind::Write, @@ -772,13 +772,13 @@ mod builder { pub(crate) fn get_mem(&mut self, addr: u32, step: u32, selector: u8) -> u32 { let val = *self.mem.get(&addr).unwrap_or(&0); - self.submachine_op( - MachineInstance::main_memory, - Some(selector), - &[0.into(), addr.into(), step.into(), val.into()], - &[], - ); if let ExecMode::Trace = self.mode { + self.submachine_op( + MachineInstance::main_memory, + Some(selector), + &[0.into(), addr.into(), step.into(), val.into()], + &[], + ); self.trace.mem_ops.push(MemOperation { row: self.trace.len, kind: MemOperationKind::Read, @@ -1147,6 +1147,10 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } macro_rules! main_op { + ($insn:ident) => { + self.proc + .main_op(stringify!($insn), self.proc.get_pc().u(), vec![]) + }; ($insn:ident, $($args:expr),*) => { self.proc .main_op(stringify!($insn), self.proc.get_pc().u(), vec![$($args, )*]) @@ -1185,14 +1189,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(Y_free_value, val); } - main_op!(set_reg,); + main_op!(set_reg); Vec::new() } "get_reg" => { let addr = args[0].u(); let val = self.reg_read(0, addr, 0); - main_op!(get_reg,); + main_op!(get_reg); vec![val] } "affine" => { @@ -1207,7 +1211,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.reg_write(1, write_reg, res, 3); set_col!(tmp1_col, val1); - main_op!(affine,); + main_op!(affine); Vec::new() } @@ -1248,9 +1252,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(X_b4, Elem::from_u32_as_fe(b4.into())); if name == "mstore" { - main_op!(mstore,); + main_op!(mstore); } else { - main_op!(mstore_bootloader,); + main_op!(mstore_bootloader); } Vec::new() } @@ -1286,7 +1290,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Elem::from_u32_as_fe(((v as u64 >> 32) & 1) as u32) ); - main_op!(mload,); + main_op!(mload); Vec::new() } // TODO: update to witness generation for continuations @@ -1301,7 +1305,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.reg_write(2, write_addr, val, 3); - main_op!(load_bootloader_input,); + main_op!(load_bootloader_input); Vec::new() } // TODO: update to witness generation for continuations @@ -1316,7 +1320,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { assert_eq!(val, actual_val); - main_op!(assert_bootloader_input,); + main_op!(assert_bootloader_input); Vec::new() } "load_label" => { @@ -1328,7 +1332,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc.set_col("main::instr_load_label_param_l", label); - main_op!(load_label,); + main_op!(load_label); Vec::new() } "jump" => { @@ -1342,7 +1346,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc.set_col("main::instr_jump_param_l", label); - main_op!(jump,); + main_op!(jump); Vec::new() } "jump_dyn" => { @@ -1357,7 +1361,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp1_col, addr); - main_op!(jump_dyn,); + main_op!(jump_dyn); Vec::new() } // TODO: update to witness generation for continuations @@ -1366,7 +1370,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let addr = self.bootloader_inputs[bootloader_input_idx]; self.proc.set_pc(addr); - main_op!(jump_to_bootloader_input,); + main_op!(jump_to_bootloader_input); Vec::new() } "branch_if_diff_nonzero" => { @@ -1392,7 +1396,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc .set_col("main::instr_branch_if_diff_nonzero_param_l", label); - main_op!(branch_if_diff_nonzero,); + main_op!(branch_if_diff_nonzero); Vec::new() } "branch_if_diff_equal" => { @@ -1419,7 +1423,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc .set_col("main::instr_branch_if_diff_equal_param_l", label); - main_op!(branch_if_diff_equal,); + main_op!(branch_if_diff_equal); Vec::new() } "skip_if_equal" => { @@ -1444,7 +1448,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(XX_inv, Elem::Field(F::one() / get_col!(XX).into_fe())); } - main_op!(skip_if_equal,); + main_op!(skip_if_equal); Vec::new() } "branch_if_diff_greater_than" => { @@ -1486,7 +1490,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); - main_op!(branch_if_diff_greater_than,); + main_op!(branch_if_diff_greater_than); Vec::new() } "is_diff_greater_than" => { @@ -1515,7 +1519,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(X_b4, Elem::from_u32_as_fe(b4.into())); set_col!(wrap_bit, Elem::from_u32_as_fe(r)); - main_op!(is_diff_greater_than,); + main_op!(is_diff_greater_than); Vec::new() } "is_equal_zero" => { @@ -1533,7 +1537,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(XX_inv, Elem::Field(F::one() / get_col!(XX).into_fe())); } - main_op!(is_equal_zero,); + main_op!(is_equal_zero); Vec::new() } "is_not_equal" => { @@ -1556,7 +1560,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(XX_inv, Elem::Field(F::one() / get_col!(XX).into_fe())); } - main_op!(is_not_equal,); + main_op!(is_not_equal); Vec::new() } "add_wrap" => { @@ -1594,7 +1598,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); - main_op!(add_wrap,); + main_op!(add_wrap); Vec::new() } "wrap16" => { @@ -1623,7 +1627,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(Y_b5, Elem::from_u32_as_fe(b5.into())); set_col!(Y_b6, Elem::from_u32_as_fe(b6.into())); - main_op!(wrap16,); + main_op!(wrap16); Vec::new() } "sub_wrap_with_offset" => { @@ -1657,7 +1661,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); - main_op!(sub_wrap_with_offset,); + main_op!(sub_wrap_with_offset); Vec::new() } "sign_extend_byte" => { @@ -1690,7 +1694,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); - main_op!(sign_extend_byte,); + main_op!(sign_extend_byte); Vec::new() } "sign_extend_16_bits" => { @@ -1727,7 +1731,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); - main_op!(sign_extend_16_bits,); + main_op!(sign_extend_16_bits); Vec::new() } "to_signed" => { @@ -1756,7 +1760,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(Y_7bit, Elem::from_u32_as_fe(b4 as u32 & 0x7f)); - main_op!(to_signed,); + main_op!(to_signed); Vec::new() } "fail" => { @@ -1818,7 +1822,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(Y_b8, Elem::from_u32_as_fe(b8.into())); } - main_op!(divremu,); + main_op!(divremu); Vec::new() } "mul" => { @@ -1847,7 +1851,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Some(selector), &[r.into(), lo.into(), hi.into()], ); - main_op!(mul,); + main_op!(mul); Vec::new() } "and" | "or" | "xor" => { @@ -1864,15 +1868,15 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let (r, op_id, selector) = match name { "and" => { - main_op!(and,); + main_op!(and); (val1.u() & val2_offset.u(), 0, 0) } "or" => { - main_op!(or,); + main_op!(or); (val1.u() | val2_offset.u(), 1, 1) } "xor" => { - main_op!(xor,); + main_op!(xor); (val1.u() ^ val2_offset.u(), 2, 2) } _ => unreachable!(), @@ -1906,11 +1910,11 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let (r, op_id, selector) = match name { "shl" => { - main_op!(shl,); + main_op!(shl); (val1.u() << val2_offset.u(), 0, 0) } "shr" => { - main_op!(shr,); + main_op!(shr); (val1.u() >> val2_offset.u(), 1, 1) } _ => unreachable!(), @@ -1958,7 +1962,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Some(selector), &[inv, low_inv.into(), high_inv.into()], ); - main_op!(invert_gl,); + main_op!(invert_gl); Vec::new() } "split_gl" => { @@ -1987,7 +1991,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { Some(selector), &[value.into(), lo.into(), hi.into()], ); - main_op!(split_gl,); + main_op!(split_gl); Vec::new() } "poseidon_gl" => { @@ -2062,7 +2066,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { outputs[2], outputs[3] ); - main_op!(poseidon_gl,); + main_op!(poseidon_gl); vec![] } "poseidon2_gl" => { @@ -2092,7 +2096,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc.set_mem(output_ptr + i as u32 * 4, v, 0, 0); // TODO: step/selector for poseidon2 }); - main_op!(poseidon2_gl,); + main_op!(poseidon2_gl); vec![] } "affine_256" => { @@ -2135,7 +2139,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { }); // TODO: main_arith event - main_op!(affine_256,); + main_op!(affine_256); vec![] } "mod_256" => { @@ -2168,7 +2172,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { }); // TODO: main_arith event - main_op!(mod_256,); + main_op!(mod_256); vec![] } "ec_add" => { @@ -2212,7 +2216,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { }); // TODO: main_arith event - main_op!(ec_add,); + main_op!(ec_add); vec![] } "ec_double" => { @@ -2248,7 +2252,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { }); // TODO: main_arith event - main_op!(ec_double,); + main_op!(ec_double); vec![] } "commit_public" => { @@ -2258,7 +2262,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp2_col, limb); log::debug!("Committing public: idx={idx}, limb={limb}"); submachine_op!(main_publics, None, &[idx.into_fe(), limb.into_fe()],); - main_op!(commit_public,); + main_op!(commit_public); vec![] } instr => { From 25ff483ae2bdf466bfed278af63bef26e77764f5 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Mon, 16 Dec 2024 09:14:30 -0300 Subject: [PATCH 27/35] fixes --- riscv-executor/src/lib.rs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index c7b79bddb9..a673d65b5e 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -1236,21 +1236,22 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { /// Gets the identity id for a link associated with a given instruction. /// idx is based on the order link appear in the assembly (assumed to be the same in the optimized pil). fn instr_link_id(&mut self, instr: Instruction, target: &'static str, idx: usize) -> u64 { - if let ExecMode::Trace = self.mode { - let entries = self - .pil_instruction_links - .entry((instr.flag(), target)) - .or_insert_with(|| { - pil::find_instruction_links(&self.pil_links, instr.flag(), target) - }); - entries.get(idx).unwrap().id() - } else { - 0 // we don't care about identity ids outside trace mode + if let ExecMode::Fast = self.mode { + return 0; // we don't care about identity ids in fast mode } + + let entries = self + .pil_instruction_links + .entry((instr.flag(), target)) + .or_insert_with(|| pil::find_instruction_links(&self.pil_links, instr.flag(), target)); + entries.get(idx).unwrap().id() } /// Find the identity id of a link. fn link_id(&mut self, from: &'static str, target: &'static str, idx: usize) -> u64 { + if let ExecMode::Fast = self.mode { + return 0; // we don't care about identity ids in fast mode + } let entries = self .pil_instruction_links .entry((from, target)) @@ -2214,9 +2215,9 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let inputs = (0..12) .map(|i| { // memory reads from the poseidon machine - let lid = self.link_id("poseidon_gl", "memory", 0); + let lid = self.link_id("main_poseidon_gl", "main_memory", 0); let lo = self.proc.get_mem(input_ptr.u() + 8 * i, self.step, lid); - let lid = self.link_id("poseidon_gl", "memory", 1); + let lid = self.link_id("main_poseidon_gl", "main_memory", 1); let hi = self.proc.get_mem(input_ptr.u() + 8 * i + 4, self.step, lid); F::from(((hi as u64) << 32) | lo as u64) }) @@ -2692,7 +2693,7 @@ pub fn execute( max_steps_to_execute: Option, profiling: Option, ) -> Execution { - log::info!("Executing..."); + log::info!("Executing (trace generation)..."); execute_inner( asm, From 04cc70a252f8fd13d59cfdb921ab51be75fe82d8 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Mon, 16 Dec 2024 10:00:53 -0300 Subject: [PATCH 28/35] parallel witness from execution trace --- riscv-executor/Cargo.toml | 2 + riscv-executor/src/lib.rs | 171 +++++++++++++++++------------- riscv-executor/src/submachines.rs | 4 +- 3 files changed, 99 insertions(+), 78 deletions(-) diff --git a/riscv-executor/Cargo.toml b/riscv-executor/Cargo.toml index 293ed81ac0..290786000b 100644 --- a/riscv-executor/Cargo.toml +++ b/riscv-executor/Cargo.toml @@ -26,6 +26,8 @@ p3-symmetric = { git = "https://github.com/plonky3/Plonky3.git", rev = "2192432d rustc-demangle = "0.1" inferno = "0.11.19" +rayon = "1.7.0" + [lints.clippy] uninlined_format_args = "deny" diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index a673d65b5e..67f5f0af9e 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -14,6 +14,7 @@ use std::{ fmt::{self, Display, Formatter}, path::Path, sync::Arc, + time::Instant, }; use builder::TraceBuilder; @@ -58,7 +59,7 @@ struct SubmachineOp { extra: Vec, } -/// This enum helps us avoid raw strings for instruction names/columns +/// Enum with asm machine RISCV instruction. Helps avoid using raw strings in the code. macro_rules! instructions { ($($name:ident),*) => { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -129,6 +130,7 @@ instructions! { fail } +/// Enum with submachines used in the RISCV vm macro_rules! machine_instances { ($($name:ident),*) => { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -137,25 +139,19 @@ macro_rules! machine_instances { $($name,)* } + #[allow(unused)] impl MachineInstance { - // fn from_name(s: &str) -> Option { - // match s { - // $(stringify!($name) => Some(Self::$name),)* - // _ => None - // } - // } - fn name(&self) -> &'static str { match *self { $(Self::$name => stringify!($name),)* } } - // fn namespace(&self) -> &'static str { - // match *self { - // $(Self::$name => concat!("main_", stringify!($name)),)* - // } - // } + fn namespace(&self) -> &'static str { + match *self { + $(Self::$name => concat!("main_", stringify!($name)),)* + } + } } }; } @@ -503,13 +499,15 @@ impl RegisterMemory { } mod builder { - use std::{cell::RefCell, cmp, collections::HashMap}; + use std::{cell::RefCell, cmp, collections::HashMap, time::Instant}; use powdr_ast::{ analyzed::{Analyzed, DegreeRange}, asm_analysis::{Machine, RegisterTy}, }; use powdr_number::FieldElement; + use rayon::iter::IntoParallelIterator; + use rayon::iter::ParallelIterator; use crate::{ pil, BinaryMachine, Elem, ExecMode, Execution, ExecutionTrace, MachineInstance, MainOp, @@ -546,7 +544,7 @@ mod builder { pub struct TraceBuilder<'b, F: FieldElement> { trace: ExecutionTrace, - submachines: HashMap>>>, + submachines: HashMap>>>, /// Maximum rows we can run before we stop the execution. max_rows: usize, @@ -619,36 +617,36 @@ mod builder { let mut regs = vec![0.into(); reg_len]; regs[pc_idx as usize] = PC_INITIAL_VAL.into(); - let submachines: HashMap>>> = + let submachines: HashMap<_, RefCell>>> = if let ExecMode::Trace = mode { [ ( - "memory".to_string(), + MachineInstance::memory, RefCell::new(Box::new(MemoryMachine::new("main_memory", &witness_cols))) as RefCell>>, // this first `as` is needed to coerce the type of the array ), ( - "regs".to_string(), + MachineInstance::regs, RefCell::new(Box::new(MemoryMachine::new("main_regs", &witness_cols))), ), ( - "binary".to_string(), + MachineInstance::binary, RefCell::new(BinaryMachine::new_boxed("main_binary", &witness_cols)), ), ( - "shift".to_string(), + MachineInstance::shift, RefCell::new(ShiftMachine::new_boxed("main_shift", &witness_cols)), ), ( - "split_gl".to_string(), + MachineInstance::split_gl, RefCell::new(SplitGlMachine::new_boxed("main_split_gl", &witness_cols)), ), ( - "publics".to_string(), + MachineInstance::publics, RefCell::new(PublicsMachine::new_boxed("main_publics", &witness_cols)), ), ( - "poseidon_gl".to_string(), + MachineInstance::poseidon_gl, RefCell::new(PoseidonGlMachine::new_boxed( "main_poseidon_gl", &witness_cols, @@ -930,65 +928,67 @@ mod builder { }; // turn register write operations into witness columns + let start = Instant::now(); let main_regs = self.trace.generate_registers_trace(); self.trace.cols.extend(main_regs); // fill up main trace to degree self.extend_rows(main_degree); + log::info!( + "Finalizing main machine trace took {}s", + start.elapsed().as_secs_f64(), + ); // generate witness for submachines // ---------------------------- let links = pil::links_from_pil(pil); - let mut link_selector = HashMap::new(); - for (m, ops) in self.trace.submachine_ops { - let m = m.name(); - for op in ops { - let selector = link_selector - .entry(op.identity_id) - .or_insert_with(|| pil::selector_for_link(&links, op.identity_id)); - self.submachines[m].borrow_mut().add_operation( - selector.as_deref(), - &op.lookup_args, - &op.extra, - ); - } - } - // add submachine traces to main trace - // ---------------------------- - for (name, mut machine) in self - .submachines + let start = Instant::now(); + let m_ops: Vec<_> = self + .trace + .submachine_ops .into_iter() - .map(|(n, m)| (n, m.into_inner())) - { - // finalize and extend the submachine traces and add to full trace - if machine.len() > 0 { - let range = namespace_degree_range(pil, machine.namespace()); - // extend with dummy blocks up to the required machine degree - let machine_degree = - std::cmp::max(machine.len().next_power_of_two(), range.min as u32); - for (col_name, col) in machine.finish(machine_degree) { - assert!(self.trace.cols.insert(col_name, col).is_none()); - } - } else if name == "publics" { - // for the publics machine, even with no operations being - // issued, the declared "publics" force the cells to be - // filled. We add operations here to emulate that. - if machine.len() == 0 { - for i in 0..8 { - machine.add_operation(None, &[i.into(), 0.into()], &[]); + .map(|(m, ops)| { + let machine = self.submachines.remove(&m).unwrap().into_inner(); + (m, machine, ops) + }) + .collect(); + let cols = m_ops + .into_par_iter() + .flat_map(|(m, mut machine, ops)| { + ops.into_iter().for_each(|op| { + let selector = pil::selector_for_link(&links, op.identity_id); + machine.add_operation(selector.as_deref(), &op.lookup_args, &op.extra); + }); + + // finalize and extend the submachine traces and add to full trace + if machine.len() > 0 { + let range = namespace_degree_range(pil, machine.namespace()); + // extend with dummy blocks up to the required machine degree + let machine_degree = + std::cmp::max(machine.len().next_power_of_two(), range.min as u32); + machine.finish(machine_degree) + } else if m == MachineInstance::publics { + // for the publics machine, even with no operations being + // issued, the declared "publics" force the cells to be + // filled. We add operations here to emulate that. + if machine.len() == 0 { + for i in 0..8 { + machine.add_operation(None, &[i.into(), 0.into()], &[]); + } } + machine.finish(8) + } else { + // keep machine columns empty + machine.finish(0) } - for (col_name, col) in machine.finish(8) { - assert!(self.trace.cols.insert(col_name, col).is_none()); - } - } else { - // keep machine columns empty - for (col_name, col) in machine.finish(0) { - assert!(self.trace.cols.insert(col_name, col).is_none()); - } - } - } + }) + .collect::>(); + self.trace.cols.extend(cols); + log::info!( + "Generating submachine traces took {}s", + start.elapsed().as_secs_f64(), + ); Execution { trace_len: self.trace.len, @@ -2052,16 +2052,16 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp1_col, val1); set_col!(tmp2_col, val2); - let (r, op_id) = match name { - "and" => { + let (r, op_id) = match instr { + Instruction::and => { main_op!(and); (val1.u() & val2_offset.u(), 0) } - "or" => { + Instruction::or => { main_op!(or); (val1.u() | val2_offset.u(), 1) } - "xor" => { + Instruction::xor => { main_op!(xor); (val1.u() ^ val2_offset.u(), 2) } @@ -2098,12 +2098,12 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { let write_reg = args[3].u(); let val2_offset: Elem = (val2.bin() + offset).into(); - let (r, op_id) = match name { - "shl" => { + let (r, op_id) = match instr { + Instruction::shl => { main_op!(shl); (val1.u() << val2_offset.u(), 0) } - "shr" => { + Instruction::shr => { main_op!(shr); (val1.u() >> val2_offset.u(), 1) } @@ -2720,6 +2720,7 @@ fn execute_inner( mode: ExecMode, profiling: Option, ) -> Execution { + let start = Instant::now(); let main_machine = get_main_machine(asm); let PreprocessedMain { @@ -2799,6 +2800,8 @@ fn execute_inner( profiling.map(|opt| Profiler::new(opt, &debug_files[..], function_starts, location_starts)); let mut curr_pc = 0u32; + let mut last = Instant::now(); + let mut count = 0; loop { let stm = statements[curr_pc as usize]; @@ -2806,6 +2809,17 @@ fn execute_inner( e.step += 4; + count += 1; + if count % 10000 == 0 { + let now = Instant::now(); + let elapsed = now - last; + if elapsed.as_secs_f64() > 1.0 { + last = now; + log::debug!("instructions/s: {}", count as f64 / elapsed.as_secs_f64(),); + count = 0; + } + } + match stm { FunctionStatement::Assignment(a) => { e.proc.push_row(); @@ -2965,6 +2979,11 @@ fn execute_inner( e.proc.set_col("main::_operation_id", sink_id.into()); } + log::info!( + "Program execution took {}s", + start.elapsed().as_secs_f64() + ); + e.proc.finish(opt_pil) } diff --git a/riscv-executor/src/submachines.rs b/riscv-executor/src/submachines.rs index 097a18bb69..ac2a7632de 100644 --- a/riscv-executor/src/submachines.rs +++ b/riscv-executor/src/submachines.rs @@ -9,7 +9,7 @@ fn only_column_name(name: &str) -> &str { } /// Each submachine kind (i.e., binary, shift) must implement this trait -trait SubmachineKind { +trait SubmachineKind: Send { /// Which of the witness columns are selectors, if any const SELECTORS: &'static str; /// Row block size @@ -41,7 +41,7 @@ impl SubmachineBoxed for M { /// Each specific submachine only needs to implement the SubmachineKind trait. /// Trace is built by calling these methods. /// It being a trait also allows us to put different submachines in the same hashmap. -pub trait Submachine { +pub trait Submachine: Send { /// submachine namespace fn namespace(&self) -> &str; /// current number of rows From d68c4133a1b172dc20eee79c0b1c424b06326464 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Mon, 16 Dec 2024 10:06:59 -0300 Subject: [PATCH 29/35] use par_bridge --- riscv-executor/src/lib.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 67f5f0af9e..a9fafec8ac 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -506,8 +506,8 @@ mod builder { asm_analysis::{Machine, RegisterTy}, }; use powdr_number::FieldElement; - use rayon::iter::IntoParallelIterator; use rayon::iter::ParallelIterator; + use rayon::iter::{IntoParallelIterator, ParallelBridge}; use crate::{ pil, BinaryMachine, Elem, ExecMode, Execution, ExecutionTrace, MachineInstance, MainOp, @@ -944,18 +944,18 @@ mod builder { let links = pil::links_from_pil(pil); let start = Instant::now(); - let m_ops: Vec<_> = self + let cols = self .trace .submachine_ops .into_iter() + // take each submachine and its operations .map(|(m, ops)| { let machine = self.submachines.remove(&m).unwrap().into_inner(); (m, machine, ops) }) - .collect(); - let cols = m_ops - .into_par_iter() + .par_bridge() .flat_map(|(m, mut machine, ops)| { + // apply the operation to the submachine ops.into_iter().for_each(|op| { let selector = pil::selector_for_link(&links, op.identity_id); machine.add_operation(selector.as_deref(), &op.lookup_args, &op.extra); @@ -2979,10 +2979,7 @@ fn execute_inner( e.proc.set_col("main::_operation_id", sink_id.into()); } - log::info!( - "Program execution took {}s", - start.elapsed().as_secs_f64() - ); + log::info!("Program execution took {}s", start.elapsed().as_secs_f64()); e.proc.finish(opt_pil) } From 47f7dd523949dba67bd4e83df84abeb3f8d11229 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Mon, 16 Dec 2024 10:16:12 -0300 Subject: [PATCH 30/35] with_capacity --- riscv-executor/src/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riscv-executor/src/memory.rs b/riscv-executor/src/memory.rs index c5e247ce23..0ec215a905 100644 --- a/riscv-executor/src/memory.rs +++ b/riscv-executor/src/memory.rs @@ -96,7 +96,7 @@ impl Submachine for MemoryMachine { let mut cols: Vec<_> = std::mem::take(&mut self.witness_cols) .into_iter() - .map(|n| (n, vec![])) + .map(|n| (n, Vec::with_capacity(self.ops.len()))) .collect(); let selector_count = cols.len() - Cols::Selectors as usize; From 2b87cc83a46a433fcfa42c94c05fa60567e09a30 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Mon, 16 Dec 2024 11:14:14 -0300 Subject: [PATCH 31/35] unused import --- riscv-executor/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index a9fafec8ac..69b06dafd7 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -506,8 +506,7 @@ mod builder { asm_analysis::{Machine, RegisterTy}, }; use powdr_number::FieldElement; - use rayon::iter::ParallelIterator; - use rayon::iter::{IntoParallelIterator, ParallelBridge}; + use rayon::iter::{ParallelBridge, ParallelIterator}; use crate::{ pil, BinaryMachine, Elem, ExecMode, Execution, ExecutionTrace, MachineInstance, MainOp, From f09ae213d07767a5ac4dd24859e5f04699c4bdc0 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Mon, 16 Dec 2024 13:18:39 -0300 Subject: [PATCH 32/35] debug print --- riscv-executor/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 69b06dafd7..9d4263a942 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -933,7 +933,7 @@ mod builder { // fill up main trace to degree self.extend_rows(main_degree); - log::info!( + log::debug!( "Finalizing main machine trace took {}s", start.elapsed().as_secs_f64(), ); @@ -984,7 +984,7 @@ mod builder { }) .collect::>(); self.trace.cols.extend(cols); - log::info!( + log::debug!( "Generating submachine traces took {}s", start.elapsed().as_secs_f64(), ); @@ -2978,7 +2978,7 @@ fn execute_inner( e.proc.set_col("main::_operation_id", sink_id.into()); } - log::info!("Program execution took {}s", start.elapsed().as_secs_f64()); + log::debug!("Program execution took {}s", start.elapsed().as_secs_f64()); e.proc.finish(opt_pil) } From 86f70859c4eb01414963a8b37a3063631c6be95c Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Mon, 16 Dec 2024 14:06:37 -0300 Subject: [PATCH 33/35] comments --- riscv-executor/src/pil.rs | 3 ++- riscv-executor/src/submachines.rs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/riscv-executor/src/pil.rs b/riscv-executor/src/pil.rs index 39c8b4f92f..a50b0c7cdc 100644 --- a/riscv-executor/src/pil.rs +++ b/riscv-executor/src/pil.rs @@ -43,11 +43,12 @@ pub fn selector_for_link(links: &[Identity], id: u64) -> Opt } if let Identity::Lookup(lookup) = l { if lookup.id == id { + // lookups don't have selectors return None; } } } - panic!("identity {id} doesnt exist") + panic!("identity {id} doesn't exist") } /// Find links referencing columns containing the string `from` on the LHS and `to` on the RHS. diff --git a/riscv-executor/src/submachines.rs b/riscv-executor/src/submachines.rs index ac2a7632de..621164d77a 100644 --- a/riscv-executor/src/submachines.rs +++ b/riscv-executor/src/submachines.rs @@ -5,6 +5,7 @@ use powdr_number::{FieldElement, LargeInt}; use std::collections::HashMap; fn only_column_name(name: &str) -> &str { + // look backwards the "::" and return only the part after it name.rfind("::").map(|i| &name[i + 2..]).unwrap_or(name) } From f324eabecc243db442262641335aa3502d6aa2b1 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Mon, 16 Dec 2024 18:04:20 -0300 Subject: [PATCH 34/35] handle machines with no ops columns should still be present but empty --- riscv-executor/src/lib.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index 9d4263a942..e387649d2d 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -944,17 +944,18 @@ mod builder { let start = Instant::now(); let cols = self - .trace - .submachine_ops + .submachines .into_iter() - // take each submachine and its operations - .map(|(m, ops)| { - let machine = self.submachines.remove(&m).unwrap().into_inner(); - (m, machine, ops) + // take each submachine and get its operations + .map(|(m, machine)| { + let ops = self.trace.submachine_ops.remove(&m).unwrap_or_default(); + (m, machine.into_inner(), ops) }) + // handle submachines in parallel .par_bridge() .flat_map(|(m, mut machine, ops)| { - // apply the operation to the submachine + println!("machine: {:?}", m.name()); + // apply the operations to the submachine ops.into_iter().for_each(|op| { let selector = pil::selector_for_link(&links, op.identity_id); machine.add_operation(selector.as_deref(), &op.lookup_args, &op.extra); From ebcc3f6380d11a9852d79b12ed68ce31d907cc46 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Mon, 16 Dec 2024 18:25:16 -0300 Subject: [PATCH 35/35] leftover --- riscv-executor/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index e387649d2d..758022fb4c 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -954,7 +954,6 @@ mod builder { // handle submachines in parallel .par_bridge() .flat_map(|(m, mut machine, ops)| { - println!("machine: {:?}", m.name()); // apply the operations to the submachine ops.into_iter().for_each(|op| { let selector = pil::selector_for_link(&links, op.identity_id);