diff --git a/riscv-executor/src/lib.rs b/riscv-executor/src/lib.rs index e8e53bb45..8208018c0 100644 --- a/riscv-executor/src/lib.rs +++ b/riscv-executor/src/lib.rs @@ -42,6 +42,36 @@ use memory::*; use crate::profiler::Profiler; +// 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(&'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, + // 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, + // we give it the input values because it doesn't have access to memory + 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, +} + /// Initial value of the PC. /// /// To match the ZK proof witness, the PC must start after some offset used for @@ -77,10 +107,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()) @@ -124,13 +150,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, @@ -266,8 +285,14 @@ pub struct ExecutionTrace { /// The length of the trace, after applying the reg_writes. len: usize, + /// Main machine instructions + main_ops: Vec>, + + /// Calls into submachines + submachine_ops: HashMap>>, + /// witness columns - cols: HashMap>>, + cols: HashMap>, } impl ExecutionTrace { @@ -280,7 +305,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 { @@ -288,6 +313,8 @@ impl ExecutionTrace { reg_writes, mem_ops: Vec::new(), len: pc, + main_ops: Vec::new(), + submachine_ops: HashMap::new(), cols, } } @@ -304,9 +331,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() { @@ -314,7 +340,7 @@ impl ExecutionTrace { reg_values .entry(reg_name) .or_default() - .push(Elem::Field(row[index as usize])); + .push(row[index as usize]); } } @@ -376,11 +402,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}, @@ -389,9 +411,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, MainOp, + MemOperation, MemOperationKind, MemoryMachine, MemoryState, PoseidonGlMachine, + PublicsMachine, RegWrite, RegisterMemory, ShiftMachine, SplitGlMachine, Submachine, + SubmachineBoxed, SubmachineOp, PC_INITIAL_VAL, }; fn namespace_degree_range( @@ -423,8 +446,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, @@ -501,28 +522,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 + ), + ( + "regs".to_string(), + RefCell::new(Box::new(MemoryMachine::new("main_regs", &witness_cols))), ), ( - "shift", - ShiftMachine::new_boxed("main_shift", &witness_cols).into(), + "binary".to_string(), + RefCell::new(BinaryMachine::new_boxed("main_binary", &witness_cols)), ), ( - "split_gl", - SplitGlMachine::new_boxed("main_split_gl", &witness_cols).into(), + "shift".to_string(), + RefCell::new(ShiftMachine::new_boxed("main_shift", &witness_cols)), ), ( - "publics", - PublicsMachine::new_boxed("main_publics", &witness_cols).into(), + "split_gl".to_string(), + RefCell::new(SplitGlMachine::new_boxed("main_split_gl", &witness_cols)), ), ( - "poseidon_gl", - PoseidonGlMachine::new_boxed("main_poseidon_gl", &witness_cols).into(), + "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() @@ -531,8 +563,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, @@ -551,6 +581,32 @@ mod builder { } } + 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)); + } + } + + 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_ops + .entry(m) + .or_default() + .push(SubmachineOp { + selector, + lookup_args: lookup_args.to_vec(), + extra: extra.to_vec(), + }); + } + } + pub(crate) fn main_columns_len(&self) -> usize { let cols_len = self .trace @@ -631,7 +687,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(); } } @@ -642,7 +698,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(); } } @@ -653,7 +709,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()) } @@ -661,10 +717,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())); } } @@ -699,28 +752,38 @@ 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) { + pub(crate) fn set_mem(&mut self, addr: u32, val: u32, step: u32, selector: u8) { 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, address: addr, }); - self.memory_machine.write(step, addr, val.into(), selector); } 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); 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, address: addr, }); - self.memory_machine.read(step, addr, val.into(), selector); } val } @@ -744,10 +807,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 { @@ -776,65 +835,68 @@ mod builder { // fill up main trace to degree self.extend_rows(main_degree); + // generate witness for submachines + // ---------------------------- + 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", + }; + for op in ops { + self.submachines[m].borrow_mut().add_operation( + op.selector, + &op.lookup_args, + &op.extra, + ); + } + } + // 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 + 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 { - 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()); + } + } 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()); } } - for (col_name, col) in machine.take_cols() { - assert!(self.trace.cols.insert(col_name, col).is_none()); - } - } - - // 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, 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(), } } @@ -1021,23 +1083,35 @@ 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: u8) -> Elem { let val = self.proc.get_reg_mem(reg); - if let ExecMode::Trace = self.mode { - self.proc - .regs_machine - .read(self.step + step_offset, reg, val, selector_idx); - } + self.proc.submachine_op( + MachineInstance::main_regs, + Some(selector), + &[ + 0.into(), + reg.into(), + (self.step + step_offset).into(), + val.into_fe(), + ], + &[], + ); 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) { - if let ExecMode::Trace = self.mode { - self.proc - .regs_machine - .write(self.step + step_offset, reg, val, selector_idx); - } + fn reg_write(&mut self, step_offset: u32, reg: u32, val: Elem, selector: u8) { + self.proc.submachine_op( + MachineInstance::main_regs, + Some(selector), + &[ + 1.into(), + reg.into(), + (self.step + step_offset).into(), + val.into_fe(), + ], + &[], + ); self.proc.set_reg_mem(reg, val); } @@ -1072,6 +1146,23 @@ 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, )*]) + }; + } + + macro_rules! submachine_op { + ($machine:ident, $selector:expr, $args:expr, $($extra:expr),*) => { + self.proc.submachine_op(MachineInstance::$machine, $selector, $args, &[$($extra, )*]) + }; + } + let args = args .iter() .map(|expr| self.eval_expression(expr)[0]) @@ -1098,12 +1189,14 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(Y_free_value, val); } + 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); vec![val] } "affine" => { @@ -1118,6 +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); Vec::new() } @@ -1157,6 +1251,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_op!(mstore); + } else { + main_op!(mstore_bootloader); + } Vec::new() } "mload" => { @@ -1191,6 +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); Vec::new() } // TODO: update to witness generation for continuations @@ -1205,6 +1305,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.reg_write(2, write_addr, val, 3); + main_op!(load_bootloader_input); Vec::new() } // TODO: update to witness generation for continuations @@ -1219,6 +1320,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { assert_eq!(val, actual_val); + main_op!(assert_bootloader_input); Vec::new() } "load_label" => { @@ -1230,6 +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); Vec::new() } "jump" => { @@ -1243,6 +1346,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { self.proc.set_col("main::instr_jump_param_l", label); + main_op!(jump); Vec::new() } "jump_dyn" => { @@ -1257,6 +1361,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { set_col!(tmp1_col, addr); + main_op!(jump_dyn); Vec::new() } // TODO: update to witness generation for continuations @@ -1265,6 +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); Vec::new() } "branch_if_diff_nonzero" => { @@ -1290,6 +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); Vec::new() } "branch_if_diff_equal" => { @@ -1316,6 +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); Vec::new() } "skip_if_equal" => { @@ -1340,6 +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); Vec::new() } "branch_if_diff_greater_than" => { @@ -1381,6 +1490,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); + main_op!(branch_if_diff_greater_than); Vec::new() } "is_diff_greater_than" => { @@ -1409,6 +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); Vec::new() } "is_equal_zero" => { @@ -1426,6 +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); Vec::new() } "is_not_equal" => { @@ -1448,6 +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); Vec::new() } "add_wrap" => { @@ -1485,6 +1598,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); + main_op!(add_wrap); Vec::new() } "wrap16" => { @@ -1513,6 +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); Vec::new() } "sub_wrap_with_offset" => { @@ -1546,6 +1661,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); + main_op!(sub_wrap_with_offset); Vec::new() } "sign_extend_byte" => { @@ -1578,6 +1694,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); + main_op!(sign_extend_byte); Vec::new() } "sign_extend_16_bits" => { @@ -1614,6 +1731,7 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { } ); + main_op!(sign_extend_16_bits); Vec::new() } "to_signed" => { @@ -1642,6 +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); Vec::new() } "fail" => { @@ -1703,6 +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); Vec::new() } "mul" => { @@ -1725,15 +1845,13 @@ 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)); - if let ExecMode::Trace = self.mode { - self.proc.submachine("split_gl").add_operation( - "split_gl", - &[hi.into(), lo.into()], - Some(0), - &[], - ); - } - + let selector = 0; + submachine_op!( + main_split_gl, + Some(selector), + &[r.into(), lo.into(), hi.into()], + ); + main_op!(mul); Vec::new() } "and" | "or" | "xor" => { @@ -1748,21 +1866,32 @@ 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, selector) = match name { + "and" => { + main_op!(and); + (val1.u() & val2_offset.u(), 0, 0) + } + "or" => { + main_op!(or); + (val1.u() | val2_offset.u(), 1, 1) + } + "xor" => { + main_op!(xor); + (val1.u() ^ val2_offset.u(), 2, 2) + } _ => unreachable!(), }; - if let ExecMode::Trace = self.mode { - self.proc.submachine("binary").add_operation( - name, - &[val1, val2_offset, r.into()], - Some(sel), - &[], - ); - } + submachine_op!( + main_binary, + Some(selector), + &[ + op_id.into(), + val1.into_fe(), + val2_offset.into_fe(), + r.into() + ], + ); self.reg_write(3, write_reg, r.into(), 3); @@ -1779,27 +1908,35 @@ 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, selector) = match name { + "shl" => { + main_op!(shl); + (val1.u() << val2_offset.u(), 0, 0) + } + "shr" => { + main_op!(shr); + (val1.u() >> val2_offset.u(), 1, 1) + } _ => unreachable!(), }; + submachine_op!( + main_shift, + 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!(tmp1_col, val1); 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( - name, - &[val1, val2_offset, r.into()], - Some(sel), - &[], - ); - } - Vec::new() } "invert_gl" => { @@ -1819,15 +1956,13 @@ 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)); - if let ExecMode::Trace = self.mode { - self.proc.submachine("split_gl").add_operation( - "split_gl", - &[high_inv.into(), low_inv.into()], - Some(0), - &[], - ); - } - + let selector = 0; + submachine_op!( + main_split_gl, + Some(selector), + &[inv, low_inv.into(), high_inv.into()], + ); + main_op!(invert_gl); Vec::new() } "split_gl" => { @@ -1836,10 +1971,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; @@ -1850,15 +1985,13 @@ 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)); - if let ExecMode::Trace = self.mode { - self.proc.submachine("split_gl").add_operation( - "split_gl", - &[hi.into(), lo.into()], - Some(0), - &[], - ); - } - + let selector = 0; + submachine_op!( + main_split_gl, + Some(selector), + &[value.into(), lo.into(), hi.into()], + ); + main_op!(split_gl); Vec::new() } "poseidon_gl" => { @@ -1899,54 +2032,41 @@ 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); - 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 selector = 1; + submachine_op!(main_split_gl, Some(selector), &[*v, lo.into(), hi.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 selector = 0; + submachine_op!( + main_poseidon_gl, + 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_op!(poseidon_gl); vec![] } "poseidon2_gl" => { @@ -1976,6 +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); vec![] } "affine_256" => { @@ -2017,6 +2138,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); }); + // TODO: main_arith event + main_op!(affine_256); vec![] } "mod_256" => { @@ -2048,6 +2171,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); }); + // TODO: main_arith event + main_op!(mod_256); vec![] } "ec_add" => { @@ -2090,6 +2215,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); }); + // TODO: main_arith event + main_op!(ec_add); vec![] } "ec_double" => { @@ -2124,6 +2251,8 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { ); }); + // TODO: main_arith event + main_op!(ec_double); vec![] } "commit_public" => { @@ -2132,14 +2261,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}"); - if let ExecMode::Trace = self.mode { - self.proc.submachine("publics").add_operation( - "access", - &[idx, limb], - None, - &[], - ); - } + 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 c29fc5cf6..3949e5d99 100644 --- a/riscv-executor/src/memory.rs +++ b/riscv-executor/src/memory.rs @@ -1,17 +1,17 @@ -use powdr_number::FieldElement; +use powdr_number::{FieldElement, LargeInt}; -use crate::Elem; +use crate::Submachine; #[derive(Debug, Eq, PartialEq)] /// Order of fields matter: will be ordered by addr then step. struct Op { addr: u32, step: u32, - value: Elem, - write: bool, + 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: u32, + selector_idx: u8, } pub struct MemoryMachine { @@ -37,33 +37,36 @@ impl MemoryMachine { } } - pub fn write(&mut self, step: u32, addr: u32, val: Elem, selector_idx: u32) { - self.ops.push(Op { - addr, - step, - value: val, - write: true, - selector_idx, - }); + pub fn len(&self) -> u32 { + self.ops.len() as u32 } +} - pub fn read(&mut self, step: u32, addr: u32, val: Elem, selector_idx: u32) { - self.ops.push(Op { - addr, - step, - value: val, - write: false, - selector_idx, - }); +impl Submachine for MemoryMachine { + fn len(&self) -> u32 { + self.ops.len() as u32 } - pub 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(), + }); } - pub fn take_cols(mut self, len: u32) -> Vec<(String, Vec>)> { + fn finish(&mut self, degree: u32) -> Vec<(String, Vec)> { assert!( - len >= self.len(), + degree >= self.len(), "trying to take less rows than memory ops" ); @@ -111,14 +114,12 @@ 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); - 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 { @@ -132,34 +133,32 @@ 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())); - if self.len() < len { + 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(len as usize, last_addr.into()); - cols[Value as usize].1.resize(len as usize, last_value); + 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((len - self.len()) as usize) - .map(|x| Elem::from_u32_as_fe(x)), + .take((degree - 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()); + 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(len as usize, 0.into()); + .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 } } diff --git a/riscv-executor/src/submachines.rs b/riscv-executor/src/submachines.rs index 8a5c8e7f4..94b92c878 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; @@ -14,10 +13,11 @@ trait SubmachineKind { /// Add an operation to the submachine trace fn add_operation( trace: &mut SubmachineTrace, - name: &str, - args: &[Elem], - selector: Option, - submachines: &[&mut dyn Submachine], + // 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) {} @@ -44,159 +44,163 @@ 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], - ); - /// 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 add_operation(&mut self, selector_idx: Option, 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)>; } /// Concrete implementation of the Submachine trait struct SubmachineImpl { - namespace: String, trace: SubmachineTrace, m: std::marker::PhantomData, + finished: bool, } 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(); + 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, } } } impl Submachine for SubmachineImpl { fn namespace(&self) -> &str { - self.namespace.as_str() + self.trace.namespace.as_str() } fn len(&self) -> u32 { 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, 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) { - 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() <= degree); + 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)) + .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 { - cols: HashMap>>, + 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 - last_row_overrides: HashMap>>, + last_row_overrides: HashMap>, } impl SubmachineTrace { - fn new(cols: HashMap>>) -> Self { - assert!(!cols.is_empty(), "machine with no witness columns"); + 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() } /// 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 .cols .get_mut(col) - .unwrap() + .unwrap_or_else(|| panic!("{} has no column {col}", self.namespace)) .get_mut(idx as usize) .unwrap() = value; } } /// set the value of a column in the current - fn set_current_row(&mut self, col: &str, value: Elem) { - *self.cols.get_mut(col).unwrap().last_mut().unwrap() = value; + fn set_current_row(&mut self, col: &str, value: F) { + *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: Elem) { - *self.last_row_overrides.get_mut(col).unwrap() = Some(value); + fn set_final_row(&mut self, col: &str, value: F) { + *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; } } } /// 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. /// 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) { 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)| { - 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]) } }); } @@ -204,29 +208,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; @@ -237,29 +221,25 @@ impl SubmachineKind for BinaryMachine { fn add_operation( trace: &mut SubmachineTrace, - op: &str, - args: &[Elem], - selector: Option, - _submachines: &[&mut dyn Submachine], + selector_idx: Option, + lookup_args: &[F], + extra: &[F], ) { - let [a, b, c] = args[..] else { + assert!(extra.is_empty()); + let selector_idx = selector_idx.unwrap(); + let [op_id, a, b, c] = lookup_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 +261,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 +271,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 +290,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); @@ -311,10 +309,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.unwrap()), - 1.into(), - ); + trace.set_current_row(&format!("{}[{}]", Self::SELECTORS, selector_idx), 1.into()); } } @@ -326,32 +321,31 @@ impl SubmachineKind for ShiftMachine { fn add_operation( trace: &mut SubmachineTrace, - op: &str, - args: &[Elem], - selector: Option, - _submachines: &[&mut dyn Submachine], + selector_idx: Option, + lookup_args: &[F], + extra: &[F], ) { - let [a, b, c] = args[..] else { + assert!(extra.is_empty()); + let selector_idx = selector_idx.unwrap(); + let [op_id, a, b, c] = lookup_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 +368,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 +381,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 +394,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); @@ -411,10 +405,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.unwrap()), - 1.into(), - ); + trace.set_current_row(&format!("{}[{}]", Self::SELECTORS, selector_idx), 1.into()); } } @@ -426,20 +417,21 @@ impl SubmachineKind for SplitGlMachine { fn add_operation( trace: &mut SubmachineTrace, - _op: &str, - args: &[Elem], - selector: Option, - _submachines: &[&mut dyn Submachine], + selector_idx: Option, + lookup_args: &[F], + extra: &[F], ) { - let [output_hi, output_low] = args[..] else { + assert!(extra.is_empty()); + let selector_idx = selector_idx.unwrap(); + let [_input, output_lo, output_hi] = lookup_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 +473,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 +483,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 +493,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 +503,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 +513,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 +523,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 +533,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()); @@ -551,12 +543,9 @@ 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()), - 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(&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)); } } @@ -567,27 +556,27 @@ impl SubmachineKind for PublicsMachine { const BLOCK_SIZE: u32 = 1; fn add_operation( trace: &mut SubmachineTrace, - _op: &str, - args: &[Elem], - selector: Option, - _submachines: &[&mut dyn Submachine], + selector_idx: Option, + lookup_args: &[F], + extra: &[F], ) { - assert!(selector.is_none()); - let [addr, value] = args[..] else { + assert!(selector_idx.is_none()); + assert!(extra.is_empty()); + let [addr, value] = lookup_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; } } @@ -600,14 +589,21 @@ impl SubmachineKind for PoseidonGlMachine { fn add_operation( trace: &mut SubmachineTrace, - _op: &str, - args: &[Elem], - selector: Option, - _submachines: &[&mut dyn Submachine], + 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]", @@ -651,22 +647,16 @@ impl SubmachineKind for PoseidonGlMachine { "x7[9]", "x7[10]", "x7[11]", ]; - let [input_addr, output_addr, time_step] = 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 +664,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 +684,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 +709,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, "time_step", time_step); @@ -732,14 +721,14 @@ impl SubmachineKind for PoseidonGlMachine { // set selector trace.set_current_block( Self::BLOCK_SIZE, - &format!("{}[{}]", Self::SELECTORS, selector.unwrap()), + &format!("{}[{}]", Self::SELECTORS, selector_idx), 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]); } }