diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 038c1b93..fbcd2115 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -260,6 +260,7 @@ mod tests { let mut row: cpu::columns::CpuColumnsView = [F::ZERO; CpuStark::::COLUMNS].into(); row.is_cpu_cycle = F::ONE; + row.program_counter = F::from_canonical_usize(i); row.opcode = [ (logic::columns::IS_AND, 0x16), (logic::columns::IS_OR, 0x17), @@ -319,10 +320,11 @@ mod tests { } // Pad to a power of two. - for _ in cpu_trace_rows.len()..cpu_trace_rows.len().next_power_of_two() { + for i in 0..cpu_trace_rows.len().next_power_of_two() - cpu_trace_rows.len() { let mut row: cpu::columns::CpuColumnsView = [F::ZERO; CpuStark::::COLUMNS].into(); row.is_cpu_cycle = F::ONE; + row.program_counter = F::from_canonical_usize(i + num_logic_rows); cpu_stark.generate(row.borrow_mut()); cpu_trace_rows.push(row.into()); } @@ -335,10 +337,6 @@ mod tests { let last_row: &mut cpu::columns::CpuColumnsView = cpu_trace_rows[num_rows - 1].borrow_mut(); last_row.program_counter = halt_label; - - let second_last_row: &mut cpu::columns::CpuColumnsView = - cpu_trace_rows[num_rows - 2].borrow_mut(); - second_last_row.next_program_counter = halt_label; } trace_rows_to_poly_values(cpu_trace_rows) diff --git a/evm/src/cpu/columns/mod.rs b/evm/src/cpu/columns/mod.rs index fbf20af2..1f0eff2d 100644 --- a/evm/src/cpu/columns/mod.rs +++ b/evm/src/cpu/columns/mod.rs @@ -146,9 +146,6 @@ pub struct CpuColumnsView { /// If CPU cycle: the opcode, broken up into bits in **big-endian** order. pub opcode_bits: [T; 8], - /// If CPU cycle: The program counter for the next instruction. - pub next_program_counter: T, - /// Filter. 1 iff a Keccak permutation is computed on this row. pub is_keccak: T, diff --git a/evm/src/cpu/control_flow.rs b/evm/src/cpu/control_flow.rs index cf24afca..90a76d46 100644 --- a/evm/src/cpu/control_flow.rs +++ b/evm/src/cpu/control_flow.rs @@ -5,9 +5,38 @@ use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::cpu::columns::CpuColumnsView; +use crate::cpu::columns::{CpuColumnsView, COL_MAP}; use crate::cpu::kernel::aggregator::KERNEL; +// TODO: This list is incomplete. +const NATIVE_INSTRUCTIONS: [usize; 25] = [ + COL_MAP.is_add, + COL_MAP.is_mul, + COL_MAP.is_sub, + COL_MAP.is_div, + COL_MAP.is_sdiv, + COL_MAP.is_mod, + COL_MAP.is_smod, + COL_MAP.is_addmod, + COL_MAP.is_mulmod, + COL_MAP.is_signextend, + COL_MAP.is_lt, + COL_MAP.is_gt, + COL_MAP.is_slt, + COL_MAP.is_sgt, + COL_MAP.is_eq, + COL_MAP.is_iszero, + COL_MAP.is_and, + COL_MAP.is_or, + COL_MAP.is_xor, + COL_MAP.is_not, + COL_MAP.is_byte, + COL_MAP.is_shl, + COL_MAP.is_shr, + COL_MAP.is_sar, + COL_MAP.is_pop, +]; + fn get_halt_pcs() -> (F, F) { let halt_pc0 = KERNEL.global_labels["halt_pc0"]; let halt_pc1 = KERNEL.global_labels["halt_pc1"]; @@ -26,10 +55,15 @@ pub fn eval_packed_generic( // Once we start executing instructions, then we continue until the end of the table. yield_constr.constraint_transition(lv.is_cpu_cycle * (nv.is_cpu_cycle - P::ONES)); - // If a row is a CPU cycle, then its `next_program_counter` becomes the `program_counter` of the - // next row. - yield_constr - .constraint_transition(lv.is_cpu_cycle * (nv.program_counter - lv.next_program_counter)); + // If a row is a CPU cycle and executing a native instruction (implemented as a table row; not + // microcoded) then the program counter is incremented by 1 to obtain the next row's program + // counter. + let is_native_instruction: P = NATIVE_INSTRUCTIONS.iter().map(|&col_i| lv[col_i]).sum(); + yield_constr.constraint_transition( + lv.is_cpu_cycle + * is_native_instruction + * (lv.program_counter - nv.program_counter + P::ONES), + ); // If a non-CPU cycle row is followed by a CPU cycle row, then the `program_counter` of the CPU // cycle row is 0. @@ -64,11 +98,15 @@ pub fn eval_ext_circuit, const D: usize>( yield_constr.constraint_transition(builder, constr); } - // If a row is a CPU cycle, then its `next_program_counter` becomes the `program_counter` of the - // next row. + // If a row is a CPU cycle and executing a native instruction (implemented as a table row; not + // microcoded) then the program counter is incremented by 1 to obtain the next row's program + // counter. { - let constr = builder.sub_extension(nv.program_counter, lv.next_program_counter); - let constr = builder.mul_extension(lv.is_cpu_cycle, constr); + let is_native_instruction = + builder.add_many_extension(NATIVE_INSTRUCTIONS.iter().map(|&col_i| lv[col_i])); + let filter = builder.mul_extension(lv.is_cpu_cycle, is_native_instruction); + let pc_diff = builder.sub_extension(lv.program_counter, nv.program_counter); + let constr = builder.mul_add_extension(filter, pc_diff, filter); yield_constr.constraint_transition(builder, constr); }