From 7a1a87ff692306b5a8c992217aa01465c8d132c7 Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Sun, 13 Apr 2025 19:04:12 -0700 Subject: [PATCH 01/49] feat: typeless `AddressMap` with typed APIs (#1559) Note: this PR is not targeting `main`. I've used `TODO` and `TEMP` to mark places in code that will need to be cleaned up before merging to `main`. Beginning the refactor of online memory to allow different host types in different address spaces. Going to touch a lot of APIs. Focusing on stabilizing APIs - currently this PR will not improve performance. Tests will not all pass because I have intentionally disabled some logging required for trace generation. Only execution tests will pass (or run the execute benchmark). In future PR(s): - [ ] make `Memory` trait for execution read/write API - [ ] better handling of type conversions for memory image - [ ] replace the underlying memory implementation with other implementations like mmap Towards INT-3743 Even with wasteful conversions, execution is faster: Before: https://github.com/openvm-org/openvm/actions/runs/14318675080 After: https://github.com/openvm-org/openvm/actions/runs/14371335248?pr=1559 --- crates/toolchain/instructions/src/exe.rs | 9 +- crates/toolchain/transpiler/src/util.rs | 15 +- crates/vm/src/arch/extensions.rs | 2 +- crates/vm/src/arch/segment.rs | 4 +- crates/vm/src/arch/vm.rs | 14 +- .../src/system/memory/controller/interface.rs | 2 +- crates/vm/src/system/memory/controller/mod.rs | 95 +-- .../vm/src/system/memory/merkle/tests/mod.rs | 76 ++- crates/vm/src/system/memory/offline.rs | 206 ++++--- crates/vm/src/system/memory/online.rs | 121 ++-- crates/vm/src/system/memory/paged_vec.rs | 581 ++++++++++-------- crates/vm/src/system/memory/persistent.rs | 8 +- crates/vm/src/system/memory/tests.rs | 13 +- crates/vm/src/system/memory/tree/mod.rs | 2 +- .../src/system/memory/tree/public_values.rs | 12 +- crates/vm/src/system/native_adapter/mod.rs | 6 +- crates/vm/src/system/program/trace.rs | 19 +- crates/vm/src/utils/stark_utils.rs | 13 +- crates/vm/tests/integration_test.rs | 8 +- .../ecc/circuit/src/weierstrass_extension.rs | 8 +- extensions/keccak256/circuit/src/lib.rs | 14 +- .../src/adapters/alu_native_adapter.rs | 4 +- .../src/adapters/branch_native_adapter.rs | 2 +- .../circuit/src/adapters/convert_adapter.rs | 4 +- .../src/adapters/loadstore_native_adapter.rs | 9 +- .../src/adapters/native_vectorized_adapter.rs | 6 +- extensions/native/circuit/src/extension.rs | 4 +- extensions/native/circuit/src/fri/mod.rs | 6 +- extensions/native/circuit/src/jal/mod.rs | 6 +- .../native/circuit/src/poseidon2/chip.rs | 26 +- .../pairing/circuit/src/pairing_extension.rs | 21 +- extensions/rv32-adapters/src/eq_mod.rs | 12 +- extensions/rv32-adapters/src/heap.rs | 10 +- extensions/rv32-adapters/src/heap_branch.rs | 4 +- extensions/rv32-adapters/src/vec_heap.rs | 12 +- .../rv32-adapters/src/vec_heap_two_reads.rs | 13 +- extensions/rv32im/circuit/src/adapters/alu.rs | 24 +- .../rv32im/circuit/src/adapters/branch.rs | 9 +- .../rv32im/circuit/src/adapters/jalr.rs | 11 +- .../rv32im/circuit/src/adapters/loadstore.rs | 29 +- extensions/rv32im/circuit/src/adapters/mod.rs | 13 +- extensions/rv32im/circuit/src/adapters/mul.rs | 12 +- .../rv32im/circuit/src/adapters/rdwrite.rs | 6 +- .../rv32im/circuit/src/base_alu/tests.rs | 14 +- extensions/rv32im/circuit/src/extension.rs | 8 +- .../rv32im/circuit/src/hintstore/mod.rs | 13 +- extensions/rv32im/tests/src/lib.rs | 2 +- .../sha256/circuit/src/sha256_chip/mod.rs | 12 +- 48 files changed, 811 insertions(+), 709 deletions(-) diff --git a/crates/toolchain/instructions/src/exe.rs b/crates/toolchain/instructions/src/exe.rs index fb84ec7da5..9db5f242ac 100644 --- a/crates/toolchain/instructions/src/exe.rs +++ b/crates/toolchain/instructions/src/exe.rs @@ -5,8 +5,9 @@ use serde::{Deserialize, Serialize}; use crate::program::Program; -/// Memory image is a map from (address space, address) to word. -pub type MemoryImage = BTreeMap<(u32, u32), F>; +// TODO[jpw]: delete this +/// Memory image is a map from (address space, address * size_of) to u8. +pub type SparseMemoryImage = BTreeMap<(u32, u32), u8>; /// Stores the starting address, end address, and name of a set of function. pub type FnBounds = BTreeMap; @@ -22,7 +23,7 @@ pub struct VmExe { /// Start address of pc. pub pc_start: u32, /// Initial memory image. - pub init_memory: MemoryImage, + pub init_memory: SparseMemoryImage, /// Starting + ending bounds for each function. pub fn_bounds: FnBounds, } @@ -40,7 +41,7 @@ impl VmExe { self.pc_start = pc_start; self } - pub fn with_init_memory(mut self, init_memory: MemoryImage) -> Self { + pub fn with_init_memory(mut self, init_memory: SparseMemoryImage) -> Self { self.init_memory = init_memory; self } diff --git a/crates/toolchain/transpiler/src/util.rs b/crates/toolchain/transpiler/src/util.rs index d9135de153..c5711653ff 100644 --- a/crates/toolchain/transpiler/src/util.rs +++ b/crates/toolchain/transpiler/src/util.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use openvm_instructions::{ - exe::MemoryImage, + exe::SparseMemoryImage, instruction::Instruction, riscv::{RV32_MEMORY_AS, RV32_REGISTER_NUM_LIMBS}, utils::isize_to_field, @@ -165,17 +165,14 @@ pub fn nop() -> Instruction { } } -/// Converts our memory image (u32 -> [u8; 4]) into Vm memory image ((as, address) -> word) -pub fn elf_memory_image_to_openvm_memory_image( +/// Converts our memory image (u32 -> [u8; 4]) into Vm memory image ((as=2, address) -> byte) +pub fn elf_memory_image_to_openvm_memory_image( memory_image: BTreeMap, -) -> MemoryImage { - let mut result = MemoryImage::new(); +) -> SparseMemoryImage { + let mut result = SparseMemoryImage::new(); for (addr, word) in memory_image { for (i, byte) in word.to_le_bytes().into_iter().enumerate() { - result.insert( - (RV32_MEMORY_AS, addr + i as u32), - F::from_canonical_u8(byte), - ); + result.insert((RV32_MEMORY_AS, addr + i as u32), byte); } } result diff --git a/crates/vm/src/arch/extensions.rs b/crates/vm/src/arch/extensions.rs index adda318f6a..839d8485ee 100644 --- a/crates/vm/src/arch/extensions.rs +++ b/crates/vm/src/arch/extensions.rs @@ -788,7 +788,7 @@ impl VmChipComplex { self.base.program_chip.set_program(program); } - pub(crate) fn set_initial_memory(&mut self, memory: MemoryImage) { + pub(crate) fn set_initial_memory(&mut self, memory: MemoryImage) { self.base.memory_controller.set_initial_memory(memory); } diff --git a/crates/vm/src/arch/segment.rs b/crates/vm/src/arch/segment.rs index 634632ce2b..686282877e 100644 --- a/crates/vm/src/arch/segment.rs +++ b/crates/vm/src/arch/segment.rs @@ -145,7 +145,7 @@ where { pub chip_complex: VmChipComplex, /// Memory image after segment was executed. Not used in trace generation. - pub final_memory: Option>, + pub final_memory: Option, pub since_last_segment_check: usize, pub trace_height_constraints: Vec, @@ -168,7 +168,7 @@ impl> ExecutionSegment { config: &VC, program: Program, init_streams: Streams, - initial_memory: Option>, + initial_memory: Option, trace_height_constraints: Vec, #[allow(unused_variables)] fn_bounds: FnBounds, ) -> Self { diff --git a/crates/vm/src/arch/vm.rs b/crates/vm/src/arch/vm.rs index a826fb4137..7783ae396e 100644 --- a/crates/vm/src/arch/vm.rs +++ b/crates/vm/src/arch/vm.rs @@ -47,7 +47,6 @@ pub enum GenerationError { } /// VM memory state for continuations. -pub type VmMemoryState = MemoryImage; #[derive(Clone, Default, Debug)] pub struct Streams { @@ -95,11 +94,11 @@ pub enum ExitCode { pub struct VmExecutorResult { pub per_segment: Vec>, /// When VM is running on persistent mode, public values are stored in a special memory space. - pub final_memory: Option>>, + pub final_memory: Option, } pub struct VmExecutorNextSegmentState { - pub memory: MemoryImage, + pub memory: MemoryImage, pub input: Streams, pub pc: u32, #[cfg(feature = "bench-metrics")] @@ -107,7 +106,7 @@ pub struct VmExecutorNextSegmentState { } impl VmExecutorNextSegmentState { - pub fn new(memory: MemoryImage, input: impl Into>, pc: u32) -> Self { + pub fn new(memory: MemoryImage, input: impl Into>, pc: u32) -> Self { Self { memory, input: input.into(), @@ -170,12 +169,13 @@ where let mem_config = self.config.system().memory_config; let exe = exe.into(); let mut segment_results = vec![]; - let memory = AddressMap::from_iter( + let memory = AddressMap::from_sparse( mem_config.as_offset, 1 << mem_config.as_height, 1 << mem_config.pointer_max_bits, exe.init_memory.clone(), ); + let pc = exe.pc_start; let mut state = VmExecutorNextSegmentState::new(memory, input, pc); @@ -278,7 +278,7 @@ where &self, exe: impl Into>, input: impl Into>, - ) -> Result>, ExecutionError> { + ) -> Result, ExecutionError> { let mut last = None; self.execute_and_then( exe, @@ -587,7 +587,7 @@ where &self, exe: impl Into>, input: impl Into>, - ) -> Result>, ExecutionError> { + ) -> Result, ExecutionError> { self.executor.execute(exe, input) } diff --git a/crates/vm/src/system/memory/controller/interface.rs b/crates/vm/src/system/memory/controller/interface.rs index b51e960a32..b00171a3c2 100644 --- a/crates/vm/src/system/memory/controller/interface.rs +++ b/crates/vm/src/system/memory/controller/interface.rs @@ -13,7 +13,7 @@ pub enum MemoryInterface { Persistent { boundary_chip: PersistentBoundaryChip, merkle_chip: MemoryMerkleChip, - initial_memory: MemoryImage, + initial_memory: MemoryImage, }, } diff --git a/crates/vm/src/system/memory/controller/mod.rs b/crates/vm/src/system/memory/controller/mod.rs index 680a03ab8e..8b4db5b32c 100644 --- a/crates/vm/src/system/memory/controller/mod.rs +++ b/crates/vm/src/system/memory/controller/mod.rs @@ -3,7 +3,6 @@ use std::{ collections::BTreeMap, iter, marker::PhantomData, - mem, sync::{Arc, Mutex}, }; @@ -62,7 +61,7 @@ pub const BOUNDARY_AIR_OFFSET: usize = 0; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct RecordId(pub usize); -pub type MemoryImage = AddressMap; +pub type MemoryImage = AddressMap; #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -98,7 +97,7 @@ pub struct MemoryController { // Store separately to avoid smart pointer reference each time range_checker_bus: VariableRangeCheckerBus, // addr_space -> Memory data structure - memory: Memory, + memory: Memory, /// A reference to the `OfflineMemory`. Will be populated after `finalize()`. offline_memory: Arc>>, pub access_adapters: AccessAdapterInventory, @@ -314,7 +313,7 @@ impl MemoryController { } } - pub fn memory_image(&self) -> &MemoryImage { + pub fn memory_image(&self) -> &MemoryImage { &self.memory.data } @@ -344,7 +343,7 @@ impl MemoryController { } } - pub fn set_initial_memory(&mut self, memory: MemoryImage) { + pub fn set_initial_memory(&mut self, memory: MemoryImage) { if self.timestamp() > INITIAL_TIMESTAMP + 1 { panic!("Cannot set initial memory after first timestamp"); } @@ -379,7 +378,16 @@ impl MemoryController { (record_id, data) } - pub fn read(&mut self, address_space: F, pointer: F) -> (RecordId, [F; N]) { + // TEMP[jpw]: Function is safe temporarily for refactoring + /// # Safety + /// The type `T` must be stack-allocated `repr(C)` or `repr(transparent)`, and it must be the + /// exact type used to represent a single memory cell in address space `address_space`. For + /// standard usage, `T` is either `u8` or `F` where `F` is the base field of the ZK backend. + pub fn read( + &mut self, + address_space: F, + pointer: F, + ) -> (RecordId, [T; N]) { let address_space_u32 = address_space.as_canonical_u32(); let ptr_u32 = pointer.as_canonical_u32(); assert!( @@ -387,7 +395,7 @@ impl MemoryController { "memory out of bounds: {ptr_u32:?}", ); - let (record_id, values) = self.memory.read::(address_space_u32, ptr_u32); + let (record_id, values) = unsafe { self.memory.read::(address_space_u32, ptr_u32) }; (record_id, values) } @@ -395,34 +403,34 @@ impl MemoryController { /// Reads a word directly from memory without updating internal state. /// /// Any value returned is unconstrained. - pub fn unsafe_read_cell(&self, addr_space: F, ptr: F) -> F { - self.unsafe_read::<1>(addr_space, ptr)[0] + pub fn unsafe_read_cell(&self, addr_space: F, ptr: F) -> T { + self.unsafe_read::(addr_space, ptr)[0] } /// Reads a word directly from memory without updating internal state. /// /// Any value returned is unconstrained. - pub fn unsafe_read(&self, addr_space: F, ptr: F) -> [F; N] { + pub fn unsafe_read(&self, addr_space: F, ptr: F) -> [T; N] { let addr_space = addr_space.as_canonical_u32(); let ptr = ptr.as_canonical_u32(); - array::from_fn(|i| self.memory.get(addr_space, ptr + i as u32)) + unsafe { array::from_fn(|i| self.memory.get::(addr_space, ptr + i as u32)) } } /// Writes `data` to the given cell. /// /// Returns the `RecordId` and previous data. - pub fn write_cell(&mut self, address_space: F, pointer: F, data: F) -> (RecordId, F) { - let (record_id, [data]) = self.write(address_space, pointer, [data]); + pub fn write_cell(&mut self, address_space: F, pointer: F, data: T) -> (RecordId, T) { + let (record_id, [data]) = self.write(address_space, pointer, &[data]); (record_id, data) } - pub fn write( + pub fn write( &mut self, address_space: F, pointer: F, - data: [F; N], - ) -> (RecordId, [F; N]) { - assert_ne!(address_space, F::ZERO); + data: &[T; N], + ) -> (RecordId, [T; N]) { + debug_assert_ne!(address_space, F::ZERO); let address_space_u32 = address_space.as_canonical_u32(); let ptr_u32 = pointer.as_canonical_u32(); assert!( @@ -430,7 +438,7 @@ impl MemoryController { "memory out of bounds: {ptr_u32:?}", ); - self.memory.write(address_space_u32, ptr_u32, data) + unsafe { self.memory.write::(address_space_u32, ptr_u32, data) } } pub fn aux_cols_factory(&self) -> MemoryAuxColsFactory { @@ -455,26 +463,27 @@ impl MemoryController { } fn replay_access_log(&mut self) { - let log = mem::take(&mut self.memory.log); - if log.is_empty() { - // Online memory logs may be empty, but offline memory may be replayed from external - // sources. In these cases, we skip the calls to replay access logs because - // `set_log_capacity` would panic. - tracing::debug!("skipping replay_access_log"); - return; - } - - let mut offline_memory = self.offline_memory.lock().unwrap(); - offline_memory.set_log_capacity(log.len()); - - for entry in log { - Self::replay_access( - entry, - &mut offline_memory, - &mut self.interface_chip, - &mut self.access_adapters, - ); - } + unimplemented!(); + // let log = mem::take(&mut self.memory.log); + // if log.is_empty() { + // // Online memory logs may be empty, but offline memory may be replayed from external + // sources. // In these cases, we skip the calls to replay access logs because + // `set_log_capacity` would // panic. + // tracing::debug!("skipping replay_access_log"); + // return; + // } + + // let mut offline_memory = self.offline_memory.lock().unwrap(); + // offline_memory.set_log_capacity(log.len()); + + // for entry in log { + // Self::replay_access( + // entry, + // &mut offline_memory, + // &mut self.interface_chip, + // &mut self.access_adapters, + // ); + // } } /// Low-level API to replay a single memory access log entry and populate the [OfflineMemory], @@ -704,13 +713,13 @@ impl MemoryController { pub fn offline_memory(&self) -> Arc>> { self.offline_memory.clone() } - pub fn get_memory_logs(&self) -> &Vec> { + pub fn get_memory_logs(&self) -> &Vec> { &self.memory.log } - pub fn set_memory_logs(&mut self, logs: Vec>) { + pub fn set_memory_logs(&mut self, logs: Vec>) { self.memory.log = logs; } - pub fn take_memory_logs(&mut self) -> Vec> { + pub fn take_memory_logs(&mut self) -> Vec> { std::mem::take(&mut self.memory.log) } } @@ -857,9 +866,9 @@ mod tests { if rng.gen_bool(0.5) { let data = F::from_canonical_u32(rng.gen_range(0..1 << 30)); - memory_controller.write(address_space, pointer, [data]); + memory_controller.write(address_space, pointer, &[data]); } else { - memory_controller.read::<1>(address_space, pointer); + memory_controller.read::(address_space, pointer); } } assert!(memory_controller diff --git a/crates/vm/src/system/memory/merkle/tests/mod.rs b/crates/vm/src/system/memory/merkle/tests/mod.rs index 05c966dc23..98ba089ad2 100644 --- a/crates/vm/src/system/memory/merkle/tests/mod.rs +++ b/crates/vm/src/system/memory/merkle/tests/mod.rs @@ -7,7 +7,7 @@ use std::{ use openvm_stark_backend::{ interaction::{PermutationCheckBus, PermutationInteractionType}, - p3_field::FieldAlgebra, + p3_field::{FieldAlgebra, PrimeField32}, p3_matrix::dense::RowMajorMatrix, prover::types::AirProofInput, Chip, ChipUsageGetter, @@ -39,9 +39,9 @@ const COMPRESSION_BUS: PermutationCheckBus = PermutationCheckBus::new(POSEIDON2_ fn test( memory_dimensions: MemoryDimensions, - initial_memory: &MemoryImage, + initial_memory: &MemoryImage, touched_labels: BTreeSet<(u32, u32)>, - final_memory: &MemoryImage, + final_memory: &MemoryImage, ) { let MemoryDimensions { as_height, @@ -51,30 +51,31 @@ fn test( let merkle_bus = PermutationCheckBus::new(MEMORY_MERKLE_BUS); // checking validity of test data - for ((address_space, pointer), value) in final_memory.items() { + for ((address_space, pointer), value) in final_memory.items::() { let label = pointer / CHUNK as u32; assert!(address_space - as_offset < (1 << as_height)); assert!(pointer < ((CHUNK << address_height).div_ceil(PAGE_SIZE) * PAGE_SIZE) as u32); - if initial_memory.get(&(address_space, pointer)) != Some(&value) { + if unsafe { initial_memory.get::((address_space, pointer)) } != value { assert!(touched_labels.contains(&(address_space, label))); } } - for key in initial_memory.items().map(|(key, _)| key) { - assert!(final_memory.get(&key).is_some()); - } - for &(address_space, label) in touched_labels.iter() { - let mut contains_some_key = false; - for i in 0..CHUNK { - if final_memory - .get(&(address_space, label * CHUNK as u32 + i as u32)) - .is_some() - { - contains_some_key = true; - break; - } - } - assert!(contains_some_key); - } + // for key in initial_memory.items().map(|(key, _)| key) { + // assert!(unsafe { final_memory.get(key).is_some() }); + // } + // for &(address_space, label) in touched_labels.iter() { + // let mut contains_some_key = false; + // for i in 0..CHUNK { + // if unsafe { + // final_memory + // .get((address_space, label * CHUNK as u32 + i as u32)) + // .is_some() + // } { + // contains_some_key = true; + // break; + // } + // } + // assert!(contains_some_key); + // } let mut hash_test_chip = HashTestChip::new(); @@ -126,12 +127,11 @@ fn test( }; for (address_space, address_label) in touched_labels { - let initial_values = array::from_fn(|i| { - initial_memory - .get(&(address_space, address_label * CHUNK as u32 + i as u32)) - .copied() - .unwrap_or_default() - }); + let initial_values = unsafe { + array::from_fn(|i| { + initial_memory.get((address_space, address_label * CHUNK as u32 + i as u32)) + }) + }; let as_label = address_space - as_offset; interaction( PermutationInteractionType::Send, @@ -180,8 +180,8 @@ fn test( .expect("Verification failed"); } -fn memory_to_partition( - memory: &MemoryImage, +fn memory_to_partition( + memory: &MemoryImage, ) -> Equipartition { let mut memory_partition = Equipartition::new(); for ((address_space, pointer), value) in memory.items() { @@ -203,8 +203,12 @@ fn random_test( let mut rng = create_seeded_rng(); let mut next_u32 = || rng.next_u64() as u32; - let mut initial_memory = AddressMap::new(1, 2, CHUNK << height); - let mut final_memory = AddressMap::new(1, 2, CHUNK << height); + let as_cnt = 2; + let mut initial_memory = AddressMap::new(1, as_cnt, CHUNK << height); + let mut final_memory = AddressMap::new(1, as_cnt, CHUNK << height); + // TEMP[jpw]: override so address space uses field element + initial_memory.cell_size = vec![4; as_cnt]; + final_memory.cell_size = vec![4; as_cnt]; let mut seen = HashSet::new(); let mut touched_labels = BTreeSet::new(); @@ -221,15 +225,19 @@ fn random_test( if is_initial && num_initial_addresses != 0 { num_initial_addresses -= 1; let value = BabyBear::from_canonical_u32(next_u32() % max_value); - initial_memory.insert(&(address_space, pointer), value); - final_memory.insert(&(address_space, pointer), value); + unsafe { + initial_memory.insert((address_space, pointer), value); + final_memory.insert((address_space, pointer), value); + } } if is_touched && num_touched_addresses != 0 { num_touched_addresses -= 1; touched_labels.insert((address_space, label)); if value_changes || !is_initial { let value = BabyBear::from_canonical_u32(next_u32() % max_value); - final_memory.insert(&(address_space, pointer), value); + unsafe { + final_memory.insert((address_space, pointer), value); + } } } } diff --git a/crates/vm/src/system/memory/offline.rs b/crates/vm/src/system/memory/offline.rs index 74bb238811..93423a3a48 100644 --- a/crates/vm/src/system/memory/offline.rs +++ b/crates/vm/src/system/memory/offline.rs @@ -28,7 +28,7 @@ struct BlockData { struct BlockMap { /// Block ids. 0 is a special value standing for the default block. - id: AddressMap, + id: AddressMap, /// The place where non-default blocks are stored. storage: Vec, initial_block_size: usize, @@ -53,23 +53,22 @@ impl BlockMap { } } - pub fn get_without_adding(&self, address: &(u32, u32)) -> BlockData { - let idx = self.id.get(address).unwrap_or(&0); - if idx == &0 { + pub fn get_without_adding(&self, address: (u32, u32)) -> BlockData { + let idx = unsafe { self.id.get::(address) }; + if idx == 0 { Self::initial_block_data(address.1, self.initial_block_size) } else { self.storage[idx - 1].clone() } } - pub fn get(&mut self, address: &(u32, u32)) -> &BlockData { - let (address_space, pointer) = *address; - let idx = self.id.get(&(address_space, pointer)).unwrap_or(&0); - if idx == &0 { + pub fn get(&mut self, (address_space, pointer): (u32, u32)) -> &BlockData { + let idx = unsafe { self.id.get::((address_space, pointer)) }; + if idx == 0 { // `initial_block_size` is a power of two, as asserted in `from_mem_config`. let pointer = pointer & !(self.initial_block_size as u32 - 1); self.set_range( - &(address_space, pointer), + (address_space, pointer), self.initial_block_size, Self::initial_block_data(pointer, self.initial_block_size), ); @@ -79,13 +78,12 @@ impl BlockMap { } } - pub fn get_mut(&mut self, address: &(u32, u32)) -> &mut BlockData { - let (address_space, pointer) = *address; - let idx = self.id.get(&(address_space, pointer)).unwrap_or(&0); - if idx == &0 { + pub fn get_mut(&mut self, (address_space, pointer): (u32, u32)) -> &mut BlockData { + let idx = unsafe { self.id.get::((address_space, pointer)) }; + if idx == 0 { let pointer = pointer - pointer % self.initial_block_size as u32; self.set_range( - &(address_space, pointer), + (address_space, pointer), self.initial_block_size, Self::initial_block_data(pointer, self.initial_block_size), ); @@ -95,18 +93,31 @@ impl BlockMap { } } - pub fn set_range(&mut self, address: &(u32, u32), len: usize, block: BlockData) { - let (address_space, pointer) = address; + pub fn set_range( + &mut self, + (address_space, pointer): (u32, u32), + len: usize, + block: BlockData, + ) { self.storage.push(block); for i in 0..len { - self.id - .insert(&(*address_space, pointer + i as u32), self.storage.len()); + unsafe { + self.id + .insert((address_space, pointer + i as u32), self.storage.len()); + } } } pub fn items(&self) -> impl Iterator + '_ { self.id - .items() + .paged_vecs + .iter() + .enumerate() + .flat_map(move |(as_idx, paged_vec)| { + paged_vec.iter::().map(move |(ptr_idx, x)| { + ((as_idx as u32 + self.id.as_offset, ptr_idx as u32), x) + }) + }) .filter(|(_, idx)| *idx > 0) .map(|(address, idx)| (address, &self.storage[idx - 1])) } @@ -141,7 +152,7 @@ impl MemoryRecord { pub struct OfflineMemory { block_data: BlockMap, - data: Vec>, + data: Vec>, as_offset: u32, timestamp: u32, timestamp_max_bits: usize, @@ -157,7 +168,7 @@ impl OfflineMemory { /// /// Panics if the initial block size is not a power of two. pub fn new( - initial_memory: MemoryImage, + initial_memory: MemoryImage, initial_block_size: usize, memory_bus: MemoryBus, range_checker: SharedVariableRangeCheckerChip, @@ -176,7 +187,7 @@ impl OfflineMemory { } } - pub fn set_initial_memory(&mut self, initial_memory: MemoryImage, config: MemoryConfig) { + pub fn set_initial_memory(&mut self, initial_memory: MemoryImage, config: MemoryConfig) { assert_eq!(self.timestamp, INITIAL_TIMESTAMP + 1); assert_eq!(initial_memory.as_offset, config.as_offset); self.as_offset = config.as_offset; @@ -227,20 +238,21 @@ impl OfflineMemory { debug_assert!(prev_timestamp < self.timestamp); - let pointer = pointer as usize; - let prev_data = self.data[(address_space - self.as_offset) as usize] - .set_range(pointer..pointer + len, &values); - - let record = MemoryRecord { - address_space: F::from_canonical_u32(address_space), - pointer: F::from_canonical_usize(pointer), - timestamp: self.timestamp, - prev_timestamp, - data: values, - prev_data: Some(prev_data), - }; - self.log.push(Some(record)); - self.timestamp += 1; + todo!(); + // let pointer = pointer as usize; + // let prev_data = self.data[(address_space - self.as_offset) as usize] + // .set_range(pointer..pointer + len, &values); + + // let record = MemoryRecord { + // address_space: F::from_canonical_u32(address_space), + // pointer: F::from_canonical_usize(pointer), + // timestamp: self.timestamp, + // prev_timestamp, + // data: values, + // prev_data: Some(prev_data), + // }; + // self.log.push(Some(record)); + // self.timestamp += 1; } /// Reads an array of values from the memory at the specified address space and start index. @@ -301,7 +313,7 @@ impl OfflineMemory { .collect(); for &(address_space, pointer) in to_access.iter() { - let block = self.block_data.get(&(address_space, pointer)); + let block = self.block_data.get((address_space, pointer)); if block.pointer != pointer || block.size != N { self.access(address_space, pointer, N, adapter_records); } @@ -309,7 +321,7 @@ impl OfflineMemory { let mut equipartition = TimestampedEquipartition::::new(); for (address_space, pointer) in to_access { - let block = self.block_data.get(&(address_space, pointer)); + let block = self.block_data.get((address_space, pointer)); debug_assert_eq!(block.pointer % N as u32, 0); debug_assert_eq!(block.size, N); @@ -348,50 +360,51 @@ impl OfflineMemory { let mut cur_ptr = original_block.pointer; let mut cur_size = original_block.size; - while cur_size > 0 { - // Split. - records.add_record(AccessAdapterRecord { - timestamp, - address_space: F::from_canonical_u32(address_space), - start_index: F::from_canonical_u32(cur_ptr), - data: data[(cur_ptr - original_block.pointer) as usize - ..(cur_ptr - original_block.pointer) as usize + cur_size] - .to_vec(), - kind: AccessAdapterRecordKind::Split, - }); - - let half_size = cur_size / 2; - let half_size_u32 = half_size as u32; - let mid_ptr = cur_ptr + half_size_u32; - - if query <= mid_ptr { - // The right is finalized; add it to the partition. - let block = BlockData { - pointer: mid_ptr, - size: half_size, - timestamp, - }; - self.block_data - .set_range(&(address_space, mid_ptr), half_size, block); - } - if query >= cur_ptr + half_size_u32 { - // The left is finalized; add it to the partition. - let block = BlockData { - pointer: cur_ptr, - size: half_size, - timestamp, - }; - self.block_data - .set_range(&(address_space, cur_ptr), half_size, block); - } - if mid_ptr <= query { - cur_ptr = mid_ptr; - } - if cur_ptr == query { - break; - } - cur_size = half_size; - } + todo!() + // while cur_size > 0 { + // // Split. + // records.add_record(AccessAdapterRecord { + // timestamp, + // address_space: F::from_canonical_u32(address_space), + // start_index: F::from_canonical_u32(cur_ptr), + // data: data[(cur_ptr - original_block.pointer) as usize + // ..(cur_ptr - original_block.pointer) as usize + cur_size] + // .to_vec(), + // kind: AccessAdapterRecordKind::Split, + // }); + + // let half_size = cur_size / 2; + // let half_size_u32 = half_size as u32; + // let mid_ptr = cur_ptr + half_size_u32; + + // if query <= mid_ptr { + // // The right is finalized; add it to the partition. + // let block = BlockData { + // pointer: mid_ptr, + // size: half_size, + // timestamp, + // }; + // self.block_data + // .set_range(&(address_space, mid_ptr), half_size, block); + // } + // if query >= cur_ptr + half_size_u32 { + // // The left is finalized; add it to the partition. + // let block = BlockData { + // pointer: cur_ptr, + // size: half_size, + // timestamp, + // }; + // self.block_data + // .set_range(&(address_space, cur_ptr), half_size, block); + // } + // if mid_ptr <= query { + // cur_ptr = mid_ptr; + // } + // if cur_ptr == query { + // break; + // } + // cur_size = half_size; + // } } fn access_updating_timestamp( @@ -407,7 +420,7 @@ impl OfflineMemory { let mut i = 0; while i < size as u32 { - let block = self.block_data.get_mut(&(address_space, pointer + i)); + let block = self.block_data.get_mut((address_space, pointer + i)); debug_assert!(i == 0 || prev_timestamp == Some(block.timestamp)); prev_timestamp = Some(block.timestamp); block.timestamp = self.timestamp; @@ -456,19 +469,19 @@ impl OfflineMemory { pointer: u32, records: &mut AccessAdapterInventory, ) { - let left_block = self.block_data.get(&(address_space, pointer)); + let left_block = self.block_data.get((address_space, pointer)); let left_timestamp = left_block.timestamp; let size = left_block.size; let right_timestamp = self .block_data - .get(&(address_space, pointer + size as u32)) + .get((address_space, pointer + size as u32)) .timestamp; let timestamp = max(left_timestamp, right_timestamp); self.block_data.set_range( - &(address_space, pointer), + (address_space, pointer), 2 * size, BlockData { pointer, @@ -489,24 +502,19 @@ impl OfflineMemory { } fn block_containing(&mut self, address_space: u32, pointer: u32) -> BlockData { - self.block_data - .get_without_adding(&(address_space, pointer)) + self.block_data.get_without_adding((address_space, pointer)) } pub fn get(&self, address_space: u32, pointer: u32) -> F { - self.data[(address_space - self.as_offset) as usize] - .get(pointer as usize) - .cloned() - .unwrap_or_default() + self.data[(address_space - self.as_offset) as usize].get(pointer as usize) } fn range_array(&self, address_space: u32, pointer: u32) -> [F; N] { array::from_fn(|i| self.get(address_space, pointer + i as u32)) } - fn range_vec(&self, address_space: u32, pointer: u32, len: usize) -> Vec { - let pointer = pointer as usize; - self.data[(address_space - self.as_offset) as usize].range_vec(pointer..pointer + len) + fn range_vec(&self, _address_space: u32, _pointer: u32, _len: usize) -> Vec { + unimplemented!("to remove") } pub fn aux_cols_factory(&self) -> MemoryAuxColsFactory { @@ -563,7 +571,7 @@ mod tests { } fn setup_test( - initial_memory: MemoryImage, + initial_memory: MemoryImage, initial_block_size: usize, ) -> (OfflineMemory, AccessAdapterInventory) { let memory_bus = MemoryBus::new(0); @@ -1002,8 +1010,10 @@ mod tests { // Initialize initial memory with blocks at indices 0 and 2 let mut initial_memory = MemoryImage::default(); for i in 0..8 { - initial_memory.insert(&(1, i), F::from_canonical_u32(i + 1)); - initial_memory.insert(&(1, 16 + i), F::from_canonical_u32(i + 1)); + unsafe { + initial_memory.insert((1, i), F::from_canonical_u32(i + 1)); + initial_memory.insert((1, 16 + i), F::from_canonical_u32(i + 1)); + } } let (mut memory, mut adapter_records) = setup_test(initial_memory, 8); diff --git a/crates/vm/src/system/memory/online.rs b/crates/vm/src/system/memory/online.rs index a5bf663e4c..c29565570c 100644 --- a/crates/vm/src/system/memory/online.rs +++ b/crates/vm/src/system/memory/online.rs @@ -1,6 +1,5 @@ use std::fmt::Debug; -use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; use super::paged_vec::{AddressMap, PAGE_SIZE}; @@ -9,6 +8,7 @@ use crate::{ system::memory::{offline::INITIAL_TIMESTAMP, MemoryImage, RecordId}, }; +// TO BE DELETED #[derive(Debug, Clone, Serialize, Deserialize)] pub enum MemoryLogEntry { Read { @@ -25,16 +25,19 @@ pub enum MemoryLogEntry { } /// A simple data structure to read to/write from memory. +/// Internally storage is in raw bytes (untyped) to align with host memory. /// /// Stores a log of memory accesses to reconstruct aspects of memory state for trace generation. -#[derive(Debug)] -pub struct Memory { - pub(super) data: AddressMap, - pub(super) log: Vec>, +pub struct Memory { + // TODO: the memory struct should contain an array of the byte size of the type per address + // space, passed in from MemoryConfig + pub(super) data: AddressMap, + // TODO: delete + pub(super) log: Vec>, timestamp: u32, } -impl Memory { +impl Memory { pub fn new(mem_config: &MemoryConfig) -> Self { Self { data: AddressMap::from_mem_config(mem_config), @@ -44,7 +47,7 @@ impl Memory { } /// Instantiates a new `Memory` data structure from an image. - pub fn from_image(image: MemoryImage, access_capacity: usize) -> Self { + pub fn from_image(image: MemoryImage, access_capacity: usize) -> Self { Self { data: image, timestamp: INITIAL_TIMESTAMP + 1, @@ -53,48 +56,64 @@ impl Memory { } fn last_record_id(&self) -> RecordId { - RecordId(self.log.len() - 1) + // TEMP[jpw] + RecordId(0) + // RecordId(self.log.len() - 1) } /// Writes an array of values to the memory at the specified address space and start index. /// /// Returns the `RecordId` for the memory record and the previous data. - pub fn write( + /// + /// # Safety + /// The type `T` must be stack-allocated `repr(C)`, and it must be the exact type used to + /// represent a single memory cell in address space `address_space`. For standard usage, `T` + /// is either `u8` or `F` where `F` is the base field of the ZK backend. + // @dev: `values` is passed by reference since the data is copied into memory. Even though the + // compiler probably optimizes it, we use reference to avoid any unnecessary copy of + // `values` onto the stack in the function call. + pub unsafe fn write( &mut self, address_space: u32, pointer: u32, - values: [F; N], - ) -> (RecordId, [F; N]) { - assert!(N.is_power_of_two()); + values: &[T; BLOCK_SIZE], + ) -> (RecordId, [T; BLOCK_SIZE]) { + debug_assert!(BLOCK_SIZE.is_power_of_two()); - let prev_data = self.data.set_range(&(address_space, pointer), &values); + let prev_data = self.data.set_range((address_space, pointer), values); - self.log.push(MemoryLogEntry::Write { - address_space, - pointer, - data: values.to_vec(), - }); + // self.log.push(MemoryLogEntry::Write { + // address_space, + // pointer, + // data: values.to_vec(), + // }); self.timestamp += 1; (self.last_record_id(), prev_data) } /// Reads an array of values from the memory at the specified address space and start index. - pub fn read(&mut self, address_space: u32, pointer: u32) -> (RecordId, [F; N]) { - assert!(N.is_power_of_two()); - - self.log.push(MemoryLogEntry::Read { - address_space, - pointer, - len: N, - }); - - let values = if address_space == 0 { - assert_eq!(N, 1, "cannot batch read from address space 0"); - [F::from_canonical_u32(pointer); N] - } else { - self.range_array::(address_space, pointer) - }; + /// + /// # Safety + /// The type `T` must be stack-allocated `repr(C)`, and it must be the exact type used to + /// represent a single memory cell in address space `address_space`. For standard usage, `T` + /// is either `u8` or `F` where `F` is the base field of the ZK backend. + #[inline(always)] + pub unsafe fn read( + &mut self, + address_space: u32, + pointer: u32, + ) -> (RecordId, [T; BLOCK_SIZE]) { + assert!(BLOCK_SIZE.is_power_of_two()); + debug_assert_ne!(address_space, 0); + + // self.log.push(MemoryLogEntry::Read { + // address_space, + // pointer, + // len: N, + // }); + + let values = self.data.get_range((address_space, pointer)); self.timestamp += 1; (self.last_record_id(), values) } @@ -108,44 +127,36 @@ impl Memory { self.timestamp } + /// # Safety + /// The type `T` must be stack-allocated `repr(C)`, and it must be the exact type used to + /// represent a single memory cell in address space `address_space`. For standard usage, `T` + /// is either `u8` or `F` where `F` is the base field of the ZK backend. #[inline(always)] - pub fn get(&self, address_space: u32, pointer: u32) -> F { - *self.data.get(&(address_space, pointer)).unwrap_or(&F::ZERO) - } - - #[inline(always)] - fn range_array(&self, address_space: u32, pointer: u32) -> [F; N] { - self.data.get_range(&(address_space, pointer)) + pub unsafe fn get(&self, address_space: u32, pointer: u32) -> T { + self.data.get((address_space, pointer)) } } #[cfg(test)] mod tests { - use openvm_stark_backend::p3_field::FieldAlgebra; - use openvm_stark_sdk::p3_baby_bear::BabyBear; - use super::Memory; use crate::arch::MemoryConfig; - macro_rules! bba { - [$($x:expr),*] => { - [$(BabyBear::from_canonical_u32($x)),*] - } - } - #[test] fn test_write_read() { let mut memory = Memory::new(&MemoryConfig::default()); let address_space = 1; - memory.write(address_space, 0, bba![1, 2, 3, 4]); + unsafe { + memory.write(address_space, 0, &[1u8, 2, 3, 4]); - let (_, data) = memory.read::<2>(address_space, 0); - assert_eq!(data, bba![1, 2]); + let (_, data) = memory.read::(address_space, 0); + assert_eq!(data, [1u8, 2]); - memory.write(address_space, 2, bba![100]); + memory.write(address_space, 2, &[100u8]); - let (_, data) = memory.read::<4>(address_space, 0); - assert_eq!(data, bba![1, 2, 100, 4]); + let (_, data) = memory.read::(address_space, 0); + assert_eq!(data, [1u8, 2, 100, 4]); + } } } diff --git a/crates/vm/src/system/memory/paged_vec.rs b/crates/vm/src/system/memory/paged_vec.rs index 8a8b030970..1de9e49fe3 100644 --- a/crates/vm/src/system/memory/paged_vec.rs +++ b/crates/vm/src/system/memory/paged_vec.rs @@ -1,27 +1,34 @@ -use std::{mem::MaybeUninit, ops::Range, ptr}; +use std::{marker::PhantomData, mem::MaybeUninit, ptr}; +use itertools::{zip_eq, Itertools}; +use openvm_instructions::exe::SparseMemoryImage; +use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; use crate::arch::MemoryConfig; /// (address_space, pointer) pub type Address = (u32, u32); +/// 4096 is the default page size on host architectures if huge pages is not enabled pub const PAGE_SIZE: usize = 1 << 12; +// TODO[jpw]: replace this with mmap implementation #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PagedVec { - pub pages: Vec>>, +pub struct PagedVec { + /// Assume each page in `pages` is either unalloc or PAGE_SIZE bytes long and aligned to + /// PAGE_SIZE + pub pages: Vec>>, } // ------------------------------------------------------------------ // Common Helper Functions // These functions encapsulate the common logic for copying ranges // across pages, both for read-only and read-write (set) cases. -impl PagedVec { +impl PagedVec { // Copies a range of length `len` starting at index `start` // into the memory pointed to by `dst`. If the relevant page is not - // initialized, fills that portion with T::default(). - fn read_range_generic(&self, start: usize, len: usize, dst: *mut T) { + // initialized, fills that portion with `0u8`. + fn read_range_generic(&self, start: usize, len: usize, dst: *mut u8) { let start_page = start / PAGE_SIZE; let end_page = (start + len - 1) / PAGE_SIZE; unsafe { @@ -30,22 +37,22 @@ impl PagedVec { if let Some(page) = self.pages[start_page].as_ref() { ptr::copy_nonoverlapping(page.as_ptr().add(offset), dst, len); } else { - std::slice::from_raw_parts_mut(dst, len).fill(T::default()); + std::slice::from_raw_parts_mut(dst, len).fill(0u8); } } else { + debug_assert_eq!(start_page + 1, end_page); let offset = start % PAGE_SIZE; let first_part = PAGE_SIZE - offset; if let Some(page) = self.pages[start_page].as_ref() { ptr::copy_nonoverlapping(page.as_ptr().add(offset), dst, first_part); } else { - std::slice::from_raw_parts_mut(dst, first_part).fill(T::default()); + std::slice::from_raw_parts_mut(dst, first_part).fill(0u8); } let second_part = len - first_part; if let Some(page) = self.pages[end_page].as_ref() { ptr::copy_nonoverlapping(page.as_ptr(), dst.add(first_part), second_part); } else { - std::slice::from_raw_parts_mut(dst.add(first_part), second_part) - .fill(T::default()); + std::slice::from_raw_parts_mut(dst.add(first_part), second_part).fill(0u8); } } } @@ -55,29 +62,26 @@ impl PagedVec { // It copies the current values into the memory pointed to by `dst` // and then writes the new values into the underlying pages, // allocating pages (with defaults) if necessary. - fn set_range_generic(&mut self, start: usize, len: usize, new: *const T, dst: *mut T) { + fn set_range_generic(&mut self, start: usize, len: usize, new: *const u8, dst: *mut u8) { let start_page = start / PAGE_SIZE; let end_page = (start + len - 1) / PAGE_SIZE; unsafe { if start_page == end_page { let offset = start % PAGE_SIZE; - let page = - self.pages[start_page].get_or_insert_with(|| vec![T::default(); PAGE_SIZE]); + let page = self.pages[start_page].get_or_insert_with(|| vec![0u8; PAGE_SIZE]); ptr::copy_nonoverlapping(page.as_ptr().add(offset), dst, len); ptr::copy_nonoverlapping(new, page.as_mut_ptr().add(offset), len); } else { let offset = start % PAGE_SIZE; let first_part = PAGE_SIZE - offset; { - let page = - self.pages[start_page].get_or_insert_with(|| vec![T::default(); PAGE_SIZE]); + let page = self.pages[start_page].get_or_insert_with(|| vec![0u8; PAGE_SIZE]); ptr::copy_nonoverlapping(page.as_ptr().add(offset), dst, first_part); ptr::copy_nonoverlapping(new, page.as_mut_ptr().add(offset), first_part); } let second_part = len - first_part; { - let page = - self.pages[end_page].get_or_insert_with(|| vec![T::default(); PAGE_SIZE]); + let page = self.pages[end_page].get_or_insert_with(|| vec![0u8; PAGE_SIZE]); ptr::copy_nonoverlapping(page.as_ptr(), dst.add(first_part), second_part); ptr::copy_nonoverlapping(new.add(first_part), page.as_mut_ptr(), second_part); } @@ -88,68 +92,13 @@ impl PagedVec { // ------------------------------------------------------------------ // Implementation for types requiring Default + Clone -impl PagedVec { +impl PagedVec { pub fn new(num_pages: usize) -> Self { Self { pages: vec![None; num_pages], } } - pub fn get(&self, index: usize) -> Option<&T> { - let page_idx = index / PAGE_SIZE; - self.pages[page_idx] - .as_ref() - .map(|page| &page[index % PAGE_SIZE]) - } - - pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { - let page_idx = index / PAGE_SIZE; - self.pages[page_idx] - .as_mut() - .map(|page| &mut page[index % PAGE_SIZE]) - } - - pub fn set(&mut self, index: usize, value: T) -> Option { - let page_idx = index / PAGE_SIZE; - if let Some(page) = self.pages[page_idx].as_mut() { - Some(std::mem::replace(&mut page[index % PAGE_SIZE], value)) - } else { - let page = self.pages[page_idx].get_or_insert_with(|| vec![T::default(); PAGE_SIZE]); - page[index % PAGE_SIZE] = value; - None - } - } - - #[inline(always)] - pub fn range_vec(&self, range: Range) -> Vec { - let len = range.end - range.start; - // Create a vector for uninitialized values. - let mut result: Vec> = Vec::with_capacity(len); - // SAFETY: We set the length and then initialize every element via read_range_generic. - unsafe { - result.set_len(len); - self.read_range_generic(range.start, len, result.as_mut_ptr() as *mut T); - std::mem::transmute::>, Vec>(result) - } - } - - pub fn set_range(&mut self, range: Range, values: &[T]) -> Vec { - let len = range.end - range.start; - assert_eq!(values.len(), len); - let mut result: Vec> = Vec::with_capacity(len); - // SAFETY: We will write to every element in result via set_range_generic. - unsafe { - result.set_len(len); - self.set_range_generic( - range.start, - len, - values.as_ptr(), - result.as_mut_ptr() as *mut T, - ); - std::mem::transmute::>, Vec>(result) - } - } - pub fn memory_size(&self) -> usize { self.pages.len() * PAGE_SIZE } @@ -157,49 +106,65 @@ impl PagedVec { pub fn is_empty(&self) -> bool { self.pages.iter().all(|page| page.is_none()) } -} -// ------------------------------------------------------------------ -// Implementation for types requiring Default + Copy -impl PagedVec { + /// # Panics + /// If `from..from + size_of()` is out of bounds. #[inline(always)] - pub fn range_array(&self, from: usize) -> [T; N] { - // Create an uninitialized array of MaybeUninit - let mut result: [MaybeUninit; N] = unsafe { - // SAFETY: An uninitialized `[MaybeUninit; N]` is valid. - MaybeUninit::uninit().assume_init() - }; - self.read_range_generic(from, N, result.as_mut_ptr() as *mut T); - // SAFETY: All elements have been initialized. - unsafe { ptr::read(&result as *const _ as *const [T; N]) } - } - + pub fn get(&self, from: usize) -> BLOCK { + // Create an uninitialized array of MaybeUninit + let mut result: MaybeUninit = MaybeUninit::uninit(); + self.read_range_generic(from, size_of::(), result.as_mut_ptr() as *mut u8); + // SAFETY: + // - All elements have been initialized (zero-initialized if page didn't exist). + // - `result` is aligned to `BLOCK` + unsafe { result.assume_init() } + } + + /// memcpy of new `values` into pages, memcpy of old existing values into new returned value. + /// # Panics + /// If `from..from + size_of()` is out of bounds. #[inline(always)] - pub fn set_range_array(&mut self, from: usize, values: &[T; N]) -> [T; N] { + pub fn set(&mut self, from: usize, values: &BLOCK) -> BLOCK { // Create an uninitialized array for old values. - let mut result: [MaybeUninit; N] = unsafe { MaybeUninit::uninit().assume_init() }; - self.set_range_generic(from, N, values.as_ptr(), result.as_mut_ptr() as *mut T); - unsafe { ptr::read(&result as *const _ as *const [T; N]) } + let mut result: MaybeUninit = MaybeUninit::uninit(); + self.set_range_generic( + from, + size_of::(), + values as *const _ as *const u8, + result.as_mut_ptr() as *mut u8, + ); + // SAFETY: + // - All elements have been initialized (zero-initialized if page didn't exist). + // - `result` is aligned to `BLOCK` + unsafe { result.assume_init() } } } -impl PagedVec { - pub fn iter(&self) -> PagedVecIter<'_, T, PAGE_SIZE> { +impl PagedVec { + /// Iterate over [PagedVec] as iterator of elements of type `T`. + /// Iterator is over `(index, element)` where `index` is the byte index divided by + /// `size_of::()`. + /// + /// `T` must be stack allocated + pub fn iter(&self) -> PagedVecIter<'_, T, PAGE_SIZE> { + assert!(size_of::() <= PAGE_SIZE); PagedVecIter { vec: self, current_page: 0, current_index_in_page: 0, + phantom: PhantomData, } } } pub struct PagedVecIter<'a, T, const PAGE_SIZE: usize> { - vec: &'a PagedVec, + vec: &'a PagedVec, current_page: usize, current_index_in_page: usize, + phantom: PhantomData, } -impl Iterator for PagedVecIter<'_, T, PAGE_SIZE> { +impl Iterator for PagedVecIter<'_, T, PAGE_SIZE> { type Item = (usize, T); fn next(&mut self) -> Option { @@ -210,39 +175,50 @@ impl Iterator for PagedVecIter<'_, T, PAGE_SIZ debug_assert_eq!(self.current_index_in_page, 0); self.current_index_in_page = 0; } - if self.current_page >= self.vec.pages.len() { + let global_index = self.current_page * PAGE_SIZE + self.current_index_in_page; + if global_index + size_of::() > self.vec.memory_size() { return None; } - let global_index = self.current_page * PAGE_SIZE + self.current_index_in_page; - let page = self.vec.pages[self.current_page].as_ref()?; - let value = page[self.current_index_in_page].clone(); + // PERF: this can be optimized + let value = self.vec.get(global_index); - self.current_index_in_page += 1; - if self.current_index_in_page == PAGE_SIZE { + self.current_index_in_page += size_of::(); + if self.current_index_in_page >= PAGE_SIZE { self.current_page += 1; - self.current_index_in_page = 0; + self.current_index_in_page -= PAGE_SIZE; } - Some((global_index, value)) + Some((global_index / size_of::(), value)) } } +/// Map from address space to guest memory. +/// The underlying memory is typeless, stored as raw bytes, but usage +/// implicitly assumes that each address space has memory cells of a fixed type (e.g., `u8, F`). +/// We do not use a typemap for performance reasons, and it is up to the user to enforce types. +/// Needless to say, this is a very `unsafe` API. #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct AddressMap { - pub paged_vecs: Vec>, +pub struct AddressMap { + pub paged_vecs: Vec>, + /// byte size of cells per address space + pub cell_size: Vec, pub as_offset: u32, } -impl Default for AddressMap { +impl Default for AddressMap { fn default() -> Self { Self::from_mem_config(&MemoryConfig::default()) } } -impl AddressMap { +impl AddressMap { pub fn new(as_offset: u32, as_cnt: usize, mem_size: usize) -> Self { + // TMP: hardcoding for now + let mut cell_size = vec![1, 1]; + cell_size.resize(as_cnt, 4); Self { paged_vecs: vec![PagedVec::new(mem_size.div_ceil(PAGE_SIZE)); as_cnt], + cell_size, as_offset, } } @@ -253,49 +229,109 @@ impl AddressMap { 1 << mem_config.pointer_max_bits, ) } - pub fn items(&self) -> impl Iterator + '_ { - self.paged_vecs - .iter() + pub fn items(&self) -> impl Iterator + '_ { + zip_eq(&self.paged_vecs, &self.cell_size) .enumerate() - .flat_map(move |(as_idx, page)| { - page.iter() - .map(move |(ptr_idx, x)| ((as_idx as u32 + self.as_offset, ptr_idx as u32), x)) + .flat_map(move |(as_idx, (page, &cell_size))| { + // TODO: better way to handle address space conversions to F + if cell_size == 1 { + page.iter::() + .map(move |(ptr_idx, x)| { + ( + (as_idx as u32 + self.as_offset, ptr_idx as u32), + F::from_canonical_u8(x), + ) + }) + .collect_vec() + } else { + // TEMP + assert_eq!(cell_size, 4); + page.iter::() + .map(move |(ptr_idx, x)| { + ((as_idx as u32 + self.as_offset, ptr_idx as u32), x) + }) + .collect_vec() + } }) } - pub fn get(&self, address: &Address) -> Option<&T> { - self.paged_vecs[(address.0 - self.as_offset) as usize].get(address.1 as usize) - } - pub fn get_mut(&mut self, address: &Address) -> Option<&mut T> { - self.paged_vecs[(address.0 - self.as_offset) as usize].get_mut(address.1 as usize) - } - pub fn insert(&mut self, address: &Address, data: T) -> Option { - self.paged_vecs[(address.0 - self.as_offset) as usize].set(address.1 as usize, data) + + /// # Safety + /// - `T` **must** be the correct type for a single memory cell for `addr_space` + /// - Assumes `addr_space` is within the configured memory and not out of bounds + pub unsafe fn get(&self, (addr_space, ptr): Address) -> T { + debug_assert_eq!( + size_of::(), + self.cell_size[(addr_space - self.as_offset) as usize] + ); + self.paged_vecs + .get_unchecked((addr_space - self.as_offset) as usize) + .get((ptr as usize) * size_of::()) + } + + /// # Safety + /// - `T` **must** be the correct type for a single memory cell for `addr_space` + /// - Assumes `addr_space` is within the configured memory and not out of bounds + pub unsafe fn insert(&mut self, (addr_space, ptr): Address, data: T) -> T { + debug_assert_eq!( + size_of::(), + self.cell_size[(addr_space - self.as_offset) as usize] + ); + self.paged_vecs + .get_unchecked_mut((addr_space - self.as_offset) as usize) + .set((ptr as usize) * size_of::(), &data) } pub fn is_empty(&self) -> bool { self.paged_vecs.iter().all(|page| page.is_empty()) } - pub fn from_iter( + // TODO[jpw]: stabilize the boundary memory image format and how to construct + /// # Safety + /// - `T` **must** be the correct type for a single memory cell for `addr_space` + /// - Assumes `addr_space` is within the configured memory and not out of bounds + pub fn from_sparse( as_offset: u32, as_cnt: usize, mem_size: usize, - iter: impl IntoIterator, + sparse_map: SparseMemoryImage, ) -> Self { let mut vec = Self::new(as_offset, as_cnt, mem_size); - for (address, data) in iter { - vec.insert(&address, data); + for ((addr_space, index), data_byte) in sparse_map.into_iter() { + vec.paged_vecs[(addr_space - as_offset) as usize].set(index as usize, &data_byte); } vec } } -impl AddressMap { - pub fn get_range(&self, address: &Address) -> [T; N] { - self.paged_vecs[(address.0 - self.as_offset) as usize].range_array(address.1 as usize) - } - pub fn set_range(&mut self, address: &Address, values: &[T; N]) -> [T; N] { - self.paged_vecs[(address.0 - self.as_offset) as usize] - .set_range_array(address.1 as usize, values) +impl AddressMap { + /// # Safety + /// - `T` **must** be the correct type for a single memory cell for `addr_space` + /// - Assumes `addr_space` is within the configured memory and not out of bounds + pub unsafe fn get_range(&self, (addr_space, ptr): Address) -> [T; N] { + debug_assert_eq!( + size_of::(), + self.cell_size[(addr_space - self.as_offset) as usize] + ); + self.paged_vecs + .get_unchecked((addr_space - self.as_offset) as usize) + .get((ptr as usize) * size_of::()) + } + + /// # Safety + /// - `T` **must** be the correct type for a single memory cell for `addr_space` + /// - Assumes `addr_space` is within the configured memory and not out of bounds + pub unsafe fn set_range( + &mut self, + (addr_space, ptr): Address, + values: &[T; N], + ) -> [T; N] { + debug_assert_eq!( + size_of::(), + self.cell_size[(addr_space - self.as_offset) as usize], + "addr_space={addr_space}" + ); + self.paged_vecs + .get_unchecked_mut((addr_space - self.as_offset) as usize) + .set((ptr as usize) * size_of::(), values) } } @@ -305,143 +341,144 @@ mod tests { #[test] fn test_basic_get_set() { - let mut v = PagedVec::<_, 4>::new(3); - assert_eq!(v.get(0), None); - v.set(0, 42); - assert_eq!(v.get(0), Some(&42)); - } - - #[test] - fn test_cross_page_operations() { - let mut v = PagedVec::<_, 4>::new(3); - v.set(3, 10); // Last element of first page - v.set(4, 20); // First element of second page - assert_eq!(v.get(3), Some(&10)); - assert_eq!(v.get(4), Some(&20)); - } - - #[test] - fn test_page_boundaries() { - let mut v = PagedVec::<_, 4>::new(2); - // Fill first page - v.set(0, 1); - v.set(1, 2); - v.set(2, 3); - v.set(3, 4); - // Fill second page - v.set(4, 5); - v.set(5, 6); - v.set(6, 7); - v.set(7, 8); - - // Verify all values - assert_eq!(v.range_vec(0..8), [1, 2, 3, 4, 5, 6, 7, 8]); - } - - #[test] - fn test_range_cross_page_boundary() { - let mut v = PagedVec::<_, 4>::new(2); - v.set_range(2..8, &[10, 11, 12, 13, 14, 15]); - assert_eq!(v.range_vec(2..8), [10, 11, 12, 13, 14, 15]); - } - - #[test] - fn test_large_indices() { - let mut v = PagedVec::<_, 4>::new(100); - let large_index = 399; - v.set(large_index, 42); - assert_eq!(v.get(large_index), Some(&42)); - } - - #[test] - fn test_range_operations_with_defaults() { - let mut v = PagedVec::<_, 4>::new(3); - v.set(2, 5); - v.set(5, 10); - - // Should include both set values and defaults - assert_eq!(v.range_vec(1..7), [0, 5, 0, 0, 10, 0]); - } - - #[test] - fn test_non_zero_default_type() { - let mut v: PagedVec = PagedVec::new(2); - assert_eq!(v.get(0), None); // bool's default - v.set(0, true); - assert_eq!(v.get(0), Some(&true)); - assert_eq!(v.get(1), Some(&false)); // because we created the page - } - - #[test] - fn test_set_range_overlapping_pages() { - let mut v = PagedVec::<_, 4>::new(3); - let test_data = [1, 2, 3, 4, 5, 6]; - v.set_range(2..8, &test_data); - - // Verify first page - assert_eq!(v.get(2), Some(&1)); - assert_eq!(v.get(3), Some(&2)); - - // Verify second page - assert_eq!(v.get(4), Some(&3)); - assert_eq!(v.get(5), Some(&4)); - assert_eq!(v.get(6), Some(&5)); - assert_eq!(v.get(7), Some(&6)); - } - - #[test] - fn test_overlapping_set_ranges() { - let mut v = PagedVec::<_, 4>::new(3); - - // Initial set_range - v.set_range(0..5, &[1, 2, 3, 4, 5]); - assert_eq!(v.range_vec(0..5), [1, 2, 3, 4, 5]); - - // Overlap from beginning - v.set_range(0..3, &[10, 20, 30]); - assert_eq!(v.range_vec(0..5), [10, 20, 30, 4, 5]); - - // Overlap in middle - v.set_range(2..4, &[42, 43]); - assert_eq!(v.range_vec(0..5), [10, 20, 42, 43, 5]); - - // Overlap at end - v.set_range(4..6, &[91, 92]); - assert_eq!(v.range_vec(0..6), [10, 20, 42, 43, 91, 92]); - } - - #[test] - fn test_overlapping_set_ranges_cross_pages() { - let mut v = PagedVec::<_, 4>::new(3); - - // Fill across first two pages - v.set_range(0..8, &[1, 2, 3, 4, 5, 6, 7, 8]); - - // Overlap end of first page and start of second - v.set_range(2..6, &[21, 22, 23, 24]); - assert_eq!(v.range_vec(0..8), [1, 2, 21, 22, 23, 24, 7, 8]); - - // Overlap multiple pages - v.set_range(1..7, &[31, 32, 33, 34, 35, 36]); - assert_eq!(v.range_vec(0..8), [1, 31, 32, 33, 34, 35, 36, 8]); - } - - #[test] - fn test_iterator() { - let mut v = PagedVec::<_, 4>::new(3); - - v.set_range(4..10, &[1, 2, 3, 4, 5, 6]); - let contents: Vec<_> = v.iter().collect(); - assert_eq!(contents.len(), 8); // two pages - - contents - .iter() - .take(6) - .enumerate() - .for_each(|(i, &(idx, val))| { - assert_eq!((idx, val), (4 + i, 1 + i)); - }); - assert_eq!(contents[6], (10, 0)); - assert_eq!(contents[7], (11, 0)); - } + let mut v = PagedVec::<16>::new(3); + assert_eq!(v.get::(0), 0u32); + v.set(0, &42u32); + assert_eq!(v.get::(0), 42u32); + } + + // TEMP: disable tests (need to update indexing * size_of) + // #[test] + // fn test_cross_page_operations() { + // let mut v = PagedVec::<16>::new(3); + // v.set(3, 10u32); // Last element of first page + // v.set(4, 20u32); // First element of second page + // assert_eq!(v.get(3), 10u32); + // assert_eq!(v.get(4), 20u32); + // } + + // #[test] + // fn test_page_boundaries() { + // let mut v = PagedVec::<16>::new(2); + // // Fill first page + // v.set(0, 1u32); + // v.set(1, 2u32); + // v.set(2, 3u32); + // v.set(3, 4u32); + // // Fill second page + // v.set(4, 5u32); + // v.set(5, 6u32); + // v.set(6, 7u32); + // v.set(7, 8u32); + + // // Verify all values + // assert_eq!(v.get::<[u32; 8]>(0), [1, 2, 3, 4, 5, 6, 7, 8]); + // } + + // #[test] + // fn test_range_cross_page_boundary() { + // let mut v = PagedVec::<16>::new(2); + // v.set::<[u32; 6]>(2, [10, 11, 12, 13, 14, 15]); + // assert_eq!(v.get::<[u32; 6]>(2), [10, 11, 12, 13, 14, 15]); + // } + + // #[test] + // fn test_large_indices() { + // let mut v = PagedVec::<16>::new(100); + // let large_index = 399; + // v.set(large_index, 42u32); + // assert_eq!(v.get(large_index), 42u32); + // } + + // #[test] + // fn test_range_operations_with_defaults() { + // let mut v = PagedVec::<16>::new(3); + // v.set(2, 5u32); + // v.set(5, 10u32); + + // // Should include both set values and defaults + // assert_eq!(v.range_vec(1..7), [0, 5, 0, 0, 10, 0]); + // } + + // #[test] + // fn test_non_zero_default_type() { + // let mut v: PagedVec<4> = PagedVec::new(2); + // assert_eq!(v.get(0), false); // bool's default + // v.set(0, true); + // assert_eq!(v.get(0), true); + // assert_eq!(v.get(1), false); // because we created the page + // } + + // #[test] + // fn test_set_range_overlapping_pages() { + // let mut v = PagedVec::<_, 16>::new(3); + // let test_data = [1u32, 2, 3, 4, 5, 6]; + // v.set(2, test_data); + + // // Verify first page + // assert_eq!(v.get(2), 1u32); + // assert_eq!(v.get(3), 2u32); + + // // Verify second page + // assert_eq!(v.get(4), 3u32); + // assert_eq!(v.get(5), 4u32); + // assert_eq!(v.get(6), 5u32); + // assert_eq!(v.get(7), 6u32); + // } + + // #[test] + // fn test_overlapping_set_ranges() { + // let mut v = PagedVec::<_, 16>::new(3); + + // // Initial set_range + // v.set(0, [1u32, 2, 3, 4, 5]); + // assert_eq!(v.get::<[u32; 5]>(0), [1, 2, 3, 4, 5]); + + // // Overlap from beginning + // v.set(0, [10u32, 20, 30]); + // assert_eq!(v.get::<[u32; 5]>(0), [10, 20, 30, 4, 5]); + + // // Overlap in middle + // v.set(2, [42u32, 43]); + // assert_eq!(v.get::<[u32; 5]>(0), [10, 20, 42, 43, 5]); + + // // Overlap at end + // v.set(4, [91u32, 92]); + // assert_eq!(v.get::<[u32; 6]>(0), [10, 20, 42, 43, 91, 92]); + // } + + // #[test] + // fn test_overlapping_set_ranges_cross_pages() { + // let mut v = PagedVec::<16>::new(3); + + // // Fill across first two pages + // v.set::<[u32; 8]>(0, [1, 2, 3, 4, 5, 6, 7, 8]); + + // // Overlap end of first page and start of second + // v.set::<[u32; 4]>(2, [21, 22, 23, 24]); + // assert_eq!(v.get::<[u32; 8]>(0), [1, 2, 21, 22, 23, 24, 7, 8]); + + // // Overlap multiple pages + // v.set::<[u32; 6]>(1, [31, 32, 33, 34, 35, 36]); + // assert_eq!(v.get::<[u32; 8]>(0), [1, 31, 32, 33, 34, 35, 36, 8]); + // } + + // #[test] + // fn test_iterator() { + // let mut v = PagedVec::<16>::new(3); + + // v.set(4..10, &[1, 2, 3, 4, 5, 6]); + // let contents: Vec<_> = v.iter().collect(); + // assert_eq!(contents.len(), 8); // two pages + + // contents + // .iter() + // .take(6) + // .enumerate() + // .for_each(|(i, &(idx, val))| { + // assert_eq!((idx, val), (4 + i, 1 + i)); + // }); + // assert_eq!(contents[6], (10, 0)); + // assert_eq!(contents[7], (11, 0)); + // } } diff --git a/crates/vm/src/system/memory/persistent.rs b/crates/vm/src/system/memory/persistent.rs index 55a178be4d..c2ce24a98a 100644 --- a/crates/vm/src/system/memory/persistent.rs +++ b/crates/vm/src/system/memory/persistent.rs @@ -200,7 +200,7 @@ impl PersistentBoundaryChip { pub fn finalize( &mut self, - initial_memory: &MemoryImage, + initial_memory: &MemoryImage, final_memory: &TimestampedEquipartition, hasher: &mut H, ) where @@ -212,10 +212,8 @@ impl PersistentBoundaryChip { .par_iter() .map(|&(address_space, label)| { let pointer = label * CHUNK as u32; - let init_values = array::from_fn(|i| { - *initial_memory - .get(&(address_space, pointer + i as u32)) - .unwrap_or(&F::ZERO) + let init_values = array::from_fn(|i| unsafe { + initial_memory.get((address_space, pointer + i as u32)) }); let initial_hash = hasher.hash(&init_values); let timestamped_values = final_memory.get(&(address_space, label)).unwrap(); diff --git a/crates/vm/src/system/memory/tests.rs b/crates/vm/src/system/memory/tests.rs index 9ebb9306aa..b950e7862e 100644 --- a/crates/vm/src/system/memory/tests.rs +++ b/crates/vm/src/system/memory/tests.rs @@ -292,34 +292,35 @@ fn make_random_accesses( ) -> Vec { (0..1024) .map(|_| { - let address_space = F::from_canonical_u32(*[1, 2].choose(&mut rng).unwrap()); + let address_space = F::from_canonical_u32(*[4, 5].choose(&mut rng).unwrap()); match rng.gen_range(0..5) { 0 => { let pointer = F::from_canonical_usize(gen_pointer(rng, 1)); let data = F::from_canonical_u32(rng.gen_range(0..1 << 30)); - let (record_id, _) = memory_controller.write(address_space, pointer, [data]); + let (record_id, _) = memory_controller.write(address_space, pointer, &[data]); record_id } 1 => { let pointer = F::from_canonical_usize(gen_pointer(rng, 1)); - let (record_id, _) = memory_controller.read::<1>(address_space, pointer); + let (record_id, _) = memory_controller.read::(address_space, pointer); record_id } 2 => { let pointer = F::from_canonical_usize(gen_pointer(rng, 4)); - let (record_id, _) = memory_controller.read::<4>(address_space, pointer); + let (record_id, _) = memory_controller.read::(address_space, pointer); record_id } 3 => { let pointer = F::from_canonical_usize(gen_pointer(rng, 4)); let data = array::from_fn(|_| F::from_canonical_u32(rng.gen_range(0..1 << 30))); - let (record_id, _) = memory_controller.write::<4>(address_space, pointer, data); + let (record_id, _) = + memory_controller.write::(address_space, pointer, &data); record_id } 4 => { let pointer = F::from_canonical_usize(gen_pointer(rng, MAX)); - let (record_id, _) = memory_controller.read::(address_space, pointer); + let (record_id, _) = memory_controller.read::(address_space, pointer); record_id } _ => unreachable!(), diff --git a/crates/vm/src/system/memory/tree/mod.rs b/crates/vm/src/system/memory/tree/mod.rs index fcdb86d8ee..cb32429d5a 100644 --- a/crates/vm/src/system/memory/tree/mod.rs +++ b/crates/vm/src/system/memory/tree/mod.rs @@ -142,7 +142,7 @@ impl MemoryNode { pub fn tree_from_memory( memory_dimensions: MemoryDimensions, - memory: &MemoryImage, + memory: &MemoryImage, hasher: &(impl Hasher + Sync), ) -> MemoryNode { // Construct a Vec that includes the address space in the label calculation, diff --git a/crates/vm/src/system/memory/tree/public_values.rs b/crates/vm/src/system/memory/tree/public_values.rs index 1c6866b959..d6914d4720 100644 --- a/crates/vm/src/system/memory/tree/public_values.rs +++ b/crates/vm/src/system/memory/tree/public_values.rs @@ -51,7 +51,7 @@ impl UserPublicValuesProof { memory_dimensions: MemoryDimensions, num_public_values: usize, hasher: &(impl Hasher + Sync), - final_memory: &MemoryImage, + final_memory: &MemoryImage, ) -> Self { let proof = compute_merkle_proof_to_user_public_values_root( memory_dimensions, @@ -121,7 +121,7 @@ fn compute_merkle_proof_to_user_public_values_root + Sync), - final_memory: &MemoryImage, + final_memory: &MemoryImage, ) -> Vec<[F; CHUNK]> { assert_eq!( num_public_values % CHUNK, @@ -169,7 +169,7 @@ fn compute_merkle_proof_to_user_public_values_root( memory_dimensions: &MemoryDimensions, num_public_values: usize, - final_memory: &MemoryImage, + final_memory: &MemoryImage, ) -> Vec { // All (addr, value) pairs in the public value address space. let f_as_start = PUBLIC_VALUES_ADDRESS_SPACE_OFFSET + memory_dimensions.as_offset; @@ -218,12 +218,14 @@ mod tests { let memory_dimensions = vm_config.memory_config.memory_dimensions(); let pv_as = PUBLIC_VALUES_ADDRESS_SPACE_OFFSET + memory_dimensions.as_offset; let num_public_values = 16; - let memory = AddressMap::from_iter( + let mut memory = AddressMap::new( memory_dimensions.as_offset, 1 << memory_dimensions.as_height, 1 << memory_dimensions.address_height, - [((pv_as, 15), F::ONE)], ); + unsafe { + memory.set_range::((pv_as, 15), &[F::ONE]); + } let mut expected_pvs = F::zero_vec(num_public_values); expected_pvs[15] = F::ONE; diff --git a/crates/vm/src/system/native_adapter/mod.rs b/crates/vm/src/system/native_adapter/mod.rs index 95c2c7c4a4..8d1d90df52 100644 --- a/crates/vm/src/system/native_adapter/mod.rs +++ b/crates/vm/src/system/native_adapter/mod.rs @@ -226,10 +226,10 @@ impl VmAdapterChip let mut reads = Vec::with_capacity(R); if R >= 1 { - reads.push(memory.read::<1>(e, b)); + reads.push(memory.read::(e, b)); } if R >= 2 { - reads.push(memory.read::<1>(f, c)); + reads.push(memory.read::(f, c)); } let i_reads: [_; R] = std::array::from_fn(|i| reads[i].1); @@ -253,7 +253,7 @@ impl VmAdapterChip let Instruction { a, d, .. } = *instruction; let mut writes = Vec::with_capacity(W); if W >= 1 { - let (record_id, _) = memory.write(d, a, output.writes[0]); + let (record_id, _) = memory.write(d, a, &output.writes[0]); writes.push((record_id, output.writes[0])); } diff --git a/crates/vm/src/system/program/trace.rs b/crates/vm/src/system/program/trace.rs index d9e2abd956..bf050b74bb 100644 --- a/crates/vm/src/system/program/trace.rs +++ b/crates/vm/src/system/program/trace.rs @@ -82,17 +82,14 @@ where let memory_dimensions = memory_config.memory_dimensions(); let app_program_commit: &[Val; CHUNK] = self.committed_program.commitment.as_ref(); let mem_config = memory_config; - let init_memory_commit = MemoryNode::tree_from_memory( - memory_dimensions, - &AddressMap::from_iter( - mem_config.as_offset, - 1 << mem_config.as_height, - 1 << mem_config.pointer_max_bits, - self.exe.init_memory.clone(), - ), - &hasher, - ) - .hash(); + let memory_image = AddressMap::from_sparse( + mem_config.as_offset, + 1 << mem_config.as_height, + 1 << mem_config.pointer_max_bits, + self.exe.init_memory.clone(), + ); + let init_memory_commit = + MemoryNode::tree_from_memory(memory_dimensions, &memory_image, &hasher).hash(); Com::::from(compute_exe_commit( &hasher, app_program_commit, diff --git a/crates/vm/src/utils/stark_utils.rs b/crates/vm/src/utils/stark_utils.rs index d940be5c75..d8aea2123d 100644 --- a/crates/vm/src/utils/stark_utils.rs +++ b/crates/vm/src/utils/stark_utils.rs @@ -16,9 +16,12 @@ use openvm_stark_sdk::{ utils::ProofInputForTest, }; -use crate::arch::{ - vm::{VirtualMachine, VmExecutor}, - Streams, VmConfig, VmMemoryState, +use crate::{ + arch::{ + vm::{VirtualMachine, VmExecutor}, + Streams, VmConfig, + }, + system::memory::MemoryImage, }; pub fn air_test(config: VC, exe: impl Into>) @@ -36,7 +39,7 @@ pub fn air_test_with_min_segments( exe: impl Into>, input: impl Into>, min_segments: usize, -) -> Option> +) -> Option where VC: VmConfig, VC::Executor: Chip, @@ -53,7 +56,7 @@ pub fn air_test_impl( input: impl Into>, min_segments: usize, debug: bool, -) -> Option> +) -> Option where VC: VmConfig, VC::Executor: Chip, diff --git a/crates/vm/tests/integration_test.rs b/crates/vm/tests/integration_test.rs index 168d756111..1f96b0b8d8 100644 --- a/crates/vm/tests/integration_test.rs +++ b/crates/vm/tests/integration_test.rs @@ -1,6 +1,7 @@ use std::{ collections::{BTreeMap, VecDeque}, iter::zip, + mem::transmute, sync::Arc, }; @@ -316,9 +317,8 @@ fn test_vm_initial_memory() { Instruction::::from_isize(TERMINATE.global_opcode(), 0, 0, 0, 0, 0), ]); - let init_memory: BTreeMap<_, _> = [((4, 7), BabyBear::from_canonical_u32(101))] - .into_iter() - .collect(); + let raw = unsafe { transmute::(BabyBear::from_canonical_u32(101)) }; + let init_memory = BTreeMap::from_iter((0..4).map(|i| ((4u32, 7u32 * 4 + i), raw[i as usize]))); let config = test_native_continuations_config(); let exe = VmExe { @@ -763,7 +763,7 @@ fn test_hint_load_2() { segment .chip_complex .memory_controller() - .unsafe_read_cell(F::from_canonical_usize(4), F::from_canonical_usize(32)), + .unsafe_read_cell::(F::from_canonical_usize(4), F::from_canonical_usize(32)), F::ZERO ); let streams = segment.chip_complex.take_streams(); diff --git a/extensions/ecc/circuit/src/weierstrass_extension.rs b/extensions/ecc/circuit/src/weierstrass_extension.rs index c5b23ccd0d..666706cd29 100644 --- a/extensions/ecc/circuit/src/weierstrass_extension.rs +++ b/extensions/ecc/circuit/src/weierstrass_extension.rs @@ -285,19 +285,19 @@ pub(crate) mod phantom { }; let mut x_limbs: Vec = Vec::with_capacity(num_limbs); for i in 0..num_limbs { - let limb = memory.unsafe_read_cell( + let limb = memory.unsafe_read_cell::( F::from_canonical_u32(RV32_MEMORY_AS), F::from_canonical_u32(rs1 + i as u32), ); - x_limbs.push(limb.as_canonical_u32() as u8); + x_limbs.push(limb); } let x = BigUint::from_bytes_le(&x_limbs); let rs2 = unsafe_read_rv32_register(memory, b); - let rec_id = memory.unsafe_read_cell( + let rec_id = memory.unsafe_read_cell::( F::from_canonical_u32(RV32_MEMORY_AS), F::from_canonical_u32(rs2), ); - let hint = self.decompress_point(x, rec_id.as_canonical_u32() & 1 == 1, c_idx); + let hint = self.decompress_point(x, rec_id & 1 == 1, c_idx); let hint_bytes = once(F::from_bool(hint.possible)) .chain(repeat(F::ZERO)) .take(4) diff --git a/extensions/keccak256/circuit/src/lib.rs b/extensions/keccak256/circuit/src/lib.rs index c9fd1c9f5a..0284322c4d 100644 --- a/extensions/keccak256/circuit/src/lib.rs +++ b/extensions/keccak256/circuit/src/lib.rs @@ -178,14 +178,10 @@ impl InstructionExecutor for KeccakVmChip { let mut bytes = [0u8; KECCAK_RATE_BYTES]; for i in (0..KECCAK_RATE_BYTES).step_by(KECCAK_WORD_SIZE) { if i < remaining_len { - let read = - memory.read::(e, F::from_canonical_usize(src + i)); + let read = memory + .read::(e, F::from_canonical_usize(src + i)); - let chunk = read.1.map(|x| { - x.as_canonical_u32() - .try_into() - .expect("Memory cell not a byte") - }); + let chunk = read.1; let copy_len = min(KECCAK_WORD_SIZE, remaining_len - i); if copy_len != KECCAK_WORD_SIZE { partial_read_idx = Some(reads.len()); @@ -230,10 +226,10 @@ impl InstructionExecutor for KeccakVmChip { let digest_writes: [_; KECCAK_DIGEST_WRITES] = from_fn(|i| { timestamp_delta += 1; memory - .write::( + .write::( e, F::from_canonical_usize(dst + i * KECCAK_WORD_SIZE), - from_fn(|j| F::from_canonical_u8(output[i * KECCAK_WORD_SIZE + j])), + &from_fn(|j| output[i * KECCAK_WORD_SIZE + j]), ) .0 }); diff --git a/extensions/native/circuit/src/adapters/alu_native_adapter.rs b/extensions/native/circuit/src/adapters/alu_native_adapter.rs index e85797536f..65340323e6 100644 --- a/extensions/native/circuit/src/adapters/alu_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/alu_native_adapter.rs @@ -160,7 +160,7 @@ impl VmAdapterChip for AluNativeAdapterChip { )> { let Instruction { b, c, e, f, .. } = *instruction; - let reads = vec![memory.read::<1>(e, b), memory.read::<1>(f, c)]; + let reads = vec![memory.read::(e, b), memory.read::(f, c)]; let i_reads: [_; 2] = std::array::from_fn(|i| reads[i].1); Ok(( @@ -183,7 +183,7 @@ impl VmAdapterChip for AluNativeAdapterChip { let writes = vec![memory.write( F::from_canonical_u32(AS::Native as u32), a, - output.writes[0], + &output.writes[0], )]; Ok(( diff --git a/extensions/native/circuit/src/adapters/branch_native_adapter.rs b/extensions/native/circuit/src/adapters/branch_native_adapter.rs index 7d3e97a6bf..ebc1152b8f 100644 --- a/extensions/native/circuit/src/adapters/branch_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/branch_native_adapter.rs @@ -161,7 +161,7 @@ impl VmAdapterChip for BranchNativeAdapterChip { )> { let Instruction { a, b, d, e, .. } = *instruction; - let reads = vec![memory.read::<1>(d, a), memory.read::<1>(e, b)]; + let reads = vec![memory.read::(d, a), memory.read::(e, b)]; let i_reads: [_; 2] = std::array::from_fn(|i| reads[i].1); Ok(( diff --git a/extensions/native/circuit/src/adapters/convert_adapter.rs b/extensions/native/circuit/src/adapters/convert_adapter.rs index cac6d91bac..2afb1c7b4b 100644 --- a/extensions/native/circuit/src/adapters/convert_adapter.rs +++ b/extensions/native/circuit/src/adapters/convert_adapter.rs @@ -173,7 +173,7 @@ impl VmAdapter )> { let Instruction { b, e, .. } = *instruction; - let y_val = memory.read::(e, b); + let y_val = memory.read::(e, b); Ok(([y_val.1], Self::ReadRecord { reads: [y_val.0] })) } @@ -187,7 +187,7 @@ impl VmAdapter _read_record: &Self::ReadRecord, ) -> Result<(ExecutionState, Self::WriteRecord)> { let Instruction { a, d, .. } = *instruction; - let (write_id, _) = memory.write::(d, a, output.writes[0]); + let (write_id, _) = memory.write::(d, a, &output.writes[0]); Ok(( ExecutionState { diff --git a/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs b/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs index 4bcf96d195..d2c6e24676 100644 --- a/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs @@ -260,7 +260,7 @@ impl VmAdapterChip let data_read = match local_opcode { HINT_STOREW => None, - LOADW | STOREW => Some(memory.read::(data_read_as, data_read_ptr)), + LOADW | STOREW => Some(memory.read::(data_read_as, data_read_ptr)), }; let record = NativeLoadStoreReadRecord { pointer_read: read_cell.0, @@ -288,8 +288,11 @@ impl VmAdapterChip output: AdapterRuntimeContext, read_record: &Self::ReadRecord, ) -> Result<(ExecutionState, Self::WriteRecord)> { - let (write_id, _) = - memory.write::(read_record.write_as, read_record.write_ptr, output.writes); + let (write_id, _) = memory.write::( + read_record.write_as, + read_record.write_ptr, + &output.writes, + ); Ok(( ExecutionState { pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), diff --git a/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs b/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs index c151197297..b04b17ce2b 100644 --- a/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs +++ b/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs @@ -172,8 +172,8 @@ impl VmAdapterChip for NativeVectorizedAdapt )> { let Instruction { b, c, d, e, .. } = *instruction; - let y_val = memory.read::(d, b); - let z_val = memory.read::(e, c); + let y_val = memory.read::(d, b); + let z_val = memory.read::(e, c); Ok(( [y_val.1, z_val.1], @@ -193,7 +193,7 @@ impl VmAdapterChip for NativeVectorizedAdapt _read_record: &Self::ReadRecord, ) -> Result<(ExecutionState, Self::WriteRecord)> { let Instruction { a, d, .. } = *instruction; - let (a_val, _) = memory.write(d, a, output.writes[0]); + let (a_val, _) = memory.write(d, a, &output.writes[0]); Ok(( ExecutionState { diff --git a/extensions/native/circuit/src/extension.rs b/extensions/native/circuit/src/extension.rs index 8f73423e40..d5ddb7f236 100644 --- a/extensions/native/circuit/src/extension.rs +++ b/extensions/native/circuit/src/extension.rs @@ -306,7 +306,7 @@ pub(crate) mod phantom { c_upper: u16, ) -> eyre::Result<()> { let addr_space = F::from_canonical_u16(c_upper); - let value = memory.unsafe_read_cell(addr_space, a); + let value = memory.unsafe_read_cell::(addr_space, a); println!("{}", value); Ok(()) } @@ -323,7 +323,7 @@ pub(crate) mod phantom { c_upper: u16, ) -> eyre::Result<()> { let addr_space = F::from_canonical_u16(c_upper); - let val = memory.unsafe_read_cell(addr_space, a); + let val = memory.unsafe_read_cell::(addr_space, a); let mut val = val.as_canonical_u32(); let len = b.as_canonical_u32(); diff --git a/extensions/native/circuit/src/fri/mod.rs b/extensions/native/circuit/src/fri/mod.rs index 7dbc3fd851..e54d5d506f 100644 --- a/extensions/native/circuit/src/fri/mod.rs +++ b/extensions/native/circuit/src/fri/mod.rs @@ -621,7 +621,7 @@ impl InstructionExecutor for FriReducedOpeningChip { let is_init_read = memory.read_cell(addr_space, is_init_ptr); let is_init = is_init_read.1.as_canonical_u32(); - let hint_id_f = memory.unsafe_read_cell(addr_space, hint_id_ptr); + let hint_id_f = memory.unsafe_read_cell::(addr_space, hint_id_ptr); let hint_id = hint_id_f.as_canonical_u32() as usize; let alpha = alpha_read.1; @@ -650,7 +650,7 @@ impl InstructionExecutor for FriReducedOpeningChip { memory.read_cell(addr_space, a_ptr + F::from_canonical_usize(i)) }; let b_read = - memory.read::(addr_space, b_ptr + F::from_canonical_usize(EXT_DEG * i)); + memory.read::(addr_space, b_ptr + F::from_canonical_usize(EXT_DEG * i)); a_rws.push(a_rw); b_reads.push(b_read); } @@ -665,7 +665,7 @@ impl InstructionExecutor for FriReducedOpeningChip { ); } - let (result_write, _) = memory.write(addr_space, result_ptr, result); + let (result_write, _) = memory.write(addr_space, result_ptr, &result); let record = FriReducedOpeningRecord { pc: F::from_canonical_u32(from_state.pc), diff --git a/extensions/native/circuit/src/jal/mod.rs b/extensions/native/circuit/src/jal/mod.rs index 28322834a2..31e342b0f3 100644 --- a/extensions/native/circuit/src/jal/mod.rs +++ b/extensions/native/circuit/src/jal/mod.rs @@ -204,7 +204,7 @@ impl InstructionExecutor for JalRangeCheckChip { let (record_id, _) = memory.write( F::from_canonical_u32(AS::Native as u32), instruction.a, - [F::from_canonical_u32(from_state.pc + DEFAULT_PC_STEP)], + &[F::from_canonical_u32(from_state.pc + DEFAULT_PC_STEP)], ); let b = instruction.b.as_canonical_u32(); self.records.push(JalRangeCheckRecord { @@ -221,8 +221,8 @@ impl InstructionExecutor for JalRangeCheckChip { } else if instruction.opcode == NativeRangeCheckOpcode::RANGE_CHECK.global_opcode() { let d = F::from_canonical_u32(AS::Native as u32); // This is a read, but we make the record have prev_data - let a_val = memory.unsafe_read_cell(d, instruction.a); - let (record_id, _) = memory.write(d, instruction.a, [a_val]); + let a_val = memory.unsafe_read_cell::(d, instruction.a); + let (record_id, _) = memory.write(d, instruction.a, &[a_val]); let a_val = a_val.as_canonical_u32(); let b = instruction.b.as_canonical_u32(); let c = instruction.c.as_canonical_u32(); diff --git a/extensions/native/circuit/src/poseidon2/chip.rs b/extensions/native/circuit/src/poseidon2/chip.rs index 426b089a9c..a25b553c0a 100644 --- a/extensions/native/circuit/src/poseidon2/chip.rs +++ b/extensions/native/circuit/src/poseidon2/chip.rs @@ -206,8 +206,10 @@ impl InstructionExecutor memory.read_cell(register_address_space, input_register_2); (Some(read_input_pointer_2), input_pointer_2) }; - let (read_data_1, data_1) = memory.read::(data_address_space, input_pointer_1); - let (read_data_2, data_2) = memory.read::(data_address_space, input_pointer_2); + let (read_data_1, data_1) = + memory.read::(data_address_space, input_pointer_1); + let (read_data_2, data_2) = + memory.read::(data_address_space, input_pointer_2); let p2_input = std::array::from_fn(|i| { if i < CHUNK { data_1[i] @@ -216,18 +218,18 @@ impl InstructionExecutor } }); let output = self.subchip.permute(p2_input); - let (write_data_1, _) = memory.write::( + let (write_data_1, _) = memory.write::( data_address_space, output_pointer, - std::array::from_fn(|i| output[i]), + &std::array::from_fn(|i| output[i]), ); let write_data_2 = if instruction.opcode == PERM_POS2.global_opcode() { Some( memory - .write::( + .write::( data_address_space, output_pointer + F::from_canonical_usize(CHUNK), - std::array::from_fn(|i| output[CHUNK + i]), + &std::array::from_fn(|i| output[CHUNK + i]), ) .0, ) @@ -277,7 +279,7 @@ impl InstructionExecutor opened_element_size += F::ONE; } - let proof_id = memory.unsafe_read_cell(address_space, proof_id_ptr); + let proof_id = memory.unsafe_read_cell::(address_space, proof_id_ptr); let (dim_base_pointer_read, dim_base_pointer) = memory.read_cell(address_space, dim_register); let (opened_base_pointer_read, opened_base_pointer) = @@ -288,12 +290,12 @@ impl InstructionExecutor memory.read_cell(address_space, index_register); let (commit_pointer_read, commit_pointer) = memory.read_cell(address_space, commit_register); - let (commit_read, commit) = memory.read(address_space, commit_pointer); + let (commit_read, commit) = memory.read::(address_space, commit_pointer); let opened_length = opened_length.as_canonical_u32() as usize; let initial_log_height = memory - .unsafe_read_cell(address_space, dim_base_pointer) + .unsafe_read_cell::(address_space, dim_base_pointer) .as_canonical_u32(); let mut log_height = initial_log_height as i32; let mut sibling_index = 0; @@ -312,7 +314,7 @@ impl InstructionExecutor while log_height >= 0 { let incorporate_row = if opened_index < opened_length - && memory.unsafe_read_cell( + && memory.unsafe_read_cell::( address_space, dim_base_pointer + F::from_canonical_usize(opened_index), ) == F::from_canonical_u32(log_height as u32) @@ -342,7 +344,7 @@ impl InstructionExecutor } else { opened_index += 1; if opened_index == opened_length - || memory.unsafe_read_cell( + || memory.unsafe_read_cell::( address_space, dim_base_pointer + F::from_canonical_usize(opened_index), @@ -351,7 +353,7 @@ impl InstructionExecutor break; } } - let (result, [new_row_pointer, row_len]) = memory.read( + let (result, [new_row_pointer, row_len]) = memory.read::( address_space, opened_base_pointer + F::from_canonical_usize(2 * opened_index), ); diff --git a/extensions/pairing/circuit/src/pairing_extension.rs b/extensions/pairing/circuit/src/pairing_extension.rs index eca4cea8dd..baf9f788b3 100644 --- a/extensions/pairing/circuit/src/pairing_extension.rs +++ b/extensions/pairing/circuit/src/pairing_extension.rs @@ -113,7 +113,7 @@ pub(crate) mod phantom { bn254::BN254_NUM_LIMBS, pairing::{FinalExp, MultiMillerLoop}, }; - use openvm_rv32im_circuit::adapters::{compose, unsafe_read_rv32_register}; + use openvm_rv32im_circuit::adapters::unsafe_read_rv32_register; use openvm_stark_backend::p3_field::PrimeField32; use super::PairingCurve; @@ -143,21 +143,21 @@ pub(crate) mod phantom { rs2: u32, c_upper: u16, ) -> eyre::Result<()> { - let p_ptr = compose(memory.unsafe_read( + let p_ptr = u32::from_le_bytes(memory.unsafe_read::( F::from_canonical_u32(RV32_MEMORY_AS), F::from_canonical_u32(rs1), )); // len in bytes - let p_len = compose(memory.unsafe_read( + let p_len = u32::from_le_bytes(memory.unsafe_read::( F::from_canonical_u32(RV32_MEMORY_AS), F::from_canonical_u32(rs1 + RV32_REGISTER_NUM_LIMBS as u32), )); - let q_ptr = compose(memory.unsafe_read( + let q_ptr = u32::from_le_bytes(memory.unsafe_read::( F::from_canonical_u32(RV32_MEMORY_AS), F::from_canonical_u32(rs2), )); // len in bytes - let q_len = compose(memory.unsafe_read( + let q_len = u32::from_le_bytes(memory.unsafe_read::( F::from_canonical_u32(RV32_MEMORY_AS), F::from_canonical_u32(rs2 + RV32_REGISTER_NUM_LIMBS as u32), )); @@ -263,13 +263,10 @@ pub(crate) mod phantom { { let mut repr = [0u8; N]; for (i, byte) in repr.iter_mut().enumerate() { - *byte = memory - .unsafe_read_cell( - F::from_canonical_u32(RV32_MEMORY_AS), - F::from_canonical_u32(ptr + i as u32), - ) - .as_canonical_u32() - .try_into()?; + *byte = memory.unsafe_read_cell::( + F::from_canonical_u32(RV32_MEMORY_AS), + F::from_canonical_u32(ptr + i as u32), + ); } Fp::from_repr(repr.into()) .into_option() diff --git a/extensions/rv32-adapters/src/eq_mod.rs b/extensions/rv32-adapters/src/eq_mod.rs index ab80481f19..3ed486bcf2 100644 --- a/extensions/rv32-adapters/src/eq_mod.rs +++ b/extensions/rv32-adapters/src/eq_mod.rs @@ -29,7 +29,7 @@ use openvm_instructions::{ riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}, }; use openvm_rv32im_circuit::adapters::{ - read_rv32_register, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + read_rv32_register, tmp_convert_to_u8s, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, }; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -339,15 +339,17 @@ impl< let read_records = rs_vals.map(|address| { debug_assert!(address < (1 << self.air.address_bits)); from_fn(|i| { - memory - .read::(e, F::from_canonical_u32(address + (i * BLOCK_SIZE) as u32)) + memory.read::( + e, + F::from_canonical_u32(address + (i * BLOCK_SIZE) as u32), + ) }) }); let read_data = read_records.map(|r| { let read = r.map(|x| x.1); let mut read_it = read.iter().flatten(); - from_fn(|_| *(read_it.next().unwrap())) + from_fn(|_| *(read_it.next().unwrap())).map(F::from_canonical_u8) }); let record = Rv32IsEqualModReadRecord { rs: rs_records, @@ -366,7 +368,7 @@ impl< _read_record: &Self::ReadRecord, ) -> Result<(ExecutionState, Self::WriteRecord)> { let Instruction { a, d, .. } = *instruction; - let (rd_id, _) = memory.write(d, a, output.writes[0]); + let (rd_id, _) = memory.write(d, a, &tmp_convert_to_u8s(output.writes[0])); debug_assert!( memory.timestamp() - from_state.timestamp diff --git a/extensions/rv32-adapters/src/heap.rs b/extensions/rv32-adapters/src/heap.rs index cd9f93abbc..8217f6833b 100644 --- a/extensions/rv32-adapters/src/heap.rs +++ b/extensions/rv32-adapters/src/heap.rs @@ -23,7 +23,7 @@ use openvm_instructions::{ program::DEFAULT_PC_STEP, riscv::{RV32_CELL_BITS, RV32_MEMORY_AS, RV32_REGISTER_AS, RV32_REGISTER_NUM_LIMBS}, }; -use openvm_rv32im_circuit::adapters::read_rv32_register; +use openvm_rv32im_circuit::adapters::{read_rv32_register, tmp_convert_to_u8s}; use openvm_stark_backend::{ interaction::InteractionBuilder, p3_air::BaseAir, @@ -173,9 +173,9 @@ impl(e, F::from_canonical_u32(address))] + [memory.read::(e, F::from_canonical_u32(address))] }); - let read_data = read_records.map(|r| r[0].1); + let read_data = read_records.map(|r| r[0].1.map(F::from_canonical_u8)); let record = Rv32VecHeapReadRecord { rs: rs_records, @@ -196,7 +196,9 @@ impl Result<(ExecutionState, Self::WriteRecord)> { let e = instruction.e; - let writes = [memory.write(e, read_record.rd_val, output.writes[0]).0]; + let writes = [memory + .write(e, read_record.rd_val, &tmp_convert_to_u8s(output.writes[0])) + .0]; let timestamp_delta = memory.timestamp() - from_state.timestamp; debug_assert!( diff --git a/extensions/rv32-adapters/src/heap_branch.rs b/extensions/rv32-adapters/src/heap_branch.rs index 29c9a151c9..fc4067e84a 100644 --- a/extensions/rv32-adapters/src/heap_branch.rs +++ b/extensions/rv32-adapters/src/heap_branch.rs @@ -244,14 +244,14 @@ impl VmAdapterC let heap_records = rs_vals.map(|address| { assert!(address as usize + READ_SIZE - 1 < (1 << self.air.address_bits)); - memory.read::(e, F::from_canonical_u32(address)) + memory.read::(e, F::from_canonical_u32(address)) }); let record = Rv32HeapBranchReadRecord { rs_reads: rs_records, heap_reads: heap_records.map(|r| r.0), }; - Ok((heap_records.map(|r| r.1), record)) + Ok((heap_records.map(|r| r.1.map(F::from_canonical_u8)), record)) } fn postprocess( diff --git a/extensions/rv32-adapters/src/vec_heap.rs b/extensions/rv32-adapters/src/vec_heap.rs index fab0df3334..0a2766e29a 100644 --- a/extensions/rv32-adapters/src/vec_heap.rs +++ b/extensions/rv32-adapters/src/vec_heap.rs @@ -29,7 +29,8 @@ use openvm_instructions::{ riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}, }; use openvm_rv32im_circuit::adapters::{ - abstract_compose, read_rv32_register, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + abstract_compose, read_rv32_register, tmp_convert_to_u8s, RV32_CELL_BITS, + RV32_REGISTER_NUM_LIMBS, }; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -405,10 +406,13 @@ impl< address as usize + READ_SIZE * BLOCKS_PER_READ - 1 < (1 << self.air.address_bits) ); from_fn(|i| { - memory.read::(e, F::from_canonical_u32(address + (i * READ_SIZE) as u32)) + memory.read::( + e, + F::from_canonical_u32(address + (i * READ_SIZE) as u32), + ) }) }); - let read_data = read_records.map(|r| r.map(|x| x.1)); + let read_data = read_records.map(|r| r.map(|x| x.1.map(F::from_canonical_u8))); assert!(rd_val as usize + WRITE_SIZE * BLOCKS_PER_WRITE - 1 < (1 << self.air.address_bits)); let record = Rv32VecHeapReadRecord { @@ -435,7 +439,7 @@ impl< let (record_id, _) = memory.write( e, read_record.rd_val + F::from_canonical_u32((i * WRITE_SIZE) as u32), - write, + &tmp_convert_to_u8s(write), ); i += 1; record_id diff --git a/extensions/rv32-adapters/src/vec_heap_two_reads.rs b/extensions/rv32-adapters/src/vec_heap_two_reads.rs index f829db8bbc..2db9ea67ef 100644 --- a/extensions/rv32-adapters/src/vec_heap_two_reads.rs +++ b/extensions/rv32-adapters/src/vec_heap_two_reads.rs @@ -29,7 +29,8 @@ use openvm_instructions::{ riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}, }; use openvm_rv32im_circuit::adapters::{ - abstract_compose, read_rv32_register, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + abstract_compose, read_rv32_register, tmp_convert_to_u8s, RV32_CELL_BITS, + RV32_REGISTER_NUM_LIMBS, }; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -427,14 +428,14 @@ impl< assert!(rs1_val as usize + READ_SIZE * BLOCKS_PER_READ1 - 1 < (1 << self.air.address_bits)); let read1_records = from_fn(|i| { - memory.read::(e, F::from_canonical_u32(rs1_val + (i * READ_SIZE) as u32)) + memory.read::(e, F::from_canonical_u32(rs1_val + (i * READ_SIZE) as u32)) }); - let read1_data = read1_records.map(|r| r.1); + let read1_data = read1_records.map(|r| r.1.map(F::from_canonical_u8)); assert!(rs2_val as usize + READ_SIZE * BLOCKS_PER_READ2 - 1 < (1 << self.air.address_bits)); let read2_records = from_fn(|i| { - memory.read::(e, F::from_canonical_u32(rs2_val + (i * READ_SIZE) as u32)) + memory.read::(e, F::from_canonical_u32(rs2_val + (i * READ_SIZE) as u32)) }); - let read2_data = read2_records.map(|r| r.1); + let read2_data = read2_records.map(|r| r.1.map(F::from_canonical_u8)); assert!(rd_val as usize + WRITE_SIZE * BLOCKS_PER_WRITE - 1 < (1 << self.air.address_bits)); let record = Rv32VecHeapTwoReadsReadRecord { @@ -463,7 +464,7 @@ impl< let (record_id, _) = memory.write( e, read_record.rd_val + F::from_canonical_u32((i * WRITE_SIZE) as u32), - write, + &tmp_convert_to_u8s(write), ); i += 1; record_id diff --git a/extensions/rv32im/circuit/src/adapters/alu.rs b/extensions/rv32im/circuit/src/adapters/alu.rs index b61e2a224a..e95fc4e8d7 100644 --- a/extensions/rv32im/circuit/src/adapters/alu.rs +++ b/extensions/rv32im/circuit/src/adapters/alu.rs @@ -35,6 +35,7 @@ use openvm_stark_backend::{ use serde::{Deserialize, Serialize}; use super::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; +use crate::adapters::tmp_convert_to_u8s; /// Reads instructions of the form OP a, b, c, d, e where \[a:4\]_d = \[b:4\]_d op \[c:4\]_e. /// Operand d can only be 1, and e can be either 1 (for register reads) or 0 (when c @@ -80,11 +81,10 @@ pub struct Rv32BaseAluReadRecord { #[repr(C)] #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct Rv32BaseAluWriteRecord { +pub struct Rv32BaseAluWriteRecord { pub from_state: ExecutionState, /// Write to destination register - pub rd: (RecordId, [F; 4]), + pub rd: RecordId, } #[repr(C)] @@ -215,7 +215,7 @@ impl VmAdapterAir for Rv32BaseAluAdapterAir { impl VmAdapterChip for Rv32BaseAluAdapterChip { type ReadRecord = Rv32BaseAluReadRecord; - type WriteRecord = Rv32BaseAluWriteRecord; + type WriteRecord = Rv32BaseAluWriteRecord; type Air = Rv32BaseAluAdapterAir; type Interface = BasicAdapterInterface< F, @@ -241,7 +241,7 @@ impl VmAdapterChip for Rv32BaseAluAdapterChip { e.as_canonical_u32() == RV32_IMM_AS || e.as_canonical_u32() == RV32_REGISTER_AS ); - let rs1 = memory.read::(d, b); + let rs1 = memory.read::(d, b); let (rs2, rs2_data, rs2_imm) = if e.is_zero() { let c_u32 = c.as_canonical_u32(); debug_assert_eq!(c_u32 >> 24, 0); @@ -253,17 +253,19 @@ impl VmAdapterChip for Rv32BaseAluAdapterChip { (c_u32 >> 8) as u8, (c_u32 >> 16) as u8, (c_u32 >> 16) as u8, - ] - .map(F::from_canonical_u8), + ], c, ) } else { - let rs2_read = memory.read::(e, c); + let rs2_read = memory.read::(e, c); (Some(rs2_read.0), rs2_read.1, F::ZERO) }; Ok(( - [rs1.1, rs2_data], + [ + rs1.1.map(F::from_canonical_u8), + rs2_data.map(F::from_canonical_u8), + ], Self::ReadRecord { rs1: rs1.0, rs2, @@ -281,7 +283,7 @@ impl VmAdapterChip for Rv32BaseAluAdapterChip { _read_record: &Self::ReadRecord, ) -> Result<(ExecutionState, Self::WriteRecord)> { let Instruction { a, d, .. } = instruction; - let rd = memory.write(*d, *a, output.writes[0]); + let (rd, _) = memory.write(*d, *a, &tmp_convert_to_u8s(output.writes[0])); let timestamp_delta = memory.timestamp() - from_state.timestamp; debug_assert!( @@ -309,7 +311,7 @@ impl VmAdapterChip for Rv32BaseAluAdapterChip { let row_slice: &mut Rv32BaseAluAdapterCols<_> = row_slice.borrow_mut(); let aux_cols_factory = memory.aux_cols_factory(); - let rd = memory.record_by_id(write_record.rd.0); + let rd = memory.record_by_id(write_record.rd); row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); row_slice.rd_ptr = rd.pointer; diff --git a/extensions/rv32im/circuit/src/adapters/branch.rs b/extensions/rv32im/circuit/src/adapters/branch.rs index 3e26f37f4c..92f96ed6d0 100644 --- a/extensions/rv32im/circuit/src/adapters/branch.rs +++ b/extensions/rv32im/circuit/src/adapters/branch.rs @@ -168,11 +168,14 @@ impl VmAdapterChip for Rv32BranchAdapterChip { debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); debug_assert_eq!(e.as_canonical_u32(), RV32_REGISTER_AS); - let rs1 = memory.read::(d, a); - let rs2 = memory.read::(e, b); + let rs1 = memory.read::(d, a); + let rs2 = memory.read::(e, b); Ok(( - [rs1.1, rs2.1], + [ + rs1.1.map(F::from_canonical_u8), + rs2.1.map(F::from_canonical_u8), + ], Self::ReadRecord { rs1: rs1.0, rs2: rs2.0, diff --git a/extensions/rv32im/circuit/src/adapters/jalr.rs b/extensions/rv32im/circuit/src/adapters/jalr.rs index f7dbf623b8..98823cd789 100644 --- a/extensions/rv32im/circuit/src/adapters/jalr.rs +++ b/extensions/rv32im/circuit/src/adapters/jalr.rs @@ -29,7 +29,7 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use super::RV32_REGISTER_NUM_LIMBS; +use super::{tmp_convert_to_u8s, RV32_REGISTER_NUM_LIMBS}; // This adapter reads from [b:4]_d (rs1) and writes to [a:4]_d (rd) #[derive(Debug)] @@ -202,9 +202,12 @@ impl VmAdapterChip for Rv32JalrAdapterChip { let Instruction { b, d, .. } = *instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - let rs1 = memory.read::(d, b); + let rs1 = memory.read::(d, b); - Ok(([rs1.1], Rv32JalrReadRecord { rs1: rs1.0 })) + Ok(( + [rs1.1.map(F::from_canonical_u8)], + Rv32JalrReadRecord { rs1: rs1.0 }, + )) } fn postprocess( @@ -219,7 +222,7 @@ impl VmAdapterChip for Rv32JalrAdapterChip { a, d, f: enabled, .. } = *instruction; let rd_id = if enabled != F::ZERO { - let (record_id, _) = memory.write(d, a, output.writes[0]); + let (record_id, _) = memory.write(d, a, &tmp_convert_to_u8s(output.writes[0])); Some(record_id) } else { memory.increment_timestamp(); diff --git a/extensions/rv32im/circuit/src/adapters/loadstore.rs b/extensions/rv32im/circuit/src/adapters/loadstore.rs index b92680a0c7..db379c7ab7 100644 --- a/extensions/rv32im/circuit/src/adapters/loadstore.rs +++ b/extensions/rv32im/circuit/src/adapters/loadstore.rs @@ -38,7 +38,7 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use super::{compose, RV32_REGISTER_NUM_LIMBS}; +use super::{tmp_convert_to_u8s, RV32_REGISTER_NUM_LIMBS}; use crate::adapters::RV32_CELL_BITS; /// LoadStore Adapter handles all memory and register operations, so it must be aware @@ -397,9 +397,9 @@ impl VmAdapterChip for Rv32LoadStoreAdapterChip { let local_opcode = Rv32LoadStoreOpcode::from_usize( opcode.local_opcode_idx(Rv32LoadStoreOpcode::CLASS_OFFSET), ); - let rs1_record = memory.read::(d, b); + let rs1_record = memory.read::(d, b); - let rs1_val = compose(rs1_record.1); + let rs1_val = u32::from_le_bytes(rs1_record.1); let imm = c.as_canonical_u32(); let imm_sign = g.as_canonical_u32(); let imm_extended = imm + imm_sign * 0xffff0000; @@ -417,24 +417,27 @@ impl VmAdapterChip for Rv32LoadStoreAdapterChip { let ptr_val = ptr_val - shift_amount; let read_record = match local_opcode { LOADW | LOADB | LOADH | LOADBU | LOADHU => { - memory.read::(e, F::from_canonical_u32(ptr_val)) + memory.read::(e, F::from_canonical_u32(ptr_val)) } - STOREW | STOREH | STOREB => memory.read::(d, a), + STOREW | STOREH | STOREB => memory.read::(d, a), }; // We need to keep values of some cells to keep them unchanged when writing to those cells let prev_data = match local_opcode { STOREW | STOREH | STOREB => array::from_fn(|i| { - memory.unsafe_read_cell(e, F::from_canonical_usize(ptr_val as usize + i)) + memory.unsafe_read_cell::(e, F::from_canonical_usize(ptr_val as usize + i)) }), LOADW | LOADB | LOADH | LOADBU | LOADHU => { - array::from_fn(|i| memory.unsafe_read_cell(d, a + F::from_canonical_usize(i))) + array::from_fn(|i| memory.unsafe_read_cell::(d, a + F::from_canonical_usize(i))) } }; Ok(( ( - [prev_data, read_record.1], + [ + prev_data.map(F::from_canonical_u8), + read_record.1.map(F::from_canonical_u8), + ], F::from_canonical_u32(shift_amount), ), Self::ReadRecord { @@ -476,9 +479,15 @@ impl VmAdapterChip for Rv32LoadStoreAdapterChip { STOREW | STOREH | STOREB => { let ptr = read_record.mem_ptr_limbs[0] + read_record.mem_ptr_limbs[1] * (1 << (RV32_CELL_BITS * 2)); - memory.write(e, F::from_canonical_u32(ptr & 0xfffffffc), output.writes[0]) + memory.write( + e, + F::from_canonical_u32(ptr & 0xfffffffc), + &tmp_convert_to_u8s(output.writes[0]), + ) + } + LOADW | LOADB | LOADH | LOADBU | LOADHU => { + memory.write(d, a, &tmp_convert_to_u8s(output.writes[0])) } - LOADW | LOADB | LOADH | LOADBU | LOADHU => memory.write(d, a, output.writes[0]), }; record_id } else { diff --git a/extensions/rv32im/circuit/src/adapters/mod.rs b/extensions/rv32im/circuit/src/adapters/mod.rs index ab15671b74..8be7b8b81d 100644 --- a/extensions/rv32im/circuit/src/adapters/mod.rs +++ b/extensions/rv32im/circuit/src/adapters/mod.rs @@ -55,16 +55,16 @@ pub fn read_rv32_register( pointer: F, ) -> (RecordId, u32) { debug_assert_eq!(address_space, F::ONE); - let record = memory.read::(address_space, pointer); - let val = compose(record.1); + let record = memory.read::(address_space, pointer); + let val = u32::from_le_bytes(record.1); (record.0, val) } /// Peeks at the value of a register without updating the memory state or incrementing the /// timestamp. pub fn unsafe_read_rv32_register(memory: &MemoryController, pointer: F) -> u32 { - let data = memory.unsafe_read::(F::ONE, pointer); - compose(data) + let data = memory.unsafe_read::(F::ONE, pointer); + u32::from_le_bytes(data) } pub fn abstract_compose>( @@ -76,3 +76,8 @@ pub fn abstract_compose>( acc + limb * T::from_canonical_u32(1 << (i * RV32_CELL_BITS)) }) } + +// TEMP[jpw] +pub fn tmp_convert_to_u8s(data: [F; N]) -> [u8; N] { + data.map(|x| x.as_canonical_u32() as u8) +} diff --git a/extensions/rv32im/circuit/src/adapters/mul.rs b/extensions/rv32im/circuit/src/adapters/mul.rs index a82e83acaa..91ad6ac0c1 100644 --- a/extensions/rv32im/circuit/src/adapters/mul.rs +++ b/extensions/rv32im/circuit/src/adapters/mul.rs @@ -29,6 +29,7 @@ use openvm_stark_backend::{ use serde::{Deserialize, Serialize}; use super::RV32_REGISTER_NUM_LIMBS; +use crate::adapters::tmp_convert_to_u8s; /// Reads instructions of the form OP a, b, c, d where \[a:4\]_d = \[b:4\]_d op \[c:4\]_d. /// Operand d can only be 1, and there is no immediate support. @@ -192,11 +193,14 @@ impl VmAdapterChip for Rv32MultAdapterChip { debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - let rs1 = memory.read::(d, b); - let rs2 = memory.read::(d, c); + let rs1 = memory.read::(d, b); + let rs2 = memory.read::(d, c); Ok(( - [rs1.1, rs2.1], + [ + rs1.1.map(F::from_canonical_u8), + rs2.1.map(F::from_canonical_u8), + ], Self::ReadRecord { rs1: rs1.0, rs2: rs2.0, @@ -213,7 +217,7 @@ impl VmAdapterChip for Rv32MultAdapterChip { _read_record: &Self::ReadRecord, ) -> Result<(ExecutionState, Self::WriteRecord)> { let Instruction { a, d, .. } = *instruction; - let (rd_id, _) = memory.write(d, a, output.writes[0]); + let (rd_id, _) = memory.write(d, a, &tmp_convert_to_u8s(output.writes[0])); let timestamp_delta = memory.timestamp() - from_state.timestamp; debug_assert!( diff --git a/extensions/rv32im/circuit/src/adapters/rdwrite.rs b/extensions/rv32im/circuit/src/adapters/rdwrite.rs index abd4d8eb17..79199f5dc4 100644 --- a/extensions/rv32im/circuit/src/adapters/rdwrite.rs +++ b/extensions/rv32im/circuit/src/adapters/rdwrite.rs @@ -29,7 +29,7 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use super::RV32_REGISTER_NUM_LIMBS; +use super::{tmp_convert_to_u8s, RV32_REGISTER_NUM_LIMBS}; /// This adapter doesn't read anything, and writes to \[a:4\]_d, where d == 1 #[derive(Debug)] @@ -270,7 +270,7 @@ impl VmAdapterChip for Rv32RdWriteAdapterChip { _read_record: &Self::ReadRecord, ) -> Result<(ExecutionState, Self::WriteRecord)> { let Instruction { a, d, .. } = *instruction; - let (rd_id, _) = memory.write(d, a, output.writes[0]); + let (rd_id, _) = memory.write(d, a, &tmp_convert_to_u8s(output.writes[0])); Ok(( ExecutionState { @@ -331,7 +331,7 @@ impl VmAdapterChip for Rv32CondRdWriteAdapterChip { ) -> Result<(ExecutionState, Self::WriteRecord)> { let Instruction { a, d, .. } = *instruction; let rd_id = if instruction.f != F::ZERO { - let (rd_id, _) = memory.write(d, a, output.writes[0]); + let (rd_id, _) = memory.write(d, a, &tmp_convert_to_u8s(output.writes[0])); Some(rd_id) } else { memory.increment_timestamp(); diff --git a/extensions/rv32im/circuit/src/base_alu/tests.rs b/extensions/rv32im/circuit/src/base_alu/tests.rs index 165cd12526..c4a5503d4f 100644 --- a/extensions/rv32im/circuit/src/base_alu/tests.rs +++ b/extensions/rv32im/circuit/src/base_alu/tests.rs @@ -340,7 +340,7 @@ struct Rv32BaseAluAdapterTestChip(Rv32BaseAluAdapterChip); impl VmAdapterChip for Rv32BaseAluAdapterTestChip { type ReadRecord = Rv32BaseAluReadRecord; - type WriteRecord = Rv32BaseAluWriteRecord; + type WriteRecord = Rv32BaseAluWriteRecord; type Air = Rv32BaseAluAdapterAir; type Interface = BasicAdapterInterface< F, @@ -361,7 +361,7 @@ impl VmAdapterChip for Rv32BaseAluAdapterTestChip { )> { let Instruction { b, c, d, e, .. } = *instruction; - let rs1 = memory.read::(d, b); + let rs1 = memory.read::(d, b); let (rs2, rs2_data, rs2_imm) = if e.is_zero() { let c_u32 = c.as_canonical_u32(); memory.increment_timestamp(); @@ -379,12 +379,16 @@ impl VmAdapterChip for Rv32BaseAluAdapterTestChip { c, ) } else { - let rs2_read = memory.read::(e, c); - (Some(rs2_read.0), rs2_read.1, F::ZERO) + let rs2_read = memory.read::(e, c); + ( + Some(rs2_read.0), + rs2_read.1.map(F::from_canonical_u8), + F::ZERO, + ) }; Ok(( - [rs1.1, rs2_data], + [rs1.1.map(F::from_canonical_u8), rs2_data], Self::ReadRecord { rs1: rs1.0, rs2, diff --git a/extensions/rv32im/circuit/src/extension.rs b/extensions/rv32im/circuit/src/extension.rs index f1f67d3994..0502bcdf16 100644 --- a/extensions/rv32im/circuit/src/extension.rs +++ b/extensions/rv32im/circuit/src/extension.rs @@ -568,12 +568,8 @@ mod phantom { let rd = unsafe_read_rv32_register(memory, a); let rs1 = unsafe_read_rv32_register(memory, b); let bytes = (0..rs1) - .map(|i| -> eyre::Result { - let val = memory.unsafe_read_cell(F::TWO, F::from_canonical_u32(rd + i)); - let byte: u8 = val.as_canonical_u32().try_into()?; - Ok(byte) - }) - .collect::>>()?; + .map(|i| memory.unsafe_read_cell::(F::TWO, F::from_canonical_u32(rd + i))) + .collect::>(); let peeked_str = String::from_utf8(bytes)?; print!("{peeked_str}"); Ok(()) diff --git a/extensions/rv32im/circuit/src/hintstore/mod.rs b/extensions/rv32im/circuit/src/hintstore/mod.rs index 6f70a584d0..69cd326ec4 100644 --- a/extensions/rv32im/circuit/src/hintstore/mod.rs +++ b/extensions/rv32im/circuit/src/hintstore/mod.rs @@ -42,7 +42,7 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use crate::adapters::{compose, decompose}; +use crate::adapters::{decompose, tmp_convert_to_u8s}; #[cfg(test)] mod tests; @@ -337,19 +337,20 @@ impl InstructionExecutor for Rv32HintStoreChip { let local_opcode = Rv32HintStoreOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - let (mem_ptr_read, mem_ptr_limbs) = memory.read::(d, mem_ptr_ptr); + let (mem_ptr_read, mem_ptr_limbs) = + memory.read::(d, mem_ptr_ptr); let (num_words, num_words_read) = if local_opcode == HINT_STOREW { memory.increment_timestamp(); (1, None) } else { let (num_words_read, num_words_limbs) = - memory.read::(d, num_words_ptr); - (compose(num_words_limbs), Some(num_words_read)) + memory.read::(d, num_words_ptr); + (u32::from_le_bytes(num_words_limbs), Some(num_words_read)) }; debug_assert_ne!(num_words, 0); debug_assert!(num_words <= (1 << self.air.pointer_max_bits)); - let mem_ptr = compose(mem_ptr_limbs); + let mem_ptr = u32::from_le_bytes(mem_ptr_limbs); debug_assert!(mem_ptr <= (1 << self.air.pointer_max_bits)); @@ -379,7 +380,7 @@ impl InstructionExecutor for Rv32HintStoreChip { let (write, _) = memory.write( e, F::from_canonical_u32(mem_ptr + (RV32_REGISTER_NUM_LIMBS as u32 * word_index)), - data, + &tmp_convert_to_u8s(data), ); record.hints.push((data, write)); } diff --git a/extensions/rv32im/tests/src/lib.rs b/extensions/rv32im/tests/src/lib.rs index e59088eac8..eae71da4fd 100644 --- a/extensions/rv32im/tests/src/lib.rs +++ b/extensions/rv32im/tests/src/lib.rs @@ -131,7 +131,7 @@ mod tests { let config = Rv32IConfig::default(); let executor = VmExecutor::::new(config.clone()); let final_memory = executor.execute(exe, vec![])?.unwrap(); - let hasher = vm_poseidon2_hasher(); + let hasher = vm_poseidon2_hasher::(); let pv_proof = UserPublicValuesProof::compute( config.system.memory_config.memory_dimensions(), 64, diff --git a/extensions/sha256/circuit/src/sha256_chip/mod.rs b/extensions/sha256/circuit/src/sha256_chip/mod.rs index 4c40eca5d8..48dd693606 100644 --- a/extensions/sha256/circuit/src/sha256_chip/mod.rs +++ b/extensions/sha256/circuit/src/sha256_chip/mod.rs @@ -146,7 +146,7 @@ impl InstructionExecutor for Sha256VmChip { let mut read_ptr = src; for _ in 0..num_blocks { let block_reads_records = array::from_fn(|i| { - memory.read( + memory.read::( e, F::from_canonical_u32(read_ptr + (i * SHA256_READ_SIZE) as u32), ) @@ -157,9 +157,7 @@ impl InstructionExecutor for Sha256VmChip { SHA256_READ_SIZE, (max(read_ptr, src + len) - read_ptr) as usize, ); - let row_input = block_reads_records[i] - .1 - .map(|x| x.as_canonical_u32().try_into().unwrap()); + let row_input = block_reads_records[i].1; hasher.update(&row_input[..num_reads]); read_ptr += SHA256_READ_SIZE as u32; row_input @@ -170,11 +168,7 @@ impl InstructionExecutor for Sha256VmChip { let mut digest = [0u8; SHA256_WRITE_SIZE]; digest.copy_from_slice(hasher.finalize().as_ref()); - let (digest_write, _) = memory.write( - e, - F::from_canonical_u32(dst), - digest.map(|b| F::from_canonical_u8(b)), - ); + let (digest_write, _) = memory.write(e, F::from_canonical_u32(dst), &digest); self.records.push(Sha256Record { from_state: from_state.map(F::from_canonical_u32), From 01c028dfd81a3d74e485c0415e0644f7b0013aac Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Sun, 13 Apr 2025 01:26:52 -0400 Subject: [PATCH 02/49] feat: `GuestMemory` trait (#1574) Not merging to main Add `GuestMemory` trait and implement for `AddressMap`. We are moving more towards a trait based style to re-use code when different types of memory might be swapped out. --- crates/vm/src/system/memory/online.rs | 48 ++++++++++++++- crates/vm/src/system/memory/paged_vec.rs | 61 ++++++++++++++----- .../src/system/memory/tree/public_values.rs | 4 +- 3 files changed, 94 insertions(+), 19 deletions(-) diff --git a/crates/vm/src/system/memory/online.rs b/crates/vm/src/system/memory/online.rs index c29565570c..3e45e65593 100644 --- a/crates/vm/src/system/memory/online.rs +++ b/crates/vm/src/system/memory/online.rs @@ -8,6 +8,50 @@ use crate::{ system::memory::{offline::INITIAL_TIMESTAMP, MemoryImage, RecordId}, }; +/// API for guest memory conforming to OpenVM ISA +pub trait GuestMemory { + /// Returns `[pointer:BLOCK_SIZE]_{address_space}` + /// + /// # Safety + /// The type `T` must be stack-allocated `repr(C)` or `repr(transparent)`, + /// and it must be the exact type used to represent a single memory cell in + /// address space `address_space`. For standard usage, + /// `T` is either `u8` or `F` where `F` is the base field of the ZK backend. + unsafe fn read( + &mut self, // &mut potentially for logs? + address_space: u32, + pointer: u32, + ) -> [T; BLOCK_SIZE]; + + /// Writes `values` to `[pointer:BLOCK_SIZE]_{address_space}` + /// + /// # Safety + /// See [`GuestMemory::read`]. + unsafe fn write( + &mut self, + address_space: u32, + pointer: u32, + values: &[T; BLOCK_SIZE], + ); + + /// Writes `values` to `[pointer:BLOCK_SIZE]_{address_space}` and returns + /// the previous values. + /// + /// # Safety + /// See [`GuestMemory::read`]. + #[inline(always)] + unsafe fn replace( + &mut self, + address_space: u32, + pointer: u32, + values: &[T; BLOCK_SIZE], + ) -> [T; BLOCK_SIZE] { + let prev = self.read(address_space, pointer); + self.write(address_space, pointer, values); + prev + } +} + // TO BE DELETED #[derive(Debug, Clone, Serialize, Deserialize)] pub enum MemoryLogEntry { @@ -80,7 +124,7 @@ impl Memory { ) -> (RecordId, [T; BLOCK_SIZE]) { debug_assert!(BLOCK_SIZE.is_power_of_two()); - let prev_data = self.data.set_range((address_space, pointer), values); + let prev_data = self.data.replace(address_space, pointer, values); // self.log.push(MemoryLogEntry::Write { // address_space, @@ -113,7 +157,7 @@ impl Memory { // len: N, // }); - let values = self.data.get_range((address_space, pointer)); + let values = self.data.read(address_space, pointer); self.timestamp += 1; (self.last_record_id(), values) } diff --git a/crates/vm/src/system/memory/paged_vec.rs b/crates/vm/src/system/memory/paged_vec.rs index 1de9e49fe3..d3ced876ec 100644 --- a/crates/vm/src/system/memory/paged_vec.rs +++ b/crates/vm/src/system/memory/paged_vec.rs @@ -5,6 +5,7 @@ use openvm_instructions::exe::SparseMemoryImage; use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; +use super::online::GuestMemory; use crate::arch::MemoryConfig; /// (address_space, pointer) @@ -72,6 +73,7 @@ impl PagedVec { ptr::copy_nonoverlapping(page.as_ptr().add(offset), dst, len); ptr::copy_nonoverlapping(new, page.as_mut_ptr().add(offset), len); } else { + assert_eq!(start_page + 1, end_page); let offset = start % PAGE_SIZE; let first_part = PAGE_SIZE - offset; { @@ -120,11 +122,41 @@ impl PagedVec { unsafe { result.assume_init() } } + /// # Panics + /// If `start..start + size_of()` is out of bounds. + #[inline(always)] + pub fn set(&mut self, start: usize, values: &BLOCK) { + let len = size_of::(); + let start_page = start / PAGE_SIZE; + let end_page = (start + len - 1) / PAGE_SIZE; + let src = values as *const _ as *const u8; + unsafe { + if start_page == end_page { + let offset = start % PAGE_SIZE; + let page = self.pages[start_page].get_or_insert_with(|| vec![0u8; PAGE_SIZE]); + ptr::copy_nonoverlapping(src, page.as_mut_ptr().add(offset), len); + } else { + assert_eq!(start_page + 1, end_page); + let offset = start % PAGE_SIZE; + let first_part = PAGE_SIZE - offset; + { + let page = self.pages[start_page].get_or_insert_with(|| vec![0u8; PAGE_SIZE]); + ptr::copy_nonoverlapping(src, page.as_mut_ptr().add(offset), first_part); + } + let second_part = len - first_part; + { + let page = self.pages[end_page].get_or_insert_with(|| vec![0u8; PAGE_SIZE]); + ptr::copy_nonoverlapping(src.add(first_part), page.as_mut_ptr(), second_part); + } + } + } + } + /// memcpy of new `values` into pages, memcpy of old existing values into new returned value. /// # Panics /// If `from..from + size_of()` is out of bounds. #[inline(always)] - pub fn set(&mut self, from: usize, values: &BLOCK) -> BLOCK { + pub fn replace(&mut self, from: usize, values: &BLOCK) -> BLOCK { // Create an uninitialized array for old values. let mut result: MaybeUninit = MaybeUninit::uninit(); self.set_range_generic( @@ -278,7 +310,7 @@ impl AddressMap { ); self.paged_vecs .get_unchecked_mut((addr_space - self.as_offset) as usize) - .set((ptr as usize) * size_of::(), &data) + .replace((ptr as usize) * size_of::(), &data) } pub fn is_empty(&self) -> bool { self.paged_vecs.iter().all(|page| page.is_empty()) @@ -302,11 +334,12 @@ impl AddressMap { } } -impl AddressMap { - /// # Safety - /// - `T` **must** be the correct type for a single memory cell for `addr_space` - /// - Assumes `addr_space` is within the configured memory and not out of bounds - pub unsafe fn get_range(&self, (addr_space, ptr): Address) -> [T; N] { +impl GuestMemory for AddressMap { + unsafe fn read( + &mut self, + addr_space: u32, + ptr: u32, + ) -> [T; BLOCK_SIZE] { debug_assert_eq!( size_of::(), self.cell_size[(addr_space - self.as_offset) as usize] @@ -316,14 +349,12 @@ impl AddressMap { .get((ptr as usize) * size_of::()) } - /// # Safety - /// - `T` **must** be the correct type for a single memory cell for `addr_space` - /// - Assumes `addr_space` is within the configured memory and not out of bounds - pub unsafe fn set_range( + unsafe fn write( &mut self, - (addr_space, ptr): Address, - values: &[T; N], - ) -> [T; N] { + addr_space: u32, + ptr: u32, + values: &[T; BLOCK_SIZE], + ) { debug_assert_eq!( size_of::(), self.cell_size[(addr_space - self.as_offset) as usize], @@ -331,7 +362,7 @@ impl AddressMap { ); self.paged_vecs .get_unchecked_mut((addr_space - self.as_offset) as usize) - .set((ptr as usize) * size_of::(), values) + .set((ptr as usize) * size_of::(), values); } } diff --git a/crates/vm/src/system/memory/tree/public_values.rs b/crates/vm/src/system/memory/tree/public_values.rs index d6914d4720..08fa6e2723 100644 --- a/crates/vm/src/system/memory/tree/public_values.rs +++ b/crates/vm/src/system/memory/tree/public_values.rs @@ -206,7 +206,7 @@ mod tests { use super::{UserPublicValuesProof, PUBLIC_VALUES_ADDRESS_SPACE_OFFSET}; use crate::{ arch::{hasher::poseidon2::vm_poseidon2_hasher, SystemConfig}, - system::memory::{paged_vec::AddressMap, tree::MemoryNode, CHUNK}, + system::memory::{online::GuestMemory, paged_vec::AddressMap, tree::MemoryNode, CHUNK}, }; type F = BabyBear; @@ -224,7 +224,7 @@ mod tests { 1 << memory_dimensions.address_height, ); unsafe { - memory.set_range::((pv_as, 15), &[F::ONE]); + memory.write::(pv_as, 15, &[F::ONE]); } let mut expected_pvs = F::zero_vec(num_public_values); expected_pvs[15] = F::ONE; From aef64c9c010b4dc093e5c9d330e3d0ea2a77dc60 Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Tue, 15 Apr 2025 19:53:16 -0400 Subject: [PATCH 03/49] feat(new-execution): make vm segment executor generic (#1582) - make `VmSegmentExecutor` generic on `` where: - `Mem`: struct that implements `GuestMemory` - `Ctx`: struct that stores host context during execution - `Ctrl`: struct that implements pre/post segment execution hooks, termination condition and instruction execution logic - add `TracegenVmSegmentExecutor` that implements the current execution flow - move segmentation strategies to new file --- crates/sdk/src/prover/vm/local.rs | 2 +- crates/vm/src/arch/config.rs | 6 +- crates/vm/src/arch/execution_control.rs | 169 ++++++++ crates/vm/src/arch/extensions.rs | 1 + crates/vm/src/arch/mod.rs | 5 + crates/vm/src/arch/segment.rs | 446 +++++++++----------- crates/vm/src/arch/segmentation_strategy.rs | 109 +++++ crates/vm/src/arch/vm.rs | 53 ++- crates/vm/src/metrics/mod.rs | 41 +- crates/vm/tests/integration_test.rs | 22 +- 10 files changed, 533 insertions(+), 321 deletions(-) create mode 100644 crates/vm/src/arch/execution_control.rs create mode 100644 crates/vm/src/arch/segmentation_strategy.rs diff --git a/crates/sdk/src/prover/vm/local.rs b/crates/sdk/src/prover/vm/local.rs index b56c6a1ad3..285396ff09 100644 --- a/crates/sdk/src/prover/vm/local.rs +++ b/crates/sdk/src/prover/vm/local.rs @@ -99,7 +99,7 @@ where exe.clone(), input.clone(), |seg_idx, mut seg| { - final_memory = mem::take(&mut seg.final_memory); + final_memory = mem::take(&mut seg.control.final_memory); let proof_input = info_span!("trace_gen", segment = seg_idx) .in_scope(|| seg.generate_proof_input(Some(committed_program.clone())))?; info_span!("prove_segment", segment = seg_idx) diff --git a/crates/vm/src/arch/config.rs b/crates/vm/src/arch/config.rs index 30d92130a1..813e88ca1c 100644 --- a/crates/vm/src/arch/config.rs +++ b/crates/vm/src/arch/config.rs @@ -8,9 +8,9 @@ use openvm_stark_backend::{p3_field::PrimeField32, ChipUsageGetter}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use super::{ - segment::DefaultSegmentationStrategy, AnyEnum, InstructionExecutor, SegmentationStrategy, - SystemComplex, SystemExecutor, SystemPeriphery, VmChipComplex, VmInventoryError, - PUBLIC_VALUES_AIR_ID, + segmentation_strategy::{DefaultSegmentationStrategy, SegmentationStrategy}, + AnyEnum, InstructionExecutor, SystemComplex, SystemExecutor, SystemPeriphery, VmChipComplex, + VmInventoryError, PUBLIC_VALUES_AIR_ID, }; use crate::system::memory::BOUNDARY_AIR_OFFSET; diff --git a/crates/vm/src/arch/execution_control.rs b/crates/vm/src/arch/execution_control.rs new file mode 100644 index 0000000000..80eb777cbb --- /dev/null +++ b/crates/vm/src/arch/execution_control.rs @@ -0,0 +1,169 @@ +use openvm_instructions::instruction::Instruction; +use openvm_stark_backend::p3_field::PrimeField32; + +use super::{segment::VmExecutionState, ExecutionError, TracegenCtx, VmChipComplex, VmConfig}; +use crate::{ + arch::{ExecutionState, InstructionExecutor}, + system::memory::{online::GuestMemory, AddressMap, MemoryImage, PAGE_SIZE}, +}; + +/// Check segment every 100 instructions. +const SEGMENT_CHECK_INTERVAL: usize = 100; + +/// Trait for execution control, determining segmentation and stopping conditions +pub trait ExecutionControl +where + F: PrimeField32, + VC: VmConfig, +{ + /// Guest memory type + type Mem: GuestMemory; + /// Host context + type Ctx; + + fn new(chip_complex: &VmChipComplex) -> Self; + + /// Determines if execution should stop + fn should_stop( + &mut self, + state: &VmExecutionState, + chip_complex: &VmChipComplex, + ) -> bool; + + /// Called before segment execution begins + fn on_segment_start( + &mut self, + vm_state: &VmExecutionState, + chip_complex: &mut VmChipComplex, + ); + + /// Called after segment execution completes + fn on_segment_end( + &mut self, + vm_state: &VmExecutionState, + chip_complex: &mut VmChipComplex, + ); + + /// Execute a single instruction + // TODO(ayush): change instruction to Instruction / PInstruction + fn execute_instruction( + &mut self, + vm_state: &mut VmExecutionState, + // instruction: &Instruction, + chip_complex: &mut VmChipComplex, + ) -> Result<(), ExecutionError> + where + F: PrimeField32; +} + +/// Implementation of the ExecutionControl trait using the old segmentation strategy +pub struct TracegenExecutionControl { + pub final_memory: Option, + pub since_last_segment_check: usize, + air_names: Vec, +} + +impl TracegenExecutionControl { + pub fn new(air_names: Vec) -> Self { + Self { + final_memory: None, + since_last_segment_check: 0, + air_names, + } + } +} + +impl ExecutionControl for TracegenExecutionControl +where + F: PrimeField32, + VC: VmConfig, +{ + type Ctx = TracegenCtx; + type Mem = AddressMap; + + fn new(chip_complex: &VmChipComplex) -> Self { + Self { + final_memory: None, + since_last_segment_check: 0, + air_names: chip_complex.air_names(), + } + } + + fn should_stop( + &mut self, + _state: &VmExecutionState, + chip_complex: &VmChipComplex, + ) -> bool { + // Avoid checking segment too often. + if self.since_last_segment_check != SEGMENT_CHECK_INTERVAL { + self.since_last_segment_check += 1; + return false; + } + self.since_last_segment_check = 0; + chip_complex.config().segmentation_strategy.should_segment( + &self.air_names, + &chip_complex.dynamic_trace_heights().collect::>(), + &chip_complex.current_trace_cells(), + ) + } + + fn on_segment_start( + &mut self, + vm_state: &VmExecutionState, + chip_complex: &mut VmChipComplex, + ) { + let timestamp = chip_complex.memory_controller().timestamp(); + chip_complex + .connector_chip_mut() + .begin(ExecutionState::new(vm_state.pc, timestamp)); + } + + fn on_segment_end( + &mut self, + vm_state: &VmExecutionState, + chip_complex: &mut VmChipComplex, + ) { + let timestamp = chip_complex.memory_controller().timestamp(); + // End the current segment with connector chip + chip_complex + .connector_chip_mut() + .end(ExecutionState::new(vm_state.pc, timestamp), None); + self.final_memory = Some(chip_complex.base.memory_controller.memory_image().clone()); + } + + /// Execute a single instruction + fn execute_instruction( + &mut self, + vm_state: &mut VmExecutionState, + // instruction: &Instruction, + chip_complex: &mut VmChipComplex, + ) -> Result<(), ExecutionError> + where + F: PrimeField32, + { + let timestamp = chip_complex.memory_controller().timestamp(); + let (instruction, _) = chip_complex + .base + .program_chip + .get_instruction(vm_state.pc)?; + + let &Instruction { opcode, .. } = instruction; + + if let Some(executor) = chip_complex.inventory.get_mut_executor(&opcode) { + let memory_controller = &mut chip_complex.base.memory_controller; + let new_state = executor.execute( + memory_controller, + instruction, + ExecutionState::new(vm_state.pc, timestamp), + )?; + vm_state.pc = new_state.pc; + } else { + return Err(ExecutionError::DisabledOperation { + pc: vm_state.pc, + opcode, + }); + }; + + Ok(()) + } +} diff --git a/crates/vm/src/arch/extensions.rs b/crates/vm/src/arch/extensions.rs index 839d8485ee..1c04910abf 100644 --- a/crates/vm/src/arch/extensions.rs +++ b/crates/vm/src/arch/extensions.rs @@ -851,6 +851,7 @@ impl VmChipComplex { .chain([self.range_checker_chip().air_name()]) .collect() } + /// Return trace heights of all chips in order corresponding to `air_names`. pub(crate) fn current_trace_heights(&self) -> Vec where diff --git a/crates/vm/src/arch/mod.rs b/crates/vm/src/arch/mod.rs index 63ee5e6f8b..7ecb289cc9 100644 --- a/crates/vm/src/arch/mod.rs +++ b/crates/vm/src/arch/mod.rs @@ -2,12 +2,16 @@ mod config; /// Instruction execution traits and types. /// Execution bus and interface. mod execution; +/// Module for controlling VM execution flow, including segmentation and instruction execution +pub mod execution_control; /// Traits and builders to compose collections of chips into a virtual machine. mod extensions; /// Traits and wrappers to facilitate VM chip integration mod integration_api; /// Runtime execution and segmentation pub mod segment; +/// Strategy for determining when to segment VM execution +pub mod segmentation_strategy; /// Top level [VirtualMachine] constructor and API. pub mod vm; @@ -23,4 +27,5 @@ pub use execution::*; pub use extensions::*; pub use integration_api::*; pub use segment::*; +pub use segmentation_strategy::*; pub use vm::*; diff --git a/crates/vm/src/arch/segment.rs b/crates/vm/src/arch/segment.rs index 686282877e..9b1f95eff1 100644 --- a/crates/vm/src/arch/segment.rs +++ b/crates/vm/src/arch/segment.rs @@ -1,10 +1,7 @@ -use std::sync::Arc; - use backtrace::Backtrace; use openvm_instructions::{ exe::FnBounds, instruction::{DebugInfo, Instruction}, - program::Program, }; use openvm_stark_backend::{ config::{Domain, StarkGenericConfig}, @@ -15,139 +12,64 @@ use openvm_stark_backend::{ utils::metrics_span, Chip, }; +use program::Program; use super::{ - ExecutionError, GenerationError, Streams, SystemBase, SystemConfig, VmChipComplex, - VmComplexTraceHeights, VmConfig, + execution_control::{ExecutionControl, TracegenExecutionControl}, + ExecutionError, GenerationError, Streams, SystemConfig, VmChipComplex, VmComplexTraceHeights, + VmConfig, }; #[cfg(feature = "bench-metrics")] use crate::metrics::VmMetrics; use crate::{ arch::{instructions::*, ExecutionState, InstructionExecutor}, - system::memory::MemoryImage, + system::memory::{ + online::GuestMemory, paged_vec::PAGE_SIZE, AddressMap, MemoryController, MemoryImage, + }, }; -/// Check segment every 100 instructions. -const SEGMENT_CHECK_INTERVAL: usize = 100; - -const DEFAULT_MAX_SEGMENT_LEN: usize = (1 << 22) - 100; -// a heuristic number for the maximum number of cells per chip in a segment -// a few reasons for this number: -// 1. `VmAirWrapper` is -// the chip with the most cells in a segment from the reth-benchmark. -// 2. `VmAirWrapper`: -// its trace width is 36 and its after challenge trace width is 80. -const DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT: usize = DEFAULT_MAX_SEGMENT_LEN * 120; - -pub trait SegmentationStrategy: - std::fmt::Debug + Send + Sync + std::panic::UnwindSafe + std::panic::RefUnwindSafe +/// Represents the state of the VM during execution +pub struct VmExecutionState +where + Mem: GuestMemory, { - /// Whether the execution should segment based on the trace heights and cells. - /// - /// Air names are provided for debugging purposes. - fn should_segment( - &self, - air_names: &[String], - trace_heights: &[usize], - trace_cells: &[usize], - ) -> bool; - - /// A strategy that segments more aggressively than the current one. - /// - /// Called when `should_segment` results in a segment that is infeasible. Execution will be - /// re-run with the stricter segmentation strategy. - fn stricter_strategy(&self) -> Arc; -} - -/// Default segmentation strategy: segment if any chip's height or cells exceed the limits. -#[derive(Debug, Clone)] -pub struct DefaultSegmentationStrategy { - max_segment_len: usize, - max_cells_per_chip_in_segment: usize, -} - -impl Default for DefaultSegmentationStrategy { - fn default() -> Self { - Self { - max_segment_len: DEFAULT_MAX_SEGMENT_LEN, - max_cells_per_chip_in_segment: DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT, - } - } + /// Program counter - current instruction address + pub pc: u32, + /// Whether execution has terminated + // TODO: see if it can be removed + pub terminated: bool, + /// Guest memory interface + pub memory: Mem, + /// Host-specific execution context + pub ctx: Ctx, } -impl DefaultSegmentationStrategy { - pub fn new_with_max_segment_len(max_segment_len: usize) -> Self { - Self { - max_segment_len, - max_cells_per_chip_in_segment: max_segment_len * 120, - } - } - - pub fn new(max_segment_len: usize, max_cells_per_chip_in_segment: usize) -> Self { +impl VmExecutionState +where + Mem: GuestMemory, +{ + /// Creates a new VM execution state with the given parameters + pub fn new(pc: u32, memory: Mem, ctx: Ctx) -> Self { Self { - max_segment_len, - max_cells_per_chip_in_segment, - } - } - - pub fn max_segment_len(&self) -> usize { - self.max_segment_len - } -} - -const SEGMENTATION_BACKOFF_FACTOR: usize = 4; - -impl SegmentationStrategy for DefaultSegmentationStrategy { - fn should_segment( - &self, - air_names: &[String], - trace_heights: &[usize], - trace_cells: &[usize], - ) -> bool { - for (i, &height) in trace_heights.iter().enumerate() { - if height > self.max_segment_len { - tracing::info!( - "Should segment because chip {} (name: {}) has height {}", - i, - air_names[i], - height - ); - return true; - } - } - for (i, &num_cells) in trace_cells.iter().enumerate() { - if num_cells > self.max_cells_per_chip_in_segment { - tracing::info!( - "Should segment because chip {} (name: {}) has {} cells", - i, - air_names[i], - num_cells - ); - return true; - } + pc, + terminated: false, + memory, + ctx, } - false - } - - fn stricter_strategy(&self) -> Arc { - Arc::new(Self { - max_segment_len: self.max_segment_len / SEGMENTATION_BACKOFF_FACTOR, - max_cells_per_chip_in_segment: self.max_cells_per_chip_in_segment - / SEGMENTATION_BACKOFF_FACTOR, - }) } } -pub struct ExecutionSegment +pub struct VmSegmentExecutor where F: PrimeField32, VC: VmConfig, + Mem: GuestMemory, + Ctrl: ExecutionControl, { pub chip_complex: VmChipComplex, - /// Memory image after segment was executed. Not used in trace generation. - pub final_memory: Option, + /// Execution control for determining segmentation and stopping conditions + pub control: Ctrl, - pub since_last_segment_check: usize, pub trace_height_constraints: Vec, /// Air names for debug purposes only. @@ -157,12 +79,42 @@ where pub metrics: VmMetrics, } -pub struct ExecutionSegmentState { - pub pc: u32, - pub is_terminated: bool, +pub struct TracegenCtx { + pub timestamp: u32, } -impl> ExecutionSegment { +impl TracegenCtx { + pub fn new(timestamp: u32) -> Self { + Self { timestamp } + } +} + +pub type TracegenVmExecutionState = VmExecutionState, TracegenCtx>; + +impl TracegenVmExecutionState { + pub fn from_pc_and_memory_controller( + pc: u32, + memory_controller: &MemoryController, + ) -> Self + where + F: PrimeField32, + { + let memory = memory_controller.memory_image().clone(); + let ctx = TracegenCtx::new(memory_controller.timestamp()); + TracegenVmExecutionState::new(pc, memory, ctx) + } +} + +pub type TracegenVmSegmentExecutor = + VmSegmentExecutor, TracegenCtx, TracegenExecutionControl>; + +impl VmSegmentExecutor +where + F: PrimeField32, + VC: VmConfig, + Mem: GuestMemory, + Ctrl: ExecutionControl, +{ /// Creates a new execution segment from a program and initial state, using parent VM config pub fn new( config: &VC, @@ -185,10 +137,11 @@ impl> ExecutionSegment { chip_complex.set_initial_memory(initial_memory); } let air_names = chip_complex.air_names(); + let control = Ctrl::new(&chip_complex); Self { chip_complex, - final_memory: None, + control, air_names, trace_height_constraints, #[cfg(feature = "bench-metrics")] @@ -196,7 +149,6 @@ impl> ExecutionSegment { fn_bounds, ..Default::default() }, - since_last_segment_check: 0, } } @@ -211,133 +163,121 @@ impl> ExecutionSegment { .set_override_inventory_trace_heights(overridden_heights.inventory); } - /// Stopping is triggered by should_segment() - pub fn execute_from_pc( + /// Stopping is triggered by should_stop() or if VM is terminated + pub fn execute_from_state( &mut self, - mut pc: u32, - ) -> Result { - let mut timestamp = self.chip_complex.memory_controller().timestamp(); + vm_state: &mut VmExecutionState, + ) -> Result<(), ExecutionError> { let mut prev_backtrace: Option = None; - self.chip_complex - .connector_chip_mut() - .begin(ExecutionState::new(pc, timestamp)); - - let mut did_terminate = false; - - loop { - #[allow(unused_variables)] - let (opcode, dsl_instr) = { - let Self { - chip_complex, - #[cfg(feature = "bench-metrics")] - metrics, - .. - } = self; - let SystemBase { - program_chip, - memory_controller, - .. - } = &mut chip_complex.base; - - let (instruction, debug_info) = program_chip.get_instruction(pc)?; - tracing::trace!("pc: {pc:#x} | time: {timestamp} | {:?}", instruction); - - #[allow(unused_variables)] - let (dsl_instr, trace) = debug_info.as_ref().map_or( - (None, None), - |DebugInfo { - dsl_instruction, - trace, - }| (Some(dsl_instruction), trace.as_ref()), - ); - - let &Instruction { opcode, c, .. } = instruction; - if opcode == SystemOpcode::TERMINATE.global_opcode() { - did_terminate = true; - self.chip_complex.connector_chip_mut().end( - ExecutionState::new(pc, timestamp), - Some(c.as_canonical_u32()), - ); - break; + // Call the pre-execution hook + self.control + .on_segment_start(vm_state, &mut self.chip_complex); + + while !vm_state.terminated && !self.should_stop(vm_state) { + // Fetch, decode and execute single instruction + self.execute_instruction(vm_state, &mut prev_backtrace)?; + } + + // Call the post-execution hook + self.control + .on_segment_end(vm_state, &mut self.chip_complex); + + Ok(()) + } + + /// Executes a single instruction and updates VM state + // TODO(ayush): clean this up, separate to smaller functions + fn execute_instruction( + &mut self, + vm_state: &mut VmExecutionState, + prev_backtrace: &mut Option, + ) -> Result<(), ExecutionError> { + let pc = vm_state.pc; + let timestamp = self.chip_complex.memory_controller().timestamp(); + + // Process an instruction and update VM state + let (instruction, debug_info) = self.chip_complex.base.program_chip.get_instruction(pc)?; + + tracing::trace!("pc: {pc:#x} | time: {timestamp} | {:?}", instruction); + + // Extract debug info components + #[allow(unused_variables)] + let (dsl_instr, trace) = debug_info.as_ref().map_or( + (None, None), + |DebugInfo { + dsl_instruction, + trace, + }| (Some(dsl_instruction.clone()), trace.as_ref()), + ); + + let &Instruction { opcode, c, .. } = instruction; + + // Handle termination instruction + if opcode == SystemOpcode::TERMINATE.global_opcode() { + self.chip_complex.connector_chip_mut().end( + ExecutionState::new(pc, timestamp), + Some(c.as_canonical_u32()), + ); + vm_state.terminated = true; + return Ok(()); + } + + // Handle phantom instructions + if opcode == SystemOpcode::PHANTOM.global_opcode() { + let discriminant = c.as_canonical_u32() as u16; + if let Some(phantom) = SysPhantom::from_repr(discriminant) { + tracing::trace!("pc: {pc:#x} | system phantom: {phantom:?}"); + + if phantom == SysPhantom::DebugPanic { + if let Some(mut backtrace) = prev_backtrace.take() { + backtrace.resolve(); + eprintln!("openvm program failure; backtrace:\n{:?}", backtrace); + } else { + eprintln!("openvm program failure; no backtrace"); + } + return Err(ExecutionError::Fail { pc }); } - // Some phantom instruction handling is more convenient to do here than in - // PhantomChip. - if opcode == SystemOpcode::PHANTOM.global_opcode() { - // Note: the discriminant is the lower 16 bits of the c operand. - let discriminant = c.as_canonical_u32() as u16; - let phantom = SysPhantom::from_repr(discriminant); - tracing::trace!("pc: {pc:#x} | system phantom: {phantom:?}"); + #[cfg(feature = "bench-metrics")] + { + let dsl_str = dsl_instr.clone().unwrap_or_else(|| "Default".to_string()); match phantom { - Some(SysPhantom::DebugPanic) => { - if let Some(mut backtrace) = prev_backtrace { - backtrace.resolve(); - eprintln!("openvm program failure; backtrace:\n{:?}", backtrace); - } else { - eprintln!("openvm program failure; no backtrace"); - } - return Err(ExecutionError::Fail { pc }); - } - Some(SysPhantom::CtStart) => - { - #[cfg(feature = "bench-metrics")] - metrics - .cycle_tracker - .start(dsl_instr.cloned().unwrap_or("Default".to_string())) - } - Some(SysPhantom::CtEnd) => - { - #[cfg(feature = "bench-metrics")] - metrics - .cycle_tracker - .end(dsl_instr.cloned().unwrap_or("Default".to_string())) - } + SysPhantom::CtStart => self.metrics.cycle_tracker.start(dsl_str), + SysPhantom::CtEnd => self.metrics.cycle_tracker.end(dsl_str), _ => {} } } - prev_backtrace = trace.cloned(); - - if let Some(executor) = chip_complex.inventory.get_mut_executor(&opcode) { - let next_state = InstructionExecutor::execute( - executor, - memory_controller, - instruction, - ExecutionState::new(pc, timestamp), - )?; - assert!(next_state.timestamp > timestamp); - pc = next_state.pc; - timestamp = next_state.timestamp; - } else { - return Err(ExecutionError::DisabledOperation { pc, opcode }); - }; - (opcode, dsl_instr.cloned()) - }; + } + } - #[cfg(feature = "bench-metrics")] + // TODO(ayush): move to vm state? + *prev_backtrace = trace.cloned(); + + // Execute the instruction using the control implementation + self.control + .execute_instruction(vm_state, &mut self.chip_complex)?; + + // Update metrics if enabled + #[cfg(feature = "bench-metrics")] + { self.update_instruction_metrics(pc, opcode, dsl_instr); + } - if self.should_segment() { - self.chip_complex - .connector_chip_mut() - .end(ExecutionState::new(pc, timestamp), None); - break; - } + Ok(()) + } + + /// Returns bool of whether to switch to next segment or not. + fn should_stop(&mut self, vm_state: &VmExecutionState) -> bool { + if !self.system_config().continuation_enabled { + return false; } - self.final_memory = Some( - self.chip_complex - .base - .memory_controller - .memory_image() - .clone(), - ); - Ok(ExecutionSegmentState { - pc, - is_terminated: did_terminate, - }) + // Check with the execution control policy + self.control.should_stop(vm_state, &self.chip_complex) } + // TODO(ayush): not sure what to do of these /// Generate ProofInput to prove the segment. Should be called after ::execute pub fn generate_proof_input( #[allow(unused_mut)] mut self, @@ -358,30 +298,28 @@ impl> ExecutionSegment { }) } - /// Returns bool of whether to switch to next segment or not. This is called every clock cycle - /// inside of Core trace generation. - fn should_segment(&mut self) -> bool { - if !self.system_config().continuation_enabled { - return false; - } - // Avoid checking segment too often. - if self.since_last_segment_check != SEGMENT_CHECK_INTERVAL { - self.since_last_segment_check += 1; - return false; + #[cfg(feature = "bench-metrics")] + #[allow(unused_variables)] + pub fn update_instruction_metrics( + &mut self, + pc: u32, + opcode: VmOpcode, + dsl_instr: Option, + ) { + self.metrics.cycle_count += 1; + + if self.system_config().profiling { + let executor = self.chip_complex.inventory.get_executor(opcode).unwrap(); + let opcode_name = executor.get_opcode_name(opcode.as_usize()); + self.metrics.update_trace_cells( + &self.air_names, + self.chip_complex.current_trace_cells(), + opcode_name, + dsl_instr, + ); + + #[cfg(feature = "function-span")] + self.metrics.update_current_fn(pc); } - self.since_last_segment_check = 0; - let segmentation_strategy = &self.system_config().segmentation_strategy; - segmentation_strategy.should_segment( - &self.air_names, - &self - .chip_complex - .dynamic_trace_heights() - .collect::>(), - &self.chip_complex.current_trace_cells(), - ) - } - - pub fn current_trace_cells(&self) -> Vec { - self.chip_complex.current_trace_cells() } } diff --git a/crates/vm/src/arch/segmentation_strategy.rs b/crates/vm/src/arch/segmentation_strategy.rs new file mode 100644 index 0000000000..a17ef97fde --- /dev/null +++ b/crates/vm/src/arch/segmentation_strategy.rs @@ -0,0 +1,109 @@ +use std::sync::Arc; + +const DEFAULT_MAX_SEGMENT_LEN: usize = (1 << 22) - 100; +// a heuristic number for the maximum number of cells per chip in a segment +// a few reasons for this number: +// 1. `VmAirWrapper` is +// the chip with the most cells in a segment from the reth-benchmark. +// 2. `VmAirWrapper`: +// its trace width is 36 and its after challenge trace width is 80. +const DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT: usize = DEFAULT_MAX_SEGMENT_LEN * 120; + +pub trait SegmentationStrategy: + std::fmt::Debug + Send + Sync + std::panic::UnwindSafe + std::panic::RefUnwindSafe +{ + /// Whether the execution should segment based on the trace heights and cells. + /// + /// Air names are provided for debugging purposes. + fn should_segment( + &self, + air_names: &[String], + trace_heights: &[usize], + trace_cells: &[usize], + ) -> bool; + + /// A strategy that segments more aggressively than the current one. + /// + /// Called when `should_segment` results in a segment that is infeasible. Execution will be + /// re-run with the stricter segmentation strategy. + fn stricter_strategy(&self) -> Arc; +} + +/// Default segmentation strategy: segment if any chip's height or cells exceed the limits. +#[derive(Debug, Clone)] +pub struct DefaultSegmentationStrategy { + max_segment_len: usize, + max_cells_per_chip_in_segment: usize, +} + +impl Default for DefaultSegmentationStrategy { + fn default() -> Self { + Self { + max_segment_len: DEFAULT_MAX_SEGMENT_LEN, + max_cells_per_chip_in_segment: DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT, + } + } +} + +impl DefaultSegmentationStrategy { + pub fn new_with_max_segment_len(max_segment_len: usize) -> Self { + Self { + max_segment_len, + max_cells_per_chip_in_segment: max_segment_len * 120, + } + } + + pub fn new(max_segment_len: usize, max_cells_per_chip_in_segment: usize) -> Self { + Self { + max_segment_len, + max_cells_per_chip_in_segment, + } + } + + pub fn max_segment_len(&self) -> usize { + self.max_segment_len + } +} + +const SEGMENTATION_BACKOFF_FACTOR: usize = 4; + +impl SegmentationStrategy for DefaultSegmentationStrategy { + fn should_segment( + &self, + air_names: &[String], + trace_heights: &[usize], + trace_cells: &[usize], + ) -> bool { + for (i, &height) in trace_heights.iter().enumerate() { + if height > self.max_segment_len { + tracing::info!( + "Should segment because chip {} (name: {}) has height {}", + i, + air_names[i], + height + ); + return true; + } + } + for (i, &num_cells) in trace_cells.iter().enumerate() { + if num_cells > self.max_cells_per_chip_in_segment { + tracing::info!( + "Should segment because chip {} (name: {}) has {} cells", + i, + air_names[i], + num_cells + ); + return true; + } + } + false + } + + fn stricter_strategy(&self) -> Arc { + Arc::new(Self { + max_segment_len: self.max_segment_len / SEGMENTATION_BACKOFF_FACTOR, + max_cells_per_chip_in_segment: self.max_cells_per_chip_in_segment + / SEGMENTATION_BACKOFF_FACTOR, + }) + } +} diff --git a/crates/vm/src/arch/vm.rs b/crates/vm/src/arch/vm.rs index 7783ae396e..0e80a8671f 100644 --- a/crates/vm/src/arch/vm.rs +++ b/crates/vm/src/arch/vm.rs @@ -25,7 +25,10 @@ use super::{ #[cfg(feature = "bench-metrics")] use crate::metrics::VmMetrics; use crate::{ - arch::{hasher::poseidon2::vm_poseidon2_hasher, segment::ExecutionSegment}, + arch::{ + hasher::poseidon2::vm_poseidon2_hasher, segment::TracegenVmSegmentExecutor, + TracegenVmExecutionState, + }, system::{ connector::{VmConnectorPvs, DEFAULT_SUSPEND_EXIT_CODE}, memory::{ @@ -117,8 +120,12 @@ impl VmExecutorNextSegmentState { } } -pub struct VmExecutorOneSegmentResult> { - pub segment: ExecutionSegment, +pub struct VmExecutorOneSegmentResult +where + F: PrimeField32, + VC: VmConfig, +{ + pub segment: TracegenVmSegmentExecutor, pub next_state: Option>, } @@ -163,7 +170,7 @@ where &self, exe: impl Into>, input: impl Into>, - mut f: impl FnMut(usize, ExecutionSegment) -> Result, + mut f: impl FnMut(usize, TracegenVmSegmentExecutor) -> Result, map_err: impl Fn(ExecutionError) -> E, ) -> Result, E> { let mem_config = self.config.system().memory_config; @@ -209,7 +216,7 @@ where &self, exe: impl Into>, input: impl Into>, - ) -> Result>, ExecutionError> { + ) -> Result>, ExecutionError> { self.execute_and_then(exe, input, |_, seg| Ok(seg), |err| err) } @@ -223,7 +230,8 @@ where from_state: VmExecutorNextSegmentState, ) -> Result, ExecutionError> { let exe = exe.into(); - let mut segment = ExecutionSegment::new( + + let mut segment = TracegenVmSegmentExecutor::new( &self.config, exe.program.clone(), from_state.input, @@ -238,9 +246,15 @@ where if let Some(overridden_heights) = self.overridden_heights.as_ref() { segment.set_override_trace_heights(overridden_heights.clone()); } - let state = metrics_span("execute_time_ms", || segment.execute_from_pc(from_state.pc))?; + let mut vm_state = TracegenVmExecutionState::from_pc_and_memory_controller( + from_state.pc, + segment.chip_complex.memory_controller(), + ); + metrics_span("execute_time_ms", || { + segment.execute_from_state(&mut vm_state) + })?; - if state.is_terminated { + if vm_state.terminated { return Ok(VmExecutorOneSegmentResult { segment, next_state: None, @@ -252,12 +266,12 @@ where "multiple segments require to enable continuations" ); assert_eq!( - state.pc, + vm_state.pc, segment.chip_complex.connector_chip().boundary_states[1] .unwrap() .pc ); - let final_memory = mem::take(&mut segment.final_memory) + let final_memory = mem::take(&mut segment.control.final_memory) .expect("final memory should be set in continuations segment"); let streams = segment.chip_complex.take_streams(); #[cfg(feature = "bench-metrics")] @@ -267,7 +281,7 @@ where next_state: Some(VmExecutorNextSegmentState { memory: final_memory, input: streams, - pc: state.pc, + pc: vm_state.pc, #[cfg(feature = "bench-metrics")] metrics, }), @@ -290,7 +304,7 @@ where |err| err, )?; let last = last.expect("at least one segment must be executed"); - let final_memory = last.final_memory; + let final_memory = last.control.final_memory; let end_state = last.chip_complex.connector_chip().boundary_states[1].expect("end state must be set"); if end_state.is_terminate != 1 { @@ -350,7 +364,7 @@ where |seg_idx, mut seg| { // Note: this will only be Some on the last segment; otherwise it is // already moved into next segment state - final_memory = mem::take(&mut seg.final_memory); + final_memory = mem::take(&mut seg.control.final_memory); tracing::info_span!("trace_gen", segment = seg_idx) .in_scope(|| seg.generate_proof_input(committed_program.clone())) }, @@ -466,9 +480,8 @@ where &self, exe: VmExe, input: impl Into>, - ) -> Result, ExecutionError> { - let pc_start = exe.pc_start; - let mut segment = ExecutionSegment::new( + ) -> Result, ExecutionError> { + let mut segment = TracegenVmSegmentExecutor::new( &self.config, exe.program.clone(), input.into(), @@ -479,7 +492,13 @@ where if let Some(overridden_heights) = self.overridden_heights.as_ref() { segment.set_override_trace_heights(overridden_heights.clone()); } - metrics_span("execute_time_ms", || segment.execute_from_pc(pc_start))?; + let mut vm_state = TracegenVmExecutionState::from_pc_and_memory_controller( + exe.pc_start, + segment.chip_complex.memory_controller(), + ); + metrics_span("execute_time_ms", || { + segment.execute_from_state(&mut vm_state) + })?; Ok(segment) } } diff --git a/crates/vm/src/metrics/mod.rs b/crates/vm/src/metrics/mod.rs index 916e8251ac..1e24cafc80 100644 --- a/crates/vm/src/metrics/mod.rs +++ b/crates/vm/src/metrics/mod.rs @@ -2,13 +2,7 @@ use std::{collections::BTreeMap, mem}; use cycle_tracker::CycleTracker; use metrics::counter; -use openvm_instructions::{ - exe::{FnBound, FnBounds}, - VmOpcode, -}; -use openvm_stark_backend::p3_field::PrimeField32; - -use crate::arch::{ExecutionSegment, InstructionExecutor, VmConfig}; +use openvm_instructions::exe::{FnBound, FnBounds}; pub mod cycle_tracker; @@ -30,39 +24,8 @@ pub struct VmMetrics { pub(crate) current_trace_cells: Vec, } -impl ExecutionSegment -where - F: PrimeField32, - VC: VmConfig, -{ - /// Update metrics that increment per instruction - #[allow(unused_variables)] - pub fn update_instruction_metrics( - &mut self, - pc: u32, - opcode: VmOpcode, - dsl_instr: Option, - ) { - self.metrics.cycle_count += 1; - - if self.system_config().profiling { - let executor = self.chip_complex.inventory.get_executor(opcode).unwrap(); - let opcode_name = executor.get_opcode_name(opcode.as_usize()); - self.metrics.update_trace_cells( - &self.air_names, - self.current_trace_cells(), - opcode_name, - dsl_instr, - ); - - #[cfg(feature = "function-span")] - self.metrics.update_current_fn(pc); - } - } -} - impl VmMetrics { - fn update_trace_cells( + pub fn update_trace_cells( &mut self, air_names: &[String], now_trace_cells: Vec, diff --git a/crates/vm/tests/integration_test.rs b/crates/vm/tests/integration_test.rs index 1f96b0b8d8..76058d21b0 100644 --- a/crates/vm/tests/integration_test.rs +++ b/crates/vm/tests/integration_test.rs @@ -8,9 +8,9 @@ use std::{ use openvm_circuit::{ arch::{ hasher::{poseidon2::vm_poseidon2_hasher, Hasher}, - ChipId, ExecutionSegment, MemoryConfig, SingleSegmentVmExecutor, SystemConfig, - SystemTraceHeights, VirtualMachine, VmComplexTraceHeights, VmConfig, - VmInventoryTraceHeights, + ChipId, MemoryConfig, SingleSegmentVmExecutor, SystemConfig, SystemTraceHeights, + TracegenVmExecutionState, TracegenVmSegmentExecutor, VirtualMachine, VmComplexTraceHeights, + VmConfig, VmInventoryTraceHeights, }, system::{ memory::{MemoryTraceHeights, VolatileMemoryTraceHeights, CHUNK}, @@ -713,7 +713,7 @@ fn test_hint_load_1() { let program = Program::from_instructions(&instructions); - let mut segment = ExecutionSegment::new( + let mut segment = TracegenVmSegmentExecutor::new( &test_native_config(), program, vec![vec![F::ONE, F::TWO]].into(), @@ -721,7 +721,11 @@ fn test_hint_load_1() { vec![], Default::default(), ); - segment.execute_from_pc(0).unwrap(); + let mut vm_state = TracegenVmExecutionState::from_pc_and_memory_controller( + 0, + segment.chip_complex.memory_controller(), + ); + segment.execute_from_state(&mut vm_state).unwrap(); let streams = segment.chip_complex.take_streams(); assert!(streams.input_stream.is_empty()); assert_eq!(streams.hint_stream, VecDeque::from(vec![F::ZERO])); @@ -750,7 +754,7 @@ fn test_hint_load_2() { let program = Program::from_instructions(&instructions); - let mut segment = ExecutionSegment::new( + let mut segment = TracegenVmSegmentExecutor::new( &test_native_config(), program, vec![vec![F::ONE, F::TWO], vec![F::TWO, F::ONE]].into(), @@ -758,7 +762,11 @@ fn test_hint_load_2() { vec![], Default::default(), ); - segment.execute_from_pc(0).unwrap(); + let mut vm_state = TracegenVmExecutionState::from_pc_and_memory_controller( + 0, + segment.chip_complex.memory_controller(), + ); + segment.execute_from_state(&mut vm_state).unwrap(); assert_eq!( segment .chip_complex From e51d918339144420a7632ae0c21df19dc30c16d4 Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Thu, 17 Apr 2025 12:10:49 -0400 Subject: [PATCH 04/49] feat(new-execution): `TracingMemory` and removal of records/logs (#1584) - deleting `Vm{Adapter,Core}Chip` traits - no more records, directly use trace buffer - jal_lui chip is a demonstration of the new changes with working unit tests - changed unit tester - [x] need to add some dummy volatile memory to the tester to balance based on touched addresses --- crates/vm/src/arch/config.rs | 1 + crates/vm/src/arch/execution.rs | 29 +- crates/vm/src/arch/extensions.rs | 11 +- crates/vm/src/arch/integration_api.rs | 170 ++++++++++- crates/vm/src/arch/testing/memory/air.rs | 145 +++++++-- crates/vm/src/arch/testing/memory/mod.rs | 206 ++++++------- crates/vm/src/arch/testing/mod.rs | 96 +++--- crates/vm/src/system/memory/controller/mod.rs | 113 ++++--- crates/vm/src/system/memory/offline.rs | 6 +- .../system/memory/offline_checker/columns.rs | 16 +- crates/vm/src/system/memory/online.rs | 285 +++++++++++++----- crates/vm/src/system/memory/paged_vec.rs | 34 ++- crates/vm/src/system/mod.rs | 1 + crates/vm/src/system/native_adapter/mod.rs | 27 +- extensions/rv32im/circuit/src/adapters/mod.rs | 33 +- .../rv32im/circuit/src/adapters/rdwrite.rs | 211 +------------ extensions/rv32im/circuit/src/auipc/core.rs | 161 +++++----- extensions/rv32im/circuit/src/auipc/mod.rs | 7 +- extensions/rv32im/circuit/src/auipc/tests.rs | 62 ++-- .../rv32im/circuit/src/base_alu/tests.rs | 69 ++--- extensions/rv32im/circuit/src/extension.rs | 38 +-- .../rv32im/circuit/src/hintstore/mod.rs | 20 +- .../rv32im/circuit/src/hintstore/tests.rs | 28 +- extensions/rv32im/circuit/src/jal_lui/core.rs | 159 +++++----- extensions/rv32im/circuit/src/jal_lui/mod.rs | 7 +- .../rv32im/circuit/src/jal_lui/tests.rs | 67 ++-- extensions/rv32im/circuit/src/jalr/tests.rs | 6 +- .../circuit/src/load_sign_extend/tests.rs | 14 +- .../rv32im/circuit/src/loadstore/tests.rs | 14 +- extensions/rv32im/circuit/src/shift/tests.rs | 4 +- 30 files changed, 1154 insertions(+), 886 deletions(-) diff --git a/crates/vm/src/arch/config.rs b/crates/vm/src/arch/config.rs index 813e88ca1c..239f9eec7c 100644 --- a/crates/vm/src/arch/config.rs +++ b/crates/vm/src/arch/config.rs @@ -45,6 +45,7 @@ pub struct MemoryConfig { /// space `0` in memory. pub as_height: usize, /// The offset of the address space. Should be fixed to equal `1`. + // TODO[jpw]: remove this and make constant pub as_offset: u32, pub pointer_max_bits: usize, /// All timestamps must be in the range `[0, 2^clk_max_bits)`. Maximum allowed: 29. diff --git a/crates/vm/src/arch/execution.rs b/crates/vm/src/arch/execution.rs index 4edc88d355..a3aad69364 100644 --- a/crates/vm/src/arch/execution.rs +++ b/crates/vm/src/arch/execution.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, rc::Rc}; +use std::cell::RefCell; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{ @@ -66,8 +66,20 @@ pub enum ExecutionError { DidNotTerminate, #[error("program exit code {0}")] FailedWithExitCode(u32), + #[error("trace buffer out of bounds: requested {requested} but capacity is {capacity}")] + TraceBufferOutOfBounds { requested: usize, capacity: usize }, } +/// Global VM state accessible during instruction execution. +/// The state is generic in guest memory `MEM` and additional host state `CTX`. +/// The host state is execution context specific. +pub struct VmStateMut<'a, MEM, CTX> { + pub pc: &'a mut u32, + pub memory: &'a mut MEM, + pub ctx: &'a mut CTX, +} + +// TODO: old pub trait InstructionExecutor { /// Runtime execution of the instruction, if the instruction is owned by the /// current instance. May internally store records of this call for later trace generation. @@ -98,21 +110,6 @@ impl> InstructionExecutor for RefCell { } } -impl> InstructionExecutor for Rc> { - fn execute( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - prev_state: ExecutionState, - ) -> Result> { - self.borrow_mut().execute(memory, instruction, prev_state) - } - - fn get_opcode_name(&self, opcode: usize) -> String { - self.borrow().get_opcode_name(opcode) - } -} - #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Default, AlignedBorrow, Serialize, Deserialize)] pub struct ExecutionState { diff --git a/crates/vm/src/arch/extensions.rs b/crates/vm/src/arch/extensions.rs index 1c04910abf..c09af9c3f0 100644 --- a/crates/vm/src/arch/extensions.rs +++ b/crates/vm/src/arch/extensions.rs @@ -494,14 +494,14 @@ impl SystemBase { self.memory_controller.memory_bridge() } - pub fn offline_memory(&self) -> Arc>> { - self.memory_controller.offline_memory().clone() - } - pub fn execution_bus(&self) -> ExecutionBus { self.connector_chip.air.execution_bus } + pub fn offline_memory(&self) -> Arc>> { + self.memory_controller.offline_memory().clone() + } + /// Return trace heights of SystemBase. Usually this is for aggregation and not useful for /// regular users. pub fn get_system_trace_heights(&self) -> SystemTraceHeights { @@ -557,7 +557,6 @@ impl SystemComplex { ) }; let memory_bridge = memory_controller.memory_bridge(); - let offline_memory = memory_controller.offline_memory(); let program_chip = ProgramChip::new(program_bus); let connector_chip = VmConnectorChip::new( execution_bus, @@ -576,7 +575,7 @@ impl SystemComplex { config.num_public_values, config.max_constraint_degree as u32 - 1, ), - offline_memory, + memory_controller.offline_memory.clone(), ); inventory .add_executor(chip, [PublishOpcode::PUBLISH.global_opcode()]) diff --git a/crates/vm/src/arch/integration_api.rs b/crates/vm/src/arch/integration_api.rs index b1116d8c48..392d7e6ca8 100644 --- a/crates/vm/src/arch/integration_api.rs +++ b/crates/vm/src/arch/integration_api.rs @@ -12,17 +12,20 @@ use openvm_stark_backend::{ air_builders::{debug::DebugConstraintBuilder, symbolic::SymbolicRapBuilder}, config::{StarkGenericConfig, Val}, p3_air::{Air, AirBuilder, BaseAir}, - p3_field::{FieldAlgebra, PrimeField32}, + p3_field::{Field, FieldAlgebra, PrimeField32}, p3_matrix::{dense::RowMajorMatrix, Matrix}, p3_maybe_rayon::prelude::*, prover::types::AirProofInput, - rap::{get_air_name, BaseAirWithPublicValues, PartitionedBaseAir}, + rap::{get_air_name, AnyRap, BaseAirWithPublicValues, PartitionedBaseAir}, AirRef, Chip, ChipUsageGetter, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use super::{ExecutionState, InstructionExecutor, Result}; -use crate::system::memory::{MemoryController, OfflineMemory}; +use super::{ExecutionError, ExecutionState, InstructionExecutor, Result, VmStateMut}; +use crate::system::memory::{ + online::TracingMemory, MemoryAuxColsFactory, MemoryController, OfflineMemory, + SharedMemoryHelper, +}; /// The interface between primitive AIR and machine adapter AIR. pub trait VmAdapterInterface { @@ -37,6 +40,7 @@ pub trait VmAdapterInterface { type ProcessedInstruction; } +// TODO: delete /// The adapter owns all memory accesses and timestamp changes. /// The adapter AIR should also own `ExecutionBridge` and `MemoryBridge`. pub trait VmAdapterChip { @@ -111,6 +115,7 @@ pub trait VmAdapterAir: BaseAir { fn get_from_pc(&self, local: &[AB::Var]) -> AB::Var; } +// TODO: delete /// Trait to be implemented on primitive chip to integrate with the machine. pub trait VmCoreChip> { /// Minimum data that must be recorded to be able to generate trace for one row of @@ -183,6 +188,7 @@ where } } +// TODO: delete pub struct AdapterRuntimeContext> { /// Leave as `None` to allow the adapter to decide the `to_pc` automatically. pub to_pc: Option, @@ -207,6 +213,161 @@ pub struct AdapterAirContext> { pub instruction: I::ProcessedInstruction, } +/// Interface for trace generation when the state transition step of a single instruction +/// uses only one trace row. The trace row is provided as a mutable buffer during both +/// instruction execution and trace generation. +/// It is expected that no additional memory allocation is necessary and the trace buffer +/// is sufficient, with possible overwriting. +pub trait SingleTraceStep { + fn execute( + &mut self, + state: VmStateMut, + instruction: &Instruction, + row_slice: &mut [F], + ) -> Result<()>; + + /// Populates `row_slice`. This function will always be called after + /// [`SingleTraceStep::execute`], so the `row_slice` should already contain context necessary to + /// fill in the rest of the row. This function will be called for each row in the trace which is + /// being used, and all other rows in the trace will be filled with zeroes. + /// + /// The provided `row_slice` will have length equal to the width of the AIR. + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]); + + /// Returns a list of public values to publish. + fn generate_public_values(&self) -> Vec { + vec![] + } + + /// Displayable opcode name for logging and debugging purposes. + fn get_opcode_name(&self, opcode: usize) -> String; +} + +pub struct NewVmChipWrapper { + pub air: AIR, + pub inner: C, + pub trace_buffer: Vec, + width: usize, + buffer_idx: usize, + mem_helper: SharedMemoryHelper, +} + +impl NewVmChipWrapper +where + F: Field, + AIR: BaseAir, +{ + pub fn new(air: AIR, inner: C, height: usize, mem_helper: SharedMemoryHelper) -> Self { + assert!(height == 0 || height.is_power_of_two()); + let width = air.width(); + let trace_buffer = F::zero_vec(height * width); + Self { + air, + inner, + trace_buffer, + width, + buffer_idx: 0, + mem_helper, + } + } +} + +impl InstructionExecutor for NewVmChipWrapper +where + F: PrimeField32, + C: SingleTraceStep, // TODO: CTX? +{ + fn execute( + &mut self, + memory: &mut MemoryController, + instruction: &Instruction, + from_state: ExecutionState, + ) -> Result> { + let mut pc = from_state.pc; + let state = VmStateMut { + pc: &mut pc, + memory: &mut memory.memory, + ctx: &mut (), + }; + let start_idx = self.buffer_idx; + self.buffer_idx += self.width; + if self.buffer_idx > self.trace_buffer.len() { + return Err(ExecutionError::TraceBufferOutOfBounds { + requested: self.buffer_idx, + capacity: self.trace_buffer.len(), + }); + } + // SAFETY: bound checked above + let row_slice = unsafe { + self.trace_buffer + .get_unchecked_mut(start_idx..self.buffer_idx) + }; + self.inner.execute(state, instruction, row_slice)?; + Ok(ExecutionState { + pc, + timestamp: memory.memory.timestamp, + }) + } + + fn get_opcode_name(&self, opcode: usize) -> String { + self.inner.get_opcode_name(opcode) + } +} + +// Note[jpw]: the statement we want is: +// - `Air` is an `Air` for all `AB: AirBuilder`s needed by stark-backend +// which is equivalent to saying it implements AirRef +// The where clauses to achieve this statement is unfortunately really verbose. +impl Chip for NewVmChipWrapper, AIR, C> +where + SC: StarkGenericConfig, + Val: PrimeField32, + C: SingleTraceStep, ()> + Send + Sync, + AIR: Clone + AnyRap + 'static, +{ + fn air(&self) -> AirRef { + Arc::new(self.air.clone()) + } + + fn generate_air_proof_input(mut self) -> AirProofInput { + assert_eq!(self.buffer_idx % self.width, 0); + let rows_used = self.current_trace_height(); + let height = next_power_of_two_or_zero(rows_used); + // This should be automatic since trace_buffer's height is a power of two: + assert!(height.checked_mul(self.width).unwrap() <= self.trace_buffer.len()); + self.trace_buffer.truncate(height * self.width); + let mem_helper = self.mem_helper.as_borrowed(); + // This zip only goes through used rows. + // TODO: check if zero-init assumption changes + // The padding(=dummy) rows between rows_used..height are ASSUMED to be filled with zeros. + self.trace_buffer[..rows_used * self.width] + .par_chunks_exact_mut(self.width) + .for_each(|row_slice| { + self.inner.fill_trace_row(&mem_helper, row_slice); + }); + drop(self.mem_helper); + let trace = RowMajorMatrix::new(self.trace_buffer, self.width); + // self.inner.finalize(&mut trace, num_records); + + AirProofInput::simple(trace, self.inner.generate_public_values()) + } +} + +impl ChipUsageGetter for NewVmChipWrapper +where + C: Sync, +{ + fn air_name(&self) -> String { + get_air_name(&self.air) + } + fn current_trace_height(&self) -> usize { + self.buffer_idx / self.width + } + fn trace_width(&self) -> usize { + self.width + } +} + pub struct VmChipWrapper, C: VmCoreChip> { pub adapter: A, pub core: C, @@ -341,6 +502,7 @@ where } } +#[derive(Clone, Copy, derive_new::new)] pub struct VmAirWrapper { pub adapter: A, pub core: C, diff --git a/crates/vm/src/arch/testing/memory/air.rs b/crates/vm/src/arch/testing/memory/air.rs index 8a394c0cce..90c1b4ce49 100644 --- a/crates/vm/src/arch/testing/memory/air.rs +++ b/crates/vm/src/arch/testing/memory/air.rs @@ -1,46 +1,155 @@ -use std::{borrow::Borrow, mem::size_of}; +use std::{mem::size_of, sync::Arc}; -use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_stark_backend::{ + config::{StarkGenericConfig, Val}, interaction::InteractionBuilder, p3_air::{Air, BaseAir}, - p3_matrix::Matrix, + p3_field::{FieldAlgebra, PrimeField32}, + p3_matrix::{dense::RowMajorMatrix, Matrix}, + prover::types::AirProofInput, rap::{BaseAirWithPublicValues, PartitionedBaseAir}, + AirRef, Chip, ChipUsageGetter, }; use crate::system::memory::{offline_checker::MemoryBus, MemoryAddress}; -#[derive(Clone, Copy, Debug, AlignedBorrow, derive_new::new)] #[repr(C)] -pub struct DummyMemoryInteractionCols { - pub address: MemoryAddress, - pub data: [T; BLOCK_SIZE], - pub timestamp: T, +#[derive(Clone, Copy)] +pub struct DummyMemoryInteractionColsRef<'a, T> { + pub address: MemoryAddress<&'a T, &'a T>, + pub data: &'a [T], + pub timestamp: &'a T, /// The send frequency. Send corresponds to write. To read, set to negative. - pub count: T, + pub count: &'a T, +} + +#[repr(C)] +pub struct DummyMemoryInteractionColsMut<'a, T> { + pub address: MemoryAddress<&'a mut T, &'a mut T>, + pub data: &'a mut [T], + pub timestamp: &'a mut T, + /// The send frequency. Send corresponds to write. To read, set to negative. + pub count: &'a mut T, +} + +impl<'a, T> DummyMemoryInteractionColsRef<'a, T> { + pub fn from_slice(slice: &'a [T]) -> Self { + let (address, slice) = slice.split_at(size_of::>()); + let (count, slice) = slice.split_last().unwrap(); + let (timestamp, data) = slice.split_last().unwrap(); + Self { + address: MemoryAddress::new(&address[0], &address[1]), + data, + timestamp, + count, + } + } +} + +impl<'a, T> DummyMemoryInteractionColsMut<'a, T> { + pub fn from_mut_slice(slice: &'a mut [T]) -> Self { + let (addr_space, slice) = slice.split_first_mut().unwrap(); + let (ptr, slice) = slice.split_first_mut().unwrap(); + let (count, slice) = slice.split_last_mut().unwrap(); + let (timestamp, data) = slice.split_last_mut().unwrap(); + Self { + address: MemoryAddress::new(addr_space, ptr), + data, + timestamp, + count, + } + } } #[derive(Clone, Copy, Debug, derive_new::new)] -pub struct MemoryDummyAir { +pub struct MemoryDummyAir { pub bus: MemoryBus, + pub block_size: usize, } -impl BaseAirWithPublicValues for MemoryDummyAir {} -impl PartitionedBaseAir for MemoryDummyAir {} -impl BaseAir for MemoryDummyAir { +impl BaseAirWithPublicValues for MemoryDummyAir {} +impl PartitionedBaseAir for MemoryDummyAir {} +impl BaseAir for MemoryDummyAir { fn width(&self) -> usize { - size_of::>() + self.block_size + 4 } } -impl Air for MemoryDummyAir { +impl Air for MemoryDummyAir { fn eval(&self, builder: &mut AB) { let main = builder.main(); let local = main.row_slice(0); - let local: &DummyMemoryInteractionCols = (*local).borrow(); + let local = DummyMemoryInteractionColsRef::from_slice(&local); self.bus - .send(local.address, local.data.to_vec(), local.timestamp) - .eval(builder, local.count); + .send( + MemoryAddress::new(*local.address.address_space, *local.address.pointer), + local.data.to_vec(), + *local.timestamp, + ) + .eval(builder, *local.count); + } +} + +#[derive(Clone)] +pub struct MemoryDummyChip { + pub air: MemoryDummyAir, + pub trace: Vec, +} + +impl MemoryDummyChip { + pub fn new(air: MemoryDummyAir) -> Self { + Self { + air, + trace: Vec::new(), + } + } +} + +impl MemoryDummyChip { + pub fn send(&mut self, addr_space: u32, ptr: u32, data: &[F], timestamp: u32) { + self.push(addr_space, ptr, data, timestamp, F::ONE); + } + + pub fn receive(&mut self, addr_space: u32, ptr: u32, data: &[F], timestamp: u32) { + self.push(addr_space, ptr, data, timestamp, F::NEG_ONE); + } + + pub fn push(&mut self, addr_space: u32, ptr: u32, data: &[F], timestamp: u32, count: F) { + assert_eq!(data.len(), self.air.block_size); + self.trace.push(F::from_canonical_u32(addr_space)); + self.trace.push(F::from_canonical_u32(ptr)); + self.trace.extend_from_slice(data); + self.trace.push(F::from_canonical_u32(timestamp)); + self.trace.push(count); + } +} + +impl Chip for MemoryDummyChip> +where + Val: PrimeField32, +{ + fn air(&self) -> AirRef { + Arc::new(self.air) + } + + fn generate_air_proof_input(mut self) -> AirProofInput { + let height = self.current_trace_height().next_power_of_two(); + let width = self.trace_width(); + self.trace.resize(height * width, Val::::ZERO); + + AirProofInput::simple_no_pis(RowMajorMatrix::new(self.trace, width)) + } +} + +impl ChipUsageGetter for MemoryDummyChip { + fn air_name(&self) -> String { + format!("MemoryDummyAir<{}>", self.air.block_size) + } + fn current_trace_height(&self) -> usize { + self.trace.len() / self.trace_width() + } + fn trace_width(&self) -> usize { + BaseAir::::width(&self.air) } } diff --git a/crates/vm/src/arch/testing/memory/mod.rs b/crates/vm/src/arch/testing/memory/mod.rs index ae1136bc7f..23b9c0e712 100644 --- a/crates/vm/src/arch/testing/memory/mod.rs +++ b/crates/vm/src/arch/testing/memory/mod.rs @@ -1,140 +1,126 @@ -use std::{array::from_fn, borrow::BorrowMut as _, cell::RefCell, mem::size_of, rc::Rc, sync::Arc}; +use std::collections::HashMap; -use air::{DummyMemoryInteractionCols, MemoryDummyAir}; +use air::{MemoryDummyAir, MemoryDummyChip}; use openvm_circuit::system::memory::MemoryController; -use openvm_stark_backend::{ - config::{StarkGenericConfig, Val}, - p3_field::{FieldAlgebra, PrimeField32}, - p3_matrix::dense::RowMajorMatrix, - prover::types::AirProofInput, - AirRef, Chip, ChipUsageGetter, -}; -use rand::{seq::SliceRandom, Rng}; +use openvm_stark_backend::p3_field::PrimeField32; +use rand::Rng; -use crate::system::memory::{offline_checker::MemoryBus, MemoryAddress, RecordId}; +use crate::system::memory::INITIAL_TIMESTAMP; pub mod air; -const WORD_SIZE: usize = 1; - /// A dummy testing chip that will add unconstrained messages into the [MemoryBus]. /// Stores a log of raw messages to send/receive to the [MemoryBus]. /// /// It will create a [air::MemoryDummyAir] to add messages to MemoryBus. pub struct MemoryTester { - pub bus: MemoryBus, - pub controller: Rc>>, - /// Log of record ids - pub records: Vec, + /// Map from `block_size` to [MemoryDummyChip] of that block size + pub chip_for_block: HashMap>, + // TODO: make this just TracedMemory? + pub controller: MemoryController, } impl MemoryTester { - pub fn new(controller: Rc>>) -> Self { - let bus = controller.borrow().memory_bus; + pub fn new(controller: MemoryController) -> Self { + let bus = controller.memory_bus; + let mut chip_for_block = HashMap::new(); + for log_block_size in 0..6 { + let block_size = 1 << log_block_size; + let chip = MemoryDummyChip::new(MemoryDummyAir::new(bus, block_size)); + chip_for_block.insert(block_size, chip); + } Self { - bus, + chip_for_block, controller, - records: Vec::new(), } } - /// Returns the cell value at the current timestamp according to `MemoryController`. - pub fn read_cell(&mut self, address_space: usize, pointer: usize) -> F { - let [addr_space, pointer] = [address_space, pointer].map(F::from_canonical_usize); - // core::BorrowMut confuses compiler - let (record_id, value) = - RefCell::borrow_mut(&self.controller).read_cell(addr_space, pointer); - self.records.push(record_id); - value - } - - pub fn write_cell(&mut self, address_space: usize, pointer: usize, value: F) { - let [addr_space, pointer] = [address_space, pointer].map(F::from_canonical_usize); - let (record_id, _) = - RefCell::borrow_mut(&self.controller).write_cell(addr_space, pointer, value); - self.records.push(record_id); - } - - pub fn read(&mut self, address_space: usize, pointer: usize) -> [F; N] { - from_fn(|i| self.read_cell(address_space, pointer + i)) - } - - pub fn write( - &mut self, - address_space: usize, - mut pointer: usize, - cells: [F; N], - ) { - for cell in cells { - self.write_cell(address_space, pointer, cell); - pointer += 1; - } - } -} - -impl Chip for MemoryTester> -where - Val: PrimeField32, -{ - fn air(&self) -> AirRef { - Arc::new(MemoryDummyAir::::new(self.bus)) - } - - fn generate_air_proof_input(self) -> AirProofInput { - let offline_memory = self.controller.borrow().offline_memory(); - let offline_memory = offline_memory.lock().unwrap(); - - let height = self.records.len().next_power_of_two(); - let width = self.trace_width(); - let mut values = Val::::zero_vec(2 * height * width); - // This zip only goes through records. The padding rows between records.len()..height - // are filled with zeros - in particular count = 0 so nothing is added to bus. - for (row, id) in values.chunks_mut(2 * width).zip(self.records) { - let (first, second) = row.split_at_mut(width); - let row: &mut DummyMemoryInteractionCols, WORD_SIZE> = first.borrow_mut(); - let record = offline_memory.record_by_id(id); - row.address = MemoryAddress { - address_space: record.address_space, - pointer: record.pointer, - }; - row.data - .copy_from_slice(record.prev_data_slice().unwrap_or(record.data_slice())); - row.timestamp = Val::::from_canonical_u32(record.prev_timestamp); - row.count = -Val::::ONE; - - let row: &mut DummyMemoryInteractionCols, WORD_SIZE> = second.borrow_mut(); - row.address = MemoryAddress { - address_space: record.address_space, - pointer: record.pointer, + // TODO: change interface by implementing GuestMemory trait after everything works + pub fn read(&mut self, addr_space: usize, ptr: usize) -> [F; N] { + let controller = &mut self.controller; + let t = controller.memory.timestamp(); + // TODO: hack + let (t_prev, data) = if addr_space <= 2 { + let (t_prev, data) = unsafe { + controller + .memory + .read::(addr_space as u32, ptr as u32) }; - row.data.copy_from_slice(record.data_slice()); - row.timestamp = Val::::from_canonical_u32(record.timestamp); - row.count = Val::::ONE; - } - AirProofInput::simple_no_pis(RowMajorMatrix::new(values, width)) + (t_prev, data.map(F::from_canonical_u8)) + } else { + unsafe { + controller + .memory + .read::(addr_space as u32, ptr as u32) + } + }; + self.chip_for_block.get_mut(&N).unwrap().receive( + addr_space as u32, + ptr as u32, + &data, + t_prev, + ); + self.chip_for_block + .get_mut(&N) + .unwrap() + .send(addr_space as u32, ptr as u32, &data, t); + + data } -} -impl ChipUsageGetter for MemoryTester { - fn air_name(&self) -> String { - "MemoryDummyAir".to_string() - } - fn current_trace_height(&self) -> usize { - self.records.len() + // TODO: see read + pub fn write(&mut self, addr_space: usize, ptr: usize, data: [F; N]) { + let controller = &mut self.controller; + let t = controller.memory.timestamp(); + // TODO: hack + let (t_prev, data_prev) = if addr_space <= 2 { + let (t_prev, data_prev) = unsafe { + controller.memory.write::( + addr_space as u32, + ptr as u32, + &data.map(|x| x.as_canonical_u32() as u8), + ) + }; + (t_prev, data_prev.map(F::from_canonical_u8)) + } else { + unsafe { + controller + .memory + .write::(addr_space as u32, ptr as u32, &data) + } + }; + self.chip_for_block.get_mut(&N).unwrap().receive( + addr_space as u32, + ptr as u32, + &data_prev, + t_prev, + ); + self.chip_for_block + .get_mut(&N) + .unwrap() + .send(addr_space as u32, ptr as u32, &data, t); } - fn trace_width(&self) -> usize { - size_of::>() + /// Fills in dummy memory chips to balance the memory bus with the initial and final boundary + /// messages. Taking a volatile memory approach: any touched address will be initialized with + /// zero. + pub fn finalize(&mut self) { + let memory = &self.controller.memory; + // TODO[jpw]: assuming the last block size matches initial block size, after adding + // adapters, fix this + for ((addr_space, ptr), metadata) in memory.touched_blocks() { + let block_size = metadata.block_size as usize; + let chip = self.chip_for_block.get_mut(&block_size).unwrap(); + let mut data = F::zero_vec(block_size); + chip.send(addr_space, ptr, &data, INITIAL_TIMESTAMP); + for (i, v) in data.iter_mut().enumerate() { + *v = memory.data().get_f(addr_space, ptr + i as u32); + } + chip.receive(addr_space, ptr, &data, metadata.timestamp); + } } } -pub fn gen_address_space(rng: &mut R) -> usize -where - R: Rng + ?Sized, -{ - *[1, 2].choose(rng).unwrap() -} - pub fn gen_pointer(rng: &mut R, len: usize) -> usize where R: Rng + ?Sized, diff --git a/crates/vm/src/arch/testing/mod.rs b/crates/vm/src/arch/testing/mod.rs index 44b19177be..6e12e1c168 100644 --- a/crates/vm/src/arch/testing/mod.rs +++ b/crates/vm/src/arch/testing/mod.rs @@ -1,9 +1,4 @@ -use std::{ - cell::RefCell, - iter::zip, - rc::Rc, - sync::{Arc, Mutex}, -}; +use std::sync::{Arc, Mutex}; use openvm_circuit_primitives::var_range::{ SharedVariableRangeCheckerChip, VariableRangeCheckerBus, @@ -32,15 +27,14 @@ use program::ProgramTester; use rand::{rngs::StdRng, RngCore, SeedableRng}; use tracing::Level; -use super::{ExecutionBus, InstructionExecutor, SystemPort}; +use super::{ExecutionBridge, ExecutionBus, InstructionExecutor, SystemPort}; use crate::{ arch::{ExecutionState, MemoryConfig}, system::{ memory::{ offline_checker::{MemoryBridge, MemoryBus}, - MemoryController, OfflineMemory, + MemoryController, OfflineMemory, SharedMemoryHelper, }, - poseidon2::Poseidon2PeripheryChip, program::ProgramBus, }, }; @@ -76,7 +70,7 @@ pub struct VmChipTestBuilder { impl VmChipTestBuilder { pub fn new( - memory_controller: Rc>>, + memory_controller: MemoryController, execution_bus: ExecutionBus, program_bus: ProgramBus, rng: StdRng, @@ -110,16 +104,12 @@ impl VmChipTestBuilder { ) { let initial_state = ExecutionState { pc: initial_pc, - timestamp: self.memory.controller.borrow().timestamp(), + timestamp: self.memory.controller.timestamp(), }; tracing::debug!(?initial_state.timestamp); let final_state = executor - .execute( - &mut *self.memory.controller.borrow_mut(), - instruction, - initial_state, - ) + .execute(&mut self.memory.controller, instruction, initial_state) .expect("Expected the execution not to fail"); self.program.execute(instruction, &initial_state); @@ -130,14 +120,6 @@ impl VmChipTestBuilder { self.rng.next_u32() % (1 << (F::bits() - 2)) } - pub fn read_cell(&mut self, address_space: usize, pointer: usize) -> F { - self.memory.read_cell(address_space, pointer) - } - - pub fn write_cell(&mut self, address_space: usize, pointer: usize, value: F) { - self.memory.write_cell(address_space, pointer, value); - } - pub fn read(&mut self, address_space: usize, pointer: usize) -> [F; N] { self.memory.read(address_space, pointer) } @@ -176,6 +158,10 @@ impl VmChipTestBuilder { } } + pub fn execution_bridge(&self) -> ExecutionBridge { + ExecutionBridge::new(self.execution.bus, self.program.bus) + } + pub fn execution_bus(&self) -> ExecutionBus { self.execution.bus } @@ -185,27 +171,32 @@ impl VmChipTestBuilder { } pub fn memory_bus(&self) -> MemoryBus { - self.memory.bus + self.memory.controller.memory_bus } - pub fn memory_controller(&self) -> Rc>> { - self.memory.controller.clone() + pub fn memory_controller(&self) -> &MemoryController { + &self.memory.controller } pub fn range_checker(&self) -> SharedVariableRangeCheckerChip { - self.memory.controller.borrow().range_checker.clone() + self.memory.controller.range_checker.clone() } pub fn memory_bridge(&self) -> MemoryBridge { - self.memory.controller.borrow().memory_bridge() + self.memory.controller.memory_bridge() } - pub fn address_bits(&self) -> usize { - self.memory.controller.borrow().mem_config.pointer_max_bits + pub fn memory_helper(&self) -> SharedMemoryHelper { + self.memory.controller.helper() } + // TODO: delete pub fn offline_memory_mutex_arc(&self) -> Arc>> { - self.memory_controller().borrow().offline_memory().clone() + self.memory.controller.offline_memory().clone() + } + + pub fn address_bits(&self) -> usize { + self.memory.controller.mem_config.pointer_max_bits } pub fn get_default_register(&mut self, increment: usize) -> usize { @@ -247,10 +238,6 @@ type TestSC = BabyBearBlake3Config; impl VmChipTestBuilder { pub fn build(self) -> VmChipTester { - self.memory - .controller - .borrow_mut() - .finalize(None::<&mut Poseidon2PeripheryChip>); let tester = VmChipTester { memory: Some(self.memory), ..Default::default() @@ -259,10 +246,6 @@ impl VmChipTestBuilder { tester.load(self.program) } pub fn build_babybear_poseidon2(self) -> VmChipTester { - self.memory - .controller - .borrow_mut() - .finalize(None::<&mut Poseidon2PeripheryChip>); let tester = VmChipTester { memory: Some(self.memory), ..Default::default() @@ -285,7 +268,7 @@ impl Default for VmChipTestBuilder { range_checker, ); Self { - memory: MemoryTester::new(Rc::new(RefCell::new(memory_controller))), + memory: MemoryTester::new(memory_controller), execution: ExecutionTester::new(ExecutionBus::new(EXECUTION_BUS)), program: ProgramTester::new(ProgramBus::new(READ_INSTRUCTION_BUS)), rng: StdRng::seed_from_u64(0), @@ -325,19 +308,26 @@ where } pub fn finalize(mut self) -> Self { - if let Some(memory_tester) = self.memory.take() { - let memory_controller = memory_tester.controller.clone(); - let range_checker = memory_controller.borrow().range_checker.clone(); - self = self.load(memory_tester); // dummy memory interactions + if let Some(mut memory_tester) = self.memory.take() { + // Balance memory boundaries + memory_tester.finalize(); + let memory_controller = memory_tester.controller; + let range_checker = memory_controller.range_checker.clone(); + drop(memory_controller); + // dummy memory interactions: + for mem_chip in memory_tester.chip_for_block.into_values() { + self = self.load(mem_chip); + } { - let airs = memory_controller.borrow().airs(); - let air_proof_inputs = Rc::try_unwrap(memory_controller) - .unwrap_or_else(|_| panic!("Memory controller was not dropped")) - .into_inner() - .generate_air_proof_inputs(); - self.air_proof_inputs.extend( - zip(airs, air_proof_inputs).filter(|(_, input)| input.main_trace_height() > 0), - ); + // todo: boundary and adapter stuff + // let airs = memory_controller.borrow().airs(); + // let air_proof_inputs = Rc::try_unwrap(memory_controller) + // .unwrap_or_else(|_| panic!("Memory controller was not dropped")) + // .into_inner() + // .generate_air_proof_inputs(); + // self.air_proof_inputs.extend( + // zip(airs, air_proof_inputs).filter(|(_, input)| input.main_trace_height() > + // 0), ); } self = self.load(range_checker); // this must be last because other trace generation // mutates its state diff --git a/crates/vm/src/system/memory/controller/mod.rs b/crates/vm/src/system/memory/controller/mod.rs index 8b4db5b32c..91ae38c7b0 100644 --- a/crates/vm/src/system/memory/controller/mod.rs +++ b/crates/vm/src/system/memory/controller/mod.rs @@ -3,6 +3,7 @@ use std::{ collections::BTreeMap, iter, marker::PhantomData, + ops::Deref, sync::{Arc, Mutex}, }; @@ -11,7 +12,9 @@ use openvm_circuit_primitives::{ assert_less_than::{AssertLtSubAir, LessThanAuxCols}, is_zero::IsZeroSubAir, utils::next_power_of_two_or_zero, - var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus}, + var_range::{ + SharedVariableRangeCheckerChip, VariableRangeCheckerBus, VariableRangeCheckerChip, + }, TraceSubRowGenerator, }; use openvm_stark_backend::{ @@ -28,6 +31,7 @@ use serde::{Deserialize, Serialize}; use self::interface::MemoryInterface; use super::{ + online::GuestMemory, paged_vec::{AddressMap, PAGE_SIZE}, volatile::VolatileBoundaryChip, }; @@ -42,7 +46,7 @@ use crate::{ MemoryBaseAuxCols, MemoryBridge, MemoryBus, MemoryReadAuxCols, MemoryReadOrImmediateAuxCols, MemoryWriteAuxCols, AUX_LEN, }, - online::{Memory, MemoryLogEntry}, + online::{MemoryLogEntry, TracingMemory}, persistent::PersistentBoundaryChip, tree::MemoryNode, }, @@ -97,9 +101,9 @@ pub struct MemoryController { // Store separately to avoid smart pointer reference each time range_checker_bus: VariableRangeCheckerBus, // addr_space -> Memory data structure - memory: Memory, + pub(crate) memory: TracingMemory, /// A reference to the `OfflineMemory`. Will be populated after `finalize()`. - offline_memory: Arc>>, + pub offline_memory: Arc>>, pub access_adapters: AccessAdapterInventory, // Filled during finalization. final_state: Option>, @@ -242,7 +246,7 @@ impl MemoryController { range_checker.clone(), ), }, - memory: Memory::new(&mem_config), + memory: TracingMemory::new(&mem_config), offline_memory: Arc::new(Mutex::new(OfflineMemory::new( initial_memory, 1, @@ -293,7 +297,8 @@ impl MemoryController { memory_bus, mem_config, interface_chip, - memory: Memory::new(&mem_config), // it is expected that the memory will be set later + memory: TracingMemory::new(&mem_config), /* it is expected that the memory will be + * set later */ offline_memory: Arc::new(Mutex::new(OfflineMemory::new( AddressMap::from_mem_config(&mem_config), CHUNK, @@ -350,7 +355,7 @@ impl MemoryController { let mut offline_memory = self.offline_memory.lock().unwrap(); offline_memory.set_initial_memory(memory.clone(), self.mem_config); - self.memory = Memory::from_image(memory.clone(), self.mem_config.access_capacity); + self.memory = TracingMemory::from_image(memory.clone(), self.mem_config.access_capacity); match &mut self.interface_chip { MemoryInterface::Volatile { .. } => { @@ -394,10 +399,11 @@ impl MemoryController { address_space == F::ZERO || ptr_u32 < (1 << self.mem_config.pointer_max_bits), "memory out of bounds: {ptr_u32:?}", ); + todo!() + // let (record_id, values) = unsafe { self.memory.read::(address_space_u32, ptr_u32) + // }; - let (record_id, values) = unsafe { self.memory.read::(address_space_u32, ptr_u32) }; - - (record_id, values) + // (record_id, values) } /// Reads a word directly from memory without updating internal state. @@ -413,7 +419,8 @@ impl MemoryController { pub fn unsafe_read(&self, addr_space: F, ptr: F) -> [T; N] { let addr_space = addr_space.as_canonical_u32(); let ptr = ptr.as_canonical_u32(); - unsafe { array::from_fn(|i| self.memory.get::(addr_space, ptr + i as u32)) } + todo!() + // unsafe { array::from_fn(|i| self.memory.get::(addr_space, ptr + i as u32)) } } /// Writes `data` to the given cell. @@ -438,13 +445,23 @@ impl MemoryController { "memory out of bounds: {ptr_u32:?}", ); - unsafe { self.memory.write::(address_space_u32, ptr_u32, data) } + todo!() + // unsafe { self.memory.write::(address_space_u32, ptr_u32, data) } + } + + pub fn helper(&self) -> SharedMemoryHelper { + let range_bus = self.range_checker.bus(); + SharedMemoryHelper { + range_checker: self.range_checker.clone(), + timestamp_lt_air: AssertLtSubAir::new(range_bus, self.mem_config.clk_max_bits), + _marker: Default::default(), + } } pub fn aux_cols_factory(&self) -> MemoryAuxColsFactory { let range_bus = self.range_checker.bus(); MemoryAuxColsFactory { - range_checker: self.range_checker.clone(), + range_checker: self.range_checker.as_ref(), timestamp_lt_air: AssertLtSubAir::new(range_bus, self.mem_config.clk_max_bits), _marker: Default::default(), } @@ -462,6 +479,10 @@ impl MemoryController { self.memory.timestamp() } + pub fn offline_memory(&self) -> &Arc>> { + &self.offline_memory + } + fn replay_access_log(&mut self) { unimplemented!(); // let log = mem::take(&mut self.memory.log); @@ -703,36 +724,26 @@ impl MemoryController { ret.extend(self.access_adapters.get_cells()); ret } - - /// Returns a reference to the offline memory. - /// - /// Until `finalize` is called, the `OfflineMemory` does not contain useful state, and should - /// therefore not be used by any chip during execution. However, to obtain a reference to the - /// offline memory that will be useful in trace generation, a chip can call `offline_memory()` - /// and store the returned reference for later use. - pub fn offline_memory(&self) -> Arc>> { - self.offline_memory.clone() - } - pub fn get_memory_logs(&self) -> &Vec> { - &self.memory.log - } - pub fn set_memory_logs(&mut self, logs: Vec>) { - self.memory.log = logs; - } - pub fn take_memory_logs(&mut self) -> Vec> { - std::mem::take(&mut self.memory.log) - } } -pub struct MemoryAuxColsFactory { +/// Owned version of [MemoryAuxColsFactory]. +pub struct SharedMemoryHelper { pub(crate) range_checker: SharedVariableRangeCheckerChip, pub(crate) timestamp_lt_air: AssertLtSubAir, pub(crate) _marker: PhantomData, } +/// A helper for generating trace values in auxiliary memory columns related to the offline memory +/// argument. +pub struct MemoryAuxColsFactory<'a, T> { + pub(crate) range_checker: &'a VariableRangeCheckerChip, + pub(crate) timestamp_lt_air: AssertLtSubAir, + pub(crate) _marker: PhantomData, +} + // NOTE[jpw]: The `make_*_aux_cols` functions should be thread-safe so they can be used in // parallelized trace generation. -impl MemoryAuxColsFactory { +impl MemoryAuxColsFactory<'_, F> { pub fn generate_read_aux(&self, read: &MemoryRecord, buffer: &mut MemoryReadAuxCols) { assert!( !read.address_space.is_zero(), @@ -741,6 +752,7 @@ impl MemoryAuxColsFactory { self.generate_base_aux(read, &mut buffer.base); } + // TODO: revisit deleting this pub fn generate_read_or_immediate_aux( &self, read: &MemoryRecord, @@ -753,6 +765,7 @@ impl MemoryAuxColsFactory { self.generate_base_aux(read, &mut buffer.base); } + // TODO: revisit deleting this pub fn generate_write_aux( &self, write: &MemoryRecord, @@ -764,6 +777,7 @@ impl MemoryAuxColsFactory { self.generate_base_aux(write, &mut buffer.base); } + // TODO: revisit deleting this pub fn generate_base_aux(&self, record: &MemoryRecord, buffer: &mut MemoryBaseAuxCols) { buffer.prev_timestamp = F::from_canonical_u32(record.prev_timestamp); self.generate_timestamp_lt( @@ -773,19 +787,29 @@ impl MemoryAuxColsFactory { ); } + /// Fill the trace assuming `prev_timestamp` is already provided in `buffer`. + pub fn fill_from_prev(&self, timestamp: u32, buffer: &mut MemoryBaseAuxCols) { + let prev_timestamp = buffer.prev_timestamp.as_canonical_u32(); + self.generate_timestamp_lt(prev_timestamp, timestamp, &mut buffer.timestamp_lt_aux); + } + fn generate_timestamp_lt( &self, prev_timestamp: u32, timestamp: u32, buffer: &mut LessThanAuxCols, ) { - debug_assert!(prev_timestamp < timestamp); + debug_assert!( + prev_timestamp < timestamp, + "prev_timestamp {prev_timestamp} >= timestamp {timestamp}" + ); self.timestamp_lt_air.generate_subrow( - (self.range_checker.as_ref(), prev_timestamp, timestamp), + (self.range_checker, prev_timestamp, timestamp), &mut buffer.lower_decomp, ); } + // TODO: revisit deleting this /// In general, prefer `generate_read_aux` which writes in-place rather than this function. pub fn make_read_aux_cols(&self, read: &MemoryRecord) -> MemoryReadAuxCols { assert!( @@ -798,6 +822,7 @@ impl MemoryAuxColsFactory { ) } + // TODO: revisit deleting this /// In general, prefer `generate_write_aux` which writes in-place rather than this function. pub fn make_write_aux_cols( &self, @@ -818,14 +843,22 @@ impl MemoryAuxColsFactory { ) -> LessThanAuxCols { debug_assert!(prev_timestamp < timestamp); let mut decomp = [F::ZERO; AUX_LEN]; - self.timestamp_lt_air.generate_subrow( - (self.range_checker.as_ref(), prev_timestamp, timestamp), - &mut decomp, - ); + self.timestamp_lt_air + .generate_subrow((self.range_checker, prev_timestamp, timestamp), &mut decomp); LessThanAuxCols::new(decomp) } } +impl SharedMemoryHelper { + pub fn as_borrowed(&self) -> MemoryAuxColsFactory<'_, T> { + MemoryAuxColsFactory { + range_checker: self.range_checker.as_ref(), + timestamp_lt_air: self.timestamp_lt_air, + _marker: PhantomData, + } + } +} + #[cfg(test)] mod tests { use openvm_circuit_primitives::var_range::{ diff --git a/crates/vm/src/system/memory/offline.rs b/crates/vm/src/system/memory/offline.rs index 93423a3a48..0be8d5214b 100644 --- a/crates/vm/src/system/memory/offline.rs +++ b/crates/vm/src/system/memory/offline.rs @@ -6,7 +6,7 @@ use openvm_circuit_primitives::{ use openvm_stark_backend::p3_field::PrimeField32; use rustc_hash::FxHashSet; -use super::{AddressMap, PagedVec, PAGE_SIZE}; +use super::{AddressMap, PagedVec, SharedMemoryHelper, PAGE_SIZE}; use crate::{ arch::MemoryConfig, system::memory::{ @@ -344,7 +344,7 @@ impl OfflineMemory { query: u32, records: &mut AccessAdapterInventory, ) { - let lim = (self.data[(address_space - self.as_offset) as usize].memory_size()) as u32; + let lim = (self.data[(address_space - self.as_offset) as usize].bytes_capacity()) as u32; if query == lim { return; } @@ -520,7 +520,7 @@ impl OfflineMemory { pub fn aux_cols_factory(&self) -> MemoryAuxColsFactory { let range_bus = self.range_checker.bus(); MemoryAuxColsFactory { - range_checker: self.range_checker.clone(), + range_checker: self.range_checker.as_ref(), timestamp_lt_air: AssertLtSubAir::new(range_bus, self.timestamp_max_bits), _marker: Default::default(), } diff --git a/crates/vm/src/system/memory/offline_checker/columns.rs b/crates/vm/src/system/memory/offline_checker/columns.rs index 5a27b3e433..c2db62301d 100644 --- a/crates/vm/src/system/memory/offline_checker/columns.rs +++ b/crates/vm/src/system/memory/offline_checker/columns.rs @@ -1,6 +1,8 @@ //! Defines auxiliary columns for memory operations: `MemoryReadAuxCols`, //! `MemoryReadWithImmediateAuxCols`, and `MemoryWriteAuxCols`. +use std::ops::DerefMut; + use openvm_circuit_primitives::is_less_than::LessThanAuxCols; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_stark_backend::p3_field::PrimeField32; @@ -40,9 +42,7 @@ impl MemoryWriteAuxCols { prev_data, } } -} -impl MemoryWriteAuxCols { pub fn from_base(base: MemoryBaseAuxCols, prev_data: [T; N]) -> Self { Self { base, prev_data } } @@ -54,6 +54,12 @@ impl MemoryWriteAuxCols { pub fn prev_data(&self) -> &[T; N] { &self.prev_data } + + /// Sets the previous timestamp and data **without** updating the less than auxiliary columns. + pub fn set_prev(&mut self, timestamp: T, data: [T; N]) { + self.base.prev_timestamp = timestamp; + self.prev_data = data; + } } /// The auxiliary columns for a memory read operation with block size `N`. @@ -102,3 +108,9 @@ impl AsRef> for MemoryWriteAuxCols unsafe { &*(self as *const MemoryWriteAuxCols as *const MemoryReadAuxCols) } } } + +impl AsMut> for MemoryWriteAuxCols { + fn as_mut(&mut self) -> &mut MemoryBaseAuxCols { + &mut self.base + } +} diff --git a/crates/vm/src/system/memory/online.rs b/crates/vm/src/system/memory/online.rs index 3e45e65593..bbf015e838 100644 --- a/crates/vm/src/system/memory/online.rs +++ b/crates/vm/src/system/memory/online.rs @@ -1,8 +1,13 @@ use std::fmt::Debug; +use getset::Getters; +use itertools::{izip, zip_eq}; use serde::{Deserialize, Serialize}; -use super::paged_vec::{AddressMap, PAGE_SIZE}; +use super::{ + paged_vec::{AddressMap, PAGE_SIZE}, + Address, PagedVec, +}; use crate::{ arch::MemoryConfig, system::memory::{offline::INITIAL_TIMESTAMP, MemoryImage, RecordId}, @@ -18,7 +23,7 @@ pub trait GuestMemory { /// address space `address_space`. For standard usage, /// `T` is either `u8` or `F` where `F` is the base field of the ZK backend. unsafe fn read( - &mut self, // &mut potentially for logs? + &self, address_space: u32, pointer: u32, ) -> [T; BLOCK_SIZE]; @@ -68,139 +73,257 @@ pub enum MemoryLogEntry { IncrementTimestampBy(u32), } -/// A simple data structure to read to/write from memory. -/// Internally storage is in raw bytes (untyped) to align with host memory. -/// -/// Stores a log of memory accesses to reconstruct aspects of memory state for trace generation. -pub struct Memory { - // TODO: the memory struct should contain an array of the byte size of the type per address - // space, passed in from MemoryConfig +// perf[jpw]: since we restrict `timestamp < 2^29`, we could pack `timestamp, log2(block_size)` +// into a single u32 to save half the memory, since `block_size` is a power of 2 and its log2 +// is less than 2^3. +#[repr(C)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, derive_new::new)] +pub struct AccessMetadata { + pub timestamp: u32, + pub block_size: u32, +} + +/// Online memory that stores additional information for trace generation purposes. +/// In particular, keeps track of timestamp. +#[derive(Getters)] +pub struct TracingMemory { + pub timestamp: u32, + /// The underlying data memory, with memory cells typed by address space: see [AddressMap]. + // TODO: make generic in GuestMemory + #[getset(get = "pub")] pub(super) data: AddressMap, - // TODO: delete - pub(super) log: Vec>, - timestamp: u32, + /// A map of `addr_space -> (ptr / min_block_size[addr_space] -> (timestamp: u32, block_size: + /// u32))` for the timestamp and block size of the latest access. + pub(super) meta: Vec>, + /// For each `addr_space`, the minimum block size allowed for memory accesses. In other words, + /// all memory accesses in `addr_space` must be aligned to this block size. + pub(super) min_block_size: Vec, + // TODO: access adapter } -impl Memory { +impl TracingMemory { + // TODO: per-address space memory capacity specification pub fn new(mem_config: &MemoryConfig) -> Self { + assert_eq!(mem_config.as_offset, 1); + let num_cells = 1usize << mem_config.pointer_max_bits; // max cells per address space + let num_addr_sp = 1 + (1 << mem_config.as_height); + let mut min_block_size = vec![1; num_addr_sp]; + // TMP: hardcoding for now + min_block_size[1] = 4; + min_block_size[2] = 4; + let meta = min_block_size + .iter() + .map(|&min_block_size| { + PagedVec::new( + num_cells + .checked_mul(size_of::()) + .unwrap() + .div_ceil(PAGE_SIZE * min_block_size as usize), + ) + }) + .collect(); Self { data: AddressMap::from_mem_config(mem_config), + meta, + min_block_size, timestamp: INITIAL_TIMESTAMP + 1, - log: Vec::with_capacity(mem_config.access_capacity), } } /// Instantiates a new `Memory` data structure from an image. pub fn from_image(image: MemoryImage, access_capacity: usize) -> Self { + let mut meta = vec![PagedVec::new(0); image.as_offset as usize]; + let mut min_block_size = vec![1; meta.len()]; + // TMP: hardcoding for now + min_block_size[1] = 4; + min_block_size[2] = 4; + for (paged_vec, cell_size, &min_block_size) in + izip!(&image.paged_vecs, &image.cell_size, &min_block_size) + { + let num_cells = paged_vec.bytes_capacity() / cell_size; + meta.push(PagedVec::new( + num_cells + .checked_mul(size_of::()) + .unwrap() + .div_ceil(PAGE_SIZE * min_block_size as usize), + )); + } Self { data: image, + meta, + min_block_size, timestamp: INITIAL_TIMESTAMP + 1, - log: Vec::with_capacity(access_capacity), } } - fn last_record_id(&self) -> RecordId { - // TEMP[jpw] - RecordId(0) - // RecordId(self.log.len() - 1) + #[inline(always)] + fn assert_alignment(&self, block_size: usize, align: usize, addr_space: u32, ptr: u32) { + debug_assert!(block_size.is_power_of_two()); + debug_assert_eq!(block_size % align, 0); + debug_assert_ne!(addr_space, 0); + debug_assert_eq!(align as u32, self.min_block_size[addr_space as usize]); + assert_eq!( + ptr % (align as u32), + 0, + "pointer={ptr} not aligned to {align}" + ); } - /// Writes an array of values to the memory at the specified address space and start index. + /// Atomic read operation which increments the timestamp by 1. + /// Returns `(t_prev, [pointer:BLOCK_SIZE]_{address_space})` where `t_prev` is the + /// timestamp of the last memory access. /// - /// Returns the `RecordId` for the memory record and the previous data. + /// The previous memory access is treated as atomic even if previous accesses were for + /// a smaller block size. This is made possible by internal memory access adapters + /// that split/merge memory blocks. More specifically, the last memory access corresponding + /// to `t_prev` may refer to an atomic access inserted by the memory access adapters. + /// + /// # Assumptions + /// The `BLOCK_SIZE` is a multiple of `ALIGN`, which must equal the minimum block size + /// of `address_space`. /// /// # Safety - /// The type `T` must be stack-allocated `repr(C)`, and it must be the exact type used to - /// represent a single memory cell in address space `address_space`. For standard usage, `T` - /// is either `u8` or `F` where `F` is the base field of the ZK backend. - // @dev: `values` is passed by reference since the data is copied into memory. Even though the - // compiler probably optimizes it, we use reference to avoid any unnecessary copy of - // `values` onto the stack in the function call. - pub unsafe fn write( + /// The type `T` must be stack-allocated `repr(C)` or `repr(transparent)`, + /// and it must be the exact type used to represent a single memory cell in + /// address space `address_space`. For standard usage, + /// `T` is either `u8` or `F` where `F` is the base field of the ZK backend. + /// + /// In addition: + /// - `address_space` must be valid. + #[inline(always)] + pub unsafe fn read( &mut self, address_space: u32, pointer: u32, - values: &[T; BLOCK_SIZE], - ) -> (RecordId, [T; BLOCK_SIZE]) { - debug_assert!(BLOCK_SIZE.is_power_of_two()); - - let prev_data = self.data.replace(address_space, pointer, values); - - // self.log.push(MemoryLogEntry::Write { - // address_space, - // pointer, - // data: values.to_vec(), - // }); + ) -> (u32, [T; BLOCK_SIZE]) { + self.assert_alignment(BLOCK_SIZE, ALIGN, address_space, pointer); + let values = self.data.read(address_space, pointer); + let t_curr = self.timestamp; self.timestamp += 1; + // Handle timestamp and block size: + let access_idx = (pointer as usize / ALIGN) * size_of::(); + // TODO: address space should be checked elsewhere + let meta = unsafe { self.meta.get_unchecked_mut(address_space as usize) }; + // The new + let AccessMetadata { + timestamp: t_prev, + mut block_size, + } = meta.replace(access_idx, &AccessMetadata::new(t_curr, BLOCK_SIZE as u32)); + // TODO: do we need to handle uninitialized memory? + if block_size == 0 { + block_size = BLOCK_SIZE as u32; + } + if (block_size as usize) != BLOCK_SIZE { + todo!("split and merge stuff") + }; - (self.last_record_id(), prev_data) + (t_prev, values) } - /// Reads an array of values from the memory at the specified address space and start index. + /// Atomic write operation that writes `values` into `[pointer:BLOCK_SIZE]_{address_space}` and + /// then increments the timestamp by 1. Returns `(t_prev, values_prev)` which equal the + /// timestamp and value `[pointer:BLOCK_SIZE]_{address_space}` of the last memory access. + /// + /// The previous memory access is treated as atomic even if previous accesses were for + /// a smaller block size. This is made possible by internal memory access adapters + /// that split/merge memory blocks. More specifically, the last memory access corresponding + /// to `t_prev` may refer to an atomic access inserted by the memory access adapters. + /// + /// # Assumptions + /// The `BLOCK_SIZE` is a multiple of `ALIGN`, which must equal the minimum block size + /// of `address_space`. /// /// # Safety - /// The type `T` must be stack-allocated `repr(C)`, and it must be the exact type used to - /// represent a single memory cell in address space `address_space`. For standard usage, `T` - /// is either `u8` or `F` where `F` is the base field of the ZK backend. + /// The type `T` must be stack-allocated `repr(C)` or `repr(transparent)`, + /// and it must be the exact type used to represent a single memory cell in + /// address space `address_space`. For standard usage, + /// `T` is either `u8` or `F` where `F` is the base field of the ZK backend. + /// + /// In addition: + /// - `address_space` must be valid. #[inline(always)] - pub unsafe fn read( + pub unsafe fn write( &mut self, address_space: u32, pointer: u32, - ) -> (RecordId, [T; BLOCK_SIZE]) { - assert!(BLOCK_SIZE.is_power_of_two()); - debug_assert_ne!(address_space, 0); + values: &[T; BLOCK_SIZE], + ) -> (u32, [T; BLOCK_SIZE]) { + self.assert_alignment(BLOCK_SIZE, ALIGN, address_space, pointer); + let values_prev = self.data.replace(address_space, pointer, values); + let t_curr = self.timestamp; + self.timestamp += 1; + // Handle timestamp and block size: + let access_idx = (pointer as usize / ALIGN) * size_of::(); + // TODO: address space should be checked elsewhere + let meta = unsafe { self.meta.get_unchecked_mut(address_space as usize) }; + // The new + let AccessMetadata { + timestamp: t_prev, + mut block_size, + } = meta.replace(access_idx, &AccessMetadata::new(t_curr, BLOCK_SIZE as u32)); + // TODO: mark as touched + if block_size == 0 { + block_size = BLOCK_SIZE as u32; + } + if (block_size as usize) != BLOCK_SIZE { + todo!("split and merge stuff") + }; - // self.log.push(MemoryLogEntry::Read { - // address_space, - // pointer, - // len: N, - // }); + (t_prev, values_prev) + } - let values = self.data.read(address_space, pointer); + pub fn increment_timestamp(&mut self) { self.timestamp += 1; - (self.last_record_id(), values) } pub fn increment_timestamp_by(&mut self, amount: u32) { self.timestamp += amount; - self.log.push(MemoryLogEntry::IncrementTimestampBy(amount)) } pub fn timestamp(&self) -> u32 { self.timestamp } - /// # Safety - /// The type `T` must be stack-allocated `repr(C)`, and it must be the exact type used to - /// represent a single memory cell in address space `address_space`. For standard usage, `T` - /// is either `u8` or `F` where `F` is the base field of the ZK backend. - #[inline(always)] - pub unsafe fn get(&self, address_space: u32, pointer: u32) -> T { - self.data.get((address_space, pointer)) + /// Returns iterator over `((addr_space, ptr), (timestamp, block_size))` of the address, last + /// accessed timestamp, and block size of all memory blocks that have been accessed since this + /// instance of [TracingMemory] was constructed. This is similar to a soft-dirty mechanism, + /// where the memory data is loaded from an initial image and considered "clean", and then + /// all future accesses are marked as "dirty". + // block_size is initialized to 0, so nonzero block_size happens to also mark "dirty" cells + // **Assuming** for now that only the start of a block has nonzero block_size + pub fn touched_blocks(&self) -> impl Iterator + '_ { + zip_eq(&self.meta, &self.min_block_size) + .enumerate() + .flat_map(move |(addr_space, (page, &align))| { + page.iter::() + .filter_map(move |(idx, metadata)| { + (metadata.block_size != 0) + .then_some(((addr_space as u32, idx as u32 * align), metadata)) + }) + }) } } -#[cfg(test)] -mod tests { - use super::Memory; - use crate::arch::MemoryConfig; +// #[cfg(test)] +// mod tests { +// use super::TracingMemory; +// use crate::arch::MemoryConfig; - #[test] - fn test_write_read() { - let mut memory = Memory::new(&MemoryConfig::default()); - let address_space = 1; +// #[test] +// fn test_write_read() { +// let mut memory = TracingMemory::new(&MemoryConfig::default()); +// let address_space = 1; - unsafe { - memory.write(address_space, 0, &[1u8, 2, 3, 4]); +// unsafe { +// memory.write(address_space, 0, &[1u8, 2, 3, 4]); - let (_, data) = memory.read::(address_space, 0); - assert_eq!(data, [1u8, 2]); +// let (_, data) = memory.read::(address_space, 0); +// assert_eq!(data, [1u8, 2]); - memory.write(address_space, 2, &[100u8]); +// memory.write(address_space, 2, &[100u8]); - let (_, data) = memory.read::(address_space, 0); - assert_eq!(data, [1u8, 2, 100, 4]); - } - } -} +// let (_, data) = memory.read::(address_space, 0); +// assert_eq!(data, [1u8, 2, 100, 4]); +// } +// } +// } diff --git a/crates/vm/src/system/memory/paged_vec.rs b/crates/vm/src/system/memory/paged_vec.rs index d3ced876ec..c8acc41804 100644 --- a/crates/vm/src/system/memory/paged_vec.rs +++ b/crates/vm/src/system/memory/paged_vec.rs @@ -29,6 +29,7 @@ impl PagedVec { // Copies a range of length `len` starting at index `start` // into the memory pointed to by `dst`. If the relevant page is not // initialized, fills that portion with `0u8`. + #[inline] fn read_range_generic(&self, start: usize, len: usize, dst: *mut u8) { let start_page = start / PAGE_SIZE; let end_page = (start + len - 1) / PAGE_SIZE; @@ -63,6 +64,7 @@ impl PagedVec { // It copies the current values into the memory pointed to by `dst` // and then writes the new values into the underlying pages, // allocating pages (with defaults) if necessary. + #[inline] fn set_range_generic(&mut self, start: usize, len: usize, new: *const u8, dst: *mut u8) { let start_page = start / PAGE_SIZE; let end_page = (start + len - 1) / PAGE_SIZE; @@ -101,8 +103,9 @@ impl PagedVec { } } - pub fn memory_size(&self) -> usize { - self.pages.len() * PAGE_SIZE + /// Total capacity across available pages, in bytes. + pub fn bytes_capacity(&self) -> usize { + self.pages.len().checked_mul(PAGE_SIZE).unwrap() } pub fn is_empty(&self) -> bool { @@ -124,6 +127,9 @@ impl PagedVec { /// # Panics /// If `start..start + size_of()` is out of bounds. + // @dev: `values` is passed by reference since the data is copied into memory. Even though the + // compiler probably optimizes it, we use reference to avoid any unnecessary copy of `values` + // onto the stack in the function call. #[inline(always)] pub fn set(&mut self, start: usize, values: &BLOCK) { let len = size_of::(); @@ -208,7 +214,7 @@ impl Iterator for PagedVecIter<'_, T, PAGE_SIZE self.current_index_in_page = 0; } let global_index = self.current_page * PAGE_SIZE + self.current_index_in_page; - if global_index + size_of::() > self.vec.memory_size() { + if global_index + size_of::() > self.vec.bytes_capacity() { return None; } @@ -248,8 +254,14 @@ impl AddressMap { // TMP: hardcoding for now let mut cell_size = vec![1, 1]; cell_size.resize(as_cnt, 4); + let paged_vecs = cell_size + .iter() + .map(|&cell_size| { + PagedVec::new(mem_size.checked_mul(cell_size).unwrap().div_ceil(PAGE_SIZE)) + }) + .collect(); Self { - paged_vecs: vec![PagedVec::new(mem_size.div_ceil(PAGE_SIZE)); as_cnt], + paged_vecs, cell_size, as_offset, } @@ -287,6 +299,18 @@ impl AddressMap { }) } + pub fn get_f(&self, addr_space: u32, ptr: u32) -> F { + debug_assert_ne!(addr_space, 0); + // TODO: fix this + unsafe { + if addr_space <= 2 { + F::from_canonical_u8(self.get::((addr_space, ptr))) + } else { + self.get::((addr_space, ptr)) + } + } + } + /// # Safety /// - `T` **must** be the correct type for a single memory cell for `addr_space` /// - Assumes `addr_space` is within the configured memory and not out of bounds @@ -336,7 +360,7 @@ impl AddressMap { impl GuestMemory for AddressMap { unsafe fn read( - &mut self, + &self, addr_space: u32, ptr: u32, ) -> [T; BLOCK_SIZE] { diff --git a/crates/vm/src/system/mod.rs b/crates/vm/src/system/mod.rs index a1038ac86a..7164846d5b 100644 --- a/crates/vm/src/system/mod.rs +++ b/crates/vm/src/system/mod.rs @@ -1,5 +1,6 @@ pub mod connector; pub mod memory; +// Necessary for the PublicValuesChip pub mod native_adapter; /// Chip to handle phantom instructions. /// The Air will always constrain a NOP which advances pc by DEFAULT_PC_STEP. diff --git a/crates/vm/src/system/native_adapter/mod.rs b/crates/vm/src/system/native_adapter/mod.rs index 8d1d90df52..101dbf00c9 100644 --- a/crates/vm/src/system/native_adapter/mod.rs +++ b/crates/vm/src/system/native_adapter/mod.rs @@ -27,7 +27,7 @@ use openvm_stark_backend::{ use serde::{Deserialize, Serialize}; use serde_big_array::BigArray; -use crate::system::memory::{OfflineMemory, RecordId}; +use crate::system::memory::{online::GuestMemory, OfflineMemory, RecordId}; /// R reads(R<=2), W writes(W<=1). /// Operands: b for the first read, c for the second read, a for the first write. @@ -226,18 +226,22 @@ impl VmAdapterChip let mut reads = Vec::with_capacity(R); if R >= 1 { - reads.push(memory.read::(e, b)); + reads.push(unsafe { + memory + .memory_image() + .read::(e.as_canonical_u32(), b.as_canonical_u32()) + }); } if R >= 2 { - reads.push(memory.read::(f, c)); + reads.push(unsafe { + memory + .memory_image() + .read::(f.as_canonical_u32(), c.as_canonical_u32()) + }); } - let i_reads: [_; R] = std::array::from_fn(|i| reads[i].1); - Ok(( - i_reads, - Self::ReadRecord { - reads: reads.try_into().unwrap(), - }, + reads.try_into().unwrap(), + Self::ReadRecord { reads: todo!() }, )) } @@ -253,8 +257,9 @@ impl VmAdapterChip let Instruction { a, d, .. } = *instruction; let mut writes = Vec::with_capacity(W); if W >= 1 { - let (record_id, _) = memory.write(d, a, &output.writes[0]); - writes.push((record_id, output.writes[0])); + todo!(); + // let (record_id, _) = memory.write(d, a, &output.writes[0]); + // writes.push((record_id, output.writes[0])); } Ok(( diff --git a/extensions/rv32im/circuit/src/adapters/mod.rs b/extensions/rv32im/circuit/src/adapters/mod.rs index 8be7b8b81d..e5b5c99496 100644 --- a/extensions/rv32im/circuit/src/adapters/mod.rs +++ b/extensions/rv32im/circuit/src/adapters/mod.rs @@ -1,6 +1,7 @@ use std::ops::Mul; -use openvm_circuit::system::memory::{MemoryController, RecordId}; +use openvm_circuit::system::memory::{online::TracingMemory, MemoryController, RecordId}; +use openvm_instructions::riscv::RV32_REGISTER_AS; use openvm_stark_backend::p3_field::{FieldAlgebra, PrimeField32}; mod alu; @@ -46,6 +47,36 @@ pub fn decompose(value: u32) -> [F; RV32_REGISTER_NUM_LIMBS] { }) } +pub fn tracing_read_reg( + memory: &mut TracingMemory, + reg_ptr: u32, +) -> (u32, [u8; RV32_REGISTER_NUM_LIMBS]) { + // SAFETY: + // - address space `RV32_REGISTER_AS` will always have cell type `u8` and minimum alignment of + // `RV32_REGISTER_NUM_LIMBS` + unsafe { + memory + .read::(RV32_REGISTER_AS, reg_ptr) + } +} + +pub fn tracing_write_reg( + memory: &mut TracingMemory, + reg_ptr: u32, + reg_val: &[u8; RV32_REGISTER_NUM_LIMBS], +) -> (u32, [u8; RV32_REGISTER_NUM_LIMBS]) { + // SAFETY: + // - address space `RV32_REGISTER_AS` will always have cell type `u8` and minimum alignment of + // `RV32_REGISTER_NUM_LIMBS` + unsafe { + memory.write::( + RV32_REGISTER_AS, + reg_ptr, + reg_val, + ) + } +} + /// Read register value as [RV32_REGISTER_NUM_LIMBS] limbs from memory. /// Returns the read record and the register value as u32. /// Does not make any range check calls. diff --git a/extensions/rv32im/circuit/src/adapters/rdwrite.rs b/extensions/rv32im/circuit/src/adapters/rdwrite.rs index 79199f5dc4..0d495ed18b 100644 --- a/extensions/rv32im/circuit/src/adapters/rdwrite.rs +++ b/extensions/rv32im/circuit/src/adapters/rdwrite.rs @@ -1,85 +1,25 @@ -use std::{ - borrow::{Borrow, BorrowMut}, - marker::PhantomData, -}; +use std::borrow::Borrow; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge, - ExecutionBus, ExecutionState, ImmInstruction, Result, VmAdapterAir, VmAdapterChip, - VmAdapterInterface, + AdapterAirContext, BasicAdapterInterface, ExecutionBridge, ExecutionState, ImmInstruction, + VmAdapterAir, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryWriteAuxCols}, - MemoryAddress, MemoryController, OfflineMemory, RecordId, - }, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryWriteAuxCols}, + MemoryAddress, }, }; use openvm_circuit_primitives::utils::not; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{ - instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_REGISTER_AS, -}; +use openvm_instructions::{program::DEFAULT_PC_STEP, riscv::RV32_REGISTER_AS}; use openvm_stark_backend::{ interaction::InteractionBuilder, p3_air::{AirBuilder, BaseAir}, - p3_field::{Field, FieldAlgebra, PrimeField32}, + p3_field::{Field, FieldAlgebra}, }; -use serde::{Deserialize, Serialize}; - -use super::{tmp_convert_to_u8s, RV32_REGISTER_NUM_LIMBS}; - -/// This adapter doesn't read anything, and writes to \[a:4\]_d, where d == 1 -#[derive(Debug)] -pub struct Rv32RdWriteAdapterChip { - pub air: Rv32RdWriteAdapterAir, - _marker: PhantomData, -} - -/// This adapter doesn't read anything, and **maybe** writes to \[a:4\]_d, where d == 1 -#[derive(Debug)] -pub struct Rv32CondRdWriteAdapterChip { - /// Do not use the inner air directly, use `air` instead. - inner: Rv32RdWriteAdapterChip, - pub air: Rv32CondRdWriteAdapterAir, -} - -impl Rv32RdWriteAdapterChip { - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - ) -> Self { - Self { - air: Rv32RdWriteAdapterAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - }, - _marker: PhantomData, - } - } -} -impl Rv32CondRdWriteAdapterChip { - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - ) -> Self { - let inner = Rv32RdWriteAdapterChip::new(execution_bus, program_bus, memory_bridge); - let air = Rv32CondRdWriteAdapterAir { inner: inner.air }; - Self { inner, air } - } -} - -#[repr(C)] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Rv32RdWriteWriteRecord { - pub from_state: ExecutionState, - pub rd_id: Option, -} +use super::RV32_REGISTER_NUM_LIMBS; #[repr(C)] #[derive(Debug, Clone, AlignedBorrow)] @@ -92,16 +32,18 @@ pub struct Rv32RdWriteAdapterCols { #[repr(C)] #[derive(Debug, Clone, AlignedBorrow)] pub struct Rv32CondRdWriteAdapterCols { - inner: Rv32RdWriteAdapterCols, + pub inner: Rv32RdWriteAdapterCols, pub needs_write: T, } +/// This adapter doesn't read anything, and writes to \[a:4\]_d, where d == 1 #[derive(Clone, Copy, Debug, derive_new::new)] pub struct Rv32RdWriteAdapterAir { pub(super) memory_bridge: MemoryBridge, pub(super) execution_bridge: ExecutionBridge, } +/// This adapter doesn't read anything, and **maybe** writes to \[a:4\]_d, where d == 1 #[derive(Clone, Copy, Debug, derive_new::new)] pub struct Rv32CondRdWriteAdapterAir { inner: Rv32RdWriteAdapterAir, @@ -240,132 +182,3 @@ impl VmAdapterAir for Rv32CondRdWriteAdapterAir { cols.inner.from_state.pc } } - -impl VmAdapterChip for Rv32RdWriteAdapterChip { - type ReadRecord = (); - type WriteRecord = Rv32RdWriteWriteRecord; - type Air = Rv32RdWriteAdapterAir; - type Interface = BasicAdapterInterface, 0, 1, 0, RV32_REGISTER_NUM_LIMBS>; - - fn preprocess( - &mut self, - _memory: &mut MemoryController, - instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { - let d = instruction.d; - debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - - Ok(([], ())) - } - - fn postprocess( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - _read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - let Instruction { a, d, .. } = *instruction; - let (rd_id, _) = memory.write(d, a, &tmp_convert_to_u8s(output.writes[0])); - - Ok(( - ExecutionState { - pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), - timestamp: memory.timestamp(), - }, - Self::WriteRecord { - from_state, - rd_id: Some(rd_id), - }, - )) - } - - fn generate_trace_row( - &self, - row_slice: &mut [F], - _read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, - ) { - let aux_cols_factory = memory.aux_cols_factory(); - let adapter_cols: &mut Rv32RdWriteAdapterCols = row_slice.borrow_mut(); - adapter_cols.from_state = write_record.from_state.map(F::from_canonical_u32); - let rd = memory.record_by_id(write_record.rd_id.unwrap()); - adapter_cols.rd_ptr = rd.pointer; - aux_cols_factory.generate_write_aux(rd, &mut adapter_cols.rd_aux_cols); - } - - fn air(&self) -> &Self::Air { - &self.air - } -} - -impl VmAdapterChip for Rv32CondRdWriteAdapterChip { - type ReadRecord = (); - type WriteRecord = Rv32RdWriteWriteRecord; - type Air = Rv32CondRdWriteAdapterAir; - type Interface = BasicAdapterInterface, 0, 1, 0, RV32_REGISTER_NUM_LIMBS>; - - fn preprocess( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { - self.inner.preprocess(memory, instruction) - } - - fn postprocess( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - _read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - let Instruction { a, d, .. } = *instruction; - let rd_id = if instruction.f != F::ZERO { - let (rd_id, _) = memory.write(d, a, &tmp_convert_to_u8s(output.writes[0])); - Some(rd_id) - } else { - memory.increment_timestamp(); - None - }; - - Ok(( - ExecutionState { - pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), - timestamp: memory.timestamp(), - }, - Self::WriteRecord { from_state, rd_id }, - )) - } - - fn generate_trace_row( - &self, - row_slice: &mut [F], - _read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, - ) { - let aux_cols_factory = memory.aux_cols_factory(); - let adapter_cols: &mut Rv32CondRdWriteAdapterCols = row_slice.borrow_mut(); - adapter_cols.inner.from_state = write_record.from_state.map(F::from_canonical_u32); - if let Some(rd_id) = write_record.rd_id { - let rd = memory.record_by_id(rd_id); - adapter_cols.inner.rd_ptr = rd.pointer; - aux_cols_factory.generate_write_aux(rd, &mut adapter_cols.inner.rd_aux_cols); - adapter_cols.needs_write = F::ONE; - } - } - - fn air(&self) -> &Self::Air { - &self.air - } -} diff --git a/extensions/rv32im/circuit/src/auipc/core.rs b/extensions/rv32im/circuit/src/auipc/core.rs index 8ec9e274f6..e0c5eb8957 100644 --- a/extensions/rv32im/circuit/src/auipc/core.rs +++ b/extensions/rv32im/circuit/src/auipc/core.rs @@ -1,17 +1,26 @@ use std::{ array, + array::from_fn, borrow::{Borrow, BorrowMut}, }; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, ImmInstruction, Result, VmAdapterInterface, - VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, ImmInstruction, Result, SingleTraceStep, VmAdapterInterface, VmCoreAir, + VmStateMut, + }, + system::memory::{online::TracingMemory, MemoryAuxColsFactory}, }; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, program::PC_BITS, LocalOpcode}; +use openvm_instructions::{ + instruction::Instruction, + program::{DEFAULT_PC_STEP, PC_BITS}, + riscv::RV32_REGISTER_AS, + LocalOpcode, +}; use openvm_rv32im_transpiler::Rv32AuipcOpcode::{self, *}; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -21,9 +30,11 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use crate::adapters::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; +use crate::adapters::{ + tracing_write_reg, Rv32RdWriteAdapterCols, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, +}; -const RV32_LIMB_MAX: u32 = (1 << RV32_CELL_BITS) - 1; +pub(super) const ADAPTER_WIDTH: usize = size_of::>(); #[repr(C)] #[derive(Debug, Clone, AlignedBorrow)] @@ -36,7 +47,7 @@ pub struct Rv32AuipcCoreCols { pub rd_data: [T; RV32_REGISTER_NUM_LIMBS], } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct Rv32AuipcCoreAir { pub bus: BitwiseOperationLookupBus, } @@ -194,6 +205,7 @@ pub struct Rv32AuipcCoreRecord { } pub struct Rv32AuipcCoreChip { + // TODO[jpw]: do we still need air in here? pub air: Rv32AuipcCoreAir, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, } @@ -209,84 +221,75 @@ impl Rv32AuipcCoreChip { } } -impl> VmCoreChip for Rv32AuipcCoreChip -where - I::Writes: From<[[F; RV32_REGISTER_NUM_LIMBS]; 1]>, -{ - type Record = Rv32AuipcCoreRecord; - type Air = Rv32AuipcCoreAir; - - #[allow(clippy::type_complexity)] - fn execute_instruction( - &self, +impl SingleTraceStep for Rv32AuipcCoreChip { + fn execute( + &mut self, + state: VmStateMut, instruction: &Instruction, - from_pc: u32, - _reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { - let local_opcode = Rv32AuipcOpcode::from_usize( - instruction - .opcode - .local_opcode_idx(Rv32AuipcOpcode::CLASS_OFFSET), - ); - let imm = instruction.c.as_canonical_u32(); - let rd_data = run_auipc(local_opcode, from_pc, imm); - let rd_data_field = rd_data.map(F::from_canonical_u32); + row_slice: &mut [F], + ) -> Result<()> { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(ADAPTER_WIDTH) }; + let adapter_row: &mut Rv32RdWriteAdapterCols = adapter_row.borrow_mut(); + let core_row: &mut Rv32AuipcCoreCols = core_row.borrow_mut(); - let output = AdapterRuntimeContext::without_pc([rd_data_field]); - - let imm_limbs = array::from_fn(|i| (imm >> (i * RV32_CELL_BITS)) & RV32_LIMB_MAX); - let pc_limbs: [u32; RV32_REGISTER_NUM_LIMBS] = - array::from_fn(|i| (from_pc >> (i * RV32_CELL_BITS)) & RV32_LIMB_MAX); - - for i in 0..(RV32_REGISTER_NUM_LIMBS / 2) { - self.bitwise_lookup_chip - .request_range(rd_data[i * 2], rd_data[i * 2 + 1]); - } - - let mut need_range_check: Vec = Vec::new(); - for limb in imm_limbs { - need_range_check.push(limb); - } + let from_timestamp = state.memory.timestamp(); + let imm = instruction.c.as_canonical_u32(); + let rd_data = run_auipc(Rv32AuipcOpcode::AUIPC, *state.pc, imm); + + debug_assert_eq!(instruction.d.as_canonical_u32(), RV32_REGISTER_AS); + let rd_ptr = instruction.a.as_canonical_u32(); + let (t_prev, data_prev) = tracing_write_reg(state.memory, rd_ptr, &rd_data); + // TODO: store as u32 directly + adapter_row.from_state.pc = F::from_canonical_u32(*state.pc); + adapter_row.from_state.timestamp = F::from_canonical_u32(from_timestamp); + adapter_row.rd_ptr = instruction.a; + adapter_row.rd_aux_cols.set_prev( + F::from_canonical_u32(t_prev), + data_prev.map(F::from_canonical_u8), + ); + core_row.rd_data = rd_data.map(F::from_canonical_u8); + // We decompose during fill_trace_row later: + core_row.imm_limbs[0] = instruction.c; - for (i, limb) in pc_limbs.iter().enumerate().skip(1) { - if i == pc_limbs.len() - 1 { - need_range_check.push((*limb) << (pc_limbs.len() * RV32_CELL_BITS - PC_BITS)); - } else { - need_range_check.push(*limb); - } - } + *state.pc += DEFAULT_PC_STEP; + Ok(()) + } - for pair in need_range_check.chunks(2) { + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(ADAPTER_WIDTH) }; + let adapter_row: &mut Rv32RdWriteAdapterCols = adapter_row.borrow_mut(); + let core_row: &mut Rv32AuipcCoreCols = core_row.borrow_mut(); + core_row.is_valid = F::ONE; + + let timestamp = adapter_row.from_state.timestamp.as_canonical_u32(); + mem_helper.fill_from_prev(timestamp, adapter_row.rd_aux_cols.as_mut()); + + let from_pc = adapter_row.from_state.pc.as_canonical_u32(); + let pc_limbs = from_pc.to_le_bytes(); + let imm = core_row.imm_limbs[0].as_canonical_u32(); + let imm_limbs = imm.to_le_bytes(); + debug_assert_eq!(imm_limbs[3], 0); + core_row.imm_limbs = from_fn(|i| F::from_canonical_u8(imm_limbs[i])); + // only the middle 2 limbs: + core_row.pc_limbs = from_fn(|i| F::from_canonical_u8(pc_limbs[i + 1])); + // range checks: + let rd_data = core_row.rd_data.map(|x| x.as_canonical_u32()); + for pair in rd_data.chunks_exact(2) { self.bitwise_lookup_chip.request_range(pair[0], pair[1]); } - - Ok(( - output, - Self::Record { - imm_limbs: imm_limbs.map(F::from_canonical_u32), - pc_limbs: array::from_fn(|i| F::from_canonical_u32(pc_limbs[i + 1])), - rd_data: rd_data.map(F::from_canonical_u32), - }, - )) - } - - fn get_opcode_name(&self, opcode: usize) -> String { - format!( - "{:?}", - Rv32AuipcOpcode::from_usize(opcode - Rv32AuipcOpcode::CLASS_OFFSET) - ) - } - - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let core_cols: &mut Rv32AuipcCoreCols = row_slice.borrow_mut(); - core_cols.imm_limbs = record.imm_limbs; - core_cols.pc_limbs = record.pc_limbs; - core_cols.rd_data = record.rd_data; - core_cols.is_valid = F::ONE; + // hardcoding for performance: first 3 limbs of imm_limbs, last 3 limbs of pc_limbs where + // most significant limb of pc_limbs is shifted up + self.bitwise_lookup_chip + .request_range(imm_limbs[0] as u32, imm_limbs[1] as u32); + self.bitwise_lookup_chip + .request_range(imm_limbs[2] as u32, pc_limbs[1] as u32); + let msl_shift = RV32_REGISTER_NUM_LIMBS * RV32_CELL_BITS - PC_BITS; + self.bitwise_lookup_chip + .request_range(pc_limbs[2] as u32, (pc_limbs[3] as u32) << msl_shift); } - fn air(&self) -> &Self::Air { - &self.air + fn get_opcode_name(&self, _: usize) -> String { + format!("{:?}", AUIPC) } } @@ -295,7 +298,7 @@ pub(super) fn run_auipc( _opcode: Rv32AuipcOpcode, pc: u32, imm: u32, -) -> [u32; RV32_REGISTER_NUM_LIMBS] { +) -> [u8; RV32_REGISTER_NUM_LIMBS] { let rd = pc.wrapping_add(imm << RV32_CELL_BITS); - array::from_fn(|i| (rd >> (RV32_CELL_BITS * i)) & RV32_LIMB_MAX) + rd.to_le_bytes() } diff --git a/extensions/rv32im/circuit/src/auipc/mod.rs b/extensions/rv32im/circuit/src/auipc/mod.rs index 6e2234bfbd..776a7beb95 100644 --- a/extensions/rv32im/circuit/src/auipc/mod.rs +++ b/extensions/rv32im/circuit/src/auipc/mod.rs @@ -1,6 +1,6 @@ -use openvm_circuit::arch::VmChipWrapper; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; -use crate::adapters::Rv32RdWriteAdapterChip; +use crate::adapters::Rv32RdWriteAdapterAir; mod core; pub use core::*; @@ -8,4 +8,5 @@ pub use core::*; #[cfg(test)] mod tests; -pub type Rv32AuipcChip = VmChipWrapper, Rv32AuipcCoreChip>; +pub type Rv32AuipcAir = VmAirWrapper; +pub type Rv32AuipcChip = NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/auipc/tests.rs b/extensions/rv32im/circuit/src/auipc/tests.rs index 2c8a399198..22a080fb6f 100644 --- a/extensions/rv32im/circuit/src/auipc/tests.rs +++ b/extensions/rv32im/circuit/src/auipc/tests.rs @@ -1,6 +1,6 @@ use std::borrow::BorrowMut; -use openvm_circuit::arch::{testing::VmChipTestBuilder, VmAdapterChip}; +use openvm_circuit::arch::{testing::VmChipTestBuilder, VmAirWrapper}; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, }; @@ -8,7 +8,6 @@ use openvm_instructions::{instruction::Instruction, program::PC_BITS, LocalOpcod use openvm_rv32im_transpiler::Rv32AuipcOpcode::{self, *}; use openvm_stark_backend::{ interaction::BusIndex, - p3_air::BaseAir, p3_field::{FieldAlgebra, PrimeField32}, p3_matrix::{dense::RowMajorMatrix, Matrix}, utils::disable_debug_builder, @@ -18,14 +17,32 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; -use super::{run_auipc, Rv32AuipcChip, Rv32AuipcCoreChip, Rv32AuipcCoreCols}; -use crate::adapters::{Rv32RdWriteAdapterChip, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; +use super::{run_auipc, Rv32AuipcChip, Rv32AuipcCoreChip, Rv32AuipcCoreCols, ADAPTER_WIDTH}; +use crate::adapters::{Rv32RdWriteAdapterAir, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; const IMM_BITS: usize = 24; const BITWISE_OP_LOOKUP_BUS: BusIndex = 9; +const MAX_INS_CAPACITY: usize = 128; type F = BabyBear; +fn create_test_chip( + tester: &VmChipTestBuilder, +) -> ( + Rv32AuipcChip, + SharedBitwiseOperationLookupChip, +) { + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + let adapter_air = Rv32RdWriteAdapterAir::new(tester.memory_bridge(), tester.execution_bridge()); + let core = Rv32AuipcCoreChip::new(bitwise_chip.clone()); + let air = VmAirWrapper::new(adapter_air, core.air); + ( + Rv32AuipcChip::::new(air, core, MAX_INS_CAPACITY, tester.memory_helper()), + bitwise_chip, + ) +} + fn set_and_execute( tester: &mut VmChipTestBuilder, chip: &mut Rv32AuipcChip, @@ -46,7 +63,7 @@ fn set_and_execute( let rd_data = run_auipc(opcode, initial_pc, imm as u32); - assert_eq!(rd_data.map(F::from_canonical_u32), tester.read::<4>(1, a)); + assert_eq!(rd_data.map(F::from_canonical_u8), tester.read::<4>(1, a)); } /////////////////////////////////////////////////////////////////////////////////////// @@ -59,17 +76,9 @@ fn set_and_execute( #[test] fn rand_auipc_test() { let mut rng = create_seeded_rng(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut tester = VmChipTestBuilder::default(); - let adapter = Rv32RdWriteAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - ); - let core = Rv32AuipcCoreChip::new(bitwise_chip.clone()); - let mut chip = Rv32AuipcChip::::new(adapter, core, tester.offline_memory_mutex_arc()); + let (mut chip, bitwise_chip) = create_test_chip(&tester); let num_tests: usize = 100; for _ in 0..num_tests { @@ -98,18 +107,8 @@ fn run_negative_auipc_test( expected_error: VerificationError, ) { let mut rng = create_seeded_rng(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let mut tester = VmChipTestBuilder::default(); - let adapter = Rv32RdWriteAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - ); - let adapter_width = BaseAir::::width(adapter.air()); - let core = Rv32AuipcCoreChip::new(bitwise_chip.clone()); - let mut chip = Rv32AuipcChip::::new(adapter, core, tester.offline_memory_mutex_arc()); + let (mut chip, bitwise_chip) = create_test_chip(&tester); set_and_execute( &mut tester, @@ -129,7 +128,7 @@ fn run_negative_auipc_test( { let mut trace_row = auipc_trace.row_slice(0).to_vec(); - let (_, core_row) = trace_row.split_at_mut(adapter_width); + let (_, core_row) = trace_row.split_at_mut(ADAPTER_WIDTH); let core_cols: &mut Rv32AuipcCoreCols = core_row.borrow_mut(); @@ -254,17 +253,8 @@ fn overflow_negative_tests() { #[test] fn execute_roundtrip_sanity_test() { let mut rng = create_seeded_rng(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let mut tester = VmChipTestBuilder::default(); - let adapter = Rv32RdWriteAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - ); - let inner = Rv32AuipcCoreChip::new(bitwise_chip); - let mut chip = Rv32AuipcChip::::new(adapter, inner, tester.offline_memory_mutex_arc()); + let (mut chip, _) = create_test_chip(&tester); let num_tests: usize = 100; for _ in 0..num_tests { diff --git a/extensions/rv32im/circuit/src/base_alu/tests.rs b/extensions/rv32im/circuit/src/base_alu/tests.rs index c4a5503d4f..912daec858 100644 --- a/extensions/rv32im/circuit/src/base_alu/tests.rs +++ b/extensions/rv32im/circuit/src/base_alu/tests.rs @@ -361,40 +361,41 @@ impl VmAdapterChip for Rv32BaseAluAdapterTestChip { )> { let Instruction { b, c, d, e, .. } = *instruction; - let rs1 = memory.read::(d, b); - let (rs2, rs2_data, rs2_imm) = if e.is_zero() { - let c_u32 = c.as_canonical_u32(); - memory.increment_timestamp(); - let mask1 = (1 << 9) - 1; - let mask2 = (1 << 3) - 2; - ( - None, - [ - (c_u32 & mask1) as u16, - ((c_u32 >> 8) & mask2) as u16, - (c_u32 >> 16) as u16, - (c_u32 >> 16) as u16, - ] - .map(F::from_canonical_u16), - c, - ) - } else { - let rs2_read = memory.read::(e, c); - ( - Some(rs2_read.0), - rs2_read.1.map(F::from_canonical_u8), - F::ZERO, - ) - }; - - Ok(( - [rs1.1.map(F::from_canonical_u8), rs2_data], - Self::ReadRecord { - rs1: rs1.0, - rs2, - rs2_imm, - }, - )) + todo!() + // let rs1 = memory.read::(d, b); + // let (rs2, rs2_data, rs2_imm) = if e.is_zero() { + // let c_u32 = c.as_canonical_u32(); + // memory.increment_timestamp(); + // let mask1 = (1 << 9) - 1; + // let mask2 = (1 << 3) - 2; + // ( + // None, + // [ + // (c_u32 & mask1) as u16, + // ((c_u32 >> 8) & mask2) as u16, + // (c_u32 >> 16) as u16, + // (c_u32 >> 16) as u16, + // ] + // .map(F::from_canonical_u16), + // c, + // ) + // } else { + // let rs2_read = memory.read::(e, c); + // ( + // Some(rs2_read.0), + // rs2_read.1.map(F::from_canonical_u8), + // F::ZERO, + // ) + // }; + + // Ok(( + // [rs1.1.map(F::from_canonical_u8), rs2_data], + // Self::ReadRecord { + // rs1: rs1.0, + // rs2, + // rs2_imm, + // }, + // )) } fn postprocess( diff --git a/extensions/rv32im/circuit/src/extension.rs b/extensions/rv32im/circuit/src/extension.rs index 0502bcdf16..57f82aba9b 100644 --- a/extensions/rv32im/circuit/src/extension.rs +++ b/extensions/rv32im/circuit/src/extension.rs @@ -312,15 +312,16 @@ impl VmExtension for Rv32I { BranchLessThanOpcode::iter().map(|x| x.global_opcode()), )?; - let jal_lui_chip = Rv32JalLuiChip::new( - Rv32CondRdWriteAdapterChip::new(execution_bus, program_bus, memory_bridge), - Rv32JalLuiCoreChip::new(bitwise_lu_chip.clone()), - offline_memory.clone(), - ); - inventory.add_executor( - jal_lui_chip, - Rv32JalLuiOpcode::iter().map(|x| x.global_opcode()), - )?; + // TODO + // let jal_lui_chip = Rv32JalLuiChip::new( + // Rv32CondRdWriteAdapterChip::new(execution_bus, program_bus, memory_bridge), + // Rv32JalLuiCoreChip::new(bitwise_lu_chip.clone()), + // offline_memory.clone(), + // ); + // inventory.add_executor( + // jal_lui_chip, + // Rv32JalLuiOpcode::iter().map(|x| x.global_opcode()), + // )?; let jalr_chip = Rv32JalrChip::new( Rv32JalrAdapterChip::new(execution_bus, program_bus, memory_bridge), @@ -329,15 +330,16 @@ impl VmExtension for Rv32I { ); inventory.add_executor(jalr_chip, Rv32JalrOpcode::iter().map(|x| x.global_opcode()))?; - let auipc_chip = Rv32AuipcChip::new( - Rv32RdWriteAdapterChip::new(execution_bus, program_bus, memory_bridge), - Rv32AuipcCoreChip::new(bitwise_lu_chip.clone()), - offline_memory.clone(), - ); - inventory.add_executor( - auipc_chip, - Rv32AuipcOpcode::iter().map(|x| x.global_opcode()), - )?; + // TODO + // let auipc_chip = Rv32AuipcChip::new( + // Rv32RdWriteAdapterChip::new(execution_bus, program_bus, memory_bridge), + // Rv32AuipcCoreChip::new(bitwise_lu_chip.clone()), + // offline_memory.clone(), + // ); + // inventory.add_executor( + // auipc_chip, + // Rv32AuipcOpcode::iter().map(|x| x.global_opcode()), + // )?; // There is no downside to adding phantom sub-executors, so we do it in the base extension. builder.add_phantom_sub_executor( diff --git a/extensions/rv32im/circuit/src/hintstore/mod.rs b/extensions/rv32im/circuit/src/hintstore/mod.rs index 69cd326ec4..ee21d09689 100644 --- a/extensions/rv32im/circuit/src/hintstore/mod.rs +++ b/extensions/rv32im/circuit/src/hintstore/mod.rs @@ -337,14 +337,14 @@ impl InstructionExecutor for Rv32HintStoreChip { let local_opcode = Rv32HintStoreOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - let (mem_ptr_read, mem_ptr_limbs) = - memory.read::(d, mem_ptr_ptr); + let (mem_ptr_read, mem_ptr_limbs) = todo!(); + // memory.read::(d, mem_ptr_ptr); let (num_words, num_words_read) = if local_opcode == HINT_STOREW { memory.increment_timestamp(); (1, None) } else { - let (num_words_read, num_words_limbs) = - memory.read::(d, num_words_ptr); + let (num_words_read, num_words_limbs) = todo!(); + // memory.read::(d, num_words_ptr); (u32::from_le_bytes(num_words_limbs), Some(num_words_read)) }; debug_assert_ne!(num_words, 0); @@ -377,12 +377,12 @@ impl InstructionExecutor for Rv32HintStoreChip { let data: [F; RV32_REGISTER_NUM_LIMBS] = std::array::from_fn(|_| streams.hint_stream.pop_front().unwrap()); - let (write, _) = memory.write( - e, - F::from_canonical_u32(mem_ptr + (RV32_REGISTER_NUM_LIMBS as u32 * word_index)), - &tmp_convert_to_u8s(data), - ); - record.hints.push((data, write)); + // let (write, _) = memory.write( + // e, + // F::from_canonical_u32(mem_ptr + (RV32_REGISTER_NUM_LIMBS as u32 * word_index)), + // &tmp_convert_to_u8s(data), + // ); + // record.hints.push((data, write)); } self.height += record.hints.len(); diff --git a/extensions/rv32im/circuit/src/hintstore/tests.rs b/extensions/rv32im/circuit/src/hintstore/tests.rs index 204070762c..40205978a1 100644 --- a/extensions/rv32im/circuit/src/hintstore/tests.rs +++ b/extensions/rv32im/circuit/src/hintstore/tests.rs @@ -40,15 +40,9 @@ fn set_and_execute( rng: &mut StdRng, opcode: Rv32HintStoreOpcode, ) { - let mem_ptr = rng.gen_range( - 0..(1 - << (tester - .memory_controller() - .borrow() - .mem_config() - .pointer_max_bits - - 2)), - ) << 2; + let mem_ptr = rng + .gen_range(0..(1 << (tester.memory_controller().mem_config().pointer_max_bits - 2))) + << 2; let b = gen_pointer(rng, 4); tester.write(1, b, decompose(mem_ptr)); @@ -80,15 +74,9 @@ fn set_and_execute_buffer( rng: &mut StdRng, opcode: Rv32HintStoreOpcode, ) { - let mem_ptr = rng.gen_range( - 0..(1 - << (tester - .memory_controller() - .borrow() - .mem_config() - .pointer_max_bits - - 2)), - ) << 2; + let mem_ptr = rng + .gen_range(0..(1 << (tester.memory_controller().mem_config().pointer_max_bits - 2))) + << 2; let b = gen_pointer(rng, 4); tester.write(1, b, decompose(mem_ptr)); @@ -140,7 +128,7 @@ fn rand_hintstore_test() { let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let range_checker_chip = tester.memory_controller().borrow().range_checker.clone(); + let range_checker_chip = tester.memory_controller().range_checker.clone(); let mut chip = Rv32HintStoreChip::::new( tester.execution_bus(), @@ -187,7 +175,7 @@ fn run_negative_hintstore_test( let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let range_checker_chip = tester.memory_controller().borrow().range_checker.clone(); + let range_checker_chip = tester.memory_controller().range_checker.clone(); let mut chip = Rv32HintStoreChip::::new( tester.execution_bus(), diff --git a/extensions/rv32im/circuit/src/jal_lui/core.rs b/extensions/rv32im/circuit/src/jal_lui/core.rs index 2ba10e615e..686380effd 100644 --- a/extensions/rv32im/circuit/src/jal_lui/core.rs +++ b/extensions/rv32im/circuit/src/jal_lui/core.rs @@ -1,11 +1,11 @@ -use std::{ - array, - borrow::{Borrow, BorrowMut}, -}; - -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, ImmInstruction, Result, VmAdapterInterface, - VmCoreAir, VmCoreChip, +use std::borrow::{Borrow, BorrowMut}; + +use openvm_circuit::{ + arch::{ + AdapterAirContext, ImmInstruction, Result, SingleTraceStep, VmAdapterInterface, VmCoreAir, + VmStateMut, + }, + system::memory::{online::TracingMemory, MemoryAuxColsFactory}, }; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, @@ -25,7 +25,13 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use crate::adapters::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, RV_J_TYPE_IMM_BITS}; +use crate::adapters::{ + tracing_write_reg, Rv32CondRdWriteAdapterCols, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + RV_J_TYPE_IMM_BITS, +}; + +pub(super) const ADAPTER_WIDTH: usize = size_of::>(); +const ADDITIONAL_BITS: u32 = 0b11000000; #[repr(C)] #[derive(Debug, Clone, AlignedBorrow)] @@ -36,7 +42,7 @@ pub struct Rv32JalLuiCoreCols { pub is_lui: T, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct Rv32JalLuiCoreAir { pub bus: BitwiseOperationLookupBus, } @@ -166,66 +172,80 @@ impl Rv32JalLuiCoreChip { } } -impl> VmCoreChip for Rv32JalLuiCoreChip -where - I::Writes: From<[[F; RV32_REGISTER_NUM_LIMBS]; 1]>, -{ - type Record = Rv32JalLuiCoreRecord; - type Air = Rv32JalLuiCoreAir; - - #[allow(clippy::type_complexity)] - fn execute_instruction( - &self, +impl SingleTraceStep for Rv32JalLuiCoreChip { + fn execute( + &mut self, + state: VmStateMut, instruction: &Instruction, - from_pc: u32, - _reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { + row_slice: &mut [F], + ) -> Result<()> { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(ADAPTER_WIDTH) }; + let adapter_row: &mut Rv32CondRdWriteAdapterCols = adapter_row.borrow_mut(); + let core_row: &mut Rv32JalLuiCoreCols = core_row.borrow_mut(); + let local_opcode = Rv32JalLuiOpcode::from_usize( instruction .opcode .local_opcode_idx(Rv32JalLuiOpcode::CLASS_OFFSET), ); - let imm = instruction.c; - + let from_timestamp = state.memory.timestamp(); + // `c` can be "negative" as a field element + let imm_f = instruction.c.as_canonical_u32(); let signed_imm = match local_opcode { JAL => { - // Note: signed_imm is a signed integer and imm is a field element - (imm + F::from_canonical_u32(1 << (RV_J_TYPE_IMM_BITS - 1))).as_canonical_u32() - as i32 - - (1 << (RV_J_TYPE_IMM_BITS - 1)) + if imm_f < (1 << (RV_J_TYPE_IMM_BITS - 1)) { + imm_f as i32 + } else { + let neg_imm_f = F::ORDER_U32 - imm_f; + debug_assert!(neg_imm_f < (1 << (RV_J_TYPE_IMM_BITS - 1))); + -(neg_imm_f as i32) + } } - LUI => imm.as_canonical_u32() as i32, + LUI => imm_f as i32, }; - let (to_pc, rd_data) = run_jal_lui(local_opcode, from_pc, signed_imm); + let (to_pc, rd_data) = run_jal_lui(local_opcode, *state.pc, signed_imm); + + if instruction.f != F::ZERO { + let rd_ptr = instruction.a.as_canonical_u32(); + let (t_prev, data_prev) = tracing_write_reg(state.memory, rd_ptr, &rd_data); + adapter_row.inner.rd_ptr = instruction.a; + adapter_row.inner.rd_aux_cols.set_prev( + F::from_canonical_u32(t_prev), + data_prev.map(F::from_canonical_u8), + ); + adapter_row.needs_write = F::ONE; + } else { + state.memory.increment_timestamp(); + } + adapter_row.inner.from_state.pc = F::from_canonical_u32(*state.pc); + adapter_row.inner.from_state.timestamp = F::from_canonical_u32(from_timestamp); + core_row.rd_data = rd_data.map(F::from_canonical_u8); + core_row.imm = instruction.c; + core_row.is_jal = F::from_bool(local_opcode == JAL); + core_row.is_lui = F::from_bool(local_opcode == LUI); + + *state.pc = to_pc; + Ok(()) + } - for i in 0..(RV32_REGISTER_NUM_LIMBS / 2) { - self.bitwise_lookup_chip - .request_range(rd_data[i * 2], rd_data[i * 2 + 1]); + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(ADAPTER_WIDTH) }; + let adapter_row: &mut Rv32CondRdWriteAdapterCols = adapter_row.borrow_mut(); + let core_row: &mut Rv32JalLuiCoreCols = core_row.borrow_mut(); + + if adapter_row.needs_write == F::ONE { + let timestamp = adapter_row.inner.from_state.timestamp.as_canonical_u32(); + mem_helper.fill_from_prev(timestamp, adapter_row.inner.rd_aux_cols.as_mut()); } - if local_opcode == JAL { - let last_limb_bits = PC_BITS - RV32_CELL_BITS * (RV32_REGISTER_NUM_LIMBS - 1); - let additional_bits = (last_limb_bits..RV32_CELL_BITS).fold(0, |acc, x| acc + (1 << x)); + let rd_data = core_row.rd_data.map(|x| x.as_canonical_u32()); + for pair in rd_data.chunks_exact(2) { + self.bitwise_lookup_chip.request_range(pair[0], pair[1]); + } + if core_row.is_jal == F::ONE { self.bitwise_lookup_chip - .request_xor(rd_data[3], additional_bits); + .request_xor(rd_data[3], ADDITIONAL_BITS); } - - let rd_data = rd_data.map(F::from_canonical_u32); - - let output = AdapterRuntimeContext { - to_pc: Some(to_pc), - writes: [rd_data].into(), - }; - - Ok(( - output, - Rv32JalLuiCoreRecord { - rd_data, - imm, - is_jal: local_opcode == JAL, - is_lui: local_opcode == LUI, - }, - )) } fn get_opcode_name(&self, opcode: usize) -> String { @@ -234,18 +254,6 @@ where Rv32JalLuiOpcode::from_usize(opcode - Rv32JalLuiOpcode::CLASS_OFFSET) ) } - - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let core_cols: &mut Rv32JalLuiCoreCols = row_slice.borrow_mut(); - core_cols.rd_data = record.rd_data; - core_cols.imm = record.imm; - core_cols.is_jal = F::from_bool(record.is_jal); - core_cols.is_lui = F::from_bool(record.is_lui); - } - - fn air(&self) -> &Self::Air { - &self.air - } } // returns (to_pc, rd_data) @@ -253,12 +261,10 @@ pub(super) fn run_jal_lui( opcode: Rv32JalLuiOpcode, pc: u32, imm: i32, -) -> (u32, [u32; RV32_REGISTER_NUM_LIMBS]) { +) -> (u32, [u8; RV32_REGISTER_NUM_LIMBS]) { match opcode { JAL => { - let rd_data = array::from_fn(|i| { - ((pc + DEFAULT_PC_STEP) >> (8 * i)) & ((1 << RV32_CELL_BITS) - 1) - }); + let rd_data = (pc + DEFAULT_PC_STEP).to_le_bytes(); let next_pc = pc as i32 + imm; assert!(next_pc >= 0); (next_pc as u32, rd_data) @@ -266,9 +272,14 @@ pub(super) fn run_jal_lui( LUI => { let imm = imm as u32; let rd = imm << 12; - let rd_data = - array::from_fn(|i| (rd >> (RV32_CELL_BITS * i)) & ((1 << RV32_CELL_BITS) - 1)); - (pc + DEFAULT_PC_STEP, rd_data) + (pc + DEFAULT_PC_STEP, rd.to_le_bytes()) } } } + +#[test] +fn test_additional_bits() { + let last_limb_bits = PC_BITS - RV32_CELL_BITS * (RV32_REGISTER_NUM_LIMBS - 1); + let additional_bits = (last_limb_bits..RV32_CELL_BITS).fold(0, |acc, x| acc + (1u32 << x)); + assert_eq!(additional_bits, ADDITIONAL_BITS); +} diff --git a/extensions/rv32im/circuit/src/jal_lui/mod.rs b/extensions/rv32im/circuit/src/jal_lui/mod.rs index 779b710bea..5479cee2e2 100644 --- a/extensions/rv32im/circuit/src/jal_lui/mod.rs +++ b/extensions/rv32im/circuit/src/jal_lui/mod.rs @@ -1,6 +1,6 @@ -use openvm_circuit::arch::VmChipWrapper; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; -use crate::adapters::Rv32CondRdWriteAdapterChip; +use crate::adapters::Rv32CondRdWriteAdapterAir; mod core; pub use core::*; @@ -8,4 +8,5 @@ pub use core::*; #[cfg(test)] mod tests; -pub type Rv32JalLuiChip = VmChipWrapper, Rv32JalLuiCoreChip>; +pub type Rv32JalLuiAir = VmAirWrapper; +pub type Rv32JalLuiChip = NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/jal_lui/tests.rs b/extensions/rv32im/circuit/src/jal_lui/tests.rs index 35e258cbfb..a74b9ae02d 100644 --- a/extensions/rv32im/circuit/src/jal_lui/tests.rs +++ b/extensions/rv32im/circuit/src/jal_lui/tests.rs @@ -2,7 +2,7 @@ use std::borrow::BorrowMut; use openvm_circuit::arch::{ testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - VmAdapterChip, + VmAirWrapper, }; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, @@ -10,7 +10,6 @@ use openvm_circuit_primitives::bitwise_op_lookup::{ use openvm_instructions::{instruction::Instruction, program::PC_BITS, LocalOpcode}; use openvm_rv32im_transpiler::Rv32JalLuiOpcode::{self, *}; use openvm_stark_backend::{ - p3_air::BaseAir, p3_field::{FieldAlgebra, PrimeField32}, p3_matrix::{dense::RowMajorMatrix, Matrix}, utils::disable_debug_builder, @@ -20,19 +19,41 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; -use super::{run_jal_lui, Rv32JalLuiChip, Rv32JalLuiCoreChip}; +use super::{run_jal_lui, Rv32JalLuiChip, Rv32JalLuiCoreChip, ADAPTER_WIDTH}; use crate::{ adapters::{ - Rv32CondRdWriteAdapterChip, Rv32CondRdWriteAdapterCols, RV32_CELL_BITS, - RV32_REGISTER_NUM_LIMBS, RV_IS_TYPE_IMM_BITS, + Rv32CondRdWriteAdapterAir, Rv32CondRdWriteAdapterCols, Rv32RdWriteAdapterAir, + RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, RV_IS_TYPE_IMM_BITS, }, jal_lui::Rv32JalLuiCoreCols, }; const IMM_BITS: usize = 20; const LIMB_MAX: u32 = (1 << RV32_CELL_BITS) - 1; +const MAX_INS_CAPACITY: usize = 256; + type F = BabyBear; +fn create_test_chip( + tester: &VmChipTestBuilder, +) -> ( + Rv32JalLuiChip, + SharedBitwiseOperationLookupChip, +) { + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + let adapter_air = Rv32CondRdWriteAdapterAir::new(Rv32RdWriteAdapterAir::new( + tester.memory_bridge(), + tester.execution_bridge(), + )); + let core = Rv32JalLuiCoreChip::new(bitwise_chip.clone()); + let air = VmAirWrapper::new(adapter_air, core.air); + ( + Rv32JalLuiChip::::new(air, core, MAX_INS_CAPACITY, tester.memory_helper()), + bitwise_chip, + ) +} + fn set_and_execute( tester: &mut VmChipTestBuilder, chip: &mut Rv32JalLuiChip, @@ -71,7 +92,7 @@ fn set_and_execute( let rd_data = if needs_write { rd_data } else { [0; 4] }; assert_eq!(next_pc, final_pc); - assert_eq!(rd_data.map(F::from_canonical_u32), tester.read::<4>(1, a)); + assert_eq!(rd_data.map(F::from_canonical_u8), tester.read::<4>(1, a)); } /////////////////////////////////////////////////////////////////////////////////////// @@ -84,17 +105,9 @@ fn set_and_execute( #[test] fn rand_jal_lui_test() { let mut rng = create_seeded_rng(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut tester = VmChipTestBuilder::default(); - let adapter = Rv32CondRdWriteAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - ); - let core = Rv32JalLuiCoreChip::new(bitwise_chip.clone()); - let mut chip = Rv32JalLuiChip::::new(adapter, core, tester.offline_memory_mutex_arc()); + let (mut chip, bitwise_chip) = create_test_chip(&tester); let num_tests: usize = 100; for _ in 0..num_tests { @@ -126,18 +139,9 @@ fn run_negative_jal_lui_test( expected_error: VerificationError, ) { let mut rng = create_seeded_rng(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut tester = VmChipTestBuilder::default(); - let adapter = Rv32CondRdWriteAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - ); - let adapter_width = BaseAir::::width(adapter.air()); - let core = Rv32JalLuiCoreChip::new(bitwise_chip.clone()); - let mut chip = Rv32JalLuiChip::::new(adapter, core, tester.offline_memory_mutex_arc()); + let (mut chip, bitwise_chip) = create_test_chip(&tester); set_and_execute( &mut tester, @@ -157,7 +161,7 @@ fn run_negative_jal_lui_test( { let mut trace_row = jal_lui_trace.row_slice(0).to_vec(); - let (adapter_row, core_row) = trace_row.split_at_mut(adapter_width); + let (adapter_row, core_row) = trace_row.split_at_mut(ADAPTER_WIDTH); let adapter_cols: &mut Rv32CondRdWriteAdapterCols = adapter_row.borrow_mut(); let core_cols: &mut Rv32JalLuiCoreCols = core_row.borrow_mut(); @@ -310,17 +314,10 @@ fn overflow_negative_tests() { #[test] fn execute_roundtrip_sanity_test() { let mut rng = create_seeded_rng(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut tester = VmChipTestBuilder::default(); - let adapter = Rv32CondRdWriteAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - ); - let core = Rv32JalLuiCoreChip::new(bitwise_chip); - let mut chip = Rv32JalLuiChip::::new(adapter, core, tester.offline_memory_mutex_arc()); + let (mut chip, _) = create_test_chip(&tester); + let num_tests: usize = 10; for _ in 0..num_tests { set_and_execute(&mut tester, &mut chip, &mut rng, JAL, None, None); diff --git a/extensions/rv32im/circuit/src/jalr/tests.rs b/extensions/rv32im/circuit/src/jalr/tests.rs index e22d97967f..30eca37dda 100644 --- a/extensions/rv32im/circuit/src/jalr/tests.rs +++ b/extensions/rv32im/circuit/src/jalr/tests.rs @@ -95,7 +95,7 @@ fn rand_jalr_test() { let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().borrow().range_checker.clone(); + let range_checker_chip = tester.memory_controller().range_checker.clone(); let adapter = Rv32JalrAdapterChip::::new( tester.execution_bus(), @@ -150,7 +150,7 @@ fn run_negative_jalr_test( let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().borrow().range_checker.clone(); + let range_checker_chip = tester.memory_controller().range_checker.clone(); let adapter = Rv32JalrAdapterChip::::new( tester.execution_bus(), @@ -307,7 +307,7 @@ fn execute_roundtrip_sanity_test() { let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().borrow().range_checker.clone(); + let range_checker_chip = tester.memory_controller().range_checker.clone(); let adapter = Rv32JalrAdapterChip::::new( tester.execution_bus(), diff --git a/extensions/rv32im/circuit/src/load_sign_extend/tests.rs b/extensions/rv32im/circuit/src/load_sign_extend/tests.rs index 0fe6d859d1..17f172587b 100644 --- a/extensions/rv32im/circuit/src/load_sign_extend/tests.rs +++ b/extensions/rv32im/circuit/src/load_sign_extend/tests.rs @@ -55,13 +55,7 @@ fn set_and_execute( _ => unreachable!(), }; let ptr_val = rng.gen_range( - 0..(1 - << (tester - .memory_controller() - .borrow() - .mem_config() - .pointer_max_bits - - alignment)), + 0..(1 << (tester.memory_controller().mem_config().pointer_max_bits - alignment)), ) << alignment; let rs1 = rs1 @@ -128,7 +122,7 @@ fn rand_load_sign_extend_test() { setup_tracing(); let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().borrow().range_checker.clone(); + let range_checker_chip = tester.memory_controller().range_checker.clone(); let adapter = Rv32LoadStoreAdapterChip::::new( tester.execution_bus(), tester.program_bus(), @@ -190,7 +184,7 @@ fn run_negative_loadstore_test( ) { let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().borrow().range_checker.clone(); + let range_checker_chip = tester.memory_controller().range_checker.clone(); let adapter = Rv32LoadStoreAdapterChip::::new( tester.execution_bus(), tester.program_bus(), @@ -298,7 +292,7 @@ fn loadstore_negative_tests() { fn execute_roundtrip_sanity_test() { let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().borrow().range_checker.clone(); + let range_checker_chip = tester.memory_controller().range_checker.clone(); let adapter = Rv32LoadStoreAdapterChip::::new( tester.execution_bus(), tester.program_bus(), diff --git a/extensions/rv32im/circuit/src/loadstore/tests.rs b/extensions/rv32im/circuit/src/loadstore/tests.rs index 0fbfa137b9..c7e388fd35 100644 --- a/extensions/rv32im/circuit/src/loadstore/tests.rs +++ b/extensions/rv32im/circuit/src/loadstore/tests.rs @@ -55,13 +55,7 @@ fn set_and_execute( }; let ptr_val = rng.gen_range( - 0..(1 - << (tester - .memory_controller() - .borrow() - .mem_config() - .pointer_max_bits - - alignment)), + 0..(1 << (tester.memory_controller().mem_config().pointer_max_bits - alignment)), ) << alignment; let rs1 = rs1 @@ -148,7 +142,7 @@ fn rand_loadstore_test() { setup_tracing(); let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().borrow().range_checker.clone(); + let range_checker_chip = tester.memory_controller().range_checker.clone(); let adapter = Rv32LoadStoreAdapterChip::::new( tester.execution_bus(), tester.program_bus(), @@ -253,7 +247,7 @@ fn run_negative_loadstore_test( ) { let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().borrow().range_checker.clone(); + let range_checker_chip = tester.memory_controller().range_checker.clone(); let adapter = Rv32LoadStoreAdapterChip::::new( tester.execution_bus(), tester.program_bus(), @@ -436,7 +430,7 @@ fn negative_wrong_address_space_tests() { fn execute_roundtrip_sanity_test() { let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().borrow().range_checker.clone(); + let range_checker_chip = tester.memory_controller().range_checker.clone(); let adapter = Rv32LoadStoreAdapterChip::::new( tester.execution_bus(), tester.program_bus(), diff --git a/extensions/rv32im/circuit/src/shift/tests.rs b/extensions/rv32im/circuit/src/shift/tests.rs index 7a3ef6e72c..449c505196 100644 --- a/extensions/rv32im/circuit/src/shift/tests.rs +++ b/extensions/rv32im/circuit/src/shift/tests.rs @@ -57,7 +57,7 @@ fn run_rv32_shift_rand_test(opcode: ShiftOpcode, num_ops: usize) { ), ShiftCoreChip::new( bitwise_chip.clone(), - tester.memory_controller().borrow().range_checker.clone(), + tester.memory_controller().range_checker.clone(), ShiftOpcode::CLASS_OFFSET, ), tester.offline_memory_mutex_arc(), @@ -145,7 +145,7 @@ fn run_rv32_shift_negative_test( let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().borrow().range_checker.clone(); + let range_checker_chip = tester.memory_controller().range_checker.clone(); let mut chip = Rv32ShiftTestChip::::new( TestAdapterChip::new( vec![[b.map(F::from_canonical_u32), c.map(F::from_canonical_u32)].concat()], From 13d62341fc93be0428936202520654c3a262ab1c Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Fri, 18 Apr 2025 16:39:33 -0400 Subject: [PATCH 05/49] feat(new-execution): add execute functions for rv32im instructions (#1590) - introduce a new generic `InsExecutorE1` trait - add `InsExecutor::execute_e1` for rv32im instructions --- crates/circuits/mod-builder/src/core_chip.rs | 23 +++- crates/vm/src/arch/execution.rs | 22 ++- crates/vm/src/arch/integration_api.rs | 41 +++++- crates/vm/src/arch/segment.rs | 1 + crates/vm/src/system/public_values/core.rs | 20 ++- .../algebra/circuit/src/modular_chip/is_eq.rs | 24 +++- .../algebra/circuit/src/modular_chip/tests.rs | 27 +++- extensions/native/circuit/src/castf/core.rs | 23 +++- .../circuit/src/field_arithmetic/core.rs | 23 +++- .../circuit/src/field_extension/core.rs | 23 +++- .../native/circuit/src/loadstore/core.rs | 25 +++- extensions/rv32im/circuit/src/auipc/core.rs | 40 +++++- .../rv32im/circuit/src/base_alu/core.rs | 71 +++++++++- .../rv32im/circuit/src/branch_eq/core.rs | 58 +++++++- .../rv32im/circuit/src/branch_lt/core.rs | 59 +++++++- extensions/rv32im/circuit/src/divrem/core.rs | 68 +++++++++- extensions/rv32im/circuit/src/jal_lui/core.rs | 128 +++++++++++++++++- extensions/rv32im/circuit/src/jalr/core.rs | 62 ++++++++- .../rv32im/circuit/src/less_than/core.rs | 75 +++++++++- .../circuit/src/load_sign_extend/core.rs | 85 +++++++++++- .../rv32im/circuit/src/loadstore/core.rs | 106 ++++++++++++++- extensions/rv32im/circuit/src/mul/core.rs | 60 +++++++- extensions/rv32im/circuit/src/mulh/core.rs | 58 +++++++- extensions/rv32im/circuit/src/shift/core.rs | 73 +++++++++- 24 files changed, 1117 insertions(+), 78 deletions(-) diff --git a/crates/circuits/mod-builder/src/core_chip.rs b/crates/circuits/mod-builder/src/core_chip.rs index 30e9c65dbb..f7b026da72 100644 --- a/crates/circuits/mod-builder/src/core_chip.rs +++ b/crates/circuits/mod-builder/src/core_chip.rs @@ -1,9 +1,12 @@ use itertools::Itertools; use num_bigint::BigUint; use num_traits::Zero; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, DynAdapterInterface, DynArray, MinimalInstruction, - Result, VmAdapterInterface, VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterRuntimeContext, DynAdapterInterface, DynArray, InsExecutorE1, + MinimalInstruction, Result, VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ var_range::SharedVariableRangeCheckerChip, SubAir, TraceSubRowGenerator, @@ -311,6 +314,20 @@ where } } +impl InsExecutorE1 for FieldExpressionCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + _state: &mut VmExecutionState, + _instruction: &Instruction, + ) -> Result<()> { + todo!("Implement execute_e1") + } +} + impl FieldExpressionCoreChip { // We will be setting is_valid = 0. That forces all flags be 0 (otherwise setup will be -1). // We generate a dummy row with all flags set to 0, then we set is_valid = 0. diff --git a/crates/vm/src/arch/execution.rs b/crates/vm/src/arch/execution.rs index a3aad69364..d8d6202e26 100644 --- a/crates/vm/src/arch/execution.rs +++ b/crates/vm/src/arch/execution.rs @@ -6,13 +6,16 @@ use openvm_instructions::{ }; use openvm_stark_backend::{ interaction::{BusIndex, InteractionBuilder, PermutationCheckBus}, - p3_field::FieldAlgebra, + p3_field::{FieldAlgebra, PrimeField32}, }; use serde::{Deserialize, Serialize}; use thiserror::Error; -use super::Streams; -use crate::system::{memory::MemoryController, program::ProgramBus}; +use super::{Streams, VmExecutionState}; +use crate::system::{ + memory::{online::GuestMemory, MemoryController}, + program::ProgramBus, +}; pub type Result = std::result::Result; @@ -95,6 +98,19 @@ pub trait InstructionExecutor { fn get_opcode_name(&self, opcode: usize) -> String; } +/// New trait for instruction execution +pub trait InsExecutorE1 +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + state: &mut VmExecutionState, + instruction: &Instruction, + ) -> Result<()>; +} + impl> InstructionExecutor for RefCell { fn execute( &mut self, diff --git a/crates/vm/src/arch/integration_api.rs b/crates/vm/src/arch/integration_api.rs index 392d7e6ca8..c805768b26 100644 --- a/crates/vm/src/arch/integration_api.rs +++ b/crates/vm/src/arch/integration_api.rs @@ -21,10 +21,13 @@ use openvm_stark_backend::{ }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use super::{ExecutionError, ExecutionState, InstructionExecutor, Result, VmStateMut}; +use super::{ + ExecutionError, ExecutionState, InsExecutorE1, InstructionExecutor, Result, VmExecutionState, + VmStateMut, +}; use crate::system::memory::{ - online::TracingMemory, MemoryAuxColsFactory, MemoryController, OfflineMemory, - SharedMemoryHelper, + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, MemoryController, OfflineMemory, SharedMemoryHelper, }; /// The interface between primitive AIR and machine adapter AIR. @@ -420,6 +423,38 @@ where } } +// TODO(ayush): delete +impl InsExecutorE1 for VmChipWrapper +where + Mem: GuestMemory, + F: PrimeField32, + A: VmAdapterChip + Send + Sync, + M: VmCoreChip + InsExecutorE1 + Send + Sync, +{ + fn execute_e1( + &mut self, + state: &mut VmExecutionState, + instruction: &Instruction, + ) -> Result<()> { + self.core.execute_e1(state, instruction) + } +} + +impl InsExecutorE1 for NewVmChipWrapper +where + Mem: GuestMemory, + F: PrimeField32, + C: InsExecutorE1, +{ + fn execute_e1( + &mut self, + state: &mut VmExecutionState, + instruction: &Instruction, + ) -> Result<()> { + self.inner.execute_e1(state, instruction) + } +} + // Note[jpw]: the statement we want is: // - when A::Air is an AdapterAir for all AirBuilders needed by stark-backend // - and when M::Air is an CoreAir for all AirBuilders needed by stark-backend, diff --git a/crates/vm/src/arch/segment.rs b/crates/vm/src/arch/segment.rs index 9b1f95eff1..a7361dfda6 100644 --- a/crates/vm/src/arch/segment.rs +++ b/crates/vm/src/arch/segment.rs @@ -99,6 +99,7 @@ impl TracegenVmExecutionState { where F: PrimeField32, { + // TODO(ayush): remove this clone let memory = memory_controller.memory_image().clone(); let ctx = TracegenCtx::new(memory_controller.timestamp()); TracegenVmExecutionState::new(pc, memory, ctx) diff --git a/crates/vm/src/system/public_values/core.rs b/crates/vm/src/system/public_values/core.rs index de189f101b..871e7d25c0 100644 --- a/crates/vm/src/system/public_values/core.rs +++ b/crates/vm/src/system/public_values/core.rs @@ -14,10 +14,10 @@ use serde::{Deserialize, Serialize}; use crate::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, MinimalInstruction, - Result, VmAdapterInterface, VmCoreAir, VmCoreChip, + AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, InsExecutorE1, + MinimalInstruction, Result, VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, }, - system::public_values::columns::PublicValuesCoreColsView, + system::{memory::online::GuestMemory, public_values::columns::PublicValuesCoreColsView}, }; pub(crate) type AdapterInterface = BasicAdapterInterface, 2, 0, 1, 1>; pub(crate) type AdapterInterfaceReads = as VmAdapterInterface>::Reads; @@ -191,3 +191,17 @@ impl VmCoreChip> for PublicValuesCoreChi &self.air } } + +impl InsExecutorE1 for PublicValuesCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + _state: &mut VmExecutionState, + _instruction: &Instruction, + ) -> Result<()> { + todo!("Implement execute_e1") + } +} diff --git a/extensions/algebra/circuit/src/modular_chip/is_eq.rs b/extensions/algebra/circuit/src/modular_chip/is_eq.rs index fe91585466..57187d0ce2 100644 --- a/extensions/algebra/circuit/src/modular_chip/is_eq.rs +++ b/extensions/algebra/circuit/src/modular_chip/is_eq.rs @@ -5,9 +5,12 @@ use std::{ use num_bigint::BigUint; use openvm_algebra_transpiler::Rv32ModularArithmeticOpcode; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, MinimalInstruction, Result, VmAdapterInterface, - VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, + VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ bigint::utils::big_uint_to_limbs, @@ -423,6 +426,21 @@ where } } +impl + InsExecutorE1 for ModularIsEqualCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + _state: &mut VmExecutionState, + _instruction: &Instruction, + ) -> Result<()> { + todo!("Implement execute_e1") + } +} + // Returns (cmp_result, diff_idx) pub(super) fn run_unsigned_less_than( x: &[u32; NUM_LIMBS], diff --git a/extensions/algebra/circuit/src/modular_chip/tests.rs b/extensions/algebra/circuit/src/modular_chip/tests.rs index 1ad3310f76..86df6161f3 100644 --- a/extensions/algebra/circuit/src/modular_chip/tests.rs +++ b/extensions/algebra/circuit/src/modular_chip/tests.rs @@ -3,10 +3,14 @@ use std::{array::from_fn, borrow::BorrowMut}; use num_bigint::BigUint; use num_traits::Zero; use openvm_algebra_transpiler::Rv32ModularArithmeticOpcode; -use openvm_circuit::arch::{ - instructions::LocalOpcode, - testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - AdapterRuntimeContext, Result, VmAdapterInterface, VmChipWrapper, VmCoreChip, +use openvm_circuit::{ + arch::{ + instructions::LocalOpcode, + testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, + AdapterRuntimeContext, InsExecutorE1, Result, VmAdapterInterface, VmChipWrapper, + VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ bigint::utils::{big_uint_to_limbs, secp256k1_coord_prime, secp256k1_scalar_prime}, @@ -491,6 +495,21 @@ where } } +impl + InsExecutorE1 for BadModularIsEqualCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + _state: &mut VmExecutionState, + _instruction: &Instruction, + ) -> Result<()> { + todo!("Implement execute_e1") + } +} + // Test that passes the wrong modulus in the setup instruction. // This proof should fail to verify. fn test_is_equal_setup_bad< diff --git a/extensions/native/circuit/src/castf/core.rs b/extensions/native/circuit/src/castf/core.rs index 664767e35e..92beb1eaf1 100644 --- a/extensions/native/circuit/src/castf/core.rs +++ b/extensions/native/circuit/src/castf/core.rs @@ -1,8 +1,11 @@ use std::borrow::{Borrow, BorrowMut}; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, MinimalInstruction, Result, VmAdapterInterface, - VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, + VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives::var_range::{ SharedVariableRangeCheckerChip, VariableRangeCheckerBus, @@ -189,6 +192,20 @@ where } } +impl InsExecutorE1 for CastFCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + _state: &mut VmExecutionState, + _instruction: &Instruction, + ) -> Result<()> { + todo!("Implement execute_e1") + } +} + pub struct CastF; impl CastF { pub(super) fn solve(y: u32) -> [u32; RV32_REGISTER_NUM_LIMBS] { diff --git a/extensions/native/circuit/src/field_arithmetic/core.rs b/extensions/native/circuit/src/field_arithmetic/core.rs index c813f6a066..f91f7fd946 100644 --- a/extensions/native/circuit/src/field_arithmetic/core.rs +++ b/extensions/native/circuit/src/field_arithmetic/core.rs @@ -1,9 +1,12 @@ use std::borrow::{Borrow, BorrowMut}; use itertools::izip; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, MinimalInstruction, Result, VmAdapterInterface, - VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, + VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{instruction::Instruction, LocalOpcode}; @@ -202,6 +205,20 @@ where } } +impl InsExecutorE1 for FieldArithmeticCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + _state: &mut VmExecutionState, + _instruction: &Instruction, + ) -> Result<()> { + todo!("Implement execute_e1") + } +} + pub struct FieldArithmetic; impl FieldArithmetic { pub(super) fn run_field_arithmetic( diff --git a/extensions/native/circuit/src/field_extension/core.rs b/extensions/native/circuit/src/field_extension/core.rs index d8c83fabdd..6fe168a714 100644 --- a/extensions/native/circuit/src/field_extension/core.rs +++ b/extensions/native/circuit/src/field_extension/core.rs @@ -5,9 +5,12 @@ use std::{ }; use itertools::izip; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, MinimalInstruction, Result, VmAdapterInterface, - VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, + VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{instruction::Instruction, LocalOpcode}; @@ -228,6 +231,20 @@ where } } +impl InsExecutorE1 for FieldExtensionCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + _state: &mut VmExecutionState, + _instruction: &Instruction, + ) -> Result<()> { + todo!("Implement execute_e1") + } +} + pub struct FieldExtension; impl FieldExtension { pub(super) fn solve( diff --git a/extensions/native/circuit/src/loadstore/core.rs b/extensions/native/circuit/src/loadstore/core.rs index 60c7bbdbdb..72b60b3e89 100644 --- a/extensions/native/circuit/src/loadstore/core.rs +++ b/extensions/native/circuit/src/loadstore/core.rs @@ -4,9 +4,13 @@ use std::{ sync::{Arc, Mutex, OnceLock}, }; -use openvm_circuit::arch::{ - instructions::LocalOpcode, AdapterAirContext, AdapterRuntimeContext, ExecutionError, Result, - Streams, VmAdapterInterface, VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + instructions::LocalOpcode, AdapterAirContext, AdapterRuntimeContext, ExecutionError, + InsExecutorE1, Result, Streams, VmAdapterInterface, VmCoreAir, VmCoreChip, + VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::instruction::Instruction; @@ -197,3 +201,18 @@ where &self.air } } + +impl InsExecutorE1 + for NativeLoadStoreCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + _state: &mut VmExecutionState, + _instruction: &Instruction, + ) -> Result<()> { + todo!("Implement execute_e1") + } +} diff --git a/extensions/rv32im/circuit/src/auipc/core.rs b/extensions/rv32im/circuit/src/auipc/core.rs index e0c5eb8957..394a0b8737 100644 --- a/extensions/rv32im/circuit/src/auipc/core.rs +++ b/extensions/rv32im/circuit/src/auipc/core.rs @@ -6,10 +6,13 @@ use std::{ use openvm_circuit::{ arch::{ - AdapterAirContext, ImmInstruction, Result, SingleTraceStep, VmAdapterInterface, VmCoreAir, - VmStateMut, + AdapterAirContext, ImmInstruction, InsExecutorE1, Result, SingleTraceStep, + VmAdapterInterface, VmCoreAir, VmExecutionState, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::{online::TracingMemory, MemoryAuxColsFactory}, }; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, @@ -293,6 +296,37 @@ impl SingleTraceStep for Rv32AuipcCoreChip { } } +impl InsExecutorE1 for Rv32AuipcCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + state: &mut VmExecutionState, + instruction: &Instruction, + ) -> Result<()> { + let Instruction { + opcode, a, c: imm, .. + } = instruction; + + let local_opcode = + Rv32AuipcOpcode::from_usize(opcode.local_opcode_idx(Rv32AuipcOpcode::CLASS_OFFSET)); + + let imm = imm.as_canonical_u32(); + let rd_bytes = run_auipc(local_opcode, state.pc, imm); + + let rd_addr = a.as_canonical_u32(); + unsafe { + state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes); + } + + state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) + } +} + // returns rd_data pub(super) fn run_auipc( _opcode: Rv32AuipcOpcode, diff --git a/extensions/rv32im/circuit/src/base_alu/core.rs b/extensions/rv32im/circuit/src/base_alu/core.rs index a87418cc91..d0c0a9bb77 100644 --- a/extensions/rv32im/circuit/src/base_alu/core.rs +++ b/extensions/rv32im/circuit/src/base_alu/core.rs @@ -3,16 +3,24 @@ use std::{ borrow::{Borrow, BorrowMut}, }; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, MinimalInstruction, Result, VmAdapterInterface, - VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, + VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, utils::not, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; +use openvm_instructions::{ + instruction::Instruction, + program::DEFAULT_PC_STEP, + riscv::{RV32_IMM_AS, RV32_REGISTER_AS}, + LocalOpcode, +}; use openvm_rv32im_transpiler::BaseAluOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -270,6 +278,61 @@ where } } +impl InsExecutorE1 + for BaseAluCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + state: &mut VmExecutionState, + instruction: &Instruction, + ) -> Result<()> { + let Instruction { + opcode, a, b, c, e, .. + } = instruction; + + let local_opcode = BaseAluOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); + + let rs1_addr = b.as_canonical_u32(); + let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; + + let rs2_bytes = if e.as_canonical_u32() == RV32_IMM_AS { + // Use immediate value + let imm = c.as_canonical_u32(); + // Convert imm from u32 to [u8; NUM_LIMBS] + let imm_bytes = imm.to_le_bytes(); + // TODO(ayush): remove this + let mut rs2_bytes = [0u8; NUM_LIMBS]; + rs2_bytes[..NUM_LIMBS].copy_from_slice(&imm_bytes[..NUM_LIMBS]); + rs2_bytes + } else { + // Read from register + let rs2_addr = c.as_canonical_u32(); + let rs2_bytes: [u8; NUM_LIMBS] = + unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; + rs2_bytes + }; + + // TODO(ayush): avoid this conversion + let rs1_bytes: [u32; NUM_LIMBS] = rs1_bytes.map(|x| x as u32); + let rs2_bytes: [u32; NUM_LIMBS] = rs2_bytes.map(|y| y as u32); + + // TODO(ayush): should this be [u8; 4]? + let rd_bytes = run_alu::(local_opcode, &rs1_bytes, &rs2_bytes); + let rd_bytes = rd_bytes.map(|x| x as u8); + + // Write result back to destination register + let rd_addr = a.as_canonical_u32(); + unsafe { state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes) }; + + state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) + } +} + pub(super) fn run_alu( opcode: BaseAluOpcode, x: &[u32; NUM_LIMBS], diff --git a/extensions/rv32im/circuit/src/branch_eq/core.rs b/extensions/rv32im/circuit/src/branch_eq/core.rs index bb04d86ee5..62db374660 100644 --- a/extensions/rv32im/circuit/src/branch_eq/core.rs +++ b/extensions/rv32im/circuit/src/branch_eq/core.rs @@ -3,13 +3,16 @@ use std::{ borrow::{Borrow, BorrowMut}, }; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, ImmInstruction, Result, VmAdapterInterface, - VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterRuntimeContext, ImmInstruction, InsExecutorE1, Result, + VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives::utils::not; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; +use openvm_instructions::{instruction::Instruction, riscv::RV32_REGISTER_AS, LocalOpcode}; use openvm_rv32im_transpiler::BranchEqualOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -232,6 +235,53 @@ where } } +impl InsExecutorE1 + for BranchEqualCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + state: &mut VmExecutionState, + instruction: &Instruction, + ) -> Result<()> { + let Instruction { + opcode, + a, + b, + c: imm, + .. + } = instruction; + + let branch_eq_opcode = + BranchEqualOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); + + let rs1_addr = a.as_canonical_u32(); + let rs2_addr = b.as_canonical_u32(); + + let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; + let rs2_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; + + // TODO(ayush): avoid this conversion + let rs1_bytes: [u32; NUM_LIMBS] = rs1_bytes.map(|x| x as u32); + let rs2_bytes: [u32; NUM_LIMBS] = rs2_bytes.map(|y| y as u32); + + // TODO(ayush): probably don't need the other values + let (cmp_result, _, _) = run_eq::(branch_eq_opcode, &rs1_bytes, &rs2_bytes); + + if cmp_result { + let imm = imm.as_canonical_u32(); + state.pc = state.pc.wrapping_add(imm); + } else { + // TODO(ayush): why not DEFAULT_PC_STEP or some constant? + state.pc = state.pc.wrapping_add(self.air.pc_step); + } + + Ok(()) + } +} + // Returns (cmp_result, diff_idx, x[diff_idx] - y[diff_idx]) pub(super) fn run_eq( local_opcode: BranchEqualOpcode, diff --git a/extensions/rv32im/circuit/src/branch_lt/core.rs b/extensions/rv32im/circuit/src/branch_lt/core.rs index 3eebb02146..45e790b8d6 100644 --- a/extensions/rv32im/circuit/src/branch_lt/core.rs +++ b/extensions/rv32im/circuit/src/branch_lt/core.rs @@ -3,16 +3,21 @@ use std::{ borrow::{Borrow, BorrowMut}, }; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, ImmInstruction, Result, VmAdapterInterface, - VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterRuntimeContext, ImmInstruction, InsExecutorE1, Result, + VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, utils::not, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; +use openvm_instructions::{ + instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_REGISTER_AS, LocalOpcode, +}; use openvm_rv32im_transpiler::BranchLessThanOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -355,6 +360,52 @@ where } } +impl InsExecutorE1 + for BranchLessThanCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + state: &mut VmExecutionState, + instruction: &Instruction, + ) -> Result<()> { + let Instruction { + opcode, + a, + b, + c: imm, + .. + } = instruction; + + let blt_opcode = BranchLessThanOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); + + let rs1_addr = a.as_canonical_u32(); + let rs2_addr = b.as_canonical_u32(); + + // TODO(ayush): why even have NUM_LIMBS when it is equal to RV32_REGISTER_NUM_LIMBS? + let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; + let rs2_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; + + // TODO(ayush): why is this conversion necessary? + let rs1_bytes: [u32; NUM_LIMBS] = rs1_bytes.map(|x| x as u32); + let rs2_bytes: [u32; NUM_LIMBS] = rs2_bytes.map(|y| y as u32); + + let (cmp_result, _, _, _) = + run_cmp::(blt_opcode, &rs1_bytes, &rs2_bytes); + + if cmp_result { + let imm = imm.as_canonical_u32(); + state.pc = state.pc.wrapping_add(imm); + } else { + state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + } + + Ok(()) + } +} + // Returns (cmp_result, diff_idx, x_sign, y_sign) pub(super) fn run_cmp( local_opcode: BranchLessThanOpcode, diff --git a/extensions/rv32im/circuit/src/divrem/core.rs b/extensions/rv32im/circuit/src/divrem/core.rs index bad043d582..d56c93ea0b 100644 --- a/extensions/rv32im/circuit/src/divrem/core.rs +++ b/extensions/rv32im/circuit/src/divrem/core.rs @@ -5,9 +5,12 @@ use std::{ use num_bigint::BigUint; use num_integer::Integer; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, MinimalInstruction, Result, VmAdapterInterface, - VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, + VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, @@ -15,7 +18,9 @@ use openvm_circuit_primitives::{ utils::{not, select}, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; +use openvm_instructions::{ + instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_REGISTER_AS, LocalOpcode, +}; use openvm_rv32im_transpiler::DivRemOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -550,6 +555,61 @@ where } } +impl InsExecutorE1 + for DivRemCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + state: &mut VmExecutionState, + instruction: &Instruction, + ) -> Result<()> { + let Instruction { + opcode, a, b, c, .. + } = *instruction; + + // Determine opcode and operation type + let divrem_opcode = DivRemOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); + + // Read input registers + let rs1_addr = b.as_canonical_u32(); + let rs2_addr = c.as_canonical_u32(); + + let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; + let rs2_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; + + // TODO(ayush): remove this conversion + let rs1_bytes = rs1_bytes.map(|x| x as u32); + let rs2_bytes = rs2_bytes.map(|y| y as u32); + + let is_div = divrem_opcode == DivRemOpcode::DIV || divrem_opcode == DivRemOpcode::DIVU; + let is_signed = divrem_opcode == DivRemOpcode::DIV || divrem_opcode == DivRemOpcode::REM; + + // Perform division/remainder computation + let (q, r, _, _, _, _) = + run_divrem::(is_signed, &rs1_bytes, &rs2_bytes); + + // Determine result based on operation type (DIV or REM) + let rd_bytes = if is_div { + q.map(|x| x as u8) + } else { + r.map(|x| x as u8) + }; + + // Write result to destination register + let rd_addr = a.as_canonical_u32(); + unsafe { + state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes); + } + + state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) + } +} + // Returns (quotient, remainder, x_sign, y_sign, q_sign, case) where case = 0 for normal, 1 // for zero divisor, and 2 for signed overflow pub(super) fn run_divrem( diff --git a/extensions/rv32im/circuit/src/jal_lui/core.rs b/extensions/rv32im/circuit/src/jal_lui/core.rs index 686380effd..d70745b626 100644 --- a/extensions/rv32im/circuit/src/jal_lui/core.rs +++ b/extensions/rv32im/circuit/src/jal_lui/core.rs @@ -2,10 +2,13 @@ use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, ImmInstruction, Result, SingleTraceStep, VmAdapterInterface, VmCoreAir, - VmStateMut, + AdapterAirContext, AdapterRuntimeContext, ImmInstruction, InsExecutorE1, Result, + SingleTraceStep, VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::{online::TracingMemory, MemoryAuxColsFactory}, }; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, @@ -14,6 +17,7 @@ use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{ instruction::Instruction, program::{DEFAULT_PC_STEP, PC_BITS}, + riscv::RV32_REGISTER_AS, LocalOpcode, }; use openvm_rv32im_transpiler::Rv32JalLuiOpcode::{self, *}; @@ -256,6 +260,124 @@ impl SingleTraceStep for Rv32JalLuiCoreChip { } } +impl> VmCoreChip for Rv32JalLuiCoreChip +where + I::Writes: From<[[F; RV32_REGISTER_NUM_LIMBS]; 1]>, +{ + type Record = Rv32JalLuiCoreRecord; + type Air = Rv32JalLuiCoreAir; + + #[allow(clippy::type_complexity)] + fn execute_instruction( + &self, + instruction: &Instruction, + from_pc: u32, + _reads: I::Reads, + ) -> Result<(AdapterRuntimeContext, Self::Record)> { + let local_opcode = Rv32JalLuiOpcode::from_usize( + instruction + .opcode + .local_opcode_idx(Rv32JalLuiOpcode::CLASS_OFFSET), + ); + let imm = instruction.c; + + let signed_imm = match local_opcode { + JAL => { + // Note: signed_imm is a signed integer and imm is a field element + (imm + F::from_canonical_u32(1 << (RV_J_TYPE_IMM_BITS - 1))).as_canonical_u32() + as i32 + - (1 << (RV_J_TYPE_IMM_BITS - 1)) + } + LUI => imm.as_canonical_u32() as i32, + }; + let (to_pc, rd_data) = run_jal_lui(local_opcode, from_pc, signed_imm); + + for i in 0..(RV32_REGISTER_NUM_LIMBS / 2) { + self.bitwise_lookup_chip + .request_range(rd_data[i * 2] as u32, rd_data[i * 2 + 1] as u32); + } + + if local_opcode == JAL { + let last_limb_bits = PC_BITS - RV32_CELL_BITS * (RV32_REGISTER_NUM_LIMBS - 1); + let additional_bits = (last_limb_bits..RV32_CELL_BITS).fold(0, |acc, x| acc + (1 << x)); + self.bitwise_lookup_chip + .request_xor(rd_data[3] as u32, additional_bits); + } + + let rd_data = rd_data.map(F::from_canonical_u8); + + let output = AdapterRuntimeContext { + to_pc: Some(to_pc), + writes: [rd_data].into(), + }; + + Ok(( + output, + Rv32JalLuiCoreRecord { + rd_data, + imm, + is_jal: local_opcode == JAL, + is_lui: local_opcode == LUI, + }, + )) + } + + fn get_opcode_name(&self, opcode: usize) -> String { + format!( + "{:?}", + Rv32JalLuiOpcode::from_usize(opcode - Rv32JalLuiOpcode::CLASS_OFFSET) + ) + } + + fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { + let core_cols: &mut Rv32JalLuiCoreCols = row_slice.borrow_mut(); + core_cols.rd_data = record.rd_data; + core_cols.imm = record.imm; + core_cols.is_jal = F::from_bool(record.is_jal); + core_cols.is_lui = F::from_bool(record.is_lui); + } + + fn air(&self) -> &Self::Air { + &self.air + } +} + +impl InsExecutorE1 for Rv32JalLuiCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + state: &mut VmExecutionState, + instruction: &Instruction, + ) -> Result<()> { + let Instruction { + opcode, a, c: imm, .. + } = instruction; + + let local_opcode = + Rv32JalLuiOpcode::from_usize(opcode.local_opcode_idx(Rv32JalLuiOpcode::CLASS_OFFSET)); + + let imm = imm.as_canonical_u32(); + let signed_imm = match local_opcode { + JAL => (imm + (1 << (RV_J_TYPE_IMM_BITS - 1))) as i32 - (1 << (RV_J_TYPE_IMM_BITS - 1)), + LUI => imm as i32, + }; + + let (to_pc, rd_bytes) = run_jal_lui(local_opcode, state.pc, signed_imm); + + let rd_addr = a.as_canonical_u32(); + unsafe { + state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes); + } + + state.pc = to_pc; + + Ok(()) + } +} + // returns (to_pc, rd_data) pub(super) fn run_jal_lui( opcode: Rv32JalLuiOpcode, diff --git a/extensions/rv32im/circuit/src/jalr/core.rs b/extensions/rv32im/circuit/src/jalr/core.rs index fd89c1e317..9152bf370a 100644 --- a/extensions/rv32im/circuit/src/jalr/core.rs +++ b/extensions/rv32im/circuit/src/jalr/core.rs @@ -3,9 +3,12 @@ use std::{ borrow::{Borrow, BorrowMut}, }; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, Result, SignedImmInstruction, VmAdapterInterface, - VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, Result, SignedImmInstruction, + VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, @@ -15,6 +18,7 @@ use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{ instruction::Instruction, program::{DEFAULT_PC_STEP, PC_BITS}, + riscv::RV32_REGISTER_AS, LocalOpcode, }; use openvm_rv32im_transpiler::Rv32JalrOpcode::{self, *}; @@ -290,6 +294,58 @@ where } } +impl InsExecutorE1 for Rv32JalrCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + state: &mut VmExecutionState, + instruction: &Instruction, + ) -> Result<()> { + let Instruction { + opcode, + b, + a, + c, + f: enabled, + g, + .. + } = instruction; + + let local_opcode = + Rv32JalrOpcode::from_usize(opcode.local_opcode_idx(Rv32JalrOpcode::CLASS_OFFSET)); + + let rs1_addr = b.as_canonical_u32(); + let rs1_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = + unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; + + // TODO(ayush): directly read as u32 from memory + let rs1_bytes = u32::from_le_bytes(rs1_bytes); + + let imm = c.as_canonical_u32(); + let imm_sign = g.as_canonical_u32(); + let imm_extended = imm + imm_sign * 0xffff0000; + + // TODO(ayush): should this be [u8; 4]? + let (to_pc, rd_bytes) = run_jalr(local_opcode, state.pc, imm_extended, rs1_bytes); + let rd_bytes = rd_bytes.map(|x| x as u8); + + // TODO(ayush): do i need this enabled check? + if *enabled != F::ZERO { + let rd_addr = a.as_canonical_u32(); + unsafe { + state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes); + } + } + + state.pc = to_pc; + + Ok(()) + } +} + // returns (to_pc, rd_data) pub(super) fn run_jalr( _opcode: Rv32JalrOpcode, diff --git a/extensions/rv32im/circuit/src/less_than/core.rs b/extensions/rv32im/circuit/src/less_than/core.rs index a605dc43de..d150a0fc14 100644 --- a/extensions/rv32im/circuit/src/less_than/core.rs +++ b/extensions/rv32im/circuit/src/less_than/core.rs @@ -3,16 +3,24 @@ use std::{ borrow::{Borrow, BorrowMut}, }; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, MinimalInstruction, Result, VmAdapterInterface, - VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, + VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, utils::not, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; +use openvm_instructions::{ + instruction::Instruction, + program::DEFAULT_PC_STEP, + riscv::{RV32_IMM_AS, RV32_REGISTER_AS}, + LocalOpcode, +}; use openvm_rv32im_transpiler::LessThanOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -312,6 +320,65 @@ where } } +impl InsExecutorE1 + for LessThanCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + state: &mut VmExecutionState, + instruction: &Instruction, + ) -> Result<()> { + let Instruction { + opcode, a, b, c, e, .. + } = instruction; + + let less_than_opcode = LessThanOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); + + let rs1_addr = b.as_canonical_u32(); + let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; + + let rs2_bytes = if e.as_canonical_u32() == RV32_IMM_AS { + // Use immediate value + let imm = c.as_canonical_u32(); + // Convert imm from u32 to [u8; NUM_LIMBS] + let imm_bytes = imm.to_le_bytes(); + // TODO(ayush): remove this + let mut rs2_bytes = [0u8; NUM_LIMBS]; + rs2_bytes[..NUM_LIMBS].copy_from_slice(&imm_bytes[..NUM_LIMBS]); + rs2_bytes + } else { + // Read from register + let rs2_addr = c.as_canonical_u32(); + let rs2_bytes: [u8; NUM_LIMBS] = + unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; + rs2_bytes + }; + + // TODO(ayush): avoid this conversion + let rs1_bytes: [u32; NUM_LIMBS] = rs1_bytes.map(|x| x as u32); + let rs2_bytes: [u32; NUM_LIMBS] = rs2_bytes.map(|y| y as u32); + + // Run the comparison + let (cmp_result, _, _, _) = + run_less_than::(less_than_opcode, &rs1_bytes, &rs2_bytes); + // TODO(ayush): can i write just [u8; 1]? + let rd_bytes = (cmp_result as u32).to_le_bytes(); + + // Write the result back to the destination register + let rd_addr = a.as_canonical_u32(); + unsafe { + state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes); + } + + state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) + } +} + // Returns (cmp_result, diff_idx, x_sign, y_sign) pub(super) fn run_less_than( opcode: LessThanOpcode, diff --git a/extensions/rv32im/circuit/src/load_sign_extend/core.rs b/extensions/rv32im/circuit/src/load_sign_extend/core.rs index 2284d6815c..26b990583b 100644 --- a/extensions/rv32im/circuit/src/load_sign_extend/core.rs +++ b/extensions/rv32im/circuit/src/load_sign_extend/core.rs @@ -3,15 +3,24 @@ use std::{ borrow::{Borrow, BorrowMut}, }; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, Result, VmAdapterInterface, VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, Result, VmAdapterInterface, + VmCoreAir, VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ utils::select, var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus}, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; +use openvm_instructions::{ + instruction::Instruction, + program::DEFAULT_PC_STEP, + riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS, RV32_REGISTER_NUM_LIMBS}, + LocalOpcode, +}; use openvm_rv32im_transpiler::Rv32LoadStoreOpcode::{self, *}; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -278,6 +287,76 @@ where } } +impl InsExecutorE1 + for LoadSignExtendCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + state: &mut VmExecutionState, + instruction: &Instruction, + ) -> Result<()> { + let Instruction { + opcode, + a, + b, + c, + f: enabled, + g, + .. + } = instruction; + + let local_opcode = Rv32LoadStoreOpcode::from_usize( + opcode.local_opcode_idx(Rv32LoadStoreOpcode::CLASS_OFFSET), + ); + + let rs1_addr = b.as_canonical_u32(); + let rs1_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = + unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; + let rs1_val = u32::from_le_bytes(rs1_bytes); + + let imm = c.as_canonical_u32(); + let imm_sign = g.as_canonical_u32(); + let imm_extended = imm + imm_sign * 0xffff0000; + + let ptr_val = rs1_val.wrapping_add(imm_extended); + let shift_amount = ptr_val % 4; + let ptr_val = ptr_val - shift_amount; // aligned ptr + + let read_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = match local_opcode { + LOADB | LOADH => unsafe { state.memory.read(RV32_MEMORY_AS, ptr_val) }, + _ => unreachable!("Only LOADB and LOADH are supported by LoadSignExtendCoreChip chip"), + }; + // TODO(ayush): handle NUM_CELLS and RV32_REGISTER_NUM_LIMBS properly + let read_data: [F; NUM_CELLS] = array::from_fn(|i| F::from_canonical_u8(read_bytes[i])); + + // TODO(ayush): clean this up for e1 + let write_data = run_write_data_sign_extend::<_, NUM_CELLS, LIMB_BITS>( + local_opcode, + read_data, + [F::ZERO; NUM_CELLS], + shift_amount, + ); + let write_bytes: [u8; NUM_CELLS] = + array::from_fn(|i| write_data[i].as_canonical_u32() as u8); + + // Only proceed if instruction is enabled + if *enabled != F::ZERO { + // Write result to destination register + let rd_addr = a.as_canonical_u32(); + unsafe { + state.memory.write(RV32_REGISTER_AS, rd_addr, &write_bytes); + } + } + + state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) + } +} + pub(super) fn run_write_data_sign_extend< F: PrimeField32, const NUM_CELLS: usize, diff --git a/extensions/rv32im/circuit/src/loadstore/core.rs b/extensions/rv32im/circuit/src/loadstore/core.rs index 36beb10629..7e0054da2a 100644 --- a/extensions/rv32im/circuit/src/loadstore/core.rs +++ b/extensions/rv32im/circuit/src/loadstore/core.rs @@ -1,10 +1,19 @@ use std::borrow::{Borrow, BorrowMut}; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, Result, VmAdapterInterface, VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, Result, VmAdapterInterface, + VmCoreAir, VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; +use openvm_instructions::{ + instruction::Instruction, + program::DEFAULT_PC_STEP, + riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS, RV32_REGISTER_NUM_LIMBS}, + LocalOpcode, +}; use openvm_rv32im_transpiler::Rv32LoadStoreOpcode::{self, *}; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -340,6 +349,97 @@ where } } +impl InsExecutorE1 + for LoadStoreCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + state: &mut VmExecutionState, + instruction: &Instruction, + ) -> Result<()> { + let Instruction { + opcode, + a, + b, + c, + g, + f: enabled, + .. + } = instruction; + + // Get the local opcode for this instruction + let local_opcode = + Rv32LoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); + + let rs1_addr = b.as_canonical_u32(); + let rs1_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = + unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; + let rs1_val = u32::from_le_bytes(rs1_bytes); + + let imm = c.as_canonical_u32(); + let imm_sign = g.as_canonical_u32(); + let imm_extended = imm + imm_sign * 0xffff0000; + + let ptr_val = rs1_val.wrapping_add(imm_extended); + let shift_amount = ptr_val % 4; + let ptr_val = ptr_val - shift_amount; // aligned ptr + + let read_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = match local_opcode { + LOADW | LOADB | LOADH | LOADBU | LOADHU => { + // For loads, read from memory + unsafe { state.memory.read(RV32_MEMORY_AS, ptr_val) } + } + STOREW | STOREH | STOREB => { + // For stores, read the register value to be stored + let rs2_addr = a.as_canonical_u32(); + unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) } + } + }; + + // For stores, we need the previous memory content to preserve unchanged bytes + let prev_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = match local_opcode { + STOREW | STOREH | STOREB => { + // For stores, read current memory content + unsafe { state.memory.read(RV32_MEMORY_AS, ptr_val) } + } + LOADW | LOADB | LOADH | LOADBU | LOADHU => { + // For loads, read current register content + let rd_addr = a.as_canonical_u32(); + unsafe { state.memory.read(RV32_REGISTER_AS, rd_addr) } + } + }; + + let read_data = read_bytes.map(F::from_canonical_u8); + let prev_data = prev_bytes.map(F::from_canonical_u8); + + // Process the data according to the load/store type and alignment + let write_data = run_write_data(local_opcode, read_data, prev_data, shift_amount); + let write_bytes = write_data.map(|x| x.as_canonical_u32() as u8); + + if *enabled != F::ZERO { + match local_opcode { + STOREW | STOREH | STOREB => { + // For stores, write to memory + unsafe { state.memory.write(RV32_MEMORY_AS, ptr_val, &write_data) }; + } + LOADW | LOADB | LOADH | LOADBU | LOADHU => { + let rd_addr = a.as_canonical_u32(); + unsafe { + state.memory.write(RV32_REGISTER_AS, rd_addr, &write_bytes); + } + } + } + } + + state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) + } +} + pub(super) fn run_write_data( opcode: Rv32LoadStoreOpcode, read_data: [F; NUM_CELLS], diff --git a/extensions/rv32im/circuit/src/mul/core.rs b/extensions/rv32im/circuit/src/mul/core.rs index fa65a6cf09..98fbc7e6b8 100644 --- a/extensions/rv32im/circuit/src/mul/core.rs +++ b/extensions/rv32im/circuit/src/mul/core.rs @@ -3,13 +3,18 @@ use std::{ borrow::{Borrow, BorrowMut}, }; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, MinimalInstruction, Result, VmAdapterInterface, - VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, + VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives::range_tuple::{RangeTupleCheckerBus, SharedRangeTupleCheckerChip}; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; +use openvm_instructions::{ + instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_REGISTER_AS, LocalOpcode, +}; use openvm_rv32im_transpiler::MulOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -212,6 +217,53 @@ where } } +impl InsExecutorE1 + for MultiplicationCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + state: &mut VmExecutionState, + instruction: &Instruction, + ) -> Result<()> { + let Instruction { + opcode, a, b, c, .. + } = instruction; + + // Verify the opcode is MUL + // TODO(ayush): debug_assert + assert_eq!( + MulOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)), + MulOpcode::MUL + ); + + // Read input registers + let rs1_addr = b.as_canonical_u32(); + let rs2_addr = c.as_canonical_u32(); + + let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; + let rs2_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; + + // TODO(ayush): remove this conversion + let rs1_bytes = rs1_bytes.map(|x| x as u32); + let rs2_bytes = rs2_bytes.map(|y| y as u32); + + // Perform the multiplication + let (rd_bytes, _) = run_mul::(&rs1_bytes, &rs2_bytes); + let rd_bytes = rd_bytes.map(|x| x as u8); + + // Write result to destination register + let rd_addr = a.as_canonical_u32(); + unsafe { state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes) }; + + state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) + } +} + // returns mul, carry pub(super) fn run_mul( x: &[u32; NUM_LIMBS], diff --git a/extensions/rv32im/circuit/src/mulh/core.rs b/extensions/rv32im/circuit/src/mulh/core.rs index 16aa8fd550..5fa707328b 100644 --- a/extensions/rv32im/circuit/src/mulh/core.rs +++ b/extensions/rv32im/circuit/src/mulh/core.rs @@ -3,16 +3,21 @@ use std::{ borrow::{Borrow, BorrowMut}, }; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, MinimalInstruction, Result, VmAdapterInterface, - VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, + VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, range_tuple::{RangeTupleCheckerBus, SharedRangeTupleCheckerChip}, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; +use openvm_instructions::{ + instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_REGISTER_AS, LocalOpcode, +}; use openvm_rv32im_transpiler::MulHOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -313,6 +318,51 @@ where } } +impl InsExecutorE1 + for MulHCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + state: &mut VmExecutionState, + instruction: &Instruction, + ) -> Result<()> { + let Instruction { + opcode, a, b, c, .. + } = instruction; + + let mulh_opcode = MulHOpcode::from_usize(opcode.local_opcode_idx(MulHOpcode::CLASS_OFFSET)); + + // Read input registers + let rs1_addr = b.as_canonical_u32(); + let rs2_addr = c.as_canonical_u32(); + + let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; + let rs2_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; + + // TODO(ayush): remove this conversion + let rs1_bytes = rs1_bytes.map(|x| x as u32); + let rs2_bytes = rs2_bytes.map(|y| y as u32); + + // Execute the multiplication high operation + let (rd_bytes, _, _, _, _) = + run_mulh::(mulh_opcode, &rs1_bytes, &rs2_bytes); + let rd_bytes = rd_bytes.map(|x| x as u8); + + // Write the result to the destination register + let rd_addr = a.as_canonical_u32(); + unsafe { + state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes); + } + + state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) + } +} + // returns mulh[[s]u], mul, carry, x_ext, y_ext pub(super) fn run_mulh( opcode: MulHOpcode, diff --git a/extensions/rv32im/circuit/src/shift/core.rs b/extensions/rv32im/circuit/src/shift/core.rs index cada97685e..32fc3c155d 100644 --- a/extensions/rv32im/circuit/src/shift/core.rs +++ b/extensions/rv32im/circuit/src/shift/core.rs @@ -3,9 +3,12 @@ use std::{ borrow::{Borrow, BorrowMut}, }; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, MinimalInstruction, Result, VmAdapterInterface, - VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, + VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + }, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, @@ -13,7 +16,12 @@ use openvm_circuit_primitives::{ var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus}, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; +use openvm_instructions::{ + instruction::Instruction, + program::DEFAULT_PC_STEP, + riscv::{RV32_IMM_AS, RV32_REGISTER_AS}, + LocalOpcode, +}; use openvm_rv32im_transpiler::ShiftOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -381,6 +389,63 @@ where } } +impl InsExecutorE1 + for ShiftCoreChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + fn execute_e1( + &mut self, + state: &mut VmExecutionState, + instruction: &Instruction, + ) -> Result<()> { + let Instruction { + opcode, a, b, c, e, .. + } = instruction; + + let shift_opcode = ShiftOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); + + let rs1_addr = b.as_canonical_u32(); + let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; + + let rs2_bytes = if e.as_canonical_u32() == RV32_IMM_AS { + // Use immediate value + let imm = c.as_canonical_u32(); + // Convert imm from u32 to [u8; NUM_LIMBS] + let imm_bytes = imm.to_le_bytes(); + // TODO(ayush): remove this + let mut rs2_bytes = [0u8; NUM_LIMBS]; + rs2_bytes[..NUM_LIMBS].copy_from_slice(&imm_bytes[..NUM_LIMBS]); + rs2_bytes + } else { + // Read from register + let rs2_addr = c.as_canonical_u32(); + let rs2_bytes: [u8; NUM_LIMBS] = + unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; + rs2_bytes + }; + + // TODO(ayush): avoid this conversion + let rs1_bytes: [u32; NUM_LIMBS] = rs1_bytes.map(|x| x as u32); + let rs2_bytes: [u32; NUM_LIMBS] = rs2_bytes.map(|y| y as u32); + + // Execute the shift operation + let (rd_bytes, _, _) = + run_shift::(shift_opcode, &rs1_bytes, &rs2_bytes); + let rd_bytes = rd_bytes.map(|x| x as u8); + + let rd_addr = a.as_canonical_u32(); + unsafe { + state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes); + } + + state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) + } +} + pub(super) fn run_shift( opcode: ShiftOpcode, x: &[u32; NUM_LIMBS], From b85bfc293c63c83c24fdedf48decaef7911493bb Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Mon, 21 Apr 2025 10:24:58 -0400 Subject: [PATCH 06/49] feat(new-execution): `AdapterTraceStep` trait and rv32 ALU refactor (#1589) Co-authored-by: Alexander Golovanov --- crates/vm/src/arch/execution.rs | 16 +- crates/vm/src/arch/integration_api.rs | 73 ++- .../system/memory/offline_checker/columns.rs | 11 + extensions/rv32im/circuit/src/adapters/alu.rs | 264 +++------- extensions/rv32im/circuit/src/adapters/mod.rs | 81 ++- extensions/rv32im/circuit/src/adapters/mul.rs | 4 +- extensions/rv32im/circuit/src/auipc/core.rs | 15 +- .../rv32im/circuit/src/base_alu/core.rs | 228 ++++----- extensions/rv32im/circuit/src/base_alu/mod.rs | 17 +- .../rv32im/circuit/src/base_alu/tests.rs | 474 +++++++++--------- extensions/rv32im/circuit/src/extension.rs | 82 +-- extensions/rv32im/circuit/src/jal_lui/core.rs | 15 +- .../rv32im/circuit/src/less_than/core.rs | 185 ++++--- .../rv32im/circuit/src/less_than/mod.rs | 16 +- .../rv32im/circuit/src/less_than/tests.rs | 84 ++-- extensions/rv32im/circuit/src/mul/core.rs | 35 +- extensions/rv32im/circuit/src/mul/tests.rs | 26 +- extensions/rv32im/circuit/src/shift/core.rs | 268 +++++----- extensions/rv32im/circuit/src/shift/mod.rs | 16 +- extensions/rv32im/circuit/src/shift/tests.rs | 97 ++-- extensions/rv32im/circuit/src/test_utils.rs | 15 +- 21 files changed, 1060 insertions(+), 962 deletions(-) diff --git a/crates/vm/src/arch/execution.rs b/crates/vm/src/arch/execution.rs index d8d6202e26..46491fd350 100644 --- a/crates/vm/src/arch/execution.rs +++ b/crates/vm/src/arch/execution.rs @@ -6,14 +6,17 @@ use openvm_instructions::{ }; use openvm_stark_backend::{ interaction::{BusIndex, InteractionBuilder, PermutationCheckBus}, - p3_field::{FieldAlgebra, PrimeField32}, + p3_field::{Field, FieldAlgebra, PrimeField32}, }; use serde::{Deserialize, Serialize}; use thiserror::Error; use super::{Streams, VmExecutionState}; use crate::system::{ - memory::{online::GuestMemory, MemoryController}, + memory::{ + online::{GuestMemory, TracingMemory}, + MemoryController, + }, program::ProgramBus, }; @@ -82,6 +85,15 @@ pub struct VmStateMut<'a, MEM, CTX> { pub ctx: &'a mut CTX, } +impl VmStateMut<'_, TracingMemory, CTX> { + // TODO: store as u32 directly + #[inline(always)] + pub fn ins_start(&self, from_state: &mut ExecutionState) { + from_state.pc = F::from_canonical_u32(*self.pc); + from_state.timestamp = F::from_canonical_u32(self.memory.timestamp); + } +} + // TODO: old pub trait InstructionExecutor { /// Runtime execution of the instruction, if the instruction is owned by the diff --git a/crates/vm/src/arch/integration_api.rs b/crates/vm/src/arch/integration_api.rs index c805768b26..9480221588 100644 --- a/crates/vm/src/arch/integration_api.rs +++ b/crates/vm/src/arch/integration_api.rs @@ -246,27 +246,27 @@ pub trait SingleTraceStep { fn get_opcode_name(&self, opcode: usize) -> String; } -pub struct NewVmChipWrapper { +pub struct NewVmChipWrapper { pub air: AIR, - pub inner: C, + pub step: STEP, pub trace_buffer: Vec, width: usize, buffer_idx: usize, mem_helper: SharedMemoryHelper, } -impl NewVmChipWrapper +impl NewVmChipWrapper where F: Field, AIR: BaseAir, { - pub fn new(air: AIR, inner: C, height: usize, mem_helper: SharedMemoryHelper) -> Self { + pub fn new(air: AIR, step: STEP, height: usize, mem_helper: SharedMemoryHelper) -> Self { assert!(height == 0 || height.is_power_of_two()); let width = air.width(); let trace_buffer = F::zero_vec(height * width); Self { air, - inner, + step, trace_buffer, width, buffer_idx: 0, @@ -275,10 +275,10 @@ where } } -impl InstructionExecutor for NewVmChipWrapper +impl InstructionExecutor for NewVmChipWrapper where F: PrimeField32, - C: SingleTraceStep, // TODO: CTX? + STEP: SingleTraceStep, // TODO: CTX? { fn execute( &mut self, @@ -305,7 +305,7 @@ where self.trace_buffer .get_unchecked_mut(start_idx..self.buffer_idx) }; - self.inner.execute(state, instruction, row_slice)?; + self.step.execute(state, instruction, row_slice)?; Ok(ExecutionState { pc, timestamp: memory.memory.timestamp, @@ -313,7 +313,7 @@ where } fn get_opcode_name(&self, opcode: usize) -> String { - self.inner.get_opcode_name(opcode) + self.step.get_opcode_name(opcode) } } @@ -321,11 +321,11 @@ where // - `Air` is an `Air` for all `AB: AirBuilder`s needed by stark-backend // which is equivalent to saying it implements AirRef // The where clauses to achieve this statement is unfortunately really verbose. -impl Chip for NewVmChipWrapper, AIR, C> +impl Chip for NewVmChipWrapper, AIR, STEP> where SC: StarkGenericConfig, Val: PrimeField32, - C: SingleTraceStep, ()> + Send + Sync, + STEP: SingleTraceStep, ()> + Send + Sync, AIR: Clone + AnyRap + 'static, { fn air(&self) -> AirRef { @@ -346,13 +346,13 @@ where self.trace_buffer[..rows_used * self.width] .par_chunks_exact_mut(self.width) .for_each(|row_slice| { - self.inner.fill_trace_row(&mem_helper, row_slice); + self.step.fill_trace_row(&mem_helper, row_slice); }); drop(self.mem_helper); let trace = RowMajorMatrix::new(self.trace_buffer, self.width); // self.inner.finalize(&mut trace, num_records); - AirProofInput::simple(trace, self.inner.generate_public_values()) + AirProofInput::simple(trace, self.step.generate_public_values()) } } @@ -371,6 +371,47 @@ where } } +// TODO[jpw]: switch read,write to store into abstract buffer, then fill_trace_row using buffer +/// A helper trait for expressing generic state accesses within the implementation of +/// [SingleTraceStep]. Note that this is only a helper trait when the same interface of state access +/// is reused or shared by multiple implementations. It is not required to implement this trait if +/// it is easier to implement the [SingleTraceStep] trait directly without this trait. +pub trait AdapterTraceStep { + /// Adapter row width + const WIDTH: usize; + type ReadData; + type WriteData; + /// The minimal amount of information needed to generate the sub-row of the trace matrix. + /// This type has a lifetime so other context, such as references to other chips, can be + /// provided. + type TraceContext<'a> + where + Self: 'a; + + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]); + + fn read( + memory: &mut TracingMemory, + instruction: &Instruction, + adapter_row: &mut [F], + ) -> Self::ReadData; + + fn write( + memory: &mut TracingMemory, + instruction: &Instruction, + adapter_row: &mut [F], + data: &Self::WriteData, + ); + + // Note[jpw]: should we reuse TraceSubRowGenerator trait instead? + /// Post-execution filling of rest of adapter row. + fn fill_trace_row( + mem_helper: &MemoryAuxColsFactory, + ctx: Self::TraceContext<'_>, + adapter_row: &mut [F], + ); +} + pub struct VmChipWrapper, C: VmCoreChip> { pub adapter: A, pub core: C, @@ -440,18 +481,18 @@ where } } -impl InsExecutorE1 for NewVmChipWrapper +impl InsExecutorE1 for NewVmChipWrapper where Mem: GuestMemory, F: PrimeField32, - C: InsExecutorE1, + S: InsExecutorE1, { fn execute_e1( &mut self, state: &mut VmExecutionState, instruction: &Instruction, ) -> Result<()> { - self.inner.execute_e1(state, instruction) + self.step.execute_e1(state, instruction) } } diff --git a/crates/vm/src/system/memory/offline_checker/columns.rs b/crates/vm/src/system/memory/offline_checker/columns.rs index c2db62301d..2e3f7fb019 100644 --- a/crates/vm/src/system/memory/offline_checker/columns.rs +++ b/crates/vm/src/system/memory/offline_checker/columns.rs @@ -88,6 +88,11 @@ impl MemoryReadAuxCols { pub fn get_base(self) -> MemoryBaseAuxCols { self.base } + + /// Sets the previous timestamp **without** updating the less than auxiliary columns. + pub fn set_prev(&mut self, timestamp: F) { + self.base.prev_timestamp = timestamp; + } } #[repr(C)] @@ -114,3 +119,9 @@ impl AsMut> for MemoryWriteAuxCols &mut self.base } } + +impl AsMut> for MemoryReadAuxCols { + fn as_mut(&mut self) -> &mut MemoryBaseAuxCols { + &mut self.base + } +} diff --git a/extensions/rv32im/circuit/src/adapters/alu.rs b/extensions/rv32im/circuit/src/adapters/alu.rs index e95fc4e8d7..2f91ea7c87 100644 --- a/extensions/rv32im/circuit/src/adapters/alu.rs +++ b/extensions/rv32im/circuit/src/adapters/alu.rs @@ -1,91 +1,34 @@ -use std::{ - borrow::{Borrow, BorrowMut}, - marker::PhantomData, -}; +use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge, - ExecutionBus, ExecutionState, MinimalInstruction, Result, VmAdapterAir, VmAdapterChip, - VmAdapterInterface, + AdapterAirContext, AdapterTraceStep, BasicAdapterInterface, ExecutionBridge, + ExecutionState, MinimalInstruction, VmAdapterAir, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, - MemoryAddress, MemoryController, OfflineMemory, RecordId, - }, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, + online::TracingMemory, + MemoryAddress, MemoryAuxColsFactory, }, }; use openvm_circuit_primitives::{ - bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, + bitwise_op_lookup::{BitwiseOperationLookupBus, BitwiseOperationLookupChip}, utils::not, }; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{ - instruction::Instruction, - program::DEFAULT_PC_STEP, - riscv::{RV32_IMM_AS, RV32_REGISTER_AS}, + instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_REGISTER_AS, }; use openvm_stark_backend::{ interaction::InteractionBuilder, p3_air::{AirBuilder, BaseAir}, p3_field::{Field, FieldAlgebra, PrimeField32}, }; -use serde::{Deserialize, Serialize}; - -use super::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; -use crate::adapters::tmp_convert_to_u8s; - -/// Reads instructions of the form OP a, b, c, d, e where \[a:4\]_d = \[b:4\]_d op \[c:4\]_e. -/// Operand d can only be 1, and e can be either 1 (for register reads) or 0 (when c -/// is an immediate). -pub struct Rv32BaseAluAdapterChip { - pub air: Rv32BaseAluAdapterAir, - bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - _marker: PhantomData, -} - -impl Rv32BaseAluAdapterChip { - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - ) -> Self { - Self { - air: Rv32BaseAluAdapterAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - bitwise_lookup_bus: bitwise_lookup_chip.bus(), - }, - bitwise_lookup_chip, - _marker: PhantomData, - } - } -} -#[repr(C)] -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct Rv32BaseAluReadRecord { - /// Read register value from address space d=1 - pub rs1: RecordId, - /// Either - /// - read rs2 register value or - /// - if `rs2_is_imm` is true, this is None - pub rs2: Option, - /// immediate value of rs2 or 0 - pub rs2_imm: F, -} - -#[repr(C)] -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Rv32BaseAluWriteRecord { - pub from_state: ExecutionState, - /// Write to destination register - pub rd: RecordId, -} +use super::{ + tracing_read_reg, tracing_read_reg_or_imm, tracing_write_reg, RV32_CELL_BITS, + RV32_REGISTER_NUM_LIMBS, +}; #[repr(C)] #[derive(AlignedBorrow)] @@ -101,7 +44,9 @@ pub struct Rv32BaseAluAdapterCols { pub writes_aux: MemoryWriteAuxCols, } -#[allow(dead_code)] +/// Reads instructions of the form OP a, b, c, d, e where \[a:4\]_d = \[b:4\]_d op \[c:4\]_e. +/// Operand d can only be 1, and e can be either 1 (for register reads) or 0 (when c +/// is an immediate). #[derive(Clone, Copy, Debug, derive_new::new)] pub struct Rv32BaseAluAdapterAir { pub(super) execution_bridge: ExecutionBridge, @@ -213,131 +158,84 @@ impl VmAdapterAir for Rv32BaseAluAdapterAir { } } -impl VmAdapterChip for Rv32BaseAluAdapterChip { - type ReadRecord = Rv32BaseAluReadRecord; - type WriteRecord = Rv32BaseAluWriteRecord; - type Air = Rv32BaseAluAdapterAir; - type Interface = BasicAdapterInterface< - F, - MinimalInstruction, - 2, - 1, - RV32_REGISTER_NUM_LIMBS, - RV32_REGISTER_NUM_LIMBS, - >; +#[derive(derive_new::new)] +pub struct Rv32BaseAluAdapterStep; + +impl AdapterTraceStep + for Rv32BaseAluAdapterStep +{ + const WIDTH: usize = size_of::>(); + type ReadData = [[u8; RV32_REGISTER_NUM_LIMBS]; 2]; + type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + type TraceContext<'a> = &'a BitwiseOperationLookupChip; + + #[inline(always)] + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let adapter_row: &mut Rv32BaseAluAdapterCols = adapter_row.borrow_mut(); + adapter_row.from_state.pc = F::from_canonical_u32(pc); + adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + } - fn preprocess( - &mut self, - memory: &mut MemoryController, + #[inline(always)] + fn read( + memory: &mut TracingMemory, instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { - let Instruction { b, c, d, e, .. } = *instruction; - + adapter_row: &mut [F], + ) -> Self::ReadData { + let &Instruction { b, c, d, e, .. } = instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - debug_assert!( - e.as_canonical_u32() == RV32_IMM_AS || e.as_canonical_u32() == RV32_REGISTER_AS + let adapter_row: &mut Rv32BaseAluAdapterCols = adapter_row.borrow_mut(); + let rs1_idx = b.as_canonical_u32(); + let rs1 = tracing_read_reg( + memory, + rs1_idx, + (&mut adapter_row.rs1_ptr, &mut adapter_row.reads_aux[0]), ); - - let rs1 = memory.read::(d, b); - let (rs2, rs2_data, rs2_imm) = if e.is_zero() { - let c_u32 = c.as_canonical_u32(); - debug_assert_eq!(c_u32 >> 24, 0); - memory.increment_timestamp(); - ( - None, - [ - c_u32 as u8, - (c_u32 >> 8) as u8, - (c_u32 >> 16) as u8, - (c_u32 >> 16) as u8, - ], - c, - ) - } else { - let rs2_read = memory.read::(e, c); - (Some(rs2_read.0), rs2_read.1, F::ZERO) - }; - - Ok(( - [ - rs1.1.map(F::from_canonical_u8), - rs2_data.map(F::from_canonical_u8), - ], - Self::ReadRecord { - rs1: rs1.0, - rs2, - rs2_imm, - }, - )) + let rs2 = tracing_read_reg_or_imm( + memory, + e.as_canonical_u32(), + c.as_canonical_u32(), + &mut adapter_row.rs2_as, + (&mut adapter_row.rs2, &mut adapter_row.reads_aux[1]), + ); + [rs1, rs2] } - fn postprocess( - &mut self, - memory: &mut MemoryController, + #[inline(always)] + fn write( + memory: &mut TracingMemory, instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - _read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - let Instruction { a, d, .. } = instruction; - let (rd, _) = memory.write(*d, *a, &tmp_convert_to_u8s(output.writes[0])); - - let timestamp_delta = memory.timestamp() - from_state.timestamp; - debug_assert!( - timestamp_delta == 3, - "timestamp delta is {}, expected 3", - timestamp_delta + adapter_row: &mut [F], + data: &Self::WriteData, + ) { + let adapter_row: &mut Rv32BaseAluAdapterCols = adapter_row.borrow_mut(); + let rd_ptr = instruction.a.as_canonical_u32(); + tracing_write_reg( + memory, + rd_ptr, + data, + (&mut adapter_row.rd_ptr, &mut adapter_row.writes_aux), ); - - Ok(( - ExecutionState { - pc: from_state.pc + DEFAULT_PC_STEP, - timestamp: memory.timestamp(), - }, - Self::WriteRecord { from_state, rd }, - )) } - fn generate_trace_row( - &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, + #[inline(always)] + fn fill_trace_row( + mem_helper: &MemoryAuxColsFactory, + bitwise_lookup_chip: &BitwiseOperationLookupChip, + adapter_row: &mut [F], ) { - let row_slice: &mut Rv32BaseAluAdapterCols<_> = row_slice.borrow_mut(); - let aux_cols_factory = memory.aux_cols_factory(); - - let rd = memory.record_by_id(write_record.rd); - row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); - row_slice.rd_ptr = rd.pointer; - - let rs1 = memory.record_by_id(read_record.rs1); - let rs2 = read_record.rs2.map(|rs2| memory.record_by_id(rs2)); - row_slice.rs1_ptr = rs1.pointer; - - if let Some(rs2) = rs2 { - row_slice.rs2 = rs2.pointer; - row_slice.rs2_as = rs2.address_space; - aux_cols_factory.generate_read_aux(rs1, &mut row_slice.reads_aux[0]); - aux_cols_factory.generate_read_aux(rs2, &mut row_slice.reads_aux[1]); + let adapter_row: &mut Rv32BaseAluAdapterCols = adapter_row.borrow_mut(); + let mut timestamp = adapter_row.from_state.timestamp.as_canonical_u32(); + mem_helper.fill_from_prev(timestamp, adapter_row.reads_aux[0].as_mut()); + timestamp += 1; + if !adapter_row.rs2_as.is_zero() { + mem_helper.fill_from_prev(timestamp, adapter_row.reads_aux[1].as_mut()); } else { - row_slice.rs2 = read_record.rs2_imm; - row_slice.rs2_as = F::ZERO; - let rs2_imm = row_slice.rs2.as_canonical_u32(); + let rs2_imm = adapter_row.rs2.as_canonical_u32(); let mask = (1 << RV32_CELL_BITS) - 1; - self.bitwise_lookup_chip - .request_range(rs2_imm & mask, (rs2_imm >> 8) & mask); - aux_cols_factory.generate_read_aux(rs1, &mut row_slice.reads_aux[0]); - // row_slice.reads_aux[1] is disabled + bitwise_lookup_chip.request_range(rs2_imm & mask, (rs2_imm >> 8) & mask); } - aux_cols_factory.generate_write_aux(rd, &mut row_slice.writes_aux); - } - - fn air(&self) -> &Self::Air { - &self.air + timestamp += 1; + mem_helper.fill_from_prev(timestamp, adapter_row.writes_aux.as_mut()); } } diff --git a/extensions/rv32im/circuit/src/adapters/mod.rs b/extensions/rv32im/circuit/src/adapters/mod.rs index e5b5c99496..e026a7b8ce 100644 --- a/extensions/rv32im/circuit/src/adapters/mod.rs +++ b/extensions/rv32im/circuit/src/adapters/mod.rs @@ -1,7 +1,11 @@ use std::ops::Mul; -use openvm_circuit::system::memory::{online::TracingMemory, MemoryController, RecordId}; -use openvm_instructions::riscv::RV32_REGISTER_AS; +use openvm_circuit::system::memory::{ + offline_checker::{MemoryReadAuxCols, MemoryWriteAuxCols}, + online::TracingMemory, + MemoryController, RecordId, +}; +use openvm_instructions::riscv::{RV32_IMM_AS, RV32_REGISTER_AS}; use openvm_stark_backend::p3_field::{FieldAlgebra, PrimeField32}; mod alu; @@ -47,7 +51,10 @@ pub fn decompose(value: u32) -> [F; RV32_REGISTER_NUM_LIMBS] { }) } -pub fn tracing_read_reg( +/// Atomic read operation which increments the timestamp by 1. +/// Returns `(t_prev, [reg_ptr:4]_1)` where `t_prev` is the timestamp of the last memory access. +#[inline(always)] +pub fn timed_read_reg( memory: &mut TracingMemory, reg_ptr: u32, ) -> (u32, [u8; RV32_REGISTER_NUM_LIMBS]) { @@ -60,7 +67,8 @@ pub fn tracing_read_reg( } } -pub fn tracing_write_reg( +#[inline(always)] +pub fn timed_write_reg( memory: &mut TracingMemory, reg_ptr: u32, reg_val: &[u8; RV32_REGISTER_NUM_LIMBS], @@ -77,6 +85,71 @@ pub fn tracing_write_reg( } } +/// Reads register value at `reg_ptr` from memory and records the memory access in mutable buffer. +/// Trace generation relevant to this memory access can be done fully from the recorded buffer. +#[inline(always)] +pub fn tracing_read_reg( + memory: &mut TracingMemory, + reg_ptr: u32, + (reg_ptr_mut, aux_cols): (&mut F, &mut MemoryReadAuxCols), /* TODO[jpw]: switch to raw u8 + * buffer */ +) -> [u8; RV32_REGISTER_NUM_LIMBS] { + let (t_prev, data) = timed_read_reg(memory, reg_ptr); + *reg_ptr_mut = F::from_canonical_u32(reg_ptr); + aux_cols.set_prev(F::from_canonical_u32(t_prev)); + data +} + +/// Writes `reg_ptr, reg_val` into memory and records the memory access in mutable buffer. +/// Trace generation relevant to this memory access can be done fully from the recorded buffer. +#[inline(always)] +pub fn tracing_write_reg( + memory: &mut TracingMemory, + reg_ptr: u32, + reg_val: &[u8; RV32_REGISTER_NUM_LIMBS], + (reg_ptr_mut, aux_cols): (&mut F, &mut MemoryWriteAuxCols), /* TODO[jpw]: switch to raw u8 + * buffer */ +) { + let (t_prev, data_prev) = timed_write_reg(memory, reg_ptr, reg_val); + *reg_ptr_mut = F::from_canonical_u32(reg_ptr); + aux_cols.set_prev( + F::from_canonical_u32(t_prev), + data_prev.map(F::from_canonical_u8), + ); +} + +/// Reads register value at `reg_ptr` from memory and records the memory access in mutable buffer. +/// Trace generation relevant to this memory access can be done fully from the recorded buffer. +/// +/// Assumes that `addr_space` is [RV32_IMM_AS] or [RV32_REGISTER_AS]. +#[inline(always)] +pub fn tracing_read_reg_or_imm( + memory: &mut TracingMemory, + addr_space: u32, + reg_ptr_or_imm: u32, + addr_space_mut: &mut F, + (reg_ptr_or_imm_mut, aux_cols): (&mut F, &mut MemoryReadAuxCols), +) -> [u8; RV32_REGISTER_NUM_LIMBS] { + debug_assert!(addr_space == RV32_IMM_AS || addr_space == RV32_REGISTER_AS); + if addr_space == RV32_IMM_AS { + *addr_space_mut = F::ZERO; + let imm = reg_ptr_or_imm; + *reg_ptr_or_imm_mut = F::from_canonical_u32(imm); + debug_assert_eq!(imm >> 24, 0); // highest byte should be zero to prevent overflow + memory.increment_timestamp(); + let mut imm_le = imm.to_le_bytes(); + // Important: we set the highest byte equal to the second highest byte, using the assumption + // that imm is at most 24 bits + imm_le[3] = imm_le[2]; + imm_le + } else { + *addr_space_mut = F::ONE; // F::from_canonical_u32(RV32_REGISTER_AS) + let reg_ptr = reg_ptr_or_imm; + tracing_read_reg(memory, reg_ptr, (reg_ptr_or_imm_mut, aux_cols)) + } +} + +// TODO: delete /// Read register value as [RV32_REGISTER_NUM_LIMBS] limbs from memory. /// Returns the read record and the register value as u32. /// Does not make any range check calls. diff --git a/extensions/rv32im/circuit/src/adapters/mul.rs b/extensions/rv32im/circuit/src/adapters/mul.rs index 91ad6ac0c1..9c026ef8e7 100644 --- a/extensions/rv32im/circuit/src/adapters/mul.rs +++ b/extensions/rv32im/circuit/src/adapters/mul.rs @@ -31,8 +31,6 @@ use serde::{Deserialize, Serialize}; use super::RV32_REGISTER_NUM_LIMBS; use crate::adapters::tmp_convert_to_u8s; -/// Reads instructions of the form OP a, b, c, d where \[a:4\]_d = \[b:4\]_d op \[c:4\]_d. -/// Operand d can only be 1, and there is no immediate support. #[derive(Debug)] pub struct Rv32MultAdapterChip { pub air: Rv32MultAdapterAir, @@ -82,6 +80,8 @@ pub struct Rv32MultAdapterCols { pub writes_aux: MemoryWriteAuxCols, } +/// Reads instructions of the form OP a, b, c, d where \[a:4\]_d = \[b:4\]_d op \[c:4\]_d. +/// Operand d can only be 1, and there is no immediate support. #[derive(Clone, Copy, Debug, derive_new::new)] pub struct Rv32MultAdapterAir { pub(super) execution_bridge: ExecutionBridge, diff --git a/extensions/rv32im/circuit/src/auipc/core.rs b/extensions/rv32im/circuit/src/auipc/core.rs index 394a0b8737..95971ec0cf 100644 --- a/extensions/rv32im/circuit/src/auipc/core.rs +++ b/extensions/rv32im/circuit/src/auipc/core.rs @@ -235,20 +235,17 @@ impl SingleTraceStep for Rv32AuipcCoreChip { let adapter_row: &mut Rv32RdWriteAdapterCols = adapter_row.borrow_mut(); let core_row: &mut Rv32AuipcCoreCols = core_row.borrow_mut(); - let from_timestamp = state.memory.timestamp(); + state.ins_start(&mut adapter_row.from_state); let imm = instruction.c.as_canonical_u32(); let rd_data = run_auipc(Rv32AuipcOpcode::AUIPC, *state.pc, imm); debug_assert_eq!(instruction.d.as_canonical_u32(), RV32_REGISTER_AS); let rd_ptr = instruction.a.as_canonical_u32(); - let (t_prev, data_prev) = tracing_write_reg(state.memory, rd_ptr, &rd_data); - // TODO: store as u32 directly - adapter_row.from_state.pc = F::from_canonical_u32(*state.pc); - adapter_row.from_state.timestamp = F::from_canonical_u32(from_timestamp); - adapter_row.rd_ptr = instruction.a; - adapter_row.rd_aux_cols.set_prev( - F::from_canonical_u32(t_prev), - data_prev.map(F::from_canonical_u8), + tracing_write_reg( + state.memory, + rd_ptr, + &rd_data, + (&mut adapter_row.rd_ptr, &mut adapter_row.rd_aux_cols), ); core_row.rd_data = rd_data.map(F::from_canonical_u8); // We decompose during fill_trace_row later: diff --git a/extensions/rv32im/circuit/src/base_alu/core.rs b/extensions/rv32im/circuit/src/base_alu/core.rs index d0c0a9bb77..d945db8e63 100644 --- a/extensions/rv32im/circuit/src/base_alu/core.rs +++ b/extensions/rv32im/circuit/src/base_alu/core.rs @@ -1,17 +1,24 @@ use std::{ array, borrow::{Borrow, BorrowMut}, + iter::zip, + marker::PhantomData, }; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, - VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + AdapterAirContext, AdapterTraceStep, InsExecutorE1, MinimalInstruction, Result, + SingleTraceStep, VmAdapterInterface, VmCoreAir, VmExecutionState, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ - bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, + bitwise_op_lookup::{ + BitwiseOperationLookupBus, BitwiseOperationLookupChip, SharedBitwiseOperationLookupChip, + }, utils::not, }; use openvm_circuit_primitives_derive::AlignedBorrow; @@ -28,8 +35,6 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, rap::BaseAirWithPublicValues, }; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_big_array::BigArray; use strum::IntoEnumIterator; #[repr(C)] @@ -173,19 +178,6 @@ where } } -#[repr(C)] -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(bound = "T: Serialize + DeserializeOwned")] -pub struct BaseAluCoreRecord { - pub opcode: BaseAluOpcode, - #[serde(with = "BigArray")] - pub a: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub b: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub c: [T; NUM_LIMBS], -} - pub struct BaseAluCoreChip { pub air: BaseAluCoreAir, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, @@ -204,77 +196,101 @@ impl BaseAluCoreChip VmCoreChip - for BaseAluCoreChip -where - F: PrimeField32, - I: VmAdapterInterface, - I::Reads: Into<[[F; NUM_LIMBS]; 2]>, - I::Writes: From<[[F; NUM_LIMBS]; 1]>, -{ - type Record = BaseAluCoreRecord; - type Air = BaseAluCoreAir; - - #[allow(clippy::type_complexity)] - fn execute_instruction( + #[inline] + pub fn execute( &self, instruction: &Instruction, - _from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { - let Instruction { opcode, .. } = instruction; + [x, y]: [[u8; NUM_LIMBS]; 2], + core_row: &mut [F], + ) -> [u8; NUM_LIMBS] { + let opcode = instruction.opcode; let local_opcode = BaseAluOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - let data: [[F; NUM_LIMBS]; 2] = reads.into(); - let b = data[0].map(|x| x.as_canonical_u32()); - let c = data[1].map(|y| y.as_canonical_u32()); - let a = run_alu::(local_opcode, &b, &c); + let z = run_alu::(local_opcode, &x, &y); + println!("{local_opcode:?} {x:?}, {y:?}: {z:?}"); - let output = AdapterRuntimeContext { - to_pc: None, - writes: [a.map(F::from_canonical_u32)].into(), - }; + let core_row: &mut BaseAluCoreCols = core_row.borrow_mut(); + core_row.a = z.map(F::from_canonical_u8); + core_row.b = x.map(F::from_canonical_u8); + core_row.c = y.map(F::from_canonical_u8); + core_row.opcode_add_flag = F::from_bool(local_opcode == BaseAluOpcode::ADD); + core_row.opcode_sub_flag = F::from_bool(local_opcode == BaseAluOpcode::SUB); + core_row.opcode_xor_flag = F::from_bool(local_opcode == BaseAluOpcode::XOR); + core_row.opcode_or_flag = F::from_bool(local_opcode == BaseAluOpcode::OR); + core_row.opcode_and_flag = F::from_bool(local_opcode == BaseAluOpcode::AND); + + z + } - if local_opcode == BaseAluOpcode::ADD || local_opcode == BaseAluOpcode::SUB { - for a_val in a { + pub fn fill_trace_row(&self, core_row: &mut [F]) { + let core_row: &mut BaseAluCoreCols = core_row.borrow_mut(); + + if core_row.opcode_add_flag == F::ONE || core_row.opcode_sub_flag == F::ONE { + for a_val in core_row.a.map(|x| x.as_canonical_u32()) { self.bitwise_lookup_chip.request_xor(a_val, a_val); } } else { - for (b_val, c_val) in b.iter().zip(c.iter()) { - self.bitwise_lookup_chip.request_xor(*b_val, *c_val); + let b = core_row.b.map(|x| x.as_canonical_u32()); + let c = core_row.c.map(|x| x.as_canonical_u32()); + for (b_val, c_val) in zip(b, c) { + self.bitwise_lookup_chip.request_xor(b_val, c_val); } } + } +} - let record = Self::Record { - opcode: local_opcode, - a: a.map(F::from_canonical_u32), - b: data[0], - c: data[1], - }; +#[derive(derive_new::new)] +pub struct BaseAluStep { + pub core: BaseAluCoreChip, + phantom: PhantomData, +} - Ok((output, record)) - } +impl SingleTraceStep + for BaseAluStep +where + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = [[u8; NUM_LIMBS]; 2], + WriteData = [u8; NUM_LIMBS], + TraceContext<'a> = &'a BitwiseOperationLookupChip, + >, +{ + fn execute( + &mut self, + state: VmStateMut, + instruction: &Instruction, + row_slice: &mut [F], + ) -> Result<()> { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - fn get_opcode_name(&self, opcode: usize) -> String { - format!("{:?}", BaseAluOpcode::from_usize(opcode - self.air.offset)) + A::start(*state.pc, state.memory, adapter_row); + let [rs1, rs2] = A::read(state.memory, instruction, adapter_row); + let output = self.core.execute(instruction, [rs1, rs2], core_row); + A::write(state.memory, instruction, adapter_row, &output); + + *state.pc += DEFAULT_PC_STEP; + Ok(()) } - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let row_slice: &mut BaseAluCoreCols<_, NUM_LIMBS, LIMB_BITS> = row_slice.borrow_mut(); - row_slice.a = record.a; - row_slice.b = record.b; - row_slice.c = record.c; - row_slice.opcode_add_flag = F::from_bool(record.opcode == BaseAluOpcode::ADD); - row_slice.opcode_sub_flag = F::from_bool(record.opcode == BaseAluOpcode::SUB); - row_slice.opcode_xor_flag = F::from_bool(record.opcode == BaseAluOpcode::XOR); - row_slice.opcode_or_flag = F::from_bool(record.opcode == BaseAluOpcode::OR); - row_slice.opcode_and_flag = F::from_bool(record.opcode == BaseAluOpcode::AND); + fn get_opcode_name(&self, opcode: usize) -> String { + format!( + "{:?}", + BaseAluOpcode::from_usize(opcode - self.core.air.offset) + ) } - fn air(&self) -> &Self::Air { - &self.air + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + A::fill_trace_row( + mem_helper, + self.core.bitwise_lookup_chip.as_ref(), + adapter_row, + ); + self.core.fill_trace_row(core_row); } } @@ -315,13 +331,7 @@ where rs2_bytes }; - // TODO(ayush): avoid this conversion - let rs1_bytes: [u32; NUM_LIMBS] = rs1_bytes.map(|x| x as u32); - let rs2_bytes: [u32; NUM_LIMBS] = rs2_bytes.map(|y| y as u32); - - // TODO(ayush): should this be [u8; 4]? let rd_bytes = run_alu::(local_opcode, &rs1_bytes, &rs2_bytes); - let rd_bytes = rd_bytes.map(|x| x as u8); // Write result back to destination register let rd_addr = a.as_canonical_u32(); @@ -335,68 +345,62 @@ where pub(super) fn run_alu( opcode: BaseAluOpcode, - x: &[u32; NUM_LIMBS], - y: &[u32; NUM_LIMBS], -) -> [u32; NUM_LIMBS] { + x: &[u8; NUM_LIMBS], + y: &[u8; NUM_LIMBS], +) -> [u8; NUM_LIMBS] { + debug_assert!(LIMB_BITS <= 8, "specialize for bytes"); match opcode { BaseAluOpcode::ADD => run_add::(x, y), BaseAluOpcode::SUB => run_subtract::(x, y), - BaseAluOpcode::XOR => run_xor::(x, y), - BaseAluOpcode::OR => run_or::(x, y), - BaseAluOpcode::AND => run_and::(x, y), + BaseAluOpcode::XOR => run_xor::(x, y), + BaseAluOpcode::OR => run_or::(x, y), + BaseAluOpcode::AND => run_and::(x, y), } } fn run_add( - x: &[u32; NUM_LIMBS], - y: &[u32; NUM_LIMBS], -) -> [u32; NUM_LIMBS] { - let mut z = [0u32; NUM_LIMBS]; - let mut carry = [0u32; NUM_LIMBS]; + x: &[u8; NUM_LIMBS], + y: &[u8; NUM_LIMBS], +) -> [u8; NUM_LIMBS] { + let mut z = [0u8; NUM_LIMBS]; + let mut carry = [0u8; NUM_LIMBS]; for i in 0..NUM_LIMBS { - z[i] = x[i] + y[i] + if i > 0 { carry[i - 1] } else { 0 }; - carry[i] = z[i] >> LIMB_BITS; - z[i] &= (1 << LIMB_BITS) - 1; + let mut overflow = + (x[i] as u16) + (y[i] as u16) + if i > 0 { carry[i - 1] as u16 } else { 0 }; + carry[i] = (overflow >> LIMB_BITS) as u8; + overflow &= (1u16 << LIMB_BITS) - 1; + z[i] = overflow as u8; } z } fn run_subtract( - x: &[u32; NUM_LIMBS], - y: &[u32; NUM_LIMBS], -) -> [u32; NUM_LIMBS] { - let mut z = [0u32; NUM_LIMBS]; - let mut carry = [0u32; NUM_LIMBS]; + x: &[u8; NUM_LIMBS], + y: &[u8; NUM_LIMBS], +) -> [u8; NUM_LIMBS] { + let mut z = [0u8; NUM_LIMBS]; + let mut carry = [0u8; NUM_LIMBS]; for i in 0..NUM_LIMBS { - let rhs = y[i] + if i > 0 { carry[i - 1] } else { 0 }; - if x[i] >= rhs { - z[i] = x[i] - rhs; + let rhs = y[i] as u16 + if i > 0 { carry[i - 1] as u16 } else { 0 }; + if x[i] as u16 >= rhs { + z[i] = x[i] - rhs as u8; carry[i] = 0; } else { - z[i] = x[i] + (1 << LIMB_BITS) - rhs; + z[i] = (x[i] as u16 + (1u16 << LIMB_BITS) - rhs) as u8; carry[i] = 1; } } z } -fn run_xor( - x: &[u32; NUM_LIMBS], - y: &[u32; NUM_LIMBS], -) -> [u32; NUM_LIMBS] { +fn run_xor(x: &[u8; NUM_LIMBS], y: &[u8; NUM_LIMBS]) -> [u8; NUM_LIMBS] { array::from_fn(|i| x[i] ^ y[i]) } -fn run_or( - x: &[u32; NUM_LIMBS], - y: &[u32; NUM_LIMBS], -) -> [u32; NUM_LIMBS] { +fn run_or(x: &[u8; NUM_LIMBS], y: &[u8; NUM_LIMBS]) -> [u8; NUM_LIMBS] { array::from_fn(|i| x[i] | y[i]) } -fn run_and( - x: &[u32; NUM_LIMBS], - y: &[u32; NUM_LIMBS], -) -> [u32; NUM_LIMBS] { +fn run_and(x: &[u8; NUM_LIMBS], y: &[u8; NUM_LIMBS]) -> [u8; NUM_LIMBS] { array::from_fn(|i| x[i] & y[i]) } diff --git a/extensions/rv32im/circuit/src/base_alu/mod.rs b/extensions/rv32im/circuit/src/base_alu/mod.rs index cbda8ce555..266a7ee453 100644 --- a/extensions/rv32im/circuit/src/base_alu/mod.rs +++ b/extensions/rv32im/circuit/src/base_alu/mod.rs @@ -1,7 +1,8 @@ -use openvm_circuit::arch::VmChipWrapper; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; -use super::adapters::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; -use crate::adapters::Rv32BaseAluAdapterChip; +use super::adapters::{ + Rv32BaseAluAdapterAir, Rv32BaseAluAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, +}; mod core; pub use core::*; @@ -9,8 +10,8 @@ pub use core::*; #[cfg(test)] mod tests; -pub type Rv32BaseAluChip = VmChipWrapper< - F, - Rv32BaseAluAdapterChip, - BaseAluCoreChip, ->; +pub type Rv32BaseAluAir = + VmAirWrapper>; +pub type Rv32BaseAluStep = + BaseAluStep, RV32_REGISTER_NUM_LIMBS, RV32_CELL_BITS>; +pub type Rv32BaseAluChip = NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/base_alu/tests.rs b/extensions/rv32im/circuit/src/base_alu/tests.rs index 912daec858..d1dcec6ada 100644 --- a/extensions/rv32im/circuit/src/base_alu/tests.rs +++ b/extensions/rv32im/circuit/src/base_alu/tests.rs @@ -3,10 +3,8 @@ use std::borrow::BorrowMut; use openvm_circuit::{ arch::{ testing::{TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge, ExecutionState, - MinimalInstruction, Result, VmAdapterChip, VmAdapterInterface, VmChipWrapper, + NewVmChipWrapper, VmAirWrapper, VmChipWrapper, }, - system::memory::{MemoryController, OfflineMemory}, utils::generate_long_number, }; use openvm_circuit_primitives::bitwise_op_lookup::{ @@ -15,8 +13,7 @@ use openvm_circuit_primitives::bitwise_op_lookup::{ use openvm_instructions::{instruction::Instruction, LocalOpcode}; use openvm_rv32im_transpiler::BaseAluOpcode; use openvm_stark_backend::{ - p3_air::BaseAir, - p3_field::{Field, FieldAlgebra, PrimeField32}, + p3_field::{FieldAlgebra, PrimeField32}, p3_matrix::{ dense::{DenseMatrix, RowMajorMatrix}, Matrix, @@ -28,18 +25,41 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::Rng; -use super::{core::run_alu, BaseAluCoreChip, Rv32BaseAluChip}; +use super::{core::run_alu, BaseAluCoreChip, Rv32BaseAluChip, Rv32BaseAluStep}; use crate::{ adapters::{ - Rv32BaseAluAdapterAir, Rv32BaseAluAdapterChip, Rv32BaseAluReadRecord, - Rv32BaseAluWriteRecord, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + Rv32BaseAluAdapterAir, Rv32BaseAluAdapterCols, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, }, base_alu::BaseAluCoreCols, test_utils::{generate_rv32_is_type_immediate, rv32_rand_write_register_or_imm}, }; type F = BabyBear; +const MAX_INS_CAPACITY: usize = 128; +fn create_test_chip( + tester: &VmChipTestBuilder, +) -> ( + Rv32BaseAluChip, + SharedBitwiseOperationLookupChip, +) { + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + let step = Rv32BaseAluStep::new(BaseAluCoreChip::new( + bitwise_chip.clone(), + BaseAluOpcode::CLASS_OFFSET, + )); + let air = VmAirWrapper::new( + Rv32BaseAluAdapterAir::new( + tester.execution_bridge(), + tester.memory_bridge(), + bitwise_bus, + ), + step.core.air, + ); + let chip = NewVmChipWrapper::new(air, step, MAX_INS_CAPACITY, tester.memory_helper()); + (chip, bitwise_chip) +} ////////////////////////////////////////////////////////////////////////////////////// // POSITIVE TESTS // @@ -49,27 +69,18 @@ type F = BabyBear; fn run_rv32_alu_rand_test(opcode: BaseAluOpcode, num_ops: usize) { let mut rng = create_seeded_rng(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut tester = VmChipTestBuilder::default(); - let mut chip = Rv32BaseAluChip::::new( - Rv32BaseAluAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - bitwise_chip.clone(), - ), - BaseAluCoreChip::new(bitwise_chip.clone(), BaseAluOpcode::CLASS_OFFSET), - tester.offline_memory_mutex_arc(), - ); + let (mut chip, bitwise_chip) = create_test_chip(&tester); for _ in 0..num_ops { - let b = generate_long_number::(&mut rng); + let b = generate_long_number::(&mut rng) + .map(|x| x as u8); let (c_imm, c) = if rng.gen_bool(0.5) { ( None, - generate_long_number::(&mut rng), + generate_long_number::(&mut rng) + .map(|x| x as u8), ) } else { let (imm, c) = generate_rv32_is_type_immediate(&mut rng); @@ -87,7 +98,7 @@ fn run_rv32_alu_rand_test(opcode: BaseAluOpcode, num_ops: usize) { tester.execute(&mut chip, &instruction); let a = run_alu::(opcode, &b, &c) - .map(F::from_canonical_u32); + .map(F::from_canonical_u8); assert_eq!(a, tester.read::(1, rd)) } @@ -130,6 +141,7 @@ fn rv32_alu_and_rand_test() { type Rv32BaseAluTestChip = VmChipWrapper, BaseAluCoreChip>; +// TODO: FIX NEGATIVE TESTS #[allow(clippy::too_many_arguments)] fn run_rv32_alu_negative_test( @@ -139,19 +151,17 @@ fn run_rv32_alu_negative_test( c: [u32; RV32_REGISTER_NUM_LIMBS], interaction_error: bool, ) { - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let mut chip = Rv32BaseAluTestChip::::new( - TestAdapterChip::new( - vec![[b.map(F::from_canonical_u32), c.map(F::from_canonical_u32)].concat()], - vec![None], - ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), - ), - BaseAluCoreChip::new(bitwise_chip.clone(), BaseAluOpcode::CLASS_OFFSET), - tester.offline_memory_mutex_arc(), - ); + let (mut chip, bitwise_chip) = create_test_chip(&tester); + // let mut chip = Rv32BaseAluTestChip::::new( + // TestAdapterChip::new( + // vec![[b.map(F::from_canonical_u32), c.map(F::from_canonical_u32)].concat()], + // vec![None], + // ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), + // ), + // BaseAluCoreChip::new(bitwise_chip.clone(), BaseAluOpcode::CLASS_OFFSET), + // tester.offline_memory_mutex_arc(), + // ); tester.execute( &mut chip, @@ -159,7 +169,7 @@ fn run_rv32_alu_negative_test( ); let trace_width = chip.trace_width(); - let adapter_width = BaseAir::::width(chip.adapter.air()); + let adapter_width = Rv32BaseAluAdapterCols::::width(); if (opcode == BaseAluOpcode::ADD || opcode == BaseAluOpcode::SUB) && a.iter().all(|&a_val| a_val < (1 << RV32_CELL_BITS)) @@ -276,9 +286,9 @@ fn rv32_alu_and_wrong_negative_test() { #[test] fn run_add_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [229, 33, 29, 111]; - let y: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 171, 44, 194]; - let z: [u32; RV32_REGISTER_NUM_LIMBS] = [23, 205, 73, 49]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [229, 33, 29, 111]; + let y: [u8; RV32_REGISTER_NUM_LIMBS] = [50, 171, 44, 194]; + let z: [u8; RV32_REGISTER_NUM_LIMBS] = [23, 205, 73, 49]; let result = run_alu::(BaseAluOpcode::ADD, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], result[i]) @@ -287,9 +297,9 @@ fn run_add_sanity_test() { #[test] fn run_sub_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [229, 33, 29, 111]; - let y: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 171, 44, 194]; - let z: [u32; RV32_REGISTER_NUM_LIMBS] = [179, 118, 240, 172]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [229, 33, 29, 111]; + let y: [u8; RV32_REGISTER_NUM_LIMBS] = [50, 171, 44, 194]; + let z: [u8; RV32_REGISTER_NUM_LIMBS] = [179, 118, 240, 172]; let result = run_alu::(BaseAluOpcode::SUB, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], result[i]) @@ -298,9 +308,9 @@ fn run_sub_sanity_test() { #[test] fn run_xor_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [229, 33, 29, 111]; - let y: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 171, 44, 194]; - let z: [u32; RV32_REGISTER_NUM_LIMBS] = [215, 138, 49, 173]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [229, 33, 29, 111]; + let y: [u8; RV32_REGISTER_NUM_LIMBS] = [50, 171, 44, 194]; + let z: [u8; RV32_REGISTER_NUM_LIMBS] = [215, 138, 49, 173]; let result = run_alu::(BaseAluOpcode::XOR, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], result[i]) @@ -309,9 +319,9 @@ fn run_xor_sanity_test() { #[test] fn run_or_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [229, 33, 29, 111]; - let y: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 171, 44, 194]; - let z: [u32; RV32_REGISTER_NUM_LIMBS] = [247, 171, 61, 239]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [229, 33, 29, 111]; + let y: [u8; RV32_REGISTER_NUM_LIMBS] = [50, 171, 44, 194]; + let z: [u8; RV32_REGISTER_NUM_LIMBS] = [247, 171, 61, 239]; let result = run_alu::(BaseAluOpcode::OR, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], result[i]) @@ -320,9 +330,9 @@ fn run_or_sanity_test() { #[test] fn run_and_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [229, 33, 29, 111]; - let y: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 171, 44, 194]; - let z: [u32; RV32_REGISTER_NUM_LIMBS] = [32, 33, 12, 66]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [229, 33, 29, 111]; + let y: [u8; RV32_REGISTER_NUM_LIMBS] = [50, 171, 44, 194]; + let z: [u8; RV32_REGISTER_NUM_LIMBS] = [32, 33, 12, 66]; let result = run_alu::(BaseAluOpcode::AND, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], result[i]) @@ -335,185 +345,185 @@ fn run_and_sanity_test() { // Ensure that the adapter is correct. ////////////////////////////////////////////////////////////////////////////////////// +// TODO: put this back // A pranking chip where `preprocess` can have `rs2` limbs that overflow. -struct Rv32BaseAluAdapterTestChip(Rv32BaseAluAdapterChip); - -impl VmAdapterChip for Rv32BaseAluAdapterTestChip { - type ReadRecord = Rv32BaseAluReadRecord; - type WriteRecord = Rv32BaseAluWriteRecord; - type Air = Rv32BaseAluAdapterAir; - type Interface = BasicAdapterInterface< - F, - MinimalInstruction, - 2, - 1, - RV32_REGISTER_NUM_LIMBS, - RV32_REGISTER_NUM_LIMBS, - >; - - fn preprocess( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { - let Instruction { b, c, d, e, .. } = *instruction; - - todo!() - // let rs1 = memory.read::(d, b); - // let (rs2, rs2_data, rs2_imm) = if e.is_zero() { - // let c_u32 = c.as_canonical_u32(); - // memory.increment_timestamp(); - // let mask1 = (1 << 9) - 1; - // let mask2 = (1 << 3) - 2; - // ( - // None, - // [ - // (c_u32 & mask1) as u16, - // ((c_u32 >> 8) & mask2) as u16, - // (c_u32 >> 16) as u16, - // (c_u32 >> 16) as u16, - // ] - // .map(F::from_canonical_u16), - // c, - // ) - // } else { - // let rs2_read = memory.read::(e, c); - // ( - // Some(rs2_read.0), - // rs2_read.1.map(F::from_canonical_u8), - // F::ZERO, - // ) - // }; - - // Ok(( - // [rs1.1.map(F::from_canonical_u8), rs2_data], - // Self::ReadRecord { - // rs1: rs1.0, - // rs2, - // rs2_imm, - // }, - // )) - } - - fn postprocess( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - _read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - self.0 - .postprocess(memory, instruction, from_state, output, _read_record) - } - - fn generate_trace_row( - &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, - ) { - self.0 - .generate_trace_row(row_slice, read_record, write_record, memory) - } - - fn air(&self) -> &Self::Air { - self.0.air() - } -} - -#[test] -fn rv32_alu_adapter_unconstrained_imm_limb_test() { - let mut rng = create_seeded_rng(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - - let mut tester = VmChipTestBuilder::default(); - let mut chip = VmChipWrapper::new( - Rv32BaseAluAdapterTestChip(Rv32BaseAluAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - bitwise_chip.clone(), - )), - BaseAluCoreChip::new(bitwise_chip.clone(), BaseAluOpcode::CLASS_OFFSET), - tester.offline_memory_mutex_arc(), - ); - - let b = [0, 0, 0, 0]; - let (c_imm, c) = { - let imm = (1 << 11) - 1; - let fake_c = [(1 << 9) - 1, (1 << 3) - 2, 0, 0]; - (Some(imm), fake_c) - }; - - let (instruction, _rd) = rv32_rand_write_register_or_imm( - &mut tester, - b, - c, - c_imm, - BaseAluOpcode::ADD.global_opcode().as_usize(), - &mut rng, - ); - tester.execute(&mut chip, &instruction); - - disable_debug_builder(); - let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - tester.simple_test_with_expected_error(VerificationError::ChallengePhaseError); -} - -#[test] -fn rv32_alu_adapter_unconstrained_rs2_read_test() { - let mut rng = create_seeded_rng(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - - let mut tester = VmChipTestBuilder::default(); - let mut chip = Rv32BaseAluChip::::new( - Rv32BaseAluAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - bitwise_chip.clone(), - ), - BaseAluCoreChip::new(bitwise_chip.clone(), BaseAluOpcode::CLASS_OFFSET), - tester.offline_memory_mutex_arc(), - ); - - let b = [1, 1, 1, 1]; - let c = [1, 1, 1, 1]; - let (instruction, _rd) = rv32_rand_write_register_or_imm( - &mut tester, - b, - c, - None, - BaseAluOpcode::ADD.global_opcode().as_usize(), - &mut rng, - ); - tester.execute(&mut chip, &instruction); - - let trace_width = chip.trace_width(); - let adapter_width = BaseAir::::width(chip.adapter.air()); - - let modify_trace = |trace: &mut DenseMatrix| { - let mut values = trace.row_slice(0).to_vec(); - let mut dummy_values = values.clone(); - let cols: &mut BaseAluCoreCols = - dummy_values.split_at_mut(adapter_width).1.borrow_mut(); - cols.opcode_add_flag = F::ZERO; - values.extend(dummy_values); - *trace = RowMajorMatrix::new(values, trace_width); - }; - - disable_debug_builder(); - let tester = tester - .build() - .load_and_prank_trace(chip, modify_trace) - .load(bitwise_chip) - .finalize(); - tester.simple_test_with_expected_error(VerificationError::OodEvaluationMismatch); -} +// struct Rv32BaseAluAdapterTestChip(Rv32BaseAluAdapterChip); + +// impl VmAdapterChip for Rv32BaseAluAdapterTestChip { +// type ReadRecord = Rv32BaseAluReadRecord; +// type WriteRecord = Rv32BaseAluWriteRecord; +// type Air = Rv32BaseAluAdapterAir; +// type Interface = BasicAdapterInterface< +// F, +// MinimalInstruction, +// 2, +// 1, +// RV32_REGISTER_NUM_LIMBS, +// RV32_REGISTER_NUM_LIMBS, +// >; + +// fn preprocess( +// &mut self, +// memory: &mut MemoryController, +// instruction: &Instruction, +// ) -> Result<( +// >::Reads, +// Self::ReadRecord, +// )> { +// let Instruction { b, c, d, e, .. } = *instruction; + +// let rs1 = memory.read::(d, b); +// let (rs2, rs2_data, rs2_imm) = if e.is_zero() { +// let c_u32 = c.as_canonical_u32(); +// memory.increment_timestamp(); +// let mask1 = (1 << 9) - 1; +// let mask2 = (1 << 3) - 2; +// ( +// None, +// [ +// (c_u32 & mask1) as u16, +// ((c_u32 >> 8) & mask2) as u16, +// (c_u32 >> 16) as u16, +// (c_u32 >> 16) as u16, +// ] +// .map(F::from_canonical_u16), +// c, +// ) +// } else { +// let rs2_read = memory.read::(e, c); +// ( +// Some(rs2_read.0), +// rs2_read.1.map(F::from_canonical_u8), +// F::ZERO, +// ) +// }; + +// Ok(( +// [rs1.1.map(F::from_canonical_u8), rs2_data], +// Self::ReadRecord { +// rs1: rs1.0, +// rs2, +// rs2_imm, +// }, +// )) +// } + +// fn postprocess( +// &mut self, +// memory: &mut MemoryController, +// instruction: &Instruction, +// from_state: ExecutionState, +// output: AdapterRuntimeContext, +// _read_record: &Self::ReadRecord, +// ) -> Result<(ExecutionState, Self::WriteRecord)> { +// self.0 +// .postprocess(memory, instruction, from_state, output, _read_record) +// } + +// fn generate_trace_row( +// &self, +// row_slice: &mut [F], +// read_record: Self::ReadRecord, +// write_record: Self::WriteRecord, +// memory: &OfflineMemory, +// ) { +// self.0 +// .generate_trace_row(row_slice, read_record, write_record, memory) +// } + +// fn air(&self) -> &Self::Air { +// self.0.air() +// } +// } + +// #[test] +// fn rv32_alu_adapter_unconstrained_imm_limb_test() { +// let mut rng = create_seeded_rng(); +// let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); +// let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + +// let mut tester = VmChipTestBuilder::default(); +// let mut chip = VmChipWrapper::new( +// Rv32BaseAluAdapterTestChip(Rv32BaseAluAdapterChip::new( +// tester.execution_bus(), +// tester.program_bus(), +// tester.memory_bridge(), +// bitwise_chip.clone(), +// )), +// BaseAluCoreChip::new(bitwise_chip.clone(), BaseAluOpcode::CLASS_OFFSET), +// tester.offline_memory_mutex_arc(), +// ); + +// let b = [0, 0, 0, 0]; +// let (c_imm, c) = { +// let imm = (1 << 11) - 1; +// let fake_c = [(1 << 9) - 1, (1 << 3) - 2, 0, 0]; +// (Some(imm), fake_c) +// }; + +// let (instruction, _rd) = rv32_rand_write_register_or_imm( +// &mut tester, +// b, +// c, +// c_imm, +// BaseAluOpcode::ADD.global_opcode().as_usize(), +// &mut rng, +// ); +// tester.execute(&mut chip, &instruction); + +// disable_debug_builder(); +// let tester = tester.build().load(chip).load(bitwise_chip).finalize(); +// tester.simple_test_with_expected_error(VerificationError::ChallengePhaseError); +// } + +// #[test] +// fn rv32_alu_adapter_unconstrained_rs2_read_test() { +// let mut rng = create_seeded_rng(); +// let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); +// let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + +// let mut tester = VmChipTestBuilder::default(); +// let mut chip = Rv32BaseAluChip::::new( +// Rv32BaseAluAdapterChip::new( +// tester.execution_bus(), +// tester.program_bus(), +// tester.memory_bridge(), +// bitwise_chip.clone(), +// ), +// BaseAluCoreChip::new(bitwise_chip.clone(), BaseAluOpcode::CLASS_OFFSET), +// tester.offline_memory_mutex_arc(), +// ); + +// let b = [1, 1, 1, 1]; +// let c = [1, 1, 1, 1]; +// let (instruction, _rd) = rv32_rand_write_register_or_imm( +// &mut tester, +// b, +// c, +// None, +// BaseAluOpcode::ADD.global_opcode().as_usize(), +// &mut rng, +// ); +// tester.execute(&mut chip, &instruction); + +// let trace_width = chip.trace_width(); +// let adapter_width = BaseAir::::width(chip.adapter.air()); + +// let modify_trace = |trace: &mut DenseMatrix| { +// let mut values = trace.row_slice(0).to_vec(); +// let mut dummy_values = values.clone(); +// let cols: &mut BaseAluCoreCols = +// dummy_values.split_at_mut(adapter_width).1.borrow_mut(); +// cols.opcode_add_flag = F::ZERO; +// values.extend(dummy_values); +// *trace = RowMajorMatrix::new(values, trace_width); +// }; + +// disable_debug_builder(); +// let tester = tester +// .build() +// .load_and_prank_trace(chip, modify_trace) +// .load(bitwise_chip) +// .finalize(); +// tester.simple_test_with_expected_error(VerificationError::OodEvaluationMismatch); +// } diff --git a/extensions/rv32im/circuit/src/extension.rs b/extensions/rv32im/circuit/src/extension.rs index 57f82aba9b..e62ad1e426 100644 --- a/extensions/rv32im/circuit/src/extension.rs +++ b/extensions/rv32im/circuit/src/extension.rs @@ -212,48 +212,48 @@ impl VmExtension for Rv32I { chip }; - let base_alu_chip = Rv32BaseAluChip::new( - Rv32BaseAluAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - bitwise_lu_chip.clone(), - ), - BaseAluCoreChip::new(bitwise_lu_chip.clone(), BaseAluOpcode::CLASS_OFFSET), - offline_memory.clone(), - ); - inventory.add_executor( - base_alu_chip, - BaseAluOpcode::iter().map(|x| x.global_opcode()), - )?; - - let lt_chip = Rv32LessThanChip::new( - Rv32BaseAluAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - bitwise_lu_chip.clone(), - ), - LessThanCoreChip::new(bitwise_lu_chip.clone(), LessThanOpcode::CLASS_OFFSET), - offline_memory.clone(), - ); - inventory.add_executor(lt_chip, LessThanOpcode::iter().map(|x| x.global_opcode()))?; + // let base_alu_chip = Rv32BaseAluChip::new( + // Rv32BaseAluAdapterChip::new( + // execution_bus, + // program_bus, + // memory_bridge, + // bitwise_lu_chip.clone(), + // ), + // BaseAluCoreChip::new(bitwise_lu_chip.clone(), BaseAluOpcode::CLASS_OFFSET), + // offline_memory.clone(), + // ); + // inventory.add_executor( + // base_alu_chip, + // BaseAluOpcode::iter().map(|x| x.global_opcode()), + // )?; - let shift_chip = Rv32ShiftChip::new( - Rv32BaseAluAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - bitwise_lu_chip.clone(), - ), - ShiftCoreChip::new( - bitwise_lu_chip.clone(), - range_checker.clone(), - ShiftOpcode::CLASS_OFFSET, - ), - offline_memory.clone(), - ); - inventory.add_executor(shift_chip, ShiftOpcode::iter().map(|x| x.global_opcode()))?; + // let lt_chip = Rv32LessThanChip::new( + // Rv32BaseAluAdapterChip::new( + // execution_bus, + // program_bus, + // memory_bridge, + // bitwise_lu_chip.clone(), + // ), + // LessThanCoreChip::new(bitwise_lu_chip.clone(), LessThanOpcode::CLASS_OFFSET), + // offline_memory.clone(), + // ); + // inventory.add_executor(lt_chip, LessThanOpcode::iter().map(|x| x.global_opcode()))?; + + // let shift_chip = Rv32ShiftChip::new( + // Rv32BaseAluAdapterChip::new( + // execution_bus, + // program_bus, + // memory_bridge, + // bitwise_lu_chip.clone(), + // ), + // ShiftCoreChip::new( + // bitwise_lu_chip.clone(), + // range_checker.clone(), + // ShiftOpcode::CLASS_OFFSET, + // ), + // offline_memory.clone(), + // ); + // inventory.add_executor(shift_chip, ShiftOpcode::iter().map(|x| x.global_opcode()))?; let load_store_chip = Rv32LoadStoreChip::new( Rv32LoadStoreAdapterChip::new( diff --git a/extensions/rv32im/circuit/src/jal_lui/core.rs b/extensions/rv32im/circuit/src/jal_lui/core.rs index d70745b626..18c2fb37cf 100644 --- a/extensions/rv32im/circuit/src/jal_lui/core.rs +++ b/extensions/rv32im/circuit/src/jal_lui/core.rs @@ -192,7 +192,7 @@ impl SingleTraceStep for Rv32JalLuiCoreChip { .opcode .local_opcode_idx(Rv32JalLuiOpcode::CLASS_OFFSET), ); - let from_timestamp = state.memory.timestamp(); + state.ins_start(&mut adapter_row.inner.from_state); // `c` can be "negative" as a field element let imm_f = instruction.c.as_canonical_u32(); let signed_imm = match local_opcode { @@ -211,18 +211,17 @@ impl SingleTraceStep for Rv32JalLuiCoreChip { if instruction.f != F::ZERO { let rd_ptr = instruction.a.as_canonical_u32(); - let (t_prev, data_prev) = tracing_write_reg(state.memory, rd_ptr, &rd_data); - adapter_row.inner.rd_ptr = instruction.a; - adapter_row.inner.rd_aux_cols.set_prev( - F::from_canonical_u32(t_prev), - data_prev.map(F::from_canonical_u8), + let write_buf = &mut adapter_row.inner; + tracing_write_reg( + state.memory, + rd_ptr, + &rd_data, + (&mut write_buf.rd_ptr, &mut write_buf.rd_aux_cols), ); adapter_row.needs_write = F::ONE; } else { state.memory.increment_timestamp(); } - adapter_row.inner.from_state.pc = F::from_canonical_u32(*state.pc); - adapter_row.inner.from_state.timestamp = F::from_canonical_u32(from_timestamp); core_row.rd_data = rd_data.map(F::from_canonical_u8); core_row.imm = instruction.c; core_row.is_jal = F::from_bool(local_opcode == JAL); diff --git a/extensions/rv32im/circuit/src/less_than/core.rs b/extensions/rv32im/circuit/src/less_than/core.rs index d150a0fc14..0efb491bad 100644 --- a/extensions/rv32im/circuit/src/less_than/core.rs +++ b/extensions/rv32im/circuit/src/less_than/core.rs @@ -1,17 +1,23 @@ use std::{ array, borrow::{Borrow, BorrowMut}, + marker::PhantomData, }; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, - VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + AdapterAirContext, AdapterTraceStep, InsExecutorE1, MinimalInstruction, Result, + SingleTraceStep, VmAdapterInterface, VmCoreAir, VmExecutionState, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ - bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, + bitwise_op_lookup::{ + BitwiseOperationLookupBus, BitwiseOperationLookupChip, SharedBitwiseOperationLookupChip, + }, utils::not, }; use openvm_circuit_primitives_derive::AlignedBorrow; @@ -205,61 +211,70 @@ impl LessThanCoreChip, const NUM_LIMBS: usize, const LIMB_BITS: usize> - VmCoreChip for LessThanCoreChip -where - I::Reads: Into<[[F; NUM_LIMBS]; 2]>, - I::Writes: From<[[F; NUM_LIMBS]; 1]>, -{ - type Record = LessThanCoreRecord; - type Air = LessThanCoreAir; - #[allow(clippy::type_complexity)] - fn execute_instruction( + #[inline] + fn execute( &self, instruction: &Instruction, - _from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { - let Instruction { opcode, .. } = instruction; - let less_than_opcode = LessThanOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); + [b, c]: [[u8; NUM_LIMBS]; 2], + core_row: &mut [F], + ) -> [u8; NUM_LIMBS] { + debug_assert!(LIMB_BITS <= 8); + let opcode = instruction.opcode; + let local_opcode = LessThanOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); + + let (cmp_result, _, _, _) = run_less_than::(local_opcode, &b, &c); + + let core_row: &mut LessThanCoreCols<_, NUM_LIMBS, LIMB_BITS> = core_row.borrow_mut(); + core_row.b = b.map(F::from_canonical_u8); + core_row.c = c.map(F::from_canonical_u8); + core_row.opcode_slt_flag = F::from_bool(local_opcode == LessThanOpcode::SLT); + core_row.opcode_sltu_flag = F::from_bool(local_opcode == LessThanOpcode::SLTU); + + let mut output = [0u8; NUM_LIMBS]; + output[0] = cmp_result as u8; + output + } - let data: [[F; NUM_LIMBS]; 2] = reads.into(); - let b = data[0].map(|x| x.as_canonical_u32()); - let c = data[1].map(|y| y.as_canonical_u32()); + pub fn fill_trace_row(&self, core_row: &mut [F]) { + let core_row: &mut LessThanCoreCols<_, NUM_LIMBS, LIMB_BITS> = core_row.borrow_mut(); + let b = core_row.b.map(|x| x.as_canonical_u32() as u8); + let c = core_row.c.map(|x| x.as_canonical_u32() as u8); + // It's easier (and faster?) to re-execute + let local_opcode = if core_row.opcode_slt_flag.is_one() { + LessThanOpcode::SLT + } else { + LessThanOpcode::SLTU + }; let (cmp_result, diff_idx, b_sign, c_sign) = - run_less_than::(less_than_opcode, &b, &c); + run_less_than::(local_opcode, &b, &c); // We range check (b_msb_f + 128) and (c_msb_f + 128) if signed, // b_msb_f and c_msb_f if not let (b_msb_f, b_msb_range) = if b_sign { ( - -F::from_canonical_u32((1 << LIMB_BITS) - b[NUM_LIMBS - 1]), - b[NUM_LIMBS - 1] - (1 << (LIMB_BITS - 1)), + -F::from_canonical_u16((1u16 << LIMB_BITS) - b[NUM_LIMBS - 1] as u16), + b[NUM_LIMBS - 1] - (1u8 << (LIMB_BITS - 1)), ) } else { ( - F::from_canonical_u32(b[NUM_LIMBS - 1]), + F::from_canonical_u8(b[NUM_LIMBS - 1]), b[NUM_LIMBS - 1] - + (((less_than_opcode == LessThanOpcode::SLT) as u32) << (LIMB_BITS - 1)), + + (((local_opcode == LessThanOpcode::SLT) as u8) << (LIMB_BITS - 1)), ) }; let (c_msb_f, c_msb_range) = if c_sign { ( - -F::from_canonical_u32((1 << LIMB_BITS) - c[NUM_LIMBS - 1]), - c[NUM_LIMBS - 1] - (1 << (LIMB_BITS - 1)), + -F::from_canonical_u16((1u16 << LIMB_BITS) - c[NUM_LIMBS - 1] as u16), + c[NUM_LIMBS - 1] - (1u8 << (LIMB_BITS - 1)), ) } else { ( - F::from_canonical_u32(c[NUM_LIMBS - 1]), + F::from_canonical_u8(c[NUM_LIMBS - 1]), c[NUM_LIMBS - 1] - + (((less_than_opcode == LessThanOpcode::SLT) as u32) << (LIMB_BITS - 1)), + + (((local_opcode == LessThanOpcode::SLT) as u8) << (LIMB_BITS - 1)), ) }; - self.bitwise_lookup_chip - .request_range(b_msb_range, c_msb_range); let diff_val = if diff_idx == NUM_LIMBS { 0 @@ -271,52 +286,77 @@ where } .as_canonical_u32() } else if cmp_result { - c[diff_idx] - b[diff_idx] + (c[diff_idx] - b[diff_idx]) as u32 } else { - b[diff_idx] - c[diff_idx] + (b[diff_idx] - c[diff_idx]) as u32 }; + self.bitwise_lookup_chip + .request_range(b_msb_range as u32, c_msb_range as u32); if diff_idx != NUM_LIMBS { self.bitwise_lookup_chip.request_range(diff_val - 1, 0); } - let mut writes = [0u32; NUM_LIMBS]; - writes[0] = cmp_result as u32; - - let output = AdapterRuntimeContext::without_pc([writes.map(F::from_canonical_u32)]); - let record = LessThanCoreRecord { - opcode: less_than_opcode, - b: data[0], - c: data[1], - cmp_result: F::from_bool(cmp_result), - b_msb_f, - c_msb_f, - diff_val: F::from_canonical_u32(diff_val), - diff_idx, - }; - - Ok((output, record)) + core_row.diff_val = F::from_canonical_u32(diff_val); + core_row.cmp_result = F::from_bool(cmp_result); + core_row.b_msb_f = b_msb_f; + core_row.c_msb_f = c_msb_f; + core_row.diff_val = F::from_canonical_u32(diff_val); + core_row.diff_marker = array::from_fn(|i| F::from_bool(i == diff_idx)); } +} - fn get_opcode_name(&self, opcode: usize) -> String { - format!("{:?}", LessThanOpcode::from_usize(opcode - self.air.offset)) +#[derive(derive_new::new)] +pub struct LessThanStep { + pub core: LessThanCoreChip, + phantom: PhantomData, +} + +impl SingleTraceStep + for LessThanStep +where + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = [[u8; NUM_LIMBS]; 2], + WriteData = [u8; NUM_LIMBS], + TraceContext<'a> = &'a BitwiseOperationLookupChip, + >, +{ + fn execute( + &mut self, + state: VmStateMut, + instruction: &Instruction, + row_slice: &mut [F], + ) -> Result<()> { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + A::start(*state.pc, state.memory, adapter_row); + let [rs1, rs2] = A::read(state.memory, instruction, adapter_row); + let output = self.core.execute(instruction, [rs1, rs2], core_row); + A::write(state.memory, instruction, adapter_row, &output); + + *state.pc += DEFAULT_PC_STEP; + Ok(()) } - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let row_slice: &mut LessThanCoreCols<_, NUM_LIMBS, LIMB_BITS> = row_slice.borrow_mut(); - row_slice.b = record.b; - row_slice.c = record.c; - row_slice.cmp_result = record.cmp_result; - row_slice.b_msb_f = record.b_msb_f; - row_slice.c_msb_f = record.c_msb_f; - row_slice.diff_val = record.diff_val; - row_slice.opcode_slt_flag = F::from_bool(record.opcode == LessThanOpcode::SLT); - row_slice.opcode_sltu_flag = F::from_bool(record.opcode == LessThanOpcode::SLTU); - row_slice.diff_marker = array::from_fn(|i| F::from_bool(i == record.diff_idx)); + fn get_opcode_name(&self, opcode: usize) -> String { + format!( + "{:?}", + LessThanOpcode::from_usize(opcode - self.core.air.offset) + ) } - fn air(&self) -> &Self::Air { - &self.air + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + A::fill_trace_row( + mem_helper, + self.core.bitwise_lookup_chip.as_ref(), + adapter_row, + ); + self.core.fill_trace_row(core_row); } } @@ -357,10 +397,6 @@ where rs2_bytes }; - // TODO(ayush): avoid this conversion - let rs1_bytes: [u32; NUM_LIMBS] = rs1_bytes.map(|x| x as u32); - let rs2_bytes: [u32; NUM_LIMBS] = rs2_bytes.map(|y| y as u32); - // Run the comparison let (cmp_result, _, _, _) = run_less_than::(less_than_opcode, &rs1_bytes, &rs2_bytes); @@ -380,10 +416,11 @@ where } // Returns (cmp_result, diff_idx, x_sign, y_sign) +#[inline(always)] pub(super) fn run_less_than( opcode: LessThanOpcode, - x: &[u32; NUM_LIMBS], - y: &[u32; NUM_LIMBS], + x: &[u8; NUM_LIMBS], + y: &[u8; NUM_LIMBS], ) -> (bool, usize, bool, bool) { let x_sign = (x[NUM_LIMBS - 1] >> (LIMB_BITS - 1) == 1) && opcode == LessThanOpcode::SLT; let y_sign = (y[NUM_LIMBS - 1] >> (LIMB_BITS - 1) == 1) && opcode == LessThanOpcode::SLT; diff --git a/extensions/rv32im/circuit/src/less_than/mod.rs b/extensions/rv32im/circuit/src/less_than/mod.rs index f8247d2d33..7fc8937d3c 100644 --- a/extensions/rv32im/circuit/src/less_than/mod.rs +++ b/extensions/rv32im/circuit/src/less_than/mod.rs @@ -1,6 +1,8 @@ -use openvm_circuit::arch::VmChipWrapper; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; -use super::adapters::{Rv32BaseAluAdapterChip, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; +use super::adapters::{ + Rv32BaseAluAdapterAir, Rv32BaseAluAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, +}; mod core; pub use core::*; @@ -8,8 +10,8 @@ pub use core::*; #[cfg(test)] mod tests; -pub type Rv32LessThanChip = VmChipWrapper< - F, - Rv32BaseAluAdapterChip, - LessThanCoreChip, ->; +pub type Rv32LessThanAir = + VmAirWrapper>; +pub type Rv32LessThanStep = + LessThanStep, RV32_REGISTER_NUM_LIMBS, RV32_CELL_BITS>; +pub type Rv32LessThanChip = NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/less_than/tests.rs b/extensions/rv32im/circuit/src/less_than/tests.rs index 18d64bf5f6..892aa22b66 100644 --- a/extensions/rv32im/circuit/src/less_than/tests.rs +++ b/extensions/rv32im/circuit/src/less_than/tests.rs @@ -3,7 +3,7 @@ use std::borrow::BorrowMut; use openvm_circuit::{ arch::{ testing::{TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - ExecutionBridge, VmAdapterChip, VmChipWrapper, + NewVmChipWrapper, VmAirWrapper, VmChipWrapper, }, utils::{generate_long_number, i32_to_f}, }; @@ -26,15 +26,39 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::Rng; -use super::{core::run_less_than, LessThanCoreChip, Rv32LessThanChip}; +use super::{core::run_less_than, LessThanCoreChip, Rv32LessThanChip, Rv32LessThanStep}; use crate::{ - adapters::{Rv32BaseAluAdapterChip, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, + adapters::{Rv32BaseAluAdapterAir, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, less_than::LessThanCoreCols, test_utils::{generate_rv32_is_type_immediate, rv32_rand_write_register_or_imm}, }; type F = BabyBear; +const MAX_INS_CAPACITY: usize = 128; +fn create_test_chip( + tester: &VmChipTestBuilder, +) -> ( + Rv32LessThanChip, + SharedBitwiseOperationLookupChip, +) { + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + let step = Rv32LessThanStep::new(LessThanCoreChip::new( + bitwise_chip.clone(), + LessThanOpcode::CLASS_OFFSET, + )); + let air = VmAirWrapper::new( + Rv32BaseAluAdapterAir::new( + tester.execution_bridge(), + tester.memory_bridge(), + bitwise_bus, + ), + step.core.air, + ); + let chip = NewVmChipWrapper::new(air, step, MAX_INS_CAPACITY, tester.memory_helper()); + (chip, bitwise_chip) +} ////////////////////////////////////////////////////////////////////////////////////// // POSITIVE TESTS // @@ -44,27 +68,17 @@ type F = BabyBear; fn run_rv32_lt_rand_test(opcode: LessThanOpcode, num_ops: usize) { let mut rng = create_seeded_rng(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut tester = VmChipTestBuilder::default(); - let mut chip = Rv32LessThanChip::::new( - Rv32BaseAluAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - bitwise_chip.clone(), - ), - LessThanCoreChip::new(bitwise_chip.clone(), LessThanOpcode::CLASS_OFFSET), - tester.offline_memory_mutex_arc(), - ); - + let (mut chip, bitwise_chip) = create_test_chip(&tester); for _ in 0..num_ops { - let b = generate_long_number::(&mut rng); + let b = generate_long_number::(&mut rng) + .map(|x| x as u8); let (c_imm, c) = if rng.gen_bool(0.5) { ( None, - generate_long_number::(&mut rng), + generate_long_number::(&mut rng) + .map(|x| x as u8), ) } else { let (imm, c) = generate_rv32_is_type_immediate(&mut rng); @@ -147,33 +161,21 @@ struct LessThanPrankValues { #[allow(clippy::too_many_arguments)] fn run_rv32_lt_negative_test( opcode: LessThanOpcode, - b: [u32; RV32_REGISTER_NUM_LIMBS], - c: [u32; RV32_REGISTER_NUM_LIMBS], + b: [u8; RV32_REGISTER_NUM_LIMBS], + c: [u8; RV32_REGISTER_NUM_LIMBS], cmp_result: bool, prank_vals: LessThanPrankValues, interaction_error: bool, ) { - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let mut chip = Rv32LessThanTestChip::::new( - TestAdapterChip::new( - vec![[b.map(F::from_canonical_u32), c.map(F::from_canonical_u32)].concat()], - vec![None], - ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), - ), - LessThanCoreChip::new(bitwise_chip.clone(), LessThanOpcode::CLASS_OFFSET), - tester.offline_memory_mutex_arc(), - ); - + let (mut chip, bitwise_chip) = create_test_chip(&tester); tester.execute( &mut chip, &Instruction::from_usize(opcode.global_opcode(), [0, 0, 0, 1, 1]), ); let trace_width = chip.trace_width(); - let adapter_width = BaseAir::::width(chip.adapter.air()); + let adapter_width = BaseAir::::width(&chip.air.adapter); let (_, _, b_sign, c_sign) = run_less_than::(opcode, &b, &c); @@ -431,8 +433,8 @@ fn rv32_sltu_wrong_c_msb_sign_negative_test() { #[test] fn run_sltu_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [145, 34, 25, 205]; - let y: [u32; RV32_REGISTER_NUM_LIMBS] = [73, 35, 25, 205]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [145, 34, 25, 205]; + let y: [u8; RV32_REGISTER_NUM_LIMBS] = [73, 35, 25, 205]; let (cmp_result, diff_idx, x_sign, y_sign) = run_less_than::(LessThanOpcode::SLTU, &x, &y); assert!(cmp_result); @@ -443,8 +445,8 @@ fn run_sltu_sanity_test() { #[test] fn run_slt_same_sign_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [145, 34, 25, 205]; - let y: [u32; RV32_REGISTER_NUM_LIMBS] = [73, 35, 25, 205]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [145, 34, 25, 205]; + let y: [u8; RV32_REGISTER_NUM_LIMBS] = [73, 35, 25, 205]; let (cmp_result, diff_idx, x_sign, y_sign) = run_less_than::(LessThanOpcode::SLT, &x, &y); assert!(cmp_result); @@ -455,8 +457,8 @@ fn run_slt_same_sign_sanity_test() { #[test] fn run_slt_diff_sign_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [45, 35, 25, 55]; - let y: [u32; RV32_REGISTER_NUM_LIMBS] = [173, 34, 25, 205]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [45, 35, 25, 55]; + let y: [u8; RV32_REGISTER_NUM_LIMBS] = [173, 34, 25, 205]; let (cmp_result, diff_idx, x_sign, y_sign) = run_less_than::(LessThanOpcode::SLT, &x, &y); assert!(!cmp_result); @@ -467,7 +469,7 @@ fn run_slt_diff_sign_sanity_test() { #[test] fn run_less_than_equal_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [45, 35, 25, 55]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [45, 35, 25, 55]; let (cmp_result, diff_idx, x_sign, y_sign) = run_less_than::(LessThanOpcode::SLT, &x, &x); assert!(!cmp_result); diff --git a/extensions/rv32im/circuit/src/mul/core.rs b/extensions/rv32im/circuit/src/mul/core.rs index 98fbc7e6b8..1cfb4abd64 100644 --- a/extensions/rv32im/circuit/src/mul/core.rs +++ b/extensions/rv32im/circuit/src/mul/core.rs @@ -181,17 +181,17 @@ where ); let data: [[F; NUM_LIMBS]; 2] = reads.into(); - let b = data[0].map(|x| x.as_canonical_u32()); - let c = data[1].map(|y| y.as_canonical_u32()); + let b = data[0].map(|x| u8::try_from(x.as_canonical_u32()).unwrap()); + let c = data[1].map(|y| u8::try_from(y.as_canonical_u32()).unwrap()); let (a, carry) = run_mul::(&b, &c); for (a, carry) in a.iter().zip(carry.iter()) { - self.range_tuple_chip.add_count(&[*a, *carry]); + self.range_tuple_chip.add_count(&[*a as u32, *carry]); } - let output = AdapterRuntimeContext::without_pc([a.map(F::from_canonical_u32)]); + let output = AdapterRuntimeContext::without_pc([a.map(F::from_canonical_u8)]); let record = MultiplicationCoreRecord { - a: a.map(F::from_canonical_u32), + a: a.map(F::from_canonical_u8), b: data[0], c: data[1], }; @@ -246,13 +246,8 @@ where let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; let rs2_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; - // TODO(ayush): remove this conversion - let rs1_bytes = rs1_bytes.map(|x| x as u32); - let rs2_bytes = rs2_bytes.map(|y| y as u32); - // Perform the multiplication let (rd_bytes, _) = run_mul::(&rs1_bytes, &rs2_bytes); - let rd_bytes = rd_bytes.map(|x| x as u8); // Write result to destination register let rd_addr = a.as_canonical_u32(); @@ -266,20 +261,22 @@ where // returns mul, carry pub(super) fn run_mul( - x: &[u32; NUM_LIMBS], - y: &[u32; NUM_LIMBS], -) -> ([u32; NUM_LIMBS], [u32; NUM_LIMBS]) { - let mut result = [0; NUM_LIMBS]; - let mut carry = [0; NUM_LIMBS]; + x: &[u8; NUM_LIMBS], + y: &[u8; NUM_LIMBS], +) -> ([u8; NUM_LIMBS], [u32; NUM_LIMBS]) { + let mut result = [0u8; NUM_LIMBS]; + let mut carry = [0u32; NUM_LIMBS]; for i in 0..NUM_LIMBS { + let mut res = 0u32; if i > 0 { - result[i] = carry[i - 1]; + res = carry[i - 1]; } for j in 0..=i { - result[i] += x[j] * y[i - j]; + res += (x[j] as u32) * (y[i - j] as u32); } - carry[i] = result[i] >> LIMB_BITS; - result[i] %= 1 << LIMB_BITS; + carry[i] = res >> LIMB_BITS; + res %= 1u32 << LIMB_BITS; + result[i] = res as u8; } (result, carry) } diff --git a/extensions/rv32im/circuit/src/mul/tests.rs b/extensions/rv32im/circuit/src/mul/tests.rs index b942c24cc3..329dc857d6 100644 --- a/extensions/rv32im/circuit/src/mul/tests.rs +++ b/extensions/rv32im/circuit/src/mul/tests.rs @@ -62,8 +62,10 @@ fn run_rv32_mul_rand_test(num_ops: usize) { ); for _ in 0..num_ops { - let b = generate_long_number::(&mut rng); - let c = generate_long_number::(&mut rng); + let b = generate_long_number::(&mut rng) + .map(|x| x as u8); + let c = generate_long_number::(&mut rng) + .map(|x| x as u8); let (mut instruction, rd) = rv32_rand_write_register_or_imm( &mut tester, @@ -78,7 +80,7 @@ fn run_rv32_mul_rand_test(num_ops: usize) { let (a, _) = run_mul::(&b, &c); assert_eq!( - a.map(F::from_canonical_u32), + a.map(F::from_canonical_u8), tester.read::(1, rd) ) } @@ -112,9 +114,9 @@ type Rv32MultiplicationTestChip = VmChipWrapper< #[allow(clippy::too_many_arguments)] fn run_rv32_mul_negative_test( - a: [u32; RV32_REGISTER_NUM_LIMBS], - b: [u32; RV32_REGISTER_NUM_LIMBS], - c: [u32; RV32_REGISTER_NUM_LIMBS], + a: [u8; RV32_REGISTER_NUM_LIMBS], + b: [u8; RV32_REGISTER_NUM_LIMBS], + c: [u8; RV32_REGISTER_NUM_LIMBS], is_valid: bool, interaction_error: bool, ) { @@ -128,7 +130,7 @@ fn run_rv32_mul_negative_test( let mut tester = VmChipTestBuilder::default(); let mut chip = Rv32MultiplicationTestChip::::new( TestAdapterChip::new( - vec![[b.map(F::from_canonical_u32), c.map(F::from_canonical_u32)].concat()], + vec![[b.map(F::from_canonical_u8), c.map(F::from_canonical_u8)].concat()], vec![None], ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), ), @@ -148,7 +150,7 @@ fn run_rv32_mul_negative_test( range_tuple_chip.clear(); if is_valid { for (a, carry) in a.iter().zip(carry.iter()) { - range_tuple_chip.add_count(&[*a, *carry]); + range_tuple_chip.add_count(&[*a as u32, *carry]); } } @@ -156,7 +158,7 @@ fn run_rv32_mul_negative_test( let mut values = trace.row_slice(0).to_vec(); let cols: &mut MultiplicationCoreCols = values.split_at_mut(adapter_width).1.borrow_mut(); - cols.a = a.map(F::from_canonical_u32); + cols.a = a.map(F::from_canonical_u8); cols.is_valid = F::from_bool(is_valid); *trace = RowMajorMatrix::new(values, trace_width); }; @@ -204,9 +206,9 @@ fn rv32_mul_is_valid_false_negative_test() { #[test] fn run_mul_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [197, 85, 150, 32]; - let y: [u32; RV32_REGISTER_NUM_LIMBS] = [51, 109, 78, 142]; - let z: [u32; RV32_REGISTER_NUM_LIMBS] = [63, 247, 125, 232]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [197, 85, 150, 32]; + let y: [u8; RV32_REGISTER_NUM_LIMBS] = [51, 109, 78, 142]; + let z: [u8; RV32_REGISTER_NUM_LIMBS] = [63, 247, 125, 232]; let c: [u32; RV32_REGISTER_NUM_LIMBS] = [39, 100, 126, 205]; let (result, carry) = run_mul::(&x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { diff --git a/extensions/rv32im/circuit/src/shift/core.rs b/extensions/rv32im/circuit/src/shift/core.rs index 32fc3c155d..30b2f5f8ef 100644 --- a/extensions/rv32im/circuit/src/shift/core.rs +++ b/extensions/rv32im/circuit/src/shift/core.rs @@ -1,17 +1,23 @@ use std::{ array, borrow::{Borrow, BorrowMut}, + marker::PhantomData, }; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, - VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + AdapterAirContext, AdapterTraceStep, InsExecutorE1, MinimalInstruction, Result, + SingleTraceStep, VmAdapterInterface, VmCoreAir, VmExecutionState, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ - bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, + bitwise_op_lookup::{ + BitwiseOperationLookupBus, BitwiseOperationLookupChip, SharedBitwiseOperationLookupChip, + }, utils::not, var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus}, }; @@ -29,8 +35,6 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, rap::BaseAirWithPublicValues, }; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_big_array::BigArray; use strum::IntoEnumIterator; #[repr(C)] @@ -245,24 +249,6 @@ where } } -#[repr(C)] -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(bound = "T: Serialize + DeserializeOwned")] -pub struct ShiftCoreRecord { - #[serde(with = "BigArray")] - pub a: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub b: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub c: [T; NUM_LIMBS], - pub b_sign: T, - #[serde(with = "BigArray")] - pub bit_shift_carry: [u32; NUM_LIMBS], - pub bit_shift: usize, - pub limb_shift: usize, - pub opcode: ShiftOpcode, -} - pub struct ShiftCoreChip { pub air: ShiftCoreAir, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, @@ -286,106 +272,136 @@ impl ShiftCoreChip, const NUM_LIMBS: usize, const LIMB_BITS: usize> - VmCoreChip for ShiftCoreChip -where - I::Reads: Into<[[F; NUM_LIMBS]; 2]>, - I::Writes: From<[[F; NUM_LIMBS]; 1]>, -{ - type Record = ShiftCoreRecord; - type Air = ShiftCoreAir; - - #[allow(clippy::type_complexity)] - fn execute_instruction( + #[inline] + pub fn execute( &self, instruction: &Instruction, - _from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { - let Instruction { opcode, .. } = instruction; - let shift_opcode = ShiftOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); + [b, c]: [[u8; NUM_LIMBS]; 2], + core_row: &mut [F], + ) -> [u8; NUM_LIMBS] { + let opcode = instruction.opcode; + let local_opcode = ShiftOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); + + let (a, limb_shift, bit_shift) = run_shift::(local_opcode, &b, &c); + + let core_row: &mut ShiftCoreCols = core_row.borrow_mut(); + core_row.a = a.map(F::from_canonical_u8); + core_row.b = b.map(F::from_canonical_u8); + core_row.c = c.map(F::from_canonical_u8); + // To be transformed later in fill_trace_row: + core_row.opcode_sll_flag = F::from_canonical_usize(local_opcode as usize); + core_row.bit_shift_marker[0] = F::from_canonical_usize(bit_shift); + core_row.limb_shift_marker[0] = F::from_canonical_usize(limb_shift); + + a + } - let data: [[F; NUM_LIMBS]; 2] = reads.into(); - let b = data[0].map(|x| x.as_canonical_u32()); - let c = data[1].map(|y| y.as_canonical_u32()); - let (a, limb_shift, bit_shift) = run_shift::(shift_opcode, &b, &c); + pub fn fill_trace_row(&self, core_row: &mut [F]) { + let core_row: &mut ShiftCoreCols = core_row.borrow_mut(); + let local_opcode = + ShiftOpcode::from_usize(core_row.opcode_sll_flag.as_canonical_u32() as usize); + let bit_shift = core_row.bit_shift_marker[0].as_canonical_u32() as usize; + let limb_shift = core_row.limb_shift_marker[0].as_canonical_u32() as usize; + let b = core_row.b.map(|x| x.as_canonical_u32()); + let c = core_row.c.map(|x| x.as_canonical_u32()); - let bit_shift_carry = array::from_fn(|i| match shift_opcode { + let bit_shift_carry = array::from_fn(|i| match local_opcode { ShiftOpcode::SLL => b[i] >> (LIMB_BITS - bit_shift), _ => b[i] % (1 << bit_shift), }); + for carry_val in bit_shift_carry { + self.range_checker_chip.add_count(carry_val, bit_shift); + } + + let num_bits_log = (NUM_LIMBS * LIMB_BITS).ilog2(); + self.range_checker_chip.add_count( + (((c[0] as usize) - bit_shift - limb_shift * LIMB_BITS) >> num_bits_log) as u32, + LIMB_BITS - num_bits_log as usize, + ); + let mut b_sign = 0; - if shift_opcode == ShiftOpcode::SRA { + if local_opcode == ShiftOpcode::SRA { b_sign = b[NUM_LIMBS - 1] >> (LIMB_BITS - 1); self.bitwise_lookup_chip .request_xor(b[NUM_LIMBS - 1], 1 << (LIMB_BITS - 1)); } - for i in 0..(NUM_LIMBS / 2) { + for pair in core_row.a.chunks_exact(2) { self.bitwise_lookup_chip - .request_range(a[i * 2], a[i * 2 + 1]); + .request_range(pair[0].as_canonical_u32(), pair[1].as_canonical_u32()); } - let output = AdapterRuntimeContext::without_pc([a.map(F::from_canonical_u32)]); - let record = ShiftCoreRecord { - opcode: shift_opcode, - a: a.map(F::from_canonical_u32), - b: data[0], - c: data[1], - bit_shift_carry, - bit_shift, - limb_shift, - b_sign: F::from_canonical_u32(b_sign), + core_row.bit_multiplier_left = match local_opcode { + ShiftOpcode::SLL => F::from_canonical_usize(1 << bit_shift), + _ => F::ZERO, }; - - Ok((output, record)) + core_row.bit_multiplier_right = match local_opcode { + ShiftOpcode::SLL => F::ZERO, + _ => F::from_canonical_usize(1 << bit_shift), + }; + core_row.b_sign = F::from_canonical_u32(b_sign); + core_row.bit_shift_marker = array::from_fn(|i| F::from_bool(i == bit_shift)); + core_row.limb_shift_marker = array::from_fn(|i| F::from_bool(i == limb_shift)); + core_row.bit_shift_carry = bit_shift_carry.map(F::from_canonical_u32); + core_row.opcode_sll_flag = F::from_bool(local_opcode == ShiftOpcode::SLL); + core_row.opcode_srl_flag = F::from_bool(local_opcode == ShiftOpcode::SRL); + core_row.opcode_sra_flag = F::from_bool(local_opcode == ShiftOpcode::SRA); } +} - fn get_opcode_name(&self, opcode: usize) -> String { - format!("{:?}", ShiftOpcode::from_usize(opcode - self.air.offset)) - } +#[derive(derive_new::new)] +pub struct ShiftStep { + pub core: ShiftCoreChip, + phantom: PhantomData, +} - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - for carry_val in record.bit_shift_carry { - self.range_checker_chip - .add_count(carry_val, record.bit_shift); - } +impl SingleTraceStep + for ShiftStep +where + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = [[u8; NUM_LIMBS]; 2], + WriteData = [u8; NUM_LIMBS], + TraceContext<'a> = &'a BitwiseOperationLookupChip, + >, +{ + fn execute( + &mut self, + state: VmStateMut, + instruction: &Instruction, + row_slice: &mut [F], + ) -> Result<()> { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - let num_bits_log = (NUM_LIMBS * LIMB_BITS).ilog2(); - self.range_checker_chip.add_count( - (((record.c[0].as_canonical_u32() as usize) - - record.bit_shift - - record.limb_shift * LIMB_BITS) - >> num_bits_log) as u32, - LIMB_BITS - num_bits_log as usize, - ); + A::start(*state.pc, state.memory, adapter_row); + let [rs1, rs2] = A::read(state.memory, instruction, adapter_row); + let output = self.core.execute(instruction, [rs1, rs2], core_row); + A::write(state.memory, instruction, adapter_row, &output); - let row_slice: &mut ShiftCoreCols<_, NUM_LIMBS, LIMB_BITS> = row_slice.borrow_mut(); - row_slice.a = record.a; - row_slice.b = record.b; - row_slice.c = record.c; - row_slice.bit_multiplier_left = match record.opcode { - ShiftOpcode::SLL => F::from_canonical_usize(1 << record.bit_shift), - _ => F::ZERO, - }; - row_slice.bit_multiplier_right = match record.opcode { - ShiftOpcode::SLL => F::ZERO, - _ => F::from_canonical_usize(1 << record.bit_shift), - }; - row_slice.b_sign = record.b_sign; - row_slice.bit_shift_marker = array::from_fn(|i| F::from_bool(i == record.bit_shift)); - row_slice.limb_shift_marker = array::from_fn(|i| F::from_bool(i == record.limb_shift)); - row_slice.bit_shift_carry = record.bit_shift_carry.map(F::from_canonical_u32); - row_slice.opcode_sll_flag = F::from_bool(record.opcode == ShiftOpcode::SLL); - row_slice.opcode_srl_flag = F::from_bool(record.opcode == ShiftOpcode::SRL); - row_slice.opcode_sra_flag = F::from_bool(record.opcode == ShiftOpcode::SRA); + *state.pc += DEFAULT_PC_STEP; + Ok(()) } - fn air(&self) -> &Self::Air { - &self.air + fn get_opcode_name(&self, opcode: usize) -> String { + format!( + "{:?}", + ShiftOpcode::from_usize(opcode - self.core.air.offset) + ) + } + + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + A::fill_trace_row( + mem_helper, + self.core.bitwise_lookup_chip.as_ref(), + adapter_row, + ); + self.core.fill_trace_row(core_row); } } @@ -426,14 +442,9 @@ where rs2_bytes }; - // TODO(ayush): avoid this conversion - let rs1_bytes: [u32; NUM_LIMBS] = rs1_bytes.map(|x| x as u32); - let rs2_bytes: [u32; NUM_LIMBS] = rs2_bytes.map(|y| y as u32); - // Execute the shift operation let (rd_bytes, _, _) = run_shift::(shift_opcode, &rs1_bytes, &rs2_bytes); - let rd_bytes = rd_bytes.map(|x| x as u8); let rd_addr = a.as_canonical_u32(); unsafe { @@ -446,11 +457,12 @@ where } } +// Returns (result, limb_shift, bit_shift) pub(super) fn run_shift( opcode: ShiftOpcode, - x: &[u32; NUM_LIMBS], - y: &[u32; NUM_LIMBS], -) -> ([u32; NUM_LIMBS], usize, usize) { + x: &[u8; NUM_LIMBS], + y: &[u8; NUM_LIMBS], +) -> ([u8; NUM_LIMBS], usize, usize) { match opcode { ShiftOpcode::SLL => run_shift_left::(x, y), ShiftOpcode::SRL => run_shift_right::(x, y, true), @@ -459,52 +471,56 @@ pub(super) fn run_shift( } fn run_shift_left( - x: &[u32; NUM_LIMBS], - y: &[u32; NUM_LIMBS], -) -> ([u32; NUM_LIMBS], usize, usize) { - let mut result = [0u32; NUM_LIMBS]; + x: &[u8; NUM_LIMBS], + y: &[u8; NUM_LIMBS], +) -> ([u8; NUM_LIMBS], usize, usize) { + let mut result = [0u8; NUM_LIMBS]; let (limb_shift, bit_shift) = get_shift::(y); for i in limb_shift..NUM_LIMBS { result[i] = if i > limb_shift { - ((x[i - limb_shift] << bit_shift) + (x[i - limb_shift - 1] >> (LIMB_BITS - bit_shift))) - % (1 << LIMB_BITS) + (((x[i - limb_shift] as u16) << bit_shift) + | ((x[i - limb_shift - 1] as u16) >> (LIMB_BITS - bit_shift))) + % (1u16 << LIMB_BITS) } else { - (x[i - limb_shift] << bit_shift) % (1 << LIMB_BITS) - }; + ((x[i - limb_shift] as u16) << bit_shift) % (1u16 << LIMB_BITS) + } as u8; } (result, limb_shift, bit_shift) } fn run_shift_right( - x: &[u32; NUM_LIMBS], - y: &[u32; NUM_LIMBS], + x: &[u8; NUM_LIMBS], + y: &[u8; NUM_LIMBS], logical: bool, -) -> ([u32; NUM_LIMBS], usize, usize) { +) -> ([u8; NUM_LIMBS], usize, usize) { let fill = if logical { 0 } else { - ((1 << LIMB_BITS) - 1) * (x[NUM_LIMBS - 1] >> (LIMB_BITS - 1)) + (((1u16 << LIMB_BITS) - 1) as u8) * (x[NUM_LIMBS - 1] >> (LIMB_BITS - 1)) }; let mut result = [fill; NUM_LIMBS]; let (limb_shift, bit_shift) = get_shift::(y); for i in 0..(NUM_LIMBS - limb_shift) { - result[i] = if i + limb_shift + 1 < NUM_LIMBS { - ((x[i + limb_shift] >> bit_shift) + (x[i + limb_shift + 1] << (LIMB_BITS - bit_shift))) - % (1 << LIMB_BITS) + let res = if i + limb_shift + 1 < NUM_LIMBS { + (((x[i + limb_shift] >> bit_shift) as u16) + | ((x[i + limb_shift + 1] as u16) << (LIMB_BITS - bit_shift))) + % (1u16 << LIMB_BITS) } else { - ((x[i + limb_shift] >> bit_shift) + (fill << (LIMB_BITS - bit_shift))) - % (1 << LIMB_BITS) - } + (((x[i + limb_shift] >> bit_shift) as u16) | ((fill as u16) << (LIMB_BITS - bit_shift))) + % (1u16 << LIMB_BITS) + }; + result[i] = res as u8; } (result, limb_shift, bit_shift) } -fn get_shift(y: &[u32]) -> (usize, usize) { - // We assume `NUM_LIMBS * LIMB_BITS <= 2^LIMB_BITS` so so the shift is defined +fn get_shift(y: &[u8]) -> (usize, usize) { + debug_assert!(NUM_LIMBS * LIMB_BITS <= (1 << LIMB_BITS)); + // We assume `NUM_LIMBS * LIMB_BITS <= 2^LIMB_BITS` so the shift is defined // entirely in y[0]. let shift = (y[0] as usize) % (NUM_LIMBS * LIMB_BITS); (shift / LIMB_BITS, shift % LIMB_BITS) diff --git a/extensions/rv32im/circuit/src/shift/mod.rs b/extensions/rv32im/circuit/src/shift/mod.rs index 58d5ad022b..e6fa09d53c 100644 --- a/extensions/rv32im/circuit/src/shift/mod.rs +++ b/extensions/rv32im/circuit/src/shift/mod.rs @@ -1,6 +1,8 @@ -use openvm_circuit::arch::VmChipWrapper; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; -use super::adapters::{Rv32BaseAluAdapterChip, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; +use super::adapters::{ + Rv32BaseAluAdapterAir, Rv32BaseAluAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, +}; mod core; pub use core::*; @@ -8,8 +10,8 @@ pub use core::*; #[cfg(test)] mod tests; -pub type Rv32ShiftChip = VmChipWrapper< - F, - Rv32BaseAluAdapterChip, - ShiftCoreChip, ->; +pub type Rv32ShiftAir = + VmAirWrapper>; +pub type Rv32ShiftStep = + ShiftStep, RV32_REGISTER_NUM_LIMBS, RV32_CELL_BITS>; +pub type Rv32ShiftChip = NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/shift/tests.rs b/extensions/rv32im/circuit/src/shift/tests.rs index 449c505196..fc08ac829a 100644 --- a/extensions/rv32im/circuit/src/shift/tests.rs +++ b/extensions/rv32im/circuit/src/shift/tests.rs @@ -3,7 +3,7 @@ use std::{array, borrow::BorrowMut}; use openvm_circuit::{ arch::{ testing::{TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - ExecutionBridge, VmAdapterChip, VmChipWrapper, + NewVmChipWrapper, VmAirWrapper, VmChipWrapper, }, utils::generate_long_number, }; @@ -26,14 +26,39 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::Rng; -use super::{core::run_shift, Rv32ShiftChip, ShiftCoreChip}; +use super::{core::run_shift, Rv32ShiftChip, Rv32ShiftStep, ShiftCoreChip, ShiftCoreCols}; use crate::{ - adapters::{Rv32BaseAluAdapterChip, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, - shift::ShiftCoreCols, + adapters::{Rv32BaseAluAdapterAir, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, test_utils::{generate_rv32_is_type_immediate, rv32_rand_write_register_or_imm}, }; type F = BabyBear; +const MAX_INS_CAPACITY: usize = 128; + +fn create_test_chip( + tester: &VmChipTestBuilder, +) -> ( + Rv32ShiftChip, + SharedBitwiseOperationLookupChip, +) { + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + let step = Rv32ShiftStep::new(ShiftCoreChip::new( + bitwise_chip.clone(), + tester.range_checker(), + ShiftOpcode::CLASS_OFFSET, + )); + let air = VmAirWrapper::new( + Rv32BaseAluAdapterAir::new( + tester.execution_bridge(), + tester.memory_bridge(), + bitwise_bus, + ), + step.core.air, + ); + let chip = NewVmChipWrapper::new(air, step, MAX_INS_CAPACITY, tester.memory_helper()); + (chip, bitwise_chip) +} ////////////////////////////////////////////////////////////////////////////////////// // POSITIVE TESTS @@ -44,31 +69,18 @@ type F = BabyBear; fn run_rv32_shift_rand_test(opcode: ShiftOpcode, num_ops: usize) { let mut rng = create_seeded_rng(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut tester = VmChipTestBuilder::default(); - let mut chip = Rv32ShiftChip::::new( - Rv32BaseAluAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - bitwise_chip.clone(), - ), - ShiftCoreChip::new( - bitwise_chip.clone(), - tester.memory_controller().range_checker.clone(), - ShiftOpcode::CLASS_OFFSET, - ), - tester.offline_memory_mutex_arc(), - ); + let (mut chip, bitwise_chip) = create_test_chip(&tester); for _ in 0..num_ops { - let b = generate_long_number::(&mut rng); + let b = generate_long_number::(&mut rng) + .map(|x| x as u8); let (c_imm, c) = if rng.gen_bool(0.5) { ( None, - generate_long_number::(&mut rng), + generate_long_number::(&mut rng) + .map(|x| x as u8), ) } else { let (imm, c) = generate_rv32_is_type_immediate(&mut rng); @@ -87,7 +99,7 @@ fn run_rv32_shift_rand_test(opcode: ShiftOpcode, num_ops: usize) { let (a, _, _) = run_shift::(opcode, &b, &c); assert_eq!( - a.map(F::from_canonical_u32), + a.map(F::from_canonical_u8), tester.read::(1, rd) ) } @@ -142,23 +154,9 @@ fn run_rv32_shift_negative_test( prank_vals: ShiftPrankValues, interaction_error: bool, ) { - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().range_checker.clone(); - let mut chip = Rv32ShiftTestChip::::new( - TestAdapterChip::new( - vec![[b.map(F::from_canonical_u32), c.map(F::from_canonical_u32)].concat()], - vec![None], - ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), - ), - ShiftCoreChip::new( - bitwise_chip.clone(), - range_checker_chip.clone(), - ShiftOpcode::CLASS_OFFSET, - ), - tester.offline_memory_mutex_arc(), - ); + let (mut chip, bitwise_chip) = create_test_chip(&tester); + let range_checker_chip = tester.range_checker(); tester.execute( &mut chip, @@ -183,7 +181,7 @@ fn run_rv32_shift_negative_test( } let trace_width = chip.trace_width(); - let adapter_width = BaseAir::::width(chip.adapter.air()); + let adapter_width = BaseAir::::width(&chip.air.adapter); let modify_trace = |trace: &mut DenseMatrix| { let mut values = trace.row_slice(0).to_vec(); @@ -213,7 +211,6 @@ fn run_rv32_shift_negative_test( *trace = RowMajorMatrix::new(values, trace_width); }; - drop(range_checker_chip); disable_debug_builder(); let tester = tester .build() @@ -375,9 +372,9 @@ fn rv32_sra_wrong_sign_negative_test() { #[test] fn run_sll_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [45, 7, 61, 186]; - let y: [u32; RV32_REGISTER_NUM_LIMBS] = [91, 0, 100, 0]; - let z: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 104]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [45, 7, 61, 186]; + let y: [u8; RV32_REGISTER_NUM_LIMBS] = [91, 0, 100, 0]; + let z: [u8; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 104]; let (result, limb_shift, bit_shift) = run_shift::(ShiftOpcode::SLL, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { @@ -390,9 +387,9 @@ fn run_sll_sanity_test() { #[test] fn run_srl_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [31, 190, 221, 200]; - let y: [u32; RV32_REGISTER_NUM_LIMBS] = [49, 190, 190, 190]; - let z: [u32; RV32_REGISTER_NUM_LIMBS] = [110, 100, 0, 0]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [31, 190, 221, 200]; + let y: [u8; RV32_REGISTER_NUM_LIMBS] = [49, 190, 190, 190]; + let z: [u8; RV32_REGISTER_NUM_LIMBS] = [110, 100, 0, 0]; let (result, limb_shift, bit_shift) = run_shift::(ShiftOpcode::SRL, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { @@ -405,9 +402,9 @@ fn run_srl_sanity_test() { #[test] fn run_sra_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [31, 190, 221, 200]; - let y: [u32; RV32_REGISTER_NUM_LIMBS] = [113, 20, 50, 80]; - let z: [u32; RV32_REGISTER_NUM_LIMBS] = [110, 228, 255, 255]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [31, 190, 221, 200]; + let y: [u8; RV32_REGISTER_NUM_LIMBS] = [113, 20, 50, 80]; + let z: [u8; RV32_REGISTER_NUM_LIMBS] = [110, 228, 255, 255]; let (result, limb_shift, bit_shift) = run_shift::(ShiftOpcode::SRA, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { diff --git a/extensions/rv32im/circuit/src/test_utils.rs b/extensions/rv32im/circuit/src/test_utils.rs index 8a105ff990..62fd55cf59 100644 --- a/extensions/rv32im/circuit/src/test_utils.rs +++ b/extensions/rv32im/circuit/src/test_utils.rs @@ -10,8 +10,8 @@ use super::adapters::{RV32_REGISTER_NUM_LIMBS, RV_IS_TYPE_IMM_BITS}; #[cfg_attr(all(feature = "test-utils", not(test)), allow(dead_code))] pub fn rv32_rand_write_register_or_imm( tester: &mut VmChipTestBuilder, - rs1_writes: [u32; NUM_LIMBS], - rs2_writes: [u32; NUM_LIMBS], + rs1_writes: [u8; NUM_LIMBS], + rs2_writes: [u8; NUM_LIMBS], imm: Option, opcode_with_offset: usize, rng: &mut StdRng, @@ -22,9 +22,9 @@ pub fn rv32_rand_write_register_or_imm( let rs2 = imm.unwrap_or_else(|| gen_pointer(rng, NUM_LIMBS)); let rd = gen_pointer(rng, NUM_LIMBS); - tester.write::(1, rs1, rs1_writes.map(BabyBear::from_canonical_u32)); + tester.write::(1, rs1, rs1_writes.map(BabyBear::from_canonical_u8)); if !rs2_is_imm { - tester.write::(1, rs2, rs2_writes.map(BabyBear::from_canonical_u32)); + tester.write::(1, rs2, rs2_writes.map(BabyBear::from_canonical_u8)); } ( @@ -37,9 +37,7 @@ pub fn rv32_rand_write_register_or_imm( } #[cfg_attr(all(feature = "test-utils", not(test)), allow(dead_code))] -pub fn generate_rv32_is_type_immediate( - rng: &mut StdRng, -) -> (usize, [u32; RV32_REGISTER_NUM_LIMBS]) { +pub fn generate_rv32_is_type_immediate(rng: &mut StdRng) -> (usize, [u8; RV32_REGISTER_NUM_LIMBS]) { let mut imm: u32 = rng.gen_range(0..(1 << RV_IS_TYPE_IMM_BITS)); if (imm & 0x800) != 0 { imm |= !0xFFF @@ -51,7 +49,6 @@ pub fn generate_rv32_is_type_immediate( (imm >> 8) as u8, (imm >> 16) as u8, (imm >> 16) as u8, - ] - .map(|x| x as u32), + ], ) } From bcde3f850711c54e977c96b7708d461a6cbb17a2 Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Tue, 29 Apr 2025 03:05:55 -0400 Subject: [PATCH 07/49] feat(new-execution): rv32im tracegen and e1 execution (#1607) --- .gitignore | 3 + benchmarks/execute/Cargo.toml | 2 +- benchmarks/execute/src/main.rs | 49 +- crates/circuits/mod-builder/src/core_chip.rs | 23 +- crates/vm/derive/src/lib.rs | 100 ++- crates/vm/src/arch/execution.rs | 31 +- crates/vm/src/arch/execution_control.rs | 145 +++- crates/vm/src/arch/extensions.rs | 51 +- crates/vm/src/arch/integration_api.rs | 71 +- crates/vm/src/arch/mod.rs | 2 +- crates/vm/src/arch/segment.rs | 143 ++-- crates/vm/src/arch/vm.rs | 104 ++- .../system/memory/offline_checker/columns.rs | 6 + crates/vm/src/system/memory/online.rs | 54 +- crates/vm/src/system/memory/paged_vec.rs | 25 +- crates/vm/src/system/native_adapter/mod.rs | 303 ++++--- crates/vm/src/system/phantom/mod.rs | 61 +- crates/vm/src/system/public_values/core.rs | 247 ++++-- crates/vm/src/system/public_values/mod.rs | 12 +- .../src/adapters/alu_native_adapter.rs | 267 ++++--- .../src/adapters/branch_native_adapter.rs | 232 ++++-- .../circuit/src/adapters/convert_adapter.rs | 240 ++++-- .../src/adapters/loadstore_native_adapter.rs | 351 ++++++--- extensions/native/circuit/src/adapters/mod.rs | 104 +++ .../src/adapters/native_vectorized_adapter.rs | 265 ++++--- .../native/circuit/src/branch_eq/mod.rs | 5 +- extensions/native/circuit/src/castf/core.rs | 199 +++-- extensions/native/circuit/src/castf/mod.rs | 7 +- .../circuit/src/field_arithmetic/core.rs | 222 ++++-- .../circuit/src/field_arithmetic/mod.rs | 7 +- .../circuit/src/field_extension/core.rs | 224 ++++-- .../native/circuit/src/field_extension/mod.rs | 5 +- .../native/circuit/src/loadstore/core.rs | 231 ++++-- .../native/circuit/src/loadstore/mod.rs | 13 +- extensions/rv32im/circuit/src/adapters/alu.rs | 129 ++- .../rv32im/circuit/src/adapters/branch.rs | 206 ++--- .../rv32im/circuit/src/adapters/jalr.rs | 225 +++--- .../rv32im/circuit/src/adapters/loadstore.rs | 466 +++++++---- extensions/rv32im/circuit/src/adapters/mod.rs | 135 ++-- extensions/rv32im/circuit/src/adapters/mul.rs | 234 +++--- .../rv32im/circuit/src/adapters/rdwrite.rs | 244 +++++- extensions/rv32im/circuit/src/auipc/core.rs | 136 ++-- extensions/rv32im/circuit/src/auipc/mod.rs | 5 +- extensions/rv32im/circuit/src/auipc/tests.rs | 29 +- .../rv32im/circuit/src/base_alu/core.rs | 199 ++--- .../rv32im/circuit/src/base_alu/tests.rs | 634 ++++++++------- .../rv32im/circuit/src/branch_eq/core.rs | 196 +++-- .../rv32im/circuit/src/branch_eq/mod.rs | 10 +- .../rv32im/circuit/src/branch_eq/tests.rs | 379 ++++----- .../rv32im/circuit/src/branch_lt/core.rs | 212 +++-- .../rv32im/circuit/src/branch_lt/mod.rs | 15 +- .../rv32im/circuit/src/branch_lt/tests.rs | 745 +++++++++--------- extensions/rv32im/circuit/src/divrem/core.rs | 283 ++++--- extensions/rv32im/circuit/src/divrem/mod.rs | 15 +- extensions/rv32im/circuit/src/divrem/tests.rs | 684 ++++++++-------- extensions/rv32im/circuit/src/extension.rs | 334 +++++--- .../rv32im/circuit/src/hintstore/mod.rs | 73 +- extensions/rv32im/circuit/src/jal_lui/core.rs | 307 ++++---- extensions/rv32im/circuit/src/jal_lui/mod.rs | 5 +- .../rv32im/circuit/src/jal_lui/tests.rs | 35 +- extensions/rv32im/circuit/src/jalr/core.rs | 229 +++--- extensions/rv32im/circuit/src/jalr/mod.rs | 8 +- extensions/rv32im/circuit/src/jalr/tests.rs | 383 ++++----- .../rv32im/circuit/src/less_than/core.rs | 197 ++--- .../rv32im/circuit/src/less_than/tests.rs | 590 +++++++------- .../circuit/src/load_sign_extend/core.rs | 306 ++++--- .../circuit/src/load_sign_extend/mod.rs | 15 +- .../circuit/src/load_sign_extend/tests.rs | 97 ++- .../rv32im/circuit/src/loadstore/core.rs | 317 ++++---- .../rv32im/circuit/src/loadstore/mod.rs | 12 +- .../rv32im/circuit/src/loadstore/tests.rs | 136 ++-- extensions/rv32im/circuit/src/mul/core.rs | 183 +++-- extensions/rv32im/circuit/src/mul/mod.rs | 17 +- extensions/rv32im/circuit/src/mul/tests.rs | 208 ++--- extensions/rv32im/circuit/src/mulh/core.rs | 209 +++-- extensions/rv32im/circuit/src/mulh/mod.rs | 12 +- extensions/rv32im/circuit/src/mulh/tests.rs | 482 +++++------ extensions/rv32im/circuit/src/shift/core.rs | 199 ++--- extensions/rv32im/circuit/src/shift/tests.rs | 506 ++++++------ 79 files changed, 8150 insertions(+), 5695 deletions(-) diff --git a/.gitignore b/.gitignore index d794a5dc57..15fa79c61f 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,6 @@ guest.syms # openvm generated files crates/cli/openvm/ + +# samply profile +profile.json.gz diff --git a/benchmarks/execute/Cargo.toml b/benchmarks/execute/Cargo.toml index 319490220a..67ce6ac821 100644 --- a/benchmarks/execute/Cargo.toml +++ b/benchmarks/execute/Cargo.toml @@ -31,11 +31,11 @@ criterion = { version = "0.5", features = ["html_reports"] } [features] default = ["jemalloc"] -profiling = ["openvm-sdk/profiling"] mimalloc = ["openvm-circuit/mimalloc"] jemalloc = ["openvm-circuit/jemalloc"] jemalloc-prof = ["openvm-circuit/jemalloc-prof"] nightly-features = ["openvm-circuit/nightly-features"] +profiling = ["openvm-circuit/function-span", "openvm-transpiler/function-span"] [[bench]] name = "fibonacci_execute" diff --git a/benchmarks/execute/src/main.rs b/benchmarks/execute/src/main.rs index 80db3ec5a4..aa927b45f8 100644 --- a/benchmarks/execute/src/main.rs +++ b/benchmarks/execute/src/main.rs @@ -1,11 +1,13 @@ -use cargo_openvm::{default::DEFAULT_APP_CONFIG_PATH, util::read_config_toml_or_default}; use clap::{Parser, ValueEnum}; use eyre::Result; use openvm_benchmarks_utils::{get_elf_path, get_programs_dir, read_elf_file}; use openvm_circuit::arch::{instructions::exe::VmExe, VmExecutor}; -use openvm_sdk::StdIn; -use openvm_stark_sdk::bench::run_with_metric_collection; -use openvm_transpiler::FromElf; +use openvm_rv32im_circuit::Rv32ImConfig; +use openvm_rv32im_transpiler::{ + Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, +}; +use openvm_stark_sdk::{bench::run_with_metric_collection, p3_baby_bear::BabyBear}; +use openvm_transpiler::{transpiler::Transpiler, FromElf}; #[derive(Debug, Clone, ValueEnum)] enum BuildProfile { @@ -13,18 +15,20 @@ enum BuildProfile { Release, } +// const DEFAULT_APP_CONFIG_PATH: &str = "./openvm.toml"; + static AVAILABLE_PROGRAMS: &[&str] = &[ - "fibonacci_recursive", - "fibonacci_iterative", + // "fibonacci_recursive", + // "fibonacci_iterative", "quicksort", - "bubblesort", - "pairing", - "keccak256", - "keccak256_iter", - "sha256", - "sha256_iter", - "revm_transfer", - "revm_snailtracer", + // "bubblesort", + // "pairing", + // "keccak256", + // "keccak256_iter", + // "sha256", + // "sha256_iter", + // "revm_transfer", + // "revm_snailtracer", ]; #[derive(Parser)] @@ -106,13 +110,22 @@ fn main() -> Result<()> { let elf_path = get_elf_path(&program_dir); let elf = read_elf_file(&elf_path)?; - let config_path = program_dir.join(DEFAULT_APP_CONFIG_PATH); - let vm_config = read_config_toml_or_default(&config_path)?.app_vm_config; + // let config_path = program_dir.join(DEFAULT_APP_CONFIG_PATH); + // let vm_config = read_config_toml_or_default(&config_path)?.app_vm_config; + // let transpiler = vm_config.transpiler; + let vm_config = Rv32ImConfig::default(); + + let transpiler = Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(Rv32MTranspilerExtension); - let exe = VmExe::from_elf(elf, vm_config.transpiler())?; + let exe = VmExe::from_elf(elf, transpiler)?; let executor = VmExecutor::new(vm_config); - executor.execute(exe, StdIn::default())?; + executor + .execute_e1(exe, vec![]) + .expect("Failed to execute program"); tracing::info!("Completed program: {}", program); } tracing::info!("All programs executed successfully"); diff --git a/crates/circuits/mod-builder/src/core_chip.rs b/crates/circuits/mod-builder/src/core_chip.rs index f7b026da72..30e9c65dbb 100644 --- a/crates/circuits/mod-builder/src/core_chip.rs +++ b/crates/circuits/mod-builder/src/core_chip.rs @@ -1,12 +1,9 @@ use itertools::Itertools; use num_bigint::BigUint; use num_traits::Zero; -use openvm_circuit::{ - arch::{ - AdapterAirContext, AdapterRuntimeContext, DynAdapterInterface, DynArray, InsExecutorE1, - MinimalInstruction, Result, VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, - }, - system::memory::online::GuestMemory, +use openvm_circuit::arch::{ + AdapterAirContext, AdapterRuntimeContext, DynAdapterInterface, DynArray, MinimalInstruction, + Result, VmAdapterInterface, VmCoreAir, VmCoreChip, }; use openvm_circuit_primitives::{ var_range::SharedVariableRangeCheckerChip, SubAir, TraceSubRowGenerator, @@ -314,20 +311,6 @@ where } } -impl InsExecutorE1 for FieldExpressionCoreChip -where - Mem: GuestMemory, - F: PrimeField32, -{ - fn execute_e1( - &mut self, - _state: &mut VmExecutionState, - _instruction: &Instruction, - ) -> Result<()> { - todo!("Implement execute_e1") - } -} - impl FieldExpressionCoreChip { // We will be setting is_valid = 0. That forces all flags be 0 (otherwise setup will be -1). // We generate a dummy row with all flags set to 0, then we set is_valid = 0. diff --git a/crates/vm/derive/src/lib.rs b/crates/vm/derive/src/lib.rs index 37dca6e4ed..156513a8f2 100644 --- a/crates/vm/derive/src/lib.rs +++ b/crates/vm/derive/src/lib.rs @@ -113,6 +113,104 @@ pub fn instruction_executor_derive(input: TokenStream) -> TokenStream { } } +#[proc_macro_derive(InsExecutorE1)] +pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { + let ast: syn::DeriveInput = syn::parse(input).unwrap(); + + let name = &ast.ident; + let generics = &ast.generics; + let (impl_generics, ty_generics, _) = generics.split_for_impl(); + + match &ast.data { + Data::Struct(inner) => { + // Check if the struct has only one unnamed field + let inner_ty = match &inner.fields { + Fields::Unnamed(fields) => { + if fields.unnamed.len() != 1 { + panic!("Only one unnamed field is supported"); + } + fields.unnamed.first().unwrap().ty.clone() + } + _ => panic!("Only unnamed fields are supported"), + }; + // Use full path ::openvm_circuit... so it can be used either within or outside the vm + // crate. Assume F is already generic of the field. + let mut new_generics = generics.clone(); + let where_clause = new_generics.make_where_clause(); + where_clause + .predicates + .push(syn::parse_quote! { #inner_ty: ::openvm_circuit::arch::InsExecutorE1 }); + quote! { + impl #impl_generics ::openvm_circuit::arch::InsExecutorE1 for #name #ty_generics #where_clause { + fn execute_e1( + &mut self, + state: ::openvm_circuit::arch::execution::VmStateMut, + instruction: &::openvm_circuit::arch::instructions::instruction::Instruction, + ) -> ::openvm_circuit::arch::Result<()> + where + Mem: ::openvm_circuit::system::memory::online::GuestMemory, + F: ::openvm_stark_backend::p3_field::PrimeField32 + { + self.0.execute_e1(state, instruction) + } + } + } + .into() + } + Data::Enum(e) => { + let variants = e + .variants + .iter() + .map(|variant| { + let variant_name = &variant.ident; + + let mut fields = variant.fields.iter(); + let field = fields.next().unwrap(); + assert!(fields.next().is_none(), "Only one field is supported"); + (variant_name, field) + }) + .collect::>(); + let first_ty_generic = ast + .generics + .params + .first() + .and_then(|param| match param { + GenericParam::Type(type_param) => Some(&type_param.ident), + _ => None, + }) + .expect("First generic must be type for Field"); + // Use full path ::openvm_circuit... so it can be used either within or outside the vm + // crate. Assume F is already generic of the field. + let execute_arms = variants.iter().map(|(variant_name, field)| { + let field_ty = &field.ty; + quote! { + #name::#variant_name(x) => <#field_ty as ::openvm_circuit::arch::InsExecutorE1<#first_ty_generic>>::execute_e1(x, state, instruction) + } + }).collect::>(); + + quote! { + impl #impl_generics ::openvm_circuit::arch::InsExecutorE1<#first_ty_generic> for #name #ty_generics { + fn execute_e1( + &mut self, + state: ::openvm_circuit::arch::execution::VmStateMut, + instruction: &::openvm_circuit::arch::instructions::instruction::Instruction<#first_ty_generic>, + ) -> ::openvm_circuit::arch::Result<()> + where + Mem: ::openvm_circuit::system::memory::online::GuestMemory, + #first_ty_generic: ::openvm_stark_backend::p3_field::PrimeField32 + { + match self { + #(#execute_arms,)* + } + } + } + } + .into() + } + Data::Union(_) => unimplemented!("Unions are not supported"), + } +} + /// Derives `AnyEnum` trait on an enum type. /// By default an enum arm will just return `self` as `&dyn Any`. /// @@ -347,7 +445,7 @@ pub fn vm_generic_config_derive(input: proc_macro::TokenStream) -> proc_macro::T let periphery_type = Ident::new(&format!("{}Periphery", name), name.span()); TokenStream::from(quote! { - #[derive(::openvm_circuit::circuit_derive::ChipUsageGetter, ::openvm_circuit::circuit_derive::Chip, ::openvm_circuit::derive::InstructionExecutor, ::derive_more::derive::From, ::openvm_circuit::derive::AnyEnum)] + #[derive(::openvm_circuit::circuit_derive::ChipUsageGetter, ::openvm_circuit::circuit_derive::Chip, ::openvm_circuit::derive::InstructionExecutor, ::openvm_circuit::derive::InsExecutorE1, ::derive_more::derive::From, ::openvm_circuit::derive::AnyEnum)] pub enum #executor_type { #[any_enum] #source_name_upper(#source_executor_type), diff --git a/crates/vm/src/arch/execution.rs b/crates/vm/src/arch/execution.rs index 46491fd350..5616495d46 100644 --- a/crates/vm/src/arch/execution.rs +++ b/crates/vm/src/arch/execution.rs @@ -11,7 +11,7 @@ use openvm_stark_backend::{ use serde::{Deserialize, Serialize}; use thiserror::Error; -use super::{Streams, VmExecutionState}; +use super::Streams; use crate::system::{ memory::{ online::{GuestMemory, TracingMemory}, @@ -79,6 +79,7 @@ pub enum ExecutionError { /// Global VM state accessible during instruction execution. /// The state is generic in guest memory `MEM` and additional host state `CTX`. /// The host state is execution context specific. +#[derive(derive_new::new)] pub struct VmStateMut<'a, MEM, CTX> { pub pc: &'a mut u32, pub memory: &'a mut MEM, @@ -111,16 +112,32 @@ pub trait InstructionExecutor { } /// New trait for instruction execution -pub trait InsExecutorE1 +pub trait InsExecutorE1 { + fn execute_e1( + &mut self, + state: VmStateMut, + instruction: &Instruction, + ) -> Result<()> + where + Mem: GuestMemory, + F: PrimeField32; +} + +impl InsExecutorE1 for RefCell where - Mem: GuestMemory, - F: PrimeField32, + C: InsExecutorE1, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()>; + ) -> Result<()> + where + Mem: GuestMemory, + F: PrimeField32, + { + self.borrow_mut().execute_e1(state, instruction) + } } impl> InstructionExecutor for RefCell { diff --git a/crates/vm/src/arch/execution_control.rs b/crates/vm/src/arch/execution_control.rs index 80eb777cbb..b62eac097a 100644 --- a/crates/vm/src/arch/execution_control.rs +++ b/crates/vm/src/arch/execution_control.rs @@ -1,9 +1,11 @@ use openvm_instructions::instruction::Instruction; use openvm_stark_backend::p3_field::PrimeField32; -use super::{segment::VmExecutionState, ExecutionError, TracegenCtx, VmChipComplex, VmConfig}; +use super::{ + ExecutionError, ExecutionSegmentState, TracegenCtx, VmChipComplex, VmConfig, VmStateMut, +}; use crate::{ - arch::{ExecutionState, InstructionExecutor}, + arch::{ExecutionState, InsExecutorE1, InstructionExecutor}, system::memory::{online::GuestMemory, AddressMap, MemoryImage, PAGE_SIZE}, }; @@ -24,31 +26,37 @@ where fn new(chip_complex: &VmChipComplex) -> Self; /// Determines if execution should stop - fn should_stop( - &mut self, - state: &VmExecutionState, - chip_complex: &VmChipComplex, - ) -> bool; + fn should_stop(&mut self, chip_complex: &VmChipComplex) + -> bool; /// Called before segment execution begins fn on_segment_start( &mut self, - vm_state: &VmExecutionState, + pc: u32, chip_complex: &mut VmChipComplex, ); + // TODO(ayush): maybe combine with on_terminate /// Called after segment execution completes fn on_segment_end( &mut self, - vm_state: &VmExecutionState, + pc: u32, + chip_complex: &mut VmChipComplex, + ); + + /// Called after program termination + fn on_terminate( + &mut self, + pc: u32, chip_complex: &mut VmChipComplex, + exit_code: u32, ); /// Execute a single instruction // TODO(ayush): change instruction to Instruction / PInstruction fn execute_instruction( &mut self, - vm_state: &mut VmExecutionState, + vm_state: &mut ExecutionSegmentState, // instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> @@ -91,7 +99,6 @@ where fn should_stop( &mut self, - _state: &VmExecutionState, chip_complex: &VmChipComplex, ) -> bool { // Avoid checking segment too often. @@ -109,32 +116,45 @@ where fn on_segment_start( &mut self, - vm_state: &VmExecutionState, + pc: u32, chip_complex: &mut VmChipComplex, ) { let timestamp = chip_complex.memory_controller().timestamp(); chip_complex .connector_chip_mut() - .begin(ExecutionState::new(vm_state.pc, timestamp)); + .begin(ExecutionState::new(pc, timestamp)); } fn on_segment_end( &mut self, - vm_state: &VmExecutionState, + pc: u32, chip_complex: &mut VmChipComplex, ) { let timestamp = chip_complex.memory_controller().timestamp(); // End the current segment with connector chip chip_complex .connector_chip_mut() - .end(ExecutionState::new(vm_state.pc, timestamp), None); + .end(ExecutionState::new(pc, timestamp), None); + self.final_memory = Some(chip_complex.base.memory_controller.memory_image().clone()); + } + + fn on_terminate( + &mut self, + pc: u32, + chip_complex: &mut VmChipComplex, + exit_code: u32, + ) { + let timestamp = chip_complex.memory_controller().timestamp(); + chip_complex + .connector_chip_mut() + .end(ExecutionState::new(pc, timestamp), Some(exit_code)); self.final_memory = Some(chip_complex.base.memory_controller.memory_image().clone()); } /// Execute a single instruction fn execute_instruction( &mut self, - vm_state: &mut VmExecutionState, + state: &mut ExecutionSegmentState, // instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> @@ -142,10 +162,7 @@ where F: PrimeField32, { let timestamp = chip_complex.memory_controller().timestamp(); - let (instruction, _) = chip_complex - .base - .program_chip - .get_instruction(vm_state.pc)?; + let (instruction, _) = chip_complex.base.program_chip.get_instruction(state.pc)?; let &Instruction { opcode, .. } = instruction; @@ -154,12 +171,94 @@ where let new_state = executor.execute( memory_controller, instruction, - ExecutionState::new(vm_state.pc, timestamp), + ExecutionState::new(state.pc, timestamp), )?; - vm_state.pc = new_state.pc; + state.pc = new_state.pc; + } else { + return Err(ExecutionError::DisabledOperation { + pc: state.pc, + opcode, + }); + }; + + Ok(()) + } +} + +/// Implementation of the ExecutionControl trait using the old segmentation strategy +pub struct E1ExecutionControl { + pub final_memory: Option, +} + +impl ExecutionControl for E1ExecutionControl +where + F: PrimeField32, + VC: VmConfig, + VC::Executor: InsExecutorE1, +{ + type Ctx = (); + type Mem = AddressMap; + + fn new(_chip_complex: &VmChipComplex) -> Self { + Self { final_memory: None } + } + + fn should_stop( + &mut self, + _chip_complex: &VmChipComplex, + ) -> bool { + false + } + + fn on_segment_start( + &mut self, + _pc: u32, + _chip_complex: &mut VmChipComplex, + ) { + } + + fn on_segment_end( + &mut self, + _pc: u32, + chip_complex: &mut VmChipComplex, + ) { + self.final_memory = Some(chip_complex.base.memory_controller.memory_image().clone()); + } + + fn on_terminate( + &mut self, + _pc: u32, + chip_complex: &mut VmChipComplex, + _exit_code: u32, + ) { + self.final_memory = Some(chip_complex.base.memory_controller.memory_image().clone()); + } + + /// Execute a single instruction + fn execute_instruction( + &mut self, + state: &mut ExecutionSegmentState, + // instruction: &Instruction, + chip_complex: &mut VmChipComplex, + ) -> Result<(), ExecutionError> + where + F: PrimeField32, + { + let (instruction, _) = chip_complex.base.program_chip.get_instruction(state.pc)?; + + let &Instruction { opcode, .. } = instruction; + + if let Some(executor) = chip_complex.inventory.get_mut_executor(&opcode) { + let memory_controller = &mut chip_complex.base.memory_controller; + let vm_state = VmStateMut { + pc: &mut state.pc, + memory: &mut memory_controller.memory.data, + ctx: &mut (), + }; + executor.execute_e1(vm_state, instruction)?; } else { return Err(ExecutionError::DisabledOperation { - pc: vm_state.pc, + pc: state.pc, opcode, }); }; diff --git a/crates/vm/src/arch/extensions.rs b/crates/vm/src/arch/extensions.rs index c09af9c3f0..e349a47518 100644 --- a/crates/vm/src/arch/extensions.rs +++ b/crates/vm/src/arch/extensions.rs @@ -10,7 +10,7 @@ use getset::Getters; use itertools::{zip_eq, Itertools}; #[cfg(feature = "bench-metrics")] use metrics::counter; -use openvm_circuit_derive::{AnyEnum, InstructionExecutor}; +use openvm_circuit_derive::{AnyEnum, InsExecutorE1, InstructionExecutor}; use openvm_circuit_primitives::{ utils::next_power_of_two_or_zero, var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus}, @@ -40,17 +40,23 @@ use super::{ }; #[cfg(feature = "bench-metrics")] use crate::metrics::VmMetrics; -use crate::system::{ - connector::VmConnectorChip, - memory::{ - offline_checker::{MemoryBridge, MemoryBus}, - MemoryController, MemoryImage, OfflineMemory, BOUNDARY_AIR_OFFSET, MERKLE_AIR_OFFSET, +use crate::{ + arch::{ExecutionBridge, VmAirWrapper}, + system::{ + connector::VmConnectorChip, + memory::{ + offline_checker::{MemoryBridge, MemoryBus}, + MemoryController, MemoryImage, OfflineMemory, BOUNDARY_AIR_OFFSET, MERKLE_AIR_OFFSET, + }, + native_adapter::{NativeAdapterAir, NativeAdapterStep}, + phantom::PhantomChip, + poseidon2::Poseidon2PeripheryChip, + program::{ProgramBus, ProgramChip}, + public_values::{ + core::{PublicValuesCoreAir, PublicValuesStep}, + PublicValuesChip, + }, }, - native_adapter::NativeAdapterChip, - phantom::PhantomChip, - poseidon2::Poseidon2PeripheryChip, - program::{ProgramBus, ProgramChip}, - public_values::{core::PublicValuesCoreChip, PublicValuesChip}, }; /// Global AIR ID in the VM circuit verifying key. @@ -519,7 +525,7 @@ impl SystemBase { } } -#[derive(ChipUsageGetter, Chip, AnyEnum, From, InstructionExecutor)] +#[derive(ChipUsageGetter, Chip, AnyEnum, From, InstructionExecutor, InsExecutorE1)] pub enum SystemExecutor { PublicValues(PublicValuesChip), Phantom(RefCell>), @@ -569,14 +575,25 @@ impl SystemComplex { // PublicValuesChip is required when num_public_values > 0 in single segment mode. if config.has_public_values_chip() { assert_eq!(inventory.executors().len(), Self::PV_EXECUTOR_IDX); + + // TODO(ayush): this should be decided after e2 execution + const MAX_INS_CAPACITY: usize = 0; let chip = PublicValuesChip::new( - NativeAdapterChip::new(execution_bus, program_bus, memory_bridge), - PublicValuesCoreChip::new( - config.num_public_values, - config.max_constraint_degree as u32 - 1, + VmAirWrapper::new( + NativeAdapterAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + ), + PublicValuesCoreAir::new( + config.num_public_values, + config.max_constraint_degree as u32 - 1, + ), ), - memory_controller.offline_memory.clone(), + PublicValuesStep::new(NativeAdapterStep::new(), config.num_public_values), + MAX_INS_CAPACITY, + memory_controller.helper(), ); + inventory .add_executor(chip, [PublishOpcode::PUBLISH.global_opcode()]) .unwrap(); diff --git a/crates/vm/src/arch/integration_api.rs b/crates/vm/src/arch/integration_api.rs index 9480221588..9f2ebbb801 100644 --- a/crates/vm/src/arch/integration_api.rs +++ b/crates/vm/src/arch/integration_api.rs @@ -22,12 +22,12 @@ use openvm_stark_backend::{ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use super::{ - ExecutionError, ExecutionState, InsExecutorE1, InstructionExecutor, Result, VmExecutionState, - VmStateMut, + ExecutionError, ExecutionState, InsExecutorE1, InstructionExecutor, Result, VmStateMut, }; use crate::system::memory::{ online::{GuestMemory, TracingMemory}, - MemoryAuxColsFactory, MemoryController, OfflineMemory, SharedMemoryHelper, + AddressMap, MemoryAuxColsFactory, MemoryController, OfflineMemory, SharedMemoryHelper, + PAGE_SIZE, }; /// The interface between primitive AIR and machine adapter AIR. @@ -246,6 +246,7 @@ pub trait SingleTraceStep { fn get_opcode_name(&self, opcode: usize) -> String; } +// TODO(ayush): rename to ChipWithExecutionContext or something pub struct NewVmChipWrapper { pub air: AIR, pub step: STEP, @@ -278,7 +279,8 @@ where impl InstructionExecutor for NewVmChipWrapper where F: PrimeField32, - STEP: SingleTraceStep, // TODO: CTX? + STEP: SingleTraceStep // TODO: CTX? + + StepExecutorE1, { fn execute( &mut self, @@ -306,6 +308,7 @@ where .get_unchecked_mut(start_idx..self.buffer_idx) }; self.step.execute(state, instruction, row_slice)?; + Ok(ExecutionState { pc, timestamp: memory.memory.timestamp, @@ -391,12 +394,14 @@ pub trait AdapterTraceStep { fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]); fn read( + &self, memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData; fn write( + &self, memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], @@ -406,12 +411,40 @@ pub trait AdapterTraceStep { // Note[jpw]: should we reuse TraceSubRowGenerator trait instead? /// Post-execution filling of rest of adapter row. fn fill_trace_row( + &self, mem_helper: &MemoryAuxColsFactory, ctx: Self::TraceContext<'_>, adapter_row: &mut [F], ); } +pub trait AdapterExecutorE1 +where + F: PrimeField32, +{ + type ReadData; + type WriteData; + + fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData + where + Mem: GuestMemory; + + fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) + where + Mem: GuestMemory; +} + +// TODO: Rename core/step to operator +pub trait StepExecutorE1 { + fn execute_e1( + &mut self, + state: VmStateMut, + instruction: &Instruction, + ) -> Result<()> + where + Mem: GuestMemory; +} + pub struct VmChipWrapper, C: VmCoreChip> { pub adapter: A, pub core: C, @@ -464,34 +497,38 @@ where } } -// TODO(ayush): delete -impl InsExecutorE1 for VmChipWrapper +// // TODO(ayush): delete +impl InsExecutorE1 for VmChipWrapper where - Mem: GuestMemory, F: PrimeField32, A: VmAdapterChip + Send + Sync, - M: VmCoreChip + InsExecutorE1 + Send + Sync, + M: VmCoreChip + StepExecutorE1 + Send + Sync, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Mem: GuestMemory, + { self.core.execute_e1(state, instruction) } } -impl InsExecutorE1 for NewVmChipWrapper +impl InsExecutorE1 for NewVmChipWrapper where - Mem: GuestMemory, F: PrimeField32, - S: InsExecutorE1, + S: StepExecutorE1, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Mem: GuestMemory, + { self.step.execute_e1(state, instruction) } } diff --git a/crates/vm/src/arch/mod.rs b/crates/vm/src/arch/mod.rs index 7ecb289cc9..3b51b45fde 100644 --- a/crates/vm/src/arch/mod.rs +++ b/crates/vm/src/arch/mod.rs @@ -1,7 +1,7 @@ mod config; /// Instruction execution traits and types. /// Execution bus and interface. -mod execution; +pub mod execution; /// Module for controlling VM execution flow, including segmentation and instruction execution pub mod execution_control; /// Traits and builders to compose collections of chips into a virtual machine. diff --git a/crates/vm/src/arch/segment.rs b/crates/vm/src/arch/segment.rs index a7361dfda6..4e0fb12258 100644 --- a/crates/vm/src/arch/segment.rs +++ b/crates/vm/src/arch/segment.rs @@ -15,50 +15,20 @@ use openvm_stark_backend::{ use program::Program; use super::{ - execution_control::{ExecutionControl, TracegenExecutionControl}, + execution_control::{E1ExecutionControl, ExecutionControl, TracegenExecutionControl}, ExecutionError, GenerationError, Streams, SystemConfig, VmChipComplex, VmComplexTraceHeights, - VmConfig, + VmConfig, VmStateMut, }; #[cfg(feature = "bench-metrics")] use crate::metrics::VmMetrics; use crate::{ arch::{instructions::*, ExecutionState, InstructionExecutor}, - system::memory::{ - online::GuestMemory, paged_vec::PAGE_SIZE, AddressMap, MemoryController, MemoryImage, + system::{ + connector::DEFAULT_SUSPEND_EXIT_CODE, + memory::{online::GuestMemory, paged_vec::PAGE_SIZE, AddressMap, MemoryImage}, }, }; -/// Represents the state of the VM during execution -pub struct VmExecutionState -where - Mem: GuestMemory, -{ - /// Program counter - current instruction address - pub pc: u32, - /// Whether execution has terminated - // TODO: see if it can be removed - pub terminated: bool, - /// Guest memory interface - pub memory: Mem, - /// Host-specific execution context - pub ctx: Ctx, -} - -impl VmExecutionState -where - Mem: GuestMemory, -{ - /// Creates a new VM execution state with the given parameters - pub fn new(pc: u32, memory: Mem, ctx: Ctx) -> Self { - Self { - pc, - terminated: false, - memory, - ctx, - } - } -} - pub struct VmSegmentExecutor where F: PrimeField32, @@ -79,36 +49,23 @@ where pub metrics: VmMetrics, } -pub struct TracegenCtx { - pub timestamp: u32, -} - -impl TracegenCtx { - pub fn new(timestamp: u32) -> Self { - Self { timestamp } - } -} +pub type E1VmSegmentExecutor = + VmSegmentExecutor, (), E1ExecutionControl>; -pub type TracegenVmExecutionState = VmExecutionState, TracegenCtx>; - -impl TracegenVmExecutionState { - pub fn from_pc_and_memory_controller( - pc: u32, - memory_controller: &MemoryController, - ) -> Self - where - F: PrimeField32, - { - // TODO(ayush): remove this clone - let memory = memory_controller.memory_image().clone(); - let ctx = TracegenCtx::new(memory_controller.timestamp()); - TracegenVmExecutionState::new(pc, memory, ctx) - } -} +// pub struct TracegenCtx; +pub type TracegenCtx = (); +pub type TracegenVmStateMut<'a> = VmStateMut<'a, AddressMap, TracegenCtx>; pub type TracegenVmSegmentExecutor = VmSegmentExecutor, TracegenCtx, TracegenExecutionControl>; +#[derive(derive_new::new)] +pub struct ExecutionSegmentState { + pub pc: u32, + pub exit_code: u32, + pub is_terminated: bool, +} + impl VmSegmentExecutor where F: PrimeField32, @@ -165,36 +122,43 @@ where } /// Stopping is triggered by should_stop() or if VM is terminated - pub fn execute_from_state( - &mut self, - vm_state: &mut VmExecutionState, - ) -> Result<(), ExecutionError> { + pub fn execute_from_pc(&mut self, pc: u32) -> Result { let mut prev_backtrace: Option = None; // Call the pre-execution hook - self.control - .on_segment_start(vm_state, &mut self.chip_complex); + self.control.on_segment_start(pc, &mut self.chip_complex); - while !vm_state.terminated && !self.should_stop(vm_state) { + let mut state = ExecutionSegmentState::new(pc, 0, false); + loop { // Fetch, decode and execute single instruction - self.execute_instruction(vm_state, &mut prev_backtrace)?; + let terminated_exit_code = self.execute_instruction(&mut state, &mut prev_backtrace)?; + + if let Some(exit_code) = terminated_exit_code { + state.is_terminated = true; + state.exit_code = exit_code; + self.control + .on_terminate(state.pc, &mut self.chip_complex, exit_code); + break; + } + if self.should_stop() { + state.exit_code = DEFAULT_SUSPEND_EXIT_CODE; + self.control + .on_segment_end(state.pc, &mut self.chip_complex); + break; + } } - // Call the post-execution hook - self.control - .on_segment_end(vm_state, &mut self.chip_complex); - - Ok(()) + Ok(state) } /// Executes a single instruction and updates VM state // TODO(ayush): clean this up, separate to smaller functions fn execute_instruction( &mut self, - vm_state: &mut VmExecutionState, + state: &mut ExecutionSegmentState, prev_backtrace: &mut Option, - ) -> Result<(), ExecutionError> { - let pc = vm_state.pc; + ) -> Result, ExecutionError> { + let pc = state.pc; let timestamp = self.chip_complex.memory_controller().timestamp(); // Process an instruction and update VM state @@ -202,6 +166,13 @@ where tracing::trace!("pc: {pc:#x} | time: {timestamp} | {:?}", instruction); + let &Instruction { opcode, c, .. } = instruction; + + // Handle termination instruction + if opcode == SystemOpcode::TERMINATE.global_opcode() { + return Ok(Some(c.as_canonical_u32())); + } + // Extract debug info components #[allow(unused_variables)] let (dsl_instr, trace) = debug_info.as_ref().map_or( @@ -212,18 +183,6 @@ where }| (Some(dsl_instruction.clone()), trace.as_ref()), ); - let &Instruction { opcode, c, .. } = instruction; - - // Handle termination instruction - if opcode == SystemOpcode::TERMINATE.global_opcode() { - self.chip_complex.connector_chip_mut().end( - ExecutionState::new(pc, timestamp), - Some(c.as_canonical_u32()), - ); - vm_state.terminated = true; - return Ok(()); - } - // Handle phantom instructions if opcode == SystemOpcode::PHANTOM.global_opcode() { let discriminant = c.as_canonical_u32() as u16; @@ -257,7 +216,7 @@ where // Execute the instruction using the control implementation self.control - .execute_instruction(vm_state, &mut self.chip_complex)?; + .execute_instruction(state, &mut self.chip_complex)?; // Update metrics if enabled #[cfg(feature = "bench-metrics")] @@ -265,17 +224,17 @@ where self.update_instruction_metrics(pc, opcode, dsl_instr); } - Ok(()) + Ok(None) } /// Returns bool of whether to switch to next segment or not. - fn should_stop(&mut self, vm_state: &VmExecutionState) -> bool { + fn should_stop(&mut self) -> bool { if !self.system_config().continuation_enabled { return false; } // Check with the execution control policy - self.control.should_stop(vm_state, &self.chip_complex) + self.control.should_stop(&self.chip_complex) } // TODO(ayush): not sure what to do of these diff --git a/crates/vm/src/arch/vm.rs b/crates/vm/src/arch/vm.rs index 0e80a8671f..56f03995c4 100644 --- a/crates/vm/src/arch/vm.rs +++ b/crates/vm/src/arch/vm.rs @@ -19,15 +19,15 @@ use thiserror::Error; use tracing::info_span; use super::{ - ExecutionError, VmComplexTraceHeights, VmConfig, CONNECTOR_AIR_ID, MERKLE_AIR_ID, - PROGRAM_AIR_ID, PROGRAM_CACHED_TRACE_INDEX, + ExecutionError, InsExecutorE1, VmComplexTraceHeights, VmConfig, CONNECTOR_AIR_ID, + MERKLE_AIR_ID, PROGRAM_AIR_ID, PROGRAM_CACHED_TRACE_INDEX, }; #[cfg(feature = "bench-metrics")] use crate::metrics::VmMetrics; use crate::{ arch::{ hasher::poseidon2::vm_poseidon2_hasher, segment::TracegenVmSegmentExecutor, - TracegenVmExecutionState, + E1VmSegmentExecutor, }, system::{ connector::{VmConnectorPvs, DEFAULT_SUSPEND_EXIT_CODE}, @@ -246,15 +246,9 @@ where if let Some(overridden_heights) = self.overridden_heights.as_ref() { segment.set_override_trace_heights(overridden_heights.clone()); } - let mut vm_state = TracegenVmExecutionState::from_pc_and_memory_controller( - from_state.pc, - segment.chip_complex.memory_controller(), - ); - metrics_span("execute_time_ms", || { - segment.execute_from_state(&mut vm_state) - })?; + let state = metrics_span("execute_time_ms", || segment.execute_from_pc(from_state.pc))?; - if vm_state.terminated { + if state.is_terminated { return Ok(VmExecutorOneSegmentResult { segment, next_state: None, @@ -266,7 +260,7 @@ where "multiple segments require to enable continuations" ); assert_eq!( - vm_state.pc, + state.pc, segment.chip_complex.connector_chip().boundary_states[1] .unwrap() .pc @@ -281,7 +275,7 @@ where next_state: Some(VmExecutorNextSegmentState { memory: final_memory, input: streams, - pc: vm_state.pc, + pc: state.pc, #[cfg(feature = "bench-metrics")] metrics, }), @@ -316,6 +310,80 @@ where Ok(final_memory) } + pub fn execute_e1( + &self, + exe: impl Into>, + input: impl Into>, + ) -> Result<(), ExecutionError> + where + VC::Executor: InsExecutorE1, + { + let mem_config = self.config.system().memory_config; + let exe = exe.into(); + let memory = AddressMap::from_sparse( + mem_config.as_offset, + 1 << mem_config.as_height, + 1 << mem_config.pointer_max_bits, + exe.init_memory.clone(), + ); + + let pc = exe.pc_start; + let mut state = VmExecutorNextSegmentState::new(memory, input, pc); + let mut segment_idx = 0; + + loop { + let _span = info_span!("execute_segment", segment = segment_idx).entered(); + + let mut segment = E1VmSegmentExecutor::new( + &self.config, + exe.program.clone(), + state.input, + Some(state.memory), + self.trace_height_constraints.clone(), + exe.fn_bounds.clone(), + ); + #[cfg(feature = "bench-metrics")] + { + segment.metrics = state.metrics; + } + + let exec_state = metrics_span("execute_time_ms", || segment.execute_from_pc(state.pc))?; + + if exec_state.is_terminated { + // Check exit code for the final segment + if exec_state.exit_code != ExitCode::Success as u32 { + return Err(ExecutionError::FailedWithExitCode(exec_state.exit_code)); + } + tracing::debug!("Execution completed in {} segments", segment_idx + 1); + #[cfg(feature = "bench-metrics")] + metrics::counter!("num_segments").absolute((segment_idx + 1) as u64); + return Ok(()); + } + + assert!( + self.continuation_enabled(), + "multiple segments require to enable continuations" + ); + + let final_memory = mem::take(&mut segment.control.final_memory) + .expect("final memory should be set in continuations segment"); + let streams = segment.chip_complex.take_streams(); + + #[cfg(feature = "bench-metrics")] + let metrics = segment.metrics.partial_take(); + + state = VmExecutorNextSegmentState { + memory: final_memory, + input: streams, + pc: exec_state.pc, + #[cfg(feature = "bench-metrics")] + metrics, + }; + + segment_idx += 1; + } + } + pub fn execute_and_generate( &self, exe: impl Into>, @@ -447,7 +515,7 @@ where let air_heights = segment.chip_complex.current_trace_heights(); let vm_heights = segment.chip_complex.get_internal_trace_heights(); let public_values = if let Some(pv_chip) = segment.chip_complex.public_values_chip() { - pv_chip.core.get_custom_public_values() + pv_chip.step.get_custom_public_values() } else { vec![] }; @@ -492,13 +560,7 @@ where if let Some(overridden_heights) = self.overridden_heights.as_ref() { segment.set_override_trace_heights(overridden_heights.clone()); } - let mut vm_state = TracegenVmExecutionState::from_pc_and_memory_controller( - exe.pc_start, - segment.chip_complex.memory_controller(), - ); - metrics_span("execute_time_ms", || { - segment.execute_from_state(&mut vm_state) - })?; + metrics_span("execute_time_ms", || segment.execute_from_pc(exe.pc_start))?; Ok(segment) } } diff --git a/crates/vm/src/system/memory/offline_checker/columns.rs b/crates/vm/src/system/memory/offline_checker/columns.rs index 2e3f7fb019..677e04fd31 100644 --- a/crates/vm/src/system/memory/offline_checker/columns.rs +++ b/crates/vm/src/system/memory/offline_checker/columns.rs @@ -21,6 +21,12 @@ pub struct MemoryBaseAuxCols { pub(in crate::system::memory) timestamp_lt_aux: LessThanAuxCols, } +impl MemoryBaseAuxCols { + pub fn set_prev(&mut self, prev_timestamp: F) { + self.prev_timestamp = prev_timestamp; + } +} + #[repr(C)] #[derive(Clone, Copy, Debug, AlignedBorrow)] pub struct MemoryWriteAuxCols { diff --git a/crates/vm/src/system/memory/online.rs b/crates/vm/src/system/memory/online.rs index bbf015e838..d13885f4af 100644 --- a/crates/vm/src/system/memory/online.rs +++ b/crates/vm/src/system/memory/online.rs @@ -22,22 +22,25 @@ pub trait GuestMemory { /// and it must be the exact type used to represent a single memory cell in /// address space `address_space`. For standard usage, /// `T` is either `u8` or `F` where `F` is the base field of the ZK backend. - unsafe fn read( + unsafe fn read( &self, address_space: u32, pointer: u32, - ) -> [T; BLOCK_SIZE]; + ) -> [T; BLOCK_SIZE] + where + T: Copy + Debug; /// Writes `values` to `[pointer:BLOCK_SIZE]_{address_space}` /// /// # Safety /// See [`GuestMemory::read`]. - unsafe fn write( + unsafe fn write( &mut self, address_space: u32, pointer: u32, values: &[T; BLOCK_SIZE], - ); + ) where + T: Copy + Debug; /// Writes `values` to `[pointer:BLOCK_SIZE]_{address_space}` and returns /// the previous values. @@ -45,12 +48,15 @@ pub trait GuestMemory { /// # Safety /// See [`GuestMemory::read`]. #[inline(always)] - unsafe fn replace( + unsafe fn replace( &mut self, address_space: u32, pointer: u32, values: &[T; BLOCK_SIZE], - ) -> [T; BLOCK_SIZE] { + ) -> [T; BLOCK_SIZE] + where + T: Copy + Debug, + { let prev = self.read(address_space, pointer); self.write(address_space, pointer, values); prev @@ -91,7 +97,7 @@ pub struct TracingMemory { /// The underlying data memory, with memory cells typed by address space: see [AddressMap]. // TODO: make generic in GuestMemory #[getset(get = "pub")] - pub(super) data: AddressMap, + pub data: AddressMap, /// A map of `addr_space -> (ptr / min_block_size[addr_space] -> (timestamp: u32, block_size: /// u32))` for the timestamp and block size of the latest access. pub(super) meta: Vec>, @@ -111,6 +117,7 @@ impl TracingMemory { // TMP: hardcoding for now min_block_size[1] = 4; min_block_size[2] = 4; + min_block_size[3] = 4; let meta = min_block_size .iter() .map(|&min_block_size| { @@ -133,19 +140,22 @@ impl TracingMemory { /// Instantiates a new `Memory` data structure from an image. pub fn from_image(image: MemoryImage, access_capacity: usize) -> Self { let mut meta = vec![PagedVec::new(0); image.as_offset as usize]; - let mut min_block_size = vec![1; meta.len()]; - // TMP: hardcoding for now - min_block_size[1] = 4; - min_block_size[2] = 4; - for (paged_vec, cell_size, &min_block_size) in - izip!(&image.paged_vecs, &image.cell_size, &min_block_size) - { + let mut min_block_size = vec![1; image.as_offset as usize]; + for (i, (paged_vec, cell_size)) in izip!(&image.paged_vecs, &image.cell_size).enumerate() { let num_cells = paged_vec.bytes_capacity() / cell_size; + + // TMP: hardcoding for now + if i <= 3 { + min_block_size.push(4); + } else { + min_block_size.push(1); + } + meta.push(PagedVec::new( num_cells .checked_mul(size_of::()) .unwrap() - .div_ceil(PAGE_SIZE * min_block_size as usize), + .div_ceil(PAGE_SIZE * min_block_size[i] as usize), )); } Self { @@ -191,11 +201,14 @@ impl TracingMemory { /// In addition: /// - `address_space` must be valid. #[inline(always)] - pub unsafe fn read( + pub unsafe fn read( &mut self, address_space: u32, pointer: u32, - ) -> (u32, [T; BLOCK_SIZE]) { + ) -> (u32, [T; BLOCK_SIZE]) + where + T: Copy + Debug, + { self.assert_alignment(BLOCK_SIZE, ALIGN, address_space, pointer); let values = self.data.read(address_space, pointer); let t_curr = self.timestamp; @@ -242,12 +255,15 @@ impl TracingMemory { /// In addition: /// - `address_space` must be valid. #[inline(always)] - pub unsafe fn write( + pub unsafe fn write( &mut self, address_space: u32, pointer: u32, values: &[T; BLOCK_SIZE], - ) -> (u32, [T; BLOCK_SIZE]) { + ) -> (u32, [T; BLOCK_SIZE]) + where + T: Copy + Debug, + { self.assert_alignment(BLOCK_SIZE, ALIGN, address_space, pointer); let values_prev = self.data.replace(address_space, pointer, values); let t_curr = self.timestamp; diff --git a/crates/vm/src/system/memory/paged_vec.rs b/crates/vm/src/system/memory/paged_vec.rs index c8acc41804..3727bfd76e 100644 --- a/crates/vm/src/system/memory/paged_vec.rs +++ b/crates/vm/src/system/memory/paged_vec.rs @@ -1,4 +1,4 @@ -use std::{marker::PhantomData, mem::MaybeUninit, ptr}; +use std::{fmt::Debug, marker::PhantomData, mem::MaybeUninit, ptr}; use itertools::{zip_eq, Itertools}; use openvm_instructions::exe::SparseMemoryImage; @@ -252,7 +252,7 @@ impl Default for AddressMap { impl AddressMap { pub fn new(as_offset: u32, as_cnt: usize, mem_size: usize) -> Self { // TMP: hardcoding for now - let mut cell_size = vec![1, 1]; + let mut cell_size = vec![1, 1, 1]; cell_size.resize(as_cnt, 4); let paged_vecs = cell_size .iter() @@ -359,26 +359,29 @@ impl AddressMap { } impl GuestMemory for AddressMap { - unsafe fn read( - &self, - addr_space: u32, - ptr: u32, - ) -> [T; BLOCK_SIZE] { + unsafe fn read(&self, addr_space: u32, ptr: u32) -> [T; BLOCK_SIZE] + where + T: Copy + Debug, + { debug_assert_eq!( size_of::(), self.cell_size[(addr_space - self.as_offset) as usize] ); - self.paged_vecs + let read = self + .paged_vecs .get_unchecked((addr_space - self.as_offset) as usize) - .get((ptr as usize) * size_of::()) + .get((ptr as usize) * size_of::()); + read } - unsafe fn write( + unsafe fn write( &mut self, addr_space: u32, ptr: u32, values: &[T; BLOCK_SIZE], - ) { + ) where + T: Copy + Debug, + { debug_assert_eq!( size_of::(), self.cell_size[(addr_space - self.as_offset) as usize], diff --git a/crates/vm/src/system/native_adapter/mod.rs b/crates/vm/src/system/native_adapter/mod.rs index 101dbf00c9..ac86f9626b 100644 --- a/crates/vm/src/system/native_adapter/mod.rs +++ b/crates/vm/src/system/native_adapter/mod.rs @@ -27,32 +27,12 @@ use openvm_stark_backend::{ use serde::{Deserialize, Serialize}; use serde_big_array::BigArray; -use crate::system::memory::{online::GuestMemory, OfflineMemory, RecordId}; - -/// R reads(R<=2), W writes(W<=1). -/// Operands: b for the first read, c for the second read, a for the first write. -/// If an operand is not used, its address space and pointer should be all 0. -#[derive(Debug)] -pub struct NativeAdapterChip { - pub air: NativeAdapterAir, - _phantom: PhantomData, -} +use crate::{ + arch::{AdapterExecutorE1, AdapterTraceStep}, + system::memory::{online::GuestMemory, OfflineMemory, RecordId}, +}; -impl NativeAdapterChip { - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - ) -> Self { - Self { - air: NativeAdapterAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - }, - _phantom: PhantomData, - } - } -} +use super::memory::{online::TracingMemory, MemoryAuxColsFactory}; #[repr(C)] #[derive(Debug, Serialize, Deserialize)] @@ -205,106 +185,207 @@ impl VmAdapterAir } } -impl VmAdapterChip - for NativeAdapterChip +/// R reads(R<=2), W writes(W<=1). +/// Operands: b for the first read, c for the second read, a for the first write. +/// If an operand is not used, its address space and pointer should be all 0. +#[derive(Debug, derive_new::new)] +pub struct NativeAdapterStep { + _phantom: PhantomData, +} + +impl AdapterTraceStep for NativeAdapterStep +where + F: PrimeField32, { - type ReadRecord = NativeReadRecord; - type WriteRecord = NativeWriteRecord; - type Air = NativeAdapterAir; - type Interface = BasicAdapterInterface, R, W, 1, 1>; - - fn preprocess( - &mut self, - memory: &mut MemoryController, + const WIDTH: usize = size_of::>(); + type ReadData = [[F; 1]; R]; + type WriteData = [[F; 1]; W]; + type TraceContext<'a> = (); + + #[inline(always)] + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let adapter_row: &mut NativeAdapterCols = adapter_row.borrow_mut(); + adapter_row.from_state.pc = F::from_canonical_u32(pc); + adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + } + + #[inline(always)] + fn read( + &self, + memory: &mut TracingMemory, instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { + adapter_row: &mut [F], + ) -> Self::ReadData { + todo!("Implement read operation"); + } + + #[inline(always)] + fn write( + &self, + memory: &mut TracingMemory, + instruction: &Instruction, + adapter_row: &mut [F], + data: &Self::WriteData, + ) { + todo!("Implement write operation"); + } + + #[inline(always)] + fn fill_trace_row( + &self, + mem_helper: &MemoryAuxColsFactory, + bitwise_lookup_chip: Self::TraceContext<'_>, + adapter_row: &mut [F], + ) { + todo!("Implement fill_trace_row operation"); + } +} + +impl AdapterExecutorE1 for NativeAdapterStep +where + F: PrimeField32, +{ + type ReadData = [F; R]; + type WriteData = [F; W]; + + #[inline(always)] + fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData + where + Mem: GuestMemory, + { assert!(R <= 2); - let Instruction { b, c, e, f, .. } = *instruction; - let mut reads = Vec::with_capacity(R); + let &Instruction { b, c, e, f, .. } = instruction; + + let mut reads = [F::ZERO; R]; if R >= 1 { - reads.push(unsafe { - memory - .memory_image() - .read::(e.as_canonical_u32(), b.as_canonical_u32()) - }); + let [value] = + unsafe { memory.read::(e.as_canonical_u32(), b.as_canonical_u32()) }; + reads[0] = value; } if R >= 2 { - reads.push(unsafe { - memory - .memory_image() - .read::(f.as_canonical_u32(), c.as_canonical_u32()) - }); + let [value] = + unsafe { memory.read::(f.as_canonical_u32(), c.as_canonical_u32()) }; + reads[1] = value; } - Ok(( - reads.try_into().unwrap(), - Self::ReadRecord { reads: todo!() }, - )) + reads } - fn postprocess( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - _read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { + #[inline(always)] + fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) + where + Mem: GuestMemory, + { assert!(W <= 1); - let Instruction { a, d, .. } = *instruction; - let mut writes = Vec::with_capacity(W); + + let &Instruction { a, d, .. } = instruction; if W >= 1 { - todo!(); - // let (record_id, _) = memory.write(d, a, &output.writes[0]); - // writes.push((record_id, output.writes[0])); + unsafe { memory.write(d.as_canonical_u32(), a.as_canonical_u32(), data) }; } - - Ok(( - ExecutionState { - pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), - timestamp: memory.timestamp(), - }, - Self::WriteRecord { - from_state, - writes: writes.try_into().unwrap(), - }, - )) } +} - fn generate_trace_row( - &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, - ) { - let row_slice: &mut NativeAdapterCols<_, R, W> = row_slice.borrow_mut(); - let aux_cols_factory = memory.aux_cols_factory(); - - row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); - - for (i, read) in read_record.reads.iter().enumerate() { - let (id, _) = read; - let record = memory.record_by_id(*id); - aux_cols_factory - .generate_read_or_immediate_aux(record, &mut row_slice.reads_aux[i].read_aux); - row_slice.reads_aux[i].address = - MemoryAddress::new(record.address_space, record.pointer); - } +// impl VmAdapterChip for NativeAdapterStep +// where +// F: PrimeField32, +// { +// type ReadRecord = NativeReadRecord; +// type WriteRecord = NativeWriteRecord; +// type Air = NativeAdapterAir; +// type Interface = BasicAdapterInterface, R, W, 1, 1>; - for (i, write) in write_record.writes.iter().enumerate() { - let (id, _) = write; - let record = memory.record_by_id(*id); - aux_cols_factory.generate_write_aux(record, &mut row_slice.writes_aux[i].write_aux); - row_slice.writes_aux[i].address = - MemoryAddress::new(record.address_space, record.pointer); - } - } +// fn preprocess( +// &mut self, +// memory: &mut MemoryController, +// instruction: &Instruction, +// ) -> Result<( +// >::Reads, +// Self::ReadRecord, +// )> { +// assert!(R <= 2); +// let Instruction { b, c, e, f, .. } = *instruction; - fn air(&self) -> &Self::Air { - &self.air - } -} +// let mut reads = Vec::with_capacity(R); +// if R >= 1 { +// reads.push(unsafe { +// memory +// .memory_image() +// .read::(e.as_canonical_u32(), b.as_canonical_u32()) +// }); +// } +// if R >= 2 { +// reads.push(unsafe { +// memory +// .memory_image() +// .read::(f.as_canonical_u32(), c.as_canonical_u32()) +// }); +// } +// Ok(( +// reads.try_into().unwrap(), +// Self::ReadRecord { reads: todo!() }, +// )) +// } + +// fn postprocess( +// &mut self, +// memory: &mut MemoryController, +// instruction: &Instruction, +// from_state: ExecutionState, +// output: AdapterRuntimeContext, +// _read_record: &Self::ReadRecord, +// ) -> Result<(ExecutionState, Self::WriteRecord)> { +// assert!(W <= 1); +// let Instruction { a, d, .. } = *instruction; +// let mut writes = Vec::with_capacity(W); +// if W >= 1 { +// todo!(); +// // let (record_id, _) = memory.write(d, a, &output.writes[0]); +// // writes.push((record_id, output.writes[0])); +// } + +// Ok(( +// ExecutionState { +// pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), +// timestamp: memory.timestamp(), +// }, +// Self::WriteRecord { +// from_state, +// writes: writes.try_into().unwrap(), +// }, +// )) +// } + +// fn generate_trace_row( +// &self, +// row_slice: &mut [F], +// read_record: Self::ReadRecord, +// write_record: Self::WriteRecord, +// memory: &OfflineMemory, +// ) { +// let row_slice: &mut NativeAdapterCols<_, R, W> = row_slice.borrow_mut(); +// let aux_cols_factory = memory.aux_cols_factory(); + +// row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); + +// for (i, read) in read_record.reads.iter().enumerate() { +// let (id, _) = read; +// let record = memory.record_by_id(*id); +// aux_cols_factory +// .generate_read_or_immediate_aux(record, &mut row_slice.reads_aux[i].read_aux); +// row_slice.reads_aux[i].address = +// MemoryAddress::new(record.address_space, record.pointer); +// } + +// for (i, write) in write_record.writes.iter().enumerate() { +// let (id, _) = write; +// let record = memory.record_by_id(*id); +// aux_cols_factory.generate_write_aux(record, &mut row_slice.writes_aux[i].write_aux); +// row_slice.writes_aux[i].address = +// MemoryAddress::new(record.address_space, record.pointer); +// } +// } + +// fn air(&self) -> &Self::Air { +// &self.air +// } +// } diff --git a/crates/vm/src/system/phantom/mod.rs b/crates/vm/src/system/phantom/mod.rs index 28977fe2cd..792739c501 100644 --- a/crates/vm/src/system/phantom/mod.rs +++ b/crates/vm/src/system/phantom/mod.rs @@ -23,11 +23,11 @@ use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; use serde_big_array::BigArray; -use super::memory::MemoryController; +use super::memory::{online::GuestMemory, MemoryController}; use crate::{ arch::{ - ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InstructionExecutor, - PcIncOrSet, PhantomSubExecutor, Streams, + ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InsExecutorE1, + InstructionExecutor, PcIncOrSet, PhantomSubExecutor, Streams, VmStateMut, }, system::program::ProgramBus, }; @@ -124,6 +124,61 @@ impl PhantomChip { } } +impl InsExecutorE1 for PhantomChip +where + F: PrimeField32, +{ + fn execute_e1( + &mut self, + state: VmStateMut, + instruction: &Instruction, + ) -> Result<(), ExecutionError> + where + Mem: GuestMemory, + F: PrimeField32, + { + let &Instruction { + opcode, a, b, c, .. + } = instruction; + assert_eq!(opcode, self.air.phantom_opcode); + + let c_u32 = c.as_canonical_u32(); + let discriminant = PhantomDiscriminant(c_u32 as u16); + // If not a system phantom sub-instruction (which is handled in + // ExecutionSegment), look for a phantom sub-executor to handle it. + if SysPhantom::from_repr(discriminant.0).is_none() { + let sub_executor = self + .phantom_executors + .get_mut(&discriminant) + .ok_or_else(|| ExecutionError::PhantomNotFound { + pc: *state.pc, + discriminant, + })?; + let mut streams = self.streams.get().unwrap().lock().unwrap(); + // TODO(ayush): implement phantom subexecutor for new traits + // sub_executor + // .as_mut() + // .phantom_execute( + // state.memory, + // &mut streams, + // discriminant, + // a, + // b, + // (c_u32 >> 16) as u16, + // ) + // .map_err(|e| ExecutionError::Phantom { + // pc: *state.pc, + // discriminant, + // inner: e, + // })?; + } + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) + } +} + impl InstructionExecutor for PhantomChip { fn execute( &mut self, diff --git a/crates/vm/src/system/public_values/core.rs b/crates/vm/src/system/public_values/core.rs index 871e7d25c0..a6bcfce68e 100644 --- a/crates/vm/src/system/public_values/core.rs +++ b/crates/vm/src/system/public_values/core.rs @@ -1,8 +1,11 @@ -use std::sync::Mutex; +use std::{borrow::BorrowMut, sync::Mutex}; use openvm_circuit_primitives::{encoder::Encoder, SubAir}; use openvm_instructions::{ - instruction::Instruction, LocalOpcode, PublishOpcode, PublishOpcode::PUBLISH, + instruction::Instruction, + program::DEFAULT_PC_STEP, + LocalOpcode, + PublishOpcode::{self, PUBLISH}, }; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -14,10 +17,17 @@ use serde::{Deserialize, Serialize}; use crate::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, InsExecutorE1, - MinimalInstruction, Result, VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, AdapterTraceStep, + BasicAdapterInterface, MinimalInstruction, Result, SingleTraceStep, StepExecutorE1, + VmAdapterInterface, VmCoreAir, VmCoreChip, VmStateMut, + }, + system::{ + memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, + }, + public_values::columns::PublicValuesCoreColsView, }, - system::{memory::online::GuestMemory, public_values::columns::PublicValuesCoreColsView}, }; pub(crate) type AdapterInterface = BasicAdapterInterface, 2, 0, 1, 1>; pub(crate) type AdapterInterfaceReads = as VmAdapterInterface>::Reads; @@ -107,19 +117,22 @@ pub struct PublicValuesRecord { /// ATTENTION: If a specific public value is not provided, a default 0 will be used when generating /// the proof but in the perspective of constraints, it could be any value. -pub struct PublicValuesCoreChip { - air: PublicValuesCoreAir, +pub struct PublicValuesStep { + adapter: A, // Mutex is to make the struct Sync. But it actually won't be accessed by multiple threads. - custom_pvs: Mutex>>, + pub(crate) custom_pvs: Mutex>>, } -impl PublicValuesCoreChip { +impl PublicValuesStep +where + F: PrimeField32, +{ /// **Note:** `max_degree` is the maximum degree of the constraint polynomials to represent the /// flags. If you want the overall AIR's constraint degree to be `<= max_constraint_degree`, /// then typically you should set `max_degree` to `max_constraint_degree - 1`. - pub fn new(num_custom_pvs: usize, max_degree: u32) -> Self { + pub fn new(adapter: A, num_custom_pvs: usize) -> Self { Self { - air: PublicValuesCoreAir::new(num_custom_pvs, max_degree), + adapter, custom_pvs: Mutex::new(vec![None; num_custom_pvs]), } } @@ -128,38 +141,18 @@ impl PublicValuesCoreChip { } } -impl VmCoreChip> for PublicValuesCoreChip { - type Record = PublicValuesRecord; - type Air = PublicValuesCoreAir; - - #[allow(clippy::type_complexity)] - fn execute_instruction( - &self, - _instruction: &Instruction, - _from_pc: u32, - reads: AdapterInterfaceReads, - ) -> Result<(AdapterRuntimeContext>, Self::Record)> { - let [[value], [index]] = reads; - { - let idx: usize = index.as_canonical_u32() as usize; - let mut custom_pvs = self.custom_pvs.lock().unwrap(); - - if custom_pvs[idx].is_none() { - custom_pvs[idx] = Some(value); - } else { - // Not a hard constraint violation when publishing the same value twice but the - // program should avoid that. - panic!("Custom public value {} already set", idx); - } - } - let output = AdapterRuntimeContext { - to_pc: None, - writes: [], - }; - let record = Self::Record { value, index }; - Ok((output, record)) - } - +impl SingleTraceStep for PublicValuesStep +where + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = [[F; 1]; 2], + WriteData = [[F; 1]; 0], + TraceContext<'a> = (), + >, +{ fn get_opcode_name(&self, opcode: usize) -> String { format!( "{:?}", @@ -167,17 +160,38 @@ impl VmCoreChip> for PublicValuesCoreChi ) } - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let mut cols = PublicValuesCoreColsView::<_, &mut F>::borrow_mut(row_slice); - debug_assert_eq!(cols.width(), BaseAir::::width(&self.air)); - *cols.is_valid = F::ONE; - *cols.value = record.value; - *cols.index = record.index; - let idx: usize = record.index.as_canonical_u32() as usize; - let pt = self.air.encoder.get_flag_pt(idx); - for (i, var) in cols.custom_pv_vars.iter_mut().enumerate() { - **var = F::from_canonical_u32(pt[i]); - } + fn execute( + &mut self, + state: VmStateMut, + instruction: &Instruction, + row_slice: &mut [F], + ) -> Result<()> { + todo!("Implement execute function"); + } + + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + todo!("Implement fill_trace_row function"); + + // let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + // self.adapter.fill_trace_row(mem_helper, (), adapter_row); + + // let core_row: &mut PublicValuesCoreColsView<_, F> = core_row.borrow_mut(); + + // // TODO(ayush): add this check + // // debug_assert_eq!(core_row.width(), BaseAir::::width(&self.air)); + + // core_row.is_valid = F::ONE; + // core_row.value = record.value; + // core_row.index = record.index; + + // let idx: usize = record.index.as_canonical_u32() as usize; + + // let pt = self.air.encoder.get_flag_pt(idx); + + // for (i, var) in core_row.custom_pv_vars.iter_mut().enumerate() { + // *var = F::from_canonical_u32(pt[i]); + // } } fn generate_public_values(&self) -> Vec { @@ -186,22 +200,125 @@ impl VmCoreChip> for PublicValuesCoreChi .map(|x| x.unwrap_or(F::ZERO)) .collect() } - - fn air(&self) -> &Self::Air { - &self.air - } } -impl InsExecutorE1 for PublicValuesCoreChip +impl StepExecutorE1 for PublicValuesStep where - Mem: GuestMemory, F: PrimeField32, + A: 'static + for<'a> AdapterExecutorE1, { - fn execute_e1( + fn execute_e1( &mut self, - _state: &mut VmExecutionState, - _instruction: &Instruction, - ) -> Result<()> { - todo!("Implement execute_e1") + state: VmStateMut, + instruction: &Instruction, + ) -> Result<()> + where + Mem: GuestMemory, + { + let [value, index] = self.adapter.read(state.memory, instruction); + + let idx: usize = index.as_canonical_u32() as usize; + { + let mut custom_pvs = self.custom_pvs.lock().unwrap(); + + if custom_pvs[idx].is_none() { + custom_pvs[idx] = Some(value); + } else { + // Not a hard constraint violation when publishing the same value twice but the + // program should avoid that. + panic!("Custom public value {} already set", idx); + } + } + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) } } + +// /// ATTENTION: If a specific public value is not provided, a default 0 will be used when generating +// /// the proof but in the perspective of constraints, it could be any value. +// pub struct PublicValuesCoreChip { +// air: PublicValuesCoreAir, +// // Mutex is to make the struct Sync. But it actually won't be accessed by multiple threads. +// pub(crate) custom_pvs: Mutex>>, +// } + +// impl PublicValuesCoreChip { +// /// **Note:** `max_degree` is the maximum degree of the constraint polynomials to represent the +// /// flags. If you want the overall AIR's constraint degree to be `<= max_constraint_degree`, +// /// then typically you should set `max_degree` to `max_constraint_degree - 1`. +// pub fn new(num_custom_pvs: usize, max_degree: u32) -> Self { +// Self { +// air: PublicValuesCoreAir::new(num_custom_pvs, max_degree), +// custom_pvs: Mutex::new(vec![None; num_custom_pvs]), +// } +// } +// pub fn get_custom_public_values(&self) -> Vec> { +// self.custom_pvs.lock().unwrap().clone() +// } +// } + +// impl VmCoreChip> for PublicValuesCoreChip { +// type Record = PublicValuesRecord; +// type Air = PublicValuesCoreAir; + +// #[allow(clippy::type_complexity)] +// fn execute_instruction( +// &self, +// _instruction: &Instruction, +// _from_pc: u32, +// reads: AdapterInterfaceReads, +// ) -> Result<(AdapterRuntimeContext>, Self::Record)> { +// let [[value], [index]] = reads; +// { +// let idx: usize = index.as_canonical_u32() as usize; +// let mut custom_pvs = self.custom_pvs.lock().unwrap(); + +// if custom_pvs[idx].is_none() { +// custom_pvs[idx] = Some(value); +// } else { +// // Not a hard constraint violation when publishing the same value twice but the +// // program should avoid that. +// panic!("Custom public value {} already set", idx); +// } +// } +// let output = AdapterRuntimeContext { +// to_pc: None, +// writes: [], +// }; +// let record = Self::Record { value, index }; +// Ok((output, record)) +// } + +// fn get_opcode_name(&self, opcode: usize) -> String { +// format!( +// "{:?}", +// PublishOpcode::from_usize(opcode - PublishOpcode::CLASS_OFFSET) +// ) +// } + +// fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { +// let mut cols = PublicValuesCoreColsView::<_, &mut F>::borrow_mut(row_slice); +// debug_assert_eq!(cols.width(), BaseAir::::width(&self.air)); +// *cols.is_valid = F::ONE; +// *cols.value = record.value; +// *cols.index = record.index; +// let idx: usize = record.index.as_canonical_u32() as usize; +// let pt = self.air.encoder.get_flag_pt(idx); +// for (i, var) in cols.custom_pv_vars.iter_mut().enumerate() { +// **var = F::from_canonical_u32(pt[i]); +// } +// } + +// fn generate_public_values(&self) -> Vec { +// self.get_custom_public_values() +// .into_iter() +// .map(|x| x.unwrap_or(F::ZERO)) +// .collect() +// } + +// fn air(&self) -> &Self::Air { +// &self.air +// } +// } diff --git a/crates/vm/src/system/public_values/mod.rs b/crates/vm/src/system/public_values/mod.rs index 918606497b..8b849ab819 100644 --- a/crates/vm/src/system/public_values/mod.rs +++ b/crates/vm/src/system/public_values/mod.rs @@ -1,8 +1,10 @@ +use core::PublicValuesStep; + use crate::{ - arch::{VmAirWrapper, VmChipWrapper}, + arch::{NewVmChipWrapper, VmAirWrapper}, system::{ - native_adapter::{NativeAdapterAir, NativeAdapterChip}, - public_values::core::{PublicValuesCoreAir, PublicValuesCoreChip}, + native_adapter::{NativeAdapterAir, NativeAdapterStep}, + public_values::core::PublicValuesCoreAir, }, }; @@ -14,5 +16,5 @@ pub mod core; mod tests; pub type PublicValuesAir = VmAirWrapper, PublicValuesCoreAir>; -pub type PublicValuesChip = - VmChipWrapper, PublicValuesCoreChip>; +pub type PublicValuesStepWithAdapter = PublicValuesStep, F>; +pub type PublicValuesChip = NewVmChipWrapper>; diff --git a/extensions/native/circuit/src/adapters/alu_native_adapter.rs b/extensions/native/circuit/src/adapters/alu_native_adapter.rs index 65340323e6..884a999d53 100644 --- a/extensions/native/circuit/src/adapters/alu_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/alu_native_adapter.rs @@ -5,9 +5,9 @@ use std::{ use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge, - ExecutionBus, ExecutionState, MinimalInstruction, Result, VmAdapterAir, VmAdapterChip, - VmAdapterInterface, + AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, AdapterTraceStep, + BasicAdapterInterface, ExecutionBridge, ExecutionBus, ExecutionState, MinimalInstruction, + Result, VmAdapterAir, VmAdapterChip, VmAdapterInterface, }, system::{ memory::{ @@ -27,27 +27,7 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, }; -#[derive(Debug)] -pub struct AluNativeAdapterChip { - pub air: AluNativeAdapterAir, - _marker: PhantomData, -} - -impl AluNativeAdapterChip { - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - ) -> Self { - Self { - air: AluNativeAdapterAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - }, - _marker: PhantomData, - } - } -} +use super::{tracing_read_or_imm, tracing_write, tracing_write_reg}; #[repr(C)] #[derive(AlignedBorrow)] @@ -144,88 +124,187 @@ impl VmAdapterAir for AluNativeAdapterAir { } } -impl VmAdapterChip for AluNativeAdapterChip { - type ReadRecord = NativeReadRecord; - type WriteRecord = NativeWriteRecord; - type Air = AluNativeAdapterAir; - type Interface = BasicAdapterInterface, 2, 1, 1, 1>; +#[derive(derive_new::new)] +pub struct AluNativeAdapterStep; + +impl AdapterTraceStep for AluNativeAdapterStep +where + F: PrimeField32, +{ + const WIDTH: usize = size_of::>(); + type ReadData = [F; 2]; + type WriteData = [F; 1]; + type TraceContext<'a> = &'a BitwiseOperationLookupChip; + + #[inline(always)] + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let adapter_row: &mut AluNativeAdapterCols = adapter_row.borrow_mut(); + + adapter_row.from_state.pc = F::from_canonical_u32(pc); + adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + } - fn preprocess( - &mut self, - memory: &mut MemoryController, + #[inline(always)] + fn read( + memory: &mut TracingMemory, instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { - let Instruction { b, c, e, f, .. } = *instruction; - - let reads = vec![memory.read::(e, b), memory.read::(f, c)]; - let i_reads: [_; 2] = std::array::from_fn(|i| reads[i].1); - - Ok(( - i_reads, - Self::ReadRecord { - reads: reads.try_into().unwrap(), - }, - )) + adapter_row: &mut [F], + ) -> Self::ReadData { + let Instruction { b, c, e, f, .. } = instruction; + + let adapter_row: &mut AluNativeAdapterCols = adapter_row.borrow_mut(); + + let read1 = tracing_read_or_imm( + memory, + e.as_canonical_u32(), + b.as_canonical_u32(), + &mut adapter_row.e_as, + (&mut adapter_row.b_pointer, &mut adapter_row.reads_aux[0]), + ); + let read2 = tracing_read_or_imm( + memory, + f.as_canonical_u32(), + c.as_canonical_u32(), + &mut adapter_row.f_as, + (&mut adapter_row.c_pointer, &mut adapter_row.reads_aux[1]), + ); + [read1, read2] } - fn postprocess( - &mut self, - memory: &mut MemoryController, - _instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - _read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - let Instruction { a, .. } = *_instruction; - let writes = vec![memory.write( - F::from_canonical_u32(AS::Native as u32), - a, - &output.writes[0], - )]; - - Ok(( - ExecutionState { - pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), - timestamp: memory.timestamp(), - }, - Self::WriteRecord { - from_state, - writes: writes.try_into().unwrap(), - }, - )) + #[inline(always)] + fn write( + memory: &mut TracingMemory, + instruction: &Instruction, + adapter_row: &mut [F], + data: &Self::WriteData, + ) { + let Instruction { a, .. } = instruction; + + let adapter_row: &mut AluNativeAdapterCols = adapter_row.borrow_mut(); + tracing_write( + memory, + a.as_canonical_u32(), + data, + (&mut adapter_row.a_pointer, &mut adapter_row.write_aux), + ); } - fn generate_trace_row( - &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, + #[inline(always)] + fn fill_trace_row( + mem_helper: &MemoryAuxColsFactory, + bitwise_lookup_chip: &BitwiseOperationLookupChip, + adapter_row: &mut [F], ) { - let row_slice: &mut AluNativeAdapterCols<_> = row_slice.borrow_mut(); - let aux_cols_factory = memory.aux_cols_factory(); + todo!("Implement fill_trace_row") + } +} - row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); +impl AdapterExecutorE1 for AluNativeAdapterStep +where + Mem: GuestMemory, + F: PrimeField32, +{ + type ReadData = (F, F); + type WriteData = F; - row_slice.a_pointer = memory.record_by_id(write_record.writes[0].0).pointer; - row_slice.b_pointer = memory.record_by_id(read_record.reads[0].0).pointer; - row_slice.c_pointer = memory.record_by_id(read_record.reads[1].0).pointer; - row_slice.e_as = memory.record_by_id(read_record.reads[0].0).address_space; - row_slice.f_as = memory.record_by_id(read_record.reads[1].0).address_space; + fn read(memory: &mut Mem, instruction: &Instruction) -> Self::ReadData { + let Instruction { b, c, e, f, .. } = instruction; - for (i, x) in read_record.reads.iter().enumerate() { - let read = memory.record_by_id(x.0); - aux_cols_factory.generate_read_or_immediate_aux(read, &mut row_slice.reads_aux[i]); - } + let [read1]: [F; 1] = unsafe { memory.read(e.as_canonical_u32(), b.as_canonical_u32()) }; + let [read2]: [F; 1] = unsafe { memory.read(f.as_canonical_u32(), c.as_canonical_u32()) }; - let write = memory.record_by_id(write_record.writes[0].0); - aux_cols_factory.generate_write_aux(write, &mut row_slice.write_aux); + (read1, read2) } - fn air(&self) -> &Self::Air { - &self.air + fn write(memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) { + let Instruction { a, .. } = instruction; + + unsafe { memory.write(AS::Native, a.as_canonical_u32(), &[data]) }; } } + +// impl VmAdapterChip for AluNativeAdapterChip { +// type ReadRecord = NativeReadRecord; +// type WriteRecord = NativeWriteRecord; +// type Air = AluNativeAdapterAir; +// type Interface = BasicAdapterInterface, 2, 1, 1, 1>; + +// fn preprocess( +// &mut self, +// memory: &mut MemoryController, +// instruction: &Instruction, +// ) -> Result<( +// >::Reads, +// Self::ReadRecord, +// )> { +// let Instruction { b, c, e, f, .. } = *instruction; + +// let reads = vec![memory.read::(e, b), memory.read::(f, c)]; +// let i_reads: [_; 2] = std::array::from_fn(|i| reads[i].1); + +// Ok(( +// i_reads, +// Self::ReadRecord { +// reads: reads.try_into().unwrap(), +// }, +// )) +// } + +// fn postprocess( +// &mut self, +// memory: &mut MemoryController, +// _instruction: &Instruction, +// from_state: ExecutionState, +// output: AdapterRuntimeContext, +// _read_record: &Self::ReadRecord, +// ) -> Result<(ExecutionState, Self::WriteRecord)> { +// let Instruction { a, .. } = *_instruction; +// let writes = vec![memory.write( +// F::from_canonical_u32(AS::Native as u32), +// a, +// &output.writes[0], +// )]; + +// Ok(( +// ExecutionState { +// pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), +// timestamp: memory.timestamp(), +// }, +// Self::WriteRecord { +// from_state, +// writes: writes.try_into().unwrap(), +// }, +// )) +// } + +// fn generate_trace_row( +// &self, +// row_slice: &mut [F], +// read_record: Self::ReadRecord, +// write_record: Self::WriteRecord, +// memory: &OfflineMemory, +// ) { +// let row_slice: &mut AluNativeAdapterCols<_> = row_slice.borrow_mut(); +// let aux_cols_factory = memory.aux_cols_factory(); + +// row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); + +// row_slice.a_pointer = memory.record_by_id(write_record.writes[0].0).pointer; +// row_slice.b_pointer = memory.record_by_id(read_record.reads[0].0).pointer; +// row_slice.c_pointer = memory.record_by_id(read_record.reads[1].0).pointer; +// row_slice.e_as = memory.record_by_id(read_record.reads[0].0).address_space; +// row_slice.f_as = memory.record_by_id(read_record.reads[1].0).address_space; + +// for (i, x) in read_record.reads.iter().enumerate() { +// let read = memory.record_by_id(x.0); +// aux_cols_factory.generate_read_or_immediate_aux(read, &mut row_slice.reads_aux[i]); +// } + +// let write = memory.record_by_id(write_record.writes[0].0); +// aux_cols_factory.generate_write_aux(write, &mut row_slice.write_aux); +// } + +// fn air(&self) -> &Self::Air { +// &self.air +// } +// } diff --git a/extensions/native/circuit/src/adapters/branch_native_adapter.rs b/extensions/native/circuit/src/adapters/branch_native_adapter.rs index ebc1152b8f..3eb42eec03 100644 --- a/extensions/native/circuit/src/adapters/branch_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/branch_native_adapter.rs @@ -5,9 +5,9 @@ use std::{ use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge, - ExecutionBus, ExecutionState, ImmInstruction, Result, VmAdapterAir, VmAdapterChip, - VmAdapterInterface, + AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, AdapterTraceStep, + BasicAdapterInterface, ExecutionBridge, ExecutionBus, ExecutionState, ImmInstruction, + Result, VmAdapterAir, VmAdapterChip, VmAdapterInterface, }, system::{ memory::{ @@ -27,28 +27,6 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, }; -#[derive(Debug)] -pub struct BranchNativeAdapterChip { - pub air: BranchNativeAdapterAir, - _marker: PhantomData, -} - -impl BranchNativeAdapterChip { - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - ) -> Self { - Self { - air: BranchNativeAdapterAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - }, - _marker: PhantomData, - } - } -} - #[repr(C)] #[derive(AlignedBorrow)] pub struct BranchNativeAdapterReadCols { @@ -145,71 +123,163 @@ impl VmAdapterAir for BranchNativeAdapterAir { } } -impl VmAdapterChip for BranchNativeAdapterChip { - type ReadRecord = NativeReadRecord; - type WriteRecord = ExecutionState; - type Air = BranchNativeAdapterAir; - type Interface = BasicAdapterInterface, 2, 0, 1, 1>; +#[derive(derive_new::new)] +pub struct BranchNativeAdapterStep; + +impl AdapterTraceStep for BranchNativeAdapterStep +where + F: PrimeField32, +{ + const WIDTH: usize = size_of::>(); + type ReadData = [F; 2]; + type WriteData = (); + // TODO(ayush): what's this? + type TraceContext<'a> = &'a BitwiseOperationLookupChip; - fn preprocess( - &mut self, - memory: &mut MemoryController, + #[inline(always)] + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let adapter_row: &mut BranchNativeAdapterCols = adapter_row.borrow_mut(); + + adapter_row.from_state.pc = F::from_canonical_u32(pc); + adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + } + + #[inline(always)] + fn read( + memory: &mut TracingMemory, instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { - let Instruction { a, b, d, e, .. } = *instruction; - - let reads = vec![memory.read::(d, a), memory.read::(e, b)]; - let i_reads: [_; 2] = std::array::from_fn(|i| reads[i].1); - - Ok(( - i_reads, - Self::ReadRecord { - reads: reads.try_into().unwrap(), - }, - )) + adapter_row: &mut [F], + ) -> Self::ReadData { + let Instruction { b, c, e, f, .. } = instruction; + let adapter_row: &mut BranchNativeAdapterCols = adapter_row.borrow_mut(); + + let read1 = tracing_read_or_imm( + memory, + d.as_canonical_u32(), + a.as_canonical_u32(), + &mut adapter_row.reads_aux[0].address.address_space, + ( + &mut adapter_row.reads_aux[0].address.pointer, + &mut adapter_row.reads_aux[0].read_aux, + ), + ); + let read2 = tracing_read_or_imm( + memory, + e.as_canonical_u32(), + b.as_canonical_u32(), + &mut adapter_row.reads_aux[1].address.address_space, + ( + &mut adapter_row.reads_aux[1].address.pointer, + &mut adapter_row.reads_aux[1].read_aux, + ), + ); + [read1, read2] } - fn postprocess( - &mut self, - memory: &mut MemoryController, - _instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - _read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - Ok(( - ExecutionState { - pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), - timestamp: memory.timestamp(), - }, - from_state, - )) + #[inline(always)] + fn write( + memory: &mut TracingMemory, + instruction: &Instruction, + adapter_row: &mut [F], + data: &Self::WriteData, + ) { } - fn generate_trace_row( - &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, + #[inline(always)] + fn fill_trace_row( + mem_helper: &MemoryAuxColsFactory, + bitwise_lookup_chip: &BitwiseOperationLookupChip, + adapter_row: &mut [F], ) { - let row_slice: &mut BranchNativeAdapterCols<_> = row_slice.borrow_mut(); - let aux_cols_factory = memory.aux_cols_factory(); + todo!("Implement fill_trace_row") + } +} - row_slice.from_state = write_record.map(F::from_canonical_u32); - for (i, x) in read_record.reads.iter().enumerate() { - let read = memory.record_by_id(x.0); +impl AdapterExecutorE1 for BranchNativeAdapterStep +where + Mem: GuestMemory, + F: PrimeField32, +{ + type ReadData = (F, F); + type WriteData = (); - row_slice.reads_aux[i].address = MemoryAddress::new(read.address_space, read.pointer); - aux_cols_factory - .generate_read_or_immediate_aux(read, &mut row_slice.reads_aux[i].read_aux); - } - } + fn read(memory: &mut Mem, instruction: &Instruction) -> Self::ReadData { + let Instruction { a, b, d, e, .. } = instruction; + + let read1 = unsafe { memory.read(d.as_canonical_u32(), a.as_canonical_u32()) }; + let read2 = unsafe { memory.read(e.as_canonical_u32(), b.as_canonical_u32()) }; - fn air(&self) -> &Self::Air { - &self.air + (read1, read2) } + + fn write(_memory: &mut Mem, _instruction: &Instruction, _data: &Self::WriteData) {} } + +// impl VmAdapterChip for BranchNativeAdapterChip { +// type ReadRecord = NativeReadRecord; +// type WriteRecord = ExecutionState; +// type Air = BranchNativeAdapterAir; +// type Interface = BasicAdapterInterface, 2, 0, 1, 1>; + +// fn preprocess( +// &mut self, +// memory: &mut MemoryController, +// instruction: &Instruction, +// ) -> Result<( +// >::Reads, +// Self::ReadRecord, +// )> { +// let Instruction { a, b, d, e, .. } = *instruction; + +// let reads = vec![memory.read::(d, a), memory.read::(e, b)]; +// let i_reads: [_; 2] = std::array::from_fn(|i| reads[i].1); + +// Ok(( +// i_reads, +// Self::ReadRecord { +// reads: reads.try_into().unwrap(), +// }, +// )) +// } + +// fn postprocess( +// &mut self, +// memory: &mut MemoryController, +// _instruction: &Instruction, +// from_state: ExecutionState, +// output: AdapterRuntimeContext, +// _read_record: &Self::ReadRecord, +// ) -> Result<(ExecutionState, Self::WriteRecord)> { +// Ok(( +// ExecutionState { +// pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), +// timestamp: memory.timestamp(), +// }, +// from_state, +// )) +// } + +// fn generate_trace_row( +// &self, +// row_slice: &mut [F], +// read_record: Self::ReadRecord, +// write_record: Self::WriteRecord, +// memory: &OfflineMemory, +// ) { +// let row_slice: &mut BranchNativeAdapterCols<_> = row_slice.borrow_mut(); +// let aux_cols_factory = memory.aux_cols_factory(); + +// row_slice.from_state = write_record.map(F::from_canonical_u32); +// for (i, x) in read_record.reads.iter().enumerate() { +// let read = memory.record_by_id(x.0); + +// row_slice.reads_aux[i].address = MemoryAddress::new(read.address_space, read.pointer); +// aux_cols_factory +// .generate_read_or_immediate_aux(read, &mut row_slice.reads_aux[i].read_aux); +// } +// } + +// fn air(&self) -> &Self::Air { +// &self.air +// } +// } diff --git a/extensions/native/circuit/src/adapters/convert_adapter.rs b/extensions/native/circuit/src/adapters/convert_adapter.rs index 2afb1c7b4b..9002aba480 100644 --- a/extensions/native/circuit/src/adapters/convert_adapter.rs +++ b/extensions/native/circuit/src/adapters/convert_adapter.rs @@ -5,13 +5,14 @@ use std::{ use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge, - ExecutionBus, ExecutionState, MinimalInstruction, Result, VmAdapterAir, VmAdapterChip, - VmAdapterInterface, + AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, AdapterTraceStep, + BasicAdapterInterface, ExecutionBridge, ExecutionBus, ExecutionState, MinimalInstruction, + Result, VmAdapterAir, VmAdapterChip, VmAdapterInterface, }, system::{ memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, + online::GuestMemory, MemoryAddress, MemoryController, OfflineMemory, RecordId, }, program::ProgramBus, @@ -42,31 +43,6 @@ pub struct VectorWriteRecord { pub writes: [RecordId; 1], } -#[allow(dead_code)] -#[derive(Debug)] -pub struct ConvertAdapterChip { - pub air: ConvertAdapterAir, - _marker: PhantomData, -} - -impl - ConvertAdapterChip -{ - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - ) -> Self { - Self { - air: ConvertAdapterAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - }, - _marker: PhantomData, - } - } -} - #[repr(C)] #[derive(AlignedBorrow)] pub struct ConvertAdapterCols { @@ -155,74 +131,172 @@ impl Vm } } -impl VmAdapterChip - for ConvertAdapterChip +#[derive(derive_new::new)] +pub struct ConvertAdapterStep; + +impl AdapterTraceStep + for ConvertAdapterStep +where + F: PrimeField32, { - type ReadRecord = VectorReadRecord<1, READ_SIZE>; - type WriteRecord = VectorWriteRecord; - type Air = ConvertAdapterAir; - type Interface = BasicAdapterInterface, 1, 1, READ_SIZE, WRITE_SIZE>; - - fn preprocess( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { - let Instruction { b, e, .. } = *instruction; + const WIDTH: usize = size_of::>(); + type ReadData = [F; READ_SIZE]; + type WriteData = [F; WRITE_SIZE]; + // TODO(ayush): what's this? + type TraceContext<'a> = &'a BitwiseOperationLookupChip; - let y_val = memory.read::(e, b); + #[inline(always)] + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let adapter_row: &mut ConvertAdapterCols = adapter_row.borrow_mut(); - Ok(([y_val.1], Self::ReadRecord { reads: [y_val.0] })) + adapter_row.from_state.pc = F::from_canonical_u32(pc); + adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); } - fn postprocess( - &mut self, - memory: &mut MemoryController, + #[inline(always)] + fn read( + memory: &mut TracingMemory, instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - _read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - let Instruction { a, d, .. } = *instruction; - let (write_id, _) = memory.write::(d, a, &output.writes[0]); - - Ok(( - ExecutionState { - pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), - timestamp: memory.timestamp(), - }, - Self::WriteRecord { - from_state, - writes: [write_id], - }, - )) + adapter_row: &mut [F], + ) -> Self::ReadData { + let Instruction { b, c, e, f, .. } = instruction; + let adapter_row: &mut ConvertAdapterCols = adapter_row.borrow_mut(); + + // TODO(ayush): create similar `tracing_read_reg_or_imm` for F + let read = tracing_read_reg_or_imm( + memory, + e.as_canonical_u32(), + b.as_canonical_u32(), + // TODO(ayush): why no address space pointer? Should this be hardcoded? + &mut adapter_row.e_as, + (&mut adapter_row.b_pointer, &mut adapter_row.reads_aux[0]), + ); + read } - fn generate_trace_row( - &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, + #[inline(always)] + fn write( + memory: &mut TracingMemory, + instruction: &Instruction, + adapter_row: &mut [F], + data: &Self::WriteData, ) { - let aux_cols_factory = memory.aux_cols_factory(); - let row_slice: &mut ConvertAdapterCols<_, READ_SIZE, WRITE_SIZE> = row_slice.borrow_mut(); + let Instruction { a, d, .. } = instruction; + let adapter_row: &mut ConvertAdapterCols = adapter_row.borrow_mut(); - let read = memory.record_by_id(read_record.reads[0]); - let write = memory.record_by_id(write_record.writes[0]); + // TODO(ayush): create similar `tracing_read_reg_or_imm` for F + tracing_write_reg( + memory, + d.as_canonical_u32(), + a.as_canonical_u32(), + data, + (&mut adapter_row.a_pointer, &mut adapter_row.write_aux[0]), + ); + } - row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); - row_slice.a_pointer = write.pointer; - row_slice.b_pointer = read.pointer; + #[inline(always)] + fn fill_trace_row( + mem_helper: &MemoryAuxColsFactory, + bitwise_lookup_chip: &BitwiseOperationLookupChip, + adapter_row: &mut [F], + ) { + todo!("Implement fill_trace_row") + } +} - aux_cols_factory.generate_read_aux(read, &mut row_slice.reads_aux[0]); - aux_cols_factory.generate_write_aux(write, &mut row_slice.writes_aux[0]); +impl AdapterExecutorE1 + for ConvertAdapterStep +where + Mem: GuestMemory, + F: PrimeField32, +{ + type ReadData = [F; READ_SIZE]; + type WriteData = [F; WRITE_SIZE]; + + fn read(memory: &mut Mem, instruction: &Instruction) -> Self::ReadData { + let Instruction { b, e, .. } = instruction; + + let read = unsafe { memory.read(e.as_canonical_u32(), b.as_canonical_u32()) }; + read } - fn air(&self) -> &Self::Air { - &self.air + fn write(memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) { + let Instruction { a, d, .. } = instruction; + + unsafe { + memory.write::(d.as_canonical_u32(), a.as_cas_canonical_u32(), &data) + }; } } + +// impl VmAdapterChip +// for ConvertAdapterChip +// { +// type ReadRecord = VectorReadRecord<1, READ_SIZE>; +// type WriteRecord = VectorWriteRecord; +// type Air = ConvertAdapterAir; +// type Interface = BasicAdapterInterface, 1, 1, READ_SIZE, WRITE_SIZE>; + +// fn preprocess( +// &mut self, +// memory: &mut MemoryController, +// instruction: &Instruction, +// ) -> Result<( +// >::Reads, +// Self::ReadRecord, +// )> { +// let Instruction { b, e, .. } = *instruction; + +// let y_val = memory.read::(e, b); + +// Ok(([y_val.1], Self::ReadRecord { reads: [y_val.0] })) +// } + +// fn postprocess( +// &mut self, +// memory: &mut MemoryController, +// instruction: &Instruction, +// from_state: ExecutionState, +// output: AdapterRuntimeContext, +// _read_record: &Self::ReadRecord, +// ) -> Result<(ExecutionState, Self::WriteRecord)> { +// let Instruction { a, d, .. } = *instruction; +// let (write_id, _) = memory.write::(d, a, &output.writes[0]); + +// Ok(( +// ExecutionState { +// pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), +// timestamp: memory.timestamp(), +// }, +// Self::WriteRecord { +// from_state, +// writes: [write_id], +// }, +// )) +// } + +// fn generate_trace_row( +// &self, +// row_slice: &mut [F], +// read_record: Self::ReadRecord, +// write_record: Self::WriteRecord, +// memory: &OfflineMemory, +// ) { +// let aux_cols_factory = memory.aux_cols_factory(); +// let row_slice: &mut ConvertAdapterCols<_, READ_SIZE, WRITE_SIZE> = row_slice.borrow_mut(); + +// let read = memory.record_by_id(read_record.reads[0]); +// let write = memory.record_by_id(write_record.writes[0]); + +// row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); +// row_slice.a_pointer = write.pointer; +// row_slice.b_pointer = read.pointer; + +// aux_cols_factory.generate_read_aux(read, &mut row_slice.reads_aux[0]); +// aux_cols_factory.generate_write_aux(write, &mut row_slice.writes_aux[0]); +// } + +// fn air(&self) -> &Self::Air { +// &self.air +// } +// } diff --git a/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs b/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs index d2c6e24676..4709fe2120 100644 --- a/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs @@ -5,8 +5,9 @@ use std::{ use openvm_circuit::{ arch::{ - instructions::LocalOpcode, AdapterAirContext, AdapterRuntimeContext, ExecutionBridge, - ExecutionBus, ExecutionState, Result, VmAdapterAir, VmAdapterChip, VmAdapterInterface, + instructions::LocalOpcode, AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, + ExecutionBridge, ExecutionBus, ExecutionState, Result, VmAdapterAir, VmAdapterChip, + VmAdapterInterface, }, system::{ memory::{ @@ -48,31 +49,6 @@ impl VmAdapterInterface type ProcessedInstruction = NativeLoadStoreInstruction; } -#[derive(Debug)] -pub struct NativeLoadStoreAdapterChip { - pub air: NativeLoadStoreAdapterAir, - offset: usize, - _marker: PhantomData, -} - -impl NativeLoadStoreAdapterChip { - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - offset: usize, - ) -> Self { - Self { - air: NativeLoadStoreAdapterAir { - memory_bridge, - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - }, - offset, - _marker: PhantomData, - } - } -} - #[repr(C)] #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(bound = "F: Field")] @@ -214,22 +190,69 @@ impl VmAdapterAir } } -impl VmAdapterChip - for NativeLoadStoreAdapterChip +#[derive(derive_new::new)] +pub struct NativeLoadStoreAdapterStep { + offset: usize, +} + +impl AdapterTraceStep + for NativeLoadStoreAdapterStep +where + F: PrimeField32, { - type ReadRecord = NativeLoadStoreReadRecord; - type WriteRecord = NativeLoadStoreWriteRecord; - type Air = NativeLoadStoreAdapterAir; - type Interface = NativeLoadStoreAdapterInterface; - - fn preprocess( - &mut self, - memory: &mut MemoryController, + const WIDTH: usize = size_of::>(); + type ReadData = (F, [F; NUM_CELLS]); + type WriteData = [F; NUM_CELLS]; + // TODO(ayush): what's this? + type TraceContext<'a> = &'a BitwiseOperationLookupChip; + + #[inline(always)] + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let adapter_row: &mut NativeLoadStoreAdapterCols = adapter_row.borrow_mut(); + + adapter_row.from_state.pc = F::from_canonical_u32(pc); + adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + } + + #[inline(always)] + fn read( + memory: &mut TracingMemory, + instruction: &Instruction, + adapter_row: &mut [F], + ) -> Self::ReadData { + todo!("Implement read method"); + } + + #[inline(always)] + fn write( + memory: &mut TracingMemory, instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { + adapter_row: &mut [F], + data: &Self::WriteData, + ) { + todo!("Implement write method"); + } + + #[inline(always)] + fn fill_trace_row( + mem_helper: &MemoryAuxColsFactory, + bitwise_lookup_chip: &BitwiseOperationLookupChip, + adapter_row: &mut [F], + ) { + todo!("Implement fill_trace_row") + } +} + +impl AdapterExecutorE1 + for NativeLoadStoreAdapterStep +where + Mem: GuestMemory, + F: PrimeField32, +{ + type ReadData = (F, [F; NUM_CELLS]); + type WriteData = [F; NUM_CELLS]; + + fn read(memory: &mut Mem, instruction: &Instruction) -> Self::ReadData { let Instruction { opcode, a, @@ -238,12 +261,12 @@ impl VmAdapterChip d, e, .. - } = *instruction; + } = instruction; + // TODO(ayush): how to handle self.offset? let local_opcode = NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let read_as = d; - let read_ptr = c; - let read_cell = memory.read_cell(read_as, read_ptr); + let [read_cell]: [F; 1] = + unsafe { memory.read(d.as_canonical_u32(), c.as_canonical_u32()) }; let (data_read_as, data_write_as) = { match local_opcode { @@ -253,88 +276,184 @@ impl VmAdapterChip }; let (data_read_ptr, data_write_ptr) = { match local_opcode { - LOADW => (read_cell.1 + b, a), - STOREW | HINT_STOREW => (a, read_cell.1 + b), + LOADW => (read_cell + b, a), + STOREW | HINT_STOREW => (a, read_cell + b), } }; - let data_read = match local_opcode { - HINT_STOREW => None, - LOADW | STOREW => Some(memory.read::(data_read_as, data_read_ptr)), + let data_read: [F; NUM_CELLS] = match local_opcode { + HINT_STOREW => [F::ZERO; NUM_CELLS], + LOADW | STOREW => unsafe { + memory.read( + data_read_as.as_canonical_u32(), + data_read_ptr.as_canonical_u32(), + ) + }, }; - let record = NativeLoadStoreReadRecord { - pointer_read: read_cell.0, - data_read: data_read.map(|x| x.0), - write_as: data_write_as, - write_ptr: data_write_ptr, + + (read_cell, data_read) + } + + fn write(memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) { + let Instruction { + opcode, a, b, c, d, e, - }; - - Ok(( - (read_cell.1, data_read.map_or([F::ZERO; NUM_CELLS], |x| x.1)), - record, - )) - } + .. + } = instruction; + // TODO(ayush): how to handle self.offset? + let local_opcode = NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - fn postprocess( - &mut self, - memory: &mut MemoryController, - _instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - let (write_id, _) = memory.write::( - read_record.write_as, - read_record.write_ptr, - &output.writes, - ); - Ok(( - ExecutionState { - pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), - timestamp: memory.timestamp(), - }, - Self::WriteRecord { - from_state: from_state.map(F::from_canonical_u32), - write_id, - }, - )) - } + let [read_cell]: [F; 1] = + unsafe { memory.read(d.as_canonical_u32(), c.as_canonical_u32()) }; - fn generate_trace_row( - &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, - ) { - let aux_cols_factory = memory.aux_cols_factory(); - let cols: &mut NativeLoadStoreAdapterCols<_, NUM_CELLS> = row_slice.borrow_mut(); - cols.from_state = write_record.from_state; - cols.a = read_record.a; - cols.b = read_record.b; - cols.c = read_record.c; - - let data_read = read_record.data_read.map(|read| memory.record_by_id(read)); - if let Some(data_read) = data_read { - aux_cols_factory.generate_read_aux(data_read, &mut cols.data_read_aux_cols); - } - - let write = memory.record_by_id(write_record.write_id); - cols.data_write_pointer = write.pointer; - - aux_cols_factory.generate_read_aux( - memory.record_by_id(read_record.pointer_read), - &mut cols.pointer_read_aux_cols, - ); - aux_cols_factory.generate_write_aux(write, &mut cols.data_write_aux_cols); - } + let (data_read_as, data_write_as) = { + match local_opcode { + LOADW => (e, d), + STOREW | HINT_STOREW => (d, e), + } + }; + let (data_read_ptr, data_write_ptr) = { + match local_opcode { + LOADW => (read_cell + b, a), + STOREW | HINT_STOREW => (a, read_cell + b), + } + }; - fn air(&self) -> &Self::Air { - &self.air + unsafe { + memory.write( + data_write_as.as_canonical_u32(), + data_write_ptr.as_cas_canonical_u32(), + &data, + ) + }; } } + +// impl VmAdapterChip +// for NativeLoadStoreAdapterChip +// { +// type ReadRecord = NativeLoadStoreReadRecord; +// type WriteRecord = NativeLoadStoreWriteRecord; +// type Air = NativeLoadStoreAdapterAir; +// type Interface = NativeLoadStoreAdapterInterface; + +// fn preprocess( +// &mut self, +// memory: &mut MemoryController, +// instruction: &Instruction, +// ) -> Result<( +// >::Reads, +// Self::ReadRecord, +// )> { +// let Instruction { +// opcode, +// a, +// b, +// c, +// d, +// e, +// .. +// } = *instruction; +// let local_opcode = NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + +// let read_as = d; +// let read_ptr = c; +// let read_cell = memory.read_cell(read_as, read_ptr); + +// let (data_read_as, data_write_as) = { +// match local_opcode { +// LOADW => (e, d), +// STOREW | HINT_STOREW => (d, e), +// } +// }; +// let (data_read_ptr, data_write_ptr) = { +// match local_opcode { +// LOADW => (read_cell.1 + b, a), +// STOREW | HINT_STOREW => (a, read_cell.1 + b), +// } +// }; + +// let data_read = match local_opcode { +// HINT_STOREW => None, +// LOADW | STOREW => Some(memory.read::(data_read_as, data_read_ptr)), +// }; +// let record = NativeLoadStoreReadRecord { +// pointer_read: read_cell.0, +// data_read: data_read.map(|x| x.0), +// write_as: data_write_as, +// write_ptr: data_write_ptr, +// a, +// b, +// c, +// d, +// e, +// }; + +// Ok(( +// (read_cell.1, data_read.map_or([F::ZERO; NUM_CELLS], |x| x.1)), +// record, +// )) +// } + +// fn postprocess( +// &mut self, +// memory: &mut MemoryController, +// _instruction: &Instruction, +// from_state: ExecutionState, +// output: AdapterRuntimeContext, +// read_record: &Self::ReadRecord, +// ) -> Result<(ExecutionState, Self::WriteRecord)> { +// let (write_id, _) = memory.write::( +// read_record.write_as, +// read_record.write_ptr, +// &output.writes, +// ); +// Ok(( +// ExecutionState { +// pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), +// timestamp: memory.timestamp(), +// }, +// Self::WriteRecord { +// from_state: from_state.map(F::from_canonical_u32), +// write_id, +// }, +// )) +// } + +// fn generate_trace_row( +// &self, +// row_slice: &mut [F], +// read_record: Self::ReadRecord, +// write_record: Self::WriteRecord, +// memory: &OfflineMemory, +// ) { +// let aux_cols_factory = memory.aux_cols_factory(); +// let cols: &mut NativeLoadStoreAdapterCols<_, NUM_CELLS> = row_slice.borrow_mut(); +// cols.from_state = write_record.from_state; +// cols.a = read_record.a; +// cols.b = read_record.b; +// cols.c = read_record.c; + +// let data_read = read_record.data_read.map(|read| memory.record_by_id(read)); +// if let Some(data_read) = data_read { +// aux_cols_factory.generate_read_aux(data_read, &mut cols.data_read_aux_cols); +// } + +// let write = memory.record_by_id(write_record.write_id); +// cols.data_write_pointer = write.pointer; + +// aux_cols_factory.generate_read_aux( +// memory.record_by_id(read_record.pointer_read), +// &mut cols.pointer_read_aux_cols, +// ); +// aux_cols_factory.generate_write_aux(write, &mut cols.data_write_aux_cols); +// } + +// fn air(&self) -> &Self::Air { +// &self.air +// } +// } diff --git a/extensions/native/circuit/src/adapters/mod.rs b/extensions/native/circuit/src/adapters/mod.rs index c5cd3b9422..25c2163b9d 100644 --- a/extensions/native/circuit/src/adapters/mod.rs +++ b/extensions/native/circuit/src/adapters/mod.rs @@ -1,3 +1,10 @@ +use openvm_circuit::system::memory::{ + offline_checker::{MemoryReadAuxCols, MemoryWriteAuxCols}, + online::TracingMemory, +}; +use openvm_native_compiler::conversion::AS; +use openvm_stark_backend::p3_field::PrimeField32; + pub mod alu_native_adapter; // 2 reads, 0 writes, imm support, jump support pub mod branch_native_adapter; @@ -6,3 +13,100 @@ pub mod convert_adapter; pub mod loadstore_native_adapter; // 2 reads, 1 write, read size = write size = N, no imm support, read/write to address space d pub mod native_vectorized_adapter; + +/// Atomic read operation which increments the timestamp by 1. +/// Returns `(t_prev, [ptr:BLOCK_SIZE]_4)` where `t_prev` is the timestamp of the last memory access. +#[inline(always)] +pub fn timed_read( + memory: &mut TracingMemory, + ptr: u32, +) -> (u32, [F; BLOCK_SIZE]) +where + F: PrimeField32, +{ + // SAFETY: + // - address space `Native` will always have cell type `F` and minimum alignment of `1` + unsafe { memory.read::(AS::Native, ptr) } +} + +#[inline(always)] +pub fn timed_write( + memory: &mut TracingMemory, + ptr: u32, + vals: &[F; BLOCK_SIZE], +) -> (u32, [F; BLOCK_SIZE]) +where + F: PrimeField32, +{ + // SAFETY: + // - address space `Native` will always have cell type `F` and minimum alignment of `1` + unsafe { memory.write::(AS::Native, ptr, vals) } +} + +/// Reads register value at `ptr` from memory and records the memory access in mutable buffer. +/// Trace generation relevant to this memory access can be done fully from the recorded buffer. +#[inline(always)] +pub fn tracing_read( + memory: &mut TracingMemory, + ptr: u32, + (ptr_mut, aux_cols): (&mut F, &mut MemoryReadAuxCols), +) -> [F; BLOCK_SIZE] +where + F: PrimeField32, +{ + let (t_prev, data) = timed_read(memory, ptr); + *ptr_mut = F::from_canonical_u32(ptr); + aux_cols.set_prev(F::from_canonical_u32(t_prev)); + data +} + +/// Writes `ptr, vals` into memory and records the memory access in mutable buffer. +/// Trace generation relevant to this memory access can be done fully from the recorded buffer. +#[inline(always)] +pub fn tracing_write( + memory: &mut TracingMemory, + ptr: u32, + vals: &[F; BLOCK_SIZE], + (ptr_mut, aux_cols): (&mut F, &mut MemoryWriteAuxCols), +) where + F: PrimeField32, +{ + let (t_prev, data_prev) = timed_write(memory, ptr, vals); + *ptr_mut = F::from_canonical_u32(ptr); + aux_cols.set_prev(F::from_canonical_u32(t_prev), data_prev); +} + +/// Reads value at `_ptr` from memory and records the memory access in mutable buffer. +/// Trace generation relevant to this memory access can be done fully from the recorded buffer. +/// +/// Assumes that `addr_space` is [Immediate] or [Native]. +#[inline(always)] +pub fn tracing_read_or_imm( + memory: &mut TracingMemory, + addr_space: u32, + ptr_or_imm: u32, + addr_space_mut: &mut F, + (ptr_or_imm_mut, aux_cols): (&mut F, &mut MemoryReadAuxCols), +) -> F +where + F: PrimeField32, +{ + debug_assert!(addr_space == AS::Immediate || addr_space == AS::Native); + + if addr_space == AS::Immediate { + // TODO(ayush): check this + *addr_space_mut = F::ZERO; + let imm = ptr_or_imm; + *ptr_or_imm_mut = F::from_canonical_u32(imm); + debug_assert_eq!(imm >> 24, 0); // highest byte should be zero to prevent overflow + memory.increment_timestamp(); + let mut imm_le = imm.to_le_bytes(); + // Important: we set the highest byte equal to the second highest byte, using the assumption + // that imm is at most 24 bits + imm_le[3] = imm_le[2]; + imm_le + } else { + *addr_space_mut = F::from_canonical_u32(AS::Native); + tracing_read(memory, ptr_or_imm, (ptr_or_imm_mut, aux_cols)) + } +} diff --git a/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs b/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs index b04b17ce2b..e362532721 100644 --- a/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs +++ b/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs @@ -5,9 +5,9 @@ use std::{ use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge, - ExecutionBus, ExecutionState, MinimalInstruction, Result, VmAdapterAir, VmAdapterChip, - VmAdapterInterface, + AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, AdapterTraceStep, + BasicAdapterInterface, ExecutionBridge, ExecutionBus, ExecutionState, MinimalInstruction, + Result, VmAdapterAir, VmAdapterChip, VmAdapterInterface, }, system::{ memory::{ @@ -27,29 +27,6 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -#[allow(dead_code)] -#[derive(Debug)] -pub struct NativeVectorizedAdapterChip { - pub air: NativeVectorizedAdapterAir, - _marker: PhantomData, -} - -impl NativeVectorizedAdapterChip { - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - ) -> Self { - Self { - air: NativeVectorizedAdapterAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - }, - _marker: PhantomData, - } - } -} - #[repr(C)] #[derive(Debug, Serialize, Deserialize)] pub struct NativeVectorizedReadRecord { @@ -156,80 +133,186 @@ impl VmAdapterAir for NativeVectoriz } } -impl VmAdapterChip for NativeVectorizedAdapterChip { - type ReadRecord = NativeVectorizedReadRecord; - type WriteRecord = NativeVectorizedWriteRecord; - type Air = NativeVectorizedAdapterAir; - type Interface = BasicAdapterInterface, 2, 1, N, N>; +#[derive(derive_new::new)] +pub struct NativeVectorizedAdapterChip; + +impl AdapterTraceStep for NativeVectorizedAdapterChip +where + F: PrimeField32, +{ + const WIDTH: usize = size_of::>(); + type ReadData = [[F; N]; 2]; + type WriteData = [F; N]; + // TODO(ayush): what's this? + type TraceContext<'a> = &'a BitwiseOperationLookupChip; - fn preprocess( - &mut self, - memory: &mut MemoryController, + #[inline(always)] + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let adapter_row: &mut NativeVectorizedAdapterCols = adapter_row.borrow_mut(); + + adapter_row.from_state.pc = F::from_canonical_u32(pc); + adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + } + + #[inline(always)] + fn read( + memory: &mut TracingMemory, instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { - let Instruction { b, c, d, e, .. } = *instruction; - - let y_val = memory.read::(d, b); - let z_val = memory.read::(e, c); - - Ok(( - [y_val.1, z_val.1], - Self::ReadRecord { - b: y_val.0, - c: z_val.0, - }, - )) + adapter_row: &mut [F], + ) -> Self::ReadData { + let Instruction { b, c, e, f, .. } = instruction; + let adapter_row: &mut NativeVectorizedAdapterCols = adapter_row.borrow_mut(); + + // TODO(ayush): create similar `tracing_read_reg_or_imm` for F + let y_val = tracing_read_reg_or_imm( + memory, + d.as_canonical_u32(), + b.as_canonical_u32(), + // TODO(ayush): why no address space pointer? Should this be hardcoded? + &mut adapter_row.d_as, + (&mut adapter_row.b_pointer, &mut adapter_row.reads_aux[0]), + ); + let z_val = tracing_read_reg_or_imm( + memory, + e.as_canonical_u32(), + c.as_canonical_u32(), + // TODO(ayush): why no address space pointer? Should this be hardcoded? + &mut adapter_row.e_as, + (&mut adapter_row.c_pointer, &mut adapter_row.reads_aux[1]), + ); + + [y_val, z_val] } - fn postprocess( - &mut self, - memory: &mut MemoryController, + #[inline(always)] + fn write( + memory: &mut TracingMemory, instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - _read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - let Instruction { a, d, .. } = *instruction; - let (a_val, _) = memory.write(d, a, &output.writes[0]); - - Ok(( - ExecutionState { - pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), - timestamp: memory.timestamp(), - }, - Self::WriteRecord { - from_state, - a: a_val, - }, - )) + adapter_row: &mut [F], + data: &Self::WriteData, + ) { + let Instruction { a, d, .. } = instruction; + let adapter_row: &mut NativeVectorizedAdapterCols = adapter_row.borrow_mut(); + + // TODO(ayush): create similar `tracing_read_reg_or_imm` for F + tracing_write_reg( + memory, + d.as_canonical_u32(), + a.as_canonical_u32(), + data, + (&mut adapter_row.a_pointer, &mut adapter_row.writes_aux[0]), + ); } - fn generate_trace_row( - &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, + #[inline(always)] + fn fill_trace_row( + mem_helper: &MemoryAuxColsFactory, + bitwise_lookup_chip: &BitwiseOperationLookupChip, + adapter_row: &mut [F], ) { - let aux_cols_factory = memory.aux_cols_factory(); - let row_slice: &mut NativeVectorizedAdapterCols<_, N> = row_slice.borrow_mut(); - - let b_record = memory.record_by_id(read_record.b); - let c_record = memory.record_by_id(read_record.c); - let a_record = memory.record_by_id(write_record.a); - row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); - row_slice.a_pointer = a_record.pointer; - row_slice.b_pointer = b_record.pointer; - row_slice.c_pointer = c_record.pointer; - aux_cols_factory.generate_read_aux(b_record, &mut row_slice.reads_aux[0]); - aux_cols_factory.generate_read_aux(c_record, &mut row_slice.reads_aux[1]); - aux_cols_factory.generate_write_aux(a_record, &mut row_slice.writes_aux[0]); + todo!("Implement fill_trace_row") + } +} + +impl AdapterExecutorE1 for NativeVectorizedAdapterChip +where + Mem: GuestMemory, + F: PrimeField32, +{ + type ReadData = ([F; N], [F; N]); + type WriteData = [F; N]; + + fn read(memory: &mut Mem, instruction: &Instruction) -> Self::ReadData { + let Instruction { b, c, d, e, .. } = instruction; + + let y_val: [F; N] = unsafe { memory.read(d.as_canonical_u32(), b.as_cas_canonical_u32()) }; + let z_val: [F; N] = + unsafe { memory.read(e.as_cas_canonical_u32(), c.as_cas_canonical_u32()) }; + + (y_val, z_val) } - fn air(&self) -> &Self::Air { - &self.air + fn write(memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) { + let Instruction { a, d, .. } = instruction; + + unsafe { memory.write(d.as_canonical_u32(), a.as_cas_canonical_u32(), &data) }; } } + +// impl VmAdapterChip for NativeVectorizedAdapterChip { +// type ReadRecord = NativeVectorizedReadRecord; +// type WriteRecord = NativeVectorizedWriteRecord; +// type Air = NativeVectorizedAdapterAir; +// type Interface = BasicAdapterInterface, 2, 1, N, N>; + +// fn preprocess( +// &mut self, +// memory: &mut MemoryController, +// instruction: &Instruction, +// ) -> Result<( +// >::Reads, +// Self::ReadRecord, +// )> { +// let Instruction { b, c, d, e, .. } = *instruction; + +// let y_val = memory.read::(d, b); +// let z_val = memory.read::(e, c); + +// Ok(( +// [y_val.1, z_val.1], +// Self::ReadRecord { +// b: y_val.0, +// c: z_val.0, +// }, +// )) +// } + +// fn postprocess( +// &mut self, +// memory: &mut MemoryController, +// instruction: &Instruction, +// from_state: ExecutionState, +// output: AdapterRuntimeContext, +// _read_record: &Self::ReadRecord, +// ) -> Result<(ExecutionState, Self::WriteRecord)> { +// let Instruction { a, d, .. } = *instruction; +// let (a_val, _) = memory.write(d, a, &output.writes[0]); + +// Ok(( +// ExecutionState { +// pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), +// timestamp: memory.timestamp(), +// }, +// Self::WriteRecord { +// from_state, +// a: a_val, +// }, +// )) +// } + +// fn generate_trace_row( +// &self, +// row_slice: &mut [F], +// read_record: Self::ReadRecord, +// write_record: Self::WriteRecord, +// memory: &OfflineMemory, +// ) { +// let aux_cols_factory = memory.aux_cols_factory(); +// let row_slice: &mut NativeVectorizedAdapterCols<_, N> = row_slice.borrow_mut(); + +// let b_record = memory.record_by_id(read_record.b); +// let c_record = memory.record_by_id(read_record.c); +// let a_record = memory.record_by_id(write_record.a); +// row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); +// row_slice.a_pointer = a_record.pointer; +// row_slice.b_pointer = b_record.pointer; +// row_slice.c_pointer = c_record.pointer; +// aux_cols_factory.generate_read_aux(b_record, &mut row_slice.reads_aux[0]); +// aux_cols_factory.generate_read_aux(c_record, &mut row_slice.reads_aux[1]); +// aux_cols_factory.generate_write_aux(a_record, &mut row_slice.writes_aux[0]); +// } + +// fn air(&self) -> &Self::Air { +// &self.air +// } +// } diff --git a/extensions/native/circuit/src/branch_eq/mod.rs b/extensions/native/circuit/src/branch_eq/mod.rs index e1b566bb7f..265f45b549 100644 --- a/extensions/native/circuit/src/branch_eq/mod.rs +++ b/extensions/native/circuit/src/branch_eq/mod.rs @@ -4,5 +4,6 @@ use openvm_rv32im_circuit::{BranchEqualCoreAir, BranchEqualCoreChip}; use super::adapters::branch_native_adapter::{BranchNativeAdapterAir, BranchNativeAdapterChip}; pub type NativeBranchEqAir = VmAirWrapper>; -pub type NativeBranchEqChip = - VmChipWrapper, BranchEqualCoreChip<1>>; +pub type NativeBranchEqualStep = BranchEqualStep; +pub type NativeBranchEqualChip = + NewVmChipWrapper; diff --git a/extensions/native/circuit/src/castf/core.rs b/extensions/native/circuit/src/castf/core.rs index 92beb1eaf1..7f8628025d 100644 --- a/extensions/native/circuit/src/castf/core.rs +++ b/extensions/native/circuit/src/castf/core.rs @@ -2,8 +2,9 @@ use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, - VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, AdapterTraceStep, + InsExecutorE1, MinimalInstruction, Result, StepExecutorE1, VmAdapterInterface, VmCoreAir, + VmCoreChip, VmExecutionState, }, system::memory::online::GuestMemory, }; @@ -11,7 +12,7 @@ use openvm_circuit_primitives::var_range::{ SharedVariableRangeCheckerChip, VariableRangeCheckerBus, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; use openvm_native_compiler::CastfOpcode; use openvm_rv32im_circuit::adapters::RV32_REGISTER_NUM_LIMBS; use openvm_stark_backend::{ @@ -114,98 +115,170 @@ pub struct CastFRecord { pub out_val: [u32; RV32_REGISTER_NUM_LIMBS], } -pub struct CastFCoreChip { - pub air: CastFCoreAir, +pub struct CastFStep { + offset: usize, pub range_checker_chip: SharedVariableRangeCheckerChip, + phantom: PhantomData, } -impl CastFCoreChip { - pub fn new(range_checker_chip: SharedVariableRangeCheckerChip) -> Self { +impl CastFStep { + pub fn new(range_checker_chip: SharedVariableRangeCheckerChip, offset: usize) -> Self { Self { - air: CastFCoreAir { - bus: range_checker_chip.bus(), - }, + offset, range_checker_chip, + phantom: PhantomData, } } -} - -impl> VmCoreChip for CastFCoreChip -where - I::Reads: Into<[[F; 1]; 1]>, - I::Writes: From<[[F; RV32_REGISTER_NUM_LIMBS]; 1]>, -{ - type Record = CastFRecord; - type Air = CastFCoreAir; - #[allow(clippy::type_complexity)] - fn execute_instruction( + #[inline] + pub fn execute_trace_core( &self, instruction: &Instruction, - _from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { - let Instruction { opcode, .. } = instruction; - - assert_eq!( - opcode.local_opcode_idx(CastfOpcode::CLASS_OFFSET), - CastfOpcode::CASTF as usize - ); - - let y = reads.into()[0][0]; - let x = CastF::solve(y.as_canonical_u32()); - - let output = AdapterRuntimeContext { - to_pc: None, - writes: [x.map(F::from_canonical_u32)].into(), - }; - - let record = CastFRecord { - in_val: y, - out_val: x, - }; + [x, y]: [[u8; NUM_LIMBS]; 2], + core_row: &mut [F], + ) -> [u8; NUM_LIMBS] + where + F: PrimeField32, + { + todo!("Implement execute_trace_core") + } - Ok((output, record)) + pub fn fill_trace_row_core(&self, core_row: &mut [F]) + where + F: PrimeField32, + { + todo!("Implement fill_trace_row_core") } +} +impl SingleTraceStep for CastFStep +where + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = [[u8; NUM_LIMBS]; 2], + WriteData = [u8; NUM_LIMBS], + TraceContext<'a> = &'a BitwiseOperationLookupChip, + >, +{ fn get_opcode_name(&self, _opcode: usize) -> String { format!("{:?}", CastfOpcode::CASTF) } - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - for (i, limb) in record.out_val.iter().enumerate() { - if i == 3 { - self.range_checker_chip.add_count(*limb, FINAL_LIMB_BITS); - } else { - self.range_checker_chip.add_count(*limb, LIMB_BITS); - } - } - - let cols: &mut CastFCoreCols = row_slice.borrow_mut(); - cols.in_val = record.in_val; - cols.out_val = record.out_val.map(F::from_canonical_u32); - cols.is_valid = F::ONE; + fn execute( + &mut self, + state: VmStateMut, + instruction: &Instruction, + row_slice: &mut [F], + ) -> Result<()> { + todo!("Implement execute") } - fn air(&self) -> &Self::Air { - &self.air + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + todo!("Implement fill_trace_row") } } -impl InsExecutorE1 for CastFCoreChip +impl StepExecutorE1 for CastFStep where Mem: GuestMemory, F: PrimeField32, + A: 'static + for<'a> AdapterExecutorE1, { fn execute_e1( &mut self, - _state: &mut VmExecutionState, - _instruction: &Instruction, + state: &mut VmExecutionState, + instruction: &Instruction, ) -> Result<()> { - todo!("Implement execute_e1") + let Instruction { + opcode, a, b, d, e, .. + } = instruction; + + assert_eq!( + opcode.local_opcode_idx(CastfOpcode::CLASS_OFFSET), + CastfOpcode::CASTF as usize + ); + + // TODO(ayush): check if can be read directly as [u8; 4] or u32? + let [y] = A::read(&mut state.memory, instruction); + + let x = CastF::solve(y.as_canonical_u32()); + let x = x.map(F::from_canonical_u32); + + A::write(&mut state.memory, instruction, x); + + state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) } } +// impl VmCoreChip for CastFCoreChip +// where +// F: PrimeField32, +// I: VmAdapterInterface, +// I::Reads: Into<[[F; 1]; 1]>, +// I::Writes: From<[[F; RV32_REGISTER_NUM_LIMBS]; 1]>, +// { +// type Record = CastFRecord; +// type Air = CastFCoreAir; + +// #[allow(clippy::type_complexity)] +// fn execute_instruction( +// &self, +// instruction: &Instruction, +// _from_pc: u32, +// reads: I::Reads, +// ) -> Result<(AdapterRuntimeContext, Self::Record)> { +// let Instruction { opcode, .. } = instruction; + +// assert_eq!( +// opcode.local_opcode_idx(CastfOpcode::CLASS_OFFSET), +// CastfOpcode::CASTF as usize +// ); + +// let y = reads.into()[0][0]; +// let x = CastF::solve(y.as_canonical_u32()); + +// let output = AdapterRuntimeContext { +// to_pc: None, +// writes: [x.map(F::from_canonical_u32)].into(), +// }; + +// let record = CastFRecord { +// in_val: y, +// out_val: x, +// }; + +// Ok((output, record)) +// } + +// fn get_opcode_name(&self, _opcode: usize) -> String { +// format!("{:?}", CastfOpcode::CASTF) +// } + +// fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { +// for (i, limb) in record.out_val.iter().enumerate() { +// if i == 3 { +// self.range_checker_chip.add_count(*limb, FINAL_LIMB_BITS); +// } else { +// self.range_checker_chip.add_count(*limb, LIMB_BITS); +// } +// } + +// let cols: &mut CastFCoreCols = row_slice.borrow_mut(); +// cols.in_val = record.in_val; +// cols.out_val = record.out_val.map(F::from_canonical_u32); +// cols.is_valid = F::ONE; +// } + +// fn air(&self) -> &Self::Air { +// &self.air +// } +// } + pub struct CastF; impl CastF { pub(super) fn solve(y: u32) -> [u32; RV32_REGISTER_NUM_LIMBS] { diff --git a/extensions/native/circuit/src/castf/mod.rs b/extensions/native/circuit/src/castf/mod.rs index 9fbd77f245..7a701eb5bf 100644 --- a/extensions/native/circuit/src/castf/mod.rs +++ b/extensions/native/circuit/src/castf/mod.rs @@ -1,6 +1,6 @@ -use openvm_circuit::arch::{VmAirWrapper, VmChipWrapper}; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper, VmChipWrapper}; -use super::adapters::convert_adapter::{ConvertAdapterAir, ConvertAdapterChip}; +use super::adapters::convert_adapter::{ConvertAdapterAir, ConvertAdapterStep}; #[cfg(test)] mod tests; @@ -9,4 +9,5 @@ mod core; pub use core::*; pub type CastFAir = VmAirWrapper, CastFCoreAir>; -pub type CastFChip = VmChipWrapper, CastFCoreChip>; +pub type CastFStepWithAdapter = CastFStep>; +pub type CastFChip = NewVmChipWrapper; diff --git a/extensions/native/circuit/src/field_arithmetic/core.rs b/extensions/native/circuit/src/field_arithmetic/core.rs index f91f7fd946..08c01215c7 100644 --- a/extensions/native/circuit/src/field_arithmetic/core.rs +++ b/extensions/native/circuit/src/field_arithmetic/core.rs @@ -1,16 +1,23 @@ -use std::borrow::{Borrow, BorrowMut}; +use std::{ + borrow::{Borrow, BorrowMut}, + marker::PhantomData, +}; use itertools::izip; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, - VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, InsExecutorE1, + MinimalInstruction, Result, SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, + VmCoreChip, VmExecutionState, }, system::memory::online::GuestMemory, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; -use openvm_native_compiler::FieldArithmeticOpcode::{self, *}; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; +use openvm_native_compiler::{ + conversion::AS, + FieldArithmeticOpcode::{self, *}, +}; use openvm_stark_backend::{ interaction::InteractionBuilder, p3_air::BaseAir, @@ -117,64 +124,56 @@ pub struct FieldArithmeticRecord { pub c: F, } -pub struct FieldArithmeticCoreChip { - pub air: FieldArithmeticCoreAir, -} - -impl FieldArithmeticCoreChip { - pub fn new() -> Self { - Self { - air: FieldArithmeticCoreAir {}, - } - } +pub struct FieldArithmeticStep { + phantom: PhantomData, } -impl Default for FieldArithmeticCoreChip { +impl Default for FieldArithmeticStep { fn default() -> Self { Self::new() } } -impl> VmCoreChip for FieldArithmeticCoreChip -where - I::Reads: Into<[[F; 1]; 2]>, - I::Writes: From<[[F; 1]; 1]>, -{ - type Record = FieldArithmeticRecord; - type Air = FieldArithmeticCoreAir; +impl FieldArithmeticStep { + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } - #[allow(clippy::type_complexity)] - fn execute_instruction( + #[inline] + pub fn execute_trace_core( &self, instruction: &Instruction, - _from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { - let Instruction { opcode, .. } = instruction; - let local_opcode = FieldArithmeticOpcode::from_usize( - opcode.local_opcode_idx(FieldArithmeticOpcode::CLASS_OFFSET), - ); - - let data: [[F; 1]; 2] = reads.into(); - let b = data[0][0]; - let c = data[1][0]; - let a = FieldArithmetic::run_field_arithmetic(local_opcode, b, c).unwrap(); - - let output: AdapterRuntimeContext = AdapterRuntimeContext { - to_pc: None, - writes: [[a]].into(), - }; - - let record = Self::Record { - opcode: local_opcode, - a, - b, - c, - }; + [x, y]: [[u8; NUM_LIMBS]; 2], + core_row: &mut [F], + ) -> [u8; NUM_LIMBS] + where + F: PrimeField32, + { + todo!("Implement execute_trace_core") + } - Ok((output, record)) + pub fn fill_trace_row_core(&self, core_row: &mut [F]) + where + F: PrimeField32, + { + todo!("Implement fill_trace_row_core") } +} +impl SingleTraceStep for FieldArithmeticStep +where + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = [[u8; NUM_LIMBS]; 2], + WriteData = [u8; NUM_LIMBS], + TraceContext<'a> = &'a BitwiseOperationLookupChip, + >, +{ fn get_opcode_name(&self, opcode: usize) -> String { format!( "{:?}", @@ -182,43 +181,122 @@ where ) } - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let FieldArithmeticRecord { opcode, a, b, c } = record; - let row_slice: &mut FieldArithmeticCoreCols<_> = row_slice.borrow_mut(); - row_slice.a = a; - row_slice.b = b; - row_slice.c = c; - - row_slice.is_add = F::from_bool(opcode == FieldArithmeticOpcode::ADD); - row_slice.is_sub = F::from_bool(opcode == FieldArithmeticOpcode::SUB); - row_slice.is_mul = F::from_bool(opcode == FieldArithmeticOpcode::MUL); - row_slice.is_div = F::from_bool(opcode == FieldArithmeticOpcode::DIV); - row_slice.divisor_inv = if opcode == FieldArithmeticOpcode::DIV { - c.inverse() - } else { - F::ZERO - }; + fn execute( + &mut self, + state: VmStateMut, + instruction: &Instruction, + row_slice: &mut [F], + ) -> Result<()> { + todo!("Implement execute") } - fn air(&self) -> &Self::Air { - &self.air + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + todo!("Implement fill_trace_row") } } -impl InsExecutorE1 for FieldArithmeticCoreChip +impl StepExecutorE1 for FieldArithmeticStep where Mem: GuestMemory, F: PrimeField32, + A: 'static + for<'a> AdapterExecutorE1, { fn execute_e1( &mut self, - _state: &mut VmExecutionState, - _instruction: &Instruction, + state: &mut VmExecutionState, + instruction: &Instruction, ) -> Result<()> { - todo!("Implement execute_e1") + let Instruction { + opcode, + a, + b, + c, + e, + f, + .. + } = instruction; + + let (b_val, c_val) = A::read(&mut state.memory, instruction); + let a_val = FieldArithmetic::run_field_arithmetic(local_opcode, b_val, c_val).unwrap(); + + A::write(&mut state.memory, instruction, &a_val); + + state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) } } +// impl> VmCoreChip for FieldArithmeticCoreChip +// where +// I::Reads: Into<[[F; 1]; 2]>, +// I::Writes: From<[[F; 1]; 1]>, +// { +// type Record = FieldArithmeticRecord; +// type Air = FieldArithmeticCoreAir; + +// #[allow(clippy::type_complexity)] +// fn execute_instruction( +// &self, +// instruction: &Instruction, +// _from_pc: u32, +// reads: I::Reads, +// ) -> Result<(AdapterRuntimeContext, Self::Record)> { +// let Instruction { opcode, .. } = instruction; +// let local_opcode = FieldArithmeticOpcode::from_usize( +// opcode.local_opcode_idx(FieldArithmeticOpcode::CLASS_OFFSET), +// ); + +// let data: [[F; 1]; 2] = reads.into(); +// let b = data[0][0]; +// let c = data[1][0]; +// let a = FieldArithmetic::run_field_arithmetic(local_opcode, b, c).unwrap(); + +// let output: AdapterRuntimeContext = AdapterRuntimeContext { +// to_pc: None, +// writes: [[a]].into(), +// }; + +// let record = Self::Record { +// opcode: local_opcode, +// a, +// b, +// c, +// }; + +// Ok((output, record)) +// } + +// fn get_opcode_name(&self, opcode: usize) -> String { +// format!( +// "{:?}", +// FieldArithmeticOpcode::from_usize(opcode - FieldArithmeticOpcode::CLASS_OFFSET) +// ) +// } + +// fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { +// let FieldArithmeticRecord { opcode, a, b, c } = record; +// let row_slice: &mut FieldArithmeticCoreCols<_> = row_slice.borrow_mut(); +// row_slice.a = a; +// row_slice.b = b; +// row_slice.c = c; + +// row_slice.is_add = F::from_bool(opcode == FieldArithmeticOpcode::ADD); +// row_slice.is_sub = F::from_bool(opcode == FieldArithmeticOpcode::SUB); +// row_slice.is_mul = F::from_bool(opcode == FieldArithmeticOpcode::MUL); +// row_slice.is_div = F::from_bool(opcode == FieldArithmeticOpcode::DIV); +// row_slice.divisor_inv = if opcode == FieldArithmeticOpcode::DIV { +// c.inverse() +// } else { +// F::ZERO +// }; +// } + +// fn air(&self) -> &Self::Air { +// &self.air +// } +// } + pub struct FieldArithmetic; impl FieldArithmetic { pub(super) fn run_field_arithmetic( diff --git a/extensions/native/circuit/src/field_arithmetic/mod.rs b/extensions/native/circuit/src/field_arithmetic/mod.rs index 865434cb37..2c9a8f061e 100644 --- a/extensions/native/circuit/src/field_arithmetic/mod.rs +++ b/extensions/native/circuit/src/field_arithmetic/mod.rs @@ -1,6 +1,6 @@ -use openvm_circuit::arch::{VmAirWrapper, VmChipWrapper}; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper, VmChipWrapper}; -use crate::adapters::alu_native_adapter::{AluNativeAdapterAir, AluNativeAdapterChip}; +use crate::adapters::alu_native_adapter::{AluNativeAdapterAir, AluNativeAdapterStep}; #[cfg(test)] mod tests; @@ -9,5 +9,6 @@ mod core; pub use core::*; pub type FieldArithmeticAir = VmAirWrapper; +pub type FieldArithmeticStepWithAdapter = FieldArithmeticStep; pub type FieldArithmeticChip = - VmChipWrapper, FieldArithmeticCoreChip>; + NewVmChipWrapper; diff --git a/extensions/native/circuit/src/field_extension/core.rs b/extensions/native/circuit/src/field_extension/core.rs index 6fe168a714..c7a7837864 100644 --- a/extensions/native/circuit/src/field_extension/core.rs +++ b/extensions/native/circuit/src/field_extension/core.rs @@ -1,19 +1,20 @@ use std::{ array, borrow::{Borrow, BorrowMut}, + marker::PhantomData, ops::{Add, Mul, Sub}, }; use itertools::izip; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, - VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, InsExecutorE1, + MinimalInstruction, Result, VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, }, system::memory::online::GuestMemory, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; use openvm_native_compiler::FieldExtensionOpcode::{self, *}; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -144,64 +145,56 @@ pub struct FieldExtensionRecord { pub z: [F; EXT_DEG], } -pub struct FieldExtensionCoreChip { - pub air: FieldExtensionCoreAir, +pub struct FieldExtensionStep { + phantom: PhantomData, } -impl FieldExtensionCoreChip { - pub fn new() -> Self { - Self { - air: FieldExtensionCoreAir {}, - } - } -} - -impl Default for FieldExtensionCoreChip { +impl Default for FieldExtensionStep { fn default() -> Self { Self::new() } } -impl> VmCoreChip for FieldExtensionCoreChip -where - I::Reads: Into<[[F; EXT_DEG]; 2]>, - I::Writes: From<[[F; EXT_DEG]; 1]>, -{ - type Record = FieldExtensionRecord; - type Air = FieldExtensionCoreAir; +impl FieldExtensionChip { + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } - #[allow(clippy::type_complexity)] - fn execute_instruction( + #[inline] + pub fn execute_trace_core( &self, instruction: &Instruction, - _from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { - let Instruction { opcode, .. } = instruction; - let local_opcode_idx = opcode.local_opcode_idx(FieldExtensionOpcode::CLASS_OFFSET); - - let data: [[F; EXT_DEG]; 2] = reads.into(); - let y: [F; EXT_DEG] = data[0]; - let z: [F; EXT_DEG] = data[1]; - - let x = FieldExtension::solve(FieldExtensionOpcode::from_usize(local_opcode_idx), y, z) - .unwrap(); - - let output = AdapterRuntimeContext { - to_pc: None, - writes: [x].into(), - }; - - let record = Self::Record { - opcode: FieldExtensionOpcode::from_usize(local_opcode_idx), - x, - y, - z, - }; + [x, y]: [[u8; NUM_LIMBS]; 2], + core_row: &mut [F], + ) -> [u8; NUM_LIMBS] + where + F: PrimeField32, + { + todo!("Implement execute_trace_core") + } - Ok((output, record)) + pub fn fill_trace_row_core(&self, core_row: &mut [F]) + where + F: PrimeField32, + { + todo!("Implement fill_trace_row_core") } +} +impl SingleTraceStep for FieldExtensionStep +where + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = [[u8; NUM_LIMBS]; 2], + WriteData = [u8; NUM_LIMBS], + TraceContext<'a> = &'a BitwiseOperationLookupChip, + >, +{ fn get_opcode_name(&self, opcode: usize) -> String { format!( "{:?}", @@ -209,42 +202,135 @@ where ) } - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let FieldExtensionRecord { opcode, x, y, z } = record; - let cols: &mut FieldExtensionCoreCols<_> = row_slice.borrow_mut(); - cols.x = x; - cols.y = y; - cols.z = z; - cols.is_add = F::from_bool(opcode == FieldExtensionOpcode::FE4ADD); - cols.is_sub = F::from_bool(opcode == FieldExtensionOpcode::FE4SUB); - cols.is_mul = F::from_bool(opcode == FieldExtensionOpcode::BBE4MUL); - cols.is_div = F::from_bool(opcode == FieldExtensionOpcode::BBE4DIV); - cols.divisor_inv = if opcode == FieldExtensionOpcode::BBE4DIV { - FieldExtension::invert(z) - } else { - [F::ZERO; EXT_DEG] - }; + fn execute( + &mut self, + state: VmStateMut, + instruction: &Instruction, + row_slice: &mut [F], + ) -> Result<()> { + todo!("Implement execute") } - fn air(&self) -> &Self::Air { - &self.air + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + todo!("Implement fill_trace_row") } } -impl InsExecutorE1 for FieldExtensionCoreChip +impl StepExecutorE1 for FieldExtensionStep where Mem: GuestMemory, F: PrimeField32, + A: 'static + + for<'a> AdapterExecutorE1< + Mem, + F, + ReadData = ([F; EXT_DEG], [F; EXT_DEG]), + WriteData = [F; EXT_DEG], + >, { fn execute_e1( &mut self, - _state: &mut VmExecutionState, - _instruction: &Instruction, + state: &mut VmExecutionState, + instruction: &Instruction, ) -> Result<()> { - todo!("Implement execute_e1") + let Instruction { + opcode, + a, + b, + c, + d, + e, + .. + } = instruction; + + let local_opcode_idx = opcode.local_opcode_idx(FieldExtensionOpcode::CLASS_OFFSET); + + let (y_val, z_val) = A::read(&mut state.memory, instruction); + + let x_val = FieldExtension::solve( + FieldExtensionOpcode::from_usize(local_opcode_idx), + y_val, + z_val, + ) + .unwrap(); + + A::write(&mut state.memory, instruction, &x_val); + + state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) } } +// impl> VmCoreChip for FieldExtensionCoreChip +// where +// I::Reads: Into<[[F; EXT_DEG]; 2]>, +// I::Writes: From<[[F; EXT_DEG]; 1]>, +// { +// type Record = FieldExtensionRecord; +// type Air = FieldExtensionCoreAir; + +// #[allow(clippy::type_complexity)] +// fn execute_instruction( +// &self, +// instruction: &Instruction, +// _from_pc: u32, +// reads: I::Reads, +// ) -> Result<(AdapterRuntimeContext, Self::Record)> { +// let Instruction { opcode, .. } = instruction; +// let local_opcode_idx = opcode.local_opcode_idx(FieldExtensionOpcode::CLASS_OFFSET); + +// let data: [[F; EXT_DEG]; 2] = reads.into(); +// let y: [F; EXT_DEG] = data[0]; +// let z: [F; EXT_DEG] = data[1]; + +// let x = FieldExtension::solve(FieldExtensionOpcode::from_usize(local_opcode_idx), y, z) +// .unwrap(); + +// let output = AdapterRuntimeContext { +// to_pc: None, +// writes: [x].into(), +// }; + +// let record = Self::Record { +// opcode: FieldExtensionOpcode::from_usize(local_opcode_idx), +// x, +// y, +// z, +// }; + +// Ok((output, record)) +// } + +// fn get_opcode_name(&self, opcode: usize) -> String { +// format!( +// "{:?}", +// FieldExtensionOpcode::from_usize(opcode - FieldExtensionOpcode::CLASS_OFFSET) +// ) +// } + +// fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { +// let FieldExtensionRecord { opcode, x, y, z } = record; +// let cols: &mut FieldExtensionCoreCols<_> = row_slice.borrow_mut(); +// cols.x = x; +// cols.y = y; +// cols.z = z; +// cols.is_add = F::from_bool(opcode == FieldExtensionOpcode::FE4ADD); +// cols.is_sub = F::from_bool(opcode == FieldExtensionOpcode::FE4SUB); +// cols.is_mul = F::from_bool(opcode == FieldExtensionOpcode::BBE4MUL); +// cols.is_div = F::from_bool(opcode == FieldExtensionOpcode::BBE4DIV); +// cols.divisor_inv = if opcode == FieldExtensionOpcode::BBE4DIV { +// FieldExtension::invert(z) +// } else { +// [F::ZERO; EXT_DEG] +// }; +// } + +// fn air(&self) -> &Self::Air { +// &self.air +// } +// } + pub struct FieldExtension; impl FieldExtension { pub(super) fn solve( diff --git a/extensions/native/circuit/src/field_extension/mod.rs b/extensions/native/circuit/src/field_extension/mod.rs index d109deb528..daa29a8e0b 100644 --- a/extensions/native/circuit/src/field_extension/mod.rs +++ b/extensions/native/circuit/src/field_extension/mod.rs @@ -1,4 +1,4 @@ -use openvm_circuit::arch::{VmAirWrapper, VmChipWrapper}; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper, VmChipWrapper}; use super::adapters::native_vectorized_adapter::{ NativeVectorizedAdapterAir, NativeVectorizedAdapterChip, @@ -12,5 +12,6 @@ pub use core::*; pub type FieldExtensionAir = VmAirWrapper, FieldExtensionCoreAir>; +pub type FieldExtensionStepWithAdapter = FieldExtensionStep>; pub type FieldExtensionChip = - VmChipWrapper, FieldExtensionCoreChip>; + NewVmChipWrapper; diff --git a/extensions/native/circuit/src/loadstore/core.rs b/extensions/native/circuit/src/loadstore/core.rs index 72b60b3e89..b20ecaa58b 100644 --- a/extensions/native/circuit/src/loadstore/core.rs +++ b/extensions/native/circuit/src/loadstore/core.rs @@ -1,19 +1,20 @@ use std::{ array, borrow::{Borrow, BorrowMut}, + marker::PhantomData, sync::{Arc, Mutex, OnceLock}, }; use openvm_circuit::{ arch::{ - instructions::LocalOpcode, AdapterAirContext, AdapterRuntimeContext, ExecutionError, - InsExecutorE1, Result, Streams, VmAdapterInterface, VmCoreAir, VmCoreChip, - VmExecutionState, + instructions::LocalOpcode, AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, + ExecutionError, InsExecutorE1, Result, StepExecutorE1, Streams, VmAdapterInterface, + VmCoreAir, VmCoreChip, VmExecutionState, }, system::memory::online::GuestMemory, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::instruction::Instruction; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP}; use openvm_native_compiler::NativeLoadStoreOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -118,68 +119,73 @@ where } #[derive(Debug)] -pub struct NativeLoadStoreCoreChip { - pub air: NativeLoadStoreCoreAir, +pub struct NativeLoadStoreStep +where + F: Field, +{ + offset: usize, pub streams: OnceLock>>>, + phantom: PhantomData, +} + +impl Default for NativeLoadStoreStep +where + F: Field, +{ + fn default() -> Self { + Self::new(NativeLoadStoreOpcode::CLASS_OFFSET) + } } -impl NativeLoadStoreCoreChip { +impl NativeLoadStoreStep +where + F: Field, +{ pub fn new(offset: usize) -> Self { Self { - air: NativeLoadStoreCoreAir:: { offset }, + offset, streams: OnceLock::new(), + phantom: PhantomData, } } pub fn set_streams(&mut self, streams: Arc>>) { self.streams.set(streams).unwrap(); } -} - -impl Default for NativeLoadStoreCoreChip { - fn default() -> Self { - Self::new(NativeLoadStoreOpcode::CLASS_OFFSET) - } -} -impl, const NUM_CELLS: usize> VmCoreChip - for NativeLoadStoreCoreChip -where - I::Reads: Into<(F, [F; NUM_CELLS])>, - I::Writes: From<[F; NUM_CELLS]>, -{ - type Record = NativeLoadStoreCoreRecord; - type Air = NativeLoadStoreCoreAir; - - fn execute_instruction( + #[inline] + pub fn execute_trace_core( &self, instruction: &Instruction, - from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { - let Instruction { opcode, .. } = *instruction; - let local_opcode = - NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - let (pointer_read, data_read) = reads.into(); - - let data = if local_opcode == NativeLoadStoreOpcode::HINT_STOREW { - let mut streams = self.streams.get().unwrap().lock().unwrap(); - if streams.hint_stream.len() < NUM_CELLS { - return Err(ExecutionError::HintOutOfBounds { pc: from_pc }); - } - array::from_fn(|_| streams.hint_stream.pop_front().unwrap()) - } else { - data_read - }; + [x, y]: [[u8; NUM_LIMBS]; 2], + core_row: &mut [F], + ) -> [u8; NUM_LIMBS] + where + F: PrimeField32, + { + todo!("Implement execute_trace_core") + } - let output = AdapterRuntimeContext::without_pc(data); - let record = NativeLoadStoreCoreRecord { - opcode: NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)), - pointer_read, - data, - }; - Ok((output, record)) + pub fn fill_trace_row_core(&self, core_row: &mut [F]) + where + F: PrimeField32, + { + todo!("Implement fill_trace_row_core") } +} +impl SingleTraceStep + for NativeLoadStoreStep +where + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = [[u8; NUM_LIMBS]; 2], + WriteData = [u8; NUM_LIMBS], + TraceContext<'a> = &'a BitwiseOperationLookupChip, + >, +{ fn get_opcode_name(&self, opcode: usize) -> String { format!( "{:?}", @@ -187,32 +193,129 @@ where ) } - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let cols: &mut NativeLoadStoreCoreCols<_, NUM_CELLS> = row_slice.borrow_mut(); - cols.is_loadw = F::from_bool(record.opcode == NativeLoadStoreOpcode::LOADW); - cols.is_storew = F::from_bool(record.opcode == NativeLoadStoreOpcode::STOREW); - cols.is_hint_storew = F::from_bool(record.opcode == NativeLoadStoreOpcode::HINT_STOREW); - - cols.pointer_read = record.pointer_read; - cols.data = record.data; + fn execute( + &mut self, + state: VmStateMut, + instruction: &Instruction, + row_slice: &mut [F], + ) -> Result<()> { + todo!("Implement execute") } - fn air(&self) -> &Self::Air { - &self.air + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + todo!("Implement fill_trace_row") } } -impl InsExecutorE1 - for NativeLoadStoreCoreChip +impl StepExecutorE1 + for NativeLoadStoreStep where Mem: GuestMemory, F: PrimeField32, + A: 'static + + for<'a> AdapterExecutorE1< + Mem, + F, + ReadData = (F, [F; NUM_CELLS]), + WriteData = [F; NUM_CELLS], + >, { fn execute_e1( &mut self, - _state: &mut VmExecutionState, - _instruction: &Instruction, + state: &mut VmExecutionState, + instruction: &Instruction, ) -> Result<()> { - todo!("Implement execute_e1") + let Instruction { + opcode, + a, + b, + c, + d, + e, + .. + } = instruction; + + // Get the local opcode for this instruction + let local_opcode = + NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); + + let (pointer_read, data_read) = A::read(&mut state.memory, instruction); + + let data = if local_opcode == NativeLoadStoreOpcode::HINT_STOREW { + let mut streams = self.streams.get().unwrap().lock().unwrap(); + if streams.hint_stream.len() < NUM_CELLS { + return Err(ExecutionError::HintOutOfBounds { pc: state.pc }); + } + array::from_fn(|_| streams.hint_stream.pop_front().unwrap()) + } else { + data_read + }; + + A::write(&mut state.memory, instruction, &data); + + state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) } } + +// impl, const NUM_CELLS: usize> VmCoreChip +// for NativeLoadStoreCoreChip +// where +// I::Reads: Into<(F, [F; NUM_CELLS])>, +// I::Writes: From<[F; NUM_CELLS]>, +// { +// type Record = NativeLoadStoreCoreRecord; +// type Air = NativeLoadStoreCoreAir; + +// fn execute_instruction( +// &self, +// instruction: &Instruction, +// from_pc: u32, +// reads: I::Reads, +// ) -> Result<(AdapterRuntimeContext, Self::Record)> { +// let Instruction { opcode, .. } = *instruction; +// let local_opcode = +// NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); +// let (pointer_read, data_read) = reads.into(); + +// let data = if local_opcode == NativeLoadStoreOpcode::HINT_STOREW { +// let mut streams = self.streams.get().unwrap().lock().unwrap(); +// if streams.hint_stream.len() < NUM_CELLS { +// return Err(ExecutionError::HintOutOfBounds { pc: from_pc }); +// } +// array::from_fn(|_| streams.hint_stream.pop_front().unwrap()) +// } else { +// data_read +// }; + +// let output = AdapterRuntimeContext::without_pc(data); +// let record = NativeLoadStoreCoreRecord { +// opcode: NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)), +// pointer_read, +// data, +// }; +// Ok((output, record)) +// } + +// fn get_opcode_name(&self, opcode: usize) -> String { +// format!( +// "{:?}", +// NativeLoadStoreOpcode::from_usize(opcode - self.air.offset) +// ) +// } + +// fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { +// let cols: &mut NativeLoadStoreCoreCols<_, NUM_CELLS> = row_slice.borrow_mut(); +// cols.is_loadw = F::from_bool(record.opcode == NativeLoadStoreOpcode::LOADW); +// cols.is_storew = F::from_bool(record.opcode == NativeLoadStoreOpcode::STOREW); +// cols.is_hint_storew = F::from_bool(record.opcode == NativeLoadStoreOpcode::HINT_STOREW); + +// cols.pointer_read = record.pointer_read; +// cols.data = record.data; +// } + +// fn air(&self) -> &Self::Air { +// &self.air +// } +// } diff --git a/extensions/native/circuit/src/loadstore/mod.rs b/extensions/native/circuit/src/loadstore/mod.rs index 3dd51113a9..2bd99db992 100644 --- a/extensions/native/circuit/src/loadstore/mod.rs +++ b/extensions/native/circuit/src/loadstore/mod.rs @@ -1,4 +1,4 @@ -use openvm_circuit::arch::{VmAirWrapper, VmChipWrapper}; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper, VmChipWrapper}; #[cfg(test)] mod tests; @@ -6,14 +6,15 @@ mod tests; mod core; pub use core::*; +use crate::adapters::loadstore_native_adapter::NativeLoadStoreAdapterStep; + use super::adapters::loadstore_native_adapter::{ NativeLoadStoreAdapterAir, NativeLoadStoreAdapterChip, }; pub type NativeLoadStoreAir = VmAirWrapper, NativeLoadStoreCoreAir>; -pub type NativeLoadStoreChip = VmChipWrapper< - F, - NativeLoadStoreAdapterChip, - NativeLoadStoreCoreChip, ->; +pub type NativeLoadStoreStepWithAdapter = + NativeLoadStoreCoreStep>; +pub type NativeLoadStoreChip = + NewVmChipWrapper, NativeLoadStoreStepWithAdapter>; diff --git a/extensions/rv32im/circuit/src/adapters/alu.rs b/extensions/rv32im/circuit/src/adapters/alu.rs index 2f91ea7c87..5c75512149 100644 --- a/extensions/rv32im/circuit/src/adapters/alu.rs +++ b/extensions/rv32im/circuit/src/adapters/alu.rs @@ -2,12 +2,12 @@ use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterTraceStep, BasicAdapterInterface, ExecutionBridge, - ExecutionState, MinimalInstruction, VmAdapterAir, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, + ExecutionBridge, ExecutionState, MinimalInstruction, VmAdapterAir, }, system::memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, - online::TracingMemory, + online::{GuestMemory, TracingMemory}, MemoryAddress, MemoryAuxColsFactory, }, }; @@ -17,7 +17,9 @@ use openvm_circuit_primitives::{ }; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{ - instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_REGISTER_AS, + instruction::Instruction, + program::DEFAULT_PC_STEP, + riscv::{RV32_IMM_AS, RV32_REGISTER_AS}, }; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -26,8 +28,7 @@ use openvm_stark_backend::{ }; use super::{ - tracing_read_reg, tracing_read_reg_or_imm, tracing_write_reg, RV32_CELL_BITS, - RV32_REGISTER_NUM_LIMBS, + tracing_read, tracing_read_imm, tracing_write, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, }; #[repr(C)] @@ -165,7 +166,7 @@ impl AdapterTraceStep for Rv32BaseAluAdapterStep { const WIDTH: usize = size_of::>(); - type ReadData = [[u8; RV32_REGISTER_NUM_LIMBS]; 2]; + type ReadData = ([u8; RV32_REGISTER_NUM_LIMBS], [u8; RV32_REGISTER_NUM_LIMBS]); type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; type TraceContext<'a> = &'a BitwiseOperationLookupChip; @@ -178,56 +179,85 @@ impl AdapterTraceStep #[inline(always)] fn read( + &self, memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { let &Instruction { b, c, d, e, .. } = instruction; + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - let adapter_row: &mut Rv32BaseAluAdapterCols = adapter_row.borrow_mut(); - let rs1_idx = b.as_canonical_u32(); - let rs1 = tracing_read_reg( - memory, - rs1_idx, - (&mut adapter_row.rs1_ptr, &mut adapter_row.reads_aux[0]), + debug_assert!( + e.as_canonical_u32() == RV32_REGISTER_AS || e.as_canonical_u32() == RV32_IMM_AS ); - let rs2 = tracing_read_reg_or_imm( + + let adapter_row: &mut Rv32BaseAluAdapterCols = adapter_row.borrow_mut(); + + adapter_row.rs1_ptr = b; + let rs1 = tracing_read( memory, - e.as_canonical_u32(), - c.as_canonical_u32(), - &mut adapter_row.rs2_as, - (&mut adapter_row.rs2, &mut adapter_row.reads_aux[1]), + d.as_canonical_u32(), + b.as_canonical_u32(), + &mut adapter_row.reads_aux[0], ); - [rs1, rs2] + + let rs2 = if e.as_canonical_u32() == RV32_REGISTER_AS { + adapter_row.rs2_as = e; + adapter_row.rs2 = c; + + tracing_read( + memory, + e.as_canonical_u32(), + c.as_canonical_u32(), + &mut adapter_row.reads_aux[1], + ) + } else { + adapter_row.rs2_as = e; + + tracing_read_imm(memory, c.as_canonical_u32(), &mut adapter_row.rs2) + }; + + (rs1, rs2) } #[inline(always)] fn write( + &self, memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, ) { + let &Instruction { a, d, .. } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + let adapter_row: &mut Rv32BaseAluAdapterCols = adapter_row.borrow_mut(); - let rd_ptr = instruction.a.as_canonical_u32(); - tracing_write_reg( + + adapter_row.rd_ptr = a; + tracing_write( memory, - rd_ptr, + d.as_canonical_u32(), + a.as_canonical_u32(), data, - (&mut adapter_row.rd_ptr, &mut adapter_row.writes_aux), + &mut adapter_row.writes_aux, ); } #[inline(always)] fn fill_trace_row( + &self, mem_helper: &MemoryAuxColsFactory, bitwise_lookup_chip: &BitwiseOperationLookupChip, adapter_row: &mut [F], ) { let adapter_row: &mut Rv32BaseAluAdapterCols = adapter_row.borrow_mut(); + let mut timestamp = adapter_row.from_state.timestamp.as_canonical_u32(); + mem_helper.fill_from_prev(timestamp, adapter_row.reads_aux[0].as_mut()); timestamp += 1; + if !adapter_row.rs2_as.is_zero() { mem_helper.fill_from_prev(timestamp, adapter_row.reads_aux[1].as_mut()); } else { @@ -236,6 +266,59 @@ impl AdapterTraceStep bitwise_lookup_chip.request_range(rs2_imm & mask, (rs2_imm >> 8) & mask); } timestamp += 1; + mem_helper.fill_from_prev(timestamp, adapter_row.writes_aux.as_mut()); } } + +impl AdapterExecutorE1 for Rv32BaseAluAdapterStep +where + F: PrimeField32, +{ + // TODO(ayush): directly use u32 + type ReadData = ([u8; RV32_REGISTER_NUM_LIMBS], [u8; RV32_REGISTER_NUM_LIMBS]); + type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + + #[inline(always)] + fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData + where + Mem: GuestMemory, + { + let Instruction { b, c, d, e, .. } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + debug_assert!( + e.as_canonical_u32() == RV32_IMM_AS || e.as_canonical_u32() == RV32_REGISTER_AS + ); + + let rs1: [u8; RV32_REGISTER_NUM_LIMBS] = + unsafe { memory.read(d.as_canonical_u32(), b.as_canonical_u32()) }; + + let rs2 = if e.as_canonical_u32() == RV32_REGISTER_AS { + let rs2: [u8; RV32_REGISTER_NUM_LIMBS] = + unsafe { memory.read(e.as_canonical_u32(), c.as_canonical_u32()) }; + rs2 + } else { + let imm = c.as_canonical_u32(); + debug_assert_eq!(imm >> 24, 0); + // TODO(ayush): why this? + let mut imm_le = imm.to_le_bytes(); + imm_le[3] = imm_le[2]; + imm_le + }; + + (rs1, rs2) + } + + #[inline(always)] + fn write(&self, memory: &mut Mem, instruction: &Instruction, rd: &Self::WriteData) + where + Mem: GuestMemory, + { + let Instruction { a, d, .. } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + + unsafe { memory.write(d.as_canonical_u32(), a.as_canonical_u32(), rd) }; + } +} diff --git a/extensions/rv32im/circuit/src/adapters/branch.rs b/extensions/rv32im/circuit/src/adapters/branch.rs index 92f96ed6d0..12f367ae68 100644 --- a/extensions/rv32im/circuit/src/adapters/branch.rs +++ b/extensions/rv32im/circuit/src/adapters/branch.rs @@ -1,20 +1,14 @@ -use std::{ - borrow::{Borrow, BorrowMut}, - marker::PhantomData, -}; +use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge, - ExecutionBus, ExecutionState, ImmInstruction, Result, VmAdapterAir, VmAdapterChip, - VmAdapterInterface, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, + ExecutionBridge, ExecutionState, ImmInstruction, VmAdapterAir, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadAuxCols}, - MemoryAddress, MemoryController, OfflineMemory, RecordId, - }, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryReadAuxCols}, + online::{GuestMemory, TracingMemory}, + MemoryAddress, MemoryAuxColsFactory, RecordId, }, }; use openvm_circuit_primitives_derive::AlignedBorrow; @@ -28,31 +22,9 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use super::RV32_REGISTER_NUM_LIMBS; - -/// Reads instructions of the form OP a, b, c, d, e where if(\[a:4\]_d op \[b:4\]_e) pc += c. -/// Operands d and e can only be 1. -#[derive(Debug)] -pub struct Rv32BranchAdapterChip { - pub air: Rv32BranchAdapterAir, - _marker: PhantomData, -} +use crate::adapters::tracing_read; -impl Rv32BranchAdapterChip { - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - ) -> Self { - Self { - air: Rv32BranchAdapterAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - }, - _marker: PhantomData, - } - } -} +use super::RV32_REGISTER_NUM_LIMBS; #[repr(C)] #[derive(Debug, Serialize, Deserialize)] @@ -149,83 +121,117 @@ impl VmAdapterAir for Rv32BranchAdapterAir { } } -impl VmAdapterChip for Rv32BranchAdapterChip { - type ReadRecord = Rv32BranchReadRecord; - type WriteRecord = Rv32BranchWriteRecord; - type Air = Rv32BranchAdapterAir; - type Interface = BasicAdapterInterface, 2, 0, RV32_REGISTER_NUM_LIMBS, 0>; +/// Reads instructions of the form OP a, b, c, d, e where if(\[a:4\]_d op \[b:4\]_e) pc += c. +/// Operands d and e can only be 1. +#[derive(derive_new::new)] +pub struct Rv32BranchAdapterStep; + +impl AdapterTraceStep for Rv32BranchAdapterStep +where + F: PrimeField32, +{ + const WIDTH: usize = size_of::>(); + type ReadData = ([u8; RV32_REGISTER_NUM_LIMBS], [u8; RV32_REGISTER_NUM_LIMBS]); + type WriteData = (); + type TraceContext<'a> = (); + + #[inline(always)] + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let adapter_row: &mut Rv32BranchAdapterCols = adapter_row.borrow_mut(); + adapter_row.from_state.pc = F::from_canonical_u32(pc); + adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + } - fn preprocess( - &mut self, - memory: &mut MemoryController, + #[inline(always)] + fn read( + &self, + memory: &mut TracingMemory, instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { - let Instruction { a, b, d, e, .. } = *instruction; + adapter_row: &mut [F], + ) -> Self::ReadData { + let &Instruction { a, b, d, e, .. } = instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); debug_assert_eq!(e.as_canonical_u32(), RV32_REGISTER_AS); - let rs1 = memory.read::(d, a); - let rs2 = memory.read::(e, b); - - Ok(( - [ - rs1.1.map(F::from_canonical_u8), - rs2.1.map(F::from_canonical_u8), - ], - Self::ReadRecord { - rs1: rs1.0, - rs2: rs2.0, - }, - )) - } + let adapter_row: &mut Rv32BranchAdapterCols = adapter_row.borrow_mut(); - fn postprocess( - &mut self, - memory: &mut MemoryController, - _instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - _read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - let timestamp_delta = memory.timestamp() - from_state.timestamp; - debug_assert!( - timestamp_delta == 2, - "timestamp delta is {}, expected 2", - timestamp_delta + adapter_row.rs1_ptr = a; + let rs1 = tracing_read( + memory, + d.as_canonical_u32(), + a.as_canonical_u32(), + &mut adapter_row.reads_aux[0], + ); + adapter_row.rs2_ptr = b; + let rs2 = tracing_read( + memory, + e.as_canonical_u32(), + b.as_canonical_u32(), + &mut adapter_row.reads_aux[1], ); - Ok(( - ExecutionState { - pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), - timestamp: memory.timestamp(), - }, - Self::WriteRecord { from_state }, - )) + (rs1, rs2) } - fn generate_trace_row( + #[inline(always)] + fn write( &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, + _memory: &mut TracingMemory, + _instruction: &Instruction, + _adapter_row: &mut [F], + _data: &Self::WriteData, + ) { + } + + #[inline(always)] + fn fill_trace_row( + &self, + mem_helper: &MemoryAuxColsFactory, + _trace_ctx: Self::TraceContext<'_>, + adapter_row: &mut [F], ) { - let aux_cols_factory = memory.aux_cols_factory(); - let row_slice: &mut Rv32BranchAdapterCols<_> = row_slice.borrow_mut(); - row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); - let rs1 = memory.record_by_id(read_record.rs1); - let rs2 = memory.record_by_id(read_record.rs2); - row_slice.rs1_ptr = rs1.pointer; - row_slice.rs2_ptr = rs2.pointer; - aux_cols_factory.generate_read_aux(rs1, &mut row_slice.reads_aux[0]); - aux_cols_factory.generate_read_aux(rs2, &mut row_slice.reads_aux[1]); + let adapter_row: &mut Rv32BranchAdapterCols = adapter_row.borrow_mut(); + + let mut timestamp = adapter_row.from_state.timestamp.as_canonical_u32(); + + mem_helper.fill_from_prev(timestamp, adapter_row.reads_aux[0].as_mut()); + timestamp += 1; + + mem_helper.fill_from_prev(timestamp, adapter_row.reads_aux[1].as_mut()); + } +} + +impl AdapterExecutorE1 for Rv32BranchAdapterStep +where + F: PrimeField32, +{ + // TODO(ayush): directly use u32 + type ReadData = ([u8; RV32_REGISTER_NUM_LIMBS], [u8; RV32_REGISTER_NUM_LIMBS]); + type WriteData = (); + + #[inline(always)] + fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData + where + Mem: GuestMemory, + { + let Instruction { a, b, d, e, .. } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + debug_assert_eq!(e.as_canonical_u32(), RV32_REGISTER_AS); + + let rs1: [u8; RV32_REGISTER_NUM_LIMBS] = + unsafe { memory.read(d.as_canonical_u32(), a.as_canonical_u32()) }; + let rs2: [u8; RV32_REGISTER_NUM_LIMBS] = + unsafe { memory.read(e.as_canonical_u32(), b.as_canonical_u32()) }; + + (rs1, rs2) } - fn air(&self) -> &Self::Air { - &self.air + #[inline(always)] + fn write(&self, _memory: &mut Mem, _instruction: &Instruction, _data: &Self::WriteData) + where + Mem: GuestMemory, + { } } diff --git a/extensions/rv32im/circuit/src/adapters/jalr.rs b/extensions/rv32im/circuit/src/adapters/jalr.rs index 98823cd789..ddea7e8212 100644 --- a/extensions/rv32im/circuit/src/adapters/jalr.rs +++ b/extensions/rv32im/circuit/src/adapters/jalr.rs @@ -1,20 +1,14 @@ -use std::{ - borrow::{Borrow, BorrowMut}, - marker::PhantomData, -}; +use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge, - ExecutionBus, ExecutionState, Result, SignedImmInstruction, VmAdapterAir, VmAdapterChip, - VmAdapterInterface, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, + ExecutionBridge, ExecutionState, SignedImmInstruction, VmAdapterAir, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, - MemoryAddress, MemoryController, OfflineMemory, RecordId, - }, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, + online::{GuestMemory, TracingMemory}, + MemoryAddress, MemoryAuxColsFactory, RecordId, }, }; use openvm_circuit_primitives::utils::not; @@ -29,30 +23,10 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use super::{tmp_convert_to_u8s, RV32_REGISTER_NUM_LIMBS}; +use crate::adapters::{tracing_read, tracing_write}; -// This adapter reads from [b:4]_d (rs1) and writes to [a:4]_d (rd) -#[derive(Debug)] -pub struct Rv32JalrAdapterChip { - pub air: Rv32JalrAdapterAir, - _marker: PhantomData, -} +use super::RV32_REGISTER_NUM_LIMBS; -impl Rv32JalrAdapterChip { - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - ) -> Self { - Self { - air: Rv32JalrAdapterAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - }, - _marker: PhantomData, - } - } -} #[repr(C)] #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Rv32JalrReadRecord { @@ -179,87 +153,138 @@ impl VmAdapterAir for Rv32JalrAdapterAir { } } -impl VmAdapterChip for Rv32JalrAdapterChip { - type ReadRecord = Rv32JalrReadRecord; - type WriteRecord = Rv32JalrWriteRecord; - type Air = Rv32JalrAdapterAir; - type Interface = BasicAdapterInterface< - F, - SignedImmInstruction, - 1, - 1, - RV32_REGISTER_NUM_LIMBS, - RV32_REGISTER_NUM_LIMBS, - >; - fn preprocess( - &mut self, - memory: &mut MemoryController, +// This adapter reads from [b:4]_d (rs1) and writes to [a:4]_d (rd) +#[derive(derive_new::new)] +pub struct Rv32JalrAdapterStep; + +impl AdapterTraceStep for Rv32JalrAdapterStep +where + F: PrimeField32, +{ + const WIDTH: usize = size_of::>(); + type ReadData = [u8; RV32_REGISTER_NUM_LIMBS]; + type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + type TraceContext<'a> = (); + + #[inline(always)] + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let adapter_row: &mut Rv32JalrAdapterCols = adapter_row.borrow_mut(); + adapter_row.from_state.pc = F::from_canonical_u32(pc); + adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + } + + #[inline(always)] + fn read( + &self, + memory: &mut TracingMemory, instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { - let Instruction { b, d, .. } = *instruction; + adapter_row: &mut [F], + ) -> Self::ReadData { + let &Instruction { b, d, .. } = instruction; + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - let rs1 = memory.read::(d, b); + let adapter_row: &mut Rv32JalrAdapterCols = adapter_row.borrow_mut(); - Ok(( - [rs1.1.map(F::from_canonical_u8)], - Rv32JalrReadRecord { rs1: rs1.0 }, - )) + adapter_row.rs1_ptr = b; + tracing_read( + memory, + d.as_canonical_u32(), + b.as_canonical_u32(), + &mut adapter_row.rs1_aux_cols, + ) } - fn postprocess( - &mut self, - memory: &mut MemoryController, + #[inline(always)] + fn write( + &self, + memory: &mut TracingMemory, instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - _read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - let Instruction { + adapter_row: &mut [F], + data: &Self::WriteData, + ) { + let &Instruction { a, d, f: enabled, .. - } = *instruction; - let rd_id = if enabled != F::ZERO { - let (record_id, _) = memory.write(d, a, &tmp_convert_to_u8s(output.writes[0])); - Some(record_id) + } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + + if enabled != F::ZERO { + let adapter_row: &mut Rv32JalrAdapterCols = adapter_row.borrow_mut(); + + adapter_row.needs_write = F::ONE; + + adapter_row.rd_ptr = a; + tracing_write( + memory, + d.as_canonical_u32(), + a.as_canonical_u32(), + data, + &mut adapter_row.rd_aux_cols, + ); } else { memory.increment_timestamp(); - None - }; - - Ok(( - ExecutionState { - pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), - timestamp: memory.timestamp(), - }, - Self::WriteRecord { from_state, rd_id }, - )) + } } - fn generate_trace_row( + #[inline(always)] + fn fill_trace_row( &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, + mem_helper: &MemoryAuxColsFactory, + _trace_ctx: Self::TraceContext<'_>, + adapter_row: &mut [F], ) { - let aux_cols_factory = memory.aux_cols_factory(); - let adapter_cols: &mut Rv32JalrAdapterCols<_> = row_slice.borrow_mut(); - adapter_cols.from_state = write_record.from_state.map(F::from_canonical_u32); - let rs1 = memory.record_by_id(read_record.rs1); - adapter_cols.rs1_ptr = rs1.pointer; - aux_cols_factory.generate_read_aux(rs1, &mut adapter_cols.rs1_aux_cols); - if let Some(id) = write_record.rd_id { - let rd = memory.record_by_id(id); - adapter_cols.rd_ptr = rd.pointer; - adapter_cols.needs_write = F::ONE; - aux_cols_factory.generate_write_aux(rd, &mut adapter_cols.rd_aux_cols); + let adapter_row: &mut Rv32JalrAdapterCols = adapter_row.borrow_mut(); + + let mut timestamp = adapter_row.from_state.timestamp.as_canonical_u32(); + + mem_helper.fill_from_prev(timestamp, adapter_row.rs1_aux_cols.as_mut()); + timestamp += 1; + + if adapter_row.needs_write.is_one() { + mem_helper.fill_from_prev(timestamp, adapter_row.rd_aux_cols.as_mut()); } } +} + +impl AdapterExecutorE1 for Rv32JalrAdapterStep +where + F: PrimeField32, +{ + // TODO(ayush): directly use u32 + type ReadData = [u8; RV32_REGISTER_NUM_LIMBS]; + type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + + #[inline(always)] + fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData + where + Mem: GuestMemory, + { + let Instruction { b, d, .. } = instruction; - fn air(&self) -> &Self::Air { - &self.air + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + + let rs1: [u8; RV32_REGISTER_NUM_LIMBS] = + unsafe { memory.read(d.as_canonical_u32(), b.as_canonical_u32()) }; + + rs1 + } + + #[inline(always)] + fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) + where + Mem: GuestMemory, + { + let Instruction { + a, d, f: enabled, .. + } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + + if *enabled != F::ZERO { + unsafe { + memory.write(d.as_canonical_u32(), a.as_canonical_u32(), data); + } + } } } diff --git a/extensions/rv32im/circuit/src/adapters/loadstore.rs b/extensions/rv32im/circuit/src/adapters/loadstore.rs index db379c7ab7..0e6f45c368 100644 --- a/extensions/rv32im/circuit/src/adapters/loadstore.rs +++ b/extensions/rv32im/circuit/src/adapters/loadstore.rs @@ -6,17 +6,13 @@ use std::{ use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, ExecutionBridge, ExecutionBus, ExecutionState, - Result, VmAdapterAir, VmAdapterChip, VmAdapterInterface, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ExecutionBridge, ExecutionState, + VmAdapterAir, VmAdapterInterface, }, - system::{ - memory::{ - offline_checker::{ - MemoryBaseAuxCols, MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols, - }, - MemoryAddress, MemoryController, OfflineMemory, RecordId, - }, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBaseAuxCols, MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, + online::{GuestMemory, TracingMemory}, + MemoryAddress, MemoryAuxColsFactory, RecordId, }, }; use openvm_circuit_primitives::{ @@ -38,8 +34,8 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use super::{tmp_convert_to_u8s, RV32_REGISTER_NUM_LIMBS}; -use crate::adapters::RV32_CELL_BITS; +use super::RV32_REGISTER_NUM_LIMBS; +use crate::adapters::{tracing_read, tracing_write_with_base_aux, RV32_CELL_BITS}; /// LoadStore Adapter handles all memory and register operations, so it must be aware /// of the instruction type, specifically whether it is a load or store @@ -92,37 +88,6 @@ impl VmAdapterInterface for Rv32LoadStoreAdapt type ProcessedInstruction = LoadStoreInstruction; } -/// This chip reads rs1 and gets a intermediate memory pointer address with rs1 + imm. -/// In case of Loads, reads from the shifted intermediate pointer and writes to rd. -/// In case of Stores, reads from rs2 and writes to the shifted intermediate pointer. -pub struct Rv32LoadStoreAdapterChip { - pub air: Rv32LoadStoreAdapterAir, - pub range_checker_chip: SharedVariableRangeCheckerChip, - _marker: PhantomData, -} - -impl Rv32LoadStoreAdapterChip { - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - pointer_max_bits: usize, - range_checker_chip: SharedVariableRangeCheckerChip, - ) -> Self { - assert!(range_checker_chip.range_max_bits() >= 15); - Self { - air: Rv32LoadStoreAdapterAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - range_bus: range_checker_chip.bus(), - pointer_max_bits, - }, - range_checker_chip, - _marker: PhantomData, - } - } -} - #[repr(C)] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(bound = "F: Field")] @@ -366,22 +331,46 @@ impl VmAdapterAir for Rv32LoadStoreAdapterAir { } } -impl VmAdapterChip for Rv32LoadStoreAdapterChip { - type ReadRecord = Rv32LoadStoreReadRecord; - type WriteRecord = Rv32LoadStoreWriteRecord; - type Air = Rv32LoadStoreAdapterAir; - type Interface = Rv32LoadStoreAdapterRuntimeInterface; +/// This chip reads rs1 and gets a intermediate memory pointer address with rs1 + imm. +/// In case of Loads, reads from the shifted intermediate pointer and writes to rd. +/// In case of Stores, reads from rs2 and writes to the shifted intermediate pointer. +pub struct Rv32LoadStoreAdapterStep { + pointer_max_bits: usize, +} + +impl Rv32LoadStoreAdapterStep { + pub fn new(pointer_max_bits: usize) -> Self { + Self { pointer_max_bits } + } +} + +impl AdapterTraceStep for Rv32LoadStoreAdapterStep +where + F: PrimeField32, +{ + const WIDTH: usize = size_of::>(); + type ReadData = ( + ([u8; RV32_REGISTER_NUM_LIMBS], [u8; RV32_REGISTER_NUM_LIMBS]), + u32, + ); + type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + type TraceContext<'a> = &'a SharedVariableRangeCheckerChip; + + #[inline(always)] + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let adapter_row: &mut Rv32LoadStoreAdapterCols = adapter_row.borrow_mut(); + adapter_row.from_state.pc = F::from_canonical_u32(pc); + adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + } - #[allow(clippy::type_complexity)] - fn preprocess( - &mut self, - memory: &mut MemoryController, + #[inline(always)] + fn read( + &self, + memory: &mut TracingMemory, instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { - let Instruction { + adapter_row: &mut [F], + ) -> Self::ReadData { + let &Instruction { opcode, a, b, @@ -390,16 +379,26 @@ impl VmAdapterChip for Rv32LoadStoreAdapterChip { e, g, .. - } = *instruction; + } = instruction; + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); debug_assert!(e.as_canonical_u32() != RV32_IMM_AS); + let adapter_row: &mut Rv32LoadStoreAdapterCols = adapter_row.borrow_mut(); + let local_opcode = Rv32LoadStoreOpcode::from_usize( opcode.local_opcode_idx(Rv32LoadStoreOpcode::CLASS_OFFSET), ); - let rs1_record = memory.read::(d, b); - let rs1_val = u32::from_le_bytes(rs1_record.1); + adapter_row.rs1_ptr = b; + let rs1 = tracing_read( + memory, + d.as_canonical_u32(), + b.as_canonical_u32(), + &mut adapter_row.rs1_aux_cols, + ); + + let rs1_val = u32::from_le_bytes(rs1); let imm = c.as_canonical_u32(); let imm_sign = g.as_canonical_u32(); let imm_extended = imm + imm_sign * 0xffff0000; @@ -407,146 +406,301 @@ impl VmAdapterChip for Rv32LoadStoreAdapterChip { let ptr_val = rs1_val.wrapping_add(imm_extended); let shift_amount = ptr_val % 4; assert!( - ptr_val < (1 << self.air.pointer_max_bits), + ptr_val < (1 << self.pointer_max_bits), "ptr_val: {ptr_val} = rs1_val: {rs1_val} + imm_extended: {imm_extended} >= 2 ** {}", - self.air.pointer_max_bits + self.pointer_max_bits ); let mem_ptr_limbs = array::from_fn(|i| ((ptr_val >> (i * (RV32_CELL_BITS * 2))) & 0xffff)); let ptr_val = ptr_val - shift_amount; - let read_record = match local_opcode { - LOADW | LOADB | LOADH | LOADBU | LOADHU => { - memory.read::(e, F::from_canonical_u32(ptr_val)) - } - STOREW | STOREH | STOREB => memory.read::(d, a), + let read_data = match local_opcode { + LOADW | LOADB | LOADH | LOADBU | LOADHU => tracing_read( + memory, + e.as_canonical_u32(), + ptr_val, + &mut adapter_row.read_data_aux, + ), + STOREW | STOREH | STOREB => tracing_read( + memory, + d.as_canonical_u32(), + a.as_canonical_u32(), + &mut adapter_row.read_data_aux, + ), }; // We need to keep values of some cells to keep them unchanged when writing to those cells let prev_data = match local_opcode { - STOREW | STOREH | STOREB => array::from_fn(|i| { - memory.unsafe_read_cell::(e, F::from_canonical_usize(ptr_val as usize + i)) - }), - LOADW | LOADB | LOADH | LOADBU | LOADHU => { - array::from_fn(|i| memory.unsafe_read_cell::(d, a + F::from_canonical_usize(i))) - } + STOREW | STOREH | STOREB => unsafe { + memory.data().read(e.as_canonical_u32(), ptr_val) + }, + LOADW | LOADB | LOADH | LOADBU | LOADHU => unsafe { + memory + .data() + .read(d.as_canonical_u32(), a.as_canonical_u32()) + }, }; - Ok(( - ( - [ - prev_data.map(F::from_canonical_u8), - read_record.1.map(F::from_canonical_u8), - ], - F::from_canonical_u32(shift_amount), - ), - Self::ReadRecord { - rs1_record: rs1_record.0, - rs1_ptr: b, - read: read_record.0, - imm: c, - imm_sign: g, - shift_amount, - mem_ptr_limbs, - mem_as: e, - }, - )) + adapter_row + .rs1_data + .copy_from_slice(&rs1.map(F::from_canonical_u8)); + adapter_row.imm = c; + adapter_row.imm_sign = g; + adapter_row.mem_ptr_limbs = mem_ptr_limbs.map(F::from_canonical_u32); + adapter_row.mem_as = e; + + ((prev_data, read_data), shift_amount) } - fn postprocess( - &mut self, - memory: &mut MemoryController, + #[inline(always)] + fn write( + &self, + memory: &mut TracingMemory, instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - let Instruction { + adapter_row: &mut [F], + data: &Self::WriteData, + ) { + let &Instruction { opcode, a, + c, d, e, f: enabled, + g, .. - } = *instruction; + } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + debug_assert!(e.as_canonical_u32() != RV32_IMM_AS); let local_opcode = Rv32LoadStoreOpcode::from_usize( opcode.local_opcode_idx(Rv32LoadStoreOpcode::CLASS_OFFSET), ); - let write_id = if enabled != F::ZERO { - let (record_id, _) = match local_opcode { + let adapter_row: &mut Rv32LoadStoreAdapterCols = adapter_row.borrow_mut(); + + let rs1 = adapter_row.rs1_data.map(|x| x.as_canonical_u32() as u8); + + let rs1_val = u32::from_le_bytes(rs1); + let imm = c.as_canonical_u32(); + let imm_sign = g.as_canonical_u32(); + let imm_extended = imm + imm_sign * 0xffff0000; + + let ptr_val = rs1_val.wrapping_add(imm_extended); + assert!( + ptr_val < (1 << self.pointer_max_bits), + "ptr_val: {ptr_val} = rs1_val: {rs1_val} + imm_extended: {imm_extended} >= 2 ** {}", + self.pointer_max_bits + ); + + let mem_ptr_limbs: [u32; 2] = + array::from_fn(|i| ((ptr_val >> (i * (RV32_CELL_BITS * 2))) & 0xffff)); + + if enabled != F::ZERO { + adapter_row.needs_write = F::ONE; + + match local_opcode { STOREW | STOREH | STOREB => { - let ptr = read_record.mem_ptr_limbs[0] - + read_record.mem_ptr_limbs[1] * (1 << (RV32_CELL_BITS * 2)); - memory.write( - e, - F::from_canonical_u32(ptr & 0xfffffffc), - &tmp_convert_to_u8s(output.writes[0]), - ) + let ptr = mem_ptr_limbs[0] + mem_ptr_limbs[1] * (1 << (RV32_CELL_BITS * 2)); + let ptr = ptr & 0xfffffffc; + + adapter_row.rd_rs2_ptr = F::from_canonical_u32(ptr); + tracing_write_with_base_aux( + memory, + e.as_canonical_u32(), + ptr, + data, + &mut adapter_row.write_base_aux, + ); } LOADW | LOADB | LOADH | LOADBU | LOADHU => { - memory.write(d, a, &tmp_convert_to_u8s(output.writes[0])) + adapter_row.rd_rs2_ptr = a; + tracing_write_with_base_aux( + memory, + d.as_canonical_u32(), + a.as_canonical_u32(), + data, + &mut adapter_row.write_base_aux, + ); } }; - record_id } else { memory.increment_timestamp(); - // RecordId will never get to usize::MAX, so it can be used as a flag for no write - RecordId(usize::MAX) }; - - Ok(( - ExecutionState { - pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), - timestamp: memory.timestamp(), - }, - Self::WriteRecord { - from_state, - write_id, - rd_rs2_ptr: a, - }, - )) } - fn generate_trace_row( + #[inline(always)] + fn fill_trace_row( &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, + mem_helper: &MemoryAuxColsFactory, + range_checker_chip: &SharedVariableRangeCheckerChip, + adapter_row: &mut [F], ) { - self.range_checker_chip.add_count( - (read_record.mem_ptr_limbs[0] - read_record.shift_amount) / 4, + // TODO(ayush): should this be here? + assert!(range_checker_chip.range_max_bits() >= 15); + + let adapter_row: &mut Rv32LoadStoreAdapterCols = adapter_row.borrow_mut(); + + let rs1 = adapter_row.rs1_data.map(|x| x.as_canonical_u32() as u8); + let rs1_val = u32::from_le_bytes(rs1); + + let imm = adapter_row.imm.as_canonical_u32(); + let imm_sign = adapter_row.imm_sign.as_canonical_u32(); + let imm_extended = imm + imm_sign * 0xffff0000; + + let ptr_val = rs1_val.wrapping_add(imm_extended); + let shift_amount = ptr_val % 4; + + range_checker_chip.add_count( + (adapter_row.mem_ptr_limbs[0].as_canonical_u32() - shift_amount) / 4, RV32_CELL_BITS * 2 - 2, ); - self.range_checker_chip.add_count( - read_record.mem_ptr_limbs[1], - self.air.pointer_max_bits - RV32_CELL_BITS * 2, + range_checker_chip.add_count( + adapter_row.mem_ptr_limbs[1].as_canonical_u32(), + self.pointer_max_bits - RV32_CELL_BITS * 2, ); - let aux_cols_factory = memory.aux_cols_factory(); - let adapter_cols: &mut Rv32LoadStoreAdapterCols<_> = row_slice.borrow_mut(); - adapter_cols.from_state = write_record.from_state.map(F::from_canonical_u32); - let rs1 = memory.record_by_id(read_record.rs1_record); - adapter_cols.rs1_data.copy_from_slice(rs1.data_slice()); - aux_cols_factory.generate_read_aux(rs1, &mut adapter_cols.rs1_aux_cols); - adapter_cols.rs1_ptr = read_record.rs1_ptr; - adapter_cols.rd_rs2_ptr = write_record.rd_rs2_ptr; - let read = memory.record_by_id(read_record.read); - aux_cols_factory.generate_read_aux(read, &mut adapter_cols.read_data_aux); - adapter_cols.imm = read_record.imm; - adapter_cols.imm_sign = read_record.imm_sign; - adapter_cols.mem_ptr_limbs = read_record.mem_ptr_limbs.map(F::from_canonical_u32); - adapter_cols.mem_as = read_record.mem_as; - if write_record.write_id.0 != usize::MAX { - let write = memory.record_by_id(write_record.write_id); - aux_cols_factory.generate_base_aux(write, &mut adapter_cols.write_base_aux); - adapter_cols.needs_write = F::ONE; + let mut timestamp = adapter_row.from_state.timestamp.as_canonical_u32(); + + mem_helper.fill_from_prev(timestamp, adapter_row.rs1_aux_cols.as_mut()); + timestamp += 1; + + mem_helper.fill_from_prev(timestamp, adapter_row.read_data_aux.as_mut()); + timestamp += 1; + + if adapter_row.needs_write.is_one() { + mem_helper.fill_from_prev(timestamp, &mut adapter_row.write_base_aux); } } +} + +impl AdapterExecutorE1 for Rv32LoadStoreAdapterStep +where + F: PrimeField32, +{ + // TODO(ayush): directly use u32 + type ReadData = ( + ([u8; RV32_REGISTER_NUM_LIMBS], [u8; RV32_REGISTER_NUM_LIMBS]), + u32, + ); + type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + + fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData + where + Mem: GuestMemory, + { + let Instruction { + opcode, + a, + b, + c, + d, + e, + g, + .. + } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + debug_assert!(e.as_canonical_u32() != RV32_IMM_AS); + + let local_opcode = Rv32LoadStoreOpcode::from_usize( + opcode.local_opcode_idx(Rv32LoadStoreOpcode::CLASS_OFFSET), + ); + + let rs1_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = + unsafe { memory.read(d.as_canonical_u32(), b.as_canonical_u32()) }; + let rs1_val = u32::from_le_bytes(rs1_bytes); + + let imm = c.as_canonical_u32(); + let imm_sign = g.as_canonical_u32(); + let imm_extended = imm + imm_sign * 0xffff0000; + + let ptr_val = rs1_val.wrapping_add(imm_extended); + assert!( + ptr_val < (1 << self.pointer_max_bits), + "ptr_val: {ptr_val} = rs1_val: {rs1_val} + imm_extended: {imm_extended} >= 2 ** {}", + self.pointer_max_bits + ); + let shift_amount = ptr_val % 4; + + let ptr_val = ptr_val - shift_amount; // aligned ptr + + let read_data: [u8; RV32_REGISTER_NUM_LIMBS] = match local_opcode { + LOADW | LOADB | LOADH | LOADBU | LOADHU => unsafe { + memory.read(e.as_canonical_u32(), ptr_val) + }, + STOREW | STOREH | STOREB => unsafe { + memory.read(d.as_canonical_u32(), a.as_canonical_u32()) + }, + }; + + // For stores, we need the previous memory content to preserve unchanged bytes + let prev_data: [u8; RV32_REGISTER_NUM_LIMBS] = match local_opcode { + STOREW | STOREH | STOREB => unsafe { memory.read(e.as_canonical_u32(), ptr_val) }, + LOADW | LOADB | LOADH | LOADBU | LOADHU => unsafe { + memory.read(d.as_canonical_u32(), a.as_canonical_u32()) + }, + }; + + ((prev_data, read_data), shift_amount) + } + + fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) + where + Mem: GuestMemory, + { + // TODO(ayush): remove duplication with read + let &Instruction { + opcode, + a, + b, + c, + d, + e, + f: enabled, + g, + .. + } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + debug_assert!(e.as_canonical_u32() != RV32_IMM_AS); + + let local_opcode = Rv32LoadStoreOpcode::from_usize( + opcode.local_opcode_idx(Rv32LoadStoreOpcode::CLASS_OFFSET), + ); + + let rs1_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = + unsafe { memory.read(d.as_canonical_u32(), b.as_canonical_u32()) }; + let rs1_val = u32::from_le_bytes(rs1_bytes); + + let imm = c.as_canonical_u32(); + let imm_sign = g.as_canonical_u32(); + let imm_extended = imm + imm_sign * 0xffff0000; - fn air(&self) -> &Self::Air { - &self.air + let ptr_val = rs1_val.wrapping_add(imm_extended); + assert!( + ptr_val < (1 << self.pointer_max_bits), + "ptr_val: {ptr_val} = rs1_val: {rs1_val} + imm_extended: {imm_extended} >= 2 ** {}", + self.pointer_max_bits + ); + let shift_amount = ptr_val % 4; + + let ptr_val = ptr_val - shift_amount; // aligned ptr + + let mem_ptr_limbs: [u32; 2] = + array::from_fn(|i| ((ptr_val >> (i * (RV32_CELL_BITS * 2))) & 0xffff)); + + if enabled != F::ZERO { + match local_opcode { + STOREW | STOREH | STOREB => { + let ptr = mem_ptr_limbs[0] + mem_ptr_limbs[1] * (1 << (RV32_CELL_BITS * 2)); + unsafe { memory.write(e.as_canonical_u32(), ptr & 0xfffffffc, data) }; + } + LOADW | LOADB | LOADH | LOADBU | LOADHU => unsafe { + memory.write(d.as_canonical_u32(), a.as_canonical_u32(), data); + }, + } + } } } diff --git a/extensions/rv32im/circuit/src/adapters/mod.rs b/extensions/rv32im/circuit/src/adapters/mod.rs index e026a7b8ce..e4370e547b 100644 --- a/extensions/rv32im/circuit/src/adapters/mod.rs +++ b/extensions/rv32im/circuit/src/adapters/mod.rs @@ -1,11 +1,11 @@ use std::ops::Mul; use openvm_circuit::system::memory::{ - offline_checker::{MemoryReadAuxCols, MemoryWriteAuxCols}, + offline_checker::{MemoryBaseAuxCols, MemoryReadAuxCols, MemoryWriteAuxCols}, online::TracingMemory, MemoryController, RecordId, }; -use openvm_instructions::riscv::{RV32_IMM_AS, RV32_REGISTER_AS}; +use openvm_instructions::riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}; use openvm_stark_backend::p3_field::{FieldAlgebra, PrimeField32}; mod alu; @@ -52,35 +52,41 @@ pub fn decompose(value: u32) -> [F; RV32_REGISTER_NUM_LIMBS] { } /// Atomic read operation which increments the timestamp by 1. -/// Returns `(t_prev, [reg_ptr:4]_1)` where `t_prev` is the timestamp of the last memory access. +/// Returns `(t_prev, [ptr:4]_{address_space})` where `t_prev` is the timestamp of the last memory access. #[inline(always)] -pub fn timed_read_reg( +pub fn timed_read( memory: &mut TracingMemory, - reg_ptr: u32, + address_space: u32, + ptr: u32, ) -> (u32, [u8; RV32_REGISTER_NUM_LIMBS]) { + debug_assert!(address_space == RV32_REGISTER_AS || address_space == RV32_MEMORY_AS); + // SAFETY: - // - address space `RV32_REGISTER_AS` will always have cell type `u8` and minimum alignment of - // `RV32_REGISTER_NUM_LIMBS` + // - address space `RV32_REGISTER_AS` and `RV32_MEMORY_ASwill always have cell type `u8` and + // minimum alignment of `RV32_REGISTER_NUM_LIMBS` unsafe { - memory - .read::(RV32_REGISTER_AS, reg_ptr) + memory.read::(address_space, ptr) } } #[inline(always)] -pub fn timed_write_reg( +pub fn timed_write( memory: &mut TracingMemory, - reg_ptr: u32, - reg_val: &[u8; RV32_REGISTER_NUM_LIMBS], + address_space: u32, + ptr: u32, + val: &[u8; RV32_REGISTER_NUM_LIMBS], ) -> (u32, [u8; RV32_REGISTER_NUM_LIMBS]) { + // TODO(ayush): should this allow hint address space + debug_assert!(address_space == RV32_REGISTER_AS || address_space == RV32_MEMORY_AS); + // SAFETY: - // - address space `RV32_REGISTER_AS` will always have cell type `u8` and minimum alignment of - // `RV32_REGISTER_NUM_LIMBS` + // - address space `RV32_REGISTER_AS` and `RV32_MEMORY_ASwill always have cell type `u8` and + // minimum alignment of `RV32_REGISTER_NUM_LIMBS` unsafe { memory.write::( - RV32_REGISTER_AS, - reg_ptr, - reg_val, + address_space, + ptr, + val, ) } } @@ -88,14 +94,18 @@ pub fn timed_write_reg( /// Reads register value at `reg_ptr` from memory and records the memory access in mutable buffer. /// Trace generation relevant to this memory access can be done fully from the recorded buffer. #[inline(always)] -pub fn tracing_read_reg( +pub fn tracing_read( memory: &mut TracingMemory, - reg_ptr: u32, - (reg_ptr_mut, aux_cols): (&mut F, &mut MemoryReadAuxCols), /* TODO[jpw]: switch to raw u8 - * buffer */ -) -> [u8; RV32_REGISTER_NUM_LIMBS] { - let (t_prev, data) = timed_read_reg(memory, reg_ptr); - *reg_ptr_mut = F::from_canonical_u32(reg_ptr); + address_space: u32, + ptr: u32, + aux_cols: &mut MemoryReadAuxCols, /* TODO[jpw]: switch to raw u8 + * buffer */ +) -> [u8; RV32_REGISTER_NUM_LIMBS] +where + F: PrimeField32, +{ + // TODO(ayush): should this allow hint address space + let (t_prev, data) = timed_read(memory, address_space, ptr); aux_cols.set_prev(F::from_canonical_u32(t_prev)); data } @@ -103,50 +113,57 @@ pub fn tracing_read_reg( /// Writes `reg_ptr, reg_val` into memory and records the memory access in mutable buffer. /// Trace generation relevant to this memory access can be done fully from the recorded buffer. #[inline(always)] -pub fn tracing_write_reg( +pub fn tracing_write( memory: &mut TracingMemory, - reg_ptr: u32, - reg_val: &[u8; RV32_REGISTER_NUM_LIMBS], - (reg_ptr_mut, aux_cols): (&mut F, &mut MemoryWriteAuxCols), /* TODO[jpw]: switch to raw u8 - * buffer */ -) { - let (t_prev, data_prev) = timed_write_reg(memory, reg_ptr, reg_val); - *reg_ptr_mut = F::from_canonical_u32(reg_ptr); + address_space: u32, + ptr: u32, + val: &[u8; RV32_REGISTER_NUM_LIMBS], + aux_cols: &mut MemoryWriteAuxCols, /* TODO[jpw]: switch to raw u8 + * buffer */ +) where + F: PrimeField32, +{ + let (t_prev, data_prev) = timed_write(memory, address_space, ptr, val); aux_cols.set_prev( F::from_canonical_u32(t_prev), data_prev.map(F::from_canonical_u8), ); } -/// Reads register value at `reg_ptr` from memory and records the memory access in mutable buffer. -/// Trace generation relevant to this memory access can be done fully from the recorded buffer. -/// -/// Assumes that `addr_space` is [RV32_IMM_AS] or [RV32_REGISTER_AS]. +// TODO(ayush): this is bad but not sure how to avoid #[inline(always)] -pub fn tracing_read_reg_or_imm( +pub fn tracing_write_with_base_aux( memory: &mut TracingMemory, - addr_space: u32, - reg_ptr_or_imm: u32, - addr_space_mut: &mut F, - (reg_ptr_or_imm_mut, aux_cols): (&mut F, &mut MemoryReadAuxCols), -) -> [u8; RV32_REGISTER_NUM_LIMBS] { - debug_assert!(addr_space == RV32_IMM_AS || addr_space == RV32_REGISTER_AS); - if addr_space == RV32_IMM_AS { - *addr_space_mut = F::ZERO; - let imm = reg_ptr_or_imm; - *reg_ptr_or_imm_mut = F::from_canonical_u32(imm); - debug_assert_eq!(imm >> 24, 0); // highest byte should be zero to prevent overflow - memory.increment_timestamp(); - let mut imm_le = imm.to_le_bytes(); - // Important: we set the highest byte equal to the second highest byte, using the assumption - // that imm is at most 24 bits - imm_le[3] = imm_le[2]; - imm_le - } else { - *addr_space_mut = F::ONE; // F::from_canonical_u32(RV32_REGISTER_AS) - let reg_ptr = reg_ptr_or_imm; - tracing_read_reg(memory, reg_ptr, (reg_ptr_or_imm_mut, aux_cols)) - } + address_space: u32, + ptr: u32, + val: &[u8; RV32_REGISTER_NUM_LIMBS], + base_aux_cols: &mut MemoryBaseAuxCols, +) where + F: PrimeField32, +{ + let (t_prev, _) = timed_write(memory, address_space, ptr, val); + base_aux_cols.set_prev(F::from_canonical_u32(t_prev)); +} + +#[inline(always)] +pub fn tracing_read_imm( + memory: &mut TracingMemory, + imm: u32, + imm_mut: &mut F, +) -> [u8; RV32_REGISTER_NUM_LIMBS] +where + F: PrimeField32, +{ + *imm_mut = F::from_canonical_u32(imm); + debug_assert_eq!(imm >> 24, 0); // highest byte should be zero to prevent overflow + + memory.increment_timestamp(); + + let mut imm_le = imm.to_le_bytes(); + // Important: we set the highest byte equal to the second highest byte, using the assumption + // that imm is at most 24 bits + imm_le[3] = imm_le[2]; + imm_le } // TODO: delete diff --git a/extensions/rv32im/circuit/src/adapters/mul.rs b/extensions/rv32im/circuit/src/adapters/mul.rs index 9c026ef8e7..84feaea7b8 100644 --- a/extensions/rv32im/circuit/src/adapters/mul.rs +++ b/extensions/rv32im/circuit/src/adapters/mul.rs @@ -1,20 +1,14 @@ -use std::{ - borrow::{Borrow, BorrowMut}, - marker::PhantomData, -}; +use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge, - ExecutionBus, ExecutionState, MinimalInstruction, Result, VmAdapterAir, VmAdapterChip, - VmAdapterInterface, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, + ExecutionBridge, ExecutionState, MinimalInstruction, VmAdapterAir, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, - MemoryAddress, MemoryController, OfflineMemory, RecordId, - }, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, + online::{GuestMemory, TracingMemory}, + MemoryAddress, MemoryAuxColsFactory, RecordId, }, }; use openvm_circuit_primitives_derive::AlignedBorrow; @@ -28,30 +22,8 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use super::RV32_REGISTER_NUM_LIMBS; -use crate::adapters::tmp_convert_to_u8s; - -#[derive(Debug)] -pub struct Rv32MultAdapterChip { - pub air: Rv32MultAdapterAir, - _marker: PhantomData, -} - -impl Rv32MultAdapterChip { - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - ) -> Self { - Self { - air: Rv32MultAdapterAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - }, - _marker: PhantomData, - } - } -} +use super::{tracing_write, RV32_REGISTER_NUM_LIMBS}; +use crate::adapters::tracing_read; #[repr(C)] #[derive(Debug, Serialize, Deserialize)] @@ -168,95 +140,135 @@ impl VmAdapterAir for Rv32MultAdapterAir { } } -impl VmAdapterChip for Rv32MultAdapterChip { - type ReadRecord = Rv32MultReadRecord; - type WriteRecord = Rv32MultWriteRecord; - type Air = Rv32MultAdapterAir; - type Interface = BasicAdapterInterface< - F, - MinimalInstruction, - 2, - 1, - RV32_REGISTER_NUM_LIMBS, - RV32_REGISTER_NUM_LIMBS, - >; +#[derive(derive_new::new)] +pub struct Rv32MultAdapterStep; + +impl AdapterTraceStep for Rv32MultAdapterStep +where + F: PrimeField32, +{ + const WIDTH: usize = size_of::>(); + type ReadData = ([u8; RV32_REGISTER_NUM_LIMBS], [u8; RV32_REGISTER_NUM_LIMBS]); + type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + type TraceContext<'a> = (); - fn preprocess( - &mut self, - memory: &mut MemoryController, + #[inline(always)] + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let adapter_row: &mut Rv32MultAdapterCols = adapter_row.borrow_mut(); + adapter_row.from_state.pc = F::from_canonical_u32(pc); + adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + } + + #[inline(always)] + fn read( + &self, + memory: &mut TracingMemory, instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { - let Instruction { b, c, d, .. } = *instruction; + adapter_row: &mut [F], + ) -> Self::ReadData { + let &Instruction { b, c, d, .. } = instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - let rs1 = memory.read::(d, b); - let rs2 = memory.read::(d, c); - - Ok(( - [ - rs1.1.map(F::from_canonical_u8), - rs2.1.map(F::from_canonical_u8), - ], - Self::ReadRecord { - rs1: rs1.0, - rs2: rs2.0, - }, - )) + let adapter_row: &mut Rv32MultAdapterCols = adapter_row.borrow_mut(); + + adapter_row.rs1_ptr = b; + let rs1 = tracing_read( + memory, + d.as_canonical_u32(), + b.as_canonical_u32(), + &mut adapter_row.reads_aux[0], + ); + adapter_row.rs2_ptr = c; + let rs2 = tracing_read( + memory, + d.as_canonical_u32(), + c.as_canonical_u32(), + &mut adapter_row.reads_aux[1], + ); + + (rs1, rs2) } - fn postprocess( - &mut self, - memory: &mut MemoryController, + #[inline(always)] + fn write( + &self, + memory: &mut TracingMemory, instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - _read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - let Instruction { a, d, .. } = *instruction; - let (rd_id, _) = memory.write(d, a, &tmp_convert_to_u8s(output.writes[0])); + adapter_row: &mut [F], + data: &Self::WriteData, + ) { + let &Instruction { a, d, .. } = instruction; - let timestamp_delta = memory.timestamp() - from_state.timestamp; - debug_assert!( - timestamp_delta == 3, - "timestamp delta is {}, expected 3", - timestamp_delta - ); + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + + let adapter_row: &mut Rv32MultAdapterCols = adapter_row.borrow_mut(); - Ok(( - ExecutionState { - pc: from_state.pc + DEFAULT_PC_STEP, - timestamp: memory.timestamp(), - }, - Self::WriteRecord { from_state, rd_id }, - )) + adapter_row.rd_ptr = a; + tracing_write( + memory, + d.as_canonical_u32(), + a.as_canonical_u32(), + data, + &mut adapter_row.writes_aux, + ) } - fn generate_trace_row( + #[inline(always)] + fn fill_trace_row( &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, + mem_helper: &MemoryAuxColsFactory, + _trace_ctx: Self::TraceContext<'_>, + adapter_row: &mut [F], ) { - let aux_cols_factory = memory.aux_cols_factory(); - let row_slice: &mut Rv32MultAdapterCols<_> = row_slice.borrow_mut(); - row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); - let rd = memory.record_by_id(write_record.rd_id); - row_slice.rd_ptr = rd.pointer; - let rs1 = memory.record_by_id(read_record.rs1); - let rs2 = memory.record_by_id(read_record.rs2); - row_slice.rs1_ptr = rs1.pointer; - row_slice.rs2_ptr = rs2.pointer; - aux_cols_factory.generate_read_aux(rs1, &mut row_slice.reads_aux[0]); - aux_cols_factory.generate_read_aux(rs2, &mut row_slice.reads_aux[1]); - aux_cols_factory.generate_write_aux(rd, &mut row_slice.writes_aux); + let adapter_row: &mut Rv32MultAdapterCols = adapter_row.borrow_mut(); + + let mut timestamp = adapter_row.from_state.timestamp.as_canonical_u32(); + + mem_helper.fill_from_prev(timestamp, adapter_row.reads_aux[0].as_mut()); + timestamp += 1; + + mem_helper.fill_from_prev(timestamp, adapter_row.reads_aux[1].as_mut()); + timestamp += 1; + + mem_helper.fill_from_prev(timestamp, adapter_row.writes_aux.as_mut()); } +} + +impl AdapterExecutorE1 for Rv32MultAdapterStep +where + F: PrimeField32, +{ + // TODO(ayush): directly use u32 + type ReadData = ([u8; RV32_REGISTER_NUM_LIMBS], [u8; RV32_REGISTER_NUM_LIMBS]); + type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + + #[inline(always)] + fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData + where + Mem: GuestMemory, + { + let Instruction { b, c, d, .. } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + + let rs1: [u8; RV32_REGISTER_NUM_LIMBS] = + unsafe { memory.read(d.as_canonical_u32(), b.as_canonical_u32()) }; + let rs2: [u8; RV32_REGISTER_NUM_LIMBS] = + unsafe { memory.read(d.as_canonical_u32(), c.as_canonical_u32()) }; + + (rs1, rs2) + } + + #[inline(always)] + fn write(&self, memory: &mut Mem, instruction: &Instruction, rd: &Self::WriteData) + where + Mem: GuestMemory, + { + let Instruction { a, d, .. } = *instruction; + + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - fn air(&self) -> &Self::Air { - &self.air + unsafe { memory.write(d.as_canonical_u32(), a.as_canonical_u32(), rd) }; } } diff --git a/extensions/rv32im/circuit/src/adapters/rdwrite.rs b/extensions/rv32im/circuit/src/adapters/rdwrite.rs index 0d495ed18b..247f0b854c 100644 --- a/extensions/rv32im/circuit/src/adapters/rdwrite.rs +++ b/extensions/rv32im/circuit/src/adapters/rdwrite.rs @@ -1,26 +1,39 @@ -use std::borrow::Borrow; +use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, BasicAdapterInterface, ExecutionBridge, ExecutionState, ImmInstruction, - VmAdapterAir, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, + ExecutionBridge, ExecutionState, ImmInstruction, VmAdapterAir, }, system::memory::{ offline_checker::{MemoryBridge, MemoryWriteAuxCols}, - MemoryAddress, + online::{GuestMemory, TracingMemory}, + MemoryAddress, MemoryAuxColsFactory, RecordId, }, }; use openvm_circuit_primitives::utils::not; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{program::DEFAULT_PC_STEP, riscv::RV32_REGISTER_AS}; +use openvm_instructions::{ + instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_REGISTER_AS, +}; use openvm_stark_backend::{ interaction::InteractionBuilder, p3_air::{AirBuilder, BaseAir}, - p3_field::{Field, FieldAlgebra}, + p3_field::{Field, FieldAlgebra, PrimeField32}, }; +use serde::{Deserialize, Serialize}; + +use crate::adapters::tracing_write; use super::RV32_REGISTER_NUM_LIMBS; +#[repr(C)] +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Rv32RdWriteWriteRecord { + pub from_state: ExecutionState, + pub rd_id: Option, +} + #[repr(C)] #[derive(Debug, Clone, AlignedBorrow)] pub struct Rv32RdWriteAdapterCols { @@ -182,3 +195,222 @@ impl VmAdapterAir for Rv32CondRdWriteAdapterAir { cols.inner.from_state.pc } } + +/// This adapter doesn't read anything, and writes to \[a:4\]_d, where d == 1 +#[derive(derive_new::new)] +pub struct Rv32RdWriteAdapterStep; + +impl AdapterTraceStep for Rv32RdWriteAdapterStep +where + F: PrimeField32, +{ + const WIDTH: usize = size_of::>(); + + type ReadData = (); + type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + type TraceContext<'a> = (); + + #[inline(always)] + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let adapter_row: &mut Rv32RdWriteAdapterCols = adapter_row.borrow_mut(); + adapter_row.from_state.pc = F::from_canonical_u32(pc); + adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + } + + #[inline(always)] + fn read( + &self, + _memory: &mut TracingMemory, + _instruction: &Instruction, + _adapter_row: &mut [F], + ) -> Self::ReadData { + } + + #[inline(always)] + fn write( + &self, + memory: &mut TracingMemory, + instruction: &Instruction, + adapter_row: &mut [F], + data: &Self::WriteData, + ) { + let &Instruction { a, d, .. } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + + let adapter_row: &mut Rv32RdWriteAdapterCols = adapter_row.borrow_mut(); + + adapter_row.rd_ptr = a; + tracing_write( + memory, + d.as_canonical_u32(), + a.as_canonical_u32(), + data, + &mut adapter_row.rd_aux_cols, + ); + } + + #[inline(always)] + fn fill_trace_row( + &self, + mem_helper: &MemoryAuxColsFactory, + _trace_ctx: Self::TraceContext<'_>, + adapter_row: &mut [F], + ) { + let adapter_row: &mut Rv32RdWriteAdapterCols = adapter_row.borrow_mut(); + + let timestamp = adapter_row.from_state.timestamp.as_canonical_u32(); + + mem_helper.fill_from_prev(timestamp, adapter_row.rd_aux_cols.as_mut()); + } +} + +impl AdapterExecutorE1 for Rv32RdWriteAdapterStep +where + F: PrimeField32, +{ + type ReadData = (); + type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + + #[inline(always)] + fn read(&self, _memory: &mut Mem, _instruction: &Instruction) -> Self::ReadData + where + Mem: GuestMemory, + { + } + + #[inline(always)] + fn write(&self, memory: &mut Mem, instruction: &Instruction, rd: &Self::WriteData) + where + Mem: GuestMemory, + { + let Instruction { a, d, .. } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + + unsafe { memory.write(d.as_canonical_u32(), a.as_canonical_u32(), rd) }; + } +} + +/// This adapter doesn't read anything, and **maybe** writes to \[a:4\]_d, where d == 1 +#[derive(derive_new::new)] +pub struct Rv32CondRdWriteAdapterStep { + inner: Rv32RdWriteAdapterStep, +} + +impl AdapterTraceStep for Rv32CondRdWriteAdapterStep +where + F: PrimeField32, +{ + const WIDTH: usize = size_of::>(); + type ReadData = (); + type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + type TraceContext<'a> = (); + + #[inline(always)] + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let adapter_row: &mut Rv32CondRdWriteAdapterCols = adapter_row.borrow_mut(); + + adapter_row.inner.from_state.pc = F::from_canonical_u32(pc); + adapter_row.inner.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + } + + #[inline(always)] + fn read( + &self, + memory: &mut TracingMemory, + instruction: &Instruction, + adapter_row: &mut [F], + ) -> Self::ReadData { + >::read( + &self.inner, + memory, + instruction, + adapter_row, + ) + } + + #[inline(always)] + fn write( + &self, + memory: &mut TracingMemory, + instruction: &Instruction, + adapter_row: &mut [F], + data: &Self::WriteData, + ) { + let Instruction { f: enabled, .. } = instruction; + + if *enabled != F::ZERO { + let (inner_row, needs_write) = unsafe { + adapter_row.split_at_mut_unchecked(size_of::>()) + }; + + needs_write[0] = F::ONE; + >::write( + &self.inner, + memory, + instruction, + inner_row, + data, + ); + } else { + memory.increment_timestamp(); + } + } + + #[inline(always)] + fn fill_trace_row( + &self, + mem_helper: &MemoryAuxColsFactory, + trace_ctx: Self::TraceContext<'_>, + adapter_row: &mut [F], + ) { + let adapter_row_ref: &mut Rv32CondRdWriteAdapterCols = adapter_row.borrow_mut(); + + if adapter_row_ref.needs_write.is_one() { + let (inner_row, _) = unsafe { + adapter_row.split_at_mut_unchecked(size_of::>()) + }; + + >::fill_trace_row( + &self.inner, + mem_helper, + trace_ctx, + inner_row, + ) + } + } +} + +impl AdapterExecutorE1 for Rv32CondRdWriteAdapterStep +where + F: PrimeField32, +{ + type ReadData = (); + type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + + #[inline(always)] + fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData + where + Mem: GuestMemory, + { + >::read(&self.inner, memory, instruction) + } + + #[inline(always)] + fn write(&self, memory: &mut Mem, instruction: &Instruction, rd: &Self::WriteData) + where + Mem: GuestMemory, + { + let Instruction { f: enabled, .. } = instruction; + + if *enabled != F::ZERO { + >::write( + &self.inner, + memory, + instruction, + rd, + ) + } + } +} diff --git a/extensions/rv32im/circuit/src/auipc/core.rs b/extensions/rv32im/circuit/src/auipc/core.rs index 95971ec0cf..e281b5b81c 100644 --- a/extensions/rv32im/circuit/src/auipc/core.rs +++ b/extensions/rv32im/circuit/src/auipc/core.rs @@ -1,13 +1,12 @@ use std::{ - array, - array::from_fn, + array::{self, from_fn}, borrow::{Borrow, BorrowMut}, }; use openvm_circuit::{ arch::{ - AdapterAirContext, ImmInstruction, InsExecutorE1, Result, SingleTraceStep, - VmAdapterInterface, VmCoreAir, VmExecutionState, VmStateMut, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ImmInstruction, Result, + SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -21,7 +20,6 @@ use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{ instruction::Instruction, program::{DEFAULT_PC_STEP, PC_BITS}, - riscv::RV32_REGISTER_AS, LocalOpcode, }; use openvm_rv32im_transpiler::Rv32AuipcOpcode::{self, *}; @@ -33,11 +31,7 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use crate::adapters::{ - tracing_write_reg, Rv32RdWriteAdapterCols, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, -}; - -pub(super) const ADAPTER_WIDTH: usize = size_of::>(); +use crate::adapters::{Rv32RdWriteAdapterCols, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; #[repr(C)] #[derive(Debug, Clone, AlignedBorrow)] @@ -50,7 +44,7 @@ pub struct Rv32AuipcCoreCols { pub rd_data: [T; RV32_REGISTER_NUM_LIMBS], } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, derive_new::new)] pub struct Rv32AuipcCoreAir { pub bus: BitwiseOperationLookupBus, } @@ -207,64 +201,87 @@ pub struct Rv32AuipcCoreRecord { pub rd_data: [F; RV32_REGISTER_NUM_LIMBS], } -pub struct Rv32AuipcCoreChip { - // TODO[jpw]: do we still need air in here? - pub air: Rv32AuipcCoreAir, +pub struct Rv32AuipcStep { + adapter: A, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, } -impl Rv32AuipcCoreChip { - pub fn new(bitwise_lookup_chip: SharedBitwiseOperationLookupChip) -> Self { +impl Rv32AuipcStep { + pub fn new( + adapter: A, + bitwise_lookup_chip: SharedBitwiseOperationLookupChip, + ) -> Self { Self { - air: Rv32AuipcCoreAir { - bus: bitwise_lookup_chip.bus(), - }, + adapter, bitwise_lookup_chip, } } } -impl SingleTraceStep for Rv32AuipcCoreChip { +impl SingleTraceStep for Rv32AuipcStep +where + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = (), + WriteData = [u8; RV32_REGISTER_NUM_LIMBS], + TraceContext<'a> = (), + >, +{ + fn get_opcode_name(&self, _: usize) -> String { + format!("{:?}", AUIPC) + } + fn execute( &mut self, state: VmStateMut, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { - let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(ADAPTER_WIDTH) }; - let adapter_row: &mut Rv32RdWriteAdapterCols = adapter_row.borrow_mut(); + let Instruction { opcode, c: imm, .. } = instruction; + + let local_opcode = + Rv32AuipcOpcode::from_usize(opcode.local_opcode_idx(Rv32AuipcOpcode::CLASS_OFFSET)); + + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + A::start(*state.pc, state.memory, adapter_row); + + let imm_u32 = imm.as_canonical_u32(); + let rd = run_auipc(local_opcode, *state.pc, imm_u32); + let core_row: &mut Rv32AuipcCoreCols = core_row.borrow_mut(); + core_row.rd_data = rd.map(F::from_canonical_u8); - state.ins_start(&mut adapter_row.from_state); - let imm = instruction.c.as_canonical_u32(); - let rd_data = run_auipc(Rv32AuipcOpcode::AUIPC, *state.pc, imm); - - debug_assert_eq!(instruction.d.as_canonical_u32(), RV32_REGISTER_AS); - let rd_ptr = instruction.a.as_canonical_u32(); - tracing_write_reg( - state.memory, - rd_ptr, - &rd_data, - (&mut adapter_row.rd_ptr, &mut adapter_row.rd_aux_cols), - ); - core_row.rd_data = rd_data.map(F::from_canonical_u8); + // TODO(ayush): see if there's a better way // We decompose during fill_trace_row later: - core_row.imm_limbs[0] = instruction.c; + core_row.imm_limbs[0] = *imm; + + self.adapter + .write(state.memory, instruction, adapter_row, &rd); + + // TODO(ayush): add increment_pc function to vmstate + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); - *state.pc += DEFAULT_PC_STEP; Ok(()) } fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { - let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(ADAPTER_WIDTH) }; - let adapter_row: &mut Rv32RdWriteAdapterCols = adapter_row.borrow_mut(); + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + self.adapter.fill_trace_row(mem_helper, (), adapter_row); + let core_row: &mut Rv32AuipcCoreCols = core_row.borrow_mut(); - core_row.is_valid = F::ONE; - let timestamp = adapter_row.from_state.timestamp.as_canonical_u32(); - mem_helper.fill_from_prev(timestamp, adapter_row.rd_aux_cols.as_mut()); + core_row.is_valid = F::ONE; + // TODO(ayush): this is bad since we're treating adapters as generic. maybe + // add a .state() function to adapters or get_from_pc like in air + let adapter_row: &mut Rv32RdWriteAdapterCols = adapter_row.borrow_mut(); let from_pc = adapter_row.from_state.pc.as_canonical_u32(); + let pc_limbs = from_pc.to_le_bytes(); let imm = core_row.imm_limbs[0].as_canonical_u32(); let imm_limbs = imm.to_le_bytes(); @@ -272,6 +289,7 @@ impl SingleTraceStep for Rv32AuipcCoreChip { core_row.imm_limbs = from_fn(|i| F::from_canonical_u8(imm_limbs[i])); // only the middle 2 limbs: core_row.pc_limbs = from_fn(|i| F::from_canonical_u8(pc_limbs[i + 1])); + // range checks: let rd_data = core_row.rd_data.map(|x| x.as_canonical_u32()); for pair in rd_data.chunks_exact(2) { @@ -283,48 +301,46 @@ impl SingleTraceStep for Rv32AuipcCoreChip { .request_range(imm_limbs[0] as u32, imm_limbs[1] as u32); self.bitwise_lookup_chip .request_range(imm_limbs[2] as u32, pc_limbs[1] as u32); + let msl_shift = RV32_REGISTER_NUM_LIMBS * RV32_CELL_BITS - PC_BITS; self.bitwise_lookup_chip .request_range(pc_limbs[2] as u32, (pc_limbs[3] as u32) << msl_shift); } - - fn get_opcode_name(&self, _: usize) -> String { - format!("{:?}", AUIPC) - } } -impl InsExecutorE1 for Rv32AuipcCoreChip +impl StepExecutorE1 for Rv32AuipcStep where - Mem: GuestMemory, F: PrimeField32, + A: 'static + + for<'a> AdapterExecutorE1, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, a, c: imm, .. - } = instruction; + ) -> Result<()> + where + Mem: GuestMemory, + { + let Instruction { opcode, c: imm, .. } = instruction; let local_opcode = Rv32AuipcOpcode::from_usize(opcode.local_opcode_idx(Rv32AuipcOpcode::CLASS_OFFSET)); let imm = imm.as_canonical_u32(); - let rd_bytes = run_auipc(local_opcode, state.pc, imm); + let rd = run_auipc(local_opcode, *state.pc, imm); - let rd_addr = a.as_canonical_u32(); - unsafe { - state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes); - } + self.adapter.write(state.memory, instruction, &rd); - state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } } // returns rd_data +// TODO(ayush): remove _opcode +#[inline(always)] pub(super) fn run_auipc( _opcode: Rv32AuipcOpcode, pc: u32, diff --git a/extensions/rv32im/circuit/src/auipc/mod.rs b/extensions/rv32im/circuit/src/auipc/mod.rs index 776a7beb95..15036753bd 100644 --- a/extensions/rv32im/circuit/src/auipc/mod.rs +++ b/extensions/rv32im/circuit/src/auipc/mod.rs @@ -1,6 +1,6 @@ use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; -use crate::adapters::Rv32RdWriteAdapterAir; +use crate::adapters::{Rv32RdWriteAdapterAir, Rv32RdWriteAdapterStep}; mod core; pub use core::*; @@ -9,4 +9,5 @@ pub use core::*; mod tests; pub type Rv32AuipcAir = VmAirWrapper; -pub type Rv32AuipcChip = NewVmChipWrapper; +pub type Rv32AuipcStepWithAdapter = Rv32AuipcStep; +pub type Rv32AuipcChip = NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/auipc/tests.rs b/extensions/rv32im/circuit/src/auipc/tests.rs index 22a080fb6f..2ac4a0d30b 100644 --- a/extensions/rv32im/circuit/src/auipc/tests.rs +++ b/extensions/rv32im/circuit/src/auipc/tests.rs @@ -17,9 +17,13 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; -use super::{run_auipc, Rv32AuipcChip, Rv32AuipcCoreChip, Rv32AuipcCoreCols, ADAPTER_WIDTH}; -use crate::adapters::{Rv32RdWriteAdapterAir, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; +use super::{run_auipc, Rv32AuipcChip, Rv32AuipcCoreAir, Rv32AuipcCoreCols, Rv32AuipcStep}; +use crate::adapters::{ + Rv32RdWriteAdapterAir, Rv32RdWriteAdapterCols, Rv32RdWriteAdapterStep, RV32_CELL_BITS, + RV32_REGISTER_NUM_LIMBS, +}; +const ADAPTER_WIDTH: usize = size_of::>(); const IMM_BITS: usize = 24; const BITWISE_OP_LOOKUP_BUS: BusIndex = 9; const MAX_INS_CAPACITY: usize = 128; @@ -34,13 +38,18 @@ fn create_test_chip( ) { let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let adapter_air = Rv32RdWriteAdapterAir::new(tester.memory_bridge(), tester.execution_bridge()); - let core = Rv32AuipcCoreChip::new(bitwise_chip.clone()); - let air = VmAirWrapper::new(adapter_air, core.air); - ( - Rv32AuipcChip::::new(air, core, MAX_INS_CAPACITY, tester.memory_helper()), - bitwise_chip, - ) + + let chip = Rv32AuipcChip::::new( + VmAirWrapper::new( + Rv32RdWriteAdapterAir::new(tester.memory_bridge(), tester.execution_bridge()), + Rv32AuipcCoreAir::new(bitwise_bus), + ), + Rv32AuipcStep::new(Rv32RdWriteAdapterStep::new(), bitwise_chip.clone()), + MAX_INS_CAPACITY, + tester.memory_helper(), + ); + + (chip, bitwise_chip) } fn set_and_execute( @@ -80,7 +89,7 @@ fn rand_auipc_test() { let mut tester = VmChipTestBuilder::default(); let (mut chip, bitwise_chip) = create_test_chip(&tester); - let num_tests: usize = 100; + let num_tests: usize = 1; for _ in 0..num_tests { set_and_execute(&mut tester, &mut chip, &mut rng, AUIPC, None, None); } diff --git a/extensions/rv32im/circuit/src/base_alu/core.rs b/extensions/rv32im/circuit/src/base_alu/core.rs index d945db8e63..c8cd3f3708 100644 --- a/extensions/rv32im/circuit/src/base_alu/core.rs +++ b/extensions/rv32im/circuit/src/base_alu/core.rs @@ -2,13 +2,12 @@ use std::{ array, borrow::{Borrow, BorrowMut}, iter::zip, - marker::PhantomData, }; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterTraceStep, InsExecutorE1, MinimalInstruction, Result, - SingleTraceStep, VmAdapterInterface, VmCoreAir, VmExecutionState, VmStateMut, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, + SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -22,12 +21,7 @@ use openvm_circuit_primitives::{ utils::not, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{ - instruction::Instruction, - program::DEFAULT_PC_STEP, - riscv::{RV32_IMM_AS, RV32_REGISTER_AS}, - LocalOpcode, -}; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; use openvm_rv32im_transpiler::BaseAluOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -51,10 +45,10 @@ pub struct BaseAluCoreCols { pub opcode_and_flag: T, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, derive_new::new)] pub struct BaseAluCoreAir { pub bus: BitwiseOperationLookupBus, - offset: usize, + pub offset: usize, } impl BaseAir @@ -178,52 +172,85 @@ where } } -pub struct BaseAluCoreChip { - pub air: BaseAluCoreAir, +pub struct BaseAluStep { + adapter: A, + pub offset: usize, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, } -impl BaseAluCoreChip { +impl BaseAluStep { pub fn new( + adapter: A, bitwise_lookup_chip: SharedBitwiseOperationLookupChip, offset: usize, ) -> Self { Self { - air: BaseAluCoreAir { - bus: bitwise_lookup_chip.bus(), - offset, - }, + adapter, + offset, bitwise_lookup_chip, } } +} - #[inline] - pub fn execute( - &self, +impl SingleTraceStep + for BaseAluStep +where + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), + WriteData = [u8; NUM_LIMBS], + TraceContext<'a> = &'a BitwiseOperationLookupChip, + >, +{ + fn get_opcode_name(&self, opcode: usize) -> String { + format!("{:?}", BaseAluOpcode::from_usize(opcode - self.offset)) + } + + fn execute( + &mut self, + state: VmStateMut, instruction: &Instruction, - [x, y]: [[u8; NUM_LIMBS]; 2], - core_row: &mut [F], - ) -> [u8; NUM_LIMBS] { - let opcode = instruction.opcode; - let local_opcode = BaseAluOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); + row_slice: &mut [F], + ) -> Result<()> { + let Instruction { opcode, .. } = instruction; + + let local_opcode = BaseAluOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - let z = run_alu::(local_opcode, &x, &y); - println!("{local_opcode:?} {x:?}, {y:?}: {z:?}"); + A::start(*state.pc, state.memory, adapter_row); + + let (rs1, rs2) = self.adapter.read(state.memory, instruction, adapter_row); + + let rd = run_alu::(local_opcode, &rs1, &rs2); let core_row: &mut BaseAluCoreCols = core_row.borrow_mut(); - core_row.a = z.map(F::from_canonical_u8); - core_row.b = x.map(F::from_canonical_u8); - core_row.c = y.map(F::from_canonical_u8); + core_row.a = rd.map(F::from_canonical_u8); + core_row.b = rs1.map(F::from_canonical_u8); + core_row.c = rs2.map(F::from_canonical_u8); core_row.opcode_add_flag = F::from_bool(local_opcode == BaseAluOpcode::ADD); core_row.opcode_sub_flag = F::from_bool(local_opcode == BaseAluOpcode::SUB); core_row.opcode_xor_flag = F::from_bool(local_opcode == BaseAluOpcode::XOR); core_row.opcode_or_flag = F::from_bool(local_opcode == BaseAluOpcode::OR); core_row.opcode_and_flag = F::from_bool(local_opcode == BaseAluOpcode::AND); - z + self.adapter + .write(state.memory, instruction, adapter_row, &rd); + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) } - pub fn fill_trace_row(&self, core_row: &mut [F]) { + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + self.adapter + .fill_trace_row(mem_helper, self.bitwise_lookup_chip.as_ref(), adapter_row); + let core_row: &mut BaseAluCoreCols = core_row.borrow_mut(); if core_row.opcode_add_flag == F::ONE || core_row.opcode_sub_flag == F::ONE { @@ -240,109 +267,40 @@ impl BaseAluCoreChip { - pub core: BaseAluCoreChip, - phantom: PhantomData, -} - -impl SingleTraceStep +impl StepExecutorE1 for BaseAluStep where F: PrimeField32, A: 'static - + for<'a> AdapterTraceStep< + + for<'a> AdapterExecutorE1< F, - CTX, - ReadData = [[u8; NUM_LIMBS]; 2], + ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), WriteData = [u8; NUM_LIMBS], - TraceContext<'a> = &'a BitwiseOperationLookupChip, >, { - fn execute( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - row_slice: &mut [F], - ) -> Result<()> { - let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - - A::start(*state.pc, state.memory, adapter_row); - let [rs1, rs2] = A::read(state.memory, instruction, adapter_row); - let output = self.core.execute(instruction, [rs1, rs2], core_row); - A::write(state.memory, instruction, adapter_row, &output); - - *state.pc += DEFAULT_PC_STEP; - Ok(()) - } - - fn get_opcode_name(&self, opcode: usize) -> String { - format!( - "{:?}", - BaseAluOpcode::from_usize(opcode - self.core.air.offset) - ) - } - - fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { - let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - A::fill_trace_row( - mem_helper, - self.core.bitwise_lookup_chip.as_ref(), - adapter_row, - ); - self.core.fill_trace_row(core_row); - } -} - -impl InsExecutorE1 - for BaseAluCoreChip -where - Mem: GuestMemory, - F: PrimeField32, -{ - fn execute_e1( - &mut self, - state: &mut VmExecutionState, - instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, a, b, c, e, .. - } = instruction; - - let local_opcode = BaseAluOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - - let rs1_addr = b.as_canonical_u32(); - let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; - - let rs2_bytes = if e.as_canonical_u32() == RV32_IMM_AS { - // Use immediate value - let imm = c.as_canonical_u32(); - // Convert imm from u32 to [u8; NUM_LIMBS] - let imm_bytes = imm.to_le_bytes(); - // TODO(ayush): remove this - let mut rs2_bytes = [0u8; NUM_LIMBS]; - rs2_bytes[..NUM_LIMBS].copy_from_slice(&imm_bytes[..NUM_LIMBS]); - rs2_bytes - } else { - // Read from register - let rs2_addr = c.as_canonical_u32(); - let rs2_bytes: [u8; NUM_LIMBS] = - unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; - rs2_bytes - }; + ) -> Result<()> + where + Mem: GuestMemory, + { + let Instruction { opcode, .. } = instruction; - let rd_bytes = run_alu::(local_opcode, &rs1_bytes, &rs2_bytes); + let local_opcode = BaseAluOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - // Write result back to destination register - let rd_addr = a.as_canonical_u32(); - unsafe { state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes) }; + let (rs1, rs2) = self.adapter.read(state.memory, instruction); + let rd = run_alu::(local_opcode, &rs1, &rs2); + self.adapter.write(state.memory, instruction, &rd); - state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } } +#[inline(always)] pub(super) fn run_alu( opcode: BaseAluOpcode, x: &[u8; NUM_LIMBS], @@ -358,6 +316,7 @@ pub(super) fn run_alu( } } +#[inline(always)] fn run_add( x: &[u8; NUM_LIMBS], y: &[u8; NUM_LIMBS], @@ -374,6 +333,7 @@ fn run_add( z } +#[inline(always)] fn run_subtract( x: &[u8; NUM_LIMBS], y: &[u8; NUM_LIMBS], @@ -393,14 +353,17 @@ fn run_subtract( z } +#[inline(always)] fn run_xor(x: &[u8; NUM_LIMBS], y: &[u8; NUM_LIMBS]) -> [u8; NUM_LIMBS] { array::from_fn(|i| x[i] ^ y[i]) } +#[inline(always)] fn run_or(x: &[u8; NUM_LIMBS], y: &[u8; NUM_LIMBS]) -> [u8; NUM_LIMBS] { array::from_fn(|i| x[i] | y[i]) } +#[inline(always)] fn run_and(x: &[u8; NUM_LIMBS], y: &[u8; NUM_LIMBS]) -> [u8; NUM_LIMBS] { array::from_fn(|i| x[i] & y[i]) } diff --git a/extensions/rv32im/circuit/src/base_alu/tests.rs b/extensions/rv32im/circuit/src/base_alu/tests.rs index d1dcec6ada..cc20c50181 100644 --- a/extensions/rv32im/circuit/src/base_alu/tests.rs +++ b/extensions/rv32im/circuit/src/base_alu/tests.rs @@ -2,17 +2,25 @@ use std::borrow::BorrowMut; use openvm_circuit::{ arch::{ - testing::{TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - NewVmChipWrapper, VmAirWrapper, VmChipWrapper, + testing::{ + test_adapter::TestAdapterAir, TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS, + }, + AdapterTraceStep, NewVmChipWrapper, VmAirWrapper, VmChipWrapper, }, + system::memory::{online::TracingMemory, MemoryAuxColsFactory}, utils::generate_long_number, }; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, }; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; +use openvm_instructions::{ + instruction::Instruction, + riscv::{RV32_IMM_AS, RV32_REGISTER_AS}, + LocalOpcode, +}; use openvm_rv32im_transpiler::BaseAluOpcode; use openvm_stark_backend::{ + p3_air::BaseAir, p3_field::{FieldAlgebra, PrimeField32}, p3_matrix::{ dense::{DenseMatrix, RowMajorMatrix}, @@ -25,18 +33,20 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::Rng; -use super::{core::run_alu, BaseAluCoreChip, Rv32BaseAluChip, Rv32BaseAluStep}; +use super::{core::run_alu, BaseAluCoreAir, BaseAluStep, Rv32BaseAluChip, Rv32BaseAluStep}; use crate::{ adapters::{ - Rv32BaseAluAdapterAir, Rv32BaseAluAdapterCols, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + tracing_read, tracing_read_imm, Rv32BaseAluAdapterAir, Rv32BaseAluAdapterCols, + Rv32BaseAluAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, }, base_alu::BaseAluCoreCols, test_utils::{generate_rv32_is_type_immediate, rv32_rand_write_register_or_imm}, }; -type F = BabyBear; const MAX_INS_CAPACITY: usize = 128; +type F = BabyBear; + fn create_test_chip( tester: &VmChipTestBuilder, ) -> ( @@ -45,19 +55,25 @@ fn create_test_chip( ) { let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let step = Rv32BaseAluStep::new(BaseAluCoreChip::new( - bitwise_chip.clone(), - BaseAluOpcode::CLASS_OFFSET, - )); - let air = VmAirWrapper::new( - Rv32BaseAluAdapterAir::new( - tester.execution_bridge(), - tester.memory_bridge(), - bitwise_bus, + + let chip = Rv32BaseAluChip::new( + VmAirWrapper::new( + Rv32BaseAluAdapterAir::new( + tester.execution_bridge(), + tester.memory_bridge(), + bitwise_bus, + ), + BaseAluCoreAir::new(bitwise_bus, BaseAluOpcode::CLASS_OFFSET), + ), + Rv32BaseAluStep::new( + Rv32BaseAluAdapterStep::new(), + bitwise_chip.clone(), + BaseAluOpcode::CLASS_OFFSET, ), - step.core.air, + MAX_INS_CAPACITY, + tester.memory_helper(), ); - let chip = NewVmChipWrapper::new(air, step, MAX_INS_CAPACITY, tester.memory_helper()); + (chip, bitwise_chip) } ////////////////////////////////////////////////////////////////////////////////////// @@ -139,144 +155,148 @@ fn rv32_alu_and_rand_test() { // A dummy adapter is used so memory interactions don't indirectly cause false passes. ////////////////////////////////////////////////////////////////////////////////////// -type Rv32BaseAluTestChip = - VmChipWrapper, BaseAluCoreChip>; -// TODO: FIX NEGATIVE TESTS - -#[allow(clippy::too_many_arguments)] -fn run_rv32_alu_negative_test( - opcode: BaseAluOpcode, - a: [u32; RV32_REGISTER_NUM_LIMBS], - b: [u32; RV32_REGISTER_NUM_LIMBS], - c: [u32; RV32_REGISTER_NUM_LIMBS], - interaction_error: bool, -) { - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let (mut chip, bitwise_chip) = create_test_chip(&tester); - // let mut chip = Rv32BaseAluTestChip::::new( - // TestAdapterChip::new( - // vec![[b.map(F::from_canonical_u32), c.map(F::from_canonical_u32)].concat()], - // vec![None], - // ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), - // ), - // BaseAluCoreChip::new(bitwise_chip.clone(), BaseAluOpcode::CLASS_OFFSET), - // tester.offline_memory_mutex_arc(), - // ); - - tester.execute( - &mut chip, - &Instruction::from_usize(opcode.global_opcode(), [0, 0, 0, 1, 1]), - ); +// type Rv32BaseAluTestChip = NewVmChipWrapper< +// F, +// VmAirWrapper>, +// BaseAluStep, +// >; + +// // TODO: FIX NEGATIVE TESTS + +// #[allow(clippy::too_many_arguments)] +// fn run_rv32_alu_negative_test( +// opcode: BaseAluOpcode, +// a: [u32; RV32_REGISTER_NUM_LIMBS], +// b: [u32; RV32_REGISTER_NUM_LIMBS], +// c: [u32; RV32_REGISTER_NUM_LIMBS], +// interaction_error: bool, +// ) { +// let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); +// let (mut chip, bitwise_chip) = create_test_chip(&tester); +// let mut chip = Rv32BaseAluTestChip::::new( +// TestAdapterChip::new( +// vec![[b.map(F::from_canonical_u32), c.map(F::from_canonical_u32)].concat()], +// vec![None], +// ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), +// ), +// BaseAluStep::new(bitwise_chip.clone(), BaseAluOpcode::CLASS_OFFSET), +// tester.offline_memory_mutex_arc(), +// ); - let trace_width = chip.trace_width(); - let adapter_width = Rv32BaseAluAdapterCols::::width(); - - if (opcode == BaseAluOpcode::ADD || opcode == BaseAluOpcode::SUB) - && a.iter().all(|&a_val| a_val < (1 << RV32_CELL_BITS)) - { - bitwise_chip.clear(); - for a_val in a { - bitwise_chip.request_xor(a_val, a_val); - } - } +// tester.execute( +// &mut chip, +// &Instruction::from_usize(opcode.global_opcode(), [0, 0, 0, 1, 1]), +// ); - let modify_trace = |trace: &mut DenseMatrix| { - let mut values = trace.row_slice(0).to_vec(); - let cols: &mut BaseAluCoreCols = - values.split_at_mut(adapter_width).1.borrow_mut(); - cols.a = a.map(F::from_canonical_u32); - *trace = RowMajorMatrix::new(values, trace_width); - }; +// let trace_width = chip.trace_width(); +// let adapter_width = Rv32BaseAluAdapterCols::::width(); + +// if (opcode == BaseAluOpcode::ADD || opcode == BaseAluOpcode::SUB) +// && a.iter().all(|&a_val| a_val < (1 << RV32_CELL_BITS)) +// { +// bitwise_chip.clear(); +// for a_val in a { +// bitwise_chip.request_xor(a_val, a_val); +// } +// } - disable_debug_builder(); - let tester = tester - .build() - .load_and_prank_trace(chip, modify_trace) - .load(bitwise_chip) - .finalize(); - tester.simple_test_with_expected_error(if interaction_error { - VerificationError::ChallengePhaseError - } else { - VerificationError::OodEvaluationMismatch - }); -} +// let modify_trace = |trace: &mut DenseMatrix| { +// let mut values = trace.row_slice(0).to_vec(); +// let cols: &mut BaseAluCoreCols = +// values.split_at_mut(adapter_width).1.borrow_mut(); +// cols.a = a.map(F::from_canonical_u32); +// *trace = RowMajorMatrix::new(values, trace_width); +// }; -#[test] -fn rv32_alu_add_wrong_negative_test() { - run_rv32_alu_negative_test( - BaseAluOpcode::ADD, - [246, 0, 0, 0], - [250, 0, 0, 0], - [250, 0, 0, 0], - false, - ); -} +// disable_debug_builder(); +// let tester = tester +// .build() +// .load_and_prank_trace(chip, modify_trace) +// .load(bitwise_chip) +// .finalize(); +// tester.simple_test_with_expected_error(if interaction_error { +// VerificationError::ChallengePhaseError +// } else { +// VerificationError::OodEvaluationMismatch +// }); +// } -#[test] -fn rv32_alu_add_out_of_range_negative_test() { - run_rv32_alu_negative_test( - BaseAluOpcode::ADD, - [500, 0, 0, 0], - [250, 0, 0, 0], - [250, 0, 0, 0], - true, - ); -} +// #[test] +// fn rv32_alu_add_wrong_negative_test() { +// run_rv32_alu_negative_test( +// BaseAluOpcode::ADD, +// [246, 0, 0, 0], +// [250, 0, 0, 0], +// [250, 0, 0, 0], +// false, +// ); +// } -#[test] -fn rv32_alu_sub_wrong_negative_test() { - run_rv32_alu_negative_test( - BaseAluOpcode::SUB, - [255, 0, 0, 0], - [1, 0, 0, 0], - [2, 0, 0, 0], - false, - ); -} +// #[test] +// fn rv32_alu_add_out_of_range_negative_test() { +// run_rv32_alu_negative_test( +// BaseAluOpcode::ADD, +// [500, 0, 0, 0], +// [250, 0, 0, 0], +// [250, 0, 0, 0], +// true, +// ); +// } -#[test] -fn rv32_alu_sub_out_of_range_negative_test() { - run_rv32_alu_negative_test( - BaseAluOpcode::SUB, - [F::NEG_ONE.as_canonical_u32(), 0, 0, 0], - [1, 0, 0, 0], - [2, 0, 0, 0], - true, - ); -} +// #[test] +// fn rv32_alu_sub_wrong_negative_test() { +// run_rv32_alu_negative_test( +// BaseAluOpcode::SUB, +// [255, 0, 0, 0], +// [1, 0, 0, 0], +// [2, 0, 0, 0], +// false, +// ); +// } -#[test] -fn rv32_alu_xor_wrong_negative_test() { - run_rv32_alu_negative_test( - BaseAluOpcode::XOR, - [255, 255, 255, 255], - [0, 0, 1, 0], - [255, 255, 255, 255], - true, - ); -} +// #[test] +// fn rv32_alu_sub_out_of_range_negative_test() { +// run_rv32_alu_negative_test( +// BaseAluOpcode::SUB, +// [F::NEG_ONE.as_canonical_u32(), 0, 0, 0], +// [1, 0, 0, 0], +// [2, 0, 0, 0], +// true, +// ); +// } -#[test] -fn rv32_alu_or_wrong_negative_test() { - run_rv32_alu_negative_test( - BaseAluOpcode::OR, - [255, 255, 255, 255], - [255, 255, 255, 254], - [0, 0, 0, 0], - true, - ); -} +// #[test] +// fn rv32_alu_xor_wrong_negative_test() { +// run_rv32_alu_negative_test( +// BaseAluOpcode::XOR, +// [255, 255, 255, 255], +// [0, 0, 1, 0], +// [255, 255, 255, 255], +// true, +// ); +// } -#[test] -fn rv32_alu_and_wrong_negative_test() { - run_rv32_alu_negative_test( - BaseAluOpcode::AND, - [255, 255, 255, 255], - [0, 0, 1, 0], - [0, 0, 0, 0], - true, - ); -} +// #[test] +// fn rv32_alu_or_wrong_negative_test() { +// run_rv32_alu_negative_test( +// BaseAluOpcode::OR, +// [255, 255, 255, 255], +// [255, 255, 255, 254], +// [0, 0, 0, 0], +// true, +// ); +// } + +// #[test] +// fn rv32_alu_and_wrong_negative_test() { +// run_rv32_alu_negative_test( +// BaseAluOpcode::AND, +// [255, 255, 255, 255], +// [0, 0, 1, 0], +// [0, 0, 0, 0], +// true, +// ); +// } /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS @@ -345,96 +365,119 @@ fn run_and_sanity_test() { // Ensure that the adapter is correct. ////////////////////////////////////////////////////////////////////////////////////// -// TODO: put this back // A pranking chip where `preprocess` can have `rs2` limbs that overflow. -// struct Rv32BaseAluAdapterTestChip(Rv32BaseAluAdapterChip); - -// impl VmAdapterChip for Rv32BaseAluAdapterTestChip { -// type ReadRecord = Rv32BaseAluReadRecord; -// type WriteRecord = Rv32BaseAluWriteRecord; -// type Air = Rv32BaseAluAdapterAir; -// type Interface = BasicAdapterInterface< -// F, -// MinimalInstruction, -// 2, -// 1, -// RV32_REGISTER_NUM_LIMBS, -// RV32_REGISTER_NUM_LIMBS, -// >; - -// fn preprocess( -// &mut self, -// memory: &mut MemoryController, -// instruction: &Instruction, -// ) -> Result<( -// >::Reads, -// Self::ReadRecord, -// )> { -// let Instruction { b, c, d, e, .. } = *instruction; - -// let rs1 = memory.read::(d, b); -// let (rs2, rs2_data, rs2_imm) = if e.is_zero() { -// let c_u32 = c.as_canonical_u32(); -// memory.increment_timestamp(); -// let mask1 = (1 << 9) - 1; -// let mask2 = (1 << 3) - 2; -// ( -// None, -// [ -// (c_u32 & mask1) as u16, -// ((c_u32 >> 8) & mask2) as u16, -// (c_u32 >> 16) as u16, -// (c_u32 >> 16) as u16, -// ] -// .map(F::from_canonical_u16), -// c, -// ) -// } else { -// let rs2_read = memory.read::(e, c); -// ( -// Some(rs2_read.0), -// rs2_read.1.map(F::from_canonical_u8), -// F::ZERO, -// ) -// }; - -// Ok(( -// [rs1.1.map(F::from_canonical_u8), rs2_data], -// Self::ReadRecord { -// rs1: rs1.0, -// rs2, -// rs2_imm, -// }, -// )) -// } +#[derive(derive_new::new)] +struct Rv32BaseAluAdapterTestStep(Rv32BaseAluAdapterStep); + +impl AdapterTraceStep for Rv32BaseAluAdapterTestStep +where + F: PrimeField32, +{ + const WIDTH: usize = + as AdapterTraceStep>::WIDTH; + type ReadData = as AdapterTraceStep>::ReadData; + type WriteData = + as AdapterTraceStep>::WriteData; + type TraceContext<'a> = + as AdapterTraceStep>::TraceContext<'a>; + + #[inline(always)] + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + as AdapterTraceStep>::start( + pc, + memory, + adapter_row, + ); + } -// fn postprocess( -// &mut self, -// memory: &mut MemoryController, -// instruction: &Instruction, -// from_state: ExecutionState, -// output: AdapterRuntimeContext, -// _read_record: &Self::ReadRecord, -// ) -> Result<(ExecutionState, Self::WriteRecord)> { -// self.0 -// .postprocess(memory, instruction, from_state, output, _read_record) -// } + #[inline(always)] + fn read( + &self, + memory: &mut TracingMemory, + instruction: &Instruction, + adapter_row: &mut [F], + ) -> Self::ReadData { + let &Instruction { b, c, d, e, .. } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + debug_assert!( + e.as_canonical_u32() == RV32_REGISTER_AS || e.as_canonical_u32() == RV32_IMM_AS + ); -// fn generate_trace_row( -// &self, -// row_slice: &mut [F], -// read_record: Self::ReadRecord, -// write_record: Self::WriteRecord, -// memory: &OfflineMemory, -// ) { -// self.0 -// .generate_trace_row(row_slice, read_record, write_record, memory) -// } + let adapter_row: &mut Rv32BaseAluAdapterCols = adapter_row.borrow_mut(); -// fn air(&self) -> &Self::Air { -// self.0.air() -// } -// } + adapter_row.rs1_ptr = b; + let rs1 = tracing_read( + memory, + d.as_canonical_u32(), + b.as_canonical_u32(), + &mut adapter_row.reads_aux[0], + ); + + let rs2 = if e.as_canonical_u32() == RV32_REGISTER_AS { + adapter_row.rs2_as = e; + adapter_row.rs2 = c; + + tracing_read( + memory, + e.as_canonical_u32(), + c.as_canonical_u32(), + &mut adapter_row.reads_aux[1], + ) + } else { + adapter_row.rs2_as = e; + + // Here we use values that can overflow + let c_u32 = c.as_canonical_u32(); + let mask1 = (1 << 9) - 1; // Allowing overflow + let mask2 = (1 << 3) - 2; // Allowing overflow + + let rs2 = [ + (c_u32 & mask1) as u8, + ((c_u32 >> 8) & mask2) as u8, + (c_u32 >> 16) as u8, + (c_u32 >> 16) as u8, + ]; + + tracing_read_imm(memory, c.as_canonical_u32(), &mut adapter_row.rs2); + rs2 + }; + + (rs1, rs2) + } + + #[inline(always)] + fn write( + &self, + memory: &mut TracingMemory, + instruction: &Instruction, + adapter_row: &mut [F], + data: &Self::WriteData, + ) { + as AdapterTraceStep>::write( + &self.0, + memory, + instruction, + adapter_row, + data, + ); + } + + #[inline(always)] + fn fill_trace_row( + &self, + mem_helper: &MemoryAuxColsFactory, + bitwise_lookup_chip: Self::TraceContext<'_>, + adapter_row: &mut [F], + ) { + as AdapterTraceStep>::fill_trace_row( + &self.0, + mem_helper, + bitwise_lookup_chip, + adapter_row, + ); + } +} // #[test] // fn rv32_alu_adapter_unconstrained_imm_limb_test() { @@ -443,21 +486,33 @@ fn run_and_sanity_test() { // let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); // let mut tester = VmChipTestBuilder::default(); -// let mut chip = VmChipWrapper::new( -// Rv32BaseAluAdapterTestChip(Rv32BaseAluAdapterChip::new( -// tester.execution_bus(), -// tester.program_bus(), -// tester.memory_bridge(), + +// let mut chip = NewVmChipWrapper::::new( +// VmAirWrapper::new( +// Rv32BaseAluAdapterAir::new( +// tester.execution_bridge(), +// tester.memory_bridge(), +// bitwise_bus, +// ), +// BaseAluCoreAir::::new( +// bitwise_bus, +// BaseAluOpcode::CLASS_OFFSET, +// ), +// ), +// BaseAluStep::new( +// Rv32BaseAluAdapterTestStep(Rv32BaseAluAdapterStep::new()), // bitwise_chip.clone(), -// )), -// BaseAluCoreChip::new(bitwise_chip.clone(), BaseAluOpcode::CLASS_OFFSET), -// tester.offline_memory_mutex_arc(), +// BaseAluOpcode::CLASS_OFFSET, +// ), +// MAX_INS_CAPACITY, +// tester.memory_helper(), // ); // let b = [0, 0, 0, 0]; // let (c_imm, c) = { // let imm = (1 << 11) - 1; -// let fake_c = [(1 << 9) - 1, (1 << 3) - 2, 0, 0]; +// let fake_c: [u32; 4] = [(1 << 9) - 1, (1 << 3) - 2, 0, 0]; +// let fake_c = fake_c.map(|x| x as u8); // (Some(imm), fake_c) // }; @@ -476,54 +531,61 @@ fn run_and_sanity_test() { // tester.simple_test_with_expected_error(VerificationError::ChallengePhaseError); // } -// #[test] -// fn rv32_alu_adapter_unconstrained_rs2_read_test() { -// let mut rng = create_seeded_rng(); -// let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); -// let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); +#[test] +fn rv32_alu_adapter_unconstrained_rs2_read_test() { + let mut rng = create_seeded_rng(); + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); -// let mut tester = VmChipTestBuilder::default(); -// let mut chip = Rv32BaseAluChip::::new( -// Rv32BaseAluAdapterChip::new( -// tester.execution_bus(), -// tester.program_bus(), -// tester.memory_bridge(), -// bitwise_chip.clone(), -// ), -// BaseAluCoreChip::new(bitwise_chip.clone(), BaseAluOpcode::CLASS_OFFSET), -// tester.offline_memory_mutex_arc(), -// ); + let mut tester = VmChipTestBuilder::default(); + let mut chip = Rv32BaseAluChip::new( + VmAirWrapper::new( + Rv32BaseAluAdapterAir::new( + tester.execution_bridge(), + tester.memory_bridge(), + bitwise_bus, + ), + BaseAluCoreAir::new(bitwise_bus, BaseAluOpcode::CLASS_OFFSET), + ), + Rv32BaseAluStep::new( + Rv32BaseAluAdapterStep::new(), + bitwise_chip.clone(), + BaseAluOpcode::CLASS_OFFSET, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), + ); -// let b = [1, 1, 1, 1]; -// let c = [1, 1, 1, 1]; -// let (instruction, _rd) = rv32_rand_write_register_or_imm( -// &mut tester, -// b, -// c, -// None, -// BaseAluOpcode::ADD.global_opcode().as_usize(), -// &mut rng, -// ); -// tester.execute(&mut chip, &instruction); + let b = [1, 1, 1, 1]; + let c = [1, 1, 1, 1]; + let (instruction, _rd) = rv32_rand_write_register_or_imm( + &mut tester, + b, + c, + None, + BaseAluOpcode::ADD.global_opcode().as_usize(), + &mut rng, + ); + tester.execute(&mut chip, &instruction); -// let trace_width = chip.trace_width(); -// let adapter_width = BaseAir::::width(chip.adapter.air()); + let trace_width = chip.trace_width(); + let adapter_width = BaseAir::::width(&chip.air.adapter); -// let modify_trace = |trace: &mut DenseMatrix| { -// let mut values = trace.row_slice(0).to_vec(); -// let mut dummy_values = values.clone(); -// let cols: &mut BaseAluCoreCols = -// dummy_values.split_at_mut(adapter_width).1.borrow_mut(); -// cols.opcode_add_flag = F::ZERO; -// values.extend(dummy_values); -// *trace = RowMajorMatrix::new(values, trace_width); -// }; + let modify_trace = |trace: &mut DenseMatrix| { + let mut values = trace.row_slice(0).to_vec(); + let mut dummy_values = values.clone(); + let cols: &mut BaseAluCoreCols = + dummy_values.split_at_mut(adapter_width).1.borrow_mut(); + cols.opcode_add_flag = F::ZERO; + values.extend(dummy_values); + *trace = RowMajorMatrix::new(values, trace_width); + }; -// disable_debug_builder(); -// let tester = tester -// .build() -// .load_and_prank_trace(chip, modify_trace) -// .load(bitwise_chip) -// .finalize(); -// tester.simple_test_with_expected_error(VerificationError::OodEvaluationMismatch); -// } + disable_debug_builder(); + let tester = tester + .build() + .load_and_prank_trace(chip, modify_trace) + .load(bitwise_chip) + .finalize(); + tester.simple_test_with_expected_error(VerificationError::OodEvaluationMismatch); +} diff --git a/extensions/rv32im/circuit/src/branch_eq/core.rs b/extensions/rv32im/circuit/src/branch_eq/core.rs index 62db374660..a6d1a71af7 100644 --- a/extensions/rv32im/circuit/src/branch_eq/core.rs +++ b/extensions/rv32im/circuit/src/branch_eq/core.rs @@ -5,14 +5,17 @@ use std::{ use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, ImmInstruction, InsExecutorE1, Result, - VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ImmInstruction, Result, + SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::online::GuestMemory, }; use openvm_circuit_primitives::utils::not; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, riscv::RV32_REGISTER_AS, LocalOpcode}; +use openvm_instructions::{instruction::Instruction, LocalOpcode}; use openvm_rv32im_transpiler::BranchEqualOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -40,7 +43,7 @@ pub struct BranchEqualCoreCols { pub diff_inv_marker: [T; NUM_LIMBS], } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, derive_new::new)] pub struct BranchEqualCoreAir { offset: usize, pc_step: u32, @@ -152,130 +155,111 @@ pub struct BranchEqualCoreRecord { } #[derive(Debug)] -pub struct BranchEqualCoreChip { - pub air: BranchEqualCoreAir, +pub struct BranchEqualStep { + adapter: A, + pub offset: usize, + pub pc_step: u32, } -impl BranchEqualCoreChip { - pub fn new(offset: usize, pc_step: u32) -> Self { +impl BranchEqualStep { + pub fn new(adapter: A, offset: usize, pc_step: u32) -> Self { Self { - air: BranchEqualCoreAir { offset, pc_step }, + adapter, + offset, + pc_step, } } } -impl, const NUM_LIMBS: usize> VmCoreChip - for BranchEqualCoreChip +impl SingleTraceStep for BranchEqualStep where - I::Reads: Into<[[F; NUM_LIMBS]; 2]>, - I::Writes: Default, + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), + WriteData = (), + TraceContext<'a> = (), + >, { - type Record = BranchEqualCoreRecord; - type Air = BranchEqualCoreAir; + fn get_opcode_name(&self, opcode: usize) -> String { + format!("{:?}", BranchEqualOpcode::from_usize(opcode - self.offset)) + } - #[allow(clippy::type_complexity)] - fn execute_instruction( - &self, + fn execute( + &mut self, + state: VmStateMut, instruction: &Instruction, - from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { - let Instruction { opcode, c: imm, .. } = *instruction; - let branch_eq_opcode = - BranchEqualOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - - let data: [[F; NUM_LIMBS]; 2] = reads.into(); - let x = data[0].map(|x| x.as_canonical_u32()); - let y = data[1].map(|y| y.as_canonical_u32()); - let (cmp_result, diff_idx, diff_inv_val) = run_eq::(branch_eq_opcode, &x, &y); - - let output = AdapterRuntimeContext { - to_pc: cmp_result.then_some((F::from_canonical_u32(from_pc) + imm).as_canonical_u32()), - writes: Default::default(), - }; - let record = BranchEqualCoreRecord { - opcode: branch_eq_opcode, - a: data[0], - b: data[1], - cmp_result: F::from_bool(cmp_result), - imm, - diff_idx, - diff_inv_val, - }; - - Ok((output, record)) - } + row_slice: &mut [F], + ) -> Result<()> { + let &Instruction { opcode, c: imm, .. } = instruction; - fn get_opcode_name(&self, opcode: usize) -> String { - format!( - "{:?}", - BranchEqualOpcode::from_usize(opcode - self.air.offset) - ) - } + let branch_eq_opcode = BranchEqualOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let row_slice: &mut BranchEqualCoreCols<_, NUM_LIMBS> = row_slice.borrow_mut(); - row_slice.a = record.a; - row_slice.b = record.b; - row_slice.cmp_result = record.cmp_result; - row_slice.imm = record.imm; - row_slice.opcode_beq_flag = F::from_bool(record.opcode == BranchEqualOpcode::BEQ); - row_slice.opcode_bne_flag = F::from_bool(record.opcode == BranchEqualOpcode::BNE); - row_slice.diff_inv_marker = array::from_fn(|i| { - if i == record.diff_idx { - record.diff_inv_val - } else { - F::ZERO - } - }); + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + A::start(*state.pc, state.memory, adapter_row); + + let (rs1, rs2) = self.adapter.read(state.memory, instruction, adapter_row); + + let (cmp_result, diff_idx, diff_inv_val) = run_eq(branch_eq_opcode, &rs1, &rs2); + + let core_row: &mut BranchEqualCoreCols<_, NUM_LIMBS> = core_row.borrow_mut(); + core_row.a = rs1.map(F::from_canonical_u8); + core_row.b = rs2.map(F::from_canonical_u8); + core_row.cmp_result = F::from_bool(cmp_result); + core_row.imm = imm; + core_row.opcode_beq_flag = F::from_bool(branch_eq_opcode == BranchEqualOpcode::BEQ); + core_row.opcode_bne_flag = F::from_bool(branch_eq_opcode == BranchEqualOpcode::BNE); + core_row.diff_inv_marker = + array::from_fn(|i| if i == diff_idx { diff_inv_val } else { F::ZERO }); + + if cmp_result { + *state.pc = (F::from_canonical_u32(*state.pc) + imm).as_canonical_u32(); + } else { + *state.pc = state.pc.wrapping_add(self.pc_step); + } + + Ok(()) } - fn air(&self) -> &Self::Air { - &self.air + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, _core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + self.adapter.fill_trace_row(mem_helper, (), adapter_row); } } -impl InsExecutorE1 - for BranchEqualCoreChip +impl StepExecutorE1 for BranchEqualStep where - Mem: GuestMemory, F: PrimeField32, + A: 'static + + for<'a> AdapterExecutorE1, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, - a, - b, - c: imm, - .. - } = instruction; - - let branch_eq_opcode = - BranchEqualOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - - let rs1_addr = a.as_canonical_u32(); - let rs2_addr = b.as_canonical_u32(); + ) -> Result<()> + where + Mem: GuestMemory, + { + let &Instruction { opcode, c: imm, .. } = instruction; - let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; - let rs2_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; + let branch_eq_opcode = BranchEqualOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - // TODO(ayush): avoid this conversion - let rs1_bytes: [u32; NUM_LIMBS] = rs1_bytes.map(|x| x as u32); - let rs2_bytes: [u32; NUM_LIMBS] = rs2_bytes.map(|y| y as u32); + let (rs1, rs2) = self.adapter.read(state.memory, instruction); // TODO(ayush): probably don't need the other values - let (cmp_result, _, _) = run_eq::(branch_eq_opcode, &rs1_bytes, &rs2_bytes); + let (cmp_result, _, _) = run_eq::(branch_eq_opcode, &rs1, &rs2); if cmp_result { - let imm = imm.as_canonical_u32(); - state.pc = state.pc.wrapping_add(imm); + // TODO(ayush): verify this is fine + // state.pc = state.pc.wrapping_add(imm.as_canonical_u32()); + *state.pc = (F::from_canonical_u32(*state.pc) + imm).as_canonical_u32(); } else { - // TODO(ayush): why not DEFAULT_PC_STEP or some constant? - state.pc = state.pc.wrapping_add(self.air.pc_step); + *state.pc = state.pc.wrapping_add(self.pc_step); } Ok(()) @@ -283,17 +267,21 @@ where } // Returns (cmp_result, diff_idx, x[diff_idx] - y[diff_idx]) -pub(super) fn run_eq( +#[inline(always)] +pub(super) fn run_eq( local_opcode: BranchEqualOpcode, - x: &[u32; NUM_LIMBS], - y: &[u32; NUM_LIMBS], -) -> (bool, usize, F) { + x: &[u8; NUM_LIMBS], + y: &[u8; NUM_LIMBS], +) -> (bool, usize, F) +where + F: PrimeField32, +{ for i in 0..NUM_LIMBS { if x[i] != y[i] { return ( local_opcode == BranchEqualOpcode::BNE, i, - (F::from_canonical_u32(x[i]) - F::from_canonical_u32(y[i])).inverse(), + (F::from_canonical_u8(x[i]) - F::from_canonical_u8(y[i])).inverse(), ); } } diff --git a/extensions/rv32im/circuit/src/branch_eq/mod.rs b/extensions/rv32im/circuit/src/branch_eq/mod.rs index 7d53946a73..0ed0d44c1d 100644 --- a/extensions/rv32im/circuit/src/branch_eq/mod.rs +++ b/extensions/rv32im/circuit/src/branch_eq/mod.rs @@ -1,7 +1,7 @@ -use openvm_circuit::arch::VmChipWrapper; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; use super::adapters::RV32_REGISTER_NUM_LIMBS; -use crate::adapters::Rv32BranchAdapterChip; +use crate::adapters::{Rv32BranchAdapterAir, Rv32BranchAdapterStep}; mod core; pub use core::*; @@ -9,5 +9,9 @@ pub use core::*; #[cfg(test)] mod tests; +pub type Rv32BranchEqualAir = + VmAirWrapper>; +pub type Rv32BranchEqualStepWithAdapter = + BranchEqualStep; pub type Rv32BranchEqualChip = - VmChipWrapper, BranchEqualCoreChip>; + NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/branch_eq/tests.rs b/extensions/rv32im/circuit/src/branch_eq/tests.rs index c16858b071..8ee4b015e1 100644 --- a/extensions/rv32im/circuit/src/branch_eq/tests.rs +++ b/extensions/rv32im/circuit/src/branch_eq/tests.rs @@ -3,9 +3,13 @@ use std::{array, borrow::BorrowMut}; use openvm_circuit::arch::{ testing::{memory::gen_pointer, TestAdapterChip, VmChipTestBuilder}, BasicAdapterInterface, ExecutionBridge, ImmInstruction, InstructionExecutor, VmAdapterChip, - VmChipWrapper, VmCoreChip, + VmAirWrapper, VmChipWrapper, VmCoreChip, +}; +use openvm_instructions::{ + instruction::Instruction, + program::{DEFAULT_PC_STEP, PC_BITS}, + LocalOpcode, }; -use openvm_instructions::{instruction::Instruction, program::PC_BITS, LocalOpcode}; use openvm_rv32im_transpiler::BranchEqualOpcode; use openvm_stark_backend::{ p3_air::BaseAir, @@ -22,12 +26,18 @@ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; use super::{ - core::{run_eq, BranchEqualCoreChip}, + core::{run_eq, BranchEqualStep}, BranchEqualCoreCols, Rv32BranchEqualChip, }; -use crate::adapters::{Rv32BranchAdapterChip, RV32_REGISTER_NUM_LIMBS, RV_B_TYPE_IMM_BITS}; +use crate::{ + adapters::{ + Rv32BranchAdapterAir, Rv32BranchAdapterStep, RV32_REGISTER_NUM_LIMBS, RV_B_TYPE_IMM_BITS, + }, + BranchEqualCoreAir, +}; type F = BabyBear; +const MAX_INS_CAPACITY: usize = 128; ////////////////////////////////////////////////////////////////////////////////////// // POSITIVE TESTS @@ -41,16 +51,17 @@ fn run_rv32_branch_eq_rand_execute>( tester: &mut VmChipTestBuilder, chip: &mut E, opcode: BranchEqualOpcode, - a: [u32; RV32_REGISTER_NUM_LIMBS], - b: [u32; RV32_REGISTER_NUM_LIMBS], + a: [u8; RV32_REGISTER_NUM_LIMBS], + b: [u8; RV32_REGISTER_NUM_LIMBS], imm: i32, rng: &mut StdRng, ) { let rs1 = gen_pointer(rng, 4); let rs2 = gen_pointer(rng, 4); - tester.write::(1, rs1, a.map(F::from_canonical_u32)); - tester.write::(1, rs2, b.map(F::from_canonical_u32)); + tester.write::(1, rs1, a.map(F::from_canonical_u8)); + tester.write::(1, rs2, b.map(F::from_canonical_u8)); + let initial_pc = rng.gen_range(imm.unsigned_abs()..(1 << (PC_BITS - 1))); tester.execute_with_pc( chip, &Instruction::from_isize( @@ -61,7 +72,7 @@ fn run_rv32_branch_eq_rand_execute>( 1, 1, ), - rng.gen_range(imm.unsigned_abs()..(1 << (PC_BITS - 1))), + initial_pc, ); let (cmp_result, _, _) = run_eq::(opcode, &a, &b); @@ -77,22 +88,27 @@ fn run_rv32_branch_eq_rand_test(opcode: BranchEqualOpcode, num_ops: usize) { const ABS_MAX_BRANCH: i32 = 1 << (RV_B_TYPE_IMM_BITS - 1); let mut tester = VmChipTestBuilder::default(); + let mut chip = Rv32BranchEqualChip::::new( - Rv32BranchAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), + VmAirWrapper::new( + Rv32BranchAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), + BranchEqualCoreAir::new(BranchEqualOpcode::CLASS_OFFSET, DEFAULT_PC_STEP), + ), + BranchEqualStep::new( + Rv32BranchAdapterStep::new(), + BranchEqualOpcode::CLASS_OFFSET, + DEFAULT_PC_STEP, ), - BranchEqualCoreChip::new(BranchEqualOpcode::CLASS_OFFSET, 4), - tester.offline_memory_mutex_arc(), + MAX_INS_CAPACITY, + tester.memory_helper(), ); for _ in 0..num_ops { - let a = array::from_fn(|_| rng.gen_range(0..F::ORDER_U32)); + let a = array::from_fn(|_| rng.gen_range(0..u8::MAX)); let b = if rng.gen_bool(0.5) { a } else { - array::from_fn(|_| rng.gen_range(0..F::ORDER_U32)) + array::from_fn(|_| rng.gen_range(0..u8::MAX)) }; let imm = rng.gen_range((-ABS_MAX_BRANCH)..ABS_MAX_BRANCH); run_rv32_branch_eq_rand_execute(&mut tester, &mut chip, opcode, a, b, imm, &mut rng); @@ -120,137 +136,137 @@ fn rv32_bne_rand_test() { // A dummy adapter is used so memory interactions don't indirectly cause false passes. ////////////////////////////////////////////////////////////////////////////////////// -type Rv32BranchEqualTestChip = - VmChipWrapper, BranchEqualCoreChip>; - -#[allow(clippy::too_many_arguments)] -fn run_rv32_beq_negative_test( - opcode: BranchEqualOpcode, - a: [u32; RV32_REGISTER_NUM_LIMBS], - b: [u32; RV32_REGISTER_NUM_LIMBS], - cmp_result: bool, - diff_inv_marker: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, -) { - let imm = 16u32; - let mut tester = VmChipTestBuilder::default(); - let mut chip = Rv32BranchEqualTestChip::::new( - TestAdapterChip::new( - vec![[a.map(F::from_canonical_u32), b.map(F::from_canonical_u32)].concat()], - vec![if cmp_result { Some(imm) } else { None }], - ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), - ), - BranchEqualCoreChip::new(BranchEqualOpcode::CLASS_OFFSET, 4), - tester.offline_memory_mutex_arc(), - ); - - tester.execute( - &mut chip, - &Instruction::from_usize(opcode.global_opcode(), [0, 0, imm as usize, 1, 1]), - ); - - let trace_width = chip.trace_width(); - let adapter_width = BaseAir::::width(chip.adapter.air()); - - let modify_trace = |trace: &mut DenseMatrix| { - let mut values = trace.row_slice(0).to_vec(); - let cols: &mut BranchEqualCoreCols = - values.split_at_mut(adapter_width).1.borrow_mut(); - cols.cmp_result = F::from_bool(cmp_result); - if let Some(diff_inv_marker) = diff_inv_marker { - cols.diff_inv_marker = diff_inv_marker.map(F::from_canonical_u32); - } - *trace = RowMajorMatrix::new(values, trace_width); - }; - - disable_debug_builder(); - let tester = tester - .build() - .load_and_prank_trace(chip, modify_trace) - .finalize(); - tester.simple_test_with_expected_error(VerificationError::OodEvaluationMismatch); -} - -#[test] -fn rv32_beq_wrong_cmp_negative_test() { - run_rv32_beq_negative_test( - BranchEqualOpcode::BEQ, - [0, 0, 7, 0], - [0, 0, 0, 7], - true, - None, - ); - - run_rv32_beq_negative_test( - BranchEqualOpcode::BEQ, - [0, 0, 7, 0], - [0, 0, 7, 0], - false, - None, - ); -} - -#[test] -fn rv32_beq_zero_inv_marker_negative_test() { - run_rv32_beq_negative_test( - BranchEqualOpcode::BEQ, - [0, 0, 7, 0], - [0, 0, 0, 7], - true, - Some([0, 0, 0, 0]), - ); -} - -#[test] -fn rv32_beq_invalid_inv_marker_negative_test() { - run_rv32_beq_negative_test( - BranchEqualOpcode::BEQ, - [0, 0, 7, 0], - [0, 0, 7, 0], - false, - Some([0, 0, 1, 0]), - ); -} - -#[test] -fn rv32_bne_wrong_cmp_negative_test() { - run_rv32_beq_negative_test( - BranchEqualOpcode::BNE, - [0, 0, 7, 0], - [0, 0, 0, 7], - false, - None, - ); - - run_rv32_beq_negative_test( - BranchEqualOpcode::BNE, - [0, 0, 7, 0], - [0, 0, 7, 0], - true, - None, - ); -} - -#[test] -fn rv32_bne_zero_inv_marker_negative_test() { - run_rv32_beq_negative_test( - BranchEqualOpcode::BNE, - [0, 0, 7, 0], - [0, 0, 0, 7], - false, - Some([0, 0, 0, 0]), - ); -} - -#[test] -fn rv32_bne_invalid_inv_marker_negative_test() { - run_rv32_beq_negative_test( - BranchEqualOpcode::BNE, - [0, 0, 7, 0], - [0, 0, 7, 0], - true, - Some([0, 0, 1, 0]), - ); -} +// type Rv32BranchEqualTestChip = +// VmChipWrapper, BranchEqualStep>; + +// #[allow(clippy::too_many_arguments)] +// fn run_rv32_beq_negative_test( +// opcode: BranchEqualOpcode, +// a: [u32; RV32_REGISTER_NUM_LIMBS], +// b: [u32; RV32_REGISTER_NUM_LIMBS], +// cmp_result: bool, +// diff_inv_marker: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, +// ) { +// let imm = 16u32; +// let mut tester = VmChipTestBuilder::default(); +// let mut chip = Rv32BranchEqualTestChip::::new( +// TestAdapterChip::new( +// vec![[a.map(F::from_canonical_u32), b.map(F::from_canonical_u32)].concat()], +// vec![if cmp_result { Some(imm) } else { None }], +// ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), +// ), +// BranchEqualStep::new(BranchEqualOpcode::CLASS_OFFSET, 4), +// tester.offline_memory_mutex_arc(), +// ); + +// tester.execute( +// &mut chip, +// &Instruction::from_usize(opcode.global_opcode(), [0, 0, imm as usize, 1, 1]), +// ); + +// let trace_width = chip.trace_width(); +// let adapter_width = BaseAir::::width(chip.adapter.air()); + +// let modify_trace = |trace: &mut DenseMatrix| { +// let mut values = trace.row_slice(0).to_vec(); +// let cols: &mut BranchEqualCoreCols = +// values.split_at_mut(adapter_width).1.borrow_mut(); +// cols.cmp_result = F::from_bool(cmp_result); +// if let Some(diff_inv_marker) = diff_inv_marker { +// cols.diff_inv_marker = diff_inv_marker.map(F::from_canonical_u32); +// } +// *trace = RowMajorMatrix::new(values, trace_width); +// }; + +// disable_debug_builder(); +// let tester = tester +// .build() +// .load_and_prank_trace(chip, modify_trace) +// .finalize(); +// tester.simple_test_with_expected_error(VerificationError::OodEvaluationMismatch); +// } + +// #[test] +// fn rv32_beq_wrong_cmp_negative_test() { +// run_rv32_beq_negative_test( +// BranchEqualOpcode::BEQ, +// [0, 0, 7, 0], +// [0, 0, 0, 7], +// true, +// None, +// ); + +// run_rv32_beq_negative_test( +// BranchEqualOpcode::BEQ, +// [0, 0, 7, 0], +// [0, 0, 7, 0], +// false, +// None, +// ); +// } + +// #[test] +// fn rv32_beq_zero_inv_marker_negative_test() { +// run_rv32_beq_negative_test( +// BranchEqualOpcode::BEQ, +// [0, 0, 7, 0], +// [0, 0, 0, 7], +// true, +// Some([0, 0, 0, 0]), +// ); +// } + +// #[test] +// fn rv32_beq_invalid_inv_marker_negative_test() { +// run_rv32_beq_negative_test( +// BranchEqualOpcode::BEQ, +// [0, 0, 7, 0], +// [0, 0, 7, 0], +// false, +// Some([0, 0, 1, 0]), +// ); +// } + +// #[test] +// fn rv32_bne_wrong_cmp_negative_test() { +// run_rv32_beq_negative_test( +// BranchEqualOpcode::BNE, +// [0, 0, 7, 0], +// [0, 0, 0, 7], +// false, +// None, +// ); + +// run_rv32_beq_negative_test( +// BranchEqualOpcode::BNE, +// [0, 0, 7, 0], +// [0, 0, 7, 0], +// true, +// None, +// ); +// } + +// #[test] +// fn rv32_bne_zero_inv_marker_negative_test() { +// run_rv32_beq_negative_test( +// BranchEqualOpcode::BNE, +// [0, 0, 7, 0], +// [0, 0, 0, 7], +// false, +// Some([0, 0, 0, 0]), +// ); +// } + +// #[test] +// fn rv32_bne_invalid_inv_marker_negative_test() { +// run_rv32_beq_negative_test( +// BranchEqualOpcode::BNE, +// [0, 0, 7, 0], +// [0, 0, 7, 0], +// true, +// Some([0, 0, 1, 0]), +// ); +// } /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS @@ -258,39 +274,38 @@ fn rv32_bne_invalid_inv_marker_negative_test() { /// Ensure that solve functions produce the correct results. /////////////////////////////////////////////////////////////////////////////////////// -#[test] -fn execute_pc_increment_sanity_test() { - let core = - BranchEqualCoreChip::::new(BranchEqualOpcode::CLASS_OFFSET, 4); - - let mut instruction = Instruction:: { - opcode: BranchEqualOpcode::BEQ.global_opcode(), - c: F::from_canonical_u8(8), - ..Default::default() - }; - let x: [F; RV32_REGISTER_NUM_LIMBS] = [19, 4, 1790, 60].map(F::from_canonical_u32); - let y: [F; RV32_REGISTER_NUM_LIMBS] = [19, 32, 1804, 60].map(F::from_canonical_u32); - - let result = as VmCoreChip< - F, - BasicAdapterInterface, 2, 0, RV32_REGISTER_NUM_LIMBS, 0>, - >>::execute_instruction(&core, &instruction, 0, [x, y]); - let (output, _) = result.expect("execute_instruction failed"); - assert!(output.to_pc.is_none()); - - instruction.opcode = BranchEqualOpcode::BNE.global_opcode(); - let result = as VmCoreChip< - F, - BasicAdapterInterface, 2, 0, RV32_REGISTER_NUM_LIMBS, 0>, - >>::execute_instruction(&core, &instruction, 0, [x, y]); - let (output, _) = result.expect("execute_instruction failed"); - assert!(output.to_pc.is_some()); - assert_eq!(output.to_pc.unwrap(), 8); -} +// #[test] +// fn execute_pc_increment_sanity_test() { +// let core = BranchEqualStep::::new(BranchEqualOpcode::CLASS_OFFSET, 4); + +// let mut instruction = Instruction:: { +// opcode: BranchEqualOpcode::BEQ.global_opcode(), +// c: F::from_canonical_u8(8), +// ..Default::default() +// }; +// let x: [F; RV32_REGISTER_NUM_LIMBS] = [19, 4, 1790, 60].map(F::from_canonical_u32); +// let y: [F; RV32_REGISTER_NUM_LIMBS] = [19, 32, 1804, 60].map(F::from_canonical_u32); + +// let result = as VmCoreChip< +// F, +// BasicAdapterInterface, 2, 0, RV32_REGISTER_NUM_LIMBS, 0>, +// >>::execute_instruction(&core, &instruction, 0, [x, y]); +// let (output, _) = result.expect("execute_instruction failed"); +// assert!(output.to_pc.is_none()); + +// instruction.opcode = BranchEqualOpcode::BNE.global_opcode(); +// let result = as VmCoreChip< +// F, +// BasicAdapterInterface, 2, 0, RV32_REGISTER_NUM_LIMBS, 0>, +// >>::execute_instruction(&core, &instruction, 0, [x, y]); +// let (output, _) = result.expect("execute_instruction failed"); +// assert!(output.to_pc.is_some()); +// assert_eq!(output.to_pc.unwrap(), 8); +// } #[test] fn run_eq_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [19, 4, 1790, 60]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [19, 4, 17, 60]; let (cmp_result, _, diff_val) = run_eq::(BranchEqualOpcode::BEQ, &x, &x); assert!(cmp_result); @@ -304,13 +319,13 @@ fn run_eq_sanity_test() { #[test] fn run_ne_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [19, 4, 1790, 60]; - let y: [u32; RV32_REGISTER_NUM_LIMBS] = [19, 32, 1804, 60]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [19, 4, 17, 60]; + let y: [u8; RV32_REGISTER_NUM_LIMBS] = [19, 32, 18, 60]; let (cmp_result, diff_idx, diff_val) = run_eq::(BranchEqualOpcode::BEQ, &x, &y); assert!(!cmp_result); assert_eq!( - diff_val * (F::from_canonical_u32(x[diff_idx]) - F::from_canonical_u32(y[diff_idx])), + diff_val * (F::from_canonical_u8(x[diff_idx]) - F::from_canonical_u8(y[diff_idx])), F::ONE ); @@ -318,7 +333,7 @@ fn run_ne_sanity_test() { run_eq::(BranchEqualOpcode::BNE, &x, &y); assert!(cmp_result); assert_eq!( - diff_val * (F::from_canonical_u32(x[diff_idx]) - F::from_canonical_u32(y[diff_idx])), + diff_val * (F::from_canonical_u8(x[diff_idx]) - F::from_canonical_u8(y[diff_idx])), F::ONE ); } diff --git a/extensions/rv32im/circuit/src/branch_lt/core.rs b/extensions/rv32im/circuit/src/branch_lt/core.rs index 45e790b8d6..01d1001336 100644 --- a/extensions/rv32im/circuit/src/branch_lt/core.rs +++ b/extensions/rv32im/circuit/src/branch_lt/core.rs @@ -5,19 +5,20 @@ use std::{ use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, ImmInstruction, InsExecutorE1, Result, - VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ImmInstruction, Result, + SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, utils::not, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{ - instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_REGISTER_AS, LocalOpcode, -}; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; use openvm_rv32im_transpiler::BranchLessThanOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -58,7 +59,7 @@ pub struct BranchLessThanCoreCols { pub bus: BitwiseOperationLookupBus, offset: usize, @@ -209,50 +210,66 @@ pub struct BranchLessThanCoreRecord { - pub air: BranchLessThanCoreAir, +pub struct BranchLessThanStep { + adapter: A, + pub offset: usize, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, } -impl BranchLessThanCoreChip { +impl + BranchLessThanStep +{ pub fn new( + adapter: A, bitwise_lookup_chip: SharedBitwiseOperationLookupChip, offset: usize, ) -> Self { Self { - air: BranchLessThanCoreAir { - bus: bitwise_lookup_chip.bus(), - offset, - }, + adapter, + offset, bitwise_lookup_chip, } } } -impl, const NUM_LIMBS: usize, const LIMB_BITS: usize> - VmCoreChip for BranchLessThanCoreChip +impl SingleTraceStep + for BranchLessThanStep where - I::Reads: Into<[[F; NUM_LIMBS]; 2]>, - I::Writes: Default, + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), + WriteData = (), + TraceContext<'a> = (), + >, { - type Record = BranchLessThanCoreRecord; - type Air = BranchLessThanCoreAir; + fn get_opcode_name(&self, opcode: usize) -> String { + format!( + "{:?}", + BranchLessThanOpcode::from_usize(opcode - self.offset) + ) + } - #[allow(clippy::type_complexity)] - fn execute_instruction( - &self, + fn execute( + &mut self, + state: VmStateMut, instruction: &Instruction, - from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { - let Instruction { opcode, c: imm, .. } = *instruction; - let blt_opcode = BranchLessThanOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - - let data: [[F; NUM_LIMBS]; 2] = reads.into(); - let a = data[0].map(|x| x.as_canonical_u32()); - let b = data[1].map(|y| y.as_canonical_u32()); + row_slice: &mut [F], + ) -> Result<()> { + let &Instruction { opcode, c: imm, .. } = instruction; + + let blt_opcode = BranchLessThanOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + A::start(*state.pc, state.memory, adapter_row); + + let (rs1, rs2) = self.adapter.read(state.memory, instruction, adapter_row); + let (cmp_result, diff_idx, a_sign, b_sign) = - run_cmp::(blt_opcode, &a, &b); + run_cmp::(blt_opcode, &rs1, &rs2); let signed = matches!( blt_opcode, @@ -264,6 +281,9 @@ where ); let cmp_lt = cmp_result ^ ge_opcode; + let a = rs1.map(u32::from); + let b = rs2.map(u32::from); + // We range check (a_msb_f + 128) and (b_msb_f + 128) if signed, // a_msb_f and b_msb_f if not let (a_msb_f, a_msb_range) = if a_sign { @@ -288,8 +308,6 @@ where b[NUM_LIMBS - 1] + ((signed as u32) << (LIMB_BITS - 1)), ) }; - self.bitwise_lookup_chip - .request_range(a_msb_range, b_msb_range); let diff_val = if diff_idx == NUM_LIMBS { 0 @@ -306,100 +324,73 @@ where a[diff_idx] - b[diff_idx] }; - if diff_idx != NUM_LIMBS { - self.bitwise_lookup_chip.request_range(diff_val - 1, 0); + let core_row: &mut BranchLessThanCoreCols<_, NUM_LIMBS, LIMB_BITS> = core_row.borrow_mut(); + core_row.a = rs1.map(F::from_canonical_u8); + core_row.b = rs2.map(F::from_canonical_u8); + core_row.cmp_result = F::from_bool(cmp_result); + core_row.cmp_lt = F::from_bool(cmp_lt); + core_row.imm = imm; + core_row.a_msb_f = a_msb_f; + core_row.b_msb_f = b_msb_f; + core_row.diff_marker = array::from_fn(|i| F::from_bool(i == diff_idx)); + core_row.diff_val = F::from_canonical_u32(diff_val); + core_row.opcode_blt_flag = F::from_bool(blt_opcode == BranchLessThanOpcode::BLT); + core_row.opcode_bltu_flag = F::from_bool(blt_opcode == BranchLessThanOpcode::BLTU); + core_row.opcode_bge_flag = F::from_bool(blt_opcode == BranchLessThanOpcode::BGE); + core_row.opcode_bgeu_flag = F::from_bool(blt_opcode == BranchLessThanOpcode::BGEU); + + if cmp_result { + *state.pc = (F::from_canonical_u32(*state.pc) + imm).as_canonical_u32(); + } else { + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); } - let output = AdapterRuntimeContext { - to_pc: cmp_result.then_some((F::from_canonical_u32(from_pc) + imm).as_canonical_u32()), - writes: Default::default(), - }; - let record = BranchLessThanCoreRecord { - opcode: blt_opcode, - a: data[0], - b: data[1], - cmp_result: F::from_bool(cmp_result), - cmp_lt: F::from_bool(cmp_lt), - imm, - a_msb_f, - b_msb_f, - diff_val: F::from_canonical_u32(diff_val), - diff_idx, - }; + // TODO(ayush): move to fill_trace_row + self.bitwise_lookup_chip + .request_range(a_msb_range, b_msb_range); - Ok((output, record)) - } + if diff_idx != NUM_LIMBS { + self.bitwise_lookup_chip.request_range(diff_val - 1, 0); + } - fn get_opcode_name(&self, opcode: usize) -> String { - format!( - "{:?}", - BranchLessThanOpcode::from_usize(opcode - self.air.offset) - ) + Ok(()) } - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let row_slice: &mut BranchLessThanCoreCols<_, NUM_LIMBS, LIMB_BITS> = - row_slice.borrow_mut(); - row_slice.a = record.a; - row_slice.b = record.b; - row_slice.cmp_result = record.cmp_result; - row_slice.cmp_lt = record.cmp_lt; - row_slice.imm = record.imm; - row_slice.a_msb_f = record.a_msb_f; - row_slice.b_msb_f = record.b_msb_f; - row_slice.diff_marker = array::from_fn(|i| F::from_bool(i == record.diff_idx)); - row_slice.diff_val = record.diff_val; - row_slice.opcode_blt_flag = F::from_bool(record.opcode == BranchLessThanOpcode::BLT); - row_slice.opcode_bltu_flag = F::from_bool(record.opcode == BranchLessThanOpcode::BLTU); - row_slice.opcode_bge_flag = F::from_bool(record.opcode == BranchLessThanOpcode::BGE); - row_slice.opcode_bgeu_flag = F::from_bool(record.opcode == BranchLessThanOpcode::BGEU); - } + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, _core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - fn air(&self) -> &Self::Air { - &self.air + self.adapter.fill_trace_row(mem_helper, (), adapter_row); } } -impl InsExecutorE1 - for BranchLessThanCoreChip +impl StepExecutorE1 + for BranchLessThanStep where - Mem: GuestMemory, F: PrimeField32, + A: 'static + + for<'a> AdapterExecutorE1, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, - a, - b, - c: imm, - .. - } = instruction; - - let blt_opcode = BranchLessThanOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - - let rs1_addr = a.as_canonical_u32(); - let rs2_addr = b.as_canonical_u32(); + ) -> Result<()> + where + Mem: GuestMemory, + { + let &Instruction { opcode, c: imm, .. } = instruction; - // TODO(ayush): why even have NUM_LIMBS when it is equal to RV32_REGISTER_NUM_LIMBS? - let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; - let rs2_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; + let blt_opcode = BranchLessThanOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - // TODO(ayush): why is this conversion necessary? - let rs1_bytes: [u32; NUM_LIMBS] = rs1_bytes.map(|x| x as u32); - let rs2_bytes: [u32; NUM_LIMBS] = rs2_bytes.map(|y| y as u32); + let (rs1, rs2) = self.adapter.read(state.memory, instruction); - let (cmp_result, _, _, _) = - run_cmp::(blt_opcode, &rs1_bytes, &rs2_bytes); + // TODO(ayush): probably don't need the other values + let (cmp_result, _, _, _) = run_cmp::(blt_opcode, &rs1, &rs2); if cmp_result { - let imm = imm.as_canonical_u32(); - state.pc = state.pc.wrapping_add(imm); + *state.pc = (F::from_canonical_u32(*state.pc) + imm).as_canonical_u32(); } else { - state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); } Ok(()) @@ -407,10 +398,11 @@ where } // Returns (cmp_result, diff_idx, x_sign, y_sign) +#[inline(always)] pub(super) fn run_cmp( local_opcode: BranchLessThanOpcode, - x: &[u32; NUM_LIMBS], - y: &[u32; NUM_LIMBS], + x: &[u8; NUM_LIMBS], + y: &[u8; NUM_LIMBS], ) -> (bool, usize, bool, bool) { let signed = local_opcode == BranchLessThanOpcode::BLT || local_opcode == BranchLessThanOpcode::BGE; diff --git a/extensions/rv32im/circuit/src/branch_lt/mod.rs b/extensions/rv32im/circuit/src/branch_lt/mod.rs index b0bf8fc417..094e72afc9 100644 --- a/extensions/rv32im/circuit/src/branch_lt/mod.rs +++ b/extensions/rv32im/circuit/src/branch_lt/mod.rs @@ -1,7 +1,7 @@ -use openvm_circuit::arch::VmChipWrapper; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; use super::adapters::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; -use crate::adapters::Rv32BranchAdapterChip; +use crate::adapters::{Rv32BranchAdapterAir, Rv32BranchAdapterStep}; mod core; pub use core::*; @@ -9,8 +9,11 @@ pub use core::*; #[cfg(test)] mod tests; -pub type Rv32BranchLessThanChip = VmChipWrapper< - F, - Rv32BranchAdapterChip, - BranchLessThanCoreChip, +pub type Rv32BranchLessThanAir = VmAirWrapper< + Rv32BranchAdapterAir, + BranchLessThanCoreAir, >; +pub type Rv32BranchLessThanStepWithAdapter = + BranchLessThanStep; +pub type Rv32BranchLessThanChip = + NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/branch_lt/tests.rs b/extensions/rv32im/circuit/src/branch_lt/tests.rs index 8c1d7f697a..f6ab9a0b93 100644 --- a/extensions/rv32im/circuit/src/branch_lt/tests.rs +++ b/extensions/rv32im/circuit/src/branch_lt/tests.rs @@ -4,7 +4,7 @@ use openvm_circuit::{ arch::{ testing::{memory::gen_pointer, TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, BasicAdapterInterface, ExecutionBridge, ImmInstruction, InstructionExecutor, VmAdapterChip, - VmChipWrapper, VmCoreChip, + VmAirWrapper, VmChipWrapper, VmCoreChip, }, utils::{generate_long_number, i32_to_f}, }; @@ -28,17 +28,20 @@ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; use super::{ - core::{run_cmp, BranchLessThanCoreChip}, + core::{run_cmp, BranchLessThanStep}, Rv32BranchLessThanChip, }; use crate::{ adapters::{ - Rv32BranchAdapterChip, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, RV_B_TYPE_IMM_BITS, + Rv32BranchAdapterAir, Rv32BranchAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + RV_B_TYPE_IMM_BITS, }, branch_lt::BranchLessThanCoreCols, + BranchLessThanCoreAir, }; type F = BabyBear; +const MAX_INS_CAPACITY: usize = 128; ////////////////////////////////////////////////////////////////////////////////////// // POSITIVE TESTS @@ -52,15 +55,15 @@ fn run_rv32_branch_lt_rand_execute>( tester: &mut VmChipTestBuilder, chip: &mut E, opcode: BranchLessThanOpcode, - a: [u32; RV32_REGISTER_NUM_LIMBS], - b: [u32; RV32_REGISTER_NUM_LIMBS], + a: [u8; RV32_REGISTER_NUM_LIMBS], + b: [u8; RV32_REGISTER_NUM_LIMBS], imm: i32, rng: &mut StdRng, ) { let rs1 = gen_pointer(rng, 4); let rs2 = gen_pointer(rng, 4); - tester.write::(1, rs1, a.map(F::from_canonical_u32)); - tester.write::(1, rs2, b.map(F::from_canonical_u32)); + tester.write::(1, rs1, a.map(F::from_canonical_u8)); + tester.write::(1, rs2, b.map(F::from_canonical_u8)); tester.execute_with_pc( chip, @@ -91,23 +94,31 @@ fn run_rv32_branch_lt_rand_test(opcode: BranchLessThanOpcode, num_ops: usize) { let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut tester = VmChipTestBuilder::default(); + let mut chip = Rv32BranchLessThanChip::::new( - Rv32BranchAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), + VmAirWrapper::new( + Rv32BranchAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), + BranchLessThanCoreAir::new(bitwise_bus, BranchLessThanOpcode::CLASS_OFFSET), + ), + BranchLessThanStep::new( + Rv32BranchAdapterStep::new(), + bitwise_chip.clone(), + BranchLessThanOpcode::CLASS_OFFSET, ), - BranchLessThanCoreChip::new(bitwise_chip.clone(), BranchLessThanOpcode::CLASS_OFFSET), - tester.offline_memory_mutex_arc(), + MAX_INS_CAPACITY, + tester.memory_helper(), ); for _ in 0..num_ops { + // TODO(ayush): directly generate u8 let a = generate_long_number::(&mut rng); let b = if rng.gen_bool(0.5) { a } else { generate_long_number::(&mut rng) }; + let a = a.map(|x| x as u8); + let b = b.map(|x| x as u8); let imm = rng.gen_range((-ABS_MAX_BRANCH)..ABS_MAX_BRANCH); run_rv32_branch_lt_rand_execute(&mut tester, &mut chip, opcode, a, b, imm, &mut rng); } @@ -164,321 +175,321 @@ fn rv32_bgeu_rand_test() { // A dummy adapter is used so memory interactions don't indirectly cause false passes. ////////////////////////////////////////////////////////////////////////////////////// -type Rv32BranchLessThanTestChip = VmChipWrapper< - F, - TestAdapterChip, - BranchLessThanCoreChip, ->; - -#[derive(Clone, Copy, Default, PartialEq)] -struct BranchLessThanPrankValues { - pub a_msb: Option, - pub b_msb: Option, - pub diff_marker: Option<[u32; NUM_LIMBS]>, - pub diff_val: Option, -} - -#[allow(clippy::too_many_arguments)] -fn run_rv32_blt_negative_test( - opcode: BranchLessThanOpcode, - a: [u32; RV32_REGISTER_NUM_LIMBS], - b: [u32; RV32_REGISTER_NUM_LIMBS], - cmp_result: bool, - prank_vals: BranchLessThanPrankValues, - interaction_error: bool, -) { - let imm = 16u32; - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let mut chip = Rv32BranchLessThanTestChip::::new( - TestAdapterChip::new( - vec![[a.map(F::from_canonical_u32), b.map(F::from_canonical_u32)].concat()], - vec![if cmp_result { Some(imm) } else { None }], - ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), - ), - BranchLessThanCoreChip::new(bitwise_chip.clone(), BranchLessThanOpcode::CLASS_OFFSET), - tester.offline_memory_mutex_arc(), - ); - - tester.execute( - &mut chip, - &Instruction::from_usize(opcode.global_opcode(), [0, 0, imm as usize, 1, 1]), - ); - - let trace_width = chip.trace_width(); - let adapter_width = BaseAir::::width(chip.adapter.air()); - let ge_opcode = opcode == BranchLessThanOpcode::BGE || opcode == BranchLessThanOpcode::BGEU; - let (_, _, a_sign, b_sign) = run_cmp::(opcode, &a, &b); - - if prank_vals != BranchLessThanPrankValues::default() { - debug_assert!(prank_vals.diff_val.is_some()); - let a_msb = prank_vals.a_msb.unwrap_or( - a[RV32_REGISTER_NUM_LIMBS - 1] as i32 - if a_sign { 1 << RV32_CELL_BITS } else { 0 }, - ); - let b_msb = prank_vals.b_msb.unwrap_or( - b[RV32_REGISTER_NUM_LIMBS - 1] as i32 - if b_sign { 1 << RV32_CELL_BITS } else { 0 }, - ); - let signed_offset = match opcode { - BranchLessThanOpcode::BLT | BranchLessThanOpcode::BGE => 1 << (RV32_CELL_BITS - 1), - _ => 0, - }; - - bitwise_chip.clear(); - bitwise_chip.request_range( - (a_msb + signed_offset) as u8 as u32, - (b_msb + signed_offset) as u8 as u32, - ); - - let diff_val = prank_vals - .diff_val - .unwrap() - .clamp(0, (1 << RV32_CELL_BITS) - 1); - if diff_val > 0 { - bitwise_chip.request_range(diff_val - 1, 0); - } - } - - let modify_trace = |trace: &mut DenseMatrix| { - let mut values = trace.row_slice(0).to_vec(); - let cols: &mut BranchLessThanCoreCols = - values.split_at_mut(adapter_width).1.borrow_mut(); - - if let Some(a_msb) = prank_vals.a_msb { - cols.a_msb_f = i32_to_f(a_msb); - } - if let Some(b_msb) = prank_vals.b_msb { - cols.b_msb_f = i32_to_f(b_msb); - } - if let Some(diff_marker) = prank_vals.diff_marker { - cols.diff_marker = diff_marker.map(F::from_canonical_u32); - } - if let Some(diff_val) = prank_vals.diff_val { - cols.diff_val = F::from_canonical_u32(diff_val); - } - cols.cmp_result = F::from_bool(cmp_result); - cols.cmp_lt = F::from_bool(ge_opcode ^ cmp_result); - - *trace = RowMajorMatrix::new(values, trace_width); - }; - - disable_debug_builder(); - let tester = tester - .build() - .load_and_prank_trace(chip, modify_trace) - .load(bitwise_chip) - .finalize(); - tester.simple_test_with_expected_error(if interaction_error { - VerificationError::ChallengePhaseError - } else { - VerificationError::OodEvaluationMismatch - }); -} - -#[test] -fn rv32_blt_wrong_lt_cmp_negative_test() { - let a = [145, 34, 25, 205]; - let b = [73, 35, 25, 205]; - let prank_vals = Default::default(); - run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, false); -} - -#[test] -fn rv32_blt_wrong_ge_cmp_negative_test() { - let a = [73, 35, 25, 205]; - let b = [145, 34, 25, 205]; - let prank_vals = Default::default(); - run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, true, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, true, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, false, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, false, prank_vals, false); -} - -#[test] -fn rv32_blt_wrong_eq_cmp_negative_test() { - let a = [73, 35, 25, 205]; - let b = [73, 35, 25, 205]; - let prank_vals = Default::default(); - run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, true, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, true, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, false, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, false, prank_vals, false); -} - -#[test] -fn rv32_blt_fake_diff_val_negative_test() { - let a = [145, 34, 25, 205]; - let b = [73, 35, 25, 205]; - let prank_vals = BranchLessThanPrankValues { - diff_val: Some(F::NEG_ONE.as_canonical_u32()), - ..Default::default() - }; - run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, true); - run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, true); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, true); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, true); -} - -#[test] -fn rv32_blt_zero_diff_val_negative_test() { - let a = [145, 34, 25, 205]; - let b = [73, 35, 25, 205]; - let prank_vals = BranchLessThanPrankValues { - diff_marker: Some([0, 0, 1, 0]), - diff_val: Some(0), - ..Default::default() - }; - run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, true); - run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, true); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, true); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, true); -} - -#[test] -fn rv32_blt_fake_diff_marker_negative_test() { - let a = [145, 34, 25, 205]; - let b = [73, 35, 25, 205]; - let prank_vals = BranchLessThanPrankValues { - diff_marker: Some([1, 0, 0, 0]), - diff_val: Some(72), - ..Default::default() - }; - run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, false); -} - -#[test] -fn rv32_blt_zero_diff_marker_negative_test() { - let a = [145, 34, 25, 205]; - let b = [73, 35, 25, 205]; - let prank_vals = BranchLessThanPrankValues { - diff_marker: Some([0, 0, 0, 0]), - diff_val: Some(0), - ..Default::default() - }; - run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, false); -} - -#[test] -fn rv32_blt_signed_wrong_a_msb_negative_test() { - let a = [145, 34, 25, 205]; - let b = [73, 35, 25, 205]; - let prank_vals = BranchLessThanPrankValues { - a_msb: Some(206), - diff_marker: Some([0, 0, 0, 1]), - diff_val: Some(1), - ..Default::default() - }; - run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, false); -} - -#[test] -fn rv32_blt_signed_wrong_a_msb_sign_negative_test() { - let a = [145, 34, 25, 205]; - let b = [73, 35, 25, 205]; - let prank_vals = BranchLessThanPrankValues { - a_msb: Some(205), - diff_marker: Some([0, 0, 0, 1]), - diff_val: Some(256), - ..Default::default() - }; - run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, true); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, true); -} - -#[test] -fn rv32_blt_signed_wrong_b_msb_negative_test() { - let a = [145, 36, 25, 205]; - let b = [73, 35, 25, 205]; - let prank_vals = BranchLessThanPrankValues { - b_msb: Some(206), - diff_marker: Some([0, 0, 0, 1]), - diff_val: Some(1), - ..Default::default() - }; - run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, true, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, false, prank_vals, false); -} - -#[test] -fn rv32_blt_signed_wrong_b_msb_sign_negative_test() { - let a = [145, 36, 25, 205]; - let b = [73, 35, 25, 205]; - let prank_vals = BranchLessThanPrankValues { - b_msb: Some(205), - diff_marker: Some([0, 0, 0, 1]), - diff_val: Some(256), - ..Default::default() - }; - run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, true, prank_vals, true); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, false, prank_vals, true); -} - -#[test] -fn rv32_blt_unsigned_wrong_a_msb_negative_test() { - let a = [145, 36, 25, 205]; - let b = [73, 35, 25, 205]; - let prank_vals = BranchLessThanPrankValues { - a_msb: Some(204), - diff_marker: Some([0, 0, 0, 1]), - diff_val: Some(1), - ..Default::default() - }; - run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, true, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, false, prank_vals, false); -} - -#[test] -fn rv32_blt_unsigned_wrong_a_msb_sign_negative_test() { - let a = [145, 36, 25, 205]; - let b = [73, 35, 25, 205]; - let prank_vals = BranchLessThanPrankValues { - a_msb: Some(-51), - diff_marker: Some([0, 0, 0, 1]), - diff_val: Some(256), - ..Default::default() - }; - run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, true, prank_vals, true); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, false, prank_vals, true); -} - -#[test] -fn rv32_blt_unsigned_wrong_b_msb_negative_test() { - let a = [145, 34, 25, 205]; - let b = [73, 35, 25, 205]; - let prank_vals = BranchLessThanPrankValues { - b_msb: Some(206), - diff_marker: Some([0, 0, 0, 1]), - diff_val: Some(1), - ..Default::default() - }; - run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, false); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, false); -} - -#[test] -fn rv32_blt_unsigned_wrong_b_msb_sign_negative_test() { - let a = [145, 34, 25, 205]; - let b = [73, 35, 25, 205]; - let prank_vals = BranchLessThanPrankValues { - b_msb: Some(-51), - diff_marker: Some([0, 0, 0, 1]), - diff_val: Some(256), - ..Default::default() - }; - run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, true); - run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, true); -} +// type Rv32BranchLessThanTestChip = VmChipWrapper< +// F, +// TestAdapterChip, +// BranchLessThanStep, +// >; + +// #[derive(Clone, Copy, Default, PartialEq)] +// struct BranchLessThanPrankValues { +// pub a_msb: Option, +// pub b_msb: Option, +// pub diff_marker: Option<[u32; NUM_LIMBS]>, +// pub diff_val: Option, +// } + +// #[allow(clippy::too_many_arguments)] +// fn run_rv32_blt_negative_test( +// opcode: BranchLessThanOpcode, +// a: [u32; RV32_REGISTER_NUM_LIMBS], +// b: [u32; RV32_REGISTER_NUM_LIMBS], +// cmp_result: bool, +// prank_vals: BranchLessThanPrankValues, +// interaction_error: bool, +// ) { +// let imm = 16u32; +// let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); +// let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + +// let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); +// let mut chip = Rv32BranchLessThanTestChip::::new( +// TestAdapterChip::new( +// vec![[a.map(F::from_canonical_u32), b.map(F::from_canonical_u32)].concat()], +// vec![if cmp_result { Some(imm) } else { None }], +// ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), +// ), +// BranchLessThanStep::new(bitwise_chip.clone(), BranchLessThanOpcode::CLASS_OFFSET), +// tester.offline_memory_mutex_arc(), +// ); + +// tester.execute( +// &mut chip, +// &Instruction::from_usize(opcode.global_opcode(), [0, 0, imm as usize, 1, 1]), +// ); + +// let trace_width = chip.trace_width(); +// let adapter_width = BaseAir::::width(chip.adapter.air()); +// let ge_opcode = opcode == BranchLessThanOpcode::BGE || opcode == BranchLessThanOpcode::BGEU; +// let (_, _, a_sign, b_sign) = run_cmp::(opcode, &a, &b); + +// if prank_vals != BranchLessThanPrankValues::default() { +// debug_assert!(prank_vals.diff_val.is_some()); +// let a_msb = prank_vals.a_msb.unwrap_or( +// a[RV32_REGISTER_NUM_LIMBS - 1] as i32 - if a_sign { 1 << RV32_CELL_BITS } else { 0 }, +// ); +// let b_msb = prank_vals.b_msb.unwrap_or( +// b[RV32_REGISTER_NUM_LIMBS - 1] as i32 - if b_sign { 1 << RV32_CELL_BITS } else { 0 }, +// ); +// let signed_offset = match opcode { +// BranchLessThanOpcode::BLT | BranchLessThanOpcode::BGE => 1 << (RV32_CELL_BITS - 1), +// _ => 0, +// }; + +// bitwise_chip.clear(); +// bitwise_chip.request_range( +// (a_msb + signed_offset) as u8 as u32, +// (b_msb + signed_offset) as u8 as u32, +// ); + +// let diff_val = prank_vals +// .diff_val +// .unwrap() +// .clamp(0, (1 << RV32_CELL_BITS) - 1); +// if diff_val > 0 { +// bitwise_chip.request_range(diff_val - 1, 0); +// } +// } + +// let modify_trace = |trace: &mut DenseMatrix| { +// let mut values = trace.row_slice(0).to_vec(); +// let cols: &mut BranchLessThanCoreCols = +// values.split_at_mut(adapter_width).1.borrow_mut(); + +// if let Some(a_msb) = prank_vals.a_msb { +// cols.a_msb_f = i32_to_f(a_msb); +// } +// if let Some(b_msb) = prank_vals.b_msb { +// cols.b_msb_f = i32_to_f(b_msb); +// } +// if let Some(diff_marker) = prank_vals.diff_marker { +// cols.diff_marker = diff_marker.map(F::from_canonical_u32); +// } +// if let Some(diff_val) = prank_vals.diff_val { +// cols.diff_val = F::from_canonical_u32(diff_val); +// } +// cols.cmp_result = F::from_bool(cmp_result); +// cols.cmp_lt = F::from_bool(ge_opcode ^ cmp_result); + +// *trace = RowMajorMatrix::new(values, trace_width); +// }; + +// disable_debug_builder(); +// let tester = tester +// .build() +// .load_and_prank_trace(chip, modify_trace) +// .load(bitwise_chip) +// .finalize(); +// tester.simple_test_with_expected_error(if interaction_error { +// VerificationError::ChallengePhaseError +// } else { +// VerificationError::OodEvaluationMismatch +// }); +// } + +// #[test] +// fn rv32_blt_wrong_lt_cmp_negative_test() { +// let a = [145, 34, 25, 205]; +// let b = [73, 35, 25, 205]; +// let prank_vals = Default::default(); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, false); +// } + +// #[test] +// fn rv32_blt_wrong_ge_cmp_negative_test() { +// let a = [73, 35, 25, 205]; +// let b = [145, 34, 25, 205]; +// let prank_vals = Default::default(); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, true, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, true, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, false, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, false, prank_vals, false); +// } + +// #[test] +// fn rv32_blt_wrong_eq_cmp_negative_test() { +// let a = [73, 35, 25, 205]; +// let b = [73, 35, 25, 205]; +// let prank_vals = Default::default(); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, true, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, true, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, false, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, false, prank_vals, false); +// } + +// #[test] +// fn rv32_blt_fake_diff_val_negative_test() { +// let a = [145, 34, 25, 205]; +// let b = [73, 35, 25, 205]; +// let prank_vals = BranchLessThanPrankValues { +// diff_val: Some(F::NEG_ONE.as_canonical_u32()), +// ..Default::default() +// }; +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, true); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, true); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, true); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, true); +// } + +// #[test] +// fn rv32_blt_zero_diff_val_negative_test() { +// let a = [145, 34, 25, 205]; +// let b = [73, 35, 25, 205]; +// let prank_vals = BranchLessThanPrankValues { +// diff_marker: Some([0, 0, 1, 0]), +// diff_val: Some(0), +// ..Default::default() +// }; +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, true); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, true); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, true); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, true); +// } + +// #[test] +// fn rv32_blt_fake_diff_marker_negative_test() { +// let a = [145, 34, 25, 205]; +// let b = [73, 35, 25, 205]; +// let prank_vals = BranchLessThanPrankValues { +// diff_marker: Some([1, 0, 0, 0]), +// diff_val: Some(72), +// ..Default::default() +// }; +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, false); +// } + +// #[test] +// fn rv32_blt_zero_diff_marker_negative_test() { +// let a = [145, 34, 25, 205]; +// let b = [73, 35, 25, 205]; +// let prank_vals = BranchLessThanPrankValues { +// diff_marker: Some([0, 0, 0, 0]), +// diff_val: Some(0), +// ..Default::default() +// }; +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, false); +// } + +// #[test] +// fn rv32_blt_signed_wrong_a_msb_negative_test() { +// let a = [145, 34, 25, 205]; +// let b = [73, 35, 25, 205]; +// let prank_vals = BranchLessThanPrankValues { +// a_msb: Some(206), +// diff_marker: Some([0, 0, 0, 1]), +// diff_val: Some(1), +// ..Default::default() +// }; +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, false); +// } + +// #[test] +// fn rv32_blt_signed_wrong_a_msb_sign_negative_test() { +// let a = [145, 34, 25, 205]; +// let b = [73, 35, 25, 205]; +// let prank_vals = BranchLessThanPrankValues { +// a_msb: Some(205), +// diff_marker: Some([0, 0, 0, 1]), +// diff_val: Some(256), +// ..Default::default() +// }; +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, true); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, true); +// } + +// #[test] +// fn rv32_blt_signed_wrong_b_msb_negative_test() { +// let a = [145, 36, 25, 205]; +// let b = [73, 35, 25, 205]; +// let prank_vals = BranchLessThanPrankValues { +// b_msb: Some(206), +// diff_marker: Some([0, 0, 0, 1]), +// diff_val: Some(1), +// ..Default::default() +// }; +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, true, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, false, prank_vals, false); +// } + +// #[test] +// fn rv32_blt_signed_wrong_b_msb_sign_negative_test() { +// let a = [145, 36, 25, 205]; +// let b = [73, 35, 25, 205]; +// let prank_vals = BranchLessThanPrankValues { +// b_msb: Some(205), +// diff_marker: Some([0, 0, 0, 1]), +// diff_val: Some(256), +// ..Default::default() +// }; +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, true, prank_vals, true); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, false, prank_vals, true); +// } + +// #[test] +// fn rv32_blt_unsigned_wrong_a_msb_negative_test() { +// let a = [145, 36, 25, 205]; +// let b = [73, 35, 25, 205]; +// let prank_vals = BranchLessThanPrankValues { +// a_msb: Some(204), +// diff_marker: Some([0, 0, 0, 1]), +// diff_val: Some(1), +// ..Default::default() +// }; +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, true, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, false, prank_vals, false); +// } + +// #[test] +// fn rv32_blt_unsigned_wrong_a_msb_sign_negative_test() { +// let a = [145, 36, 25, 205]; +// let b = [73, 35, 25, 205]; +// let prank_vals = BranchLessThanPrankValues { +// a_msb: Some(-51), +// diff_marker: Some([0, 0, 0, 1]), +// diff_val: Some(256), +// ..Default::default() +// }; +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, true, prank_vals, true); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, false, prank_vals, true); +// } + +// #[test] +// fn rv32_blt_unsigned_wrong_b_msb_negative_test() { +// let a = [145, 34, 25, 205]; +// let b = [73, 35, 25, 205]; +// let prank_vals = BranchLessThanPrankValues { +// b_msb: Some(206), +// diff_marker: Some([0, 0, 0, 1]), +// diff_val: Some(1), +// ..Default::default() +// }; +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, false); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, false); +// } + +// #[test] +// fn rv32_blt_unsigned_wrong_b_msb_sign_negative_test() { +// let a = [145, 34, 25, 205]; +// let b = [73, 35, 25, 205]; +// let prank_vals = BranchLessThanPrankValues { +// b_msb: Some(-51), +// diff_marker: Some([0, 0, 0, 1]), +// diff_val: Some(256), +// ..Default::default() +// }; +// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, true); +// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, true); +// } /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS @@ -486,43 +497,43 @@ fn rv32_blt_unsigned_wrong_b_msb_sign_negative_test() { /// Ensure that solve functions produce the correct results. /////////////////////////////////////////////////////////////////////////////////////// -#[test] -fn execute_pc_increment_sanity_test() { - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let core = BranchLessThanCoreChip::::new( - bitwise_chip, - BranchLessThanOpcode::CLASS_OFFSET, - ); - - let mut instruction = Instruction:: { - opcode: BranchLessThanOpcode::BLT.global_opcode(), - c: F::from_canonical_u8(8), - ..Default::default() - }; - let x: [F; RV32_REGISTER_NUM_LIMBS] = [145, 34, 25, 205].map(F::from_canonical_u32); - - let result = as VmCoreChip< - F, - BasicAdapterInterface, 2, 0, RV32_REGISTER_NUM_LIMBS, 0>, - >>::execute_instruction(&core, &instruction, 0, [x, x]); - let (output, _) = result.expect("execute_instruction failed"); - assert!(output.to_pc.is_none()); - - instruction.opcode = BranchLessThanOpcode::BGE.global_opcode(); - let result = as VmCoreChip< - F, - BasicAdapterInterface, 2, 0, RV32_REGISTER_NUM_LIMBS, 0>, - >>::execute_instruction(&core, &instruction, 0, [x, x]); - let (output, _) = result.expect("execute_instruction failed"); - assert!(output.to_pc.is_some()); - assert_eq!(output.to_pc.unwrap(), 8); -} +// #[test] +// fn execute_pc_increment_sanity_test() { +// let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); +// let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); +// let core = BranchLessThanStep::::new( +// bitwise_chip, +// BranchLessThanOpcode::CLASS_OFFSET, +// ); + +// let mut instruction = Instruction:: { +// opcode: BranchLessThanOpcode::BLT.global_opcode(), +// c: F::from_canonical_u8(8), +// ..Default::default() +// }; +// let x: [F; RV32_REGISTER_NUM_LIMBS] = [145, 34, 25, 205].map(F::from_canonical_u32); + +// let result = as VmCoreChip< +// F, +// BasicAdapterInterface, 2, 0, RV32_REGISTER_NUM_LIMBS, 0>, +// >>::execute_instruction(&core, &instruction, 0, [x, x]); +// let (output, _) = result.expect("execute_instruction failed"); +// assert!(output.to_pc.is_none()); + +// instruction.opcode = BranchLessThanOpcode::BGE.global_opcode(); +// let result = as VmCoreChip< +// F, +// BasicAdapterInterface, 2, 0, RV32_REGISTER_NUM_LIMBS, 0>, +// >>::execute_instruction(&core, &instruction, 0, [x, x]); +// let (output, _) = result.expect("execute_instruction failed"); +// assert!(output.to_pc.is_some()); +// assert_eq!(output.to_pc.unwrap(), 8); +// } #[test] fn run_cmp_unsigned_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [145, 34, 25, 205]; - let y: [u32; RV32_REGISTER_NUM_LIMBS] = [73, 35, 25, 205]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [145, 34, 25, 205]; + let y: [u8; RV32_REGISTER_NUM_LIMBS] = [73, 35, 25, 205]; let (cmp_result, diff_idx, x_sign, y_sign) = run_cmp::(BranchLessThanOpcode::BLTU, &x, &y); assert!(cmp_result); @@ -540,8 +551,8 @@ fn run_cmp_unsigned_sanity_test() { #[test] fn run_cmp_same_sign_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [145, 34, 25, 205]; - let y: [u32; RV32_REGISTER_NUM_LIMBS] = [73, 35, 25, 205]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [145, 34, 25, 205]; + let y: [u8; RV32_REGISTER_NUM_LIMBS] = [73, 35, 25, 205]; let (cmp_result, diff_idx, x_sign, y_sign) = run_cmp::(BranchLessThanOpcode::BLT, &x, &y); assert!(cmp_result); @@ -559,8 +570,8 @@ fn run_cmp_same_sign_sanity_test() { #[test] fn run_cmp_diff_sign_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [45, 35, 25, 55]; - let y: [u32; RV32_REGISTER_NUM_LIMBS] = [173, 34, 25, 205]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [45, 35, 25, 55]; + let y: [u8; RV32_REGISTER_NUM_LIMBS] = [173, 34, 25, 205]; let (cmp_result, diff_idx, x_sign, y_sign) = run_cmp::(BranchLessThanOpcode::BLT, &x, &y); assert!(!cmp_result); @@ -578,7 +589,7 @@ fn run_cmp_diff_sign_sanity_test() { #[test] fn run_cmp_eq_sanity_test() { - let x: [u32; RV32_REGISTER_NUM_LIMBS] = [45, 35, 25, 55]; + let x: [u8; RV32_REGISTER_NUM_LIMBS] = [45, 35, 25, 55]; let (cmp_result, diff_idx, x_sign, y_sign) = run_cmp::(BranchLessThanOpcode::BLT, &x, &x); assert!(!cmp_result); diff --git a/extensions/rv32im/circuit/src/divrem/core.rs b/extensions/rv32im/circuit/src/divrem/core.rs index d56c93ea0b..8dcbcabe61 100644 --- a/extensions/rv32im/circuit/src/divrem/core.rs +++ b/extensions/rv32im/circuit/src/divrem/core.rs @@ -7,10 +7,13 @@ use num_bigint::BigUint; use num_integer::Integer; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, - VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, + SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, @@ -18,9 +21,7 @@ use openvm_circuit_primitives::{ utils::{not, select}, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{ - instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_REGISTER_AS, LocalOpcode, -}; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; use openvm_rv32im_transpiler::DivRemOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -72,7 +73,7 @@ pub struct DivRemCoreCols { pub opcode_remu_flag: T, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, derive_new::new)] pub struct DivRemCoreAir { pub bitwise_lookup_bus: BitwiseOperationLookupBus, pub range_tuple_bus: RangeTupleCheckerBus<2>, @@ -347,44 +348,6 @@ where } } -pub struct DivRemCoreChip { - pub air: DivRemCoreAir, - pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - pub range_tuple_chip: SharedRangeTupleCheckerChip<2>, -} - -impl DivRemCoreChip { - pub fn new( - bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - range_tuple_chip: SharedRangeTupleCheckerChip<2>, - offset: usize, - ) -> Self { - // The RangeTupleChecker is used to range check (a[i], carry[i]) pairs where 0 <= i - // < 2 * NUM_LIMBS. a[i] must have LIMB_BITS bits and carry[i] is the sum of i + 1 - // bytes (with LIMB_BITS bits). BitwiseOperationLookup is used to sign check bytes. - debug_assert!( - range_tuple_chip.sizes()[0] == 1 << LIMB_BITS, - "First element of RangeTupleChecker must have size {}", - 1 << LIMB_BITS - ); - debug_assert!( - range_tuple_chip.sizes()[1] >= (1 << LIMB_BITS) * 2 * NUM_LIMBS as u32, - "Second element of RangeTupleChecker must have size of at least {}", - (1 << LIMB_BITS) * 2 * NUM_LIMBS as u32 - ); - - Self { - air: DivRemCoreAir { - bitwise_lookup_bus: bitwise_lookup_chip.bus(), - range_tuple_bus: *range_tuple_chip.bus(), - offset, - }, - bitwise_lookup_chip, - range_tuple_chip, - } - } -} - #[repr(C)] #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(bound = "T: Serialize + DeserializeOwned")] @@ -422,34 +385,85 @@ pub(super) enum DivRemCoreSpecialCase { SignedOverflow, } -impl, const NUM_LIMBS: usize, const LIMB_BITS: usize> - VmCoreChip for DivRemCoreChip +pub struct DivRemStep { + adapter: A, + pub offset: usize, + pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, + pub range_tuple_chip: SharedRangeTupleCheckerChip<2>, +} + +impl DivRemStep { + pub fn new( + adapter: A, + bitwise_lookup_chip: SharedBitwiseOperationLookupChip, + range_tuple_chip: SharedRangeTupleCheckerChip<2>, + offset: usize, + ) -> Self { + // The RangeTupleChecker is used to range check (a[i], carry[i]) pairs where 0 <= i + // < 2 * NUM_LIMBS. a[i] must have LIMB_BITS bits and carry[i] is the sum of i + 1 + // bytes (with LIMB_BITS bits). BitwiseOperationLookup is used to sign check bytes. + debug_assert!( + range_tuple_chip.sizes()[0] == 1 << LIMB_BITS, + "First element of RangeTupleChecker must have size {}", + 1 << LIMB_BITS + ); + debug_assert!( + range_tuple_chip.sizes()[1] >= (1 << LIMB_BITS) * 2 * NUM_LIMBS as u32, + "Second element of RangeTupleChecker must have size of at least {}", + (1 << LIMB_BITS) * 2 * NUM_LIMBS as u32 + ); + + Self { + adapter, + offset, + bitwise_lookup_chip, + range_tuple_chip, + } + } +} + +impl SingleTraceStep + for DivRemStep where - I::Reads: Into<[[F; NUM_LIMBS]; 2]>, - I::Writes: From<[[F; NUM_LIMBS]; 1]>, + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), + WriteData = [u8; NUM_LIMBS], + TraceContext<'a> = (), + >, { - type Record = DivRemCoreRecord; - type Air = DivRemCoreAir; + fn get_opcode_name(&self, opcode: usize) -> String { + format!("{:?}", DivRemOpcode::from_usize(opcode - self.offset)) + } - #[allow(clippy::type_complexity)] - fn execute_instruction( - &self, + fn execute( + &mut self, + state: VmStateMut, instruction: &Instruction, - _from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { + row_slice: &mut [F], + ) -> Result<()> { let Instruction { opcode, .. } = instruction; - let divrem_opcode = DivRemOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - let is_div = divrem_opcode == DivRemOpcode::DIV || divrem_opcode == DivRemOpcode::DIVU; + let divrem_opcode = DivRemOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + let is_signed = divrem_opcode == DivRemOpcode::DIV || divrem_opcode == DivRemOpcode::REM; + let is_div = divrem_opcode == DivRemOpcode::DIV || divrem_opcode == DivRemOpcode::DIVU; - let data: [[F; NUM_LIMBS]; 2] = reads.into(); - let b = data[0].map(|x| x.as_canonical_u32()); - let c = data[1].map(|y| y.as_canonical_u32()); + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + A::start(*state.pc, state.memory, adapter_row); + + let (rs1, rs2) = self.adapter.read(state.memory, instruction, adapter_row); + + let b = rs1.map(u32::from); + let c = rs2.map(u32::from); let (q, r, b_sign, c_sign, q_sign, case) = run_divrem::(is_signed, &b, &c); + // TODO(ayush): move parts to fill_trace_row let carries = run_mul_carries::(is_signed, &c, &q, &r, q_sign); for i in 0..NUM_LIMBS { self.range_tuple_chip.add_count(&[q[i], carries[i]]); @@ -474,7 +488,7 @@ where ); } - let c_sum_f = data[1].iter().fold(F::ZERO, |acc, c| acc + *c); + let c_sum_f = F::from_canonical_u32(c.iter().fold(0, |acc, c| acc + c)); let c_sum_inv_f = c_sum_f.try_inverse().unwrap_or(F::ZERO); let r_sum_f = r @@ -496,115 +510,94 @@ where }; let r_prime_f = r_prime.map(F::from_canonical_u32); - let output = AdapterRuntimeContext::without_pc([ - (if is_div { &q } else { &r }).map(F::from_canonical_u32) - ]); - let record = DivRemCoreRecord { - opcode: divrem_opcode, - b: data[0], - c: data[1], - q: q.map(F::from_canonical_u32), - r: r.map(F::from_canonical_u32), - zero_divisor: F::from_bool(case == DivRemCoreSpecialCase::ZeroDivisor), - r_zero: F::from_bool(r_zero), - b_sign: F::from_bool(b_sign), - c_sign: F::from_bool(c_sign), - q_sign: F::from_bool(q_sign), - sign_xor: F::from_bool(sign_xor), - c_sum_inv: c_sum_inv_f, - r_sum_inv: r_sum_inv_f, - r_prime: r_prime_f, - r_inv: r_prime_f.map(|r| (r - F::from_canonical_u32(256)).inverse()), - lt_diff_val: F::from_canonical_u32(lt_diff_val), - lt_diff_idx, + + let core_row: &mut DivRemCoreCols<_, NUM_LIMBS, LIMB_BITS> = core_row.borrow_mut(); + core_row.b = rs1.map(F::from_canonical_u8); + core_row.c = rs2.map(F::from_canonical_u8); + core_row.q = q.map(F::from_canonical_u32); + core_row.r = r.map(F::from_canonical_u32); + core_row.zero_divisor = F::from_bool(case == DivRemCoreSpecialCase::ZeroDivisor); + core_row.r_zero = F::from_bool(r_zero); + core_row.b_sign = F::from_bool(b_sign); + core_row.c_sign = F::from_bool(c_sign); + core_row.q_sign = F::from_bool(q_sign); + core_row.sign_xor = F::from_bool(sign_xor); + core_row.c_sum_inv = c_sum_inv_f; + core_row.r_sum_inv = r_sum_inv_f; + core_row.r_prime = r_prime_f; + core_row.r_inv = r_prime_f.map(|r| (r - F::from_canonical_u32(256)).inverse()); + core_row.lt_marker = array::from_fn(|i| F::from_bool(i == lt_diff_idx)); + core_row.lt_diff = F::from_canonical_u32(lt_diff_val); + core_row.opcode_div_flag = F::from_bool(divrem_opcode == DivRemOpcode::DIV); + core_row.opcode_divu_flag = F::from_bool(divrem_opcode == DivRemOpcode::DIVU); + core_row.opcode_rem_flag = F::from_bool(divrem_opcode == DivRemOpcode::REM); + core_row.opcode_remu_flag = F::from_bool(divrem_opcode == DivRemOpcode::REMU); + + let rd = if is_div { + q.map(|x| x as u8) + } else { + r.map(|x| x as u8) }; - Ok((output, record)) - } + self.adapter + .write(state.memory, instruction, adapter_row, &rd); - fn get_opcode_name(&self, opcode: usize) -> String { - format!("{:?}", DivRemOpcode::from_usize(opcode - self.air.offset)) - } + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let row_slice: &mut DivRemCoreCols<_, NUM_LIMBS, LIMB_BITS> = row_slice.borrow_mut(); - row_slice.b = record.b; - row_slice.c = record.c; - row_slice.q = record.q; - row_slice.r = record.r; - row_slice.zero_divisor = record.zero_divisor; - row_slice.r_zero = record.r_zero; - row_slice.b_sign = record.b_sign; - row_slice.c_sign = record.c_sign; - row_slice.q_sign = record.q_sign; - row_slice.sign_xor = record.sign_xor; - row_slice.c_sum_inv = record.c_sum_inv; - row_slice.r_sum_inv = record.r_sum_inv; - row_slice.r_prime = record.r_prime; - row_slice.r_inv = record.r_inv; - row_slice.lt_marker = array::from_fn(|i| F::from_bool(i == record.lt_diff_idx)); - row_slice.lt_diff = record.lt_diff_val; - row_slice.opcode_div_flag = F::from_bool(record.opcode == DivRemOpcode::DIV); - row_slice.opcode_divu_flag = F::from_bool(record.opcode == DivRemOpcode::DIVU); - row_slice.opcode_rem_flag = F::from_bool(record.opcode == DivRemOpcode::REM); - row_slice.opcode_remu_flag = F::from_bool(record.opcode == DivRemOpcode::REMU); + Ok(()) } - fn air(&self) -> &Self::Air { - &self.air + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, _core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + self.adapter.fill_trace_row(mem_helper, (), adapter_row); } } -impl InsExecutorE1 - for DivRemCoreChip +impl StepExecutorE1 + for DivRemStep where - Mem: GuestMemory, F: PrimeField32, + A: 'static + + for<'a> AdapterExecutorE1< + F, + ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), + WriteData = [u8; NUM_LIMBS], + >, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, a, b, c, .. - } = *instruction; + ) -> Result<()> + where + Mem: GuestMemory, + { + let Instruction { opcode, .. } = *instruction; // Determine opcode and operation type - let divrem_opcode = DivRemOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - - // Read input registers - let rs1_addr = b.as_canonical_u32(); - let rs2_addr = c.as_canonical_u32(); + let divrem_opcode = DivRemOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; - let rs2_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; - - // TODO(ayush): remove this conversion - let rs1_bytes = rs1_bytes.map(|x| x as u32); - let rs2_bytes = rs2_bytes.map(|y| y as u32); + let (rs1, rs2) = self.adapter.read(state.memory, instruction); + let rs1 = rs1.map(u32::from); + let rs2 = rs2.map(u32::from); let is_div = divrem_opcode == DivRemOpcode::DIV || divrem_opcode == DivRemOpcode::DIVU; let is_signed = divrem_opcode == DivRemOpcode::DIV || divrem_opcode == DivRemOpcode::REM; // Perform division/remainder computation - let (q, r, _, _, _, _) = - run_divrem::(is_signed, &rs1_bytes, &rs2_bytes); + let (q, r, _, _, _, _) = run_divrem::(is_signed, &rs1, &rs2); // Determine result based on operation type (DIV or REM) - let rd_bytes = if is_div { + let rd = if is_div { q.map(|x| x as u8) } else { r.map(|x| x as u8) }; - // Write result to destination register - let rd_addr = a.as_canonical_u32(); - unsafe { - state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes); - } + self.adapter.write(state.memory, instruction, &rd); - state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } @@ -612,6 +605,7 @@ where // Returns (quotient, remainder, x_sign, y_sign, q_sign, case) where case = 0 for normal, 1 // for zero divisor, and 2 for signed overflow +#[inline(always)] pub(super) fn run_divrem( signed: bool, x: &[u32; NUM_LIMBS], @@ -688,6 +682,7 @@ pub(super) fn run_divrem( (q, r, x_sign, y_sign, q_sign, DivRemCoreSpecialCase::None) } +#[inline(always)] pub(super) fn run_sltu_diff_idx( x: &[u32; NUM_LIMBS], y: &[u32; NUM_LIMBS], @@ -704,6 +699,7 @@ pub(super) fn run_sltu_diff_idx( } // returns carries of d * q + r +#[inline(always)] pub(super) fn run_mul_carries( signed: bool, d: &[u32; NUM_LIMBS], @@ -744,6 +740,7 @@ pub(super) fn run_mul_carries( carry } +#[inline(always)] fn limbs_to_biguint( x: &[u32; NUM_LIMBS], ) -> BigUint { @@ -756,6 +753,7 @@ fn limbs_to_biguint( res } +#[inline(always)] fn biguint_to_limbs( x: &BigUint, ) -> [u32; NUM_LIMBS] { @@ -771,6 +769,7 @@ fn biguint_to_limbs( res } +#[inline(always)] fn negate( x: &[u32; NUM_LIMBS], ) -> [u32; NUM_LIMBS] { diff --git a/extensions/rv32im/circuit/src/divrem/mod.rs b/extensions/rv32im/circuit/src/divrem/mod.rs index 979ab38dc3..15fd1ae6ea 100644 --- a/extensions/rv32im/circuit/src/divrem/mod.rs +++ b/extensions/rv32im/circuit/src/divrem/mod.rs @@ -1,6 +1,8 @@ -use openvm_circuit::arch::VmChipWrapper; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; -use super::adapters::{Rv32MultAdapterChip, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; +use crate::adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep}; + +use super::adapters::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; mod core; pub use core::*; @@ -8,8 +10,7 @@ pub use core::*; #[cfg(test)] mod tests; -pub type Rv32DivRemChip = VmChipWrapper< - F, - Rv32MultAdapterChip, - DivRemCoreChip, ->; +pub type Rv32DivRemAir = + VmAirWrapper>; +pub type Rv32DivRemStep = DivRemStep; +pub type Rv32DivRemChip = NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/divrem/tests.rs b/extensions/rv32im/circuit/src/divrem/tests.rs index 41d8a9cc46..393b2d678a 100644 --- a/extensions/rv32im/circuit/src/divrem/tests.rs +++ b/extensions/rv32im/circuit/src/divrem/tests.rs @@ -6,7 +6,7 @@ use openvm_circuit::{ memory::gen_pointer, TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS, RANGE_TUPLE_CHECKER_BUS, }, - ExecutionBridge, InstructionExecutor, VmAdapterChip, VmChipWrapper, + ExecutionBridge, InstructionExecutor, VmAdapterChip, VmAirWrapper, VmChipWrapper, }, utils::generate_long_number, }; @@ -32,14 +32,16 @@ use rand::{rngs::StdRng, Rng}; use super::core::run_divrem; use crate::{ - adapters::{Rv32MultAdapterChip, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, + adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, divrem::{ - run_mul_carries, run_sltu_diff_idx, DivRemCoreChip, DivRemCoreCols, DivRemCoreSpecialCase, + run_mul_carries, run_sltu_diff_idx, DivRemCoreCols, DivRemCoreSpecialCase, DivRemStep, Rv32DivRemChip, }, + DivRemCoreAir, Rv32DivRemStep, }; type F = BabyBear; +const MAX_INS_CAPACITY: usize = 128; ////////////////////////////////////////////////////////////////////////////////////// // POSITIVE TESTS @@ -104,18 +106,20 @@ fn run_rv32_divrem_rand_test(opcode: DivRemOpcode, num_ops: usize) { let range_tuple_checker = SharedRangeTupleCheckerChip::new(range_tuple_bus); let mut tester = VmChipTestBuilder::default(); + let mut chip = Rv32DivRemChip::::new( - Rv32MultAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), + VmAirWrapper::new( + Rv32MultAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), + DivRemCoreAir::new(bitwise_bus, range_tuple_bus, DivRemOpcode::CLASS_OFFSET), ), - DivRemCoreChip::new( + DivRemStep::new( + Rv32MultAdapterStep::new(), bitwise_chip.clone(), range_tuple_checker.clone(), DivRemOpcode::CLASS_OFFSET, ), - tester.offline_memory_mutex_arc(), + MAX_INS_CAPACITY, + tester.memory_helper(), ); for _ in 0..num_ops { @@ -216,337 +220,337 @@ fn rv32_remu_rand_test() { // A dummy adapter is used so memory interactions don't indirectly cause false passes. ////////////////////////////////////////////////////////////////////////////////////// -type Rv32DivRemTestChip = - VmChipWrapper, DivRemCoreChip>; - -#[derive(Default, Clone, Copy)] -struct DivRemPrankValues { - pub q: Option<[u32; NUM_LIMBS]>, - pub r: Option<[u32; NUM_LIMBS]>, - pub r_prime: Option<[u32; NUM_LIMBS]>, - pub diff_val: Option, - pub zero_divisor: Option, - pub r_zero: Option, -} - -fn run_rv32_divrem_negative_test( - signed: bool, - b: [u32; RV32_REGISTER_NUM_LIMBS], - c: [u32; RV32_REGISTER_NUM_LIMBS], - prank_vals: &DivRemPrankValues, - interaction_error: bool, -) { - // the max number of limbs we currently support MUL for is 32 (i.e. for U256s) - const MAX_NUM_LIMBS: u32 = 32; - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let range_tuple_bus = RangeTupleCheckerBus::new( - RANGE_TUPLE_CHECKER_BUS, - [1 << RV32_CELL_BITS, MAX_NUM_LIMBS * (1 << RV32_CELL_BITS)], - ); - - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let range_tuple_chip = SharedRangeTupleCheckerChip::new(range_tuple_bus); - - let mut tester = VmChipTestBuilder::default(); - let mut chip = Rv32DivRemTestChip::::new( - TestAdapterChip::new( - vec![[b.map(F::from_canonical_u32), c.map(F::from_canonical_u32)].concat(); 2], - vec![None], - ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), - ), - DivRemCoreChip::new( - bitwise_chip.clone(), - range_tuple_chip.clone(), - DivRemOpcode::CLASS_OFFSET, - ), - tester.offline_memory_mutex_arc(), - ); - - let (div_opcode, rem_opcode) = if signed { - (DivRemOpcode::DIV, DivRemOpcode::REM) - } else { - (DivRemOpcode::DIVU, DivRemOpcode::REMU) - }; - tester.execute( - &mut chip, - &Instruction::from_usize(div_opcode.global_opcode(), [0, 0, 0, 1, 1]), - ); - tester.execute( - &mut chip, - &Instruction::from_usize(rem_opcode.global_opcode(), [0, 0, 0, 1, 1]), - ); - - let (q, r, b_sign, c_sign, q_sign, case) = - run_divrem::(signed, &b, &c); - let q = prank_vals.q.unwrap_or(q); - let r = prank_vals.r.unwrap_or(r); - let carries = - run_mul_carries::(signed, &c, &q, &r, q_sign); - - range_tuple_chip.clear(); - for i in 0..RV32_REGISTER_NUM_LIMBS { - range_tuple_chip.add_count(&[q[i], carries[i]]); - range_tuple_chip.add_count(&[r[i], carries[i + RV32_REGISTER_NUM_LIMBS]]); - } - - if let Some(diff_val) = prank_vals.diff_val { - bitwise_chip.clear(); - if signed { - let b_sign_mask = if b_sign { 1 << (RV32_CELL_BITS - 1) } else { 0 }; - let c_sign_mask = if c_sign { 1 << (RV32_CELL_BITS - 1) } else { 0 }; - bitwise_chip.request_range( - (b[RV32_REGISTER_NUM_LIMBS - 1] - b_sign_mask) << 1, - (c[RV32_REGISTER_NUM_LIMBS - 1] - c_sign_mask) << 1, - ); - } - if case == DivRemCoreSpecialCase::None { - bitwise_chip.request_range(diff_val - 1, 0); - } - } - - let trace_width = chip.trace_width(); - let adapter_width = BaseAir::::width(chip.adapter.air()); - - let modify_trace = |trace: &mut DenseMatrix| { - let mut values = trace.row_slice(0).to_vec(); - let cols: &mut DivRemCoreCols = - values.split_at_mut(adapter_width).1.borrow_mut(); - - if let Some(q) = prank_vals.q { - cols.q = q.map(F::from_canonical_u32); - } - if let Some(r) = prank_vals.r { - cols.r = r.map(F::from_canonical_u32); - let r_sum = r.iter().sum::(); - cols.r_sum_inv = F::from_canonical_u32(r_sum) - .try_inverse() - .unwrap_or(F::ZERO); - } - if let Some(r_prime) = prank_vals.r_prime { - cols.r_prime = r_prime.map(F::from_canonical_u32); - cols.r_inv = cols - .r_prime - .map(|r| (r - F::from_canonical_u32(256)).inverse()); - } - if let Some(diff_val) = prank_vals.diff_val { - cols.lt_diff = F::from_canonical_u32(diff_val); - } - if let Some(zero_divisor) = prank_vals.zero_divisor { - cols.zero_divisor = F::from_bool(zero_divisor); - } - if let Some(r_zero) = prank_vals.r_zero { - cols.r_zero = F::from_bool(r_zero); - } - - *trace = RowMajorMatrix::new(values, trace_width); - }; - - disable_debug_builder(); - let tester = tester - .build() - .load_and_prank_trace(chip, modify_trace) - .load(bitwise_chip) - .load(range_tuple_chip) - .finalize(); - tester.simple_test_with_expected_error(if interaction_error { - VerificationError::ChallengePhaseError - } else { - VerificationError::OodEvaluationMismatch - }); -} - -#[test] -fn rv32_divrem_unsigned_wrong_q_negative_test() { - let b: [u32; RV32_REGISTER_NUM_LIMBS] = [98, 188, 163, 229]; - let c: [u32; RV32_REGISTER_NUM_LIMBS] = [123, 34, 0, 0]; - let prank_vals = DivRemPrankValues { - q: Some([245, 168, 7, 0]), - ..Default::default() - }; - run_rv32_divrem_negative_test(false, b, c, &prank_vals, true); -} - -#[test] -fn rv32_divrem_unsigned_wrong_r_negative_test() { - let b: [u32; RV32_REGISTER_NUM_LIMBS] = [98, 188, 163, 229]; - let c: [u32; RV32_REGISTER_NUM_LIMBS] = [123, 34, 0, 0]; - let prank_vals = DivRemPrankValues { - r: Some([171, 3, 0, 0]), - r_prime: Some([171, 3, 0, 0]), - diff_val: Some(31), - ..Default::default() - }; - run_rv32_divrem_negative_test(false, b, c, &prank_vals, true); -} - -#[test] -fn rv32_divrem_unsigned_high_mult_negative_test() { - let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; - let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 2, 0, 0]; - let prank_vals = DivRemPrankValues { - q: Some([128, 0, 0, 1]), - ..Default::default() - }; - run_rv32_divrem_negative_test(false, b, c, &prank_vals, true); -} - -#[test] -fn rv32_divrem_unsigned_zero_divisor_wrong_r_negative_test() { - let b: [u32; RV32_REGISTER_NUM_LIMBS] = [254, 255, 255, 255]; - let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; - let prank_vals = DivRemPrankValues { - r: Some([255, 255, 255, 255]), - r_prime: Some([255, 255, 255, 255]), - diff_val: Some(255), - ..Default::default() - }; - run_rv32_divrem_negative_test(false, b, c, &prank_vals, true); -} - -#[test] -fn rv32_divrem_signed_wrong_q_negative_test() { - let b: [u32; RV32_REGISTER_NUM_LIMBS] = [98, 188, 163, 229]; - let c: [u32; RV32_REGISTER_NUM_LIMBS] = [123, 34, 0, 0]; - let prank_vals = DivRemPrankValues { - q: Some([74, 61, 255, 255]), - ..Default::default() - }; - run_rv32_divrem_negative_test(true, b, c, &prank_vals, true); -} - -#[test] -fn rv32_divrem_signed_wrong_r_negative_test() { - let b: [u32; RV32_REGISTER_NUM_LIMBS] = [98, 188, 163, 229]; - let c: [u32; RV32_REGISTER_NUM_LIMBS] = [123, 34, 0, 0]; - let prank_vals = DivRemPrankValues { - r: Some([212, 241, 255, 255]), - r_prime: Some([44, 14, 0, 0]), - diff_val: Some(20), - ..Default::default() - }; - run_rv32_divrem_negative_test(true, b, c, &prank_vals, true); -} - -#[test] -fn rv32_divrem_signed_high_mult_negative_test() { - let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 255]; - let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 255]; - let prank_vals = DivRemPrankValues { - q: Some([1, 0, 0, 1]), - ..Default::default() - }; - run_rv32_divrem_negative_test(true, b, c, &prank_vals, true); -} - -#[test] -fn rv32_divrem_signed_r_wrong_sign_negative_test() { - let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; - let c: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 0, 0, 0]; - let prank_vals = DivRemPrankValues { - q: Some([31, 5, 0, 0]), - r: Some([242, 255, 255, 255]), - r_prime: Some([242, 255, 255, 255]), - diff_val: Some(192), - ..Default::default() - }; - run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); -} - -#[test] -fn rv32_divrem_signed_r_wrong_prime_negative_test() { - let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; - let c: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 0, 0, 0]; - let prank_vals = DivRemPrankValues { - q: Some([31, 5, 0, 0]), - r: Some([242, 255, 255, 255]), - r_prime: Some([14, 0, 0, 0]), - diff_val: Some(36), - ..Default::default() - }; - run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); -} - -#[test] -fn rv32_divrem_signed_zero_divisor_wrong_r_negative_test() { - let b: [u32; RV32_REGISTER_NUM_LIMBS] = [254, 255, 255, 255]; - let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; - let prank_vals = DivRemPrankValues { - r: Some([255, 255, 255, 255]), - r_prime: Some([1, 0, 0, 0]), - diff_val: Some(1), - ..Default::default() - }; - run_rv32_divrem_negative_test(true, b, c, &prank_vals, true); -} - -#[test] -fn rv32_divrem_false_zero_divisor_flag_negative_test() { - let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; - let c: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 0, 0, 0]; - let prank_vals = DivRemPrankValues { - q: Some([29, 5, 0, 0]), - r: Some([86, 0, 0, 0]), - r_prime: Some([86, 0, 0, 0]), - diff_val: Some(36), - zero_divisor: Some(true), - ..Default::default() - }; - run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); - run_rv32_divrem_negative_test(false, b, c, &prank_vals, false); -} - -#[test] -fn rv32_divrem_false_r_zero_flag_negative_test() { - let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; - let c: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 0, 0, 0]; - let prank_vals = DivRemPrankValues { - q: Some([29, 5, 0, 0]), - r: Some([86, 0, 0, 0]), - r_prime: Some([86, 0, 0, 0]), - diff_val: Some(36), - r_zero: Some(true), - ..Default::default() - }; - run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); - run_rv32_divrem_negative_test(false, b, c, &prank_vals, false); -} - -#[test] -fn rv32_divrem_unset_zero_divisor_flag_negative_test() { - let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; - let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; - let prank_vals = DivRemPrankValues { - zero_divisor: Some(false), - ..Default::default() - }; - run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); - run_rv32_divrem_negative_test(false, b, c, &prank_vals, false); -} - -#[test] -fn rv32_divrem_wrong_r_zero_flag_negative_test() { - let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; - let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; - let prank_vals = DivRemPrankValues { - zero_divisor: Some(false), - r_zero: Some(true), - ..Default::default() - }; - run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); - run_rv32_divrem_negative_test(false, b, c, &prank_vals, false); -} - -#[test] -fn rv32_divrem_unset_r_zero_flag_negative_test() { - let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; - let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; - let prank_vals = DivRemPrankValues { - r_zero: Some(false), - ..Default::default() - }; - run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); - run_rv32_divrem_negative_test(false, b, c, &prank_vals, false); -} +// type Rv32DivRemTestChip = +// VmChipWrapper, DivRemStep>; + +// #[derive(Default, Clone, Copy)] +// struct DivRemPrankValues { +// pub q: Option<[u32; NUM_LIMBS]>, +// pub r: Option<[u32; NUM_LIMBS]>, +// pub r_prime: Option<[u32; NUM_LIMBS]>, +// pub diff_val: Option, +// pub zero_divisor: Option, +// pub r_zero: Option, +// } + +// fn run_rv32_divrem_negative_test( +// signed: bool, +// b: [u32; RV32_REGISTER_NUM_LIMBS], +// c: [u32; RV32_REGISTER_NUM_LIMBS], +// prank_vals: &DivRemPrankValues, +// interaction_error: bool, +// ) { +// // the max number of limbs we currently support MUL for is 32 (i.e. for U256s) +// const MAX_NUM_LIMBS: u32 = 32; +// let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); +// let range_tuple_bus = RangeTupleCheckerBus::new( +// RANGE_TUPLE_CHECKER_BUS, +// [1 << RV32_CELL_BITS, MAX_NUM_LIMBS * (1 << RV32_CELL_BITS)], +// ); + +// let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); +// let range_tuple_chip = SharedRangeTupleCheckerChip::new(range_tuple_bus); + +// let mut tester = VmChipTestBuilder::default(); +// let mut chip = Rv32DivRemTestChip::::new( +// TestAdapterChip::new( +// vec![[b.map(F::from_canonical_u32), c.map(F::from_canonical_u32)].concat(); 2], +// vec![None], +// ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), +// ), +// DivRemStep::new( +// bitwise_chip.clone(), +// range_tuple_chip.clone(), +// DivRemOpcode::CLASS_OFFSET, +// ), +// tester.offline_memory_mutex_arc(), +// ); + +// let (div_opcode, rem_opcode) = if signed { +// (DivRemOpcode::DIV, DivRemOpcode::REM) +// } else { +// (DivRemOpcode::DIVU, DivRemOpcode::REMU) +// }; +// tester.execute( +// &mut chip, +// &Instruction::from_usize(div_opcode.global_opcode(), [0, 0, 0, 1, 1]), +// ); +// tester.execute( +// &mut chip, +// &Instruction::from_usize(rem_opcode.global_opcode(), [0, 0, 0, 1, 1]), +// ); + +// let (q, r, b_sign, c_sign, q_sign, case) = +// run_divrem::(signed, &b, &c); +// let q = prank_vals.q.unwrap_or(q); +// let r = prank_vals.r.unwrap_or(r); +// let carries = +// run_mul_carries::(signed, &c, &q, &r, q_sign); + +// range_tuple_chip.clear(); +// for i in 0..RV32_REGISTER_NUM_LIMBS { +// range_tuple_chip.add_count(&[q[i], carries[i]]); +// range_tuple_chip.add_count(&[r[i], carries[i + RV32_REGISTER_NUM_LIMBS]]); +// } + +// if let Some(diff_val) = prank_vals.diff_val { +// bitwise_chip.clear(); +// if signed { +// let b_sign_mask = if b_sign { 1 << (RV32_CELL_BITS - 1) } else { 0 }; +// let c_sign_mask = if c_sign { 1 << (RV32_CELL_BITS - 1) } else { 0 }; +// bitwise_chip.request_range( +// (b[RV32_REGISTER_NUM_LIMBS - 1] - b_sign_mask) << 1, +// (c[RV32_REGISTER_NUM_LIMBS - 1] - c_sign_mask) << 1, +// ); +// } +// if case == DivRemCoreSpecialCase::None { +// bitwise_chip.request_range(diff_val - 1, 0); +// } +// } + +// let trace_width = chip.trace_width(); +// let adapter_width = BaseAir::::width(chip.adapter.air()); + +// let modify_trace = |trace: &mut DenseMatrix| { +// let mut values = trace.row_slice(0).to_vec(); +// let cols: &mut DivRemCoreCols = +// values.split_at_mut(adapter_width).1.borrow_mut(); + +// if let Some(q) = prank_vals.q { +// cols.q = q.map(F::from_canonical_u32); +// } +// if let Some(r) = prank_vals.r { +// cols.r = r.map(F::from_canonical_u32); +// let r_sum = r.iter().sum::(); +// cols.r_sum_inv = F::from_canonical_u32(r_sum) +// .try_inverse() +// .unwrap_or(F::ZERO); +// } +// if let Some(r_prime) = prank_vals.r_prime { +// cols.r_prime = r_prime.map(F::from_canonical_u32); +// cols.r_inv = cols +// .r_prime +// .map(|r| (r - F::from_canonical_u32(256)).inverse()); +// } +// if let Some(diff_val) = prank_vals.diff_val { +// cols.lt_diff = F::from_canonical_u32(diff_val); +// } +// if let Some(zero_divisor) = prank_vals.zero_divisor { +// cols.zero_divisor = F::from_bool(zero_divisor); +// } +// if let Some(r_zero) = prank_vals.r_zero { +// cols.r_zero = F::from_bool(r_zero); +// } + +// *trace = RowMajorMatrix::new(values, trace_width); +// }; + +// disable_debug_builder(); +// let tester = tester +// .build() +// .load_and_prank_trace(chip, modify_trace) +// .load(bitwise_chip) +// .load(range_tuple_chip) +// .finalize(); +// tester.simple_test_with_expected_error(if interaction_error { +// VerificationError::ChallengePhaseError +// } else { +// VerificationError::OodEvaluationMismatch +// }); +// } + +// #[test] +// fn rv32_divrem_unsigned_wrong_q_negative_test() { +// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [98, 188, 163, 229]; +// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [123, 34, 0, 0]; +// let prank_vals = DivRemPrankValues { +// q: Some([245, 168, 7, 0]), +// ..Default::default() +// }; +// run_rv32_divrem_negative_test(false, b, c, &prank_vals, true); +// } + +// #[test] +// fn rv32_divrem_unsigned_wrong_r_negative_test() { +// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [98, 188, 163, 229]; +// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [123, 34, 0, 0]; +// let prank_vals = DivRemPrankValues { +// r: Some([171, 3, 0, 0]), +// r_prime: Some([171, 3, 0, 0]), +// diff_val: Some(31), +// ..Default::default() +// }; +// run_rv32_divrem_negative_test(false, b, c, &prank_vals, true); +// } + +// #[test] +// fn rv32_divrem_unsigned_high_mult_negative_test() { +// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; +// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 2, 0, 0]; +// let prank_vals = DivRemPrankValues { +// q: Some([128, 0, 0, 1]), +// ..Default::default() +// }; +// run_rv32_divrem_negative_test(false, b, c, &prank_vals, true); +// } + +// #[test] +// fn rv32_divrem_unsigned_zero_divisor_wrong_r_negative_test() { +// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [254, 255, 255, 255]; +// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; +// let prank_vals = DivRemPrankValues { +// r: Some([255, 255, 255, 255]), +// r_prime: Some([255, 255, 255, 255]), +// diff_val: Some(255), +// ..Default::default() +// }; +// run_rv32_divrem_negative_test(false, b, c, &prank_vals, true); +// } + +// #[test] +// fn rv32_divrem_signed_wrong_q_negative_test() { +// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [98, 188, 163, 229]; +// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [123, 34, 0, 0]; +// let prank_vals = DivRemPrankValues { +// q: Some([74, 61, 255, 255]), +// ..Default::default() +// }; +// run_rv32_divrem_negative_test(true, b, c, &prank_vals, true); +// } + +// #[test] +// fn rv32_divrem_signed_wrong_r_negative_test() { +// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [98, 188, 163, 229]; +// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [123, 34, 0, 0]; +// let prank_vals = DivRemPrankValues { +// r: Some([212, 241, 255, 255]), +// r_prime: Some([44, 14, 0, 0]), +// diff_val: Some(20), +// ..Default::default() +// }; +// run_rv32_divrem_negative_test(true, b, c, &prank_vals, true); +// } + +// #[test] +// fn rv32_divrem_signed_high_mult_negative_test() { +// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 255]; +// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 255]; +// let prank_vals = DivRemPrankValues { +// q: Some([1, 0, 0, 1]), +// ..Default::default() +// }; +// run_rv32_divrem_negative_test(true, b, c, &prank_vals, true); +// } + +// #[test] +// fn rv32_divrem_signed_r_wrong_sign_negative_test() { +// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; +// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 0, 0, 0]; +// let prank_vals = DivRemPrankValues { +// q: Some([31, 5, 0, 0]), +// r: Some([242, 255, 255, 255]), +// r_prime: Some([242, 255, 255, 255]), +// diff_val: Some(192), +// ..Default::default() +// }; +// run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); +// } + +// #[test] +// fn rv32_divrem_signed_r_wrong_prime_negative_test() { +// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; +// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 0, 0, 0]; +// let prank_vals = DivRemPrankValues { +// q: Some([31, 5, 0, 0]), +// r: Some([242, 255, 255, 255]), +// r_prime: Some([14, 0, 0, 0]), +// diff_val: Some(36), +// ..Default::default() +// }; +// run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); +// } + +// #[test] +// fn rv32_divrem_signed_zero_divisor_wrong_r_negative_test() { +// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [254, 255, 255, 255]; +// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; +// let prank_vals = DivRemPrankValues { +// r: Some([255, 255, 255, 255]), +// r_prime: Some([1, 0, 0, 0]), +// diff_val: Some(1), +// ..Default::default() +// }; +// run_rv32_divrem_negative_test(true, b, c, &prank_vals, true); +// } + +// #[test] +// fn rv32_divrem_false_zero_divisor_flag_negative_test() { +// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; +// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 0, 0, 0]; +// let prank_vals = DivRemPrankValues { +// q: Some([29, 5, 0, 0]), +// r: Some([86, 0, 0, 0]), +// r_prime: Some([86, 0, 0, 0]), +// diff_val: Some(36), +// zero_divisor: Some(true), +// ..Default::default() +// }; +// run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); +// run_rv32_divrem_negative_test(false, b, c, &prank_vals, false); +// } + +// #[test] +// fn rv32_divrem_false_r_zero_flag_negative_test() { +// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; +// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 0, 0, 0]; +// let prank_vals = DivRemPrankValues { +// q: Some([29, 5, 0, 0]), +// r: Some([86, 0, 0, 0]), +// r_prime: Some([86, 0, 0, 0]), +// diff_val: Some(36), +// r_zero: Some(true), +// ..Default::default() +// }; +// run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); +// run_rv32_divrem_negative_test(false, b, c, &prank_vals, false); +// } + +// #[test] +// fn rv32_divrem_unset_zero_divisor_flag_negative_test() { +// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; +// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; +// let prank_vals = DivRemPrankValues { +// zero_divisor: Some(false), +// ..Default::default() +// }; +// run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); +// run_rv32_divrem_negative_test(false, b, c, &prank_vals, false); +// } + +// #[test] +// fn rv32_divrem_wrong_r_zero_flag_negative_test() { +// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; +// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; +// let prank_vals = DivRemPrankValues { +// zero_divisor: Some(false), +// r_zero: Some(true), +// ..Default::default() +// }; +// run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); +// run_rv32_divrem_negative_test(false, b, c, &prank_vals, false); +// } + +// #[test] +// fn rv32_divrem_unset_r_zero_flag_negative_test() { +// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; +// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; +// let prank_vals = DivRemPrankValues { +// r_zero: Some(false), +// ..Default::default() +// }; +// run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); +// run_rv32_divrem_negative_test(false, b, c, &prank_vals, false); +// } /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS diff --git a/extensions/rv32im/circuit/src/extension.rs b/extensions/rv32im/circuit/src/extension.rs index e62ad1e426..635a4f0ec4 100644 --- a/extensions/rv32im/circuit/src/extension.rs +++ b/extensions/rv32im/circuit/src/extension.rs @@ -1,11 +1,12 @@ use derive_more::derive::From; use openvm_circuit::{ arch::{ - SystemConfig, SystemPort, VmExtension, VmInventory, VmInventoryBuilder, VmInventoryError, + ExecutionBridge, SystemConfig, SystemPort, VmAirWrapper, VmExtension, VmInventory, + VmInventoryBuilder, VmInventoryError, }, system::phantom::PhantomChip, }; -use openvm_circuit_derive::{AnyEnum, InstructionExecutor, VmConfig}; +use openvm_circuit_derive::{AnyEnum, InsExecutorE1, InstructionExecutor, VmConfig}; use openvm_circuit_primitives::{ bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, range_tuple::{RangeTupleCheckerBus, SharedRangeTupleCheckerChip}, @@ -23,6 +24,9 @@ use strum::IntoEnumIterator; use crate::{adapters::*, *}; +// TODO(ayush): this should be decided after e2 execution +const MAX_INS_CAPACITY: usize = 0; + /// Config for a VM with base extension and IO extension #[derive(Clone, Debug, VmConfig, derive_new::new, Serialize, Deserialize)] pub struct Rv32IConfig { @@ -127,7 +131,7 @@ fn default_range_tuple_checker_sizes() -> [u32; 2] { // ============ Executor and Periphery Enums for Extension ============ /// RISC-V 32-bit Base (RV32I) Instruction Executors -#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)] +#[derive(ChipUsageGetter, Chip, InstructionExecutor, InsExecutorE1, From, AnyEnum)] pub enum Rv32IExecutor { // Rv32 (for standard 32-bit integers): BaseAlu(Rv32BaseAluChip), @@ -143,7 +147,7 @@ pub enum Rv32IExecutor { } /// RISC-V 32-bit Multiplication Extension (RV32M) Instruction Executors -#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)] +#[derive(ChipUsageGetter, Chip, InstructionExecutor, InsExecutorE1, From, AnyEnum)] pub enum Rv32MExecutor { Multiplication(Rv32MultiplicationChip), MultiplicationHigh(Rv32MulHChip), @@ -151,7 +155,7 @@ pub enum Rv32MExecutor { } /// RISC-V 32-bit Io Instruction Executors -#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)] +#[derive(ChipUsageGetter, Chip, InstructionExecutor, InsExecutorE1, From, AnyEnum)] pub enum Rv32IoExecutor { HintStore(Rv32HintStoreChip), } @@ -197,7 +201,6 @@ impl VmExtension for Rv32I { } = builder.system_port(); let range_checker = builder.system_base().range_checker_chip.clone(); - let offline_memory = builder.system_base().offline_memory(); let pointer_max_bits = builder.system_config().memory_config.pointer_max_bits; let bitwise_lu_chip = if let Some(&chip) = builder @@ -212,59 +215,88 @@ impl VmExtension for Rv32I { chip }; - // let base_alu_chip = Rv32BaseAluChip::new( - // Rv32BaseAluAdapterChip::new( - // execution_bus, - // program_bus, - // memory_bridge, - // bitwise_lu_chip.clone(), - // ), - // BaseAluCoreChip::new(bitwise_lu_chip.clone(), BaseAluOpcode::CLASS_OFFSET), - // offline_memory.clone(), - // ); - // inventory.add_executor( - // base_alu_chip, - // BaseAluOpcode::iter().map(|x| x.global_opcode()), - // )?; - - // let lt_chip = Rv32LessThanChip::new( - // Rv32BaseAluAdapterChip::new( - // execution_bus, - // program_bus, - // memory_bridge, - // bitwise_lu_chip.clone(), - // ), - // LessThanCoreChip::new(bitwise_lu_chip.clone(), LessThanOpcode::CLASS_OFFSET), - // offline_memory.clone(), - // ); - // inventory.add_executor(lt_chip, LessThanOpcode::iter().map(|x| x.global_opcode()))?; - - // let shift_chip = Rv32ShiftChip::new( - // Rv32BaseAluAdapterChip::new( - // execution_bus, - // program_bus, - // memory_bridge, - // bitwise_lu_chip.clone(), - // ), - // ShiftCoreChip::new( - // bitwise_lu_chip.clone(), - // range_checker.clone(), - // ShiftOpcode::CLASS_OFFSET, - // ), - // offline_memory.clone(), - // ); - // inventory.add_executor(shift_chip, ShiftOpcode::iter().map(|x| x.global_opcode()))?; + let base_alu_chip = Rv32BaseAluChip::new( + VmAirWrapper::new( + Rv32BaseAluAdapterAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + bitwise_lu_chip.bus(), + ), + BaseAluCoreAir::new(bitwise_lu_chip.bus(), BaseAluOpcode::CLASS_OFFSET), + ), + Rv32BaseAluStep::new( + Rv32BaseAluAdapterStep::new(), + bitwise_lu_chip.clone(), + BaseAluOpcode::CLASS_OFFSET, + ), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), + ); + inventory.add_executor( + base_alu_chip, + BaseAluOpcode::iter().map(|x| x.global_opcode()), + )?; + + let lt_chip = Rv32LessThanChip::new( + VmAirWrapper::new( + Rv32BaseAluAdapterAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + bitwise_lu_chip.bus(), + ), + LessThanCoreAir::new(bitwise_lu_chip.bus(), LessThanOpcode::CLASS_OFFSET), + ), + LessThanStep::new( + Rv32BaseAluAdapterStep::new(), + bitwise_lu_chip.clone(), + LessThanOpcode::CLASS_OFFSET, + ), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), + ); + inventory.add_executor(lt_chip, LessThanOpcode::iter().map(|x| x.global_opcode()))?; + + let shift_chip = Rv32ShiftChip::new( + VmAirWrapper::new( + Rv32BaseAluAdapterAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + bitwise_lu_chip.bus(), + ), + ShiftCoreAir::new( + bitwise_lu_chip.bus(), + range_checker.bus(), + ShiftOpcode::CLASS_OFFSET, + ), + ), + ShiftStep::new( + Rv32BaseAluAdapterStep::new(), + bitwise_lu_chip.clone(), + range_checker.clone(), + ShiftOpcode::CLASS_OFFSET, + ), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), + ); + inventory.add_executor(shift_chip, ShiftOpcode::iter().map(|x| x.global_opcode()))?; let load_store_chip = Rv32LoadStoreChip::new( - Rv32LoadStoreAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - pointer_max_bits, + VmAirWrapper::new( + Rv32LoadStoreAdapterAir::new( + memory_bridge, + ExecutionBridge::new(execution_bus, program_bus), + range_checker.bus(), + pointer_max_bits, + ), + LoadStoreCoreAir::new(Rv32LoadStoreOpcode::CLASS_OFFSET), + ), + LoadStoreStep::new( + Rv32LoadStoreAdapterStep::new(pointer_max_bits), range_checker.clone(), + Rv32LoadStoreOpcode::CLASS_OFFSET, ), - LoadStoreCoreChip::new(Rv32LoadStoreOpcode::CLASS_OFFSET), - offline_memory.clone(), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor( load_store_chip, @@ -274,15 +306,21 @@ impl VmExtension for Rv32I { )?; let load_sign_extend_chip = Rv32LoadSignExtendChip::new( - Rv32LoadStoreAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - pointer_max_bits, + VmAirWrapper::new( + Rv32LoadStoreAdapterAir::new( + memory_bridge, + ExecutionBridge::new(execution_bus, program_bus), + range_checker.bus(), + pointer_max_bits, + ), + LoadSignExtendCoreAir::new(range_checker.bus()), + ), + LoadSignExtendStep::new( + Rv32LoadStoreAdapterStep::new(pointer_max_bits), range_checker.clone(), ), - LoadSignExtendCoreChip::new(range_checker.clone()), - offline_memory.clone(), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor( load_sign_extend_chip, @@ -290,56 +328,104 @@ impl VmExtension for Rv32I { )?; let beq_chip = Rv32BranchEqualChip::new( - Rv32BranchAdapterChip::new(execution_bus, program_bus, memory_bridge), - BranchEqualCoreChip::new(BranchEqualOpcode::CLASS_OFFSET, DEFAULT_PC_STEP), - offline_memory.clone(), + VmAirWrapper::new( + Rv32BranchAdapterAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + ), + BranchEqualCoreAir::new(BranchEqualOpcode::CLASS_OFFSET, DEFAULT_PC_STEP), + ), + BranchEqualStep::new( + Rv32BranchAdapterStep::new(), + BranchEqualOpcode::CLASS_OFFSET, + DEFAULT_PC_STEP, + ), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor( beq_chip, BranchEqualOpcode::iter().map(|x| x.global_opcode()), )?; - let blt_chip = Rv32BranchLessThanChip::new( - Rv32BranchAdapterChip::new(execution_bus, program_bus, memory_bridge), - BranchLessThanCoreChip::new( + let blt_chip = Rv32BranchLessThanChip::::new( + VmAirWrapper::new( + Rv32BranchAdapterAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + ), + BranchLessThanCoreAir::new( + bitwise_lu_chip.bus(), + BranchLessThanOpcode::CLASS_OFFSET, + ), + ), + BranchLessThanStep::new( + Rv32BranchAdapterStep::new(), bitwise_lu_chip.clone(), BranchLessThanOpcode::CLASS_OFFSET, ), - offline_memory.clone(), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor( blt_chip, BranchLessThanOpcode::iter().map(|x| x.global_opcode()), )?; - // TODO - // let jal_lui_chip = Rv32JalLuiChip::new( - // Rv32CondRdWriteAdapterChip::new(execution_bus, program_bus, memory_bridge), - // Rv32JalLuiCoreChip::new(bitwise_lu_chip.clone()), - // offline_memory.clone(), - // ); - // inventory.add_executor( - // jal_lui_chip, - // Rv32JalLuiOpcode::iter().map(|x| x.global_opcode()), - // )?; - - let jalr_chip = Rv32JalrChip::new( - Rv32JalrAdapterChip::new(execution_bus, program_bus, memory_bridge), - Rv32JalrCoreChip::new(bitwise_lu_chip.clone(), range_checker.clone()), - offline_memory.clone(), + let jal_lui_chip = Rv32JalLuiChip::::new( + VmAirWrapper::new( + Rv32CondRdWriteAdapterAir::new(Rv32RdWriteAdapterAir::new( + memory_bridge, + ExecutionBridge::new(execution_bus, program_bus), + )), + Rv32JalLuiCoreAir::new(bitwise_lu_chip.bus()), + ), + Rv32JalLuiStep::new( + Rv32CondRdWriteAdapterStep::new(Rv32RdWriteAdapterStep::new()), + bitwise_lu_chip.clone(), + ), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), + ); + inventory.add_executor( + jal_lui_chip, + Rv32JalLuiOpcode::iter().map(|x| x.global_opcode()), + )?; + + let jalr_chip = Rv32JalrChip::::new( + VmAirWrapper::new( + Rv32JalrAdapterAir::new( + memory_bridge, + ExecutionBridge::new(execution_bus, program_bus), + ), + Rv32JalrCoreAir::new(bitwise_lu_chip.bus(), range_checker.bus()), + ), + Rv32JalrStep::new( + Rv32JalrAdapterStep::new(), + bitwise_lu_chip.clone(), + range_checker.clone(), + ), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor(jalr_chip, Rv32JalrOpcode::iter().map(|x| x.global_opcode()))?; - // TODO - // let auipc_chip = Rv32AuipcChip::new( - // Rv32RdWriteAdapterChip::new(execution_bus, program_bus, memory_bridge), - // Rv32AuipcCoreChip::new(bitwise_lu_chip.clone()), - // offline_memory.clone(), - // ); - // inventory.add_executor( - // auipc_chip, - // Rv32AuipcOpcode::iter().map(|x| x.global_opcode()), - // )?; + let auipc_chip = Rv32AuipcChip::::new( + VmAirWrapper::new( + Rv32RdWriteAdapterAir::new( + memory_bridge, + ExecutionBridge::new(execution_bus, program_bus), + ), + Rv32AuipcCoreAir::new(bitwise_lu_chip.bus()), + ), + Rv32AuipcStep::new(Rv32RdWriteAdapterStep::new(), bitwise_lu_chip.clone()), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), + ); + inventory.add_executor( + auipc_chip, + Rv32AuipcOpcode::iter().map(|x| x.global_opcode()), + )?; // There is no downside to adding phantom sub-executors, so we do it in the base extension. builder.add_phantom_sub_executor( @@ -373,7 +459,6 @@ impl VmExtension for Rv32M { program_bus, memory_bridge, } = builder.system_port(); - let offline_memory = builder.system_base().offline_memory(); let bitwise_lu_chip = if let Some(&chip) = builder .find_chip::>() @@ -403,28 +488,63 @@ impl VmExtension for Rv32M { chip }; - let mul_chip = Rv32MultiplicationChip::new( - Rv32MultAdapterChip::new(execution_bus, program_bus, memory_bridge), - MultiplicationCoreChip::new(range_tuple_checker.clone(), MulOpcode::CLASS_OFFSET), - offline_memory.clone(), + let mul_chip = Rv32MultiplicationChip::::new( + VmAirWrapper::new( + Rv32MultAdapterAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + ), + // TODO(ayush): bus should return value not reference + MultiplicationCoreAir::new(*range_tuple_checker.bus(), MulOpcode::CLASS_OFFSET), + ), + MultiplicationStep::new( + Rv32MultAdapterStep::new(), + range_tuple_checker.clone(), + MulOpcode::CLASS_OFFSET, + ), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor(mul_chip, MulOpcode::iter().map(|x| x.global_opcode()))?; - let mul_h_chip = Rv32MulHChip::new( - Rv32MultAdapterChip::new(execution_bus, program_bus, memory_bridge), - MulHCoreChip::new(bitwise_lu_chip.clone(), range_tuple_checker.clone()), - offline_memory.clone(), + let mul_h_chip = Rv32MulHChip::::new( + VmAirWrapper::new( + Rv32MultAdapterAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + ), + MulHCoreAir::new(bitwise_lu_chip.bus(), *range_tuple_checker.bus()), + ), + MulHStep::new( + Rv32MultAdapterStep::new(), + bitwise_lu_chip.clone(), + range_tuple_checker.clone(), + ), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor(mul_h_chip, MulHOpcode::iter().map(|x| x.global_opcode()))?; - let div_rem_chip = Rv32DivRemChip::new( - Rv32MultAdapterChip::new(execution_bus, program_bus, memory_bridge), - DivRemCoreChip::new( + let div_rem_chip = Rv32DivRemChip::::new( + VmAirWrapper::new( + Rv32MultAdapterAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + ), + DivRemCoreAir::new( + bitwise_lu_chip.bus(), + *range_tuple_checker.bus(), + DivRemOpcode::CLASS_OFFSET, + ), + ), + DivRemStep::new( + Rv32MultAdapterStep::new(), bitwise_lu_chip.clone(), range_tuple_checker.clone(), DivRemOpcode::CLASS_OFFSET, ), - offline_memory.clone(), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor( div_rem_chip, diff --git a/extensions/rv32im/circuit/src/hintstore/mod.rs b/extensions/rv32im/circuit/src/hintstore/mod.rs index ee21d09689..18ca880c99 100644 --- a/extensions/rv32im/circuit/src/hintstore/mod.rs +++ b/extensions/rv32im/circuit/src/hintstore/mod.rs @@ -5,11 +5,13 @@ use std::{ use openvm_circuit::{ arch::{ - ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InstructionExecutor, Streams, + ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InsExecutorE1, + InstructionExecutor, Streams, VmStateMut, }, system::{ memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, + online::GuestMemory, MemoryAddress, MemoryAuxColsFactory, MemoryController, OfflineMemory, RecordId, }, program::ProgramBus, @@ -44,8 +46,8 @@ use serde::{Deserialize, Serialize}; use crate::adapters::{decompose, tmp_convert_to_u8s}; -#[cfg(test)] -mod tests; +// #[cfg(test)] +// mod tests; #[repr(C)] #[derive(AlignedBorrow, Debug)] @@ -524,3 +526,68 @@ where AirProofInput::simple_no_pis(self.generate_trace()) } } + +impl InsExecutorE1 for Rv32HintStoreChip +where + F: PrimeField32, +{ + fn execute_e1( + &mut self, + state: VmStateMut, + instruction: &Instruction, + ) -> Result<(), ExecutionError> + where + Mem: GuestMemory, + F: PrimeField32, + { + let &Instruction { + opcode, + a: num_words_ptr, + b: mem_ptr_ptr, + d, + e, + .. + } = instruction; + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + debug_assert_eq!(e.as_canonical_u32(), RV32_MEMORY_AS); + let local_opcode = + Rv32HintStoreOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); + + // TODO(ayush): fix this + // let (mem_ptr_read, mem_ptr_limbs) = todo!(); + // memory.read::(d, mem_ptr_ptr); + // let (num_words, num_words_read) = if local_opcode == HINT_STOREW { + // (1, None) + // } else { + // let (num_words_read, num_words_limbs) = todo!(); + // // memory.read::(d, num_words_ptr); + // (u32::from_le_bytes(num_words_limbs), Some(num_words_read)) + // }; + // debug_assert_ne!(num_words, 0); + // debug_assert!(num_words <= (1 << self.air.pointer_max_bits)); + + // let mem_ptr = u32::from_le_bytes(mem_ptr_limbs); + + // debug_assert!(mem_ptr <= (1 << self.air.pointer_max_bits)); + + // let mut streams = self.streams.get().unwrap().lock().unwrap(); + // if streams.hint_stream.len() < RV32_REGISTER_NUM_LIMBS * num_words as usize { + // return Err(ExecutionError::HintOutOfBounds { pc: *state.pc }); + // } + + // for word_index in 0..num_words { + // let data: [F; RV32_REGISTER_NUM_LIMBS] = + // std::array::from_fn(|_| streams.hint_stream.pop_front().unwrap()); + // // let (write, _) = memory.write( + // // e, + // // F::from_canonical_u32(mem_ptr + (RV32_REGISTER_NUM_LIMBS as u32 * word_index)), + // // &tmp_convert_to_u8s(data), + // // ); + // // record.hints.push((data, write)); + // } + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) + } +} diff --git a/extensions/rv32im/circuit/src/jal_lui/core.rs b/extensions/rv32im/circuit/src/jal_lui/core.rs index 18c2fb37cf..f26488254e 100644 --- a/extensions/rv32im/circuit/src/jal_lui/core.rs +++ b/extensions/rv32im/circuit/src/jal_lui/core.rs @@ -2,8 +2,8 @@ use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, ImmInstruction, InsExecutorE1, Result, - SingleTraceStep, VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, VmStateMut, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ImmInstruction, Result, + SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -17,7 +17,6 @@ use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{ instruction::Instruction, program::{DEFAULT_PC_STEP, PC_BITS}, - riscv::RV32_REGISTER_AS, LocalOpcode, }; use openvm_rv32im_transpiler::Rv32JalLuiOpcode::{self, *}; @@ -29,12 +28,8 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use crate::adapters::{ - tracing_write_reg, Rv32CondRdWriteAdapterCols, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, - RV_J_TYPE_IMM_BITS, -}; +use crate::adapters::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, RV_J_TYPE_IMM_BITS}; -pub(super) const ADAPTER_WIDTH: usize = size_of::>(); const ADDITIONAL_BITS: u32 = 0b11000000; #[repr(C)] @@ -46,7 +41,7 @@ pub struct Rv32JalLuiCoreCols { pub is_lui: T, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, derive_new::new)] pub struct Rv32JalLuiCoreAir { pub bus: BitwiseOperationLookupBus, } @@ -160,41 +155,61 @@ pub struct Rv32JalLuiCoreRecord { pub is_lui: bool, } -pub struct Rv32JalLuiCoreChip { - pub air: Rv32JalLuiCoreAir, +pub struct Rv32JalLuiStep { + adapter: A, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, } -impl Rv32JalLuiCoreChip { - pub fn new(bitwise_lookup_chip: SharedBitwiseOperationLookupChip) -> Self { +impl Rv32JalLuiStep { + pub fn new( + adapter: A, + bitwise_lookup_chip: SharedBitwiseOperationLookupChip, + ) -> Self { Self { - air: Rv32JalLuiCoreAir { - bus: bitwise_lookup_chip.bus(), - }, + adapter, bitwise_lookup_chip, } } } -impl SingleTraceStep for Rv32JalLuiCoreChip { +impl SingleTraceStep for Rv32JalLuiStep +where + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = (), + WriteData = [u8; RV32_REGISTER_NUM_LIMBS], + TraceContext<'a> = (), + >, +{ + fn get_opcode_name(&self, opcode: usize) -> String { + format!( + "{:?}", + Rv32JalLuiOpcode::from_usize(opcode - Rv32JalLuiOpcode::CLASS_OFFSET) + ) + } + fn execute( &mut self, state: VmStateMut, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { - let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(ADAPTER_WIDTH) }; - let adapter_row: &mut Rv32CondRdWriteAdapterCols = adapter_row.borrow_mut(); + let Instruction { opcode, c: imm, .. } = instruction; + + let local_opcode = + Rv32JalLuiOpcode::from_usize(opcode.local_opcode_idx(Rv32JalLuiOpcode::CLASS_OFFSET)); + + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + A::start(*state.pc, state.memory, adapter_row); + let core_row: &mut Rv32JalLuiCoreCols = core_row.borrow_mut(); - let local_opcode = Rv32JalLuiOpcode::from_usize( - instruction - .opcode - .local_opcode_idx(Rv32JalLuiOpcode::CLASS_OFFSET), - ); - state.ins_start(&mut adapter_row.inner.from_state); // `c` can be "negative" as a field element - let imm_f = instruction.c.as_canonical_u32(); + let imm_f = imm.as_canonical_u32(); let signed_imm = match local_opcode { JAL => { if imm_f < (1 << (RV_J_TYPE_IMM_BITS - 1)) { @@ -209,37 +224,24 @@ impl SingleTraceStep for Rv32JalLuiCoreChip { }; let (to_pc, rd_data) = run_jal_lui(local_opcode, *state.pc, signed_imm); - if instruction.f != F::ZERO { - let rd_ptr = instruction.a.as_canonical_u32(); - let write_buf = &mut adapter_row.inner; - tracing_write_reg( - state.memory, - rd_ptr, - &rd_data, - (&mut write_buf.rd_ptr, &mut write_buf.rd_aux_cols), - ); - adapter_row.needs_write = F::ONE; - } else { - state.memory.increment_timestamp(); - } core_row.rd_data = rd_data.map(F::from_canonical_u8); core_row.imm = instruction.c; core_row.is_jal = F::from_bool(local_opcode == JAL); core_row.is_lui = F::from_bool(local_opcode == LUI); + self.adapter + .write(state.memory, instruction, adapter_row, &rd_data); + *state.pc = to_pc; + Ok(()) } fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { - let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(ADAPTER_WIDTH) }; - let adapter_row: &mut Rv32CondRdWriteAdapterCols = adapter_row.borrow_mut(); + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; let core_row: &mut Rv32JalLuiCoreCols = core_row.borrow_mut(); - if adapter_row.needs_write == F::ONE { - let timestamp = adapter_row.inner.from_state.timestamp.as_canonical_u32(); - mem_helper.fill_from_prev(timestamp, adapter_row.inner.rd_aux_cols.as_mut()); - } + self.adapter.fill_trace_row(mem_helper, (), adapter_row); let rd_data = core_row.rd_data.map(|x| x.as_canonical_u32()); for pair in rd_data.chunks_exact(2) { @@ -250,134 +252,134 @@ impl SingleTraceStep for Rv32JalLuiCoreChip { .request_xor(rd_data[3], ADDITIONAL_BITS); } } - - fn get_opcode_name(&self, opcode: usize) -> String { - format!( - "{:?}", - Rv32JalLuiOpcode::from_usize(opcode - Rv32JalLuiOpcode::CLASS_OFFSET) - ) - } -} - -impl> VmCoreChip for Rv32JalLuiCoreChip -where - I::Writes: From<[[F; RV32_REGISTER_NUM_LIMBS]; 1]>, -{ - type Record = Rv32JalLuiCoreRecord; - type Air = Rv32JalLuiCoreAir; - - #[allow(clippy::type_complexity)] - fn execute_instruction( - &self, - instruction: &Instruction, - from_pc: u32, - _reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { - let local_opcode = Rv32JalLuiOpcode::from_usize( - instruction - .opcode - .local_opcode_idx(Rv32JalLuiOpcode::CLASS_OFFSET), - ); - let imm = instruction.c; - - let signed_imm = match local_opcode { - JAL => { - // Note: signed_imm is a signed integer and imm is a field element - (imm + F::from_canonical_u32(1 << (RV_J_TYPE_IMM_BITS - 1))).as_canonical_u32() - as i32 - - (1 << (RV_J_TYPE_IMM_BITS - 1)) - } - LUI => imm.as_canonical_u32() as i32, - }; - let (to_pc, rd_data) = run_jal_lui(local_opcode, from_pc, signed_imm); - - for i in 0..(RV32_REGISTER_NUM_LIMBS / 2) { - self.bitwise_lookup_chip - .request_range(rd_data[i * 2] as u32, rd_data[i * 2 + 1] as u32); - } - - if local_opcode == JAL { - let last_limb_bits = PC_BITS - RV32_CELL_BITS * (RV32_REGISTER_NUM_LIMBS - 1); - let additional_bits = (last_limb_bits..RV32_CELL_BITS).fold(0, |acc, x| acc + (1 << x)); - self.bitwise_lookup_chip - .request_xor(rd_data[3] as u32, additional_bits); - } - - let rd_data = rd_data.map(F::from_canonical_u8); - - let output = AdapterRuntimeContext { - to_pc: Some(to_pc), - writes: [rd_data].into(), - }; - - Ok(( - output, - Rv32JalLuiCoreRecord { - rd_data, - imm, - is_jal: local_opcode == JAL, - is_lui: local_opcode == LUI, - }, - )) - } - - fn get_opcode_name(&self, opcode: usize) -> String { - format!( - "{:?}", - Rv32JalLuiOpcode::from_usize(opcode - Rv32JalLuiOpcode::CLASS_OFFSET) - ) - } - - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let core_cols: &mut Rv32JalLuiCoreCols = row_slice.borrow_mut(); - core_cols.rd_data = record.rd_data; - core_cols.imm = record.imm; - core_cols.is_jal = F::from_bool(record.is_jal); - core_cols.is_lui = F::from_bool(record.is_lui); - } - - fn air(&self) -> &Self::Air { - &self.air - } } -impl InsExecutorE1 for Rv32JalLuiCoreChip +impl StepExecutorE1 for Rv32JalLuiStep where - Mem: GuestMemory, F: PrimeField32, + A: 'static + + for<'a> AdapterExecutorE1, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, a, c: imm, .. - } = instruction; + ) -> Result<()> + where + Mem: GuestMemory, + { + let Instruction { opcode, c: imm, .. } = instruction; let local_opcode = Rv32JalLuiOpcode::from_usize(opcode.local_opcode_idx(Rv32JalLuiOpcode::CLASS_OFFSET)); - let imm = imm.as_canonical_u32(); + let imm_f = imm.as_canonical_u32(); let signed_imm = match local_opcode { - JAL => (imm + (1 << (RV_J_TYPE_IMM_BITS - 1))) as i32 - (1 << (RV_J_TYPE_IMM_BITS - 1)), - LUI => imm as i32, + JAL => { + if imm_f < (1 << (RV_J_TYPE_IMM_BITS - 1)) { + imm_f as i32 + } else { + let neg_imm_f = F::ORDER_U32 - imm_f; + debug_assert!(neg_imm_f < (1 << (RV_J_TYPE_IMM_BITS - 1))); + -(neg_imm_f as i32) + } + } + LUI => imm_f as i32, }; + let (to_pc, rd) = run_jal_lui(local_opcode, *state.pc, signed_imm); - let (to_pc, rd_bytes) = run_jal_lui(local_opcode, state.pc, signed_imm); - - let rd_addr = a.as_canonical_u32(); - unsafe { - state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes); - } + self.adapter.write(state.memory, instruction, &rd); - state.pc = to_pc; + *state.pc = to_pc; Ok(()) } } +// impl> VmCoreChip for Rv32JalLuiCoreChip +// where +// I::Writes: From<[[F; RV32_REGISTER_NUM_LIMBS]; 1]>, +// { +// type Record = Rv32JalLuiCoreRecord; +// type Air = Rv32JalLuiCoreAir; + +// #[allow(clippy::type_complexity)] +// fn execute_instruction( +// &self, +// instruction: &Instruction, +// from_pc: u32, +// _reads: I::Reads, +// ) -> Result<(AdapterRuntimeContext, Self::Record)> { +// let local_opcode = Rv32JalLuiOpcode::from_usize( +// instruction +// .opcode +// .local_opcode_idx(Rv32JalLuiOpcode::CLASS_OFFSET), +// ); +// let imm = instruction.c; + +// let signed_imm = match local_opcode { +// JAL => { +// // Note: signed_imm is a signed integer and imm is a field element +// (imm + F::from_canonical_u32(1 << (RV_J_TYPE_IMM_BITS - 1))).as_canonical_u32() +// as i32 +// - (1 << (RV_J_TYPE_IMM_BITS - 1)) +// } +// LUI => imm.as_canonical_u32() as i32, +// }; +// let (to_pc, rd_data) = run_jal_lui(local_opcode, from_pc, signed_imm); + +// for i in 0..(RV32_REGISTER_NUM_LIMBS / 2) { +// self.bitwise_lookup_chip +// .request_range(rd_data[i * 2] as u32, rd_data[i * 2 + 1] as u32); +// } + +// if local_opcode == JAL { +// let last_limb_bits = PC_BITS - RV32_CELL_BITS * (RV32_REGISTER_NUM_LIMBS - 1); +// let additional_bits = (last_limb_bits..RV32_CELL_BITS).fold(0, |acc, x| acc + (1 << x)); +// self.bitwise_lookup_chip +// .request_xor(rd_data[3] as u32, additional_bits); +// } + +// let rd_data = rd_data.map(F::from_canonical_u8); + +// let output = AdapterRuntimeContext { +// to_pc: Some(to_pc), +// writes: [rd_data].into(), +// }; + +// Ok(( +// output, +// Rv32JalLuiCoreRecord { +// rd_data, +// imm, +// is_jal: local_opcode == JAL, +// is_lui: local_opcode == LUI, +// }, +// )) +// } + +// fn get_opcode_name(&self, opcode: usize) -> String { +// format!( +// "{:?}", +// Rv32JalLuiOpcode::from_usize(opcode - Rv32JalLuiOpcode::CLASS_OFFSET) +// ) +// } + +// fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { +// let core_cols: &mut Rv32JalLuiCoreCols = row_slice.borrow_mut(); +// core_cols.rd_data = record.rd_data; +// core_cols.imm = record.imm; +// core_cols.is_jal = F::from_bool(record.is_jal); +// core_cols.is_lui = F::from_bool(record.is_lui); +// } + +// fn air(&self) -> &Self::Air { +// &self.air +// } +// } + // returns (to_pc, rd_data) +#[inline(always)] pub(super) fn run_jal_lui( opcode: Rv32JalLuiOpcode, pc: u32, @@ -398,6 +400,7 @@ pub(super) fn run_jal_lui( } } +// TODO(ayush): move from here #[test] fn test_additional_bits() { let last_limb_bits = PC_BITS - RV32_CELL_BITS * (RV32_REGISTER_NUM_LIMBS - 1); diff --git a/extensions/rv32im/circuit/src/jal_lui/mod.rs b/extensions/rv32im/circuit/src/jal_lui/mod.rs index 5479cee2e2..cd10a47fca 100644 --- a/extensions/rv32im/circuit/src/jal_lui/mod.rs +++ b/extensions/rv32im/circuit/src/jal_lui/mod.rs @@ -1,6 +1,6 @@ use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; -use crate::adapters::Rv32CondRdWriteAdapterAir; +use crate::adapters::{Rv32CondRdWriteAdapterAir, Rv32CondRdWriteAdapterStep}; mod core; pub use core::*; @@ -9,4 +9,5 @@ pub use core::*; mod tests; pub type Rv32JalLuiAir = VmAirWrapper; -pub type Rv32JalLuiChip = NewVmChipWrapper; +pub type Rv32JalLuiStepWithAdapter = Rv32JalLuiStep; +pub type Rv32JalLuiChip = NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/jal_lui/tests.rs b/extensions/rv32im/circuit/src/jal_lui/tests.rs index a74b9ae02d..8cf33625fb 100644 --- a/extensions/rv32im/circuit/src/jal_lui/tests.rs +++ b/extensions/rv32im/circuit/src/jal_lui/tests.rs @@ -19,11 +19,12 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; -use super::{run_jal_lui, Rv32JalLuiChip, Rv32JalLuiCoreChip, ADAPTER_WIDTH}; +use super::{run_jal_lui, Rv32JalLuiChip, Rv32JalLuiCoreAir, Rv32JalLuiStep}; use crate::{ adapters::{ - Rv32CondRdWriteAdapterAir, Rv32CondRdWriteAdapterCols, Rv32RdWriteAdapterAir, - RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, RV_IS_TYPE_IMM_BITS, + Rv32CondRdWriteAdapterAir, Rv32CondRdWriteAdapterCols, Rv32CondRdWriteAdapterStep, + Rv32RdWriteAdapterAir, Rv32RdWriteAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + RV_IS_TYPE_IMM_BITS, }, jal_lui::Rv32JalLuiCoreCols, }; @@ -31,6 +32,7 @@ use crate::{ const IMM_BITS: usize = 20; const LIMB_MAX: u32 = (1 << RV32_CELL_BITS) - 1; const MAX_INS_CAPACITY: usize = 256; +const ADAPTER_WIDTH: usize = size_of::>(); type F = BabyBear; @@ -42,16 +44,23 @@ fn create_test_chip( ) { let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let adapter_air = Rv32CondRdWriteAdapterAir::new(Rv32RdWriteAdapterAir::new( - tester.memory_bridge(), - tester.execution_bridge(), - )); - let core = Rv32JalLuiCoreChip::new(bitwise_chip.clone()); - let air = VmAirWrapper::new(adapter_air, core.air); - ( - Rv32JalLuiChip::::new(air, core, MAX_INS_CAPACITY, tester.memory_helper()), - bitwise_chip, - ) + + let chip = Rv32JalLuiChip::::new( + VmAirWrapper::new( + Rv32CondRdWriteAdapterAir::new(Rv32RdWriteAdapterAir::new( + tester.memory_bridge(), + tester.execution_bridge(), + )), + Rv32JalLuiCoreAir::new(bitwise_bus), + ), + Rv32JalLuiStep::new( + Rv32CondRdWriteAdapterStep::new(Rv32RdWriteAdapterStep::new()), + bitwise_chip.clone(), + ), + MAX_INS_CAPACITY, + tester.memory_helper(), + ); + (chip, bitwise_chip) } fn set_and_execute( diff --git a/extensions/rv32im/circuit/src/jalr/core.rs b/extensions/rv32im/circuit/src/jalr/core.rs index 9152bf370a..81f702ed54 100644 --- a/extensions/rv32im/circuit/src/jalr/core.rs +++ b/extensions/rv32im/circuit/src/jalr/core.rs @@ -5,10 +5,13 @@ use std::{ use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, Result, SignedImmInstruction, - VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, Result, SignedImmInstruction, + SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, @@ -18,7 +21,6 @@ use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{ instruction::Instruction, program::{DEFAULT_PC_STEP, PC_BITS}, - riscv::RV32_REGISTER_AS, LocalOpcode, }; use openvm_rv32im_transpiler::Rv32JalrOpcode::{self, *}; @@ -30,7 +32,7 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use crate::adapters::{compose, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; +use crate::adapters::{compose, Rv32JalrAdapterCols, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; const RV32_LIMB_MAX: u32 = (1 << RV32_CELL_BITS) - 1; @@ -61,7 +63,7 @@ pub struct Rv32JalrCoreRecord { pub imm_sign: F, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, derive_new::new)] pub struct Rv32JalrCoreAir { pub bitwise_lookup_bus: BitwiseOperationLookupBus, pub range_bus: VariableRangeCheckerBus, @@ -185,168 +187,191 @@ where } } -pub struct Rv32JalrCoreChip { - pub air: Rv32JalrCoreAir, +pub struct Rv32JalrStep { + adapter: A, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, pub range_checker_chip: SharedVariableRangeCheckerChip, } -impl Rv32JalrCoreChip { +impl Rv32JalrStep { pub fn new( + adapter: A, bitwise_lookup_chip: SharedBitwiseOperationLookupChip, range_checker_chip: SharedVariableRangeCheckerChip, ) -> Self { assert!(range_checker_chip.range_max_bits() >= 16); Self { - air: Rv32JalrCoreAir { - bitwise_lookup_bus: bitwise_lookup_chip.bus(), - range_bus: range_checker_chip.bus(), - }, + adapter, bitwise_lookup_chip, range_checker_chip, } } } -impl> VmCoreChip for Rv32JalrCoreChip +impl SingleTraceStep for Rv32JalrStep where - I::Reads: Into<[[F; RV32_REGISTER_NUM_LIMBS]; 1]>, - I::Writes: From<[[F; RV32_REGISTER_NUM_LIMBS]; 1]>, + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = [u8; RV32_REGISTER_NUM_LIMBS], + WriteData = [u8; RV32_REGISTER_NUM_LIMBS], + TraceContext<'a> = (), + >, { - type Record = Rv32JalrCoreRecord; - type Air = Rv32JalrCoreAir; + fn get_opcode_name(&self, opcode: usize) -> String { + format!( + "{:?}", + Rv32JalrOpcode::from_usize(opcode - Rv32JalrOpcode::CLASS_OFFSET) + ) + } - #[allow(clippy::type_complexity)] - fn execute_instruction( - &self, + fn execute( + &mut self, + state: VmStateMut, instruction: &Instruction, - from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { + row_slice: &mut [F], + ) -> Result<()> { let Instruction { opcode, c, g, .. } = *instruction; + let local_opcode = Rv32JalrOpcode::from_usize(opcode.local_opcode_idx(Rv32JalrOpcode::CLASS_OFFSET)); + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + A::start(*state.pc, state.memory, adapter_row); + + let rs1 = self.adapter.read(state.memory, instruction, adapter_row); + // TODO(ayush): avoid this conversion + let rs1_val = compose(rs1.map(F::from_canonical_u8)); + let imm = c.as_canonical_u32(); let imm_sign = g.as_canonical_u32(); let imm_extended = imm + imm_sign * 0xffff0000; - let rs1 = reads.into()[0]; - let rs1_val = compose(rs1); + // TODO(ayush): this is bad since we're treating adapters as generic. maybe + // add a .state() function to adapters or get_from_pc like in air + let adapter_row_ref: &mut Rv32JalrAdapterCols = adapter_row.borrow_mut(); + let from_pc = adapter_row_ref.from_state.pc.as_canonical_u32(); let (to_pc, rd_data) = run_jalr(local_opcode, from_pc, imm_extended, rs1_val); - self.bitwise_lookup_chip - .request_range(rd_data[0], rd_data[1]); - self.range_checker_chip - .add_count(rd_data[2], RV32_CELL_BITS); - self.range_checker_chip - .add_count(rd_data[3], PC_BITS - RV32_CELL_BITS * 3); - let mask = (1 << 15) - 1; let to_pc_least_sig_bit = rs1_val.wrapping_add(imm_extended) & 1; let to_pc_limbs = array::from_fn(|i| ((to_pc >> (1 + i * 15)) & mask)); - let rd_data = rd_data.map(F::from_canonical_u32); + let core_row: &mut Rv32JalrCoreCols = core_row.borrow_mut(); + core_row.imm = c; + core_row.rd_data = array::from_fn(|i| F::from_canonical_u32(rd_data[i + 1])); + core_row.rs1_data = rs1.map(F::from_canonical_u8); + core_row.to_pc_least_sig_bit = F::from_canonical_u32(to_pc_least_sig_bit); + core_row.to_pc_limbs = to_pc_limbs.map(F::from_canonical_u32); + core_row.imm_sign = g; + core_row.is_valid = F::ONE; - let output = AdapterRuntimeContext { - to_pc: Some(to_pc), - writes: [rd_data].into(), - }; - - Ok(( - output, - Rv32JalrCoreRecord { - imm: c, - rd_data: array::from_fn(|i| rd_data[i + 1]), - rs1_data: rs1, - to_pc_least_sig_bit: F::from_canonical_u32(to_pc_least_sig_bit), - to_pc_limbs, - imm_sign: g, - }, - )) - } + self.adapter.write( + state.memory, + instruction, + adapter_row, + &rd_data.map(|x| x as u8), + ); - fn get_opcode_name(&self, opcode: usize) -> String { - format!( - "{:?}", - Rv32JalrOpcode::from_usize(opcode - Rv32JalrOpcode::CLASS_OFFSET) - ) - } + *state.pc = to_pc; - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - self.range_checker_chip.add_count(record.to_pc_limbs[0], 15); - self.range_checker_chip.add_count(record.to_pc_limbs[1], 14); - - let core_cols: &mut Rv32JalrCoreCols = row_slice.borrow_mut(); - core_cols.imm = record.imm; - core_cols.rd_data = record.rd_data; - core_cols.rs1_data = record.rs1_data; - core_cols.to_pc_least_sig_bit = record.to_pc_least_sig_bit; - core_cols.to_pc_limbs = record.to_pc_limbs.map(F::from_canonical_u32); - core_cols.imm_sign = record.imm_sign; - core_cols.is_valid = F::ONE; + Ok(()) } - fn air(&self) -> &Self::Air { - &self.air + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + let core_row: &mut Rv32JalrCoreCols = core_row.borrow_mut(); + + self.adapter.fill_trace_row(mem_helper, (), adapter_row); + + // TODO(ayush): this shouldn't be here since it is generic on A + let adapter_row: &mut Rv32JalrAdapterCols = adapter_row.borrow_mut(); + + // composed is the composition of 3 most significant limbs of rd + let composed = core_row + .rd_data + .iter() + .enumerate() + .fold(F::ZERO, |acc, (i, &val)| { + acc + val * F::from_canonical_u32(1 << ((i + 1) * RV32_CELL_BITS)) + }); + + let least_sig_limb = + adapter_row.from_state.pc + F::from_canonical_u32(DEFAULT_PC_STEP) - composed; + + let rd_data: [F; RV32_REGISTER_NUM_LIMBS] = array::from_fn(|i| { + if i == 0 { + least_sig_limb + } else { + core_row.rd_data[i - 1] + } + }); + + self.bitwise_lookup_chip + .request_range(rd_data[0].as_canonical_u32(), rd_data[1].as_canonical_u32()); + + self.range_checker_chip + .add_count(rd_data[2].as_canonical_u32(), RV32_CELL_BITS); + self.range_checker_chip + .add_count(rd_data[3].as_canonical_u32(), PC_BITS - RV32_CELL_BITS * 3); + + self.range_checker_chip + .add_count(core_row.to_pc_limbs[0].as_canonical_u32(), 15); + self.range_checker_chip + .add_count(core_row.to_pc_limbs[1].as_canonical_u32(), 14); } } -impl InsExecutorE1 for Rv32JalrCoreChip +impl StepExecutorE1 for Rv32JalrStep where - Mem: GuestMemory, F: PrimeField32, + A: 'static + + for<'a> AdapterExecutorE1< + F, + ReadData = [u8; RV32_REGISTER_NUM_LIMBS], + WriteData = [u8; RV32_REGISTER_NUM_LIMBS], + >, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, - b, - a, - c, - f: enabled, - g, - .. - } = instruction; + ) -> Result<()> + where + Mem: GuestMemory, + { + let Instruction { opcode, c, g, .. } = instruction; let local_opcode = Rv32JalrOpcode::from_usize(opcode.local_opcode_idx(Rv32JalrOpcode::CLASS_OFFSET)); - let rs1_addr = b.as_canonical_u32(); - let rs1_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = - unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; - - // TODO(ayush): directly read as u32 from memory - let rs1_bytes = u32::from_le_bytes(rs1_bytes); + let rs1 = self.adapter.read(state.memory, instruction); + let rs1 = u32::from_le_bytes(rs1); let imm = c.as_canonical_u32(); let imm_sign = g.as_canonical_u32(); let imm_extended = imm + imm_sign * 0xffff0000; // TODO(ayush): should this be [u8; 4]? - let (to_pc, rd_bytes) = run_jalr(local_opcode, state.pc, imm_extended, rs1_bytes); - let rd_bytes = rd_bytes.map(|x| x as u8); - - // TODO(ayush): do i need this enabled check? - if *enabled != F::ZERO { - let rd_addr = a.as_canonical_u32(); - unsafe { - state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes); - } - } + let (to_pc, rd) = run_jalr(local_opcode, *state.pc, imm_extended, rs1); + let rd = rd.map(|x| x as u8); + + self.adapter.write(state.memory, instruction, &rd); - state.pc = to_pc; + *state.pc = to_pc; Ok(()) } } // returns (to_pc, rd_data) +#[inline(always)] pub(super) fn run_jalr( _opcode: Rv32JalrOpcode, pc: u32, diff --git a/extensions/rv32im/circuit/src/jalr/mod.rs b/extensions/rv32im/circuit/src/jalr/mod.rs index 1d85dcbe4a..5bf0bf7543 100644 --- a/extensions/rv32im/circuit/src/jalr/mod.rs +++ b/extensions/rv32im/circuit/src/jalr/mod.rs @@ -1,6 +1,6 @@ -use openvm_circuit::arch::VmChipWrapper; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; -use crate::adapters::Rv32JalrAdapterChip; +use crate::adapters::{Rv32JalrAdapterAir, Rv32JalrAdapterStep}; mod core; pub use core::*; @@ -8,4 +8,6 @@ pub use core::*; #[cfg(test)] mod tests; -pub type Rv32JalrChip = VmChipWrapper, Rv32JalrCoreChip>; +pub type Rv32JalrAir = VmAirWrapper; +pub type Rv32JalrStepWithAdapter = Rv32JalrStep; +pub type Rv32JalrChip = NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/jalr/tests.rs b/extensions/rv32im/circuit/src/jalr/tests.rs index 30eca37dda..30703e250a 100644 --- a/extensions/rv32im/circuit/src/jalr/tests.rs +++ b/extensions/rv32im/circuit/src/jalr/tests.rs @@ -2,7 +2,7 @@ use std::{array, borrow::BorrowMut}; use openvm_circuit::arch::{ testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - VmAdapterChip, + VmAdapterChip, VmAirWrapper, }; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, @@ -21,11 +21,17 @@ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; use crate::{ - adapters::{compose, Rv32JalrAdapterChip, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, - jalr::{run_jalr, Rv32JalrChip, Rv32JalrCoreChip, Rv32JalrCoreCols}, + adapters::{ + compose, Rv32JalrAdapterAir, Rv32JalrAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + }, + jalr::{run_jalr, Rv32JalrChip, Rv32JalrCoreCols, Rv32JalrStep}, }; +use super::Rv32JalrCoreAir; + const IMM_BITS: usize = 16; +const MAX_INS_CAPACITY: usize = 128; + type F = BabyBear; fn into_limbs(num: u32) -> [u32; 4] { @@ -55,6 +61,7 @@ fn set_and_execute( tester.write(1, b, rs1); + let initial_pc = initial_pc.unwrap_or(rng.gen_range(0..(1 << PC_BITS))); tester.execute_with_pc( chip, &Instruction::from_usize( @@ -69,9 +76,8 @@ fn set_and_execute( imm_sign as usize, ], ), - initial_pc.unwrap_or(rng.gen_range(0..(1 << PC_BITS))), + initial_pc, ); - let initial_pc = tester.execution.last_from_pc().as_canonical_u32(); let final_pc = tester.execution.last_to_pc().as_canonical_u32(); let rs1 = compose(rs1); @@ -97,13 +103,19 @@ fn rand_jalr_test() { let mut tester = VmChipTestBuilder::default(); let range_checker_chip = tester.memory_controller().range_checker.clone(); - let adapter = Rv32JalrAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), + let mut chip = Rv32JalrChip::::new( + VmAirWrapper::new( + Rv32JalrAdapterAir::new(tester.memory_bridge(), tester.execution_bridge()), + Rv32JalrCoreAir::new(bitwise_bus, range_checker_chip.bus()), + ), + Rv32JalrStep::new( + Rv32JalrAdapterStep::new(), + bitwise_chip.clone(), + range_checker_chip.clone(), + ), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - let inner = Rv32JalrCoreChip::new(bitwise_chip.clone(), range_checker_chip.clone()); - let mut chip = Rv32JalrChip::::new(adapter, inner, tester.offline_memory_mutex_arc()); let num_tests: usize = 100; for _ in 0..num_tests { @@ -120,8 +132,8 @@ fn rand_jalr_test() { } drop(range_checker_chip); - let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - tester.simple_test().expect("Verification failed"); + // let tester = tester.build().load(chip).load(bitwise_chip).finalize(); + // tester.simple_test().expect("Verification failed"); } ////////////////////////////////////////////////////////////////////////////////////// @@ -132,175 +144,176 @@ fn rand_jalr_test() { // A dummy adaptor is used so memory interactions don't indirectly cause false passes. ////////////////////////////////////////////////////////////////////////////////////// -#[allow(clippy::too_many_arguments)] -fn run_negative_jalr_test( - opcode: Rv32JalrOpcode, - initial_pc: Option, - initial_rs1: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, - initial_imm: Option, - initial_imm_sign: Option, - rd_data: Option<[u32; RV32_REGISTER_NUM_LIMBS - 1]>, - rs1_data: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, - to_pc_least_sig_bit: Option, - to_pc_limbs: Option<[u32; 2]>, - imm_sign: Option, - expected_error: VerificationError, -) { - let mut rng = create_seeded_rng(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().range_checker.clone(); - - let adapter = Rv32JalrAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - ); - let adapter_width = BaseAir::::width(adapter.air()); - let inner = Rv32JalrCoreChip::new(bitwise_chip.clone(), range_checker_chip.clone()); - let mut chip = Rv32JalrChip::::new(adapter, inner, tester.offline_memory_mutex_arc()); - - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - opcode, - initial_imm, - initial_imm_sign, - initial_pc, - initial_rs1, - ); - - let tester = tester.build(); - - let jalr_trace_width = chip.trace_width(); - let air = chip.air(); - let mut chip_input = chip.generate_air_proof_input(); - let jalr_trace = chip_input.raw.common_main.as_mut().unwrap(); - { - let mut trace_row = jalr_trace.row_slice(0).to_vec(); - - let (_, core_row) = trace_row.split_at_mut(adapter_width); - - let core_cols: &mut Rv32JalrCoreCols = core_row.borrow_mut(); - - if let Some(data) = rd_data { - core_cols.rd_data = data.map(F::from_canonical_u32); - } - - if let Some(data) = rs1_data { - core_cols.rs1_data = data.map(F::from_canonical_u32); - } - - if let Some(data) = to_pc_least_sig_bit { - core_cols.to_pc_least_sig_bit = F::from_canonical_u32(data); - } - - if let Some(data) = to_pc_limbs { - core_cols.to_pc_limbs = data.map(F::from_canonical_u32); - } - - if let Some(data) = imm_sign { - core_cols.imm_sign = F::from_canonical_u32(data); - } - - *jalr_trace = RowMajorMatrix::new(trace_row, jalr_trace_width); - } - - drop(range_checker_chip); - disable_debug_builder(); - let tester = tester - .load_air_proof_input((air, chip_input)) - .load(bitwise_chip) - .finalize(); - tester.simple_test_with_expected_error(expected_error); -} - -#[test] -fn invalid_cols_negative_tests() { - run_negative_jalr_test( - JALR, - None, - None, - Some(15362), - Some(0), - None, - None, - None, - None, - Some(1), - VerificationError::OodEvaluationMismatch, - ); - - run_negative_jalr_test( - JALR, - None, - None, - Some(15362), - Some(1), - None, - None, - None, - None, - Some(0), - VerificationError::OodEvaluationMismatch, - ); - - run_negative_jalr_test( - JALR, - None, - Some([23, 154, 67, 28]), - Some(42512), - Some(1), - None, - None, - Some(0), - None, - None, - VerificationError::OodEvaluationMismatch, - ); -} - -#[test] -fn overflow_negative_tests() { - run_negative_jalr_test( - JALR, - Some(251), - None, - None, - None, - Some([1, 0, 0]), - None, - None, - None, - None, - VerificationError::ChallengePhaseError, - ); - - run_negative_jalr_test( - JALR, - None, - Some([0, 0, 0, 0]), - Some((1 << 15) - 2), - Some(0), - None, - None, - None, - Some([ - (F::NEG_ONE * F::from_canonical_u32((1 << 14) + 1)).as_canonical_u32(), - 1, - ]), - None, - VerificationError::ChallengePhaseError, - ); -} +// #[allow(clippy::too_many_arguments)] +// fn run_negative_jalr_test( +// opcode: Rv32JalrOpcode, +// initial_pc: Option, +// initial_rs1: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, +// initial_imm: Option, +// initial_imm_sign: Option, +// rd_data: Option<[u32; RV32_REGISTER_NUM_LIMBS - 1]>, +// rs1_data: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, +// to_pc_least_sig_bit: Option, +// to_pc_limbs: Option<[u32; 2]>, +// imm_sign: Option, +// expected_error: VerificationError, +// ) { +// let mut rng = create_seeded_rng(); +// let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); +// let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); +// let mut tester = VmChipTestBuilder::default(); +// let range_checker_chip = tester.memory_controller().range_checker.clone(); + +// let adapter = Rv32JalrAdapterStep::::new( +// tester.execution_bus(), +// tester.program_bus(), +// tester.memory_bridge(), +// ); +// let adapter_width = BaseAir::::width(adapter.air()); +// let inner = Rv32JalrStep::new(bitwise_chip.clone(), range_checker_chip.clone()); +// let mut chip = Rv32JalrChip::::new(adapter, inner, tester.offline_memory_mutex_arc()); + +// set_and_execute( +// &mut tester, +// &mut chip, +// &mut rng, +// opcode, +// initial_imm, +// initial_imm_sign, +// initial_pc, +// initial_rs1, +// ); + +// let tester = tester.build(); + +// let jalr_trace_width = chip.trace_width(); +// let air = chip.air(); +// let mut chip_input = chip.generate_air_proof_input(); +// let jalr_trace = chip_input.raw.common_main.as_mut().unwrap(); +// { +// let mut trace_row = jalr_trace.row_slice(0).to_vec(); + +// let (_, core_row) = trace_row.split_at_mut(adapter_width); + +// let core_cols: &mut Rv32JalrCoreCols = core_row.borrow_mut(); + +// if let Some(data) = rd_data { +// core_cols.rd_data = data.map(F::from_canonical_u32); +// } + +// if let Some(data) = rs1_data { +// core_cols.rs1_data = data.map(F::from_canonical_u32); +// } + +// if let Some(data) = to_pc_least_sig_bit { +// core_cols.to_pc_least_sig_bit = F::from_canonical_u32(data); +// } + +// if let Some(data) = to_pc_limbs { +// core_cols.to_pc_limbs = data.map(F::from_canonical_u32); +// } + +// if let Some(data) = imm_sign { +// core_cols.imm_sign = F::from_canonical_u32(data); +// } + +// *jalr_trace = RowMajorMatrix::new(trace_row, jalr_trace_width); +// } + +// drop(range_checker_chip); +// disable_debug_builder(); +// let tester = tester +// .load_air_proof_input((air, chip_input)) +// .load(bitwise_chip) +// .finalize(); +// tester.simple_test_with_expected_error(expected_error); +// } + +// #[test] +// fn invalid_cols_negative_tests() { +// run_negative_jalr_test( +// JALR, +// None, +// None, +// Some(15362), +// Some(0), +// None, +// None, +// None, +// None, +// Some(1), +// VerificationError::OodEvaluationMismatch, +// ); + +// run_negative_jalr_test( +// JALR, +// None, +// None, +// Some(15362), +// Some(1), +// None, +// None, +// None, +// None, +// Some(0), +// VerificationError::OodEvaluationMismatch, +// ); + +// run_negative_jalr_test( +// JALR, +// None, +// Some([23, 154, 67, 28]), +// Some(42512), +// Some(1), +// None, +// None, +// Some(0), +// None, +// None, +// VerificationError::OodEvaluationMismatch, +// ); +// } + +// #[test] +// fn overflow_negative_tests() { +// run_negative_jalr_test( +// JALR, +// Some(251), +// None, +// None, +// None, +// Some([1, 0, 0]), +// None, +// None, +// None, +// None, +// VerificationError::ChallengePhaseError, +// ); + +// run_negative_jalr_test( +// JALR, +// None, +// Some([0, 0, 0, 0]), +// Some((1 << 15) - 2), +// Some(0), +// None, +// None, +// None, +// Some([ +// (F::NEG_ONE * F::from_canonical_u32((1 << 14) + 1)).as_canonical_u32(), +// 1, +// ]), +// None, +// VerificationError::ChallengePhaseError, +// ); +// } /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS /// /// Ensure that solve functions produce the correct results. /////////////////////////////////////////////////////////////////////////////////////// + #[test] fn execute_roundtrip_sanity_test() { let mut rng = create_seeded_rng(); @@ -309,13 +322,19 @@ fn execute_roundtrip_sanity_test() { let mut tester = VmChipTestBuilder::default(); let range_checker_chip = tester.memory_controller().range_checker.clone(); - let adapter = Rv32JalrAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), + let mut chip = Rv32JalrChip::::new( + VmAirWrapper::new( + Rv32JalrAdapterAir::new(tester.memory_bridge(), tester.execution_bridge()), + Rv32JalrCoreAir::new(bitwise_bus, range_checker_chip.bus()), + ), + Rv32JalrStep::new( + Rv32JalrAdapterStep::new(), + bitwise_chip.clone(), + range_checker_chip.clone(), + ), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - let inner = Rv32JalrCoreChip::new(bitwise_chip, range_checker_chip); - let mut chip = Rv32JalrChip::::new(adapter, inner, tester.offline_memory_mutex_arc()); let num_tests: usize = 10; for _ in 0..num_tests { diff --git a/extensions/rv32im/circuit/src/less_than/core.rs b/extensions/rv32im/circuit/src/less_than/core.rs index 0efb491bad..f11e4f76f2 100644 --- a/extensions/rv32im/circuit/src/less_than/core.rs +++ b/extensions/rv32im/circuit/src/less_than/core.rs @@ -1,13 +1,12 @@ use std::{ array, borrow::{Borrow, BorrowMut}, - marker::PhantomData, }; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterTraceStep, InsExecutorE1, MinimalInstruction, Result, - SingleTraceStep, VmAdapterInterface, VmCoreAir, VmExecutionState, VmStateMut, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, + SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -21,12 +20,7 @@ use openvm_circuit_primitives::{ utils::not, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{ - instruction::Instruction, - program::DEFAULT_PC_STEP, - riscv::{RV32_IMM_AS, RV32_REGISTER_AS}, - LocalOpcode, -}; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; use openvm_rv32im_transpiler::LessThanOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -59,7 +53,7 @@ pub struct LessThanCoreCols { pub diff_val: T, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, derive_new::new)] pub struct LessThanCoreAir { pub bus: BitwiseOperationLookupBus, offset: usize, @@ -193,51 +187,88 @@ pub struct LessThanCoreRecord pub opcode: LessThanOpcode, } -pub struct LessThanCoreChip { - pub air: LessThanCoreAir, +pub struct LessThanStep { + adapter: A, + offset: usize, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, } -impl LessThanCoreChip { +impl LessThanStep { pub fn new( + adapter: A, bitwise_lookup_chip: SharedBitwiseOperationLookupChip, offset: usize, ) -> Self { Self { - air: LessThanCoreAir { - bus: bitwise_lookup_chip.bus(), - offset, - }, + adapter, + offset, bitwise_lookup_chip, } } +} - #[inline] - fn execute( - &self, +impl SingleTraceStep + for LessThanStep +where + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), + WriteData = [u8; NUM_LIMBS], + TraceContext<'a> = &'a BitwiseOperationLookupChip, + >, +{ + fn get_opcode_name(&self, opcode: usize) -> String { + format!("{:?}", LessThanOpcode::from_usize(opcode - self.offset)) + } + + fn execute( + &mut self, + state: VmStateMut, instruction: &Instruction, - [b, c]: [[u8; NUM_LIMBS]; 2], - core_row: &mut [F], - ) -> [u8; NUM_LIMBS] { + row_slice: &mut [F], + ) -> Result<()> { debug_assert!(LIMB_BITS <= 8); - let opcode = instruction.opcode; - let local_opcode = LessThanOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - let (cmp_result, _, _, _) = run_less_than::(local_opcode, &b, &c); + let Instruction { opcode, .. } = instruction; + + let local_opcode = LessThanOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + A::start(*state.pc, state.memory, adapter_row); + + let (rs1, rs2) = self.adapter.read(state.memory, instruction, adapter_row); + + let (cmp_result, _, _, _) = run_less_than::(local_opcode, &rs1, &rs2); let core_row: &mut LessThanCoreCols<_, NUM_LIMBS, LIMB_BITS> = core_row.borrow_mut(); - core_row.b = b.map(F::from_canonical_u8); - core_row.c = c.map(F::from_canonical_u8); + core_row.b = rs1.map(F::from_canonical_u8); + core_row.c = rs2.map(F::from_canonical_u8); core_row.opcode_slt_flag = F::from_bool(local_opcode == LessThanOpcode::SLT); core_row.opcode_sltu_flag = F::from_bool(local_opcode == LessThanOpcode::SLTU); let mut output = [0u8; NUM_LIMBS]; output[0] = cmp_result as u8; - output + + self.adapter + .write(state.memory, instruction, adapter_row, &output); + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) } - pub fn fill_trace_row(&self, core_row: &mut [F]) { + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + self.adapter + .fill_trace_row(mem_helper, self.bitwise_lookup_chip.as_ref(), adapter_row); + let core_row: &mut LessThanCoreCols<_, NUM_LIMBS, LIMB_BITS> = core_row.borrow_mut(); + let b = core_row.b.map(|x| x.as_canonical_u32() as u8); let c = core_row.c.map(|x| x.as_canonical_u32() as u8); // It's easier (and faster?) to re-execute @@ -306,110 +337,40 @@ impl LessThanCoreChip { - pub core: LessThanCoreChip, - phantom: PhantomData, -} - -impl SingleTraceStep +impl StepExecutorE1 for LessThanStep where F: PrimeField32, A: 'static - + for<'a> AdapterTraceStep< + + for<'a> AdapterExecutorE1< F, - CTX, - ReadData = [[u8; NUM_LIMBS]; 2], + ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), WriteData = [u8; NUM_LIMBS], - TraceContext<'a> = &'a BitwiseOperationLookupChip, >, { - fn execute( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - row_slice: &mut [F], - ) -> Result<()> { - let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + ) -> Result<()> + where + Mem: GuestMemory, + { + let Instruction { opcode, .. } = instruction; - A::start(*state.pc, state.memory, adapter_row); - let [rs1, rs2] = A::read(state.memory, instruction, adapter_row); - let output = self.core.execute(instruction, [rs1, rs2], core_row); - A::write(state.memory, instruction, adapter_row, &output); + let less_than_opcode = LessThanOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - *state.pc += DEFAULT_PC_STEP; - Ok(()) - } - - fn get_opcode_name(&self, opcode: usize) -> String { - format!( - "{:?}", - LessThanOpcode::from_usize(opcode - self.core.air.offset) - ) - } - - fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { - let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - A::fill_trace_row( - mem_helper, - self.core.bitwise_lookup_chip.as_ref(), - adapter_row, - ); - self.core.fill_trace_row(core_row); - } -} - -impl InsExecutorE1 - for LessThanCoreChip -where - Mem: GuestMemory, - F: PrimeField32, -{ - fn execute_e1( - &mut self, - state: &mut VmExecutionState, - instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, a, b, c, e, .. - } = instruction; - - let less_than_opcode = LessThanOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - - let rs1_addr = b.as_canonical_u32(); - let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; - - let rs2_bytes = if e.as_canonical_u32() == RV32_IMM_AS { - // Use immediate value - let imm = c.as_canonical_u32(); - // Convert imm from u32 to [u8; NUM_LIMBS] - let imm_bytes = imm.to_le_bytes(); - // TODO(ayush): remove this - let mut rs2_bytes = [0u8; NUM_LIMBS]; - rs2_bytes[..NUM_LIMBS].copy_from_slice(&imm_bytes[..NUM_LIMBS]); - rs2_bytes - } else { - // Read from register - let rs2_addr = c.as_canonical_u32(); - let rs2_bytes: [u8; NUM_LIMBS] = - unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; - rs2_bytes - }; + let (rs1, rs2) = self.adapter.read(state.memory, instruction); // Run the comparison let (cmp_result, _, _, _) = - run_less_than::(less_than_opcode, &rs1_bytes, &rs2_bytes); - // TODO(ayush): can i write just [u8; 1]? - let rd_bytes = (cmp_result as u32).to_le_bytes(); - - // Write the result back to the destination register - let rd_addr = a.as_canonical_u32(); - unsafe { - state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes); - } + run_less_than::(less_than_opcode, &rs1, &rs2); + let mut rd = [0u8; NUM_LIMBS]; + rd[0] = cmp_result as u8; + + self.adapter.write(state.memory, instruction, &rd); - state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } diff --git a/extensions/rv32im/circuit/src/less_than/tests.rs b/extensions/rv32im/circuit/src/less_than/tests.rs index 892aa22b66..fe3f13aca6 100644 --- a/extensions/rv32im/circuit/src/less_than/tests.rs +++ b/extensions/rv32im/circuit/src/less_than/tests.rs @@ -26,9 +26,13 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::Rng; -use super::{core::run_less_than, LessThanCoreChip, Rv32LessThanChip, Rv32LessThanStep}; +use super::{ + core::run_less_than, LessThanCoreAir, LessThanStep, Rv32LessThanChip, Rv32LessThanStep, +}; use crate::{ - adapters::{Rv32BaseAluAdapterAir, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, + adapters::{ + Rv32BaseAluAdapterAir, Rv32BaseAluAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + }, less_than::LessThanCoreCols, test_utils::{generate_rv32_is_type_immediate, rv32_rand_write_register_or_imm}, }; @@ -44,19 +48,25 @@ fn create_test_chip( ) { let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let step = Rv32LessThanStep::new(LessThanCoreChip::new( - bitwise_chip.clone(), - LessThanOpcode::CLASS_OFFSET, - )); - let air = VmAirWrapper::new( - Rv32BaseAluAdapterAir::new( - tester.execution_bridge(), - tester.memory_bridge(), - bitwise_bus, + + let chip = Rv32LessThanChip::::new( + VmAirWrapper::new( + Rv32BaseAluAdapterAir::new( + tester.execution_bridge(), + tester.memory_bridge(), + bitwise_bus, + ), + LessThanCoreAir::new(bitwise_bus, LessThanOpcode::CLASS_OFFSET), ), - step.core.air, + LessThanStep::new( + Rv32BaseAluAdapterStep::new(), + bitwise_chip.clone(), + LessThanOpcode::CLASS_OFFSET, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - let chip = NewVmChipWrapper::new(air, step, MAX_INS_CAPACITY, tester.memory_helper()); + (chip, bitwise_chip) } ////////////////////////////////////////////////////////////////////////////////////// @@ -147,283 +157,283 @@ fn rv32_sltu_rand_test() { // A dummy adapter is used so memory interactions don't indirectly cause false passes. ////////////////////////////////////////////////////////////////////////////////////// -type Rv32LessThanTestChip = - VmChipWrapper, LessThanCoreChip>; - -#[derive(Clone, Copy, Default, PartialEq)] -struct LessThanPrankValues { - pub b_msb: Option, - pub c_msb: Option, - pub diff_marker: Option<[u32; NUM_LIMBS]>, - pub diff_val: Option, -} - -#[allow(clippy::too_many_arguments)] -fn run_rv32_lt_negative_test( - opcode: LessThanOpcode, - b: [u8; RV32_REGISTER_NUM_LIMBS], - c: [u8; RV32_REGISTER_NUM_LIMBS], - cmp_result: bool, - prank_vals: LessThanPrankValues, - interaction_error: bool, -) { - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let (mut chip, bitwise_chip) = create_test_chip(&tester); - tester.execute( - &mut chip, - &Instruction::from_usize(opcode.global_opcode(), [0, 0, 0, 1, 1]), - ); - - let trace_width = chip.trace_width(); - let adapter_width = BaseAir::::width(&chip.air.adapter); - let (_, _, b_sign, c_sign) = - run_less_than::(opcode, &b, &c); - - if prank_vals != LessThanPrankValues::default() { - debug_assert!(prank_vals.diff_val.is_some()); - let b_msb = prank_vals.b_msb.unwrap_or( - b[RV32_REGISTER_NUM_LIMBS - 1] as i32 - if b_sign { 1 << RV32_CELL_BITS } else { 0 }, - ); - let c_msb = prank_vals.c_msb.unwrap_or( - c[RV32_REGISTER_NUM_LIMBS - 1] as i32 - if c_sign { 1 << RV32_CELL_BITS } else { 0 }, - ); - let sign_offset = if opcode == LessThanOpcode::SLT { - 1 << (RV32_CELL_BITS - 1) - } else { - 0 - }; - - bitwise_chip.clear(); - bitwise_chip.request_range( - (b_msb + sign_offset) as u8 as u32, - (c_msb + sign_offset) as u8 as u32, - ); - - let diff_val = prank_vals - .diff_val - .unwrap() - .clamp(0, (1 << RV32_CELL_BITS) - 1); - if diff_val > 0 { - bitwise_chip.request_range(diff_val - 1, 0); - } - }; - - let modify_trace = |trace: &mut DenseMatrix| { - let mut values = trace.row_slice(0).to_vec(); - let cols: &mut LessThanCoreCols = - values.split_at_mut(adapter_width).1.borrow_mut(); - - if let Some(b_msb) = prank_vals.b_msb { - cols.b_msb_f = i32_to_f(b_msb); - } - if let Some(c_msb) = prank_vals.c_msb { - cols.c_msb_f = i32_to_f(c_msb); - } - if let Some(diff_marker) = prank_vals.diff_marker { - cols.diff_marker = diff_marker.map(F::from_canonical_u32); - } - if let Some(diff_val) = prank_vals.diff_val { - cols.diff_val = F::from_canonical_u32(diff_val); - } - cols.cmp_result = F::from_bool(cmp_result); - - *trace = RowMajorMatrix::new(values, trace_width); - }; - - disable_debug_builder(); - let tester = tester - .build() - .load_and_prank_trace(chip, modify_trace) - .load(bitwise_chip) - .finalize(); - tester.simple_test_with_expected_error(if interaction_error { - VerificationError::ChallengePhaseError - } else { - VerificationError::OodEvaluationMismatch - }); -} - -#[test] -fn rv32_lt_wrong_false_cmp_negative_test() { - let b = [145, 34, 25, 205]; - let c = [73, 35, 25, 205]; - let prank_vals = Default::default(); - run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, false); - run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, false); -} - -#[test] -fn rv32_lt_wrong_true_cmp_negative_test() { - let b = [73, 35, 25, 205]; - let c = [145, 34, 25, 205]; - let prank_vals = Default::default(); - run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, true, prank_vals, false); - run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, true, prank_vals, false); -} - -#[test] -fn rv32_lt_wrong_eq_negative_test() { - let b = [73, 35, 25, 205]; - let c = [73, 35, 25, 205]; - let prank_vals = Default::default(); - run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, true, prank_vals, false); - run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, true, prank_vals, false); -} - -#[test] -fn rv32_lt_fake_diff_val_negative_test() { - let b = [145, 34, 25, 205]; - let c = [73, 35, 25, 205]; - let prank_vals = LessThanPrankValues { - diff_val: Some(F::NEG_ONE.as_canonical_u32()), - ..Default::default() - }; - run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, true); - run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, true); -} - -#[test] -fn rv32_lt_zero_diff_val_negative_test() { - let b = [145, 34, 25, 205]; - let c = [73, 35, 25, 205]; - let prank_vals = LessThanPrankValues { - diff_marker: Some([0, 0, 1, 0]), - diff_val: Some(0), - ..Default::default() - }; - run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, true); - run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, true); -} - -#[test] -fn rv32_lt_fake_diff_marker_negative_test() { - let b = [145, 34, 25, 205]; - let c = [73, 35, 25, 205]; - let prank_vals = LessThanPrankValues { - diff_marker: Some([1, 0, 0, 0]), - diff_val: Some(72), - ..Default::default() - }; - run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, false); - run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, false); -} - -#[test] -fn rv32_lt_zero_diff_marker_negative_test() { - let b = [145, 34, 25, 205]; - let c = [73, 35, 25, 205]; - let prank_vals = LessThanPrankValues { - diff_marker: Some([0, 0, 0, 0]), - diff_val: Some(0), - ..Default::default() - }; - run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, false); - run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, false); -} - -#[test] -fn rv32_slt_wrong_b_msb_negative_test() { - let b = [145, 34, 25, 205]; - let c = [73, 35, 25, 205]; - let prank_vals = LessThanPrankValues { - b_msb: Some(206), - diff_marker: Some([0, 0, 0, 1]), - diff_val: Some(1), - ..Default::default() - }; - run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, false); -} - -#[test] -fn rv32_slt_wrong_b_msb_sign_negative_test() { - let b = [145, 34, 25, 205]; - let c = [73, 35, 25, 205]; - let prank_vals = LessThanPrankValues { - b_msb: Some(205), - diff_marker: Some([0, 0, 0, 1]), - diff_val: Some(256), - ..Default::default() - }; - run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, true); -} - -#[test] -fn rv32_slt_wrong_c_msb_negative_test() { - let b = [145, 36, 25, 205]; - let c = [73, 35, 25, 205]; - let prank_vals = LessThanPrankValues { - c_msb: Some(204), - diff_marker: Some([0, 0, 0, 1]), - diff_val: Some(1), - ..Default::default() - }; - run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, true, prank_vals, false); -} - -#[test] -fn rv32_slt_wrong_c_msb_sign_negative_test() { - let b = [145, 36, 25, 205]; - let c = [73, 35, 25, 205]; - let prank_vals = LessThanPrankValues { - c_msb: Some(205), - diff_marker: Some([0, 0, 0, 1]), - diff_val: Some(256), - ..Default::default() - }; - run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, true, prank_vals, true); -} - -#[test] -fn rv32_sltu_wrong_b_msb_negative_test() { - let b = [145, 36, 25, 205]; - let c = [73, 35, 25, 205]; - let prank_vals = LessThanPrankValues { - b_msb: Some(204), - diff_marker: Some([0, 0, 0, 1]), - diff_val: Some(1), - ..Default::default() - }; - run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, true, prank_vals, false); -} - -#[test] -fn rv32_sltu_wrong_b_msb_sign_negative_test() { - let b = [145, 36, 25, 205]; - let c = [73, 35, 25, 205]; - let prank_vals = LessThanPrankValues { - b_msb: Some(-51), - diff_marker: Some([0, 0, 0, 1]), - diff_val: Some(256), - ..Default::default() - }; - run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, true, prank_vals, true); -} - -#[test] -fn rv32_sltu_wrong_c_msb_negative_test() { - let b = [145, 34, 25, 205]; - let c = [73, 35, 25, 205]; - let prank_vals = LessThanPrankValues { - c_msb: Some(204), - diff_marker: Some([0, 0, 0, 1]), - diff_val: Some(1), - ..Default::default() - }; - run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, false); -} - -#[test] -fn rv32_sltu_wrong_c_msb_sign_negative_test() { - let b = [145, 34, 25, 205]; - let c = [73, 35, 25, 205]; - let prank_vals = LessThanPrankValues { - c_msb: Some(-51), - diff_marker: Some([0, 0, 0, 1]), - diff_val: Some(256), - ..Default::default() - }; - run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, true); -} +// type Rv32LessThanTestChip = +// VmChipWrapper, LessThanStep>; + +// #[derive(Clone, Copy, Default, PartialEq)] +// struct LessThanPrankValues { +// pub b_msb: Option, +// pub c_msb: Option, +// pub diff_marker: Option<[u32; NUM_LIMBS]>, +// pub diff_val: Option, +// } + +// #[allow(clippy::too_many_arguments)] +// fn run_rv32_lt_negative_test( +// opcode: LessThanOpcode, +// b: [u8; RV32_REGISTER_NUM_LIMBS], +// c: [u8; RV32_REGISTER_NUM_LIMBS], +// cmp_result: bool, +// prank_vals: LessThanPrankValues, +// interaction_error: bool, +// ) { +// let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); +// let (mut chip, bitwise_chip) = create_test_chip(&tester); +// tester.execute( +// &mut chip, +// &Instruction::from_usize(opcode.global_opcode(), [0, 0, 0, 1, 1]), +// ); + +// let trace_width = chip.trace_width(); +// let adapter_width = BaseAir::::width(&chip.air.adapter); +// let (_, _, b_sign, c_sign) = +// run_less_than::(opcode, &b, &c); + +// if prank_vals != LessThanPrankValues::default() { +// debug_assert!(prank_vals.diff_val.is_some()); +// let b_msb = prank_vals.b_msb.unwrap_or( +// b[RV32_REGISTER_NUM_LIMBS - 1] as i32 - if b_sign { 1 << RV32_CELL_BITS } else { 0 }, +// ); +// let c_msb = prank_vals.c_msb.unwrap_or( +// c[RV32_REGISTER_NUM_LIMBS - 1] as i32 - if c_sign { 1 << RV32_CELL_BITS } else { 0 }, +// ); +// let sign_offset = if opcode == LessThanOpcode::SLT { +// 1 << (RV32_CELL_BITS - 1) +// } else { +// 0 +// }; + +// bitwise_chip.clear(); +// bitwise_chip.request_range( +// (b_msb + sign_offset) as u8 as u32, +// (c_msb + sign_offset) as u8 as u32, +// ); + +// let diff_val = prank_vals +// .diff_val +// .unwrap() +// .clamp(0, (1 << RV32_CELL_BITS) - 1); +// if diff_val > 0 { +// bitwise_chip.request_range(diff_val - 1, 0); +// } +// }; + +// let modify_trace = |trace: &mut DenseMatrix| { +// let mut values = trace.row_slice(0).to_vec(); +// let cols: &mut LessThanCoreCols = +// values.split_at_mut(adapter_width).1.borrow_mut(); + +// if let Some(b_msb) = prank_vals.b_msb { +// cols.b_msb_f = i32_to_f(b_msb); +// } +// if let Some(c_msb) = prank_vals.c_msb { +// cols.c_msb_f = i32_to_f(c_msb); +// } +// if let Some(diff_marker) = prank_vals.diff_marker { +// cols.diff_marker = diff_marker.map(F::from_canonical_u32); +// } +// if let Some(diff_val) = prank_vals.diff_val { +// cols.diff_val = F::from_canonical_u32(diff_val); +// } +// cols.cmp_result = F::from_bool(cmp_result); + +// *trace = RowMajorMatrix::new(values, trace_width); +// }; + +// disable_debug_builder(); +// let tester = tester +// .build() +// .load_and_prank_trace(chip, modify_trace) +// .load(bitwise_chip) +// .finalize(); +// tester.simple_test_with_expected_error(if interaction_error { +// VerificationError::ChallengePhaseError +// } else { +// VerificationError::OodEvaluationMismatch +// }); +// } + +// #[test] +// fn rv32_lt_wrong_false_cmp_negative_test() { +// let b = [145, 34, 25, 205]; +// let c = [73, 35, 25, 205]; +// let prank_vals = Default::default(); +// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, false); +// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, false); +// } + +// #[test] +// fn rv32_lt_wrong_true_cmp_negative_test() { +// let b = [73, 35, 25, 205]; +// let c = [145, 34, 25, 205]; +// let prank_vals = Default::default(); +// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, true, prank_vals, false); +// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, true, prank_vals, false); +// } + +// #[test] +// fn rv32_lt_wrong_eq_negative_test() { +// let b = [73, 35, 25, 205]; +// let c = [73, 35, 25, 205]; +// let prank_vals = Default::default(); +// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, true, prank_vals, false); +// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, true, prank_vals, false); +// } + +// #[test] +// fn rv32_lt_fake_diff_val_negative_test() { +// let b = [145, 34, 25, 205]; +// let c = [73, 35, 25, 205]; +// let prank_vals = LessThanPrankValues { +// diff_val: Some(F::NEG_ONE.as_canonical_u32()), +// ..Default::default() +// }; +// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, true); +// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, true); +// } + +// #[test] +// fn rv32_lt_zero_diff_val_negative_test() { +// let b = [145, 34, 25, 205]; +// let c = [73, 35, 25, 205]; +// let prank_vals = LessThanPrankValues { +// diff_marker: Some([0, 0, 1, 0]), +// diff_val: Some(0), +// ..Default::default() +// }; +// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, true); +// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, true); +// } + +// #[test] +// fn rv32_lt_fake_diff_marker_negative_test() { +// let b = [145, 34, 25, 205]; +// let c = [73, 35, 25, 205]; +// let prank_vals = LessThanPrankValues { +// diff_marker: Some([1, 0, 0, 0]), +// diff_val: Some(72), +// ..Default::default() +// }; +// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, false); +// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, false); +// } + +// #[test] +// fn rv32_lt_zero_diff_marker_negative_test() { +// let b = [145, 34, 25, 205]; +// let c = [73, 35, 25, 205]; +// let prank_vals = LessThanPrankValues { +// diff_marker: Some([0, 0, 0, 0]), +// diff_val: Some(0), +// ..Default::default() +// }; +// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, false); +// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, false); +// } + +// #[test] +// fn rv32_slt_wrong_b_msb_negative_test() { +// let b = [145, 34, 25, 205]; +// let c = [73, 35, 25, 205]; +// let prank_vals = LessThanPrankValues { +// b_msb: Some(206), +// diff_marker: Some([0, 0, 0, 1]), +// diff_val: Some(1), +// ..Default::default() +// }; +// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, false); +// } + +// #[test] +// fn rv32_slt_wrong_b_msb_sign_negative_test() { +// let b = [145, 34, 25, 205]; +// let c = [73, 35, 25, 205]; +// let prank_vals = LessThanPrankValues { +// b_msb: Some(205), +// diff_marker: Some([0, 0, 0, 1]), +// diff_val: Some(256), +// ..Default::default() +// }; +// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, true); +// } + +// #[test] +// fn rv32_slt_wrong_c_msb_negative_test() { +// let b = [145, 36, 25, 205]; +// let c = [73, 35, 25, 205]; +// let prank_vals = LessThanPrankValues { +// c_msb: Some(204), +// diff_marker: Some([0, 0, 0, 1]), +// diff_val: Some(1), +// ..Default::default() +// }; +// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, true, prank_vals, false); +// } + +// #[test] +// fn rv32_slt_wrong_c_msb_sign_negative_test() { +// let b = [145, 36, 25, 205]; +// let c = [73, 35, 25, 205]; +// let prank_vals = LessThanPrankValues { +// c_msb: Some(205), +// diff_marker: Some([0, 0, 0, 1]), +// diff_val: Some(256), +// ..Default::default() +// }; +// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, true, prank_vals, true); +// } + +// #[test] +// fn rv32_sltu_wrong_b_msb_negative_test() { +// let b = [145, 36, 25, 205]; +// let c = [73, 35, 25, 205]; +// let prank_vals = LessThanPrankValues { +// b_msb: Some(204), +// diff_marker: Some([0, 0, 0, 1]), +// diff_val: Some(1), +// ..Default::default() +// }; +// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, true, prank_vals, false); +// } + +// #[test] +// fn rv32_sltu_wrong_b_msb_sign_negative_test() { +// let b = [145, 36, 25, 205]; +// let c = [73, 35, 25, 205]; +// let prank_vals = LessThanPrankValues { +// b_msb: Some(-51), +// diff_marker: Some([0, 0, 0, 1]), +// diff_val: Some(256), +// ..Default::default() +// }; +// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, true, prank_vals, true); +// } + +// #[test] +// fn rv32_sltu_wrong_c_msb_negative_test() { +// let b = [145, 34, 25, 205]; +// let c = [73, 35, 25, 205]; +// let prank_vals = LessThanPrankValues { +// c_msb: Some(204), +// diff_marker: Some([0, 0, 0, 1]), +// diff_val: Some(1), +// ..Default::default() +// }; +// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, false); +// } + +// #[test] +// fn rv32_sltu_wrong_c_msb_sign_negative_test() { +// let b = [145, 34, 25, 205]; +// let c = [73, 35, 25, 205]; +// let prank_vals = LessThanPrankValues { +// c_msb: Some(-51), +// diff_marker: Some([0, 0, 0, 1]), +// diff_val: Some(256), +// ..Default::default() +// }; +// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, true); +// } /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS diff --git a/extensions/rv32im/circuit/src/load_sign_extend/core.rs b/extensions/rv32im/circuit/src/load_sign_extend/core.rs index 26b990583b..d2a9a23ef6 100644 --- a/extensions/rv32im/circuit/src/load_sign_extend/core.rs +++ b/extensions/rv32im/circuit/src/load_sign_extend/core.rs @@ -5,22 +5,20 @@ use std::{ use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, Result, VmAdapterInterface, - VmCoreAir, VmCoreChip, VmExecutionState, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, Result, SingleTraceStep, + StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ utils::select, var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus}, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{ - instruction::Instruction, - program::DEFAULT_PC_STEP, - riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS, RV32_REGISTER_NUM_LIMBS}, - LocalOpcode, -}; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; use openvm_rv32im_transpiler::Rv32LoadStoreOpcode::{self, *}; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -68,7 +66,7 @@ pub struct LoadSignExtendCoreRecord { pub most_sig_bit: bool, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, derive_new::new)] pub struct LoadSignExtendCoreAir { pub range_bus: VariableRangeCheckerBus, } @@ -187,53 +185,70 @@ where } } -pub struct LoadSignExtendCoreChip { - pub air: LoadSignExtendCoreAir, +pub struct LoadSignExtendStep { + adapter: A, pub range_checker_chip: SharedVariableRangeCheckerChip, } -impl LoadSignExtendCoreChip { - pub fn new(range_checker_chip: SharedVariableRangeCheckerChip) -> Self { +impl + LoadSignExtendStep +{ + pub fn new(adapter: A, range_checker_chip: SharedVariableRangeCheckerChip) -> Self { Self { - air: LoadSignExtendCoreAir:: { - range_bus: range_checker_chip.bus(), - }, + adapter, range_checker_chip, } } } -impl, const NUM_CELLS: usize, const LIMB_BITS: usize> - VmCoreChip for LoadSignExtendCoreChip +impl SingleTraceStep + for LoadSignExtendStep where - I::Reads: Into<([[F; NUM_CELLS]; 2], F)>, - I::Writes: From<[[F; NUM_CELLS]; 1]>, + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = (([u8; NUM_CELLS], [u8; NUM_CELLS]), u32), + WriteData = [u8; NUM_CELLS], + TraceContext<'a> = &'a SharedVariableRangeCheckerChip, + >, { - type Record = LoadSignExtendCoreRecord; - type Air = LoadSignExtendCoreAir; + fn get_opcode_name(&self, opcode: usize) -> String { + format!( + "{:?}", + Rv32LoadStoreOpcode::from_usize(opcode - Rv32LoadStoreOpcode::CLASS_OFFSET) + ) + } - #[allow(clippy::type_complexity)] - fn execute_instruction( - &self, + fn execute( + &mut self, + state: VmStateMut, instruction: &Instruction, - _from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { + row_slice: &mut [F], + ) -> Result<()> { + let Instruction { opcode, .. } = instruction; + let local_opcode = Rv32LoadStoreOpcode::from_usize( - instruction - .opcode - .local_opcode_idx(Rv32LoadStoreOpcode::CLASS_OFFSET), + opcode.local_opcode_idx(Rv32LoadStoreOpcode::CLASS_OFFSET), ); - let (data, shift_amount) = reads.into(); - let shift_amount = shift_amount.as_canonical_u32(); + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + A::start(*state.pc, state.memory, adapter_row); + + let ((prev_data, read_data), shift_amount) = + self.adapter.read(state.memory, instruction, adapter_row); + let prev_data = prev_data.map(F::from_canonical_u8); + let read_data = read_data.map(F::from_canonical_u8); + + // TODO(ayush): should functions operate on u8 limbs instead of F? let write_data: [F; NUM_CELLS] = run_write_data_sign_extend::<_, NUM_CELLS, LIMB_BITS>( local_opcode, - data[1], - data[0], + read_data, + prev_data, shift_amount, ); - let output = AdapterRuntimeContext::without_pc([write_data]); let most_sig_limb = match local_opcode { LOADB => write_data[0], @@ -243,94 +258,73 @@ where .as_canonical_u32(); let most_sig_bit = most_sig_limb & (1 << (LIMB_BITS - 1)); - self.range_checker_chip - .add_count(most_sig_limb - most_sig_bit, LIMB_BITS - 1); let read_shift = shift_amount & 2; - Ok(( - output, - LoadSignExtendCoreRecord { - opcode: local_opcode, - most_sig_bit: most_sig_bit != 0, - prev_data: data[0], - shifted_read_data: array::from_fn(|i| { - data[1][(i + read_shift as usize) % NUM_CELLS] - }), - shift_amount, - }, - )) - } + let core_row: &mut LoadSignExtendCoreCols = core_row.borrow_mut(); + core_row.opcode_loadb_flag0 = + F::from_bool(local_opcode == LOADB && (shift_amount & 1) == 0); + core_row.opcode_loadb_flag1 = + F::from_bool(local_opcode == LOADB && (shift_amount & 1) == 1); + core_row.opcode_loadh_flag = F::from_bool(local_opcode == LOADH); + core_row.shift_most_sig_bit = F::from_canonical_u32((shift_amount & 2) >> 1); + core_row.data_most_sig_bit = F::from_bool(most_sig_bit != 0); + core_row.prev_data = prev_data; + core_row.shifted_read_data = + array::from_fn(|i| read_data[(i + read_shift as usize) % NUM_CELLS]); + + self.adapter.write( + state.memory, + instruction, + adapter_row, + &write_data.map(|x| x.as_canonical_u32() as u8), + ); - fn get_opcode_name(&self, opcode: usize) -> String { - format!( - "{:?}", - Rv32LoadStoreOpcode::from_usize(opcode - Rv32LoadStoreOpcode::CLASS_OFFSET) - ) - } + // TODO(ayush): move to fill_trace_row + self.range_checker_chip + .add_count(most_sig_limb - most_sig_bit, LIMB_BITS - 1); - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let core_cols: &mut LoadSignExtendCoreCols = row_slice.borrow_mut(); - let opcode = record.opcode; - let shift = record.shift_amount; - core_cols.opcode_loadb_flag0 = F::from_bool(opcode == LOADB && (shift & 1) == 0); - core_cols.opcode_loadb_flag1 = F::from_bool(opcode == LOADB && (shift & 1) == 1); - core_cols.opcode_loadh_flag = F::from_bool(opcode == LOADH); - core_cols.shift_most_sig_bit = F::from_canonical_u32((shift & 2) >> 1); - core_cols.data_most_sig_bit = F::from_bool(record.most_sig_bit); - core_cols.prev_data = record.prev_data; - core_cols.shifted_read_data = record.shifted_read_data; + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) } - fn air(&self) -> &Self::Air { - &self.air + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + let _core_row: &mut LoadSignExtendCoreCols = core_row.borrow_mut(); + + self.adapter + .fill_trace_row(mem_helper, &self.range_checker_chip, adapter_row); } } -impl InsExecutorE1 - for LoadSignExtendCoreChip +impl StepExecutorE1 + for LoadSignExtendStep where - Mem: GuestMemory, F: PrimeField32, + A: 'static + + for<'a> AdapterExecutorE1< + F, + ReadData = (([u8; NUM_CELLS], [u8; NUM_CELLS]), u32), + WriteData = [u8; NUM_CELLS], + >, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, - a, - b, - c, - f: enabled, - g, - .. - } = instruction; + ) -> Result<()> + where + Mem: GuestMemory, + { + let Instruction { opcode, .. } = instruction; let local_opcode = Rv32LoadStoreOpcode::from_usize( opcode.local_opcode_idx(Rv32LoadStoreOpcode::CLASS_OFFSET), ); - let rs1_addr = b.as_canonical_u32(); - let rs1_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = - unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; - let rs1_val = u32::from_le_bytes(rs1_bytes); - - let imm = c.as_canonical_u32(); - let imm_sign = g.as_canonical_u32(); - let imm_extended = imm + imm_sign * 0xffff0000; - - let ptr_val = rs1_val.wrapping_add(imm_extended); - let shift_amount = ptr_val % 4; - let ptr_val = ptr_val - shift_amount; // aligned ptr - - let read_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = match local_opcode { - LOADB | LOADH => unsafe { state.memory.read(RV32_MEMORY_AS, ptr_val) }, - _ => unreachable!("Only LOADB and LOADH are supported by LoadSignExtendCoreChip chip"), - }; - // TODO(ayush): handle NUM_CELLS and RV32_REGISTER_NUM_LIMBS properly - let read_data: [F; NUM_CELLS] = array::from_fn(|i| F::from_canonical_u8(read_bytes[i])); + let ((_, read_data), shift_amount) = self.adapter.read(state.memory, instruction); + let read_data = read_data.map(F::from_canonical_u8); // TODO(ayush): clean this up for e1 let write_data = run_write_data_sign_extend::<_, NUM_CELLS, LIMB_BITS>( @@ -339,24 +333,102 @@ where [F::ZERO; NUM_CELLS], shift_amount, ); - let write_bytes: [u8; NUM_CELLS] = - array::from_fn(|i| write_data[i].as_canonical_u32() as u8); - - // Only proceed if instruction is enabled - if *enabled != F::ZERO { - // Write result to destination register - let rd_addr = a.as_canonical_u32(); - unsafe { - state.memory.write(RV32_REGISTER_AS, rd_addr, &write_bytes); - } - } + let write_data = write_data.map(|x| x.as_canonical_u32() as u8); + + self.adapter.write(state.memory, instruction, &write_data); - state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } } +// impl, const NUM_CELLS: usize, const LIMB_BITS: usize> +// VmCoreChip for LoadSignExtendCoreChip +// where +// I::Reads: Into<([[F; NUM_CELLS]; 2], F)>, +// I::Writes: From<[[F; NUM_CELLS]; 1]>, +// { +// type Record = LoadSignExtendCoreRecord; +// type Air = LoadSignExtendCoreAir; + +// #[allow(clippy::type_complexity)] +// fn execute_instruction( +// &self, +// instruction: &Instruction, +// _from_pc: u32, +// reads: I::Reads, +// ) -> Result<(AdapterRuntimeContext, Self::Record)> { +// let local_opcode = Rv32LoadStoreOpcode::from_usize( +// instruction +// .opcode +// .local_opcode_idx(Rv32LoadStoreOpcode::CLASS_OFFSET), +// ); + +// let (data, shift_amount) = reads.into(); +// let shift_amount = shift_amount.as_canonical_u32(); +// let write_data: [F; NUM_CELLS] = run_write_data_sign_extend::<_, NUM_CELLS, LIMB_BITS>( +// local_opcode, +// data[1], +// data[0], +// shift_amount, +// ); +// let output = AdapterRuntimeContext::without_pc([write_data]); + +// let most_sig_limb = match local_opcode { +// LOADB => write_data[0], +// LOADH => write_data[NUM_CELLS / 2 - 1], +// _ => unreachable!(), +// } +// .as_canonical_u32(); + +// let most_sig_bit = most_sig_limb & (1 << (LIMB_BITS - 1)); +// self.range_checker_chip +// .add_count(most_sig_limb - most_sig_bit, LIMB_BITS - 1); + +// let read_shift = shift_amount & 2; + +// Ok(( +// output, +// LoadSignExtendCoreRecord { +// opcode: local_opcode, +// most_sig_bit: most_sig_bit != 0, +// prev_data: data[0], +// shifted_read_data: array::from_fn(|i| { +// data[1][(i + read_shift as usize) % NUM_CELLS] +// }), +// shift_amount, +// }, +// )) +// } + +// fn get_opcode_name(&self, opcode: usize) -> String { +// format!( +// "{:?}", +// Rv32LoadStoreOpcode::from_usize(opcode - Rv32LoadStoreOpcode::CLASS_OFFSET) +// ) +// } + +// fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { +// let core_cols: &mut LoadSignExtendCoreCols = row_slice.borrow_mut(); +// let opcode = record.opcode; +// let shift = record.shift_amount; +// core_cols.opcode_loadb_flag0 = F::from_bool(opcode == LOADB && (shift & 1) == 0); +// core_cols.opcode_loadb_flag1 = F::from_bool(opcode == LOADB && (shift & 1) == 1); +// core_cols.opcode_loadh_flag = F::from_bool(opcode == LOADH); +// core_cols.shift_most_sig_bit = F::from_canonical_u32((shift & 2) >> 1); +// core_cols.data_most_sig_bit = F::from_bool(record.most_sig_bit); +// core_cols.prev_data = record.prev_data; +// core_cols.shifted_read_data = record.shifted_read_data; +// } + +// fn air(&self) -> &Self::Air { +// &self.air +// } +// } + +// TODO(ayush): remove _prev_data +#[inline(always)] pub(super) fn run_write_data_sign_extend< F: PrimeField32, const NUM_CELLS: usize, diff --git a/extensions/rv32im/circuit/src/load_sign_extend/mod.rs b/extensions/rv32im/circuit/src/load_sign_extend/mod.rs index 79efbe912e..43c563e432 100644 --- a/extensions/rv32im/circuit/src/load_sign_extend/mod.rs +++ b/extensions/rv32im/circuit/src/load_sign_extend/mod.rs @@ -1,7 +1,7 @@ -use openvm_circuit::arch::VmChipWrapper; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; use super::adapters::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; -use crate::adapters::Rv32LoadStoreAdapterChip; +use crate::adapters::{Rv32LoadStoreAdapterAir, Rv32LoadStoreAdapterStep}; mod core; pub use core::*; @@ -9,8 +9,11 @@ pub use core::*; #[cfg(test)] mod tests; -pub type Rv32LoadSignExtendChip = VmChipWrapper< - F, - Rv32LoadStoreAdapterChip, - LoadSignExtendCoreChip, +pub type Rv32LoadSignExtendAir = VmAirWrapper< + Rv32LoadStoreAdapterAir, + LoadSignExtendCoreAir, >; +pub type Rv32LoadSignExtendStep = + LoadSignExtendStep; +pub type Rv32LoadSignExtendChip = + NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/load_sign_extend/tests.rs b/extensions/rv32im/circuit/src/load_sign_extend/tests.rs index 17f172587b..658884f99c 100644 --- a/extensions/rv32im/circuit/src/load_sign_extend/tests.rs +++ b/extensions/rv32im/circuit/src/load_sign_extend/tests.rs @@ -2,7 +2,7 @@ use std::{array, borrow::BorrowMut}; use openvm_circuit::arch::{ testing::{memory::gen_pointer, VmChipTestBuilder}, - VmAdapterChip, + VmAirWrapper, }; use openvm_instructions::{instruction::Instruction, LocalOpcode}; use openvm_rv32im_transpiler::Rv32LoadStoreOpcode::{self, *}; @@ -19,14 +19,18 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{config::setup_tracing, p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; -use super::run_write_data_sign_extend; +use super::{run_write_data_sign_extend, LoadSignExtendCoreAir}; use crate::{ - adapters::{compose, Rv32LoadStoreAdapterChip, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, + adapters::{ + compose, Rv32LoadStoreAdapterAir, Rv32LoadStoreAdapterStep, RV32_CELL_BITS, + RV32_REGISTER_NUM_LIMBS, + }, load_sign_extend::LoadSignExtendCoreCols, - LoadSignExtendCoreChip, Rv32LoadSignExtendChip, + LoadSignExtendStep, Rv32LoadSignExtendChip, }; const IMM_BITS: usize = 16; +const MAX_INS_CAPACITY: usize = 256; type F = BabyBear; @@ -122,19 +126,28 @@ fn rand_load_sign_extend_test() { setup_tracing(); let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); + let range_checker_chip = tester.memory_controller().range_checker.clone(); - let adapter = Rv32LoadStoreAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - range_checker_chip.clone(), + + let mut chip = Rv32LoadSignExtendChip::::new( + VmAirWrapper::new( + Rv32LoadStoreAdapterAir::new( + tester.memory_bridge(), + tester.execution_bridge(), + range_checker_chip.bus(), + tester.address_bits(), + ), + LoadSignExtendCoreAir::new(range_checker_chip.bus()), + ), + LoadSignExtendStep::new( + Rv32LoadStoreAdapterStep::new(tester.address_bits()), + range_checker_chip, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - let core = LoadSignExtendCoreChip::new(range_checker_chip); - let mut chip = - Rv32LoadSignExtendChip::::new(adapter, core, tester.offline_memory_mutex_arc()); - let num_tests: usize = 100; + let num_tests: usize = 1; for _ in 0..num_tests { set_and_execute( &mut tester, @@ -185,17 +198,26 @@ fn run_negative_loadstore_test( let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); let range_checker_chip = tester.memory_controller().range_checker.clone(); - let adapter = Rv32LoadStoreAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - range_checker_chip.clone(), + + let mut chip = Rv32LoadSignExtendChip::::new( + VmAirWrapper::new( + Rv32LoadStoreAdapterAir::new( + tester.memory_bridge(), + tester.execution_bridge(), + range_checker_chip.bus(), + tester.address_bits(), + ), + LoadSignExtendCoreAir::new(range_checker_chip.bus()), + ), + LoadSignExtendStep::new( + Rv32LoadStoreAdapterStep::new(tester.address_bits()), + range_checker_chip, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - let core = LoadSignExtendCoreChip::new(range_checker_chip.clone()); - let adapter_width = BaseAir::::width(adapter.air()); - let mut chip = - Rv32LoadSignExtendChip::::new(adapter, core, tester.offline_memory_mutex_arc()); + + let adapter_width = BaseAir::::width(&chip.air.adapter); set_and_execute( &mut tester, @@ -235,7 +257,6 @@ fn run_negative_loadstore_test( *trace = RowMajorMatrix::new(trace_row, trace.width()); }; - drop(range_checker_chip); disable_debug_builder(); let tester = tester .build() @@ -293,16 +314,24 @@ fn execute_roundtrip_sanity_test() { let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); let range_checker_chip = tester.memory_controller().range_checker.clone(); - let adapter = Rv32LoadStoreAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - range_checker_chip.clone(), + + let mut chip = Rv32LoadSignExtendChip::::new( + VmAirWrapper::new( + Rv32LoadStoreAdapterAir::new( + tester.memory_bridge(), + tester.execution_bridge(), + range_checker_chip.bus(), + tester.address_bits(), + ), + LoadSignExtendCoreAir::new(range_checker_chip.bus()), + ), + LoadSignExtendStep::new( + Rv32LoadStoreAdapterStep::new(tester.address_bits()), + range_checker_chip, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - let core = LoadSignExtendCoreChip::new(range_checker_chip); - let mut chip = - Rv32LoadSignExtendChip::::new(adapter, core, tester.offline_memory_mutex_arc()); let num_tests: usize = 10; for _ in 0..num_tests { diff --git a/extensions/rv32im/circuit/src/loadstore/core.rs b/extensions/rv32im/circuit/src/loadstore/core.rs index 7e0054da2a..4c51a22073 100644 --- a/extensions/rv32im/circuit/src/loadstore/core.rs +++ b/extensions/rv32im/circuit/src/loadstore/core.rs @@ -2,18 +2,17 @@ use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, Result, VmAdapterInterface, - VmCoreAir, VmCoreChip, VmExecutionState, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, Result, SingleTraceStep, + StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::online::GuestMemory, }; +use openvm_circuit_primitives::var_range::SharedVariableRangeCheckerChip; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{ - instruction::Instruction, - program::DEFAULT_PC_STEP, - riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS, RV32_REGISTER_NUM_LIMBS}, - LocalOpcode, -}; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; use openvm_rv32im_transpiler::Rv32LoadStoreOpcode::{self, *}; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -79,7 +78,7 @@ pub struct LoadStoreCoreRecord { pub write_data: [F; NUM_CELLS], } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, derive_new::new)] pub struct LoadStoreCoreAir { pub offset: usize, } @@ -255,70 +254,69 @@ where } } -#[derive(Debug)] -pub struct LoadStoreCoreChip { - pub air: LoadStoreCoreAir, +pub struct LoadStoreStep { + adapter: A, + pub range_checker_chip: SharedVariableRangeCheckerChip, + pub offset: usize, } -impl LoadStoreCoreChip { - pub fn new(offset: usize) -> Self { +impl LoadStoreStep { + pub fn new( + adapter: A, + range_checker_chip: SharedVariableRangeCheckerChip, + offset: usize, + ) -> Self { Self { - air: LoadStoreCoreAir { offset }, + adapter, + range_checker_chip, + offset, } } } -impl, const NUM_CELLS: usize> VmCoreChip - for LoadStoreCoreChip +impl SingleTraceStep for LoadStoreStep where - I::Reads: Into<([[F; NUM_CELLS]; 2], F)>, - I::Writes: From<[[F; NUM_CELLS]; 1]>, + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = (([u8; NUM_CELLS], [u8; NUM_CELLS]), u32), + WriteData = [u8; NUM_CELLS], + TraceContext<'a> = &'a SharedVariableRangeCheckerChip, + >, { - type Record = LoadStoreCoreRecord; - type Air = LoadStoreCoreAir; - - #[allow(clippy::type_complexity)] - fn execute_instruction( - &self, - instruction: &Instruction, - _from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { - let local_opcode = - Rv32LoadStoreOpcode::from_usize(instruction.opcode.local_opcode_idx(self.air.offset)); - - let (reads, shift_amount) = reads.into(); - let shift = shift_amount.as_canonical_u32(); - let prev_data = reads[0]; - let read_data = reads[1]; - let write_data = run_write_data(local_opcode, read_data, prev_data, shift); - let output = AdapterRuntimeContext::without_pc([write_data]); - - Ok(( - output, - LoadStoreCoreRecord { - opcode: local_opcode, - shift, - prev_data, - read_data, - write_data, - }, - )) - } - fn get_opcode_name(&self, opcode: usize) -> String { format!( "{:?}", - Rv32LoadStoreOpcode::from_usize(opcode - self.air.offset) + Rv32LoadStoreOpcode::from_usize(opcode - self.offset) ) } - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let core_cols: &mut LoadStoreCoreCols = row_slice.borrow_mut(); - let opcode = record.opcode; + fn execute( + &mut self, + state: VmStateMut, + instruction: &Instruction, + row_slice: &mut [F], + ) -> Result<()> { + let Instruction { opcode, .. } = instruction; + + let local_opcode = Rv32LoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + let ((prev_data, read_data), shift) = + self.adapter.read(state.memory, instruction, adapter_row); + let prev_data = prev_data.map(F::from_canonical_u8); + let read_data = read_data.map(F::from_canonical_u8); + + let write_data = run_write_data(local_opcode, read_data, prev_data, shift); + + let core_cols: &mut LoadStoreCoreCols = core_row.borrow_mut(); + let flags = &mut core_cols.flags; *flags = [F::ZERO; 4]; - match (opcode, record.shift) { + match (local_opcode, shift) { (LOADW, 0) => flags[0] = F::TWO, (LOADHU, 0) => flags[1] = F::TWO, (LOADHU, 2) => flags[2] = F::TWO, @@ -337,109 +335,154 @@ where (STOREB, 3) => (flags[2], flags[3]) = (F::ONE, F::ONE), _ => unreachable!(), }; - core_cols.prev_data = record.prev_data; - core_cols.read_data = record.read_data; + core_cols.prev_data = prev_data; + core_cols.read_data = read_data; core_cols.is_valid = F::ONE; - core_cols.is_load = F::from_bool([LOADW, LOADHU, LOADBU].contains(&opcode)); - core_cols.write_data = record.write_data; + core_cols.is_load = F::from_bool([LOADW, LOADHU, LOADBU].contains(&local_opcode)); + core_cols.write_data = write_data; + + self.adapter.write( + state.memory, + instruction, + adapter_row, + &write_data.map(|x| x.as_canonical_u32() as u8), + ); + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) } - fn air(&self) -> &Self::Air { - &self.air + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + let _core_row: &mut LoadStoreCoreCols = core_row.borrow_mut(); + + self.adapter + .fill_trace_row(mem_helper, &self.range_checker_chip, adapter_row); } } -impl InsExecutorE1 - for LoadStoreCoreChip +impl StepExecutorE1 for LoadStoreStep where - Mem: GuestMemory, F: PrimeField32, + A: 'static + + for<'a> AdapterExecutorE1< + F, + ReadData = (([u8; NUM_CELLS], [u8; NUM_CELLS]), u32), + WriteData = [u8; NUM_CELLS], + >, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, - a, - b, - c, - g, - f: enabled, - .. - } = instruction; + ) -> Result<()> + where + Mem: GuestMemory, + { + let Instruction { opcode, .. } = instruction; // Get the local opcode for this instruction - let local_opcode = - Rv32LoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - - let rs1_addr = b.as_canonical_u32(); - let rs1_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = - unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; - let rs1_val = u32::from_le_bytes(rs1_bytes); - - let imm = c.as_canonical_u32(); - let imm_sign = g.as_canonical_u32(); - let imm_extended = imm + imm_sign * 0xffff0000; - - let ptr_val = rs1_val.wrapping_add(imm_extended); - let shift_amount = ptr_val % 4; - let ptr_val = ptr_val - shift_amount; // aligned ptr - - let read_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = match local_opcode { - LOADW | LOADB | LOADH | LOADBU | LOADHU => { - // For loads, read from memory - unsafe { state.memory.read(RV32_MEMORY_AS, ptr_val) } - } - STOREW | STOREH | STOREB => { - // For stores, read the register value to be stored - let rs2_addr = a.as_canonical_u32(); - unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) } - } - }; + let local_opcode = Rv32LoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - // For stores, we need the previous memory content to preserve unchanged bytes - let prev_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = match local_opcode { - STOREW | STOREH | STOREB => { - // For stores, read current memory content - unsafe { state.memory.read(RV32_MEMORY_AS, ptr_val) } - } - LOADW | LOADB | LOADH | LOADBU | LOADHU => { - // For loads, read current register content - let rd_addr = a.as_canonical_u32(); - unsafe { state.memory.read(RV32_REGISTER_AS, rd_addr) } - } - }; - - let read_data = read_bytes.map(F::from_canonical_u8); - let prev_data = prev_bytes.map(F::from_canonical_u8); + let ((prev_data, read_data), shift_amount) = self.adapter.read(state.memory, instruction); + let prev_data = prev_data.map(F::from_canonical_u8); + let read_data = read_data.map(F::from_canonical_u8); // Process the data according to the load/store type and alignment let write_data = run_write_data(local_opcode, read_data, prev_data, shift_amount); - let write_bytes = write_data.map(|x| x.as_canonical_u32() as u8); - - if *enabled != F::ZERO { - match local_opcode { - STOREW | STOREH | STOREB => { - // For stores, write to memory - unsafe { state.memory.write(RV32_MEMORY_AS, ptr_val, &write_data) }; - } - LOADW | LOADB | LOADH | LOADBU | LOADHU => { - let rd_addr = a.as_canonical_u32(); - unsafe { - state.memory.write(RV32_REGISTER_AS, rd_addr, &write_bytes); - } - } - } - } + let write_data = write_data.map(|x| x.as_canonical_u32() as u8); + + self.adapter.write(state.memory, instruction, &write_data); - state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } } +// impl, const NUM_CELLS: usize> VmCoreChip +// for LoadStoreCoreChip +// where +// I::Reads: Into<([[F; NUM_CELLS]; 2], F)>, +// I::Writes: From<[[F; NUM_CELLS]; 1]>, +// { +// type Record = LoadStoreCoreRecord; +// type Air = LoadStoreCoreAir; + +// #[allow(clippy::type_complexity)] +// fn execute_instruction( +// &self, +// instruction: &Instruction, +// _from_pc: u32, +// reads: I::Reads, +// ) -> Result<(AdapterRuntimeContext, Self::Record)> { +// let local_opcode = +// Rv32LoadStoreOpcode::from_usize(instruction.opcode.local_opcode_idx(self.air.offset)); + +// let (reads, shift_amount) = reads.into(); +// let shift = shift_amount.as_canonical_u32(); +// let prev_data = reads[0]; +// let read_data = reads[1]; +// let write_data = run_write_data(local_opcode, read_data, prev_data, shift); +// let output = AdapterRuntimeContext::without_pc([write_data]); + +// Ok(( +// output, +// LoadStoreCoreRecord { +// opcode: local_opcode, +// shift, +// prev_data, +// read_data, +// write_data, +// }, +// )) +// } + +// fn get_opcode_name(&self, opcode: usize) -> String { +// format!( +// "{:?}", +// Rv32LoadStoreOpcode::from_usize(opcode - self.air.offset) +// ) +// } + +// fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { +// let core_cols: &mut LoadStoreCoreCols = row_slice.borrow_mut(); +// let opcode = record.opcode; +// let flags = &mut core_cols.flags; +// *flags = [F::ZERO; 4]; +// match (opcode, record.shift) { +// (LOADW, 0) => flags[0] = F::TWO, +// (LOADHU, 0) => flags[1] = F::TWO, +// (LOADHU, 2) => flags[2] = F::TWO, +// (LOADBU, 0) => flags[3] = F::TWO, + +// (LOADBU, 1) => flags[0] = F::ONE, +// (LOADBU, 2) => flags[1] = F::ONE, +// (LOADBU, 3) => flags[2] = F::ONE, +// (STOREW, 0) => flags[3] = F::ONE, + +// (STOREH, 0) => (flags[0], flags[1]) = (F::ONE, F::ONE), +// (STOREH, 2) => (flags[0], flags[2]) = (F::ONE, F::ONE), +// (STOREB, 0) => (flags[0], flags[3]) = (F::ONE, F::ONE), +// (STOREB, 1) => (flags[1], flags[2]) = (F::ONE, F::ONE), +// (STOREB, 2) => (flags[1], flags[3]) = (F::ONE, F::ONE), +// (STOREB, 3) => (flags[2], flags[3]) = (F::ONE, F::ONE), +// _ => unreachable!(), +// }; +// core_cols.prev_data = record.prev_data; +// core_cols.read_data = record.read_data; +// core_cols.is_valid = F::ONE; +// core_cols.is_load = F::from_bool([LOADW, LOADHU, LOADBU].contains(&opcode)); +// core_cols.write_data = record.write_data; +// } + +// fn air(&self) -> &Self::Air { +// &self.air +// } +// } + +#[inline(always)] pub(super) fn run_write_data( opcode: Rv32LoadStoreOpcode, read_data: [F; NUM_CELLS], diff --git a/extensions/rv32im/circuit/src/loadstore/mod.rs b/extensions/rv32im/circuit/src/loadstore/mod.rs index 825f82166c..c7ea7fbd82 100644 --- a/extensions/rv32im/circuit/src/loadstore/mod.rs +++ b/extensions/rv32im/circuit/src/loadstore/mod.rs @@ -2,12 +2,16 @@ mod core; pub use core::*; -use openvm_circuit::arch::VmChipWrapper; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; -use super::adapters::{Rv32LoadStoreAdapterChip, RV32_REGISTER_NUM_LIMBS}; +use crate::adapters::{Rv32LoadStoreAdapterAir, Rv32LoadStoreAdapterStep}; + +use super::adapters::RV32_REGISTER_NUM_LIMBS; #[cfg(test)] mod tests; -pub type Rv32LoadStoreChip = - VmChipWrapper, LoadStoreCoreChip>; +pub type Rv32LoadStoreAir = + VmAirWrapper>; +pub type Rv32LoadStoreStep = LoadStoreStep; +pub type Rv32LoadStoreChip = NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/loadstore/tests.rs b/extensions/rv32im/circuit/src/loadstore/tests.rs index c7e388fd35..781a275c3a 100644 --- a/extensions/rv32im/circuit/src/loadstore/tests.rs +++ b/extensions/rv32im/circuit/src/loadstore/tests.rs @@ -3,7 +3,7 @@ use std::{array, borrow::BorrowMut}; use openvm_circuit::{ arch::{ testing::{memory::gen_pointer, VmChipTestBuilder}, - VmAdapterChip, + VmAdapterChip, VmAirWrapper, }, utils::u32_into_limbs, }; @@ -22,13 +22,17 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{config::setup_tracing, p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, seq::SliceRandom, Rng}; -use super::{run_write_data, LoadStoreCoreChip, Rv32LoadStoreChip}; +use super::{run_write_data, LoadStoreCoreAir, LoadStoreStep, Rv32LoadStoreChip}; use crate::{ - adapters::{compose, Rv32LoadStoreAdapterChip, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, + adapters::{ + compose, Rv32LoadStoreAdapterAir, Rv32LoadStoreAdapterStep, RV32_CELL_BITS, + RV32_REGISTER_NUM_LIMBS, + }, loadstore::LoadStoreCoreCols, }; const IMM_BITS: usize = 16; +const MAX_INS_CAPACITY: usize = 128; type F = BabyBear; @@ -69,6 +73,8 @@ fn set_and_execute( let mem_as = mem_as.unwrap_or(if is_load { *[1, 2].choose(rng).unwrap() } else { + // TODO(ayush): how can memory address space be variable? + // how can this write to native as? *[2, 3, 4].choose(rng).unwrap() }); @@ -143,16 +149,25 @@ fn rand_loadstore_test() { let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); let range_checker_chip = tester.memory_controller().range_checker.clone(); - let adapter = Rv32LoadStoreAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - range_checker_chip.clone(), - ); - let core = LoadStoreCoreChip::new(Rv32LoadStoreOpcode::CLASS_OFFSET); - let mut chip = Rv32LoadStoreChip::::new(adapter, core, tester.offline_memory_mutex_arc()); + let mut chip = Rv32LoadStoreChip::::new( + VmAirWrapper::new( + Rv32LoadStoreAdapterAir::new( + tester.memory_bridge(), + tester.execution_bridge(), + range_checker_chip.bus(), + tester.address_bits(), + ), + LoadStoreCoreAir::new(Rv32LoadStoreOpcode::CLASS_OFFSET), + ), + LoadStoreStep::new( + Rv32LoadStoreAdapterStep::new(tester.address_bits()), + range_checker_chip.clone(), + Rv32LoadStoreOpcode::CLASS_OFFSET, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), + ); let num_tests: usize = 100; for _ in 0..num_tests { @@ -248,17 +263,27 @@ fn run_negative_loadstore_test( let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); let range_checker_chip = tester.memory_controller().range_checker.clone(); - let adapter = Rv32LoadStoreAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - range_checker_chip.clone(), + + let mut chip = Rv32LoadStoreChip::::new( + VmAirWrapper::new( + Rv32LoadStoreAdapterAir::new( + tester.memory_bridge(), + tester.execution_bridge(), + range_checker_chip.bus(), + tester.address_bits(), + ), + LoadStoreCoreAir::new(Rv32LoadStoreOpcode::CLASS_OFFSET), + ), + LoadStoreStep::new( + Rv32LoadStoreAdapterStep::new(tester.address_bits()), + range_checker_chip.clone(), + Rv32LoadStoreOpcode::CLASS_OFFSET, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - let core = LoadStoreCoreChip::new(Rv32LoadStoreOpcode::CLASS_OFFSET); - let adapter_width = BaseAir::::width(adapter.air()); - let mut chip = Rv32LoadStoreChip::::new(adapter, core, tester.offline_memory_mutex_arc()); + let adapter_width = BaseAir::::width(&chip.air.adapter); set_and_execute( &mut tester, @@ -431,17 +456,27 @@ fn execute_roundtrip_sanity_test() { let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); let range_checker_chip = tester.memory_controller().range_checker.clone(); - let adapter = Rv32LoadStoreAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - range_checker_chip.clone(), + + let mut chip = Rv32LoadStoreChip::::new( + VmAirWrapper::new( + Rv32LoadStoreAdapterAir::new( + tester.memory_bridge(), + tester.execution_bridge(), + range_checker_chip.bus(), + tester.address_bits(), + ), + LoadStoreCoreAir::new(Rv32LoadStoreOpcode::CLASS_OFFSET), + ), + LoadStoreStep::new( + Rv32LoadStoreAdapterStep::new(tester.address_bits()), + range_checker_chip.clone(), + Rv32LoadStoreOpcode::CLASS_OFFSET, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - let core = LoadStoreCoreChip::new(Rv32LoadStoreOpcode::CLASS_OFFSET); - let mut chip = Rv32LoadStoreChip::::new(adapter, core, tester.offline_memory_mutex_arc()); - let num_tests: usize = 100; + let num_tests: usize = 1; for _ in 0..num_tests { set_and_execute( &mut tester, @@ -463,16 +498,17 @@ fn execute_roundtrip_sanity_test() { None, None, ); - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - LOADHU, - None, - None, - None, - None, - ); + // TODO(ayush): what are alignment requirements for hint as? + // set_and_execute( + // &mut tester, + // &mut chip, + // &mut rng, + // LOADHU, + // None, + // None, + // None, + // None, + // ); set_and_execute( &mut tester, &mut chip, @@ -493,16 +529,16 @@ fn execute_roundtrip_sanity_test() { None, None, ); - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - STOREH, - None, - None, - None, - None, - ); + // set_and_execute( + // &mut tester, + // &mut chip, + // &mut rng, + // STOREH, + // None, + // None, + // None, + // None, + // ); } } diff --git a/extensions/rv32im/circuit/src/mul/core.rs b/extensions/rv32im/circuit/src/mul/core.rs index 1cfb4abd64..57c580a75a 100644 --- a/extensions/rv32im/circuit/src/mul/core.rs +++ b/extensions/rv32im/circuit/src/mul/core.rs @@ -5,16 +5,17 @@ use std::{ use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, - VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, + SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::online::GuestMemory, }; use openvm_circuit_primitives::range_tuple::{RangeTupleCheckerBus, SharedRangeTupleCheckerChip}; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{ - instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_REGISTER_AS, LocalOpcode, -}; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; use openvm_rv32im_transpiler::MulOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -34,7 +35,7 @@ pub struct MultiplicationCoreCols { pub bus: RangeTupleCheckerBus<2>, pub offset: usize, @@ -114,14 +115,33 @@ where } } +#[repr(C)] +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "T: Serialize + DeserializeOwned")] +pub struct MultiplicationCoreRecord { + #[serde(with = "BigArray")] + pub a: [T; NUM_LIMBS], + #[serde(with = "BigArray")] + pub b: [T; NUM_LIMBS], + #[serde(with = "BigArray")] + pub c: [T; NUM_LIMBS], +} + #[derive(Debug)] -pub struct MultiplicationCoreChip { - pub air: MultiplicationCoreAir, +pub struct MultiplicationStep { + adapter: A, + pub offset: usize, pub range_tuple_chip: SharedRangeTupleCheckerChip<2>, } -impl MultiplicationCoreChip { - pub fn new(range_tuple_chip: SharedRangeTupleCheckerChip<2>, offset: usize) -> Self { +impl + MultiplicationStep +{ + pub fn new( + adapter: A, + range_tuple_chip: SharedRangeTupleCheckerChip<2>, + offset: usize, + ) -> Self { // The RangeTupleChecker is used to range check (a[i], carry[i]) pairs where 0 <= i // < NUM_LIMBS. a[i] must have LIMB_BITS bits and carry[i] is the sum of i + 1 bytes // (with LIMB_BITS bits). @@ -137,129 +157,120 @@ impl MultiplicationCoreChip { - #[serde(with = "BigArray")] - pub a: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub b: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub c: [T; NUM_LIMBS], -} - -impl, const NUM_LIMBS: usize, const LIMB_BITS: usize> - VmCoreChip for MultiplicationCoreChip +impl SingleTraceStep + for MultiplicationStep where - I::Reads: Into<[[F; NUM_LIMBS]; 2]>, - I::Writes: From<[[F; NUM_LIMBS]; 1]>, + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), + WriteData = [u8; NUM_LIMBS], + TraceContext<'a> = (), + >, { - type Record = MultiplicationCoreRecord; - type Air = MultiplicationCoreAir; + fn get_opcode_name(&self, opcode: usize) -> String { + format!("{:?}", MulOpcode::from_usize(opcode - self.offset)) + } - #[allow(clippy::type_complexity)] - fn execute_instruction( - &self, + fn execute( + &mut self, + state: VmStateMut, instruction: &Instruction, - _from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { + row_slice: &mut [F], + ) -> Result<()> { let Instruction { opcode, .. } = instruction; + assert_eq!( - MulOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)), + MulOpcode::from_usize(opcode.local_opcode_idx(self.offset)), MulOpcode::MUL ); - let data: [[F; NUM_LIMBS]; 2] = reads.into(); - let b = data[0].map(|x| u8::try_from(x.as_canonical_u32()).unwrap()); - let c = data[1].map(|y| u8::try_from(y.as_canonical_u32()).unwrap()); - let (a, carry) = run_mul::(&b, &c); + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + A::start(*state.pc, state.memory, adapter_row); + + let (rs1, rs2) = self.adapter.read(state.memory, instruction, adapter_row); + + let (a, carry) = run_mul::(&rs1, &rs2); + + let core_row: &mut MultiplicationCoreCols<_, NUM_LIMBS, LIMB_BITS> = core_row.borrow_mut(); + core_row.a = a.map(F::from_canonical_u8); + core_row.b = rs1.map(F::from_canonical_u8); + core_row.c = rs2.map(F::from_canonical_u8); + core_row.is_valid = F::ONE; + + // TODO(ayush): move to fill_trace_row for (a, carry) in a.iter().zip(carry.iter()) { self.range_tuple_chip.add_count(&[*a as u32, *carry]); } - let output = AdapterRuntimeContext::without_pc([a.map(F::from_canonical_u8)]); - let record = MultiplicationCoreRecord { - a: a.map(F::from_canonical_u8), - b: data[0], - c: data[1], - }; + // TODO(ayush): avoid this conversion + self.adapter + .write(state.memory, instruction, adapter_row, &a); - Ok((output, record)) - } + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); - fn get_opcode_name(&self, opcode: usize) -> String { - format!("{:?}", MulOpcode::from_usize(opcode - self.air.offset)) + Ok(()) } - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let row_slice: &mut MultiplicationCoreCols<_, NUM_LIMBS, LIMB_BITS> = - row_slice.borrow_mut(); - row_slice.a = record.a; - row_slice.b = record.b; - row_slice.c = record.c; - row_slice.is_valid = F::ONE; - } + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, _core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - fn air(&self) -> &Self::Air { - &self.air + self.adapter.fill_trace_row(mem_helper, (), adapter_row); } } -impl InsExecutorE1 - for MultiplicationCoreChip +impl StepExecutorE1 + for MultiplicationStep where - Mem: GuestMemory, F: PrimeField32, + A: 'static + + for<'a> AdapterExecutorE1< + F, + ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), + WriteData = [u8; NUM_LIMBS], + >, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, a, b, c, .. - } = instruction; + ) -> Result<()> + where + Mem: GuestMemory, + { + let Instruction { opcode, .. } = instruction; // Verify the opcode is MUL // TODO(ayush): debug_assert assert_eq!( - MulOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)), + MulOpcode::from_usize(opcode.local_opcode_idx(self.offset)), MulOpcode::MUL ); - // Read input registers - let rs1_addr = b.as_canonical_u32(); - let rs2_addr = c.as_canonical_u32(); - - let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; - let rs2_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; + let (rs1, rs2) = self.adapter.read(state.memory, instruction); - // Perform the multiplication - let (rd_bytes, _) = run_mul::(&rs1_bytes, &rs2_bytes); + let (rd, _) = run_mul::(&rs1, &rs2); - // Write result to destination register - let rd_addr = a.as_canonical_u32(); - unsafe { state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes) }; + self.adapter.write(state.memory, instruction, &rd); - state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } } // returns mul, carry +#[inline(always)] pub(super) fn run_mul( x: &[u8; NUM_LIMBS], y: &[u8; NUM_LIMBS], diff --git a/extensions/rv32im/circuit/src/mul/mod.rs b/extensions/rv32im/circuit/src/mul/mod.rs index 5f28439977..95a0fc6468 100644 --- a/extensions/rv32im/circuit/src/mul/mod.rs +++ b/extensions/rv32im/circuit/src/mul/mod.rs @@ -1,6 +1,8 @@ -use openvm_circuit::arch::VmChipWrapper; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; -use super::adapters::{Rv32MultAdapterChip, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; +use crate::adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep}; + +use super::adapters::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; mod core; pub use core::*; @@ -8,8 +10,11 @@ pub use core::*; #[cfg(test)] mod tests; -pub type Rv32MultiplicationChip = VmChipWrapper< - F, - Rv32MultAdapterChip, - MultiplicationCoreChip, +pub type Rv32MultiplicationAir = VmAirWrapper< + Rv32MultAdapterAir, + MultiplicationCoreAir, >; +pub type Rv32MultiplicationStep = + MultiplicationStep; +pub type Rv32MultiplicationChip = + NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/mul/tests.rs b/extensions/rv32im/circuit/src/mul/tests.rs index 329dc857d6..d477420462 100644 --- a/extensions/rv32im/circuit/src/mul/tests.rs +++ b/extensions/rv32im/circuit/src/mul/tests.rs @@ -3,7 +3,7 @@ use std::borrow::BorrowMut; use openvm_circuit::{ arch::{ testing::{TestAdapterChip, VmChipTestBuilder, RANGE_TUPLE_CHECKER_BUS}, - ExecutionBridge, VmAdapterChip, VmChipWrapper, + ExecutionBridge, VmAdapterChip, VmAirWrapper, VmChipWrapper, }, utils::generate_long_number, }; @@ -25,11 +25,14 @@ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use super::core::run_mul; use crate::{ - adapters::{Rv32MultAdapterChip, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, - mul::{MultiplicationCoreChip, MultiplicationCoreCols, Rv32MultiplicationChip}, + adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, + mul::{MultiplicationCoreCols, MultiplicationStep, Rv32MultiplicationChip}, test_utils::rv32_rand_write_register_or_imm, + MultiplicationCoreAir, }; +const MAX_INS_CAPACITY: usize = 128; + type F = BabyBear; ////////////////////////////////////////////////////////////////////////////////////// @@ -51,14 +54,19 @@ fn run_rv32_mul_rand_test(num_ops: usize) { let range_tuple_checker = SharedRangeTupleCheckerChip::new(range_tuple_bus); let mut tester = VmChipTestBuilder::default(); + let mut chip = Rv32MultiplicationChip::::new( - Rv32MultAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), + VmAirWrapper::new( + Rv32MultAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), + MultiplicationCoreAir::new(range_tuple_bus, MulOpcode::CLASS_OFFSET), ), - MultiplicationCoreChip::new(range_tuple_checker.clone(), MulOpcode::CLASS_OFFSET), - tester.offline_memory_mutex_arc(), + MultiplicationStep::new( + Rv32MultAdapterStep::new(), + range_tuple_checker.clone(), + MulOpcode::CLASS_OFFSET, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), ); for _ in 0..num_ops { @@ -106,97 +114,97 @@ fn rv32_mul_rand_test() { // A dummy adapter is used so memory interactions don't indirectly cause false passes. ////////////////////////////////////////////////////////////////////////////////////// -type Rv32MultiplicationTestChip = VmChipWrapper< - F, - TestAdapterChip, - MultiplicationCoreChip, ->; - -#[allow(clippy::too_many_arguments)] -fn run_rv32_mul_negative_test( - a: [u8; RV32_REGISTER_NUM_LIMBS], - b: [u8; RV32_REGISTER_NUM_LIMBS], - c: [u8; RV32_REGISTER_NUM_LIMBS], - is_valid: bool, - interaction_error: bool, -) { - const MAX_NUM_LIMBS: u32 = 32; - let range_tuple_bus = RangeTupleCheckerBus::new( - RANGE_TUPLE_CHECKER_BUS, - [1 << RV32_CELL_BITS, MAX_NUM_LIMBS * (1 << RV32_CELL_BITS)], - ); - let range_tuple_chip = SharedRangeTupleCheckerChip::new(range_tuple_bus); - - let mut tester = VmChipTestBuilder::default(); - let mut chip = Rv32MultiplicationTestChip::::new( - TestAdapterChip::new( - vec![[b.map(F::from_canonical_u8), c.map(F::from_canonical_u8)].concat()], - vec![None], - ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), - ), - MultiplicationCoreChip::new(range_tuple_chip.clone(), MulOpcode::CLASS_OFFSET), - tester.offline_memory_mutex_arc(), - ); - - tester.execute( - &mut chip, - &Instruction::from_usize(MulOpcode::MUL.global_opcode(), [0, 0, 0, 1, 0]), - ); - - let trace_width = chip.trace_width(); - let adapter_width = BaseAir::::width(chip.adapter.air()); - let (_, carry) = run_mul::(&b, &c); - - range_tuple_chip.clear(); - if is_valid { - for (a, carry) in a.iter().zip(carry.iter()) { - range_tuple_chip.add_count(&[*a as u32, *carry]); - } - } - - let modify_trace = |trace: &mut DenseMatrix| { - let mut values = trace.row_slice(0).to_vec(); - let cols: &mut MultiplicationCoreCols = - values.split_at_mut(adapter_width).1.borrow_mut(); - cols.a = a.map(F::from_canonical_u8); - cols.is_valid = F::from_bool(is_valid); - *trace = RowMajorMatrix::new(values, trace_width); - }; - - disable_debug_builder(); - let tester = tester - .build() - .load_and_prank_trace(chip, modify_trace) - .load(range_tuple_chip) - .finalize(); - tester.simple_test_with_expected_error(if interaction_error { - VerificationError::ChallengePhaseError - } else { - VerificationError::OodEvaluationMismatch - }); -} - -#[test] -fn rv32_mul_wrong_negative_test() { - run_rv32_mul_negative_test( - [63, 247, 125, 234], - [51, 109, 78, 142], - [197, 85, 150, 32], - true, - true, - ); -} - -#[test] -fn rv32_mul_is_valid_false_negative_test() { - run_rv32_mul_negative_test( - [63, 247, 125, 234], - [51, 109, 78, 142], - [197, 85, 150, 32], - false, - true, - ); -} +// type Rv32MultiplicationTestChip = VmChipWrapper< +// F, +// TestAdapterChip, +// MultiplicationStep, +// >; + +// #[allow(clippy::too_many_arguments)] +// fn run_rv32_mul_negative_test( +// a: [u8; RV32_REGISTER_NUM_LIMBS], +// b: [u8; RV32_REGISTER_NUM_LIMBS], +// c: [u8; RV32_REGISTER_NUM_LIMBS], +// is_valid: bool, +// interaction_error: bool, +// ) { +// const MAX_NUM_LIMBS: u32 = 32; +// let range_tuple_bus = RangeTupleCheckerBus::new( +// RANGE_TUPLE_CHECKER_BUS, +// [1 << RV32_CELL_BITS, MAX_NUM_LIMBS * (1 << RV32_CELL_BITS)], +// ); +// let range_tuple_chip = SharedRangeTupleCheckerChip::new(range_tuple_bus); + +// let mut tester = VmChipTestBuilder::default(); +// let mut chip = Rv32MultiplicationTestChip::::new( +// TestAdapterChip::new( +// vec![[b.map(F::from_canonical_u8), c.map(F::from_canonical_u8)].concat()], +// vec![None], +// ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), +// ), +// MultiplicationStep::new(range_tuple_chip.clone(), MulOpcode::CLASS_OFFSET), +// tester.offline_memory_mutex_arc(), +// ); + +// tester.execute( +// &mut chip, +// &Instruction::from_usize(MulOpcode::MUL.global_opcode(), [0, 0, 0, 1, 0]), +// ); + +// let trace_width = chip.trace_width(); +// let adapter_width = BaseAir::::width(chip.adapter.air()); +// let (_, carry) = run_mul::(&b, &c); + +// range_tuple_chip.clear(); +// if is_valid { +// for (a, carry) in a.iter().zip(carry.iter()) { +// range_tuple_chip.add_count(&[*a as u32, *carry]); +// } +// } + +// let modify_trace = |trace: &mut DenseMatrix| { +// let mut values = trace.row_slice(0).to_vec(); +// let cols: &mut MultiplicationCoreCols = +// values.split_at_mut(adapter_width).1.borrow_mut(); +// cols.a = a.map(F::from_canonical_u8); +// cols.is_valid = F::from_bool(is_valid); +// *trace = RowMajorMatrix::new(values, trace_width); +// }; + +// disable_debug_builder(); +// let tester = tester +// .build() +// .load_and_prank_trace(chip, modify_trace) +// .load(range_tuple_chip) +// .finalize(); +// tester.simple_test_with_expected_error(if interaction_error { +// VerificationError::ChallengePhaseError +// } else { +// VerificationError::OodEvaluationMismatch +// }); +// } + +// #[test] +// fn rv32_mul_wrong_negative_test() { +// run_rv32_mul_negative_test( +// [63, 247, 125, 234], +// [51, 109, 78, 142], +// [197, 85, 150, 32], +// true, +// true, +// ); +// } + +// #[test] +// fn rv32_mul_is_valid_false_negative_test() { +// run_rv32_mul_negative_test( +// [63, 247, 125, 234], +// [51, 109, 78, 142], +// [197, 85, 150, 32], +// false, +// true, +// ); +// } /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS diff --git a/extensions/rv32im/circuit/src/mulh/core.rs b/extensions/rv32im/circuit/src/mulh/core.rs index 5fa707328b..27091a18ec 100644 --- a/extensions/rv32im/circuit/src/mulh/core.rs +++ b/extensions/rv32im/circuit/src/mulh/core.rs @@ -5,19 +5,20 @@ use std::{ use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, - VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, + SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, range_tuple::{RangeTupleCheckerBus, SharedRangeTupleCheckerChip}, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{ - instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_REGISTER_AS, LocalOpcode, -}; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; use openvm_rv32im_transpiler::MulHOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -45,7 +46,7 @@ pub struct MulHCoreCols { pub opcode_mulhu_flag: T, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, derive_new::new)] pub struct MulHCoreAir { pub bitwise_lookup_bus: BitwiseOperationLookupBus, pub range_tuple_bus: RangeTupleCheckerBus<2>, @@ -188,14 +189,31 @@ where } } -pub struct MulHCoreChip { - pub air: MulHCoreAir, +#[repr(C)] +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct MulHCoreRecord { + pub opcode: MulHOpcode, + #[serde(with = "BigArray")] + pub a: [T; NUM_LIMBS], + #[serde(with = "BigArray")] + pub b: [T; NUM_LIMBS], + #[serde(with = "BigArray")] + pub c: [T; NUM_LIMBS], + #[serde(with = "BigArray")] + pub a_mul: [T; NUM_LIMBS], + pub b_ext: T, + pub c_ext: T, +} + +pub struct MulHStep { + adapter: A, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, pub range_tuple_chip: SharedRangeTupleCheckerChip<2>, } -impl MulHCoreChip { +impl MulHStep { pub fn new( + adapter: A, bitwise_lookup_chip: SharedBitwiseOperationLookupChip, range_tuple_chip: SharedRangeTupleCheckerChip<2>, ) -> Self { @@ -214,56 +232,65 @@ impl MulHCoreChip { - pub opcode: MulHOpcode, - #[serde(with = "BigArray")] - pub a: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub b: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub c: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub a_mul: [T; NUM_LIMBS], - pub b_ext: T, - pub c_ext: T, -} - -impl, const NUM_LIMBS: usize, const LIMB_BITS: usize> - VmCoreChip for MulHCoreChip +impl SingleTraceStep + for MulHStep where - I::Reads: Into<[[F; NUM_LIMBS]; 2]>, - I::Writes: From<[[F; NUM_LIMBS]; 1]>, + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), + WriteData = [u8; NUM_LIMBS], + TraceContext<'a> = (), + >, { - type Record = MulHCoreRecord; - type Air = MulHCoreAir; + fn get_opcode_name(&self, opcode: usize) -> String { + format!( + "{:?}", + MulHOpcode::from_usize(opcode - MulHOpcode::CLASS_OFFSET) + ) + } - #[allow(clippy::type_complexity)] - fn execute_instruction( - &self, + fn execute( + &mut self, + state: VmStateMut, instruction: &Instruction, - _from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { + row_slice: &mut [F], + ) -> Result<()> { let Instruction { opcode, .. } = instruction; + let mulh_opcode = MulHOpcode::from_usize(opcode.local_opcode_idx(MulHOpcode::CLASS_OFFSET)); - let data: [[F; NUM_LIMBS]; 2] = reads.into(); - let b = data[0].map(|x| x.as_canonical_u32()); - let c = data[1].map(|y| y.as_canonical_u32()); + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + A::start(*state.pc, state.memory, adapter_row); + + let (rs1, rs2) = self.adapter.read(state.memory, instruction, adapter_row); + + let b = rs1.map(u32::from); + let c = rs2.map(u32::from); let (a, a_mul, carry, b_ext, c_ext) = run_mulh::(mulh_opcode, &b, &c); + let core_row: &mut MulHCoreCols<_, NUM_LIMBS, LIMB_BITS> = core_row.borrow_mut(); + core_row.a = a.map(F::from_canonical_u32); + core_row.b = b.map(F::from_canonical_u32); + core_row.c = c.map(F::from_canonical_u32); + core_row.a_mul = a_mul.map(F::from_canonical_u32); + core_row.b_ext = F::from_canonical_u32(b_ext); + core_row.c_ext = F::from_canonical_u32(c_ext); + core_row.opcode_mulh_flag = F::from_bool(mulh_opcode == MulHOpcode::MULH); + core_row.opcode_mulhsu_flag = F::from_bool(mulh_opcode == MulHOpcode::MULHSU); + core_row.opcode_mulhu_flag = F::from_bool(mulh_opcode == MulHOpcode::MULHU); + + // TODO(ayush): move to fill_trace_row for i in 0..NUM_LIMBS { self.range_tuple_chip.add_count(&[a_mul[i], carry[i]]); self.range_tuple_chip @@ -279,91 +306,63 @@ where ); } - let output = AdapterRuntimeContext::without_pc([a.map(F::from_canonical_u32)]); - let record = MulHCoreRecord { - opcode: mulh_opcode, - a: a.map(F::from_canonical_u32), - b: data[0], - c: data[1], - a_mul: a_mul.map(F::from_canonical_u32), - b_ext: F::from_canonical_u32(b_ext), - c_ext: F::from_canonical_u32(c_ext), - }; + // TODO(ayush): avoid this conversion + let a = a.map(|x| x as u8); + self.adapter + .write(state.memory, instruction, adapter_row, &a); - Ok((output, record)) - } + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); - fn get_opcode_name(&self, opcode: usize) -> String { - format!( - "{:?}", - MulHOpcode::from_usize(opcode - MulHOpcode::CLASS_OFFSET) - ) + Ok(()) } - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let row_slice: &mut MulHCoreCols<_, NUM_LIMBS, LIMB_BITS> = row_slice.borrow_mut(); - row_slice.a = record.a; - row_slice.b = record.b; - row_slice.c = record.c; - row_slice.a_mul = record.a_mul; - row_slice.b_ext = record.b_ext; - row_slice.c_ext = record.c_ext; - row_slice.opcode_mulh_flag = F::from_bool(record.opcode == MulHOpcode::MULH); - row_slice.opcode_mulhsu_flag = F::from_bool(record.opcode == MulHOpcode::MULHSU); - row_slice.opcode_mulhu_flag = F::from_bool(record.opcode == MulHOpcode::MULHU); - } + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, _core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - fn air(&self) -> &Self::Air { - &self.air + self.adapter.fill_trace_row(mem_helper, (), adapter_row); } } -impl InsExecutorE1 - for MulHCoreChip +impl StepExecutorE1 + for MulHStep where - Mem: GuestMemory, F: PrimeField32, + A: 'static + + for<'a> AdapterExecutorE1< + F, + ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), + WriteData = [u8; NUM_LIMBS], + >, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, a, b, c, .. - } = instruction; + ) -> Result<()> + where + Mem: GuestMemory, + { + let Instruction { opcode, .. } = instruction; let mulh_opcode = MulHOpcode::from_usize(opcode.local_opcode_idx(MulHOpcode::CLASS_OFFSET)); - // Read input registers - let rs1_addr = b.as_canonical_u32(); - let rs2_addr = c.as_canonical_u32(); + let (rs1, rs2) = self.adapter.read(state.memory, instruction); + let rs1 = rs1.map(u32::from); + let rs2 = rs2.map(u32::from); - let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; - let rs2_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; + let (rd, _, _, _, _) = run_mulh::(mulh_opcode, &rs1, &rs2); + let rd = rd.map(|x| x as u8); - // TODO(ayush): remove this conversion - let rs1_bytes = rs1_bytes.map(|x| x as u32); - let rs2_bytes = rs2_bytes.map(|y| y as u32); - - // Execute the multiplication high operation - let (rd_bytes, _, _, _, _) = - run_mulh::(mulh_opcode, &rs1_bytes, &rs2_bytes); - let rd_bytes = rd_bytes.map(|x| x as u8); - - // Write the result to the destination register - let rd_addr = a.as_canonical_u32(); - unsafe { - state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes); - } + self.adapter.write(state.memory, instruction, &rd); - state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } } // returns mulh[[s]u], mul, carry, x_ext, y_ext +#[inline(always)] pub(super) fn run_mulh( opcode: MulHOpcode, x: &[u32; NUM_LIMBS], diff --git a/extensions/rv32im/circuit/src/mulh/mod.rs b/extensions/rv32im/circuit/src/mulh/mod.rs index 284b77191a..480ec3adaa 100644 --- a/extensions/rv32im/circuit/src/mulh/mod.rs +++ b/extensions/rv32im/circuit/src/mulh/mod.rs @@ -1,6 +1,8 @@ -use openvm_circuit::arch::VmChipWrapper; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; -use super::adapters::{Rv32MultAdapterChip, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; +use crate::adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep}; + +use super::adapters::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; mod core; pub use core::*; @@ -8,5 +10,7 @@ pub use core::*; #[cfg(test)] mod tests; -pub type Rv32MulHChip = - VmChipWrapper, MulHCoreChip>; +pub type Rv32MulHAir = + VmAirWrapper>; +pub type Rv32MulHStep = MulHStep; +pub type Rv32MulHChip = NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/mulh/tests.rs b/extensions/rv32im/circuit/src/mulh/tests.rs index 1c7cf5b5cb..4a20075d3e 100644 --- a/extensions/rv32im/circuit/src/mulh/tests.rs +++ b/extensions/rv32im/circuit/src/mulh/tests.rs @@ -6,7 +6,7 @@ use openvm_circuit::{ memory::gen_pointer, TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS, RANGE_TUPLE_CHECKER_BUS, }, - ExecutionBridge, InstructionExecutor, VmAdapterChip, VmChipWrapper, + ExecutionBridge, InstructionExecutor, VmAdapterChip, VmAirWrapper, VmChipWrapper, }, utils::generate_long_number, }; @@ -32,10 +32,13 @@ use rand::rngs::StdRng; use super::core::run_mulh; use crate::{ - adapters::{Rv32MultAdapterChip, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, - mulh::{MulHCoreChip, MulHCoreCols, Rv32MulHChip}, + adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, + mulh::{MulHCoreCols, MulHStep, Rv32MulHChip}, + MulHCoreAir, }; +const MAX_INS_CAPACITY: usize = 128; + type F = BabyBear; ////////////////////////////////////////////////////////////////////////////////////// @@ -88,14 +91,19 @@ fn run_rv32_mulh_rand_test(opcode: MulHOpcode, num_ops: usize) { let range_tuple_checker = SharedRangeTupleCheckerChip::new(range_tuple_bus); let mut tester = VmChipTestBuilder::default(); + let mut chip = Rv32MulHChip::::new( - Rv32MultAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), + VmAirWrapper::new( + Rv32MultAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), + MulHCoreAir::new(bitwise_bus, range_tuple_bus), + ), + MulHStep::new( + Rv32MultAdapterStep::new(), + bitwise_chip.clone(), + range_tuple_checker.clone(), ), - MulHCoreChip::new(bitwise_chip.clone(), range_tuple_checker.clone()), - tester.offline_memory_mutex_arc(), + MAX_INS_CAPACITY, + tester.memory_helper(), ); for _ in 0..num_ops { @@ -136,234 +144,234 @@ fn rv32_mulhu_rand_test() { // A dummy adapter is used so memory interactions don't indirectly cause false passes. ////////////////////////////////////////////////////////////////////////////////////// -type Rv32MulHTestChip = - VmChipWrapper, MulHCoreChip>; - -#[allow(clippy::too_many_arguments)] -fn run_rv32_mulh_negative_test( - opcode: MulHOpcode, - a: [u32; RV32_REGISTER_NUM_LIMBS], - b: [u32; RV32_REGISTER_NUM_LIMBS], - c: [u32; RV32_REGISTER_NUM_LIMBS], - a_mul: [u32; RV32_REGISTER_NUM_LIMBS], - b_ext: u32, - c_ext: u32, - interaction_error: bool, -) { - const MAX_NUM_LIMBS: u32 = 32; - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let range_tuple_bus = RangeTupleCheckerBus::new( - RANGE_TUPLE_CHECKER_BUS, - [1 << RV32_CELL_BITS, MAX_NUM_LIMBS * (1 << RV32_CELL_BITS)], - ); - - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let range_tuple_chip = SharedRangeTupleCheckerChip::new(range_tuple_bus); - - let mut tester = VmChipTestBuilder::default(); - let mut chip = Rv32MulHTestChip::::new( - TestAdapterChip::new( - vec![[b.map(F::from_canonical_u32), c.map(F::from_canonical_u32)].concat()], - vec![None], - ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), - ), - MulHCoreChip::new(bitwise_chip.clone(), range_tuple_chip.clone()), - tester.offline_memory_mutex_arc(), - ); - - tester.execute( - &mut chip, - &Instruction::from_usize(opcode.global_opcode(), [0, 0, 0, 1, 0]), - ); - - let trace_width = chip.trace_width(); - let adapter_width = BaseAir::::width(chip.adapter.air()); - let (_, _, carry, _, _) = run_mulh::(opcode, &b, &c); - - range_tuple_chip.clear(); - for i in 0..RV32_REGISTER_NUM_LIMBS { - range_tuple_chip.add_count(&[a_mul[i], carry[i]]); - range_tuple_chip.add_count(&[a[i], carry[RV32_REGISTER_NUM_LIMBS + i]]); - } - - let modify_trace = |trace: &mut DenseMatrix| { - let mut values = trace.row_slice(0).to_vec(); - let cols: &mut MulHCoreCols = - values.split_at_mut(adapter_width).1.borrow_mut(); - cols.a = a.map(F::from_canonical_u32); - cols.a_mul = a_mul.map(F::from_canonical_u32); - cols.b_ext = F::from_canonical_u32(b_ext); - cols.c_ext = F::from_canonical_u32(c_ext); - *trace = RowMajorMatrix::new(values, trace_width); - }; - - disable_debug_builder(); - let tester = tester - .build() - .load_and_prank_trace(chip, modify_trace) - .load(bitwise_chip) - .load(range_tuple_chip) - .finalize(); - tester.simple_test_with_expected_error(if interaction_error { - VerificationError::ChallengePhaseError - } else { - VerificationError::OodEvaluationMismatch - }); -} - -#[test] -fn rv32_mulh_wrong_a_mul_negative_test() { - run_rv32_mulh_negative_test( - MulHOpcode::MULH, - [130, 9, 135, 241], - [197, 85, 150, 32], - [51, 109, 78, 142], - [63, 247, 125, 234], - 0, - 255, - true, - ); -} - -#[test] -fn rv32_mulh_wrong_a_negative_test() { - run_rv32_mulh_negative_test( - MulHOpcode::MULH, - [130, 9, 135, 242], - [197, 85, 150, 32], - [51, 109, 78, 142], - [63, 247, 125, 232], - 0, - 255, - true, - ); -} - -#[test] -fn rv32_mulh_wrong_ext_negative_test() { - run_rv32_mulh_negative_test( - MulHOpcode::MULH, - [1, 0, 0, 0], - [0, 0, 0, 128], - [2, 0, 0, 0], - [0, 0, 0, 0], - 0, - 0, - true, - ); -} - -#[test] -fn rv32_mulh_invalid_ext_negative_test() { - run_rv32_mulh_negative_test( - MulHOpcode::MULH, - [3, 2, 2, 2], - [0, 0, 0, 128], - [2, 0, 0, 0], - [0, 0, 0, 0], - 1, - 0, - false, - ); -} - -#[test] -fn rv32_mulhsu_wrong_a_mul_negative_test() { - run_rv32_mulh_negative_test( - MulHOpcode::MULHSU, - [174, 40, 246, 202], - [197, 85, 150, 160], - [51, 109, 78, 142], - [63, 247, 125, 105], - 255, - 0, - true, - ); -} - -#[test] -fn rv32_mulhsu_wrong_a_negative_test() { - run_rv32_mulh_negative_test( - MulHOpcode::MULHSU, - [174, 40, 246, 201], - [197, 85, 150, 160], - [51, 109, 78, 142], - [63, 247, 125, 104], - 255, - 0, - true, - ); -} - -#[test] -fn rv32_mulhsu_wrong_b_ext_negative_test() { - run_rv32_mulh_negative_test( - MulHOpcode::MULHSU, - [1, 0, 0, 0], - [0, 0, 0, 128], - [2, 0, 0, 0], - [0, 0, 0, 0], - 0, - 0, - true, - ); -} - -#[test] -fn rv32_mulhsu_wrong_c_ext_negative_test() { - run_rv32_mulh_negative_test( - MulHOpcode::MULHSU, - [0, 0, 0, 64], - [0, 0, 0, 128], - [0, 0, 0, 128], - [0, 0, 0, 0], - 255, - 255, - false, - ); -} - -#[test] -fn rv32_mulhu_wrong_a_mul_negative_test() { - run_rv32_mulh_negative_test( - MulHOpcode::MULHU, - [130, 9, 135, 241], - [197, 85, 150, 32], - [51, 109, 78, 142], - [63, 247, 125, 234], - 0, - 0, - true, - ); -} - -#[test] -fn rv32_mulhu_wrong_a_negative_test() { - run_rv32_mulh_negative_test( - MulHOpcode::MULHU, - [130, 9, 135, 240], - [197, 85, 150, 32], - [51, 109, 78, 142], - [63, 247, 125, 232], - 0, - 0, - true, - ); -} - -#[test] -fn rv32_mulhu_wrong_ext_negative_test() { - run_rv32_mulh_negative_test( - MulHOpcode::MULHU, - [255, 255, 255, 255], - [0, 0, 0, 128], - [2, 0, 0, 0], - [0, 0, 0, 0], - 255, - 0, - false, - ); -} +// type Rv32MulHTestChip = +// VmChipWrapper, MulHStep>; + +// #[allow(clippy::too_many_arguments)] +// fn run_rv32_mulh_negative_test( +// opcode: MulHOpcode, +// a: [u32; RV32_REGISTER_NUM_LIMBS], +// b: [u32; RV32_REGISTER_NUM_LIMBS], +// c: [u32; RV32_REGISTER_NUM_LIMBS], +// a_mul: [u32; RV32_REGISTER_NUM_LIMBS], +// b_ext: u32, +// c_ext: u32, +// interaction_error: bool, +// ) { +// const MAX_NUM_LIMBS: u32 = 32; +// let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); +// let range_tuple_bus = RangeTupleCheckerBus::new( +// RANGE_TUPLE_CHECKER_BUS, +// [1 << RV32_CELL_BITS, MAX_NUM_LIMBS * (1 << RV32_CELL_BITS)], +// ); + +// let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); +// let range_tuple_chip = SharedRangeTupleCheckerChip::new(range_tuple_bus); + +// let mut tester = VmChipTestBuilder::default(); +// let mut chip = Rv32MulHTestChip::::new( +// TestAdapterChip::new( +// vec![[b.map(F::from_canonical_u32), c.map(F::from_canonical_u32)].concat()], +// vec![None], +// ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), +// ), +// MulHStep::new(bitwise_chip.clone(), range_tuple_chip.clone()), +// tester.offline_memory_mutex_arc(), +// ); + +// tester.execute( +// &mut chip, +// &Instruction::from_usize(opcode.global_opcode(), [0, 0, 0, 1, 0]), +// ); + +// let trace_width = chip.trace_width(); +// let adapter_width = BaseAir::::width(chip.adapter.air()); +// let (_, _, carry, _, _) = run_mulh::(opcode, &b, &c); + +// range_tuple_chip.clear(); +// for i in 0..RV32_REGISTER_NUM_LIMBS { +// range_tuple_chip.add_count(&[a_mul[i], carry[i]]); +// range_tuple_chip.add_count(&[a[i], carry[RV32_REGISTER_NUM_LIMBS + i]]); +// } + +// let modify_trace = |trace: &mut DenseMatrix| { +// let mut values = trace.row_slice(0).to_vec(); +// let cols: &mut MulHCoreCols = +// values.split_at_mut(adapter_width).1.borrow_mut(); +// cols.a = a.map(F::from_canonical_u32); +// cols.a_mul = a_mul.map(F::from_canonical_u32); +// cols.b_ext = F::from_canonical_u32(b_ext); +// cols.c_ext = F::from_canonical_u32(c_ext); +// *trace = RowMajorMatrix::new(values, trace_width); +// }; + +// disable_debug_builder(); +// let tester = tester +// .build() +// .load_and_prank_trace(chip, modify_trace) +// .load(bitwise_chip) +// .load(range_tuple_chip) +// .finalize(); +// tester.simple_test_with_expected_error(if interaction_error { +// VerificationError::ChallengePhaseError +// } else { +// VerificationError::OodEvaluationMismatch +// }); +// } + +// #[test] +// fn rv32_mulh_wrong_a_mul_negative_test() { +// run_rv32_mulh_negative_test( +// MulHOpcode::MULH, +// [130, 9, 135, 241], +// [197, 85, 150, 32], +// [51, 109, 78, 142], +// [63, 247, 125, 234], +// 0, +// 255, +// true, +// ); +// } + +// #[test] +// fn rv32_mulh_wrong_a_negative_test() { +// run_rv32_mulh_negative_test( +// MulHOpcode::MULH, +// [130, 9, 135, 242], +// [197, 85, 150, 32], +// [51, 109, 78, 142], +// [63, 247, 125, 232], +// 0, +// 255, +// true, +// ); +// } + +// #[test] +// fn rv32_mulh_wrong_ext_negative_test() { +// run_rv32_mulh_negative_test( +// MulHOpcode::MULH, +// [1, 0, 0, 0], +// [0, 0, 0, 128], +// [2, 0, 0, 0], +// [0, 0, 0, 0], +// 0, +// 0, +// true, +// ); +// } + +// #[test] +// fn rv32_mulh_invalid_ext_negative_test() { +// run_rv32_mulh_negative_test( +// MulHOpcode::MULH, +// [3, 2, 2, 2], +// [0, 0, 0, 128], +// [2, 0, 0, 0], +// [0, 0, 0, 0], +// 1, +// 0, +// false, +// ); +// } + +// #[test] +// fn rv32_mulhsu_wrong_a_mul_negative_test() { +// run_rv32_mulh_negative_test( +// MulHOpcode::MULHSU, +// [174, 40, 246, 202], +// [197, 85, 150, 160], +// [51, 109, 78, 142], +// [63, 247, 125, 105], +// 255, +// 0, +// true, +// ); +// } + +// #[test] +// fn rv32_mulhsu_wrong_a_negative_test() { +// run_rv32_mulh_negative_test( +// MulHOpcode::MULHSU, +// [174, 40, 246, 201], +// [197, 85, 150, 160], +// [51, 109, 78, 142], +// [63, 247, 125, 104], +// 255, +// 0, +// true, +// ); +// } + +// #[test] +// fn rv32_mulhsu_wrong_b_ext_negative_test() { +// run_rv32_mulh_negative_test( +// MulHOpcode::MULHSU, +// [1, 0, 0, 0], +// [0, 0, 0, 128], +// [2, 0, 0, 0], +// [0, 0, 0, 0], +// 0, +// 0, +// true, +// ); +// } + +// #[test] +// fn rv32_mulhsu_wrong_c_ext_negative_test() { +// run_rv32_mulh_negative_test( +// MulHOpcode::MULHSU, +// [0, 0, 0, 64], +// [0, 0, 0, 128], +// [0, 0, 0, 128], +// [0, 0, 0, 0], +// 255, +// 255, +// false, +// ); +// } + +// #[test] +// fn rv32_mulhu_wrong_a_mul_negative_test() { +// run_rv32_mulh_negative_test( +// MulHOpcode::MULHU, +// [130, 9, 135, 241], +// [197, 85, 150, 32], +// [51, 109, 78, 142], +// [63, 247, 125, 234], +// 0, +// 0, +// true, +// ); +// } + +// #[test] +// fn rv32_mulhu_wrong_a_negative_test() { +// run_rv32_mulh_negative_test( +// MulHOpcode::MULHU, +// [130, 9, 135, 240], +// [197, 85, 150, 32], +// [51, 109, 78, 142], +// [63, 247, 125, 232], +// 0, +// 0, +// true, +// ); +// } + +// #[test] +// fn rv32_mulhu_wrong_ext_negative_test() { +// run_rv32_mulh_negative_test( +// MulHOpcode::MULHU, +// [255, 255, 255, 255], +// [0, 0, 0, 128], +// [2, 0, 0, 0], +// [0, 0, 0, 0], +// 255, +// 0, +// false, +// ); +// } /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS diff --git a/extensions/rv32im/circuit/src/shift/core.rs b/extensions/rv32im/circuit/src/shift/core.rs index 30b2f5f8ef..b796e7c9db 100644 --- a/extensions/rv32im/circuit/src/shift/core.rs +++ b/extensions/rv32im/circuit/src/shift/core.rs @@ -1,13 +1,12 @@ use std::{ array, borrow::{Borrow, BorrowMut}, - marker::PhantomData, }; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterTraceStep, InsExecutorE1, MinimalInstruction, Result, - SingleTraceStep, VmAdapterInterface, VmCoreAir, VmExecutionState, VmStateMut, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, + SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -22,12 +21,7 @@ use openvm_circuit_primitives::{ var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus}, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{ - instruction::Instruction, - program::DEFAULT_PC_STEP, - riscv::{RV32_IMM_AS, RV32_REGISTER_AS}, - LocalOpcode, -}; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; use openvm_rv32im_transpiler::ShiftOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -63,7 +57,7 @@ pub struct ShiftCoreCols { pub bit_shift_carry: [T; NUM_LIMBS], } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, derive_new::new)] pub struct ShiftCoreAir { pub bitwise_lookup_bus: BitwiseOperationLookupBus, pub range_bus: VariableRangeCheckerBus, @@ -249,58 +243,94 @@ where } } -pub struct ShiftCoreChip { - pub air: ShiftCoreAir, +pub struct ShiftStep { + adapter: A, + pub offset: usize, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, pub range_checker_chip: SharedVariableRangeCheckerChip, } -impl ShiftCoreChip { +impl ShiftStep { pub fn new( + adapter: A, bitwise_lookup_chip: SharedBitwiseOperationLookupChip, range_checker_chip: SharedVariableRangeCheckerChip, offset: usize, ) -> Self { assert_eq!(NUM_LIMBS % 2, 0, "Number of limbs must be divisible by 2"); Self { - air: ShiftCoreAir { - bitwise_lookup_bus: bitwise_lookup_chip.bus(), - range_bus: range_checker_chip.bus(), - offset, - }, + adapter, + offset, bitwise_lookup_chip, range_checker_chip, } } +} - #[inline] - pub fn execute( - &self, +impl SingleTraceStep + for ShiftStep +where + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), + WriteData = [u8; NUM_LIMBS], + TraceContext<'a> = &'a BitwiseOperationLookupChip, + >, +{ + fn get_opcode_name(&self, opcode: usize) -> String { + format!("{:?}", ShiftOpcode::from_usize(opcode - self.offset)) + } + + fn execute( + &mut self, + state: VmStateMut, instruction: &Instruction, - [b, c]: [[u8; NUM_LIMBS]; 2], - core_row: &mut [F], - ) -> [u8; NUM_LIMBS] { - let opcode = instruction.opcode; - let local_opcode = ShiftOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); + row_slice: &mut [F], + ) -> Result<()> { + let Instruction { opcode, .. } = instruction; + + let local_opcode = ShiftOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - let (a, limb_shift, bit_shift) = run_shift::(local_opcode, &b, &c); + A::start(*state.pc, state.memory, adapter_row); + + let (rs1, rs2) = self.adapter.read(state.memory, instruction, adapter_row); + + let (output, limb_shift, bit_shift) = + run_shift::(local_opcode, &rs1, &rs2); let core_row: &mut ShiftCoreCols = core_row.borrow_mut(); - core_row.a = a.map(F::from_canonical_u8); - core_row.b = b.map(F::from_canonical_u8); - core_row.c = c.map(F::from_canonical_u8); + core_row.a = output.map(F::from_canonical_u8); + core_row.b = rs1.map(F::from_canonical_u8); + core_row.c = rs2.map(F::from_canonical_u8); // To be transformed later in fill_trace_row: core_row.opcode_sll_flag = F::from_canonical_usize(local_opcode as usize); core_row.bit_shift_marker[0] = F::from_canonical_usize(bit_shift); core_row.limb_shift_marker[0] = F::from_canonical_usize(limb_shift); - a + self.adapter + .write(state.memory, instruction, adapter_row, &output); + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) } - pub fn fill_trace_row(&self, core_row: &mut [F]) { + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + self.adapter + .fill_trace_row(mem_helper, self.bitwise_lookup_chip.as_ref(), adapter_row); + let core_row: &mut ShiftCoreCols = core_row.borrow_mut(); + let local_opcode = ShiftOpcode::from_usize(core_row.opcode_sll_flag.as_canonical_u32() as usize); + let bit_shift = core_row.bit_shift_marker[0].as_canonical_u32() as usize; let limb_shift = core_row.limb_shift_marker[0].as_canonical_u32() as usize; let b = core_row.b.map(|x| x.as_canonical_u32()); @@ -351,113 +381,43 @@ impl ShiftCoreChip { - pub core: ShiftCoreChip, - phantom: PhantomData, -} - -impl SingleTraceStep +impl StepExecutorE1 for ShiftStep where F: PrimeField32, A: 'static - + for<'a> AdapterTraceStep< + + for<'a> AdapterExecutorE1< F, - CTX, - ReadData = [[u8; NUM_LIMBS]; 2], + ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), WriteData = [u8; NUM_LIMBS], - TraceContext<'a> = &'a BitwiseOperationLookupChip, >, { - fn execute( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - row_slice: &mut [F], - ) -> Result<()> { - let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + ) -> Result<()> + where + Mem: GuestMemory, + { + let Instruction { opcode, .. } = instruction; - A::start(*state.pc, state.memory, adapter_row); - let [rs1, rs2] = A::read(state.memory, instruction, adapter_row); - let output = self.core.execute(instruction, [rs1, rs2], core_row); - A::write(state.memory, instruction, adapter_row, &output); + let shift_opcode = ShiftOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - *state.pc += DEFAULT_PC_STEP; - Ok(()) - } + let (rs1, rs2) = self.adapter.read(state.memory, instruction); - fn get_opcode_name(&self, opcode: usize) -> String { - format!( - "{:?}", - ShiftOpcode::from_usize(opcode - self.core.air.offset) - ) - } - - fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { - let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - A::fill_trace_row( - mem_helper, - self.core.bitwise_lookup_chip.as_ref(), - adapter_row, - ); - self.core.fill_trace_row(core_row); - } -} - -impl InsExecutorE1 - for ShiftCoreChip -where - Mem: GuestMemory, - F: PrimeField32, -{ - fn execute_e1( - &mut self, - state: &mut VmExecutionState, - instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, a, b, c, e, .. - } = instruction; - - let shift_opcode = ShiftOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - - let rs1_addr = b.as_canonical_u32(); - let rs1_bytes: [u8; NUM_LIMBS] = unsafe { state.memory.read(RV32_REGISTER_AS, rs1_addr) }; - - let rs2_bytes = if e.as_canonical_u32() == RV32_IMM_AS { - // Use immediate value - let imm = c.as_canonical_u32(); - // Convert imm from u32 to [u8; NUM_LIMBS] - let imm_bytes = imm.to_le_bytes(); - // TODO(ayush): remove this - let mut rs2_bytes = [0u8; NUM_LIMBS]; - rs2_bytes[..NUM_LIMBS].copy_from_slice(&imm_bytes[..NUM_LIMBS]); - rs2_bytes - } else { - // Read from register - let rs2_addr = c.as_canonical_u32(); - let rs2_bytes: [u8; NUM_LIMBS] = - unsafe { state.memory.read(RV32_REGISTER_AS, rs2_addr) }; - rs2_bytes - }; + let (rd, _, _) = run_shift::(shift_opcode, &rs1, &rs2); - // Execute the shift operation - let (rd_bytes, _, _) = - run_shift::(shift_opcode, &rs1_bytes, &rs2_bytes); - - let rd_addr = a.as_canonical_u32(); - unsafe { - state.memory.write(RV32_REGISTER_AS, rd_addr, &rd_bytes); - } + self.adapter.write(state.memory, instruction, &rd); - state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } } // Returns (result, limb_shift, bit_shift) +#[inline(always)] pub(super) fn run_shift( opcode: ShiftOpcode, x: &[u8; NUM_LIMBS], @@ -470,6 +430,7 @@ pub(super) fn run_shift( } } +#[inline(always)] fn run_shift_left( x: &[u8; NUM_LIMBS], y: &[u8; NUM_LIMBS], @@ -490,6 +451,7 @@ fn run_shift_left( (result, limb_shift, bit_shift) } +#[inline(always)] fn run_shift_right( x: &[u8; NUM_LIMBS], y: &[u8; NUM_LIMBS], @@ -518,6 +480,7 @@ fn run_shift_right( (result, limb_shift, bit_shift) } +#[inline(always)] fn get_shift(y: &[u8]) -> (usize, usize) { debug_assert!(NUM_LIMBS * LIMB_BITS <= (1 << LIMB_BITS)); // We assume `NUM_LIMBS * LIMB_BITS <= 2^LIMB_BITS` so the shift is defined diff --git a/extensions/rv32im/circuit/src/shift/tests.rs b/extensions/rv32im/circuit/src/shift/tests.rs index fc08ac829a..aaaf786bc6 100644 --- a/extensions/rv32im/circuit/src/shift/tests.rs +++ b/extensions/rv32im/circuit/src/shift/tests.rs @@ -26,9 +26,13 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::Rng; -use super::{core::run_shift, Rv32ShiftChip, Rv32ShiftStep, ShiftCoreChip, ShiftCoreCols}; +use super::{ + core::run_shift, Rv32ShiftChip, Rv32ShiftStep, ShiftCoreAir, ShiftCoreCols, ShiftStep, +}; use crate::{ - adapters::{Rv32BaseAluAdapterAir, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, + adapters::{ + Rv32BaseAluAdapterAir, Rv32BaseAluAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + }, test_utils::{generate_rv32_is_type_immediate, rv32_rand_write_register_or_imm}, }; @@ -43,20 +47,30 @@ fn create_test_chip( ) { let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let step = Rv32ShiftStep::new(ShiftCoreChip::new( - bitwise_chip.clone(), - tester.range_checker(), - ShiftOpcode::CLASS_OFFSET, - )); - let air = VmAirWrapper::new( - Rv32BaseAluAdapterAir::new( - tester.execution_bridge(), - tester.memory_bridge(), - bitwise_bus, + + let chip = Rv32ShiftChip::::new( + VmAirWrapper::new( + Rv32BaseAluAdapterAir::new( + tester.execution_bridge(), + tester.memory_bridge(), + bitwise_bus, + ), + ShiftCoreAir::new( + bitwise_bus, + tester.range_checker().bus(), + ShiftOpcode::CLASS_OFFSET, + ), ), - step.core.air, + ShiftStep::new( + Rv32BaseAluAdapterStep::new(), + bitwise_chip.clone(), + tester.range_checker().clone(), + ShiftOpcode::CLASS_OFFSET, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - let chip = NewVmChipWrapper::new(air, step, MAX_INS_CAPACITY, tester.memory_helper()); + (chip, bitwise_chip) } @@ -131,238 +145,238 @@ fn rv32_shift_sra_rand_test() { // A dummy adapter is used so memory interactions don't indirectly cause false passes. ////////////////////////////////////////////////////////////////////////////////////// -type Rv32ShiftTestChip = - VmChipWrapper, ShiftCoreChip>; - -#[derive(Clone, Copy, Default, PartialEq)] -struct ShiftPrankValues { - pub bit_shift: Option, - pub bit_multiplier_left: Option, - pub bit_multiplier_right: Option, - pub b_sign: Option, - pub bit_shift_marker: Option<[u32; LIMB_BITS]>, - pub limb_shift_marker: Option<[u32; NUM_LIMBS]>, - pub bit_shift_carry: Option<[u32; NUM_LIMBS]>, -} - -#[allow(clippy::too_many_arguments)] -fn run_rv32_shift_negative_test( - opcode: ShiftOpcode, - a: [u32; RV32_REGISTER_NUM_LIMBS], - b: [u32; RV32_REGISTER_NUM_LIMBS], - c: [u32; RV32_REGISTER_NUM_LIMBS], - prank_vals: ShiftPrankValues, - interaction_error: bool, -) { - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let (mut chip, bitwise_chip) = create_test_chip(&tester); - let range_checker_chip = tester.range_checker(); - - tester.execute( - &mut chip, - &Instruction::from_usize(opcode.global_opcode(), [0, 0, 0, 1, 1]), - ); - - let bit_shift = prank_vals - .bit_shift - .unwrap_or(c[0] % (RV32_CELL_BITS as u32)); - let bit_shift_carry = prank_vals - .bit_shift_carry - .unwrap_or(array::from_fn(|i| match opcode { - ShiftOpcode::SLL => b[i] >> ((RV32_CELL_BITS as u32) - bit_shift), - _ => b[i] % (1 << bit_shift), - })); - - range_checker_chip.clear(); - range_checker_chip.add_count(bit_shift, RV32_CELL_BITS.ilog2() as usize); - for (a_val, carry_val) in a.iter().zip(bit_shift_carry.iter()) { - range_checker_chip.add_count(*a_val, RV32_CELL_BITS); - range_checker_chip.add_count(*carry_val, bit_shift as usize); - } - - let trace_width = chip.trace_width(); - let adapter_width = BaseAir::::width(&chip.air.adapter); - - let modify_trace = |trace: &mut DenseMatrix| { - let mut values = trace.row_slice(0).to_vec(); - let cols: &mut ShiftCoreCols = - values.split_at_mut(adapter_width).1.borrow_mut(); - - cols.a = a.map(F::from_canonical_u32); - if let Some(bit_multiplier_left) = prank_vals.bit_multiplier_left { - cols.bit_multiplier_left = F::from_canonical_u32(bit_multiplier_left); - } - if let Some(bit_multiplier_right) = prank_vals.bit_multiplier_right { - cols.bit_multiplier_right = F::from_canonical_u32(bit_multiplier_right); - } - if let Some(b_sign) = prank_vals.b_sign { - cols.b_sign = F::from_canonical_u32(b_sign); - } - if let Some(bit_shift_marker) = prank_vals.bit_shift_marker { - cols.bit_shift_marker = bit_shift_marker.map(F::from_canonical_u32); - } - if let Some(limb_shift_marker) = prank_vals.limb_shift_marker { - cols.limb_shift_marker = limb_shift_marker.map(F::from_canonical_u32); - } - if let Some(bit_shift_carry) = prank_vals.bit_shift_carry { - cols.bit_shift_carry = bit_shift_carry.map(F::from_canonical_u32); - } - - *trace = RowMajorMatrix::new(values, trace_width); - }; - - disable_debug_builder(); - let tester = tester - .build() - .load_and_prank_trace(chip, modify_trace) - .load(bitwise_chip) - .finalize(); - tester.simple_test_with_expected_error(if interaction_error { - VerificationError::ChallengePhaseError - } else { - VerificationError::OodEvaluationMismatch - }); -} - -#[test] -fn rv32_shift_wrong_negative_test() { - let a = [1, 0, 0, 0]; - let b = [1, 0, 0, 0]; - let c = [1, 0, 0, 0]; - let prank_vals = Default::default(); - run_rv32_shift_negative_test(ShiftOpcode::SLL, a, b, c, prank_vals, false); - run_rv32_shift_negative_test(ShiftOpcode::SRL, a, b, c, prank_vals, false); - run_rv32_shift_negative_test(ShiftOpcode::SRA, a, b, c, prank_vals, false); -} - -#[test] -fn rv32_sll_wrong_bit_shift_negative_test() { - let a = [0, 4, 4, 4]; - let b = [1, 1, 1, 1]; - let c = [9, 10, 100, 0]; - let prank_vals = ShiftPrankValues { - bit_shift: Some(2), - bit_multiplier_left: Some(4), - bit_shift_marker: Some([0, 0, 1, 0, 0, 0, 0, 0]), - ..Default::default() - }; - run_rv32_shift_negative_test(ShiftOpcode::SLL, a, b, c, prank_vals, true); -} - -#[test] -fn rv32_sll_wrong_limb_shift_negative_test() { - let a = [0, 0, 2, 2]; - let b = [1, 1, 1, 1]; - let c = [9, 0, 0, 0]; - let prank_vals = ShiftPrankValues { - limb_shift_marker: Some([0, 0, 1, 0]), - ..Default::default() - }; - run_rv32_shift_negative_test(ShiftOpcode::SLL, a, b, c, prank_vals, true); -} - -#[test] -fn rv32_sll_wrong_bit_carry_negative_test() { - let a = [0, 510, 510, 510]; - let b = [255, 255, 255, 255]; - let c = [9, 0, 0, 0]; - let prank_vals = ShiftPrankValues { - bit_shift_carry: Some([0, 0, 0, 0]), - ..Default::default() - }; - run_rv32_shift_negative_test(ShiftOpcode::SLL, a, b, c, prank_vals, true); -} - -#[test] -fn rv32_sll_wrong_bit_mult_side_negative_test() { - let a = [128, 128, 128, 0]; - let b = [1, 1, 1, 1]; - let c = [9, 0, 0, 0]; - let prank_vals = ShiftPrankValues { - bit_multiplier_left: Some(0), - bit_multiplier_right: Some(1), - ..Default::default() - }; - run_rv32_shift_negative_test(ShiftOpcode::SLL, a, b, c, prank_vals, false); -} - -#[test] -fn rv32_srl_wrong_bit_shift_negative_test() { - let a = [0, 0, 32, 0]; - let b = [0, 0, 0, 128]; - let c = [9, 0, 0, 0]; - let prank_vals = ShiftPrankValues { - bit_shift: Some(2), - bit_multiplier_left: Some(4), - bit_shift_marker: Some([0, 0, 1, 0, 0, 0, 0, 0]), - ..Default::default() - }; - run_rv32_shift_negative_test(ShiftOpcode::SRL, a, b, c, prank_vals, false); -} - -#[test] -fn rv32_srl_wrong_limb_shift_negative_test() { - let a = [0, 64, 0, 0]; - let b = [0, 0, 0, 128]; - let c = [9, 0, 0, 0]; - let prank_vals = ShiftPrankValues { - limb_shift_marker: Some([0, 1, 0, 0]), - ..Default::default() - }; - run_rv32_shift_negative_test(ShiftOpcode::SRL, a, b, c, prank_vals, false); -} - -#[test] -fn rv32_srx_wrong_bit_mult_side_negative_test() { - let a = [0, 0, 0, 0]; - let b = [0, 0, 0, 128]; - let c = [9, 0, 0, 0]; - let prank_vals = ShiftPrankValues { - bit_multiplier_left: Some(1), - bit_multiplier_right: Some(0), - ..Default::default() - }; - run_rv32_shift_negative_test(ShiftOpcode::SRL, a, b, c, prank_vals, false); - run_rv32_shift_negative_test(ShiftOpcode::SRA, a, b, c, prank_vals, false); -} - -#[test] -fn rv32_sra_wrong_bit_shift_negative_test() { - let a = [0, 0, 224, 255]; - let b = [0, 0, 0, 128]; - let c = [9, 0, 0, 0]; - let prank_vals = ShiftPrankValues { - bit_shift: Some(2), - bit_multiplier_left: Some(4), - bit_shift_marker: Some([0, 0, 1, 0, 0, 0, 0, 0]), - ..Default::default() - }; - run_rv32_shift_negative_test(ShiftOpcode::SRA, a, b, c, prank_vals, false); -} - -#[test] -fn rv32_sra_wrong_limb_shift_negative_test() { - let a = [0, 192, 255, 255]; - let b = [0, 0, 0, 128]; - let c = [9, 0, 0, 0]; - let prank_vals = ShiftPrankValues { - limb_shift_marker: Some([0, 1, 0, 0]), - ..Default::default() - }; - run_rv32_shift_negative_test(ShiftOpcode::SRA, a, b, c, prank_vals, false); -} - -#[test] -fn rv32_sra_wrong_sign_negative_test() { - let a = [0, 0, 64, 0]; - let b = [0, 0, 0, 128]; - let c = [9, 0, 0, 0]; - let prank_vals = ShiftPrankValues { - b_sign: Some(0), - ..Default::default() - }; - run_rv32_shift_negative_test(ShiftOpcode::SRA, a, b, c, prank_vals, true); -} +// type Rv32ShiftTestChip = +// VmChipWrapper, ShiftStep>; + +// #[derive(Clone, Copy, Default, PartialEq)] +// struct ShiftPrankValues { +// pub bit_shift: Option, +// pub bit_multiplier_left: Option, +// pub bit_multiplier_right: Option, +// pub b_sign: Option, +// pub bit_shift_marker: Option<[u32; LIMB_BITS]>, +// pub limb_shift_marker: Option<[u32; NUM_LIMBS]>, +// pub bit_shift_carry: Option<[u32; NUM_LIMBS]>, +// } + +// #[allow(clippy::too_many_arguments)] +// fn run_rv32_shift_negative_test( +// opcode: ShiftOpcode, +// a: [u32; RV32_REGISTER_NUM_LIMBS], +// b: [u32; RV32_REGISTER_NUM_LIMBS], +// c: [u32; RV32_REGISTER_NUM_LIMBS], +// prank_vals: ShiftPrankValues, +// interaction_error: bool, +// ) { +// let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); +// let (mut chip, bitwise_chip) = create_test_chip(&tester); +// let range_checker_chip = tester.range_checker(); + +// tester.execute( +// &mut chip, +// &Instruction::from_usize(opcode.global_opcode(), [0, 0, 0, 1, 1]), +// ); + +// let bit_shift = prank_vals +// .bit_shift +// .unwrap_or(c[0] % (RV32_CELL_BITS as u32)); +// let bit_shift_carry = prank_vals +// .bit_shift_carry +// .unwrap_or(array::from_fn(|i| match opcode { +// ShiftOpcode::SLL => b[i] >> ((RV32_CELL_BITS as u32) - bit_shift), +// _ => b[i] % (1 << bit_shift), +// })); + +// range_checker_chip.clear(); +// range_checker_chip.add_count(bit_shift, RV32_CELL_BITS.ilog2() as usize); +// for (a_val, carry_val) in a.iter().zip(bit_shift_carry.iter()) { +// range_checker_chip.add_count(*a_val, RV32_CELL_BITS); +// range_checker_chip.add_count(*carry_val, bit_shift as usize); +// } + +// let trace_width = chip.trace_width(); +// let adapter_width = BaseAir::::width(&chip.air.adapter); + +// let modify_trace = |trace: &mut DenseMatrix| { +// let mut values = trace.row_slice(0).to_vec(); +// let cols: &mut ShiftCoreCols = +// values.split_at_mut(adapter_width).1.borrow_mut(); + +// cols.a = a.map(F::from_canonical_u32); +// if let Some(bit_multiplier_left) = prank_vals.bit_multiplier_left { +// cols.bit_multiplier_left = F::from_canonical_u32(bit_multiplier_left); +// } +// if let Some(bit_multiplier_right) = prank_vals.bit_multiplier_right { +// cols.bit_multiplier_right = F::from_canonical_u32(bit_multiplier_right); +// } +// if let Some(b_sign) = prank_vals.b_sign { +// cols.b_sign = F::from_canonical_u32(b_sign); +// } +// if let Some(bit_shift_marker) = prank_vals.bit_shift_marker { +// cols.bit_shift_marker = bit_shift_marker.map(F::from_canonical_u32); +// } +// if let Some(limb_shift_marker) = prank_vals.limb_shift_marker { +// cols.limb_shift_marker = limb_shift_marker.map(F::from_canonical_u32); +// } +// if let Some(bit_shift_carry) = prank_vals.bit_shift_carry { +// cols.bit_shift_carry = bit_shift_carry.map(F::from_canonical_u32); +// } + +// *trace = RowMajorMatrix::new(values, trace_width); +// }; + +// disable_debug_builder(); +// let tester = tester +// .build() +// .load_and_prank_trace(chip, modify_trace) +// .load(bitwise_chip) +// .finalize(); +// tester.simple_test_with_expected_error(if interaction_error { +// VerificationError::ChallengePhaseError +// } else { +// VerificationError::OodEvaluationMismatch +// }); +// } + +// #[test] +// fn rv32_shift_wrong_negative_test() { +// let a = [1, 0, 0, 0]; +// let b = [1, 0, 0, 0]; +// let c = [1, 0, 0, 0]; +// let prank_vals = Default::default(); +// run_rv32_shift_negative_test(ShiftOpcode::SLL, a, b, c, prank_vals, false); +// run_rv32_shift_negative_test(ShiftOpcode::SRL, a, b, c, prank_vals, false); +// run_rv32_shift_negative_test(ShiftOpcode::SRA, a, b, c, prank_vals, false); +// } + +// #[test] +// fn rv32_sll_wrong_bit_shift_negative_test() { +// let a = [0, 4, 4, 4]; +// let b = [1, 1, 1, 1]; +// let c = [9, 10, 100, 0]; +// let prank_vals = ShiftPrankValues { +// bit_shift: Some(2), +// bit_multiplier_left: Some(4), +// bit_shift_marker: Some([0, 0, 1, 0, 0, 0, 0, 0]), +// ..Default::default() +// }; +// run_rv32_shift_negative_test(ShiftOpcode::SLL, a, b, c, prank_vals, true); +// } + +// #[test] +// fn rv32_sll_wrong_limb_shift_negative_test() { +// let a = [0, 0, 2, 2]; +// let b = [1, 1, 1, 1]; +// let c = [9, 0, 0, 0]; +// let prank_vals = ShiftPrankValues { +// limb_shift_marker: Some([0, 0, 1, 0]), +// ..Default::default() +// }; +// run_rv32_shift_negative_test(ShiftOpcode::SLL, a, b, c, prank_vals, true); +// } + +// #[test] +// fn rv32_sll_wrong_bit_carry_negative_test() { +// let a = [0, 510, 510, 510]; +// let b = [255, 255, 255, 255]; +// let c = [9, 0, 0, 0]; +// let prank_vals = ShiftPrankValues { +// bit_shift_carry: Some([0, 0, 0, 0]), +// ..Default::default() +// }; +// run_rv32_shift_negative_test(ShiftOpcode::SLL, a, b, c, prank_vals, true); +// } + +// #[test] +// fn rv32_sll_wrong_bit_mult_side_negative_test() { +// let a = [128, 128, 128, 0]; +// let b = [1, 1, 1, 1]; +// let c = [9, 0, 0, 0]; +// let prank_vals = ShiftPrankValues { +// bit_multiplier_left: Some(0), +// bit_multiplier_right: Some(1), +// ..Default::default() +// }; +// run_rv32_shift_negative_test(ShiftOpcode::SLL, a, b, c, prank_vals, false); +// } + +// #[test] +// fn rv32_srl_wrong_bit_shift_negative_test() { +// let a = [0, 0, 32, 0]; +// let b = [0, 0, 0, 128]; +// let c = [9, 0, 0, 0]; +// let prank_vals = ShiftPrankValues { +// bit_shift: Some(2), +// bit_multiplier_left: Some(4), +// bit_shift_marker: Some([0, 0, 1, 0, 0, 0, 0, 0]), +// ..Default::default() +// }; +// run_rv32_shift_negative_test(ShiftOpcode::SRL, a, b, c, prank_vals, false); +// } + +// #[test] +// fn rv32_srl_wrong_limb_shift_negative_test() { +// let a = [0, 64, 0, 0]; +// let b = [0, 0, 0, 128]; +// let c = [9, 0, 0, 0]; +// let prank_vals = ShiftPrankValues { +// limb_shift_marker: Some([0, 1, 0, 0]), +// ..Default::default() +// }; +// run_rv32_shift_negative_test(ShiftOpcode::SRL, a, b, c, prank_vals, false); +// } + +// #[test] +// fn rv32_srx_wrong_bit_mult_side_negative_test() { +// let a = [0, 0, 0, 0]; +// let b = [0, 0, 0, 128]; +// let c = [9, 0, 0, 0]; +// let prank_vals = ShiftPrankValues { +// bit_multiplier_left: Some(1), +// bit_multiplier_right: Some(0), +// ..Default::default() +// }; +// run_rv32_shift_negative_test(ShiftOpcode::SRL, a, b, c, prank_vals, false); +// run_rv32_shift_negative_test(ShiftOpcode::SRA, a, b, c, prank_vals, false); +// } + +// #[test] +// fn rv32_sra_wrong_bit_shift_negative_test() { +// let a = [0, 0, 224, 255]; +// let b = [0, 0, 0, 128]; +// let c = [9, 0, 0, 0]; +// let prank_vals = ShiftPrankValues { +// bit_shift: Some(2), +// bit_multiplier_left: Some(4), +// bit_shift_marker: Some([0, 0, 1, 0, 0, 0, 0, 0]), +// ..Default::default() +// }; +// run_rv32_shift_negative_test(ShiftOpcode::SRA, a, b, c, prank_vals, false); +// } + +// #[test] +// fn rv32_sra_wrong_limb_shift_negative_test() { +// let a = [0, 192, 255, 255]; +// let b = [0, 0, 0, 128]; +// let c = [9, 0, 0, 0]; +// let prank_vals = ShiftPrankValues { +// limb_shift_marker: Some([0, 1, 0, 0]), +// ..Default::default() +// }; +// run_rv32_shift_negative_test(ShiftOpcode::SRA, a, b, c, prank_vals, false); +// } + +// #[test] +// fn rv32_sra_wrong_sign_negative_test() { +// let a = [0, 0, 64, 0]; +// let b = [0, 0, 0, 128]; +// let c = [9, 0, 0, 0]; +// let prank_vals = ShiftPrankValues { +// b_sign: Some(0), +// ..Default::default() +// }; +// run_rv32_shift_negative_test(ShiftOpcode::SRA, a, b, c, prank_vals, true); +// } /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS From ae9635ccb3446dec7737149bdd8889aaa2f14a05 Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Tue, 29 Apr 2025 15:25:06 -0400 Subject: [PATCH 08/49] fix(new-execution): fix some rv32im loadstore tests (#1611) - fix some loadstore tests - remove records - wrap unsafe memory read/writes into safe wrappers --------- Co-authored-by: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> --- benchmarks/execute/src/main.rs | 6 +- crates/toolchain/tests/Cargo.toml | 11 +- crates/toolchain/tests/src/utils.rs | 0 crates/vm/derive/src/lib.rs | 4 +- crates/vm/src/arch/mod.rs | 2 +- crates/vm/src/arch/testing/memory/mod.rs | 4 +- crates/vm/src/arch/vm.rs | 1 + crates/vm/src/system/memory/online.rs | 2 +- crates/vm/src/system/memory/paged_vec.rs | 2 +- .../src/system/memory/tree/public_values.rs | 1 + crates/vm/src/system/native_adapter/mod.rs | 3 +- crates/vm/src/system/public_values/core.rs | 12 +-- .../src/adapters/branch_native_adapter.rs | 4 +- .../circuit/src/adapters/convert_adapter.rs | 6 +- .../src/adapters/loadstore_native_adapter.rs | 3 +- extensions/native/circuit/src/adapters/mod.rs | 3 +- .../native/circuit/src/loadstore/mod.rs | 3 +- extensions/rv32im/circuit/src/adapters/alu.rs | 14 +-- .../rv32im/circuit/src/adapters/branch.rs | 28 ++--- .../rv32im/circuit/src/adapters/jalr.rs | 28 ++--- .../rv32im/circuit/src/adapters/loadstore.rs | 101 +++++------------- extensions/rv32im/circuit/src/adapters/mod.rs | 66 ++++++++++-- extensions/rv32im/circuit/src/adapters/mul.rs | 32 ++---- .../rv32im/circuit/src/adapters/rdwrite.rs | 16 +-- extensions/rv32im/circuit/src/auipc/core.rs | 11 +- .../rv32im/circuit/src/base_alu/tests.rs | 69 +++++++++++- .../rv32im/circuit/src/branch_eq/core.rs | 14 --- .../rv32im/circuit/src/branch_eq/tests.rs | 3 +- .../rv32im/circuit/src/branch_lt/core.rs | 17 --- .../rv32im/circuit/src/branch_lt/tests.rs | 3 +- extensions/rv32im/circuit/src/divrem/core.rs | 29 ----- extensions/rv32im/circuit/src/divrem/mod.rs | 3 +- .../rv32im/circuit/src/hintstore/mod.rs | 4 +- extensions/rv32im/circuit/src/jal_lui/core.rs | 92 ---------------- extensions/rv32im/circuit/src/jalr/core.rs | 11 -- extensions/rv32im/circuit/src/jalr/tests.rs | 3 +- .../rv32im/circuit/src/less_than/core.rs | 16 --- .../circuit/src/load_sign_extend/core.rs | 84 --------------- .../rv32im/circuit/src/loadstore/core.rs | 86 +-------------- .../rv32im/circuit/src/loadstore/mod.rs | 3 +- .../rv32im/circuit/src/loadstore/tests.rs | 78 +++++++------- extensions/rv32im/circuit/src/mul/core.rs | 12 --- extensions/rv32im/circuit/src/mul/mod.rs | 3 +- extensions/rv32im/circuit/src/mulh/core.rs | 16 --- extensions/rv32im/circuit/src/mulh/mod.rs | 3 +- extensions/rv32im/circuit/src/mulh/tests.rs | 3 +- 46 files changed, 274 insertions(+), 641 deletions(-) delete mode 100644 crates/toolchain/tests/src/utils.rs diff --git a/benchmarks/execute/src/main.rs b/benchmarks/execute/src/main.rs index aa927b45f8..9115888898 100644 --- a/benchmarks/execute/src/main.rs +++ b/benchmarks/execute/src/main.rs @@ -18,10 +18,10 @@ enum BuildProfile { // const DEFAULT_APP_CONFIG_PATH: &str = "./openvm.toml"; static AVAILABLE_PROGRAMS: &[&str] = &[ - // "fibonacci_recursive", - // "fibonacci_iterative", + "fibonacci_recursive", + "fibonacci_iterative", "quicksort", - // "bubblesort", + "bubblesort", // "pairing", // "keccak256", // "keccak256_iter", diff --git a/crates/toolchain/tests/Cargo.toml b/crates/toolchain/tests/Cargo.toml index d31d388c32..ce882932a7 100644 --- a/crates/toolchain/tests/Cargo.toml +++ b/crates/toolchain/tests/Cargo.toml @@ -8,11 +8,15 @@ homepage.workspace = true repository.workspace = true [dependencies] +openvm-build.workspace = true +openvm-transpiler.workspace = true +eyre.workspace = true +tempfile.workspace = true + +[dev-dependencies] openvm-stark-backend.workspace = true openvm-stark-sdk.workspace = true openvm-circuit = { workspace = true, features = ["test-utils"] } -openvm-transpiler.workspace = true -openvm-build.workspace = true openvm-algebra-transpiler.workspace = true openvm-bigint-circuit.workspace = true openvm-rv32im-circuit.workspace = true @@ -21,10 +25,7 @@ openvm-algebra-circuit.workspace = true openvm-ecc-guest = { workspace = true, features = ["halo2curves", "k256"] } openvm-instructions = { workspace = true } openvm-platform = { workspace = true } - -eyre.workspace = true test-case.workspace = true -tempfile.workspace = true serde = { workspace = true, features = ["alloc"] } derive_more = { workspace = true, features = ["from"] } diff --git a/crates/toolchain/tests/src/utils.rs b/crates/toolchain/tests/src/utils.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/crates/vm/derive/src/lib.rs b/crates/vm/derive/src/lib.rs index 156513a8f2..3733ce74f6 100644 --- a/crates/vm/derive/src/lib.rs +++ b/crates/vm/derive/src/lib.rs @@ -144,7 +144,7 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { impl #impl_generics ::openvm_circuit::arch::InsExecutorE1 for #name #ty_generics #where_clause { fn execute_e1( &mut self, - state: ::openvm_circuit::arch::execution::VmStateMut, + state: ::openvm_circuit::arch::VmStateMut, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction, ) -> ::openvm_circuit::arch::Result<()> where @@ -192,7 +192,7 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { impl #impl_generics ::openvm_circuit::arch::InsExecutorE1<#first_ty_generic> for #name #ty_generics { fn execute_e1( &mut self, - state: ::openvm_circuit::arch::execution::VmStateMut, + state: ::openvm_circuit::arch::VmStateMut, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction<#first_ty_generic>, ) -> ::openvm_circuit::arch::Result<()> where diff --git a/crates/vm/src/arch/mod.rs b/crates/vm/src/arch/mod.rs index 3b51b45fde..7ecb289cc9 100644 --- a/crates/vm/src/arch/mod.rs +++ b/crates/vm/src/arch/mod.rs @@ -1,7 +1,7 @@ mod config; /// Instruction execution traits and types. /// Execution bus and interface. -pub mod execution; +mod execution; /// Module for controlling VM execution flow, including segmentation and instruction execution pub mod execution_control; /// Traits and builders to compose collections of chips into a virtual machine. diff --git a/crates/vm/src/arch/testing/memory/mod.rs b/crates/vm/src/arch/testing/memory/mod.rs index 23b9c0e712..6fc5caadcd 100644 --- a/crates/vm/src/arch/testing/memory/mod.rs +++ b/crates/vm/src/arch/testing/memory/mod.rs @@ -40,7 +40,7 @@ impl MemoryTester { let controller = &mut self.controller; let t = controller.memory.timestamp(); // TODO: hack - let (t_prev, data) = if addr_space <= 2 { + let (t_prev, data) = if addr_space <= 3 { let (t_prev, data) = unsafe { controller .memory @@ -73,7 +73,7 @@ impl MemoryTester { let controller = &mut self.controller; let t = controller.memory.timestamp(); // TODO: hack - let (t_prev, data_prev) = if addr_space <= 2 { + let (t_prev, data_prev) = if addr_space <= 3 { let (t_prev, data_prev) = unsafe { controller.memory.write::( addr_space as u32, diff --git a/crates/vm/src/arch/vm.rs b/crates/vm/src/arch/vm.rs index 56f03995c4..2743768a37 100644 --- a/crates/vm/src/arch/vm.rs +++ b/crates/vm/src/arch/vm.rs @@ -336,6 +336,7 @@ where let mut segment = E1VmSegmentExecutor::new( &self.config, + // TODO(ayush): avoid clones exe.program.clone(), state.input, Some(state.memory), diff --git a/crates/vm/src/system/memory/online.rs b/crates/vm/src/system/memory/online.rs index d13885f4af..2d6201edd3 100644 --- a/crates/vm/src/system/memory/online.rs +++ b/crates/vm/src/system/memory/online.rs @@ -145,7 +145,7 @@ impl TracingMemory { let num_cells = paged_vec.bytes_capacity() / cell_size; // TMP: hardcoding for now - if i <= 3 { + if i < 3 { min_block_size.push(4); } else { min_block_size.push(1); diff --git a/crates/vm/src/system/memory/paged_vec.rs b/crates/vm/src/system/memory/paged_vec.rs index 3727bfd76e..35e4e7de03 100644 --- a/crates/vm/src/system/memory/paged_vec.rs +++ b/crates/vm/src/system/memory/paged_vec.rs @@ -303,7 +303,7 @@ impl AddressMap { debug_assert_ne!(addr_space, 0); // TODO: fix this unsafe { - if addr_space <= 2 { + if addr_space <= 3 { F::from_canonical_u8(self.get::((addr_space, ptr))) } else { self.get::((addr_space, ptr)) diff --git a/crates/vm/src/system/memory/tree/public_values.rs b/crates/vm/src/system/memory/tree/public_values.rs index 08fa6e2723..cb62c4917a 100644 --- a/crates/vm/src/system/memory/tree/public_values.rs +++ b/crates/vm/src/system/memory/tree/public_values.rs @@ -11,6 +11,7 @@ use crate::{ }, }; +pub const PUBLIC_VALUES_AS: u32 = 3; pub const PUBLIC_VALUES_ADDRESS_SPACE_OFFSET: u32 = 2; /// Merkle proof for user public values in the memory state. diff --git a/crates/vm/src/system/native_adapter/mod.rs b/crates/vm/src/system/native_adapter/mod.rs index ac86f9626b..be00ef4b7f 100644 --- a/crates/vm/src/system/native_adapter/mod.rs +++ b/crates/vm/src/system/native_adapter/mod.rs @@ -27,13 +27,12 @@ use openvm_stark_backend::{ use serde::{Deserialize, Serialize}; use serde_big_array::BigArray; +use super::memory::{online::TracingMemory, MemoryAuxColsFactory}; use crate::{ arch::{AdapterExecutorE1, AdapterTraceStep}, system::memory::{online::GuestMemory, OfflineMemory, RecordId}, }; -use super::memory::{online::TracingMemory, MemoryAuxColsFactory}; - #[repr(C)] #[derive(Debug, Serialize, Deserialize)] pub struct NativeReadRecord { diff --git a/crates/vm/src/system/public_values/core.rs b/crates/vm/src/system/public_values/core.rs index a6bcfce68e..8c29b749b7 100644 --- a/crates/vm/src/system/public_values/core.rs +++ b/crates/vm/src/system/public_values/core.rs @@ -236,8 +236,8 @@ where } } -// /// ATTENTION: If a specific public value is not provided, a default 0 will be used when generating -// /// the proof but in the perspective of constraints, it could be any value. +// /// ATTENTION: If a specific public value is not provided, a default 0 will be used when +// generating /// the proof but in the perspective of constraints, it could be any value. // pub struct PublicValuesCoreChip { // air: PublicValuesCoreAir, // // Mutex is to make the struct Sync. But it actually won't be accessed by multiple threads. @@ -245,10 +245,10 @@ where // } // impl PublicValuesCoreChip { -// /// **Note:** `max_degree` is the maximum degree of the constraint polynomials to represent the -// /// flags. If you want the overall AIR's constraint degree to be `<= max_constraint_degree`, -// /// then typically you should set `max_degree` to `max_constraint_degree - 1`. -// pub fn new(num_custom_pvs: usize, max_degree: u32) -> Self { +// /// **Note:** `max_degree` is the maximum degree of the constraint polynomials to represent +// the /// flags. If you want the overall AIR's constraint degree to be `<= +// max_constraint_degree`, /// then typically you should set `max_degree` to +// `max_constraint_degree - 1`. pub fn new(num_custom_pvs: usize, max_degree: u32) -> Self { // Self { // air: PublicValuesCoreAir::new(num_custom_pvs, max_degree), // custom_pvs: Mutex::new(vec![None; num_custom_pvs]), diff --git a/extensions/native/circuit/src/adapters/branch_native_adapter.rs b/extensions/native/circuit/src/adapters/branch_native_adapter.rs index 3eb42eec03..cf67ade35d 100644 --- a/extensions/native/circuit/src/adapters/branch_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/branch_native_adapter.rs @@ -273,8 +273,8 @@ where // for (i, x) in read_record.reads.iter().enumerate() { // let read = memory.record_by_id(x.0); -// row_slice.reads_aux[i].address = MemoryAddress::new(read.address_space, read.pointer); -// aux_cols_factory +// row_slice.reads_aux[i].address = MemoryAddress::new(read.address_space, +// read.pointer); aux_cols_factory // .generate_read_or_immediate_aux(read, &mut row_slice.reads_aux[i].read_aux); // } // } diff --git a/extensions/native/circuit/src/adapters/convert_adapter.rs b/extensions/native/circuit/src/adapters/convert_adapter.rs index 9002aba480..bdf3e46f5d 100644 --- a/extensions/native/circuit/src/adapters/convert_adapter.rs +++ b/extensions/native/circuit/src/adapters/convert_adapter.rs @@ -235,7 +235,8 @@ where // type ReadRecord = VectorReadRecord<1, READ_SIZE>; // type WriteRecord = VectorWriteRecord; // type Air = ConvertAdapterAir; -// type Interface = BasicAdapterInterface, 1, 1, READ_SIZE, WRITE_SIZE>; +// type Interface = BasicAdapterInterface, 1, 1, READ_SIZE, +// WRITE_SIZE>; // fn preprocess( // &mut self, @@ -283,7 +284,8 @@ where // memory: &OfflineMemory, // ) { // let aux_cols_factory = memory.aux_cols_factory(); -// let row_slice: &mut ConvertAdapterCols<_, READ_SIZE, WRITE_SIZE> = row_slice.borrow_mut(); +// let row_slice: &mut ConvertAdapterCols<_, READ_SIZE, WRITE_SIZE> = +// row_slice.borrow_mut(); // let read = memory.record_by_id(read_record.reads[0]); // let write = memory.record_by_id(write_record.writes[0]); diff --git a/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs b/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs index 4709fe2120..942e6ab201 100644 --- a/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs @@ -358,7 +358,8 @@ where // e, // .. // } = *instruction; -// let local_opcode = NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); +// let local_opcode = +// NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); // let read_as = d; // let read_ptr = c; diff --git a/extensions/native/circuit/src/adapters/mod.rs b/extensions/native/circuit/src/adapters/mod.rs index 25c2163b9d..ea17cb9b55 100644 --- a/extensions/native/circuit/src/adapters/mod.rs +++ b/extensions/native/circuit/src/adapters/mod.rs @@ -15,7 +15,8 @@ pub mod loadstore_native_adapter; pub mod native_vectorized_adapter; /// Atomic read operation which increments the timestamp by 1. -/// Returns `(t_prev, [ptr:BLOCK_SIZE]_4)` where `t_prev` is the timestamp of the last memory access. +/// Returns `(t_prev, [ptr:BLOCK_SIZE]_4)` where `t_prev` is the timestamp of the last memory +/// access. #[inline(always)] pub fn timed_read( memory: &mut TracingMemory, diff --git a/extensions/native/circuit/src/loadstore/mod.rs b/extensions/native/circuit/src/loadstore/mod.rs index 2bd99db992..15f85c6582 100644 --- a/extensions/native/circuit/src/loadstore/mod.rs +++ b/extensions/native/circuit/src/loadstore/mod.rs @@ -6,11 +6,10 @@ mod tests; mod core; pub use core::*; -use crate::adapters::loadstore_native_adapter::NativeLoadStoreAdapterStep; - use super::adapters::loadstore_native_adapter::{ NativeLoadStoreAdapterAir, NativeLoadStoreAdapterChip, }; +use crate::adapters::loadstore_native_adapter::NativeLoadStoreAdapterStep; pub type NativeLoadStoreAir = VmAirWrapper, NativeLoadStoreCoreAir>; diff --git a/extensions/rv32im/circuit/src/adapters/alu.rs b/extensions/rv32im/circuit/src/adapters/alu.rs index 5c75512149..eb6e868d5a 100644 --- a/extensions/rv32im/circuit/src/adapters/alu.rs +++ b/extensions/rv32im/circuit/src/adapters/alu.rs @@ -30,6 +30,7 @@ use openvm_stark_backend::{ use super::{ tracing_read, tracing_read_imm, tracing_write, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, }; +use crate::adapters::{memory_read, memory_write}; #[repr(C)] #[derive(AlignedBorrow)] @@ -196,7 +197,7 @@ impl AdapterTraceStep adapter_row.rs1_ptr = b; let rs1 = tracing_read( memory, - d.as_canonical_u32(), + RV32_REGISTER_AS, b.as_canonical_u32(), &mut adapter_row.reads_aux[0], ); @@ -207,7 +208,7 @@ impl AdapterTraceStep tracing_read( memory, - e.as_canonical_u32(), + RV32_REGISTER_AS, c.as_canonical_u32(), &mut adapter_row.reads_aux[1], ) @@ -237,7 +238,7 @@ impl AdapterTraceStep adapter_row.rd_ptr = a; tracing_write( memory, - d.as_canonical_u32(), + RV32_REGISTER_AS, a.as_canonical_u32(), data, &mut adapter_row.writes_aux, @@ -292,16 +293,15 @@ where ); let rs1: [u8; RV32_REGISTER_NUM_LIMBS] = - unsafe { memory.read(d.as_canonical_u32(), b.as_canonical_u32()) }; + memory_read(memory, RV32_REGISTER_AS, b.as_canonical_u32()); let rs2 = if e.as_canonical_u32() == RV32_REGISTER_AS { let rs2: [u8; RV32_REGISTER_NUM_LIMBS] = - unsafe { memory.read(e.as_canonical_u32(), c.as_canonical_u32()) }; + memory_read(memory, RV32_REGISTER_AS, c.as_canonical_u32()); rs2 } else { let imm = c.as_canonical_u32(); debug_assert_eq!(imm >> 24, 0); - // TODO(ayush): why this? let mut imm_le = imm.to_le_bytes(); imm_le[3] = imm_le[2]; imm_le @@ -319,6 +319,6 @@ where debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - unsafe { memory.write(d.as_canonical_u32(), a.as_canonical_u32(), rd) }; + memory_write(memory, d.as_canonical_u32(), a.as_canonical_u32(), rd); } } diff --git a/extensions/rv32im/circuit/src/adapters/branch.rs b/extensions/rv32im/circuit/src/adapters/branch.rs index 12f367ae68..76d15bb229 100644 --- a/extensions/rv32im/circuit/src/adapters/branch.rs +++ b/extensions/rv32im/circuit/src/adapters/branch.rs @@ -8,7 +8,7 @@ use openvm_circuit::{ system::memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols}, online::{GuestMemory, TracingMemory}, - MemoryAddress, MemoryAuxColsFactory, RecordId, + MemoryAddress, MemoryAuxColsFactory, }, }; use openvm_circuit_primitives_derive::AlignedBorrow; @@ -22,24 +22,8 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use crate::adapters::tracing_read; - use super::RV32_REGISTER_NUM_LIMBS; - -#[repr(C)] -#[derive(Debug, Serialize, Deserialize)] -pub struct Rv32BranchReadRecord { - /// Read register value from address space d = 1 - pub rs1: RecordId, - /// Read register value from address space e = 1 - pub rs2: RecordId, -} - -#[repr(C)] -#[derive(Debug, Serialize, Deserialize)] -pub struct Rv32BranchWriteRecord { - pub from_state: ExecutionState, -} +use crate::adapters::{memory_read, tracing_read}; #[repr(C)] #[derive(AlignedBorrow)] @@ -159,14 +143,14 @@ where adapter_row.rs1_ptr = a; let rs1 = tracing_read( memory, - d.as_canonical_u32(), + RV32_REGISTER_AS, a.as_canonical_u32(), &mut adapter_row.reads_aux[0], ); adapter_row.rs2_ptr = b; let rs2 = tracing_read( memory, - e.as_canonical_u32(), + RV32_REGISTER_AS, b.as_canonical_u32(), &mut adapter_row.reads_aux[1], ); @@ -221,9 +205,9 @@ where debug_assert_eq!(e.as_canonical_u32(), RV32_REGISTER_AS); let rs1: [u8; RV32_REGISTER_NUM_LIMBS] = - unsafe { memory.read(d.as_canonical_u32(), a.as_canonical_u32()) }; + memory_read(memory, RV32_REGISTER_AS, a.as_canonical_u32()); let rs2: [u8; RV32_REGISTER_NUM_LIMBS] = - unsafe { memory.read(e.as_canonical_u32(), b.as_canonical_u32()) }; + memory_read(memory, RV32_REGISTER_AS, b.as_canonical_u32()); (rs1, rs2) } diff --git a/extensions/rv32im/circuit/src/adapters/jalr.rs b/extensions/rv32im/circuit/src/adapters/jalr.rs index ddea7e8212..614b15d926 100644 --- a/extensions/rv32im/circuit/src/adapters/jalr.rs +++ b/extensions/rv32im/circuit/src/adapters/jalr.rs @@ -8,7 +8,7 @@ use openvm_circuit::{ system::memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, online::{GuestMemory, TracingMemory}, - MemoryAddress, MemoryAuxColsFactory, RecordId, + MemoryAddress, MemoryAuxColsFactory, }, }; use openvm_circuit_primitives::utils::not; @@ -23,22 +23,8 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use crate::adapters::{tracing_read, tracing_write}; - use super::RV32_REGISTER_NUM_LIMBS; - -#[repr(C)] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Rv32JalrReadRecord { - pub rs1: RecordId, -} - -#[repr(C)] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Rv32JalrWriteRecord { - pub from_state: ExecutionState, - pub rd_id: Option, -} +use crate::adapters::{memory_read, memory_write, tracing_read, tracing_write}; #[repr(C)] #[derive(Debug, Clone, AlignedBorrow)] @@ -189,7 +175,7 @@ where adapter_row.rs1_ptr = b; tracing_read( memory, - d.as_canonical_u32(), + RV32_REGISTER_AS, b.as_canonical_u32(), &mut adapter_row.rs1_aux_cols, ) @@ -217,7 +203,7 @@ where adapter_row.rd_ptr = a; tracing_write( memory, - d.as_canonical_u32(), + RV32_REGISTER_AS, a.as_canonical_u32(), data, &mut adapter_row.rd_aux_cols, @@ -265,7 +251,7 @@ where debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); let rs1: [u8; RV32_REGISTER_NUM_LIMBS] = - unsafe { memory.read(d.as_canonical_u32(), b.as_canonical_u32()) }; + memory_read(memory, RV32_REGISTER_AS, b.as_canonical_u32()); rs1 } @@ -282,9 +268,7 @@ where debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); if *enabled != F::ZERO { - unsafe { - memory.write(d.as_canonical_u32(), a.as_canonical_u32(), data); - } + memory_write(memory, RV32_REGISTER_AS, a.as_canonical_u32(), data); } } } diff --git a/extensions/rv32im/circuit/src/adapters/loadstore.rs b/extensions/rv32im/circuit/src/adapters/loadstore.rs index 0e6f45c368..221d8b3272 100644 --- a/extensions/rv32im/circuit/src/adapters/loadstore.rs +++ b/extensions/rv32im/circuit/src/adapters/loadstore.rs @@ -12,7 +12,7 @@ use openvm_circuit::{ system::memory::{ offline_checker::{MemoryBaseAuxCols, MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, online::{GuestMemory, TracingMemory}, - MemoryAddress, MemoryAuxColsFactory, RecordId, + MemoryAddress, MemoryAuxColsFactory, }, }; use openvm_circuit_primitives::{ @@ -35,7 +35,9 @@ use openvm_stark_backend::{ use serde::{Deserialize, Serialize}; use super::RV32_REGISTER_NUM_LIMBS; -use crate::adapters::{tracing_read, tracing_write_with_base_aux, RV32_CELL_BITS}; +use crate::adapters::{ + memory_read, memory_write, tracing_read, tracing_write_with_base_aux, RV32_CELL_BITS, +}; /// LoadStore Adapter handles all memory and register operations, so it must be aware /// of the instruction type, specifically whether it is a load or store @@ -60,22 +62,6 @@ pub struct LoadStoreInstruction { pub store_shift_amount: T, } -/// The LoadStoreAdapter separates Runtime and Air AdapterInterfaces. -/// This is necessary because `prev_data` should be owned by the core chip and sent to the adapter, -/// and it must have an AB::Var type in AIR as to satisfy the memory_bridge interface. -/// This is achieved by having different types for reads and writes in Air AdapterInterface. -/// This method ensures that there are no modifications to the global interfaces. -/// -/// Here 2 reads represent read_data and prev_data, -/// The second element of the tuple in Reads is the shift amount needed to be passed to the core -/// chip Getting the intermediate pointer is completely internal to the adapter and shouldn't be a -/// part of the AdapterInterface -pub struct Rv32LoadStoreAdapterRuntimeInterface(PhantomData); -impl VmAdapterInterface for Rv32LoadStoreAdapterRuntimeInterface { - type Reads = ([[T; RV32_REGISTER_NUM_LIMBS]; 2], T); - type Writes = [[T; RV32_REGISTER_NUM_LIMBS]; 1]; - type ProcessedInstruction = (); -} pub struct Rv32LoadStoreAdapterAirInterface(PhantomData); /// Using AB::Var for prev_data and AB::Expr for read_data @@ -88,34 +74,6 @@ impl VmAdapterInterface for Rv32LoadStoreAdapt type ProcessedInstruction = LoadStoreInstruction; } -#[repr(C)] -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct Rv32LoadStoreReadRecord { - pub rs1_record: RecordId, - /// This will be a read from a register in case of Stores and a read from RISC-V memory in case - /// of Loads. - pub read: RecordId, - pub rs1_ptr: F, - pub imm: F, - pub imm_sign: F, - pub mem_as: F, - pub mem_ptr_limbs: [u32; 2], - pub shift_amount: u32, -} - -#[repr(C)] -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct Rv32LoadStoreWriteRecord { - /// This will be a write to a register in case of Load and a write to RISC-V memory in case of - /// Stores. For better struct packing, `RecordId(usize::MAX)` is used to indicate that - /// there is no write. - pub write_id: RecordId, - pub from_state: ExecutionState, - pub rd_rs2_ptr: F, -} - #[repr(C)] #[derive(Debug, Clone, AlignedBorrow)] pub struct Rv32LoadStoreAdapterCols { @@ -393,7 +351,7 @@ where adapter_row.rs1_ptr = b; let rs1 = tracing_read( memory, - d.as_canonical_u32(), + RV32_REGISTER_AS, b.as_canonical_u32(), &mut adapter_row.rs1_aux_cols, ); @@ -423,7 +381,7 @@ where ), STOREW | STOREH | STOREB => tracing_read( memory, - d.as_canonical_u32(), + RV32_REGISTER_AS, a.as_canonical_u32(), &mut adapter_row.read_data_aux, ), @@ -431,14 +389,10 @@ where // We need to keep values of some cells to keep them unchanged when writing to those cells let prev_data = match local_opcode { - STOREW | STOREH | STOREB => unsafe { - memory.data().read(e.as_canonical_u32(), ptr_val) - }, - LOADW | LOADB | LOADH | LOADBU | LOADHU => unsafe { - memory - .data() - .read(d.as_canonical_u32(), a.as_canonical_u32()) - }, + STOREW | STOREH | STOREB => memory_read(memory.data(), e.as_canonical_u32(), ptr_val), + LOADW | LOADB | LOADH | LOADBU | LOADHU => { + memory_read(memory.data(), d.as_canonical_u32(), a.as_canonical_u32()) + } }; adapter_row @@ -505,7 +459,6 @@ where let ptr = mem_ptr_limbs[0] + mem_ptr_limbs[1] * (1 << (RV32_CELL_BITS * 2)); let ptr = ptr & 0xfffffffc; - adapter_row.rd_rs2_ptr = F::from_canonical_u32(ptr); tracing_write_with_base_aux( memory, e.as_canonical_u32(), @@ -515,16 +468,16 @@ where ); } LOADW | LOADB | LOADH | LOADBU | LOADHU => { - adapter_row.rd_rs2_ptr = a; tracing_write_with_base_aux( memory, - d.as_canonical_u32(), + RV32_REGISTER_AS, a.as_canonical_u32(), data, &mut adapter_row.write_base_aux, ); } }; + adapter_row.rd_rs2_ptr = a; } else { memory.increment_timestamp(); }; @@ -609,7 +562,7 @@ where ); let rs1_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = - unsafe { memory.read(d.as_canonical_u32(), b.as_canonical_u32()) }; + memory_read(memory, d.as_canonical_u32(), b.as_canonical_u32()); let rs1_val = u32::from_le_bytes(rs1_bytes); let imm = c.as_canonical_u32(); @@ -627,20 +580,18 @@ where let ptr_val = ptr_val - shift_amount; // aligned ptr let read_data: [u8; RV32_REGISTER_NUM_LIMBS] = match local_opcode { - LOADW | LOADB | LOADH | LOADBU | LOADHU => unsafe { - memory.read(e.as_canonical_u32(), ptr_val) - }, - STOREW | STOREH | STOREB => unsafe { - memory.read(d.as_canonical_u32(), a.as_canonical_u32()) - }, + LOADW | LOADB | LOADH | LOADBU | LOADHU => { + memory_read(memory, e.as_canonical_u32(), ptr_val) + } + STOREW | STOREH | STOREB => memory_read(memory, RV32_REGISTER_AS, a.as_canonical_u32()), }; // For stores, we need the previous memory content to preserve unchanged bytes let prev_data: [u8; RV32_REGISTER_NUM_LIMBS] = match local_opcode { - STOREW | STOREH | STOREB => unsafe { memory.read(e.as_canonical_u32(), ptr_val) }, - LOADW | LOADB | LOADH | LOADBU | LOADHU => unsafe { - memory.read(d.as_canonical_u32(), a.as_canonical_u32()) - }, + STOREW | STOREH | STOREB => memory_read(memory, e.as_canonical_u32(), ptr_val), + LOADW | LOADB | LOADH | LOADBU | LOADHU => { + memory_read(memory, RV32_REGISTER_AS, a.as_canonical_u32()) + } }; ((prev_data, read_data), shift_amount) @@ -671,7 +622,7 @@ where ); let rs1_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = - unsafe { memory.read(d.as_canonical_u32(), b.as_canonical_u32()) }; + memory_read(memory, RV32_REGISTER_AS, b.as_canonical_u32()); let rs1_val = u32::from_le_bytes(rs1_bytes); let imm = c.as_canonical_u32(); @@ -695,11 +646,11 @@ where match local_opcode { STOREW | STOREH | STOREB => { let ptr = mem_ptr_limbs[0] + mem_ptr_limbs[1] * (1 << (RV32_CELL_BITS * 2)); - unsafe { memory.write(e.as_canonical_u32(), ptr & 0xfffffffc, data) }; + memory_write(memory, e.as_canonical_u32(), ptr & 0xfffffffc, data); + } + LOADW | LOADB | LOADH | LOADBU | LOADHU => { + memory_write(memory, RV32_REGISTER_AS, a.as_canonical_u32(), data); } - LOADW | LOADB | LOADH | LOADBU | LOADHU => unsafe { - memory.write(d.as_canonical_u32(), a.as_canonical_u32(), data); - }, } } } diff --git a/extensions/rv32im/circuit/src/adapters/mod.rs b/extensions/rv32im/circuit/src/adapters/mod.rs index e4370e547b..c2c3c2f768 100644 --- a/extensions/rv32im/circuit/src/adapters/mod.rs +++ b/extensions/rv32im/circuit/src/adapters/mod.rs @@ -2,7 +2,8 @@ use std::ops::Mul; use openvm_circuit::system::memory::{ offline_checker::{MemoryBaseAuxCols, MemoryReadAuxCols, MemoryWriteAuxCols}, - online::TracingMemory, + online::{GuestMemory, TracingMemory}, + tree::public_values::PUBLIC_VALUES_AS, MemoryController, RecordId, }; use openvm_instructions::riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}; @@ -51,18 +52,63 @@ pub fn decompose(value: u32) -> [F; RV32_REGISTER_NUM_LIMBS] { }) } +#[inline(always)] +pub fn memory_read(memory: &Mem, address_space: u32, ptr: u32) -> [u8; RV32_REGISTER_NUM_LIMBS] +where + Mem: GuestMemory, +{ + debug_assert!( + address_space == RV32_REGISTER_AS + || address_space == RV32_MEMORY_AS + || address_space == PUBLIC_VALUES_AS, + ); + + // TODO(ayush): PUBLIC_VALUES_AS safety? + // SAFETY: + // - address space `RV32_REGISTER_AS` and `RV32_MEMORY_AS` will always have cell type `u8` and + // minimum alignment of `RV32_REGISTER_NUM_LIMBS` + unsafe { memory.read::(address_space, ptr) } +} + +#[inline(always)] +pub fn memory_write( + memory: &mut Mem, + address_space: u32, + ptr: u32, + data: &[u8; RV32_REGISTER_NUM_LIMBS], +) where + Mem: GuestMemory, +{ + debug_assert!( + address_space == RV32_REGISTER_AS + || address_space == RV32_MEMORY_AS + || address_space == PUBLIC_VALUES_AS + ); + + // TODO(ayush): PUBLIC_VALUES_AS safety? + // SAFETY: + // - address space `RV32_REGISTER_AS` and `RV32_MEMORY_AS` will always have cell type `u8` and + // minimum alignment of `RV32_REGISTER_NUM_LIMBS` + unsafe { memory.write::(address_space, ptr, data) } +} + /// Atomic read operation which increments the timestamp by 1. -/// Returns `(t_prev, [ptr:4]_{address_space})` where `t_prev` is the timestamp of the last memory access. +/// Returns `(t_prev, [ptr:4]_{address_space})` where `t_prev` is the timestamp of the last memory +/// access. #[inline(always)] pub fn timed_read( memory: &mut TracingMemory, address_space: u32, ptr: u32, ) -> (u32, [u8; RV32_REGISTER_NUM_LIMBS]) { - debug_assert!(address_space == RV32_REGISTER_AS || address_space == RV32_MEMORY_AS); + debug_assert!( + address_space == RV32_REGISTER_AS + || address_space == RV32_MEMORY_AS + || address_space == PUBLIC_VALUES_AS + ); // SAFETY: - // - address space `RV32_REGISTER_AS` and `RV32_MEMORY_ASwill always have cell type `u8` and + // - address space `RV32_REGISTER_AS` and `RV32_MEMORY_AS` will always have cell type `u8` and // minimum alignment of `RV32_REGISTER_NUM_LIMBS` unsafe { memory.read::(address_space, ptr) @@ -76,8 +122,12 @@ pub fn timed_write( ptr: u32, val: &[u8; RV32_REGISTER_NUM_LIMBS], ) -> (u32, [u8; RV32_REGISTER_NUM_LIMBS]) { - // TODO(ayush): should this allow hint address space - debug_assert!(address_space == RV32_REGISTER_AS || address_space == RV32_MEMORY_AS); + // TODO(ayush): should this allow public values address space + debug_assert!( + address_space == RV32_REGISTER_AS + || address_space == RV32_MEMORY_AS + || address_space == PUBLIC_VALUES_AS + ); // SAFETY: // - address space `RV32_REGISTER_AS` and `RV32_MEMORY_ASwill always have cell type `u8` and @@ -104,7 +154,6 @@ pub fn tracing_read( where F: PrimeField32, { - // TODO(ayush): should this allow hint address space let (t_prev, data) = timed_read(memory, address_space, ptr); aux_cols.set_prev(F::from_canonical_u32(t_prev)); data @@ -118,7 +167,8 @@ pub fn tracing_write( address_space: u32, ptr: u32, val: &[u8; RV32_REGISTER_NUM_LIMBS], - aux_cols: &mut MemoryWriteAuxCols, /* TODO[jpw]: switch to raw u8 + aux_cols: &mut MemoryWriteAuxCols, /* TODO[jpw]: switch to raw + * u8 * buffer */ ) where F: PrimeField32, diff --git a/extensions/rv32im/circuit/src/adapters/mul.rs b/extensions/rv32im/circuit/src/adapters/mul.rs index 84feaea7b8..c6f6b998ae 100644 --- a/extensions/rv32im/circuit/src/adapters/mul.rs +++ b/extensions/rv32im/circuit/src/adapters/mul.rs @@ -8,7 +8,7 @@ use openvm_circuit::{ system::memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, online::{GuestMemory, TracingMemory}, - MemoryAddress, MemoryAuxColsFactory, RecordId, + MemoryAddress, MemoryAuxColsFactory, }, }; use openvm_circuit_primitives_derive::AlignedBorrow; @@ -23,23 +23,7 @@ use openvm_stark_backend::{ use serde::{Deserialize, Serialize}; use super::{tracing_write, RV32_REGISTER_NUM_LIMBS}; -use crate::adapters::tracing_read; - -#[repr(C)] -#[derive(Debug, Serialize, Deserialize)] -pub struct Rv32MultReadRecord { - /// Reads from operand registers - pub rs1: RecordId, - pub rs2: RecordId, -} - -#[repr(C)] -#[derive(Debug, Serialize, Deserialize)] -pub struct Rv32MultWriteRecord { - pub from_state: ExecutionState, - /// Write to destination register - pub rd_id: RecordId, -} +use crate::adapters::{memory_read, memory_write, tracing_read}; #[repr(C)] #[derive(AlignedBorrow)] @@ -175,14 +159,14 @@ where adapter_row.rs1_ptr = b; let rs1 = tracing_read( memory, - d.as_canonical_u32(), + RV32_REGISTER_AS, b.as_canonical_u32(), &mut adapter_row.reads_aux[0], ); adapter_row.rs2_ptr = c; let rs2 = tracing_read( memory, - d.as_canonical_u32(), + RV32_REGISTER_AS, c.as_canonical_u32(), &mut adapter_row.reads_aux[1], ); @@ -207,7 +191,7 @@ where adapter_row.rd_ptr = a; tracing_write( memory, - d.as_canonical_u32(), + RV32_REGISTER_AS, a.as_canonical_u32(), data, &mut adapter_row.writes_aux, @@ -253,9 +237,9 @@ where debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); let rs1: [u8; RV32_REGISTER_NUM_LIMBS] = - unsafe { memory.read(d.as_canonical_u32(), b.as_canonical_u32()) }; + memory_read(memory, RV32_REGISTER_AS, b.as_canonical_u32()); let rs2: [u8; RV32_REGISTER_NUM_LIMBS] = - unsafe { memory.read(d.as_canonical_u32(), c.as_canonical_u32()) }; + memory_read(memory, RV32_REGISTER_AS, c.as_canonical_u32()); (rs1, rs2) } @@ -269,6 +253,6 @@ where debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - unsafe { memory.write(d.as_canonical_u32(), a.as_canonical_u32(), rd) }; + memory_write(memory, RV32_REGISTER_AS, a.as_canonical_u32(), rd); } } diff --git a/extensions/rv32im/circuit/src/adapters/rdwrite.rs b/extensions/rv32im/circuit/src/adapters/rdwrite.rs index 247f0b854c..d3736942cd 100644 --- a/extensions/rv32im/circuit/src/adapters/rdwrite.rs +++ b/extensions/rv32im/circuit/src/adapters/rdwrite.rs @@ -8,7 +8,7 @@ use openvm_circuit::{ system::memory::{ offline_checker::{MemoryBridge, MemoryWriteAuxCols}, online::{GuestMemory, TracingMemory}, - MemoryAddress, MemoryAuxColsFactory, RecordId, + MemoryAddress, MemoryAuxColsFactory, }, }; use openvm_circuit_primitives::utils::not; @@ -23,16 +23,8 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use crate::adapters::tracing_write; - use super::RV32_REGISTER_NUM_LIMBS; - -#[repr(C)] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Rv32RdWriteWriteRecord { - pub from_state: ExecutionState, - pub rd_id: Option, -} +use crate::adapters::{memory_write, tracing_write}; #[repr(C)] #[derive(Debug, Clone, AlignedBorrow)] @@ -243,7 +235,7 @@ where adapter_row.rd_ptr = a; tracing_write( memory, - d.as_canonical_u32(), + RV32_REGISTER_AS, a.as_canonical_u32(), data, &mut adapter_row.rd_aux_cols, @@ -288,7 +280,7 @@ where debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - unsafe { memory.write(d.as_canonical_u32(), a.as_canonical_u32(), rd) }; + memory_write(memory, RV32_REGISTER_AS, a.as_canonical_u32(), rd); } } diff --git a/extensions/rv32im/circuit/src/auipc/core.rs b/extensions/rv32im/circuit/src/auipc/core.rs index e281b5b81c..1ac4641ad2 100644 --- a/extensions/rv32im/circuit/src/auipc/core.rs +++ b/extensions/rv32im/circuit/src/auipc/core.rs @@ -193,14 +193,6 @@ where } } -#[repr(C)] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Rv32AuipcCoreRecord { - pub imm_limbs: [F; RV32_REGISTER_NUM_LIMBS - 1], - pub pc_limbs: [F; RV32_REGISTER_NUM_LIMBS - 2], - pub rd_data: [F; RV32_REGISTER_NUM_LIMBS], -} - pub struct Rv32AuipcStep { adapter: A, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, @@ -302,9 +294,8 @@ where self.bitwise_lookup_chip .request_range(imm_limbs[2] as u32, pc_limbs[1] as u32); - let msl_shift = RV32_REGISTER_NUM_LIMBS * RV32_CELL_BITS - PC_BITS; self.bitwise_lookup_chip - .request_range(pc_limbs[2] as u32, (pc_limbs[3] as u32) << msl_shift); + .request_range(pc_limbs[2] as u32, pc_limbs[3] as u32); } } diff --git a/extensions/rv32im/circuit/src/base_alu/tests.rs b/extensions/rv32im/circuit/src/base_alu/tests.rs index cc20c50181..d5547679db 100644 --- a/extensions/rv32im/circuit/src/base_alu/tests.rs +++ b/extensions/rv32im/circuit/src/base_alu/tests.rs @@ -5,9 +5,12 @@ use openvm_circuit::{ testing::{ test_adapter::TestAdapterAir, TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS, }, - AdapterTraceStep, NewVmChipWrapper, VmAirWrapper, VmChipWrapper, + AdapterExecutorE1, AdapterTraceStep, NewVmChipWrapper, VmAirWrapper, VmChipWrapper, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::{online::TracingMemory, MemoryAuxColsFactory}, utils::generate_long_number, }; use openvm_circuit_primitives::bitwise_op_lookup::{ @@ -479,6 +482,68 @@ where } } +impl AdapterExecutorE1 for Rv32BaseAluAdapterTestStep +where + F: PrimeField32, +{ + type ReadData = as AdapterExecutorE1>::ReadData; + type WriteData = as AdapterExecutorE1>::WriteData; + + #[inline(always)] + fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData + where + Mem: GuestMemory, + { + let &Instruction { b, c, d, e, .. } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + debug_assert!( + e.as_canonical_u32() == RV32_REGISTER_AS || e.as_canonical_u32() == RV32_IMM_AS + ); + + let rs1: [u8; RV32_REGISTER_NUM_LIMBS] = + unsafe { memory.read(d.as_canonical_u32(), b.as_canonical_u32()) }; + + let rs2 = if e.as_canonical_u32() == RV32_REGISTER_AS { + let rs2: [u8; RV32_REGISTER_NUM_LIMBS] = + unsafe { memory.read(e.as_canonical_u32(), c.as_canonical_u32()) }; + rs2 + } else { + // Here we use values that can overflow + let imm = c.as_canonical_u32(); + + debug_assert_eq!(imm >> 24, 0); + + let mask1 = (1 << 9) - 1; // Allowing overflow + let mask2 = (1 << 3) - 2; // Allowing overflow + + let mut imm_le = [ + (imm & mask1) as u8, + ((imm >> 8) & mask2) as u8, + (imm >> 16) as u8, + (imm >> 16) as u8, + ]; + imm_le[3] = imm_le[2]; + imm_le + }; + + (rs1, rs2) + } + + #[inline(always)] + fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) + where + Mem: GuestMemory, + { + as AdapterExecutorE1>::write( + &self.0, + memory, + instruction, + data, + ); + } +} + // #[test] // fn rv32_alu_adapter_unconstrained_imm_limb_test() { // let mut rng = create_seeded_rng(); diff --git a/extensions/rv32im/circuit/src/branch_eq/core.rs b/extensions/rv32im/circuit/src/branch_eq/core.rs index a6d1a71af7..7d3b44c913 100644 --- a/extensions/rv32im/circuit/src/branch_eq/core.rs +++ b/extensions/rv32im/circuit/src/branch_eq/core.rs @@ -140,20 +140,6 @@ where } } -#[repr(C)] -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct BranchEqualCoreRecord { - #[serde(with = "BigArray")] - pub a: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub b: [T; NUM_LIMBS], - pub cmp_result: T, - pub imm: T, - pub diff_inv_val: T, - pub diff_idx: usize, - pub opcode: BranchEqualOpcode, -} - #[derive(Debug)] pub struct BranchEqualStep { adapter: A, diff --git a/extensions/rv32im/circuit/src/branch_eq/tests.rs b/extensions/rv32im/circuit/src/branch_eq/tests.rs index 8ee4b015e1..3097f4ba17 100644 --- a/extensions/rv32im/circuit/src/branch_eq/tests.rs +++ b/extensions/rv32im/circuit/src/branch_eq/tests.rs @@ -276,7 +276,8 @@ fn rv32_bne_rand_test() { // #[test] // fn execute_pc_increment_sanity_test() { -// let core = BranchEqualStep::::new(BranchEqualOpcode::CLASS_OFFSET, 4); +// let core = BranchEqualStep::::new(BranchEqualOpcode::CLASS_OFFSET, +// 4); // let mut instruction = Instruction:: { // opcode: BranchEqualOpcode::BEQ.global_opcode(), diff --git a/extensions/rv32im/circuit/src/branch_lt/core.rs b/extensions/rv32im/circuit/src/branch_lt/core.rs index 01d1001336..f5542102d8 100644 --- a/extensions/rv32im/circuit/src/branch_lt/core.rs +++ b/extensions/rv32im/circuit/src/branch_lt/core.rs @@ -193,23 +193,6 @@ where } } -#[repr(C)] -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct BranchLessThanCoreRecord { - #[serde(with = "BigArray")] - pub a: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub b: [T; NUM_LIMBS], - pub cmp_result: T, - pub cmp_lt: T, - pub imm: T, - pub a_msb_f: T, - pub b_msb_f: T, - pub diff_val: T, - pub diff_idx: usize, - pub opcode: BranchLessThanOpcode, -} - pub struct BranchLessThanStep { adapter: A, pub offset: usize, diff --git a/extensions/rv32im/circuit/src/branch_lt/tests.rs b/extensions/rv32im/circuit/src/branch_lt/tests.rs index f6ab9a0b93..205d029dc5 100644 --- a/extensions/rv32im/circuit/src/branch_lt/tests.rs +++ b/extensions/rv32im/circuit/src/branch_lt/tests.rs @@ -221,7 +221,8 @@ fn rv32_bgeu_rand_test() { // let trace_width = chip.trace_width(); // let adapter_width = BaseAir::::width(chip.adapter.air()); // let ge_opcode = opcode == BranchLessThanOpcode::BGE || opcode == BranchLessThanOpcode::BGEU; -// let (_, _, a_sign, b_sign) = run_cmp::(opcode, &a, &b); +// let (_, _, a_sign, b_sign) = run_cmp::(opcode, &a, +// &b); // if prank_vals != BranchLessThanPrankValues::default() { // debug_assert!(prank_vals.diff_val.is_some()); diff --git a/extensions/rv32im/circuit/src/divrem/core.rs b/extensions/rv32im/circuit/src/divrem/core.rs index 8dcbcabe61..05826cc0f3 100644 --- a/extensions/rv32im/circuit/src/divrem/core.rs +++ b/extensions/rv32im/circuit/src/divrem/core.rs @@ -348,35 +348,6 @@ where } } -#[repr(C)] -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(bound = "T: Serialize + DeserializeOwned")] -pub struct DivRemCoreRecord { - #[serde(with = "BigArray")] - pub b: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub c: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub q: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub r: [T; NUM_LIMBS], - pub zero_divisor: T, - pub r_zero: T, - pub b_sign: T, - pub c_sign: T, - pub q_sign: T, - pub sign_xor: T, - pub c_sum_inv: T, - pub r_sum_inv: T, - #[serde(with = "BigArray")] - pub r_prime: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub r_inv: [T; NUM_LIMBS], - pub lt_diff_val: T, - pub lt_diff_idx: usize, - pub opcode: DivRemOpcode, -} - #[derive(Debug, Eq, PartialEq)] #[repr(u8)] pub(super) enum DivRemCoreSpecialCase { diff --git a/extensions/rv32im/circuit/src/divrem/mod.rs b/extensions/rv32im/circuit/src/divrem/mod.rs index 15fd1ae6ea..ab75cebf18 100644 --- a/extensions/rv32im/circuit/src/divrem/mod.rs +++ b/extensions/rv32im/circuit/src/divrem/mod.rs @@ -1,8 +1,7 @@ use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; -use crate::adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep}; - use super::adapters::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; +use crate::adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep}; mod core; pub use core::*; diff --git a/extensions/rv32im/circuit/src/hintstore/mod.rs b/extensions/rv32im/circuit/src/hintstore/mod.rs index 18ca880c99..3b643fa5ed 100644 --- a/extensions/rv32im/circuit/src/hintstore/mod.rs +++ b/extensions/rv32im/circuit/src/hintstore/mod.rs @@ -580,8 +580,8 @@ where // std::array::from_fn(|_| streams.hint_stream.pop_front().unwrap()); // // let (write, _) = memory.write( // // e, - // // F::from_canonical_u32(mem_ptr + (RV32_REGISTER_NUM_LIMBS as u32 * word_index)), - // // &tmp_convert_to_u8s(data), + // // F::from_canonical_u32(mem_ptr + (RV32_REGISTER_NUM_LIMBS as u32 * + // word_index)), // &tmp_convert_to_u8s(data), // // ); // // record.hints.push((data, write)); // } diff --git a/extensions/rv32im/circuit/src/jal_lui/core.rs b/extensions/rv32im/circuit/src/jal_lui/core.rs index f26488254e..48d74d5706 100644 --- a/extensions/rv32im/circuit/src/jal_lui/core.rs +++ b/extensions/rv32im/circuit/src/jal_lui/core.rs @@ -145,16 +145,6 @@ where } } -#[repr(C)] -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct Rv32JalLuiCoreRecord { - pub rd_data: [F; RV32_REGISTER_NUM_LIMBS], - pub imm: F, - pub is_jal: bool, - pub is_lui: bool, -} - pub struct Rv32JalLuiStep { adapter: A, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, @@ -296,88 +286,6 @@ where } } -// impl> VmCoreChip for Rv32JalLuiCoreChip -// where -// I::Writes: From<[[F; RV32_REGISTER_NUM_LIMBS]; 1]>, -// { -// type Record = Rv32JalLuiCoreRecord; -// type Air = Rv32JalLuiCoreAir; - -// #[allow(clippy::type_complexity)] -// fn execute_instruction( -// &self, -// instruction: &Instruction, -// from_pc: u32, -// _reads: I::Reads, -// ) -> Result<(AdapterRuntimeContext, Self::Record)> { -// let local_opcode = Rv32JalLuiOpcode::from_usize( -// instruction -// .opcode -// .local_opcode_idx(Rv32JalLuiOpcode::CLASS_OFFSET), -// ); -// let imm = instruction.c; - -// let signed_imm = match local_opcode { -// JAL => { -// // Note: signed_imm is a signed integer and imm is a field element -// (imm + F::from_canonical_u32(1 << (RV_J_TYPE_IMM_BITS - 1))).as_canonical_u32() -// as i32 -// - (1 << (RV_J_TYPE_IMM_BITS - 1)) -// } -// LUI => imm.as_canonical_u32() as i32, -// }; -// let (to_pc, rd_data) = run_jal_lui(local_opcode, from_pc, signed_imm); - -// for i in 0..(RV32_REGISTER_NUM_LIMBS / 2) { -// self.bitwise_lookup_chip -// .request_range(rd_data[i * 2] as u32, rd_data[i * 2 + 1] as u32); -// } - -// if local_opcode == JAL { -// let last_limb_bits = PC_BITS - RV32_CELL_BITS * (RV32_REGISTER_NUM_LIMBS - 1); -// let additional_bits = (last_limb_bits..RV32_CELL_BITS).fold(0, |acc, x| acc + (1 << x)); -// self.bitwise_lookup_chip -// .request_xor(rd_data[3] as u32, additional_bits); -// } - -// let rd_data = rd_data.map(F::from_canonical_u8); - -// let output = AdapterRuntimeContext { -// to_pc: Some(to_pc), -// writes: [rd_data].into(), -// }; - -// Ok(( -// output, -// Rv32JalLuiCoreRecord { -// rd_data, -// imm, -// is_jal: local_opcode == JAL, -// is_lui: local_opcode == LUI, -// }, -// )) -// } - -// fn get_opcode_name(&self, opcode: usize) -> String { -// format!( -// "{:?}", -// Rv32JalLuiOpcode::from_usize(opcode - Rv32JalLuiOpcode::CLASS_OFFSET) -// ) -// } - -// fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { -// let core_cols: &mut Rv32JalLuiCoreCols = row_slice.borrow_mut(); -// core_cols.rd_data = record.rd_data; -// core_cols.imm = record.imm; -// core_cols.is_jal = F::from_bool(record.is_jal); -// core_cols.is_lui = F::from_bool(record.is_lui); -// } - -// fn air(&self) -> &Self::Air { -// &self.air -// } -// } - // returns (to_pc, rd_data) #[inline(always)] pub(super) fn run_jal_lui( diff --git a/extensions/rv32im/circuit/src/jalr/core.rs b/extensions/rv32im/circuit/src/jalr/core.rs index 81f702ed54..cac574e0a8 100644 --- a/extensions/rv32im/circuit/src/jalr/core.rs +++ b/extensions/rv32im/circuit/src/jalr/core.rs @@ -52,17 +52,6 @@ pub struct Rv32JalrCoreCols { pub imm_sign: T, } -#[repr(C)] -#[derive(Serialize, Deserialize)] -pub struct Rv32JalrCoreRecord { - pub imm: F, - pub rs1_data: [F; RV32_REGISTER_NUM_LIMBS], - pub rd_data: [F; RV32_REGISTER_NUM_LIMBS - 1], - pub to_pc_least_sig_bit: F, - pub to_pc_limbs: [u32; 2], - pub imm_sign: F, -} - #[derive(Debug, Clone, derive_new::new)] pub struct Rv32JalrCoreAir { pub bitwise_lookup_bus: BitwiseOperationLookupBus, diff --git a/extensions/rv32im/circuit/src/jalr/tests.rs b/extensions/rv32im/circuit/src/jalr/tests.rs index 30703e250a..5430b8de47 100644 --- a/extensions/rv32im/circuit/src/jalr/tests.rs +++ b/extensions/rv32im/circuit/src/jalr/tests.rs @@ -20,6 +20,7 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; +use super::Rv32JalrCoreAir; use crate::{ adapters::{ compose, Rv32JalrAdapterAir, Rv32JalrAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, @@ -27,8 +28,6 @@ use crate::{ jalr::{run_jalr, Rv32JalrChip, Rv32JalrCoreCols, Rv32JalrStep}, }; -use super::Rv32JalrCoreAir; - const IMM_BITS: usize = 16; const MAX_INS_CAPACITY: usize = 128; diff --git a/extensions/rv32im/circuit/src/less_than/core.rs b/extensions/rv32im/circuit/src/less_than/core.rs index f11e4f76f2..9da8b4507f 100644 --- a/extensions/rv32im/circuit/src/less_than/core.rs +++ b/extensions/rv32im/circuit/src/less_than/core.rs @@ -171,22 +171,6 @@ where } } -#[repr(C)] -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(bound = "T: Serialize + DeserializeOwned")] -pub struct LessThanCoreRecord { - #[serde(with = "BigArray")] - pub b: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub c: [T; NUM_LIMBS], - pub cmp_result: T, - pub b_msb_f: T, - pub c_msb_f: T, - pub diff_val: T, - pub diff_idx: usize, - pub opcode: LessThanOpcode, -} - pub struct LessThanStep { adapter: A, offset: usize, diff --git a/extensions/rv32im/circuit/src/load_sign_extend/core.rs b/extensions/rv32im/circuit/src/load_sign_extend/core.rs index d2a9a23ef6..c0391f033c 100644 --- a/extensions/rv32im/circuit/src/load_sign_extend/core.rs +++ b/extensions/rv32im/circuit/src/load_sign_extend/core.rs @@ -343,90 +343,6 @@ where } } -// impl, const NUM_CELLS: usize, const LIMB_BITS: usize> -// VmCoreChip for LoadSignExtendCoreChip -// where -// I::Reads: Into<([[F; NUM_CELLS]; 2], F)>, -// I::Writes: From<[[F; NUM_CELLS]; 1]>, -// { -// type Record = LoadSignExtendCoreRecord; -// type Air = LoadSignExtendCoreAir; - -// #[allow(clippy::type_complexity)] -// fn execute_instruction( -// &self, -// instruction: &Instruction, -// _from_pc: u32, -// reads: I::Reads, -// ) -> Result<(AdapterRuntimeContext, Self::Record)> { -// let local_opcode = Rv32LoadStoreOpcode::from_usize( -// instruction -// .opcode -// .local_opcode_idx(Rv32LoadStoreOpcode::CLASS_OFFSET), -// ); - -// let (data, shift_amount) = reads.into(); -// let shift_amount = shift_amount.as_canonical_u32(); -// let write_data: [F; NUM_CELLS] = run_write_data_sign_extend::<_, NUM_CELLS, LIMB_BITS>( -// local_opcode, -// data[1], -// data[0], -// shift_amount, -// ); -// let output = AdapterRuntimeContext::without_pc([write_data]); - -// let most_sig_limb = match local_opcode { -// LOADB => write_data[0], -// LOADH => write_data[NUM_CELLS / 2 - 1], -// _ => unreachable!(), -// } -// .as_canonical_u32(); - -// let most_sig_bit = most_sig_limb & (1 << (LIMB_BITS - 1)); -// self.range_checker_chip -// .add_count(most_sig_limb - most_sig_bit, LIMB_BITS - 1); - -// let read_shift = shift_amount & 2; - -// Ok(( -// output, -// LoadSignExtendCoreRecord { -// opcode: local_opcode, -// most_sig_bit: most_sig_bit != 0, -// prev_data: data[0], -// shifted_read_data: array::from_fn(|i| { -// data[1][(i + read_shift as usize) % NUM_CELLS] -// }), -// shift_amount, -// }, -// )) -// } - -// fn get_opcode_name(&self, opcode: usize) -> String { -// format!( -// "{:?}", -// Rv32LoadStoreOpcode::from_usize(opcode - Rv32LoadStoreOpcode::CLASS_OFFSET) -// ) -// } - -// fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { -// let core_cols: &mut LoadSignExtendCoreCols = row_slice.borrow_mut(); -// let opcode = record.opcode; -// let shift = record.shift_amount; -// core_cols.opcode_loadb_flag0 = F::from_bool(opcode == LOADB && (shift & 1) == 0); -// core_cols.opcode_loadb_flag1 = F::from_bool(opcode == LOADB && (shift & 1) == 1); -// core_cols.opcode_loadh_flag = F::from_bool(opcode == LOADH); -// core_cols.shift_most_sig_bit = F::from_canonical_u32((shift & 2) >> 1); -// core_cols.data_most_sig_bit = F::from_bool(record.most_sig_bit); -// core_cols.prev_data = record.prev_data; -// core_cols.shifted_read_data = record.shifted_read_data; -// } - -// fn air(&self) -> &Self::Air { -// &self.air -// } -// } - // TODO(ayush): remove _prev_data #[inline(always)] pub(super) fn run_write_data_sign_extend< diff --git a/extensions/rv32im/circuit/src/loadstore/core.rs b/extensions/rv32im/circuit/src/loadstore/core.rs index 4c51a22073..af73a6eb30 100644 --- a/extensions/rv32im/circuit/src/loadstore/core.rs +++ b/extensions/rv32im/circuit/src/loadstore/core.rs @@ -305,6 +305,8 @@ where let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + A::start(*state.pc, state.memory, adapter_row); + let ((prev_data, read_data), shift) = self.adapter.read(state.memory, instruction, adapter_row); let prev_data = prev_data.map(F::from_canonical_u8); @@ -354,8 +356,7 @@ where } fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { - let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - let _core_row: &mut LoadStoreCoreCols = core_row.borrow_mut(); + let (adapter_row, _core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; self.adapter .fill_trace_row(mem_helper, &self.range_checker_chip, adapter_row); @@ -401,87 +402,6 @@ where } } -// impl, const NUM_CELLS: usize> VmCoreChip -// for LoadStoreCoreChip -// where -// I::Reads: Into<([[F; NUM_CELLS]; 2], F)>, -// I::Writes: From<[[F; NUM_CELLS]; 1]>, -// { -// type Record = LoadStoreCoreRecord; -// type Air = LoadStoreCoreAir; - -// #[allow(clippy::type_complexity)] -// fn execute_instruction( -// &self, -// instruction: &Instruction, -// _from_pc: u32, -// reads: I::Reads, -// ) -> Result<(AdapterRuntimeContext, Self::Record)> { -// let local_opcode = -// Rv32LoadStoreOpcode::from_usize(instruction.opcode.local_opcode_idx(self.air.offset)); - -// let (reads, shift_amount) = reads.into(); -// let shift = shift_amount.as_canonical_u32(); -// let prev_data = reads[0]; -// let read_data = reads[1]; -// let write_data = run_write_data(local_opcode, read_data, prev_data, shift); -// let output = AdapterRuntimeContext::without_pc([write_data]); - -// Ok(( -// output, -// LoadStoreCoreRecord { -// opcode: local_opcode, -// shift, -// prev_data, -// read_data, -// write_data, -// }, -// )) -// } - -// fn get_opcode_name(&self, opcode: usize) -> String { -// format!( -// "{:?}", -// Rv32LoadStoreOpcode::from_usize(opcode - self.air.offset) -// ) -// } - -// fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { -// let core_cols: &mut LoadStoreCoreCols = row_slice.borrow_mut(); -// let opcode = record.opcode; -// let flags = &mut core_cols.flags; -// *flags = [F::ZERO; 4]; -// match (opcode, record.shift) { -// (LOADW, 0) => flags[0] = F::TWO, -// (LOADHU, 0) => flags[1] = F::TWO, -// (LOADHU, 2) => flags[2] = F::TWO, -// (LOADBU, 0) => flags[3] = F::TWO, - -// (LOADBU, 1) => flags[0] = F::ONE, -// (LOADBU, 2) => flags[1] = F::ONE, -// (LOADBU, 3) => flags[2] = F::ONE, -// (STOREW, 0) => flags[3] = F::ONE, - -// (STOREH, 0) => (flags[0], flags[1]) = (F::ONE, F::ONE), -// (STOREH, 2) => (flags[0], flags[2]) = (F::ONE, F::ONE), -// (STOREB, 0) => (flags[0], flags[3]) = (F::ONE, F::ONE), -// (STOREB, 1) => (flags[1], flags[2]) = (F::ONE, F::ONE), -// (STOREB, 2) => (flags[1], flags[3]) = (F::ONE, F::ONE), -// (STOREB, 3) => (flags[2], flags[3]) = (F::ONE, F::ONE), -// _ => unreachable!(), -// }; -// core_cols.prev_data = record.prev_data; -// core_cols.read_data = record.read_data; -// core_cols.is_valid = F::ONE; -// core_cols.is_load = F::from_bool([LOADW, LOADHU, LOADBU].contains(&opcode)); -// core_cols.write_data = record.write_data; -// } - -// fn air(&self) -> &Self::Air { -// &self.air -// } -// } - #[inline(always)] pub(super) fn run_write_data( opcode: Rv32LoadStoreOpcode, diff --git a/extensions/rv32im/circuit/src/loadstore/mod.rs b/extensions/rv32im/circuit/src/loadstore/mod.rs index c7ea7fbd82..51735afbd7 100644 --- a/extensions/rv32im/circuit/src/loadstore/mod.rs +++ b/extensions/rv32im/circuit/src/loadstore/mod.rs @@ -4,9 +4,8 @@ pub use core::*; use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; -use crate::adapters::{Rv32LoadStoreAdapterAir, Rv32LoadStoreAdapterStep}; - use super::adapters::RV32_REGISTER_NUM_LIMBS; +use crate::adapters::{Rv32LoadStoreAdapterAir, Rv32LoadStoreAdapterStep}; #[cfg(test)] mod tests; diff --git a/extensions/rv32im/circuit/src/loadstore/tests.rs b/extensions/rv32im/circuit/src/loadstore/tests.rs index 781a275c3a..1eb53b156d 100644 --- a/extensions/rv32im/circuit/src/loadstore/tests.rs +++ b/extensions/rv32im/circuit/src/loadstore/tests.rs @@ -32,7 +32,7 @@ use crate::{ }; const IMM_BITS: usize = 16; -const MAX_INS_CAPACITY: usize = 128; +const MAX_INS_CAPACITY: usize = 1024; type F = BabyBear; @@ -73,9 +73,9 @@ fn set_and_execute( let mem_as = mem_as.unwrap_or(if is_load { *[1, 2].choose(rng).unwrap() } else { - // TODO(ayush): how can memory address space be variable? - // how can this write to native as? - *[2, 3, 4].choose(rng).unwrap() + *[2, 3].choose(rng).unwrap() + // TODO(ayush): should this allow 4? + // *[2, 3, 4].choose(rng).unwrap() }); let ptr_val = imm_ext.wrapping_add(compose(rs1)); @@ -418,19 +418,20 @@ fn negative_wrong_address_space_tests() { Some(3), VerificationError::OodEvaluationMismatch, ); - run_negative_loadstore_test( - LOADW, - None, - None, - None, - None, - None, - None, - None, - None, - Some(4), - VerificationError::OodEvaluationMismatch, - ); + // TODO(ayush): add back + // run_negative_loadstore_test( + // LOADW, + // None, + // None, + // None, + // None, + // None, + // None, + // None, + // None, + // Some(4), + // VerificationError::OodEvaluationMismatch, + // ); run_negative_loadstore_test( STOREW, None, @@ -476,7 +477,7 @@ fn execute_roundtrip_sanity_test() { tester.memory_helper(), ); - let num_tests: usize = 1; + let num_tests: usize = 100; for _ in 0..num_tests { set_and_execute( &mut tester, @@ -498,17 +499,16 @@ fn execute_roundtrip_sanity_test() { None, None, ); - // TODO(ayush): what are alignment requirements for hint as? - // set_and_execute( - // &mut tester, - // &mut chip, - // &mut rng, - // LOADHU, - // None, - // None, - // None, - // None, - // ); + set_and_execute( + &mut tester, + &mut chip, + &mut rng, + LOADHU, + None, + None, + None, + None, + ); set_and_execute( &mut tester, &mut chip, @@ -529,16 +529,16 @@ fn execute_roundtrip_sanity_test() { None, None, ); - // set_and_execute( - // &mut tester, - // &mut chip, - // &mut rng, - // STOREH, - // None, - // None, - // None, - // None, - // ); + set_and_execute( + &mut tester, + &mut chip, + &mut rng, + STOREH, + None, + None, + None, + None, + ); } } diff --git a/extensions/rv32im/circuit/src/mul/core.rs b/extensions/rv32im/circuit/src/mul/core.rs index 57c580a75a..d83c45ac08 100644 --- a/extensions/rv32im/circuit/src/mul/core.rs +++ b/extensions/rv32im/circuit/src/mul/core.rs @@ -115,18 +115,6 @@ where } } -#[repr(C)] -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(bound = "T: Serialize + DeserializeOwned")] -pub struct MultiplicationCoreRecord { - #[serde(with = "BigArray")] - pub a: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub b: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub c: [T; NUM_LIMBS], -} - #[derive(Debug)] pub struct MultiplicationStep { adapter: A, diff --git a/extensions/rv32im/circuit/src/mul/mod.rs b/extensions/rv32im/circuit/src/mul/mod.rs index 95a0fc6468..00a3fe77bb 100644 --- a/extensions/rv32im/circuit/src/mul/mod.rs +++ b/extensions/rv32im/circuit/src/mul/mod.rs @@ -1,8 +1,7 @@ use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; -use crate::adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep}; - use super::adapters::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; +use crate::adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep}; mod core; pub use core::*; diff --git a/extensions/rv32im/circuit/src/mulh/core.rs b/extensions/rv32im/circuit/src/mulh/core.rs index 27091a18ec..4311edaca6 100644 --- a/extensions/rv32im/circuit/src/mulh/core.rs +++ b/extensions/rv32im/circuit/src/mulh/core.rs @@ -189,22 +189,6 @@ where } } -#[repr(C)] -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct MulHCoreRecord { - pub opcode: MulHOpcode, - #[serde(with = "BigArray")] - pub a: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub b: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub c: [T; NUM_LIMBS], - #[serde(with = "BigArray")] - pub a_mul: [T; NUM_LIMBS], - pub b_ext: T, - pub c_ext: T, -} - pub struct MulHStep { adapter: A, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, diff --git a/extensions/rv32im/circuit/src/mulh/mod.rs b/extensions/rv32im/circuit/src/mulh/mod.rs index 480ec3adaa..39607bdf5c 100644 --- a/extensions/rv32im/circuit/src/mulh/mod.rs +++ b/extensions/rv32im/circuit/src/mulh/mod.rs @@ -1,8 +1,7 @@ use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; -use crate::adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep}; - use super::adapters::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; +use crate::adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep}; mod core; pub use core::*; diff --git a/extensions/rv32im/circuit/src/mulh/tests.rs b/extensions/rv32im/circuit/src/mulh/tests.rs index 4a20075d3e..ea2dcca24b 100644 --- a/extensions/rv32im/circuit/src/mulh/tests.rs +++ b/extensions/rv32im/circuit/src/mulh/tests.rs @@ -186,7 +186,8 @@ fn rv32_mulhu_rand_test() { // let trace_width = chip.trace_width(); // let adapter_width = BaseAir::::width(chip.adapter.air()); -// let (_, _, carry, _, _) = run_mulh::(opcode, &b, &c); +// let (_, _, carry, _, _) = run_mulh::(opcode, &b, +// &c); // range_tuple_chip.clear(); // for i in 0..RV32_REGISTER_NUM_LIMBS { From e885ec0e35880d7fc0a2e7041db7158c92eb9794 Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Fri, 2 May 2025 11:06:41 -0700 Subject: [PATCH 09/49] feat: access adapters (#1614) closes INT-3839 --------- Co-authored-by: Ayush Shukla --- crates/vm/src/arch/execution.rs | 6 +- crates/vm/src/arch/integration_api.rs | 8 +- crates/vm/src/system/memory/adapter/mod.rs | 160 ++++++++++- crates/vm/src/system/memory/controller/mod.rs | 15 +- crates/vm/src/system/memory/online.rs | 254 ++++++++++++++---- crates/vm/src/system/native_adapter/mod.rs | 6 +- crates/vm/src/system/public_values/core.rs | 2 +- .../src/adapters/alu_native_adapter.rs | 6 +- .../src/adapters/branch_native_adapter.rs | 6 +- .../circuit/src/adapters/convert_adapter.rs | 6 +- .../src/adapters/loadstore_native_adapter.rs | 6 +- extensions/native/circuit/src/adapters/mod.rs | 10 +- .../src/adapters/native_vectorized_adapter.rs | 6 +- extensions/native/circuit/src/castf/core.rs | 2 +- .../circuit/src/field_arithmetic/core.rs | 2 +- .../circuit/src/field_extension/core.rs | 2 +- .../native/circuit/src/loadstore/core.rs | 2 +- extensions/rv32im/circuit/src/adapters/alu.rs | 6 +- .../rv32im/circuit/src/adapters/branch.rs | 6 +- .../rv32im/circuit/src/adapters/jalr.rs | 6 +- .../rv32im/circuit/src/adapters/loadstore.rs | 6 +- extensions/rv32im/circuit/src/adapters/mod.rs | 16 +- extensions/rv32im/circuit/src/adapters/mul.rs | 6 +- .../rv32im/circuit/src/adapters/rdwrite.rs | 12 +- extensions/rv32im/circuit/src/auipc/core.rs | 2 +- .../rv32im/circuit/src/base_alu/core.rs | 2 +- .../rv32im/circuit/src/base_alu/tests.rs | 6 +- .../rv32im/circuit/src/branch_eq/core.rs | 2 +- .../rv32im/circuit/src/branch_lt/core.rs | 2 +- extensions/rv32im/circuit/src/divrem/core.rs | 2 +- extensions/rv32im/circuit/src/jal_lui/core.rs | 2 +- extensions/rv32im/circuit/src/jalr/core.rs | 2 +- .../rv32im/circuit/src/less_than/core.rs | 2 +- .../circuit/src/load_sign_extend/core.rs | 2 +- .../rv32im/circuit/src/loadstore/core.rs | 2 +- extensions/rv32im/circuit/src/mul/core.rs | 2 +- extensions/rv32im/circuit/src/mulh/core.rs | 2 +- extensions/rv32im/circuit/src/shift/core.rs | 2 +- 38 files changed, 453 insertions(+), 136 deletions(-) diff --git a/crates/vm/src/arch/execution.rs b/crates/vm/src/arch/execution.rs index 5616495d46..a529c80e2f 100644 --- a/crates/vm/src/arch/execution.rs +++ b/crates/vm/src/arch/execution.rs @@ -6,7 +6,7 @@ use openvm_instructions::{ }; use openvm_stark_backend::{ interaction::{BusIndex, InteractionBuilder, PermutationCheckBus}, - p3_field::{Field, FieldAlgebra, PrimeField32}, + p3_field::{FieldAlgebra, PrimeField32}, }; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -86,10 +86,10 @@ pub struct VmStateMut<'a, MEM, CTX> { pub ctx: &'a mut CTX, } -impl VmStateMut<'_, TracingMemory, CTX> { +impl VmStateMut<'_, TracingMemory, CTX> { // TODO: store as u32 directly #[inline(always)] - pub fn ins_start(&self, from_state: &mut ExecutionState) { + pub fn ins_start(&self, from_state: &mut ExecutionState) { from_state.pc = F::from_canonical_u32(*self.pc); from_state.timestamp = F::from_canonical_u32(self.memory.timestamp); } diff --git a/crates/vm/src/arch/integration_api.rs b/crates/vm/src/arch/integration_api.rs index 9f2ebbb801..d0200f9834 100644 --- a/crates/vm/src/arch/integration_api.rs +++ b/crates/vm/src/arch/integration_api.rs @@ -224,7 +224,7 @@ pub struct AdapterAirContext> { pub trait SingleTraceStep { fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()>; @@ -391,18 +391,18 @@ pub trait AdapterTraceStep { where Self: 'a; - fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]); + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]); fn read( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData; fn write( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, diff --git a/crates/vm/src/system/memory/adapter/mod.rs b/crates/vm/src/system/memory/adapter/mod.rs index 64e79a920b..e2e0ab9ff1 100644 --- a/crates/vm/src/system/memory/adapter/mod.rs +++ b/crates/vm/src/system/memory/adapter/mod.rs @@ -1,4 +1,4 @@ -use std::{borrow::BorrowMut, cmp::max, sync::Arc}; +use std::{borrow::BorrowMut, cmp::max, io::Cursor, sync::Arc}; pub use air::*; pub use columns::*; @@ -145,6 +145,39 @@ impl AccessAdapterInventory { None } } + + pub(crate) fn execute_split( + &mut self, + address: MemoryAddress, + values: &[F], + timestamp: u32, + row_slice: &mut [F], + ) where + F: PrimeField32, + { + let index = get_chip_index(values.len()); + self.chips[index].execute_split(address, values, timestamp, row_slice); + } + + pub(crate) fn execute_merge( + &mut self, + address: MemoryAddress, + values: &[F], + left_timestamp: u32, + right_timestamp: u32, + row_slice: &mut [F], + ) where + F: PrimeField32, + { + let index = get_chip_index(values.len()); + self.chips[index].execute_merge( + address, + values, + left_timestamp, + right_timestamp, + row_slice, + ); + } } #[derive(Debug, Clone, PartialEq, Eq)] @@ -173,6 +206,25 @@ pub trait GenericAccessAdapterChipTrait { fn generate_trace(self) -> RowMajorMatrix where F: PrimeField32; + + fn execute_split( + &mut self, + address: MemoryAddress, + values: &[F], + timestamp: u32, + row_slice: &mut [F], + ) where + F: PrimeField32; + + fn execute_merge( + &mut self, + address: MemoryAddress, + values: &[F], + left_timestamp: u32, + right_timestamp: u32, + row_slice: &mut [F], + ) where + F: PrimeField32; } #[derive(Chip, ChipUsageGetter)] @@ -216,6 +268,7 @@ impl GenericAccessAdapterChip { } } } + pub struct AccessAdapterChip { air: AccessAdapterAir, range_checker: SharedVariableRangeCheckerChip, @@ -294,6 +347,72 @@ impl GenericAccessAdapterChipTrait for AccessAdapterChip, + values: &[F], + timestamp: u32, + row_slice: &mut [F], + ) where + F: PrimeField32, + { + let row: &mut AccessAdapterCols = row_slice.borrow_mut(); + row.is_valid = F::ONE; + row.is_split = F::ONE; + row.address = MemoryAddress::new( + F::from_canonical_u32(address.address_space), + F::from_canonical_u32(address.pointer), + ); + let timestamp = F::from_canonical_u32(timestamp); + row.left_timestamp = timestamp; + row.right_timestamp = timestamp; + row.is_right_larger = F::ZERO; + debug_assert_eq!( + values.len(), + N, + "Input values slice length must match the access adapter type" + ); + + // SAFETY: `values` slice is asserted to have length N. `row.values` is an array of length + // N. Pointers are valid and regions do not overlap because exactly one of them is a + // part of the trace. + unsafe { + std::ptr::copy_nonoverlapping(values.as_ptr(), row.values.as_mut_ptr(), N); + } + } + + fn execute_merge( + &mut self, + address: MemoryAddress, + values: &[F], + left_timestamp: u32, + right_timestamp: u32, + row_slice: &mut [F], + ) where + F: PrimeField32, + { + let row: &mut AccessAdapterCols = row_slice.borrow_mut(); + row.is_valid = F::ONE; + row.is_split = F::ZERO; + row.address = MemoryAddress::new( + F::from_canonical_u32(address.address_space), + F::from_canonical_u32(address.pointer), + ); + row.left_timestamp = F::from_canonical_u32(left_timestamp); + row.right_timestamp = F::from_canonical_u32(right_timestamp); + debug_assert_eq!( + values.len(), + N, + "Input values slice length must match the access adapter type" + ); + // SAFETY: `values` slice is asserted to have length N. `row.values` is an array of length + // N. Pointers are valid and regions do not overlap because exactly one of them is a + // part of the trace. + unsafe { + std::ptr::copy_nonoverlapping(values.as_ptr(), row.values.as_mut_ptr(), N); + } + } } impl Chip for AccessAdapterChip, N> @@ -328,3 +447,42 @@ impl ChipUsageGetter for AccessAdapterChip { fn air_name(n: usize) -> String { format!("AccessAdapter<{}>", n) } + +#[inline(always)] +pub fn get_chip_index(block_size: usize) -> usize { + assert!( + block_size.is_power_of_two() && block_size >= 2, + "Invalid block size {} for split operation", + block_size + ); + let index = block_size.trailing_zeros() - 1; + index as usize +} + +pub struct AdapterInventoryTraceCursor { + // [AG] TODO: replace with a pre-allocated space + cursors: Vec>>, + widths: Vec, +} + +impl AdapterInventoryTraceCursor { + pub fn new(as_cnt: usize) -> Self { + let cursors = vec![Cursor::new(Vec::new()); as_cnt]; + let widths = vec![ + size_of::>(), + size_of::>(), + size_of::>(), + size_of::>(), + size_of::>(), + ]; + Self { cursors, widths } + } + + pub fn get_row_slice(&mut self, block_size: usize) -> &mut [F] { + let index = get_chip_index(block_size); + let begin = self.cursors[index].position() as usize; + let end = begin + self.widths[index]; + self.cursors[index].set_position(end as u64); + &mut self.cursors[index].get_mut()[begin..end] + } +} diff --git a/crates/vm/src/system/memory/controller/mod.rs b/crates/vm/src/system/memory/controller/mod.rs index 91ae38c7b0..f98ae783ec 100644 --- a/crates/vm/src/system/memory/controller/mod.rs +++ b/crates/vm/src/system/memory/controller/mod.rs @@ -101,7 +101,7 @@ pub struct MemoryController { // Store separately to avoid smart pointer reference each time range_checker_bus: VariableRangeCheckerBus, // addr_space -> Memory data structure - pub(crate) memory: TracingMemory, + pub(crate) memory: TracingMemory, /// A reference to the `OfflineMemory`. Will be populated after `finalize()`. pub offline_memory: Arc>>, pub access_adapters: AccessAdapterInventory, @@ -246,7 +246,7 @@ impl MemoryController { range_checker.clone(), ), }, - memory: TracingMemory::new(&mem_config), + memory: TracingMemory::new(&mem_config, range_checker.clone(), memory_bus), offline_memory: Arc::new(Mutex::new(OfflineMemory::new( initial_memory, 1, @@ -297,8 +297,8 @@ impl MemoryController { memory_bus, mem_config, interface_chip, - memory: TracingMemory::new(&mem_config), /* it is expected that the memory will be - * set later */ + memory: TracingMemory::new(&mem_config, range_checker.clone(), memory_bus), /* it is expected that the memory will be + * set later */ offline_memory: Arc::new(Mutex::new(OfflineMemory::new( AddressMap::from_mem_config(&mem_config), CHUNK, @@ -355,7 +355,12 @@ impl MemoryController { let mut offline_memory = self.offline_memory.lock().unwrap(); offline_memory.set_initial_memory(memory.clone(), self.mem_config); - self.memory = TracingMemory::from_image(memory.clone(), self.mem_config.access_capacity); + self.memory = TracingMemory::new( + &self.mem_config, + self.range_checker.clone(), + self.memory_bus, + ) + .with_image(memory.clone(), self.mem_config.access_capacity); match &mut self.interface_chip { MemoryInterface::Volatile { .. } => { diff --git a/crates/vm/src/system/memory/online.rs b/crates/vm/src/system/memory/online.rs index 2d6201edd3..85b9dd66bd 100644 --- a/crates/vm/src/system/memory/online.rs +++ b/crates/vm/src/system/memory/online.rs @@ -2,11 +2,15 @@ use std::fmt::Debug; use getset::Getters; use itertools::{izip, zip_eq}; +use openvm_circuit_primitives::var_range::SharedVariableRangeCheckerChip; +use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; use super::{ + adapter::{AccessAdapterInventory, AdapterInventoryTraceCursor}, + offline_checker::MemoryBus, paged_vec::{AddressMap, PAGE_SIZE}, - Address, PagedVec, + Address, MemoryAddress, PagedVec, }; use crate::{ arch::MemoryConfig, @@ -89,10 +93,15 @@ pub struct AccessMetadata { pub block_size: u32, } +impl AccessMetadata { + /// A marker indicating that the element is a part of a larger block which starts earlier. + const OCCUPIED: u32 = u32::MAX; +} + /// Online memory that stores additional information for trace generation purposes. /// In particular, keeps track of timestamp. #[derive(Getters)] -pub struct TracingMemory { +pub struct TracingMemory { pub timestamp: u32, /// The underlying data memory, with memory cells typed by address space: see [AddressMap]. // TODO: make generic in GuestMemory @@ -104,12 +113,17 @@ pub struct TracingMemory { /// For each `addr_space`, the minimum block size allowed for memory accesses. In other words, /// all memory accesses in `addr_space` must be aligned to this block size. pub(super) min_block_size: Vec, - // TODO: access adapter + pub(super) access_adapter_inventory: AccessAdapterInventory, + pub(super) adapter_inventory_trace_cursor: AdapterInventoryTraceCursor, } -impl TracingMemory { +impl TracingMemory { // TODO: per-address space memory capacity specification - pub fn new(mem_config: &MemoryConfig) -> Self { + pub fn new( + mem_config: &MemoryConfig, + range_checker: SharedVariableRangeCheckerChip, + memory_bus: MemoryBus, + ) -> Self { assert_eq!(mem_config.as_offset, 1); let num_cells = 1usize << mem_config.pointer_max_bits; // max cells per address space let num_addr_sp = 1 + (1 << mem_config.as_height); @@ -134,36 +148,31 @@ impl TracingMemory { meta, min_block_size, timestamp: INITIAL_TIMESTAMP + 1, + access_adapter_inventory: AccessAdapterInventory::new( + range_checker, + memory_bus, + mem_config.clk_max_bits, + mem_config.max_access_adapter_n, + ), + adapter_inventory_trace_cursor: AdapterInventoryTraceCursor::new(num_addr_sp), } } /// Instantiates a new `Memory` data structure from an image. - pub fn from_image(image: MemoryImage, access_capacity: usize) -> Self { - let mut meta = vec![PagedVec::new(0); image.as_offset as usize]; - let mut min_block_size = vec![1; image.as_offset as usize]; + pub fn with_image(mut self, image: MemoryImage, _access_capacity: usize) -> Self { + self.min_block_size = vec![1; self.meta.len()]; for (i, (paged_vec, cell_size)) in izip!(&image.paged_vecs, &image.cell_size).enumerate() { let num_cells = paged_vec.bytes_capacity() / cell_size; - // TMP: hardcoding for now - if i < 3 { - min_block_size.push(4); - } else { - min_block_size.push(1); - } - - meta.push(PagedVec::new( + self.meta[i] = PagedVec::new( num_cells .checked_mul(size_of::()) .unwrap() - .div_ceil(PAGE_SIZE * min_block_size[i] as usize), - )); - } - Self { - data: image, - meta, - min_block_size, - timestamp: INITIAL_TIMESTAMP + 1, + .div_ceil(PAGE_SIZE * self.min_block_size[i] as usize), + ); } + self.data = image; + self } #[inline(always)] @@ -179,6 +188,143 @@ impl TracingMemory { ); } + fn execute_splits( + &mut self, + address: MemoryAddress, + values: &[F], + timestamp: u32, + ) { + let mut size = ALIGN; + let MemoryAddress { + address_space, + pointer, + } = address; + while size < values.len() { + size *= 2; + for i in (0..values.len()).step_by(size) { + self.access_adapter_inventory.execute_split( + MemoryAddress { + address_space, + pointer: pointer + (i * size) as u32, + }, + &values[i * size..(i + 1) * size], + timestamp, + self.adapter_inventory_trace_cursor.get_row_slice(size), + ); + } + } + } + + fn execute_merges( + &mut self, + address: MemoryAddress, + values: &[F], + timestamps: &[u32], + ) { + let mut size = ALIGN; + let MemoryAddress { + address_space, + pointer, + } = address; + while size < values.len() { + size *= 2; + for i in (0..values.len()).step_by(size) { + let left_timestamp = timestamps[(i / ALIGN)..((i + size / 2) / ALIGN)] + .iter() + .max() + .unwrap(); + let right_timestamp = timestamps[(i + size / 2 / ALIGN)..((i + size) / ALIGN)] + .iter() + .max() + .unwrap(); + self.access_adapter_inventory.execute_merge( + MemoryAddress { + address_space, + pointer: pointer + (i * size) as u32, + }, + &values[i * size..(i + 1) * size], + *left_timestamp, + *right_timestamp, + self.adapter_inventory_trace_cursor.get_row_slice(size), + ); + } + } + } + + /// Returns the timestamp of the previous access to `[pointer:BLOCK_SIZE]_{address_space}`. + /// If we need to split/merge/initialize something for this, we first do all the necessary + /// actions. In the end of this process, we have this segment intact in our `meta`. + /// + /// Caller must ensure alignment (e.g. via `assert_alignment`) prior to calling this function. + fn prev_access_time( + &mut self, + address_space: usize, + pointer: usize, + ) -> u32 { + let size = size_of::(); + let seg_size = ALIGN * size; + let num_segs = BLOCK_SIZE / ALIGN; + + let begin = pointer / ALIGN; + let end = begin + BLOCK_SIZE / ALIGN; + + let mut prev_ts = INITIAL_TIMESTAMP; + let mut block_timestamps = vec![INITIAL_TIMESTAMP; num_segs]; + let mut cur_ptr = begin; + let need_to_merge = loop { + if cur_ptr >= end { + break true; + } + let mut current_metadata = self.meta[address_space] + .get::(cur_ptr * size_of::()); + if current_metadata.block_size == BLOCK_SIZE as u32 && cur_ptr + num_segs == end { + // We do not have to do anything + prev_ts = current_metadata.timestamp; + break false; + } else if current_metadata.block_size == 0 { + // Initialize + self.meta[address_space].set( + cur_ptr * size_of::(), + &AccessMetadata { + timestamp: INITIAL_TIMESTAMP, + block_size: ALIGN as u32, + }, + ); + } + prev_ts = prev_ts.max(current_metadata.timestamp); + while current_metadata.block_size == AccessMetadata::OCCUPIED { + cur_ptr -= 1; + current_metadata = + self.meta[address_space].get::(cur_ptr * seg_size); + } + block_timestamps[cur_ptr.saturating_sub(begin) + ..((cur_ptr + current_metadata.block_size as usize).min(end) - begin)] + .fill(current_metadata.timestamp); + // Split + let address = MemoryAddress::new(address_space as u32, (cur_ptr * seg_size) as u32); + let values = (0..current_metadata.block_size as usize) + .map(|i| { + self.data + .get_f(address.address_space, address.pointer + (i as u32)) + }) + .collect::>(); + self.execute_splits::(address, &values, self.timestamp); + cur_ptr += current_metadata.block_size as usize; + }; + if need_to_merge { + // Merge + let values = (0..BLOCK_SIZE) + .map(|i| self.data.get_f(address_space as u32, (pointer + i) as u32)) + .collect::>(); + self.execute_merges::( + MemoryAddress::new(address_space as u32, pointer as u32), + &values, + &block_timestamps, + ); + } + prev_ts + } + /// Atomic read operation which increments the timestamp by 1. /// Returns `(t_prev, [pointer:BLOCK_SIZE]_{address_space})` where `t_prev` is the /// timestamp of the last memory access. @@ -215,20 +361,24 @@ impl TracingMemory { self.timestamp += 1; // Handle timestamp and block size: let access_idx = (pointer as usize / ALIGN) * size_of::(); - // TODO: address space should be checked elsewhere + // TODO: this is wrong and must be replaced with normal logic + // let t_prev = { + // // TODO: address space should be checked elsewhere + // let meta = unsafe { self.meta.get_unchecked_mut(address_space as usize) }; + // let AccessMetadata { + // timestamp: t_prev, + // mut block_size, + // } = meta.replace(access_idx, &AccessMetadata::new(t_curr, BLOCK_SIZE as u32)); + // // TODO: mark as touched + // if block_size == 0 { + // block_size = BLOCK_SIZE as u32; + // } + // t_prev + // }; + let t_prev = + self.prev_access_time::(address_space as usize, pointer as usize); let meta = unsafe { self.meta.get_unchecked_mut(address_space as usize) }; - // The new - let AccessMetadata { - timestamp: t_prev, - mut block_size, - } = meta.replace(access_idx, &AccessMetadata::new(t_curr, BLOCK_SIZE as u32)); - // TODO: do we need to handle uninitialized memory? - if block_size == 0 { - block_size = BLOCK_SIZE as u32; - } - if (block_size as usize) != BLOCK_SIZE { - todo!("split and merge stuff") - }; + meta.set(access_idx, &AccessMetadata::new(t_curr, BLOCK_SIZE as u32)); (t_prev, values) } @@ -270,20 +420,24 @@ impl TracingMemory { self.timestamp += 1; // Handle timestamp and block size: let access_idx = (pointer as usize / ALIGN) * size_of::(); - // TODO: address space should be checked elsewhere + // TODO: this is wrong and must be replaced with normal logic + // let t_prev = { + // // TODO: address space should be checked elsewhere + // let meta = unsafe { self.meta.get_unchecked_mut(address_space as usize) }; + // let AccessMetadata { + // timestamp: t_prev, + // mut block_size, + // } = meta.replace(access_idx, &AccessMetadata::new(t_curr, BLOCK_SIZE as u32)); + // // TODO: mark as touched + // if block_size == 0 { + // block_size = BLOCK_SIZE as u32; + // } + // t_prev + // }; + let t_prev = + self.prev_access_time::(address_space as usize, pointer as usize); let meta = unsafe { self.meta.get_unchecked_mut(address_space as usize) }; - // The new - let AccessMetadata { - timestamp: t_prev, - mut block_size, - } = meta.replace(access_idx, &AccessMetadata::new(t_curr, BLOCK_SIZE as u32)); - // TODO: mark as touched - if block_size == 0 { - block_size = BLOCK_SIZE as u32; - } - if (block_size as usize) != BLOCK_SIZE { - todo!("split and merge stuff") - }; + meta.set(access_idx, &AccessMetadata::new(t_curr, BLOCK_SIZE as u32)); (t_prev, values_prev) } diff --git a/crates/vm/src/system/native_adapter/mod.rs b/crates/vm/src/system/native_adapter/mod.rs index be00ef4b7f..8be14277c4 100644 --- a/crates/vm/src/system/native_adapter/mod.rs +++ b/crates/vm/src/system/native_adapter/mod.rs @@ -202,7 +202,7 @@ where type TraceContext<'a> = (); #[inline(always)] - fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { let adapter_row: &mut NativeAdapterCols = adapter_row.borrow_mut(); adapter_row.from_state.pc = F::from_canonical_u32(pc); adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); @@ -211,7 +211,7 @@ where #[inline(always)] fn read( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { @@ -221,7 +221,7 @@ where #[inline(always)] fn write( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, diff --git a/crates/vm/src/system/public_values/core.rs b/crates/vm/src/system/public_values/core.rs index 8c29b749b7..90378d210f 100644 --- a/crates/vm/src/system/public_values/core.rs +++ b/crates/vm/src/system/public_values/core.rs @@ -162,7 +162,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/native/circuit/src/adapters/alu_native_adapter.rs b/extensions/native/circuit/src/adapters/alu_native_adapter.rs index 884a999d53..c476a48c91 100644 --- a/extensions/native/circuit/src/adapters/alu_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/alu_native_adapter.rs @@ -137,7 +137,7 @@ where type TraceContext<'a> = &'a BitwiseOperationLookupChip; #[inline(always)] - fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { let adapter_row: &mut AluNativeAdapterCols = adapter_row.borrow_mut(); adapter_row.from_state.pc = F::from_canonical_u32(pc); @@ -146,7 +146,7 @@ where #[inline(always)] fn read( - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { @@ -173,7 +173,7 @@ where #[inline(always)] fn write( - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, diff --git a/extensions/native/circuit/src/adapters/branch_native_adapter.rs b/extensions/native/circuit/src/adapters/branch_native_adapter.rs index cf67ade35d..ad36890336 100644 --- a/extensions/native/circuit/src/adapters/branch_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/branch_native_adapter.rs @@ -137,7 +137,7 @@ where type TraceContext<'a> = &'a BitwiseOperationLookupChip; #[inline(always)] - fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { let adapter_row: &mut BranchNativeAdapterCols = adapter_row.borrow_mut(); adapter_row.from_state.pc = F::from_canonical_u32(pc); @@ -146,7 +146,7 @@ where #[inline(always)] fn read( - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { @@ -178,7 +178,7 @@ where #[inline(always)] fn write( - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, diff --git a/extensions/native/circuit/src/adapters/convert_adapter.rs b/extensions/native/circuit/src/adapters/convert_adapter.rs index bdf3e46f5d..f0df852d01 100644 --- a/extensions/native/circuit/src/adapters/convert_adapter.rs +++ b/extensions/native/circuit/src/adapters/convert_adapter.rs @@ -146,7 +146,7 @@ where type TraceContext<'a> = &'a BitwiseOperationLookupChip; #[inline(always)] - fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { let adapter_row: &mut ConvertAdapterCols = adapter_row.borrow_mut(); adapter_row.from_state.pc = F::from_canonical_u32(pc); @@ -155,7 +155,7 @@ where #[inline(always)] fn read( - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { @@ -176,7 +176,7 @@ where #[inline(always)] fn write( - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, diff --git a/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs b/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs index 942e6ab201..d8e0d7b4c9 100644 --- a/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs @@ -207,7 +207,7 @@ where type TraceContext<'a> = &'a BitwiseOperationLookupChip; #[inline(always)] - fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { let adapter_row: &mut NativeLoadStoreAdapterCols = adapter_row.borrow_mut(); adapter_row.from_state.pc = F::from_canonical_u32(pc); @@ -216,7 +216,7 @@ where #[inline(always)] fn read( - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { @@ -225,7 +225,7 @@ where #[inline(always)] fn write( - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, diff --git a/extensions/native/circuit/src/adapters/mod.rs b/extensions/native/circuit/src/adapters/mod.rs index ea17cb9b55..18525af962 100644 --- a/extensions/native/circuit/src/adapters/mod.rs +++ b/extensions/native/circuit/src/adapters/mod.rs @@ -19,7 +19,7 @@ pub mod native_vectorized_adapter; /// access. #[inline(always)] pub fn timed_read( - memory: &mut TracingMemory, + memory: &mut TracingMemory, ptr: u32, ) -> (u32, [F; BLOCK_SIZE]) where @@ -32,7 +32,7 @@ where #[inline(always)] pub fn timed_write( - memory: &mut TracingMemory, + memory: &mut TracingMemory, ptr: u32, vals: &[F; BLOCK_SIZE], ) -> (u32, [F; BLOCK_SIZE]) @@ -48,7 +48,7 @@ where /// Trace generation relevant to this memory access can be done fully from the recorded buffer. #[inline(always)] pub fn tracing_read( - memory: &mut TracingMemory, + memory: &mut TracingMemory, ptr: u32, (ptr_mut, aux_cols): (&mut F, &mut MemoryReadAuxCols), ) -> [F; BLOCK_SIZE] @@ -65,7 +65,7 @@ where /// Trace generation relevant to this memory access can be done fully from the recorded buffer. #[inline(always)] pub fn tracing_write( - memory: &mut TracingMemory, + memory: &mut TracingMemory, ptr: u32, vals: &[F; BLOCK_SIZE], (ptr_mut, aux_cols): (&mut F, &mut MemoryWriteAuxCols), @@ -83,7 +83,7 @@ pub fn tracing_write( /// Assumes that `addr_space` is [Immediate] or [Native]. #[inline(always)] pub fn tracing_read_or_imm( - memory: &mut TracingMemory, + memory: &mut TracingMemory, addr_space: u32, ptr_or_imm: u32, addr_space_mut: &mut F, diff --git a/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs b/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs index e362532721..fffb338958 100644 --- a/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs +++ b/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs @@ -147,7 +147,7 @@ where type TraceContext<'a> = &'a BitwiseOperationLookupChip; #[inline(always)] - fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { let adapter_row: &mut NativeVectorizedAdapterCols = adapter_row.borrow_mut(); adapter_row.from_state.pc = F::from_canonical_u32(pc); @@ -156,7 +156,7 @@ where #[inline(always)] fn read( - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { @@ -186,7 +186,7 @@ where #[inline(always)] fn write( - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, diff --git a/extensions/native/circuit/src/castf/core.rs b/extensions/native/circuit/src/castf/core.rs index 7f8628025d..30704a1a36 100644 --- a/extensions/native/circuit/src/castf/core.rs +++ b/extensions/native/circuit/src/castf/core.rs @@ -169,7 +169,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/native/circuit/src/field_arithmetic/core.rs b/extensions/native/circuit/src/field_arithmetic/core.rs index 08c01215c7..d9078622fa 100644 --- a/extensions/native/circuit/src/field_arithmetic/core.rs +++ b/extensions/native/circuit/src/field_arithmetic/core.rs @@ -183,7 +183,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/native/circuit/src/field_extension/core.rs b/extensions/native/circuit/src/field_extension/core.rs index c7a7837864..00848ae3c4 100644 --- a/extensions/native/circuit/src/field_extension/core.rs +++ b/extensions/native/circuit/src/field_extension/core.rs @@ -204,7 +204,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/native/circuit/src/loadstore/core.rs b/extensions/native/circuit/src/loadstore/core.rs index b20ecaa58b..e10e7dd870 100644 --- a/extensions/native/circuit/src/loadstore/core.rs +++ b/extensions/native/circuit/src/loadstore/core.rs @@ -195,7 +195,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/adapters/alu.rs b/extensions/rv32im/circuit/src/adapters/alu.rs index eb6e868d5a..f8322c143e 100644 --- a/extensions/rv32im/circuit/src/adapters/alu.rs +++ b/extensions/rv32im/circuit/src/adapters/alu.rs @@ -172,7 +172,7 @@ impl AdapterTraceStep type TraceContext<'a> = &'a BitwiseOperationLookupChip; #[inline(always)] - fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { let adapter_row: &mut Rv32BaseAluAdapterCols = adapter_row.borrow_mut(); adapter_row.from_state.pc = F::from_canonical_u32(pc); adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); @@ -181,7 +181,7 @@ impl AdapterTraceStep #[inline(always)] fn read( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { @@ -224,7 +224,7 @@ impl AdapterTraceStep #[inline(always)] fn write( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, diff --git a/extensions/rv32im/circuit/src/adapters/branch.rs b/extensions/rv32im/circuit/src/adapters/branch.rs index 76d15bb229..25f55bd93d 100644 --- a/extensions/rv32im/circuit/src/adapters/branch.rs +++ b/extensions/rv32im/circuit/src/adapters/branch.rs @@ -120,7 +120,7 @@ where type TraceContext<'a> = (); #[inline(always)] - fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { let adapter_row: &mut Rv32BranchAdapterCols = adapter_row.borrow_mut(); adapter_row.from_state.pc = F::from_canonical_u32(pc); adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); @@ -129,7 +129,7 @@ where #[inline(always)] fn read( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { @@ -161,7 +161,7 @@ where #[inline(always)] fn write( &self, - _memory: &mut TracingMemory, + _memory: &mut TracingMemory, _instruction: &Instruction, _adapter_row: &mut [F], _data: &Self::WriteData, diff --git a/extensions/rv32im/circuit/src/adapters/jalr.rs b/extensions/rv32im/circuit/src/adapters/jalr.rs index 614b15d926..a4cdb9ef1d 100644 --- a/extensions/rv32im/circuit/src/adapters/jalr.rs +++ b/extensions/rv32im/circuit/src/adapters/jalr.rs @@ -153,7 +153,7 @@ where type TraceContext<'a> = (); #[inline(always)] - fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { let adapter_row: &mut Rv32JalrAdapterCols = adapter_row.borrow_mut(); adapter_row.from_state.pc = F::from_canonical_u32(pc); adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); @@ -162,7 +162,7 @@ where #[inline(always)] fn read( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { @@ -184,7 +184,7 @@ where #[inline(always)] fn write( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, diff --git a/extensions/rv32im/circuit/src/adapters/loadstore.rs b/extensions/rv32im/circuit/src/adapters/loadstore.rs index 221d8b3272..7e3285bb47 100644 --- a/extensions/rv32im/circuit/src/adapters/loadstore.rs +++ b/extensions/rv32im/circuit/src/adapters/loadstore.rs @@ -315,7 +315,7 @@ where type TraceContext<'a> = &'a SharedVariableRangeCheckerChip; #[inline(always)] - fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { let adapter_row: &mut Rv32LoadStoreAdapterCols = adapter_row.borrow_mut(); adapter_row.from_state.pc = F::from_canonical_u32(pc); adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); @@ -324,7 +324,7 @@ where #[inline(always)] fn read( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { @@ -409,7 +409,7 @@ where #[inline(always)] fn write( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, diff --git a/extensions/rv32im/circuit/src/adapters/mod.rs b/extensions/rv32im/circuit/src/adapters/mod.rs index c2c3c2f768..bcf906e7fd 100644 --- a/extensions/rv32im/circuit/src/adapters/mod.rs +++ b/extensions/rv32im/circuit/src/adapters/mod.rs @@ -96,8 +96,8 @@ pub fn memory_write( /// Returns `(t_prev, [ptr:4]_{address_space})` where `t_prev` is the timestamp of the last memory /// access. #[inline(always)] -pub fn timed_read( - memory: &mut TracingMemory, +pub fn timed_read( + memory: &mut TracingMemory, address_space: u32, ptr: u32, ) -> (u32, [u8; RV32_REGISTER_NUM_LIMBS]) { @@ -116,8 +116,8 @@ pub fn timed_read( } #[inline(always)] -pub fn timed_write( - memory: &mut TracingMemory, +pub fn timed_write( + memory: &mut TracingMemory, address_space: u32, ptr: u32, val: &[u8; RV32_REGISTER_NUM_LIMBS], @@ -145,7 +145,7 @@ pub fn timed_write( /// Trace generation relevant to this memory access can be done fully from the recorded buffer. #[inline(always)] pub fn tracing_read( - memory: &mut TracingMemory, + memory: &mut TracingMemory, address_space: u32, ptr: u32, aux_cols: &mut MemoryReadAuxCols, /* TODO[jpw]: switch to raw u8 @@ -163,7 +163,7 @@ where /// Trace generation relevant to this memory access can be done fully from the recorded buffer. #[inline(always)] pub fn tracing_write( - memory: &mut TracingMemory, + memory: &mut TracingMemory, address_space: u32, ptr: u32, val: &[u8; RV32_REGISTER_NUM_LIMBS], @@ -183,7 +183,7 @@ pub fn tracing_write( // TODO(ayush): this is bad but not sure how to avoid #[inline(always)] pub fn tracing_write_with_base_aux( - memory: &mut TracingMemory, + memory: &mut TracingMemory, address_space: u32, ptr: u32, val: &[u8; RV32_REGISTER_NUM_LIMBS], @@ -197,7 +197,7 @@ pub fn tracing_write_with_base_aux( #[inline(always)] pub fn tracing_read_imm( - memory: &mut TracingMemory, + memory: &mut TracingMemory, imm: u32, imm_mut: &mut F, ) -> [u8; RV32_REGISTER_NUM_LIMBS] diff --git a/extensions/rv32im/circuit/src/adapters/mul.rs b/extensions/rv32im/circuit/src/adapters/mul.rs index c6f6b998ae..4b937459f2 100644 --- a/extensions/rv32im/circuit/src/adapters/mul.rs +++ b/extensions/rv32im/circuit/src/adapters/mul.rs @@ -137,7 +137,7 @@ where type TraceContext<'a> = (); #[inline(always)] - fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { let adapter_row: &mut Rv32MultAdapterCols = adapter_row.borrow_mut(); adapter_row.from_state.pc = F::from_canonical_u32(pc); adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); @@ -146,7 +146,7 @@ where #[inline(always)] fn read( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { @@ -177,7 +177,7 @@ where #[inline(always)] fn write( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, diff --git a/extensions/rv32im/circuit/src/adapters/rdwrite.rs b/extensions/rv32im/circuit/src/adapters/rdwrite.rs index d3736942cd..e8e1c02c25 100644 --- a/extensions/rv32im/circuit/src/adapters/rdwrite.rs +++ b/extensions/rv32im/circuit/src/adapters/rdwrite.rs @@ -203,7 +203,7 @@ where type TraceContext<'a> = (); #[inline(always)] - fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { let adapter_row: &mut Rv32RdWriteAdapterCols = adapter_row.borrow_mut(); adapter_row.from_state.pc = F::from_canonical_u32(pc); adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); @@ -212,7 +212,7 @@ where #[inline(always)] fn read( &self, - _memory: &mut TracingMemory, + _memory: &mut TracingMemory, _instruction: &Instruction, _adapter_row: &mut [F], ) -> Self::ReadData { @@ -221,7 +221,7 @@ where #[inline(always)] fn write( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, @@ -300,7 +300,7 @@ where type TraceContext<'a> = (); #[inline(always)] - fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { let adapter_row: &mut Rv32CondRdWriteAdapterCols = adapter_row.borrow_mut(); adapter_row.inner.from_state.pc = F::from_canonical_u32(pc); @@ -310,7 +310,7 @@ where #[inline(always)] fn read( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { @@ -325,7 +325,7 @@ where #[inline(always)] fn write( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, diff --git a/extensions/rv32im/circuit/src/auipc/core.rs b/extensions/rv32im/circuit/src/auipc/core.rs index 1ac4641ad2..6d75088c72 100644 --- a/extensions/rv32im/circuit/src/auipc/core.rs +++ b/extensions/rv32im/circuit/src/auipc/core.rs @@ -228,7 +228,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/base_alu/core.rs b/extensions/rv32im/circuit/src/base_alu/core.rs index c8cd3f3708..072e854b42 100644 --- a/extensions/rv32im/circuit/src/base_alu/core.rs +++ b/extensions/rv32im/circuit/src/base_alu/core.rs @@ -211,7 +211,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/base_alu/tests.rs b/extensions/rv32im/circuit/src/base_alu/tests.rs index d5547679db..313bd83f52 100644 --- a/extensions/rv32im/circuit/src/base_alu/tests.rs +++ b/extensions/rv32im/circuit/src/base_alu/tests.rs @@ -385,7 +385,7 @@ where as AdapterTraceStep>::TraceContext<'a>; #[inline(always)] - fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { as AdapterTraceStep>::start( pc, memory, @@ -396,7 +396,7 @@ where #[inline(always)] fn read( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { @@ -452,7 +452,7 @@ where #[inline(always)] fn write( &self, - memory: &mut TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, diff --git a/extensions/rv32im/circuit/src/branch_eq/core.rs b/extensions/rv32im/circuit/src/branch_eq/core.rs index 7d3b44c913..7e6928d62a 100644 --- a/extensions/rv32im/circuit/src/branch_eq/core.rs +++ b/extensions/rv32im/circuit/src/branch_eq/core.rs @@ -175,7 +175,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/branch_lt/core.rs b/extensions/rv32im/circuit/src/branch_lt/core.rs index f5542102d8..9ae94ac71b 100644 --- a/extensions/rv32im/circuit/src/branch_lt/core.rs +++ b/extensions/rv32im/circuit/src/branch_lt/core.rs @@ -237,7 +237,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/divrem/core.rs b/extensions/rv32im/circuit/src/divrem/core.rs index 05826cc0f3..486f5c1ca7 100644 --- a/extensions/rv32im/circuit/src/divrem/core.rs +++ b/extensions/rv32im/circuit/src/divrem/core.rs @@ -412,7 +412,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/jal_lui/core.rs b/extensions/rv32im/circuit/src/jal_lui/core.rs index 48d74d5706..eec4ab73ef 100644 --- a/extensions/rv32im/circuit/src/jal_lui/core.rs +++ b/extensions/rv32im/circuit/src/jal_lui/core.rs @@ -183,7 +183,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/jalr/core.rs b/extensions/rv32im/circuit/src/jalr/core.rs index cac574e0a8..fc6f3772f8 100644 --- a/extensions/rv32im/circuit/src/jalr/core.rs +++ b/extensions/rv32im/circuit/src/jalr/core.rs @@ -218,7 +218,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/less_than/core.rs b/extensions/rv32im/circuit/src/less_than/core.rs index 9da8b4507f..4632176455 100644 --- a/extensions/rv32im/circuit/src/less_than/core.rs +++ b/extensions/rv32im/circuit/src/less_than/core.rs @@ -210,7 +210,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/load_sign_extend/core.rs b/extensions/rv32im/circuit/src/load_sign_extend/core.rs index c0391f033c..d6dccee0b0 100644 --- a/extensions/rv32im/circuit/src/load_sign_extend/core.rs +++ b/extensions/rv32im/circuit/src/load_sign_extend/core.rs @@ -223,7 +223,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/loadstore/core.rs b/extensions/rv32im/circuit/src/loadstore/core.rs index af73a6eb30..99564e910c 100644 --- a/extensions/rv32im/circuit/src/loadstore/core.rs +++ b/extensions/rv32im/circuit/src/loadstore/core.rs @@ -295,7 +295,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/mul/core.rs b/extensions/rv32im/circuit/src/mul/core.rs index d83c45ac08..79a3c65044 100644 --- a/extensions/rv32im/circuit/src/mul/core.rs +++ b/extensions/rv32im/circuit/src/mul/core.rs @@ -171,7 +171,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/mulh/core.rs b/extensions/rv32im/circuit/src/mulh/core.rs index 4311edaca6..e1578f0613 100644 --- a/extensions/rv32im/circuit/src/mulh/core.rs +++ b/extensions/rv32im/circuit/src/mulh/core.rs @@ -245,7 +245,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/shift/core.rs b/extensions/rv32im/circuit/src/shift/core.rs index b796e7c9db..8f2ba4f2d6 100644 --- a/extensions/rv32im/circuit/src/shift/core.rs +++ b/extensions/rv32im/circuit/src/shift/core.rs @@ -286,7 +286,7 @@ where fn execute( &mut self, - state: VmStateMut, + state: VmStateMut, CTX>, instruction: &Instruction, row_slice: &mut [F], ) -> Result<()> { From 8c2d6af9f57b07122e6f1a7182437e49572200be Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Fri, 2 May 2025 11:06:57 -0700 Subject: [PATCH 10/49] fix(new-execution): make rv32im hintstore work (#1616) - make `Rv32HintStoreChip` use the `NewVmChipWrapper` - rename `SingleTraceStep` to `TraceStep` and update it to work for chips whose execution creates multiple trace rows - comment out criterion execute benchmarks for now --- Cargo.lock | 66 ++- benchmarks/execute/Cargo.toml | 13 +- benchmarks/execute/benches/execute.rs | 57 +++ .../execute/benches/fibonacci_execute.rs | 12 +- benchmarks/execute/benches/regex_execute.rs | 80 ++-- crates/vm/src/arch/execution_control.rs | 1 + crates/vm/src/arch/integration_api.rs | 73 ++-- crates/vm/src/system/memory/controller/mod.rs | 2 +- crates/vm/src/system/public_values/core.rs | 8 +- extensions/algebra/moduli-macros/src/lib.rs | 2 - extensions/native/circuit/src/castf/core.rs | 2 +- .../circuit/src/field_arithmetic/core.rs | 4 +- .../circuit/src/field_extension/core.rs | 2 +- .../native/circuit/src/loadstore/core.rs | 3 +- extensions/rv32im/circuit/src/auipc/core.rs | 11 +- .../rv32im/circuit/src/base_alu/core.rs | 11 +- .../rv32im/circuit/src/branch_eq/core.rs | 11 +- .../rv32im/circuit/src/branch_lt/core.rs | 11 +- extensions/rv32im/circuit/src/divrem/core.rs | 11 +- extensions/rv32im/circuit/src/extension.rs | 28 +- .../rv32im/circuit/src/hintstore/mod.rs | 393 ++++++++---------- .../rv32im/circuit/src/hintstore/tests.rs | 86 ++-- extensions/rv32im/circuit/src/jal_lui/core.rs | 11 +- extensions/rv32im/circuit/src/jalr/core.rs | 11 +- .../rv32im/circuit/src/less_than/core.rs | 11 +- .../circuit/src/load_sign_extend/core.rs | 13 +- .../rv32im/circuit/src/loadstore/core.rs | 13 +- extensions/rv32im/circuit/src/mul/core.rs | 11 +- extensions/rv32im/circuit/src/mulh/core.rs | 11 +- extensions/rv32im/circuit/src/shift/core.rs | 11 +- 30 files changed, 581 insertions(+), 398 deletions(-) create mode 100644 benchmarks/execute/benches/execute.rs diff --git a/Cargo.lock b/Cargo.lock index 0faf467b81..a6692d798e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1423,6 +1423,7 @@ dependencies = [ "anstyle", "clap_lex", "strsim", + "terminal_size", ] [[package]] @@ -1449,6 +1450,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "condtype" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf0a07a401f374238ab8e2f11a104d2851bf9ce711ec69804834de8af45c7af" + [[package]] name = "const-default" version = "1.0.0" @@ -1944,6 +1951,31 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "divan" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a405457ec78b8fe08b0e32b4a3570ab5dff6dd16eb9e76a5ee0a9d9cbd898933" +dependencies = [ + "cfg-if", + "clap", + "condtype", + "divan-macros", + "libc", + "regex-lite", +] + +[[package]] +name = "divan-macros" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9556bc800956545d6420a640173e5ba7dfa82f38d3ea5a167eb555bc69ac3323" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "downcast-rs" version = "1.2.1" @@ -3494,6 +3526,12 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + [[package]] name = "litemap" version = "0.7.4" @@ -4021,6 +4059,7 @@ dependencies = [ "clap", "criterion", "derive_more 1.0.0", + "divan", "eyre", "openvm-benchmarks-utils", "openvm-circuit", @@ -6303,7 +6342,20 @@ dependencies = [ "bitflags 2.8.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags 2.8.0", + "errno", + "libc", + "linux-raw-sys 0.9.4", "windows-sys 0.59.0", ] @@ -7066,7 +7118,7 @@ dependencies = [ "fastrand", "getrandom 0.3.1", "once_cell", - "rustix", + "rustix 0.38.44", "windows-sys 0.59.0", ] @@ -7081,6 +7133,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "terminal_size" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" +dependencies = [ + "rustix 1.0.7", + "windows-sys 0.59.0", +] + [[package]] name = "test-case" version = "3.3.1" diff --git a/benchmarks/execute/Cargo.toml b/benchmarks/execute/Cargo.toml index 67ce6ac821..8a77dc4a5f 100644 --- a/benchmarks/execute/Cargo.toml +++ b/benchmarks/execute/Cargo.toml @@ -28,6 +28,7 @@ tracing-subscriber = { version = "0.3.17", features = ["std", "env-filter"] } [dev-dependencies] criterion = { version = "0.5", features = ["html_reports"] } +divan = { version = "0.1.21" } [features] default = ["jemalloc"] @@ -37,12 +38,16 @@ jemalloc-prof = ["openvm-circuit/jemalloc-prof"] nightly-features = ["openvm-circuit/nightly-features"] profiling = ["openvm-circuit/function-span", "openvm-transpiler/function-span"] -[[bench]] -name = "fibonacci_execute" -harness = false +# [[bench]] +# name = "fibonacci_execute" +# harness = false + +# [[bench]] +# name = "regex_execute" +# harness = false [[bench]] -name = "regex_execute" +name = "execute" harness = false [package.metadata.cargo-shear] diff --git a/benchmarks/execute/benches/execute.rs b/benchmarks/execute/benches/execute.rs new file mode 100644 index 0000000000..0ca09e19e4 --- /dev/null +++ b/benchmarks/execute/benches/execute.rs @@ -0,0 +1,57 @@ +use divan; +use eyre::Result; +use openvm_benchmarks_utils::{get_elf_path, get_programs_dir, read_elf_file}; +use openvm_circuit::arch::{instructions::exe::VmExe, VmExecutor}; +use openvm_rv32im_circuit::Rv32ImConfig; +use openvm_rv32im_transpiler::{ + Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, +}; +use openvm_stark_sdk::p3_baby_bear::BabyBear; +use openvm_transpiler::{transpiler::Transpiler, FromElf}; +use std::path::PathBuf; + +static AVAILABLE_PROGRAMS: &[&str] = &[ + "fibonacci_recursive", + "fibonacci_iterative", + "quicksort", + "bubblesort", + // "pairing", + // "keccak256", + // "keccak256_iter", + // "sha256", + // "sha256_iter", + // "revm_transfer", + // "revm_snailtracer", +]; + +fn main() { + divan::main(); +} + +/// Run a specific OpenVM program +fn run_program(program: &str) -> Result<()> { + let program_dir = get_programs_dir().join(program); + let elf_path = get_elf_path(&program_dir); + let elf = read_elf_file(&elf_path)?; + + let vm_config = Rv32ImConfig::default(); + + let transpiler = Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(Rv32MTranspilerExtension); + + let exe = VmExe::from_elf(elf, transpiler)?; + + let executor = VmExecutor::new(vm_config); + executor + .execute_e1(exe, vec![]) + .expect("Failed to execute program"); + + Ok(()) +} + +#[divan::bench(args = AVAILABLE_PROGRAMS, sample_count=10)] +fn benchmark_execute(program: &str) { + run_program(program).unwrap(); +} diff --git a/benchmarks/execute/benches/fibonacci_execute.rs b/benchmarks/execute/benches/fibonacci_execute.rs index 70952b53c9..d7eb47f04f 100644 --- a/benchmarks/execute/benches/fibonacci_execute.rs +++ b/benchmarks/execute/benches/fibonacci_execute.rs @@ -5,7 +5,8 @@ use openvm_rv32im_circuit::Rv32ImConfig; use openvm_rv32im_transpiler::{ Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, }; -use openvm_sdk::StdIn; +// TODO(ayush): add this back +// use openvm_sdk::StdIn; use openvm_stark_sdk::p3_baby_bear::BabyBear; use openvm_transpiler::{transpiler::Transpiler, FromElf}; @@ -28,10 +29,11 @@ fn benchmark_function(c: &mut Criterion) { group.bench_function("execute", |b| { b.iter(|| { - let n = 100_000u64; - let mut stdin = StdIn::default(); - stdin.write(&n); - executor.execute(exe.clone(), stdin).unwrap(); + // TODO(ayush): add this back + // let n = 100_000u64; + // let mut stdin = StdIn::default(); + // stdin.write(&n); + executor.execute(exe.clone(), vec![]).unwrap(); }) }); diff --git a/benchmarks/execute/benches/regex_execute.rs b/benchmarks/execute/benches/regex_execute.rs index a3a110e344..d4116b5aab 100644 --- a/benchmarks/execute/benches/regex_execute.rs +++ b/benchmarks/execute/benches/regex_execute.rs @@ -1,47 +1,47 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use openvm_benchmarks_utils::{build_elf, get_programs_dir}; -use openvm_circuit::arch::{instructions::exe::VmExe, VmExecutor}; -use openvm_keccak256_circuit::Keccak256Rv32Config; -use openvm_keccak256_transpiler::Keccak256TranspilerExtension; -use openvm_rv32im_transpiler::{ - Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, -}; -use openvm_sdk::StdIn; -use openvm_stark_sdk::p3_baby_bear::BabyBear; -use openvm_transpiler::{transpiler::Transpiler, FromElf}; +// TODO(ayush): add this back +// use criterion::{black_box, criterion_group, criterion_main, Criterion}; +// use openvm_benchmarks_utils::{build_elf, get_programs_dir}; +// use openvm_circuit::arch::{instructions::exe::VmExe, VmExecutor}; +// use openvm_keccak256_circuit::Keccak256Rv32Config; +// use openvm_keccak256_transpiler::Keccak256TranspilerExtension; +// use openvm_rv32im_transpiler::{ +// Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, +// }; +// use openvm_sdk::StdIn; +// use openvm_stark_sdk::p3_baby_bear::BabyBear; +// use openvm_transpiler::{transpiler::Transpiler, FromElf}; -fn benchmark_function(c: &mut Criterion) { - let program_dir = get_programs_dir().join("regex"); - let elf = build_elf(&program_dir, "release").unwrap(); +// fn benchmark_function(c: &mut Criterion) { +// let program_dir = get_programs_dir().join("regex"); +// let elf = build_elf(&program_dir, "release").unwrap(); - let exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(Keccak256TranspilerExtension), - ) - .unwrap(); +// let exe = VmExe::from_elf( +// elf, +// Transpiler::::default() +// .with_extension(Rv32ITranspilerExtension) +// .with_extension(Rv32MTranspilerExtension) +// .with_extension(Rv32IoTranspilerExtension) +// .with_extension(Keccak256TranspilerExtension), +// ) +// .unwrap(); - let mut group = c.benchmark_group("regex"); - group.sample_size(10); - let config = Keccak256Rv32Config::default(); - let executor = VmExecutor::::new(config); +// let mut group = c.benchmark_group("regex"); +// group.sample_size(10); +// let config = Keccak256Rv32Config::default(); +// let executor = VmExecutor::::new(config); - let data = include_str!("../../guest/regex/regex_email.txt"); +// let data = include_str!("../../guest/regex/regex_email.txt"); - let fe_bytes = data.to_owned().into_bytes(); - group.bench_function("execute", |b| { - b.iter(|| { - executor - .execute(exe.clone(), black_box(StdIn::from_bytes(&fe_bytes))) - .unwrap(); - }) - }); +// let fe_bytes = data.to_owned().into_bytes(); +// group.bench_function("execute", |b| { +// b.iter(|| { +// let input = black_box(Stdin::from_bytes(&fe_bytes)); +// executor.execute(exe.clone(), input).unwrap(); +// }) +// }); - group.finish(); -} +// group.finish(); +// } -criterion_group!(benches, benchmark_function); -criterion_main!(benches); +// criterion_group!(benches, benchmark_function); +// criterion_main!(benches); diff --git a/crates/vm/src/arch/execution_control.rs b/crates/vm/src/arch/execution_control.rs index b62eac097a..3fe18c6961 100644 --- a/crates/vm/src/arch/execution_control.rs +++ b/crates/vm/src/arch/execution_control.rs @@ -26,6 +26,7 @@ where fn new(chip_complex: &VmChipComplex) -> Self; /// Determines if execution should stop + // TODO(ayush): rename to should_suspend fn should_stop(&mut self, chip_complex: &VmChipComplex) -> bool; diff --git a/crates/vm/src/arch/integration_api.rs b/crates/vm/src/arch/integration_api.rs index d0200f9834..5b22d16e25 100644 --- a/crates/vm/src/arch/integration_api.rs +++ b/crates/vm/src/arch/integration_api.rs @@ -216,26 +216,45 @@ pub struct AdapterAirContext> { pub instruction: I::ProcessedInstruction, } -/// Interface for trace generation when the state transition step of a single instruction -/// uses only one trace row. The trace row is provided as a mutable buffer during both -/// instruction execution and trace generation. +/// Interface for trace generation of a single instruction.The trace is provided as a mutable +/// buffer during both instruction execution and trace generation. /// It is expected that no additional memory allocation is necessary and the trace buffer /// is sufficient, with possible overwriting. -pub trait SingleTraceStep { +pub trait TraceStep { fn execute( &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + // TODO(ayush): combine to a single struct + trace: &mut [F], + trace_offset: &mut usize, + // TODO(ayush): move air inside step and remove width + width: usize, ) -> Result<()>; + /// Populates `trace`. This function will always be called after + /// [`TraceStep::execute`], so the `trace` should already contain context necessary to + /// fill in the rest of it. + // TODO(ayush): come up with a better abstraction for chips that fill a dynamic number of rows + fn fill_trace(&self, mem_helper: &MemoryAuxColsFactory, trace: &mut [F], width: usize) + where + Self: Send + Sync, + F: Send + Sync, + { + trace.par_chunks_exact_mut(width).for_each(|row_slice| { + self.fill_trace_row(mem_helper, row_slice); + }); + } + /// Populates `row_slice`. This function will always be called after - /// [`SingleTraceStep::execute`], so the `row_slice` should already contain context necessary to + /// [`TraceStep::execute`], so the `row_slice` should already contain context necessary to /// fill in the rest of the row. This function will be called for each row in the trace which is /// being used, and all other rows in the trace will be filled with zeroes. /// /// The provided `row_slice` will have length equal to the width of the AIR. - fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]); + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + unreachable!("fill_trace_row is not implemented") + } /// Returns a list of public values to publish. fn generate_public_values(&self) -> Vec { @@ -251,6 +270,7 @@ pub struct NewVmChipWrapper { pub air: AIR, pub step: STEP, pub trace_buffer: Vec, + // TODO(ayush): width should be a constant? width: usize, buffer_idx: usize, mem_helper: SharedMemoryHelper, @@ -279,7 +299,7 @@ where impl InstructionExecutor for NewVmChipWrapper where F: PrimeField32, - STEP: SingleTraceStep // TODO: CTX? + STEP: TraceStep // TODO: CTX? + StepExecutorE1, { fn execute( @@ -294,20 +314,13 @@ where memory: &mut memory.memory, ctx: &mut (), }; - let start_idx = self.buffer_idx; - self.buffer_idx += self.width; - if self.buffer_idx > self.trace_buffer.len() { - return Err(ExecutionError::TraceBufferOutOfBounds { - requested: self.buffer_idx, - capacity: self.trace_buffer.len(), - }); - } - // SAFETY: bound checked above - let row_slice = unsafe { - self.trace_buffer - .get_unchecked_mut(start_idx..self.buffer_idx) - }; - self.step.execute(state, instruction, row_slice)?; + self.step.execute( + state, + instruction, + &mut self.trace_buffer, + &mut self.buffer_idx, + self.width, + )?; Ok(ExecutionState { pc, @@ -328,7 +341,7 @@ impl Chip for NewVmChipWrapper, AIR, STEP> where SC: StarkGenericConfig, Val: PrimeField32, - STEP: SingleTraceStep, ()> + Send + Sync, + STEP: TraceStep, ()> + Send + Sync, AIR: Clone + AnyRap + 'static, { fn air(&self) -> AirRef { @@ -346,11 +359,11 @@ where // This zip only goes through used rows. // TODO: check if zero-init assumption changes // The padding(=dummy) rows between rows_used..height are ASSUMED to be filled with zeros. - self.trace_buffer[..rows_used * self.width] - .par_chunks_exact_mut(self.width) - .for_each(|row_slice| { - self.step.fill_trace_row(&mem_helper, row_slice); - }); + self.step.fill_trace( + &mem_helper, + &mut self.trace_buffer[..rows_used * self.width], + self.width, + ); drop(self.mem_helper); let trace = RowMajorMatrix::new(self.trace_buffer, self.width); // self.inner.finalize(&mut trace, num_records); @@ -376,9 +389,9 @@ where // TODO[jpw]: switch read,write to store into abstract buffer, then fill_trace_row using buffer /// A helper trait for expressing generic state accesses within the implementation of -/// [SingleTraceStep]. Note that this is only a helper trait when the same interface of state access +/// [TraceStep]. Note that this is only a helper trait when the same interface of state access /// is reused or shared by multiple implementations. It is not required to implement this trait if -/// it is easier to implement the [SingleTraceStep] trait directly without this trait. +/// it is easier to implement the [TraceStep] trait directly without this trait. pub trait AdapterTraceStep { /// Adapter row width const WIDTH: usize; diff --git a/crates/vm/src/system/memory/controller/mod.rs b/crates/vm/src/system/memory/controller/mod.rs index f98ae783ec..bca5999f52 100644 --- a/crates/vm/src/system/memory/controller/mod.rs +++ b/crates/vm/src/system/memory/controller/mod.rs @@ -101,7 +101,7 @@ pub struct MemoryController { // Store separately to avoid smart pointer reference each time range_checker_bus: VariableRangeCheckerBus, // addr_space -> Memory data structure - pub(crate) memory: TracingMemory, + pub memory: TracingMemory, /// A reference to the `OfflineMemory`. Will be populated after `finalize()`. pub offline_memory: Arc>>, pub access_adapters: AccessAdapterInventory, diff --git a/crates/vm/src/system/public_values/core.rs b/crates/vm/src/system/public_values/core.rs index 90378d210f..0641b41f0d 100644 --- a/crates/vm/src/system/public_values/core.rs +++ b/crates/vm/src/system/public_values/core.rs @@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize}; use crate::{ arch::{ AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, AdapterTraceStep, - BasicAdapterInterface, MinimalInstruction, Result, SingleTraceStep, StepExecutorE1, + BasicAdapterInterface, MinimalInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmCoreChip, VmStateMut, }, system::{ @@ -141,7 +141,7 @@ where } } -impl SingleTraceStep for PublicValuesStep +impl TraceStep for PublicValuesStep where F: PrimeField32, A: 'static @@ -164,7 +164,9 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { todo!("Implement execute function"); } diff --git a/extensions/algebra/moduli-macros/src/lib.rs b/extensions/algebra/moduli-macros/src/lib.rs index 5d8d921f2f..bfd0f1362c 100644 --- a/extensions/algebra/moduli-macros/src/lib.rs +++ b/extensions/algebra/moduli-macros/src/lib.rs @@ -746,8 +746,6 @@ pub fn moduli_init(input: TokenStream) -> TokenStream { for (mod_idx, item) in items.into_iter().enumerate() { let modulus = item.value(); - println!("[init] modulus #{} = {}", mod_idx, modulus); - let modulus_bytes = string_to_bytes(&modulus); let mut limbs = modulus_bytes.len(); let mut block_size = 32; diff --git a/extensions/native/circuit/src/castf/core.rs b/extensions/native/circuit/src/castf/core.rs index 30704a1a36..b79f517103 100644 --- a/extensions/native/circuit/src/castf/core.rs +++ b/extensions/native/circuit/src/castf/core.rs @@ -151,7 +151,7 @@ impl CastFStep { } } -impl SingleTraceStep for CastFStep +impl TraceStep for CastFStep where F: PrimeField32, A: 'static diff --git a/extensions/native/circuit/src/field_arithmetic/core.rs b/extensions/native/circuit/src/field_arithmetic/core.rs index d9078622fa..d9ab7d0c7f 100644 --- a/extensions/native/circuit/src/field_arithmetic/core.rs +++ b/extensions/native/circuit/src/field_arithmetic/core.rs @@ -7,7 +7,7 @@ use itertools::izip; use openvm_circuit::{ arch::{ AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, InsExecutorE1, - MinimalInstruction, Result, SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, + MinimalInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, }, system::memory::online::GuestMemory, @@ -162,7 +162,7 @@ impl FieldArithmeticStep { } } -impl SingleTraceStep for FieldArithmeticStep +impl TraceStep for FieldArithmeticStep where F: PrimeField32, A: 'static diff --git a/extensions/native/circuit/src/field_extension/core.rs b/extensions/native/circuit/src/field_extension/core.rs index 00848ae3c4..b96b3a6bab 100644 --- a/extensions/native/circuit/src/field_extension/core.rs +++ b/extensions/native/circuit/src/field_extension/core.rs @@ -183,7 +183,7 @@ impl FieldExtensionChip { } } -impl SingleTraceStep for FieldExtensionStep +impl TraceStep for FieldExtensionStep where F: PrimeField32, A: 'static diff --git a/extensions/native/circuit/src/loadstore/core.rs b/extensions/native/circuit/src/loadstore/core.rs index e10e7dd870..3a9aa498f0 100644 --- a/extensions/native/circuit/src/loadstore/core.rs +++ b/extensions/native/circuit/src/loadstore/core.rs @@ -173,8 +173,7 @@ where } } -impl SingleTraceStep - for NativeLoadStoreStep +impl TraceStep for NativeLoadStoreStep where F: PrimeField32, A: 'static diff --git a/extensions/rv32im/circuit/src/auipc/core.rs b/extensions/rv32im/circuit/src/auipc/core.rs index 6d75088c72..a507e1e58c 100644 --- a/extensions/rv32im/circuit/src/auipc/core.rs +++ b/extensions/rv32im/circuit/src/auipc/core.rs @@ -6,7 +6,7 @@ use std::{ use openvm_circuit::{ arch::{ AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ImmInstruction, Result, - SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -210,7 +210,7 @@ impl Rv32AuipcStep { } } -impl SingleTraceStep for Rv32AuipcStep +impl TraceStep for Rv32AuipcStep where F: PrimeField32, A: 'static @@ -230,13 +230,16 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { let Instruction { opcode, c: imm, .. } = instruction; let local_opcode = Rv32AuipcOpcode::from_usize(opcode.local_opcode_idx(Rv32AuipcOpcode::CLASS_OFFSET)); + let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -257,6 +260,8 @@ where // TODO(ayush): add increment_pc function to vmstate *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *trace_offset += width; + Ok(()) } diff --git a/extensions/rv32im/circuit/src/base_alu/core.rs b/extensions/rv32im/circuit/src/base_alu/core.rs index 072e854b42..21281996e2 100644 --- a/extensions/rv32im/circuit/src/base_alu/core.rs +++ b/extensions/rv32im/circuit/src/base_alu/core.rs @@ -7,7 +7,7 @@ use std::{ use openvm_circuit::{ arch::{ AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, - SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -192,7 +192,7 @@ impl BaseAluStep SingleTraceStep +impl TraceStep for BaseAluStep where F: PrimeField32, @@ -213,12 +213,15 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { let Instruction { opcode, .. } = instruction; let local_opcode = BaseAluOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -242,6 +245,8 @@ where *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *trace_offset += width; + Ok(()) } diff --git a/extensions/rv32im/circuit/src/branch_eq/core.rs b/extensions/rv32im/circuit/src/branch_eq/core.rs index 7e6928d62a..ba464dbd9b 100644 --- a/extensions/rv32im/circuit/src/branch_eq/core.rs +++ b/extensions/rv32im/circuit/src/branch_eq/core.rs @@ -6,7 +6,7 @@ use std::{ use openvm_circuit::{ arch::{ AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ImmInstruction, Result, - SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -157,7 +157,7 @@ impl BranchEqualStep { } } -impl SingleTraceStep for BranchEqualStep +impl TraceStep for BranchEqualStep where F: PrimeField32, A: 'static @@ -177,12 +177,15 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { let &Instruction { opcode, c: imm, .. } = instruction; let branch_eq_opcode = BranchEqualOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -207,6 +210,8 @@ where *state.pc = state.pc.wrapping_add(self.pc_step); } + *trace_offset += width; + Ok(()) } diff --git a/extensions/rv32im/circuit/src/branch_lt/core.rs b/extensions/rv32im/circuit/src/branch_lt/core.rs index 9ae94ac71b..6f15ec80af 100644 --- a/extensions/rv32im/circuit/src/branch_lt/core.rs +++ b/extensions/rv32im/circuit/src/branch_lt/core.rs @@ -6,7 +6,7 @@ use std::{ use openvm_circuit::{ arch::{ AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ImmInstruction, Result, - SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -215,7 +215,7 @@ impl } } -impl SingleTraceStep +impl TraceStep for BranchLessThanStep where F: PrimeField32, @@ -239,12 +239,15 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { let &Instruction { opcode, c: imm, .. } = instruction; let blt_opcode = BranchLessThanOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -336,6 +339,8 @@ where self.bitwise_lookup_chip.request_range(diff_val - 1, 0); } + *trace_offset += width; + Ok(()) } diff --git a/extensions/rv32im/circuit/src/divrem/core.rs b/extensions/rv32im/circuit/src/divrem/core.rs index 486f5c1ca7..8e38e6aabc 100644 --- a/extensions/rv32im/circuit/src/divrem/core.rs +++ b/extensions/rv32im/circuit/src/divrem/core.rs @@ -8,7 +8,7 @@ use num_integer::Integer; use openvm_circuit::{ arch::{ AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, - SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -393,7 +393,7 @@ impl DivRemStep SingleTraceStep +impl TraceStep for DivRemStep where F: PrimeField32, @@ -414,7 +414,9 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { let Instruction { opcode, .. } = instruction; @@ -423,6 +425,7 @@ where let is_signed = divrem_opcode == DivRemOpcode::DIV || divrem_opcode == DivRemOpcode::REM; let is_div = divrem_opcode == DivRemOpcode::DIV || divrem_opcode == DivRemOpcode::DIVU; + let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -515,6 +518,8 @@ where *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *trace_offset += width; + Ok(()) } diff --git a/extensions/rv32im/circuit/src/extension.rs b/extensions/rv32im/circuit/src/extension.rs index 635a4f0ec4..a802419c7b 100644 --- a/extensions/rv32im/circuit/src/extension.rs +++ b/extensions/rv32im/circuit/src/extension.rs @@ -25,7 +25,7 @@ use strum::IntoEnumIterator; use crate::{adapters::*, *}; // TODO(ayush): this should be decided after e2 execution -const MAX_INS_CAPACITY: usize = 0; +const MAX_INS_CAPACITY: usize = 1 << 22; /// Config for a VM with base extension and IO extension #[derive(Clone, Debug, VmConfig, derive_new::new, Serialize, Deserialize)] @@ -583,16 +583,24 @@ impl VmExtension for Rv32Io { chip }; - let mut hintstore_chip = Rv32HintStoreChip::new( - execution_bus, - program_bus, - bitwise_lu_chip.clone(), - memory_bridge, - offline_memory.clone(), - builder.system_config().memory_config.pointer_max_bits, - Rv32HintStoreOpcode::CLASS_OFFSET, + let mut hintstore_chip = Rv32HintStoreChip::::new( + Rv32HintStoreAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + bitwise_lu_chip.bus(), + Rv32HintStoreOpcode::CLASS_OFFSET, + builder.system_config().memory_config.pointer_max_bits, + ), + Rv32HintStoreStep::new( + bitwise_lu_chip, + offline_memory.clone(), + builder.system_config().memory_config.pointer_max_bits, + Rv32HintStoreOpcode::CLASS_OFFSET, + ), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); - hintstore_chip.set_streams(builder.streams().clone()); + hintstore_chip.step.set_streams(builder.streams().clone()); inventory.add_executor( hintstore_chip, diff --git a/extensions/rv32im/circuit/src/hintstore/mod.rs b/extensions/rv32im/circuit/src/hintstore/mod.rs index 3b643fa5ed..c679aa27ca 100644 --- a/extensions/rv32im/circuit/src/hintstore/mod.rs +++ b/extensions/rv32im/circuit/src/hintstore/mod.rs @@ -6,12 +6,13 @@ use std::{ use openvm_circuit::{ arch::{ ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InsExecutorE1, - InstructionExecutor, Streams, VmStateMut, + InstructionExecutor, NewVmChipWrapper, Result, StepExecutorE1, Streams, TraceStep, + VmStateMut, }, system::{ memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, - online::GuestMemory, + online::{GuestMemory, TracingMemory}, MemoryAddress, MemoryAuxColsFactory, MemoryController, OfflineMemory, RecordId, }, program::ProgramBus, @@ -42,12 +43,15 @@ use openvm_stark_backend::{ rap::{AnyRap, BaseAirWithPublicValues, PartitionedBaseAir}, Chip, ChipUsageGetter, }; +use rand::distributions::weighted; use serde::{Deserialize, Serialize}; -use crate::adapters::{decompose, tmp_convert_to_u8s}; +use crate::adapters::{ + decompose, memory_read, memory_write, tmp_convert_to_u8s, tracing_read, tracing_write, +}; -// #[cfg(test)] -// mod tests; +#[cfg(test)] +mod tests; #[repr(C)] #[derive(AlignedBorrow, Debug)] @@ -72,7 +76,7 @@ pub struct Rv32HintStoreCols { pub num_words_aux_cols: MemoryReadAuxCols, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, derive_new::new)] pub struct Rv32HintStoreAir { pub execution_bridge: ExecutionBridge, pub memory_bridge: MemoryBridge, @@ -279,53 +283,57 @@ pub struct Rv32HintStoreRecord { pub hints: Vec<([F; RV32_REGISTER_NUM_LIMBS], RecordId)>, } -pub struct Rv32HintStoreChip { - air: Rv32HintStoreAir, - pub records: Vec>, - pub height: usize, +pub struct Rv32HintStoreStep { + pointer_max_bits: usize, + offset: usize, offline_memory: Arc>>, pub streams: OnceLock>>>, bitwise_lookup_chip: SharedBitwiseOperationLookupChip, } -impl Rv32HintStoreChip { +impl Rv32HintStoreStep { pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - memory_bridge: MemoryBridge, offline_memory: Arc>>, pointer_max_bits: usize, offset: usize, ) -> Self { - let air = Rv32HintStoreAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - bitwise_operation_lookup_bus: bitwise_lookup_chip.bus(), - offset, - pointer_max_bits, - }; Self { - records: vec![], - air, - height: 0, + pointer_max_bits, + offset, offline_memory, streams: OnceLock::new(), bitwise_lookup_chip, } } + pub fn set_streams(&mut self, streams: Arc>>) { self.streams.set(streams).unwrap(); } } -impl InstructionExecutor for Rv32HintStoreChip { +impl TraceStep for Rv32HintStoreStep +where + F: PrimeField32, +{ + fn get_opcode_name(&self, opcode: usize) -> String { + if opcode == HINT_STOREW.global_opcode().as_usize() { + String::from("HINT_STOREW") + } else if opcode == HINT_BUFFER.global_opcode().as_usize() { + String::from("HINT_BUFFER") + } else { + unreachable!("unsupported opcode: {}", opcode) + } + } + fn execute( &mut self, - memory: &mut MemoryController, + state: VmStateMut, CTX>, instruction: &Instruction, - from_state: ExecutionState, - ) -> Result, ExecutionError> { + trace: &mut [F], + trace_offset: &mut usize, + width: usize, + ) -> Result<()> { let &Instruction { opcode, a: num_words_ptr, @@ -334,200 +342,126 @@ impl InstructionExecutor for Rv32HintStoreChip { e, .. } = instruction; + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); debug_assert_eq!(e.as_canonical_u32(), RV32_MEMORY_AS); - let local_opcode = - Rv32HintStoreOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - - let (mem_ptr_read, mem_ptr_limbs) = todo!(); - // memory.read::(d, mem_ptr_ptr); - let (num_words, num_words_read) = if local_opcode == HINT_STOREW { - memory.increment_timestamp(); - (1, None) - } else { - let (num_words_read, num_words_limbs) = todo!(); - // memory.read::(d, num_words_ptr); - (u32::from_le_bytes(num_words_limbs), Some(num_words_read)) - }; - debug_assert_ne!(num_words, 0); - debug_assert!(num_words <= (1 << self.air.pointer_max_bits)); + let local_opcode = Rv32HintStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + + let mut row: &mut Rv32HintStoreCols = + trace[*trace_offset..*trace_offset + width].borrow_mut(); + + row.from_state.pc = F::from_canonical_u32(*state.pc); + row.from_state.timestamp = F::from_canonical_u32(state.memory.timestamp); + + row.mem_ptr_ptr = mem_ptr_ptr; + let mem_ptr_limbs: [u8; RV32_REGISTER_NUM_LIMBS] = tracing_read( + state.memory, + RV32_REGISTER_AS, + mem_ptr_ptr.as_canonical_u32(), + &mut row.mem_ptr_aux_cols, + ); let mem_ptr = u32::from_le_bytes(mem_ptr_limbs); + debug_assert!(mem_ptr <= (1 << self.pointer_max_bits)); - debug_assert!(mem_ptr <= (1 << self.air.pointer_max_bits)); + row.num_words_ptr = num_words_ptr; + let num_words = if local_opcode == HINT_STOREW { + row.is_single = F::ONE; + state.memory.increment_timestamp(); + 1 + } else { + row.is_buffer_start = F::ONE; + row.is_buffer = F::ONE; + let num_words_limbs: [u8; RV32_REGISTER_NUM_LIMBS] = tracing_read( + state.memory, + RV32_REGISTER_AS, + num_words_ptr.as_canonical_u32(), + &mut row.num_words_aux_cols, + ); + u32::from_le_bytes(num_words_limbs) + }; + debug_assert_ne!(num_words, 0); + debug_assert!(num_words <= (1 << self.pointer_max_bits)); let mut streams = self.streams.get().unwrap().lock().unwrap(); if streams.hint_stream.len() < RV32_REGISTER_NUM_LIMBS * num_words as usize { - return Err(ExecutionError::HintOutOfBounds { pc: from_state.pc }); + return Err(ExecutionError::HintOutOfBounds { pc: *state.pc }); } - let mut record = Rv32HintStoreRecord { - from_state, - instruction: instruction.clone(), - mem_ptr_read, - mem_ptr, - num_words, - num_words_read, - hints: vec![], - }; + let mem_ptr_msl = mem_ptr >> ((RV32_REGISTER_NUM_LIMBS - 1) * RV32_CELL_BITS); + let num_words_msl = num_words >> ((RV32_REGISTER_NUM_LIMBS - 1) * RV32_CELL_BITS); + // TODO(ayush): see if this can be moved to fill_trace_row + self.bitwise_lookup_chip.request_range( + mem_ptr_msl << (RV32_REGISTER_NUM_LIMBS * RV32_CELL_BITS - self.pointer_max_bits), + num_words_msl << (RV32_REGISTER_NUM_LIMBS * RV32_CELL_BITS - self.pointer_max_bits), + ); + + for word_index in 0..(num_words as usize) { + let offset = *trace_offset + word_index * width; + let mut row: &mut Rv32HintStoreCols = trace[offset..offset + width].borrow_mut(); - for word_index in 0..num_words { if word_index != 0 { - memory.increment_timestamp(); - memory.increment_timestamp(); + row.is_buffer = F::ONE; + row.from_state.timestamp = F::from_canonical_u32(state.memory.timestamp); + + state.memory.increment_timestamp(); + state.memory.increment_timestamp(); } - let data: [F; RV32_REGISTER_NUM_LIMBS] = + let data_f: [F; RV32_REGISTER_NUM_LIMBS] = std::array::from_fn(|_| streams.hint_stream.pop_front().unwrap()); - // let (write, _) = memory.write( - // e, - // F::from_canonical_u32(mem_ptr + (RV32_REGISTER_NUM_LIMBS as u32 * word_index)), - // &tmp_convert_to_u8s(data), - // ); - // record.hints.push((data, write)); - } - - self.height += record.hints.len(); - self.records.push(record); - - let next_state = ExecutionState { - pc: from_state.pc + DEFAULT_PC_STEP, - timestamp: memory.timestamp(), - }; - Ok(next_state) - } + let data: [u8; RV32_REGISTER_NUM_LIMBS] = + data_f.map(|byte| byte.as_canonical_u32() as u8); + + let mem_ptr_word = mem_ptr + (RV32_REGISTER_NUM_LIMBS * word_index) as u32; + + row.data = data_f; + tracing_write( + state.memory, + RV32_MEMORY_AS, + mem_ptr_word, + &data, + &mut row.write_aux, + ); - fn get_opcode_name(&self, opcode: usize) -> String { - if opcode == HINT_STOREW.global_opcode().as_usize() { - String::from("HINT_STOREW") - } else if opcode == HINT_BUFFER.global_opcode().as_usize() { - String::from("HINT_BUFFER") - } else { - unreachable!("unsupported opcode: {}", opcode) + row.rem_words_limbs = decompose(num_words - word_index as u32); + row.mem_ptr_limbs = decompose(mem_ptr_word); } - } -} -impl ChipUsageGetter for Rv32HintStoreChip { - fn air_name(&self) -> String { - "Rv32HintStoreAir".to_string() - } + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); - fn current_trace_height(&self) -> usize { - self.height - } + *trace_offset += (num_words as usize) * width; - fn trace_width(&self) -> usize { - Rv32HintStoreCols::::width() + Ok(()) } -} -impl Rv32HintStoreChip { - // returns number of used u32s - fn record_to_rows( - record: Rv32HintStoreRecord, - aux_cols_factory: &MemoryAuxColsFactory, - slice: &mut [F], - memory: &OfflineMemory, - bitwise_lookup_chip: &SharedBitwiseOperationLookupChip, - pointer_max_bits: usize, - ) -> usize { - let width = Rv32HintStoreCols::::width(); - let cols: &mut Rv32HintStoreCols = slice[..width].borrow_mut(); - - cols.is_single = F::from_bool(record.num_words_read.is_none()); - cols.is_buffer = F::from_bool(record.num_words_read.is_some()); - cols.is_buffer_start = cols.is_buffer; - - cols.from_state = record.from_state.map(F::from_canonical_u32); - cols.mem_ptr_ptr = record.instruction.b; - aux_cols_factory.generate_read_aux( - memory.record_by_id(record.mem_ptr_read), - &mut cols.mem_ptr_aux_cols, - ); + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let row: &mut Rv32HintStoreCols = row_slice.borrow_mut(); - cols.num_words_ptr = record.instruction.a; - if let Some(num_words_read) = record.num_words_read { - aux_cols_factory.generate_read_aux( - memory.record_by_id(num_words_read), - &mut cols.num_words_aux_cols, - ); - } - - let mut mem_ptr = record.mem_ptr; - let mut rem_words = record.num_words; - let mut used_u32s = 0; - - let mem_ptr_msl = mem_ptr >> ((RV32_REGISTER_NUM_LIMBS - 1) * RV32_CELL_BITS); - let rem_words_msl = rem_words >> ((RV32_REGISTER_NUM_LIMBS - 1) * RV32_CELL_BITS); - bitwise_lookup_chip.request_range( - mem_ptr_msl << (RV32_REGISTER_NUM_LIMBS * RV32_CELL_BITS - pointer_max_bits), - rem_words_msl << (RV32_REGISTER_NUM_LIMBS * RV32_CELL_BITS - pointer_max_bits), - ); - for (i, &(data, write)) in record.hints.iter().enumerate() { - for half in 0..(RV32_REGISTER_NUM_LIMBS / 2) { - bitwise_lookup_chip.request_range( - data[2 * half].as_canonical_u32(), - data[2 * half + 1].as_canonical_u32(), - ); - } + let mut timestamp = row.from_state.timestamp.as_canonical_u32(); - let cols: &mut Rv32HintStoreCols = slice[used_u32s..used_u32s + width].borrow_mut(); - cols.from_state.timestamp = - F::from_canonical_u32(record.from_state.timestamp + (3 * i as u32)); - cols.data = data; - aux_cols_factory.generate_write_aux(memory.record_by_id(write), &mut cols.write_aux); - cols.rem_words_limbs = decompose(rem_words); - cols.mem_ptr_limbs = decompose(mem_ptr); - if i != 0 { - cols.is_buffer = F::ONE; - } - used_u32s += width; - mem_ptr += RV32_REGISTER_NUM_LIMBS as u32; - rem_words -= 1; + if row.is_single.is_one() || row.is_buffer_start.is_one() { + mem_helper.fill_from_prev(timestamp, row.mem_ptr_aux_cols.as_mut()); } + timestamp += 1; - used_u32s - } - - fn generate_trace(self) -> RowMajorMatrix { - let width = self.trace_width(); - let height = next_power_of_two_or_zero(self.height); - let mut flat_trace = F::zero_vec(width * height); - - let memory = self.offline_memory.lock().unwrap(); + if row.is_buffer_start.is_one() { + mem_helper.fill_from_prev(timestamp, row.num_words_aux_cols.as_mut()); + } + timestamp += 1; - let aux_cols_factory = memory.aux_cols_factory(); + mem_helper.fill_from_prev(timestamp, row.write_aux.as_mut()); - let mut used_u32s = 0; - for record in self.records { - used_u32s += Self::record_to_rows( - record, - &aux_cols_factory, - &mut flat_trace[used_u32s..], - &memory, - &self.bitwise_lookup_chip, - self.air.pointer_max_bits, + for half in 0..(RV32_REGISTER_NUM_LIMBS / 2) { + self.bitwise_lookup_chip.request_range( + row.data[2 * half].as_canonical_u32(), + row.data[2 * half + 1].as_canonical_u32(), ); } - // padding rows can just be all zeros - RowMajorMatrix::new(flat_trace, width) } } -impl Chip for Rv32HintStoreChip> -where - Val: PrimeField32, -{ - fn air(&self) -> Arc> { - Arc::new(self.air) - } - fn generate_air_proof_input(self) -> AirProofInput { - AirProofInput::simple_no_pis(self.generate_trace()) - } -} - -impl InsExecutorE1 for Rv32HintStoreChip +impl StepExecutorE1 for Rv32HintStoreStep where F: PrimeField32, { @@ -535,10 +469,9 @@ where &mut self, state: VmStateMut, instruction: &Instruction, - ) -> Result<(), ExecutionError> + ) -> Result<()> where Mem: GuestMemory, - F: PrimeField32, { let &Instruction { opcode, @@ -548,46 +481,54 @@ where e, .. } = instruction; + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); debug_assert_eq!(e.as_canonical_u32(), RV32_MEMORY_AS); - let local_opcode = - Rv32HintStoreOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - - // TODO(ayush): fix this - // let (mem_ptr_read, mem_ptr_limbs) = todo!(); - // memory.read::(d, mem_ptr_ptr); - // let (num_words, num_words_read) = if local_opcode == HINT_STOREW { - // (1, None) - // } else { - // let (num_words_read, num_words_limbs) = todo!(); - // // memory.read::(d, num_words_ptr); - // (u32::from_le_bytes(num_words_limbs), Some(num_words_read)) - // }; - // debug_assert_ne!(num_words, 0); - // debug_assert!(num_words <= (1 << self.air.pointer_max_bits)); - - // let mem_ptr = u32::from_le_bytes(mem_ptr_limbs); - - // debug_assert!(mem_ptr <= (1 << self.air.pointer_max_bits)); - - // let mut streams = self.streams.get().unwrap().lock().unwrap(); - // if streams.hint_stream.len() < RV32_REGISTER_NUM_LIMBS * num_words as usize { - // return Err(ExecutionError::HintOutOfBounds { pc: *state.pc }); - // } - - // for word_index in 0..num_words { - // let data: [F; RV32_REGISTER_NUM_LIMBS] = - // std::array::from_fn(|_| streams.hint_stream.pop_front().unwrap()); - // // let (write, _) = memory.write( - // // e, - // // F::from_canonical_u32(mem_ptr + (RV32_REGISTER_NUM_LIMBS as u32 * - // word_index)), // &tmp_convert_to_u8s(data), - // // ); - // // record.hints.push((data, write)); - // } + + let local_opcode = Rv32HintStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + + let mem_ptr_limbs = memory_read( + state.memory, + RV32_REGISTER_AS, + mem_ptr_ptr.as_canonical_u32(), + ); + let mem_ptr = u32::from_le_bytes(mem_ptr_limbs); + debug_assert!(mem_ptr <= (1 << self.pointer_max_bits)); + + let num_words = if local_opcode == HINT_STOREW { + 1 + } else { + let num_words_limbs = memory_read( + state.memory, + RV32_REGISTER_AS, + num_words_ptr.as_canonical_u32(), + ); + u32::from_le_bytes(num_words_limbs) + }; + debug_assert_ne!(num_words, 0); + debug_assert!(num_words <= (1 << self.pointer_max_bits)); + + let mut streams = self.streams.get().unwrap().lock().unwrap(); + if streams.hint_stream.len() < RV32_REGISTER_NUM_LIMBS * num_words as usize { + return Err(ExecutionError::HintOutOfBounds { pc: *state.pc }); + } + + for word_index in 0..num_words { + let data: [u8; RV32_REGISTER_NUM_LIMBS] = std::array::from_fn(|_| { + streams.hint_stream.pop_front().unwrap().as_canonical_u32() as u8 + }); + memory_write( + state.memory, + RV32_MEMORY_AS, + mem_ptr + (RV32_REGISTER_NUM_LIMBS as u32 * word_index), + &data, + ); + } *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } } + +pub type Rv32HintStoreChip = NewVmChipWrapper>; diff --git a/extensions/rv32im/circuit/src/hintstore/tests.rs b/extensions/rv32im/circuit/src/hintstore/tests.rs index 40205978a1..69bfdbee84 100644 --- a/extensions/rv32im/circuit/src/hintstore/tests.rs +++ b/extensions/rv32im/circuit/src/hintstore/tests.rs @@ -6,7 +6,7 @@ use std::{ use openvm_circuit::arch::{ testing::{memory::gen_pointer, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - Streams, + ExecutionBridge, Streams, }; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, @@ -29,10 +29,11 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{config::setup_tracing, p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; -use super::{Rv32HintStoreChip, Rv32HintStoreCols}; +use super::{Rv32HintStoreAir, Rv32HintStoreChip, Rv32HintStoreCols, Rv32HintStoreStep}; use crate::adapters::decompose; type F = BabyBear; +const MAX_INS_CAPACITY: usize = 128; fn set_and_execute( tester: &mut VmChipTestBuilder, @@ -50,7 +51,8 @@ fn set_and_execute( let read_data: [F; RV32_REGISTER_NUM_LIMBS] = array::from_fn(|_| F::from_canonical_u32(rng.gen_range(0..(1 << RV32_CELL_BITS)))); for data in read_data { - chip.streams + chip.step + .streams .get() .unwrap() .lock() @@ -90,7 +92,8 @@ fn set_and_execute_buffer( .collect(); for i in 0..num_words { for datum in data[i as usize] { - chip.streams + chip.step + .streams .get() .unwrap() .lock() @@ -131,15 +134,24 @@ fn rand_hintstore_test() { let range_checker_chip = tester.memory_controller().range_checker.clone(); let mut chip = Rv32HintStoreChip::::new( - tester.execution_bus(), - tester.program_bus(), - bitwise_chip.clone(), - tester.memory_bridge(), - tester.offline_memory_mutex_arc(), - tester.address_bits(), - 0, + Rv32HintStoreAir::new( + ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), + tester.memory_bridge(), + bitwise_chip.bus(), + 0, + tester.address_bits(), + ), + Rv32HintStoreStep::new( + bitwise_chip.clone(), + tester.offline_memory_mutex_arc(), + tester.address_bits(), + 0, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - chip.set_streams(Arc::new(Mutex::new(Streams::default()))); + chip.step + .set_streams(Arc::new(Mutex::new(Streams::default()))); let num_tests: usize = 8; for _ in 0..num_tests { @@ -178,15 +190,24 @@ fn run_negative_hintstore_test( let range_checker_chip = tester.memory_controller().range_checker.clone(); let mut chip = Rv32HintStoreChip::::new( - tester.execution_bus(), - tester.program_bus(), - bitwise_chip.clone(), - tester.memory_bridge(), - tester.offline_memory_mutex_arc(), - tester.address_bits(), - 0, + Rv32HintStoreAir::new( + ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), + tester.memory_bridge(), + bitwise_chip.bus(), + 0, + tester.address_bits(), + ), + Rv32HintStoreStep::new( + bitwise_chip.clone(), + tester.offline_memory_mutex_arc(), + tester.address_bits(), + 0, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - chip.set_streams(Arc::new(Mutex::new(Streams::default()))); + chip.step + .set_streams(Arc::new(Mutex::new(Streams::default()))); set_and_execute(&mut tester, &mut chip, &mut rng, opcode); @@ -231,15 +252,24 @@ fn execute_roundtrip_sanity_test() { let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut chip = Rv32HintStoreChip::::new( - tester.execution_bus(), - tester.program_bus(), - bitwise_chip.clone(), - tester.memory_bridge(), - tester.offline_memory_mutex_arc(), - tester.address_bits(), - 0, + Rv32HintStoreAir::new( + ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), + tester.memory_bridge(), + bitwise_chip.bus(), + 0, + tester.address_bits(), + ), + Rv32HintStoreStep::new( + bitwise_chip.clone(), + tester.offline_memory_mutex_arc(), + tester.address_bits(), + 0, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - chip.set_streams(Arc::new(Mutex::new(Streams::default()))); + chip.step + .set_streams(Arc::new(Mutex::new(Streams::default()))); let num_tests: usize = 100; for _ in 0..num_tests { diff --git a/extensions/rv32im/circuit/src/jal_lui/core.rs b/extensions/rv32im/circuit/src/jal_lui/core.rs index eec4ab73ef..c6d11a6f72 100644 --- a/extensions/rv32im/circuit/src/jal_lui/core.rs +++ b/extensions/rv32im/circuit/src/jal_lui/core.rs @@ -3,7 +3,7 @@ use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ImmInstruction, Result, - SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -162,7 +162,7 @@ impl Rv32JalLuiStep { } } -impl SingleTraceStep for Rv32JalLuiStep +impl TraceStep for Rv32JalLuiStep where F: PrimeField32, A: 'static @@ -185,13 +185,16 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { let Instruction { opcode, c: imm, .. } = instruction; let local_opcode = Rv32JalLuiOpcode::from_usize(opcode.local_opcode_idx(Rv32JalLuiOpcode::CLASS_OFFSET)); + let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -224,6 +227,8 @@ where *state.pc = to_pc; + *trace_offset += width; + Ok(()) } diff --git a/extensions/rv32im/circuit/src/jalr/core.rs b/extensions/rv32im/circuit/src/jalr/core.rs index fc6f3772f8..3192307450 100644 --- a/extensions/rv32im/circuit/src/jalr/core.rs +++ b/extensions/rv32im/circuit/src/jalr/core.rs @@ -6,7 +6,7 @@ use std::{ use openvm_circuit::{ arch::{ AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, Result, SignedImmInstruction, - SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -197,7 +197,7 @@ impl Rv32JalrStep { } } -impl SingleTraceStep for Rv32JalrStep +impl TraceStep for Rv32JalrStep where F: PrimeField32, A: 'static @@ -220,13 +220,16 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { let Instruction { opcode, c, g, .. } = *instruction; let local_opcode = Rv32JalrOpcode::from_usize(opcode.local_opcode_idx(Rv32JalrOpcode::CLASS_OFFSET)); + let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -269,6 +272,8 @@ where *state.pc = to_pc; + *trace_offset += width; + Ok(()) } diff --git a/extensions/rv32im/circuit/src/less_than/core.rs b/extensions/rv32im/circuit/src/less_than/core.rs index 4632176455..5d63f03652 100644 --- a/extensions/rv32im/circuit/src/less_than/core.rs +++ b/extensions/rv32im/circuit/src/less_than/core.rs @@ -6,7 +6,7 @@ use std::{ use openvm_circuit::{ arch::{ AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, - SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -191,7 +191,7 @@ impl LessThanStep SingleTraceStep +impl TraceStep for LessThanStep where F: PrimeField32, @@ -212,7 +212,9 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { debug_assert!(LIMB_BITS <= 8); @@ -220,6 +222,7 @@ where let local_opcode = LessThanOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -242,6 +245,8 @@ where *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *trace_offset += width; + Ok(()) } diff --git a/extensions/rv32im/circuit/src/load_sign_extend/core.rs b/extensions/rv32im/circuit/src/load_sign_extend/core.rs index d6dccee0b0..dd4051ae9a 100644 --- a/extensions/rv32im/circuit/src/load_sign_extend/core.rs +++ b/extensions/rv32im/circuit/src/load_sign_extend/core.rs @@ -5,8 +5,8 @@ use std::{ use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, Result, SingleTraceStep, - StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, Result, StepExecutorE1, TraceStep, + VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -201,7 +201,7 @@ impl } } -impl SingleTraceStep +impl TraceStep for LoadSignExtendStep where F: PrimeField32, @@ -225,7 +225,9 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { let Instruction { opcode, .. } = instruction; @@ -233,6 +235,7 @@ where opcode.local_opcode_idx(Rv32LoadStoreOpcode::CLASS_OFFSET), ); + let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -286,6 +289,8 @@ where *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *trace_offset += width; + Ok(()) } diff --git a/extensions/rv32im/circuit/src/loadstore/core.rs b/extensions/rv32im/circuit/src/loadstore/core.rs index 99564e910c..8b8e499f3a 100644 --- a/extensions/rv32im/circuit/src/loadstore/core.rs +++ b/extensions/rv32im/circuit/src/loadstore/core.rs @@ -2,8 +2,8 @@ use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, Result, SingleTraceStep, - StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, Result, StepExecutorE1, TraceStep, + VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -274,7 +274,7 @@ impl LoadStoreStep { } } -impl SingleTraceStep for LoadStoreStep +impl TraceStep for LoadStoreStep where F: PrimeField32, A: 'static @@ -297,12 +297,15 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { let Instruction { opcode, .. } = instruction; let local_opcode = Rv32LoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -352,6 +355,8 @@ where *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *trace_offset += width; + Ok(()) } diff --git a/extensions/rv32im/circuit/src/mul/core.rs b/extensions/rv32im/circuit/src/mul/core.rs index 79a3c65044..fb4469fdaf 100644 --- a/extensions/rv32im/circuit/src/mul/core.rs +++ b/extensions/rv32im/circuit/src/mul/core.rs @@ -6,7 +6,7 @@ use std::{ use openvm_circuit::{ arch::{ AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, - SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -152,7 +152,7 @@ impl } } -impl SingleTraceStep +impl TraceStep for MultiplicationStep where F: PrimeField32, @@ -173,7 +173,9 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { let Instruction { opcode, .. } = instruction; @@ -182,6 +184,7 @@ where MulOpcode::MUL ); + let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -207,6 +210,8 @@ where *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *trace_offset += width; + Ok(()) } diff --git a/extensions/rv32im/circuit/src/mulh/core.rs b/extensions/rv32im/circuit/src/mulh/core.rs index e1578f0613..62672f05dc 100644 --- a/extensions/rv32im/circuit/src/mulh/core.rs +++ b/extensions/rv32im/circuit/src/mulh/core.rs @@ -6,7 +6,7 @@ use std::{ use openvm_circuit::{ arch::{ AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, - SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -223,7 +223,7 @@ impl MulHStep SingleTraceStep +impl TraceStep for MulHStep where F: PrimeField32, @@ -247,12 +247,15 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { let Instruction { opcode, .. } = instruction; let mulh_opcode = MulHOpcode::from_usize(opcode.local_opcode_idx(MulHOpcode::CLASS_OFFSET)); + let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -297,6 +300,8 @@ where *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *trace_offset += width; + Ok(()) } diff --git a/extensions/rv32im/circuit/src/shift/core.rs b/extensions/rv32im/circuit/src/shift/core.rs index 8f2ba4f2d6..c31ff76b9b 100644 --- a/extensions/rv32im/circuit/src/shift/core.rs +++ b/extensions/rv32im/circuit/src/shift/core.rs @@ -6,7 +6,7 @@ use std::{ use openvm_circuit::{ arch::{ AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, - SingleTraceStep, StepExecutorE1, VmAdapterInterface, VmCoreAir, VmStateMut, + StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -267,7 +267,7 @@ impl ShiftStep SingleTraceStep +impl TraceStep for ShiftStep where F: PrimeField32, @@ -288,12 +288,15 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { let Instruction { opcode, .. } = instruction; let local_opcode = ShiftOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -317,6 +320,8 @@ where *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *trace_offset += width; + Ok(()) } From 6c868768fac17292ef4e52296f4ccbcdd82e861e Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Thu, 1 May 2025 16:34:56 -0400 Subject: [PATCH 11/49] fix(new-execution): don't override min block size (#1619) one line fix. now that we're only initializing `TracingMemory` with `new`, we should remove this line from `with_image` --- crates/vm/src/system/memory/online.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/vm/src/system/memory/online.rs b/crates/vm/src/system/memory/online.rs index 85b9dd66bd..1ac72bb83e 100644 --- a/crates/vm/src/system/memory/online.rs +++ b/crates/vm/src/system/memory/online.rs @@ -160,7 +160,6 @@ impl TracingMemory { /// Instantiates a new `Memory` data structure from an image. pub fn with_image(mut self, image: MemoryImage, _access_capacity: usize) -> Self { - self.min_block_size = vec![1; self.meta.len()]; for (i, (paged_vec, cell_size)) in izip!(&image.paged_vecs, &image.cell_size).enumerate() { let num_cells = paged_vec.bytes_capacity() / cell_size; From 4a2ac139e72ffc763a40b9e4a4766261f332d69c Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Fri, 2 May 2025 10:51:07 -0700 Subject: [PATCH 12/49] fix: auipc tracegen --- Cargo.lock | 2 +- benchmarks/execute/benches/execute.rs | 3 ++- extensions/rv32im/circuit/src/auipc/core.rs | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a6692d798e..0aae880022 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5280,7 +5280,7 @@ dependencies = [ "p3-monty-31", "p3-poseidon2", "p3-symmetric", - "rand", + "rand 0.8.5", "serde", ] diff --git a/benchmarks/execute/benches/execute.rs b/benchmarks/execute/benches/execute.rs index 0ca09e19e4..bcd60f6811 100644 --- a/benchmarks/execute/benches/execute.rs +++ b/benchmarks/execute/benches/execute.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use divan; use eyre::Result; use openvm_benchmarks_utils::{get_elf_path, get_programs_dir, read_elf_file}; @@ -8,7 +10,6 @@ use openvm_rv32im_transpiler::{ }; use openvm_stark_sdk::p3_baby_bear::BabyBear; use openvm_transpiler::{transpiler::Transpiler, FromElf}; -use std::path::PathBuf; static AVAILABLE_PROGRAMS: &[&str] = &[ "fibonacci_recursive", diff --git a/extensions/rv32im/circuit/src/auipc/core.rs b/extensions/rv32im/circuit/src/auipc/core.rs index a507e1e58c..f9599b0e87 100644 --- a/extensions/rv32im/circuit/src/auipc/core.rs +++ b/extensions/rv32im/circuit/src/auipc/core.rs @@ -298,9 +298,9 @@ where .request_range(imm_limbs[0] as u32, imm_limbs[1] as u32); self.bitwise_lookup_chip .request_range(imm_limbs[2] as u32, pc_limbs[1] as u32); - + let msl_shift = RV32_REGISTER_NUM_LIMBS * RV32_CELL_BITS - PC_BITS; self.bitwise_lookup_chip - .request_range(pc_limbs[2] as u32, pc_limbs[3] as u32); + .request_range(pc_limbs[2] as u32, (pc_limbs[3] as u32) << msl_shift); } } From e9cabd282acdb7edea8280a8e12be1b735433c91 Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Sat, 3 May 2025 01:52:28 -0400 Subject: [PATCH 13/49] chore: remove `OfflineMemory` (#1623) remove `memory/offline.rs` as we aren't using it anymore. Delete `VmAdapterChip` trait and `VmChipWrapper` since we also aren't using them anymore. --- Cargo.lock | 2 +- crates/vm/src/arch/extensions.rs | 6 +- crates/vm/src/arch/integration_api.rs | 221 +--- crates/vm/src/arch/segment.rs | 2 +- crates/vm/src/arch/testing/mod.rs | 10 +- crates/vm/src/arch/testing/test_adapter.rs | 76 +- crates/vm/src/system/memory/controller/mod.rs | 200 +-- crates/vm/src/system/memory/mod.rs | 3 +- crates/vm/src/system/memory/offline.rs | 1080 ----------------- crates/vm/src/system/memory/online.rs | 7 +- crates/vm/src/system/memory/persistent.rs | 4 +- crates/vm/src/system/native_adapter/mod.rs | 5 +- extensions/keccak256/circuit/src/extension.rs | 1 - .../rv32im/circuit/src/base_alu/tests.rs | 2 +- .../rv32im/circuit/src/branch_eq/tests.rs | 4 +- .../rv32im/circuit/src/branch_lt/tests.rs | 4 +- extensions/rv32im/circuit/src/divrem/tests.rs | 2 +- extensions/rv32im/circuit/src/extension.rs | 2 - .../rv32im/circuit/src/hintstore/mod.rs | 7 +- .../rv32im/circuit/src/hintstore/tests.rs | 21 +- extensions/rv32im/circuit/src/jalr/tests.rs | 2 +- .../rv32im/circuit/src/less_than/tests.rs | 2 +- .../rv32im/circuit/src/loadstore/tests.rs | 2 +- extensions/rv32im/circuit/src/mul/tests.rs | 2 +- extensions/rv32im/circuit/src/mulh/tests.rs | 2 +- extensions/rv32im/circuit/src/shift/tests.rs | 2 +- 26 files changed, 57 insertions(+), 1614 deletions(-) delete mode 100644 crates/vm/src/system/memory/offline.rs diff --git a/Cargo.lock b/Cargo.lock index 0aae880022..a6692d798e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5280,7 +5280,7 @@ dependencies = [ "p3-monty-31", "p3-poseidon2", "p3-symmetric", - "rand 0.8.5", + "rand", "serde", ] diff --git a/crates/vm/src/arch/extensions.rs b/crates/vm/src/arch/extensions.rs index e349a47518..b20e4f6630 100644 --- a/crates/vm/src/arch/extensions.rs +++ b/crates/vm/src/arch/extensions.rs @@ -46,7 +46,7 @@ use crate::{ connector::VmConnectorChip, memory::{ offline_checker::{MemoryBridge, MemoryBus}, - MemoryController, MemoryImage, OfflineMemory, BOUNDARY_AIR_OFFSET, MERKLE_AIR_OFFSET, + MemoryController, MemoryImage, BOUNDARY_AIR_OFFSET, MERKLE_AIR_OFFSET, }, native_adapter::{NativeAdapterAir, NativeAdapterStep}, phantom::PhantomChip, @@ -504,10 +504,6 @@ impl SystemBase { self.connector_chip.air.execution_bus } - pub fn offline_memory(&self) -> Arc>> { - self.memory_controller.offline_memory().clone() - } - /// Return trace heights of SystemBase. Usually this is for aggregation and not useful for /// regular users. pub fn get_system_trace_heights(&self) -> SystemTraceHeights { diff --git a/crates/vm/src/arch/integration_api.rs b/crates/vm/src/arch/integration_api.rs index 5b22d16e25..dbb18c4c41 100644 --- a/crates/vm/src/arch/integration_api.rs +++ b/crates/vm/src/arch/integration_api.rs @@ -1,15 +1,9 @@ -use std::{ - array::from_fn, - borrow::Borrow, - marker::PhantomData, - sync::{Arc, Mutex}, -}; +use std::{array::from_fn, borrow::Borrow, marker::PhantomData, sync::Arc}; use openvm_circuit_primitives::utils::next_power_of_two_or_zero; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{instruction::Instruction, LocalOpcode}; use openvm_stark_backend::{ - air_builders::{debug::DebugConstraintBuilder, symbolic::SymbolicRapBuilder}, config::{StarkGenericConfig, Val}, p3_air::{Air, AirBuilder, BaseAir}, p3_field::{Field, FieldAlgebra, PrimeField32}, @@ -21,13 +15,10 @@ use openvm_stark_backend::{ }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use super::{ - ExecutionError, ExecutionState, InsExecutorE1, InstructionExecutor, Result, VmStateMut, -}; +use super::{ExecutionState, InsExecutorE1, InstructionExecutor, Result, VmStateMut}; use crate::system::memory::{ online::{GuestMemory, TracingMemory}, - AddressMap, MemoryAuxColsFactory, MemoryController, OfflineMemory, SharedMemoryHelper, - PAGE_SIZE, + MemoryAuxColsFactory, MemoryController, SharedMemoryHelper, }; /// The interface between primitive AIR and machine adapter AIR. @@ -43,61 +34,6 @@ pub trait VmAdapterInterface { type ProcessedInstruction; } -// TODO: delete -/// The adapter owns all memory accesses and timestamp changes. -/// The adapter AIR should also own `ExecutionBridge` and `MemoryBridge`. -pub trait VmAdapterChip { - /// Records generated by adapter before main instruction execution - type ReadRecord: Send + Serialize + DeserializeOwned; - /// Records generated by adapter after main instruction execution - type WriteRecord: Send + Serialize + DeserializeOwned; - /// AdapterAir should not have public values - type Air: BaseAir + Clone; - - type Interface: VmAdapterInterface; - - /// Given instruction, perform memory reads and return only the read data that the integrator - /// needs to use. This is called at the start of instruction execution. - /// - /// The implementer may choose to store data in the `Self::ReadRecord` struct, for example in - /// an [Option], which will later be sent to the `postprocess` method. - #[allow(clippy::type_complexity)] - fn preprocess( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )>; - - /// Given instruction and the data to write, perform memory writes and return the `(record, - /// next_timestamp)` of the full adapter record for this instruction. This is guaranteed to - /// be called after `preprocess`. - fn postprocess( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)>; - - /// Populates `row_slice` with values corresponding to `record`. - /// The provided `row_slice` will have length equal to `self.air().width()`. - /// This function will be called for each row in the trace which is being used, and all other - /// rows in the trace will be filled with zeroes. - fn generate_trace_row( - &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, - ); - - fn air(&self) -> &Self::Air; -} - pub trait VmAdapterAir: BaseAir { type Interface: VmAdapterInterface; @@ -458,77 +394,8 @@ pub trait StepExecutorE1 { Mem: GuestMemory; } -pub struct VmChipWrapper, C: VmCoreChip> { - pub adapter: A, - pub core: C, - pub records: Vec<(A::ReadRecord, A::WriteRecord, C::Record)>, - offline_memory: Arc>>, -} - const DEFAULT_RECORDS_CAPACITY: usize = 1 << 20; -impl VmChipWrapper -where - A: VmAdapterChip, - C: VmCoreChip, -{ - pub fn new(adapter: A, core: C, offline_memory: Arc>>) -> Self { - Self { - adapter, - core, - records: Vec::with_capacity(DEFAULT_RECORDS_CAPACITY), - offline_memory, - } - } -} - -impl InstructionExecutor for VmChipWrapper -where - F: PrimeField32, - A: VmAdapterChip + Send + Sync, - M: VmCoreChip + Send + Sync, -{ - fn execute( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - from_state: ExecutionState, - ) -> Result> { - let (reads, read_record) = self.adapter.preprocess(memory, instruction)?; - let (output, core_record) = - self.core - .execute_instruction(instruction, from_state.pc, reads)?; - let (to_state, write_record) = - self.adapter - .postprocess(memory, instruction, from_state, output, &read_record)?; - self.records.push((read_record, write_record, core_record)); - Ok(to_state) - } - - fn get_opcode_name(&self, opcode: usize) -> String { - self.core.get_opcode_name(opcode) - } -} - -// // TODO(ayush): delete -impl InsExecutorE1 for VmChipWrapper -where - F: PrimeField32, - A: VmAdapterChip + Send + Sync, - M: VmCoreChip + StepExecutorE1 + Send + Sync, -{ - fn execute_e1( - &mut self, - state: VmStateMut, - instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { - self.core.execute_e1(state, instruction) - } -} - impl InsExecutorE1 for NewVmChipWrapper where F: PrimeField32, @@ -546,88 +413,6 @@ where } } -// Note[jpw]: the statement we want is: -// - when A::Air is an AdapterAir for all AirBuilders needed by stark-backend -// - and when M::Air is an CoreAir for all AirBuilders needed by stark-backend, -// then VmAirWrapper is an Air for all AirBuilders needed -// by stark-backend, which is equivalent to saying it implements AirRef -// The where clauses to achieve this statement is unfortunately really verbose. -impl Chip for VmChipWrapper, A, C> -where - SC: StarkGenericConfig, - Val: PrimeField32, - A: VmAdapterChip> + Send + Sync, - C: VmCoreChip, A::Interface> + Send + Sync, - A::Air: Send + Sync + 'static, - A::Air: VmAdapterAir>>, - A::Air: for<'a> VmAdapterAir>, - C::Air: Send + Sync + 'static, - C::Air: VmCoreAir< - SymbolicRapBuilder>, - >>>::Interface, - >, - C::Air: for<'a> VmCoreAir< - DebugConstraintBuilder<'a, SC>, - >>::Interface, - >, -{ - fn air(&self) -> AirRef { - let air: VmAirWrapper = VmAirWrapper { - adapter: self.adapter.air().clone(), - core: self.core.air().clone(), - }; - Arc::new(air) - } - - fn generate_air_proof_input(self) -> AirProofInput { - let num_records = self.records.len(); - let height = next_power_of_two_or_zero(num_records); - let core_width = self.core.air().width(); - let adapter_width = self.adapter.air().width(); - let width = core_width + adapter_width; - let mut values = Val::::zero_vec(height * width); - - let memory = self.offline_memory.lock().unwrap(); - - // This zip only goes through records. - // The padding rows between records.len()..height are filled with zeros. - values - .par_chunks_mut(width) - .zip(self.records.into_par_iter()) - .for_each(|(row_slice, record)| { - let (adapter_row, core_row) = row_slice.split_at_mut(adapter_width); - self.adapter - .generate_trace_row(adapter_row, record.0, record.1, &memory); - self.core.generate_trace_row(core_row, record.2); - }); - - let mut trace = RowMajorMatrix::new(values, width); - self.core.finalize(&mut trace, num_records); - - AirProofInput::simple(trace, self.core.generate_public_values()) - } -} - -impl ChipUsageGetter for VmChipWrapper -where - A: VmAdapterChip + Sync, - M: VmCoreChip + Sync, -{ - fn air_name(&self) -> String { - format!( - "<{},{}>", - get_air_name(self.adapter.air()), - get_air_name(self.core.air()) - ) - } - fn current_trace_height(&self) -> usize { - self.records.len() - } - fn trace_width(&self) -> usize { - self.adapter.air().width() + self.core.air().width() - } -} - #[derive(Clone, Copy, derive_new::new)] pub struct VmAirWrapper { pub adapter: A, diff --git a/crates/vm/src/arch/segment.rs b/crates/vm/src/arch/segment.rs index 4e0fb12258..fd9d481988 100644 --- a/crates/vm/src/arch/segment.rs +++ b/crates/vm/src/arch/segment.rs @@ -22,7 +22,7 @@ use super::{ #[cfg(feature = "bench-metrics")] use crate::metrics::VmMetrics; use crate::{ - arch::{instructions::*, ExecutionState, InstructionExecutor}, + arch::{instructions::*, InstructionExecutor}, system::{ connector::DEFAULT_SUSPEND_EXIT_CODE, memory::{online::GuestMemory, paged_vec::PAGE_SIZE, AddressMap, MemoryImage}, diff --git a/crates/vm/src/arch/testing/mod.rs b/crates/vm/src/arch/testing/mod.rs index 6e12e1c168..15d1dbf9c9 100644 --- a/crates/vm/src/arch/testing/mod.rs +++ b/crates/vm/src/arch/testing/mod.rs @@ -1,5 +1,3 @@ -use std::sync::{Arc, Mutex}; - use openvm_circuit_primitives::var_range::{ SharedVariableRangeCheckerChip, VariableRangeCheckerBus, }; @@ -33,7 +31,7 @@ use crate::{ system::{ memory::{ offline_checker::{MemoryBridge, MemoryBus}, - MemoryController, OfflineMemory, SharedMemoryHelper, + MemoryController, SharedMemoryHelper, }, program::ProgramBus, }, @@ -42,6 +40,7 @@ use crate::{ pub mod execution; pub mod memory; pub mod program; +// TODO[jpw]: delete or fix pub mod test_adapter; pub use execution::ExecutionTester; @@ -190,11 +189,6 @@ impl VmChipTestBuilder { self.memory.controller.helper() } - // TODO: delete - pub fn offline_memory_mutex_arc(&self) -> Arc>> { - self.memory.controller.offline_memory().clone() - } - pub fn address_bits(&self) -> usize { self.memory.controller.mem_config.pointer_max_bits } diff --git a/crates/vm/src/arch/testing/test_adapter.rs b/crates/vm/src/arch/testing/test_adapter.rs index bca9eed724..f004a0a2f7 100644 --- a/crates/vm/src/arch/testing/test_adapter.rs +++ b/crates/vm/src/arch/testing/test_adapter.rs @@ -16,9 +16,9 @@ use serde::{Deserialize, Serialize}; use crate::{ arch::{ AdapterAirContext, AdapterRuntimeContext, DynAdapterInterface, DynArray, ExecutionBridge, - ExecutionState, MinimalInstruction, Result, VmAdapterAir, VmAdapterChip, + ExecutionState, MinimalInstruction, Result, VmAdapterAir, }, - system::memory::{MemoryController, OfflineMemory}, + system::memory::MemoryController, }; // Replaces A: VmAdapterChip while testing VmCoreChip functionality, as it has no @@ -53,78 +53,6 @@ pub struct TestAdapterRecord { pub operands: [T; 7], } -impl VmAdapterChip for TestAdapterChip { - type ReadRecord = (); - type WriteRecord = TestAdapterRecord; - type Air = TestAdapterAir; - type Interface = DynAdapterInterface; - - fn preprocess( - &mut self, - _memory: &mut MemoryController, - _instruction: &Instruction, - ) -> Result<(DynArray, Self::ReadRecord)> { - Ok(( - self.prank_reads - .pop_front() - .expect("Not enough prank reads provided") - .into(), - (), - )) - } - - fn postprocess( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - from_state: ExecutionState, - _output: AdapterRuntimeContext, - _read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - let pc_inc = self - .prank_pc_inc - .pop_front() - .map(|x| x.unwrap_or(4)) - .unwrap_or(4); - Ok(( - ExecutionState { - pc: from_state.pc + pc_inc, - timestamp: memory.timestamp(), - }, - TestAdapterRecord { - operands: [ - instruction.a, - instruction.b, - instruction.c, - instruction.d, - instruction.e, - instruction.f, - instruction.g, - ], - from_pc: from_state.pc, - }, - )) - } - - fn generate_trace_row( - &self, - row_slice: &mut [F], - _read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - _memory: &OfflineMemory, - ) { - let cols: &mut TestAdapterCols = row_slice.borrow_mut(); - cols.from_pc = F::from_canonical_u32(write_record.from_pc); - cols.operands = write_record.operands; - // row_slice[0] = F::from_canonical_u32(write_record.from_pc); - // row_slice[1..].copy_from_slice(&write_record.operands); - } - - fn air(&self) -> &Self::Air { - &self.air - } -} - #[derive(Clone, Copy, Debug)] pub struct TestAdapterAir { pub execution_bridge: ExecutionBridge, diff --git a/crates/vm/src/system/memory/controller/mod.rs b/crates/vm/src/system/memory/controller/mod.rs index bca5999f52..0b37974e76 100644 --- a/crates/vm/src/system/memory/controller/mod.rs +++ b/crates/vm/src/system/memory/controller/mod.rs @@ -1,9 +1,7 @@ use std::{ - array, collections::BTreeMap, iter, marker::PhantomData, - ops::Deref, sync::{Arc, Mutex}, }; @@ -31,7 +29,7 @@ use serde::{Deserialize, Serialize}; use self::interface::MemoryInterface; use super::{ - online::GuestMemory, + online::{GuestMemory, INITIAL_TIMESTAMP}, paged_vec::{AddressMap, PAGE_SIZE}, volatile::VolatileBoundaryChip, }; @@ -41,7 +39,6 @@ use crate::{ adapter::AccessAdapterInventory, dimensions::MemoryDimensions, merkle::{MemoryMerkleChip, SerialReceiver}, - offline::{MemoryRecord, OfflineMemory, INITIAL_TIMESTAMP}, offline_checker::{ MemoryBaseAuxCols, MemoryBridge, MemoryBus, MemoryReadAuxCols, MemoryReadOrImmediateAuxCols, MemoryWriteAuxCols, AUX_LEN, @@ -102,8 +99,6 @@ pub struct MemoryController { range_checker_bus: VariableRangeCheckerBus, // addr_space -> Memory data structure pub memory: TracingMemory, - /// A reference to the `OfflineMemory`. Will be populated after `finalize()`. - pub offline_memory: Arc>>, pub access_adapters: AccessAdapterInventory, // Filled during finalization. final_state: Option>, @@ -229,7 +224,6 @@ impl MemoryController { range_checker: SharedVariableRangeCheckerChip, ) -> Self { let range_checker_bus = range_checker.bus(); - let initial_memory = AddressMap::from_mem_config(&mem_config); assert!(mem_config.pointer_max_bits <= F::bits() - 2); assert!(mem_config.as_height < F::bits() - 2); let addr_space_max_bits = log2_ceil_usize( @@ -247,13 +241,6 @@ impl MemoryController { ), }, memory: TracingMemory::new(&mem_config, range_checker.clone(), memory_bus), - offline_memory: Arc::new(Mutex::new(OfflineMemory::new( - initial_memory, - 1, - memory_bus, - range_checker.clone(), - mem_config, - ))), access_adapters: AccessAdapterInventory::new( range_checker.clone(), memory_bus, @@ -299,13 +286,6 @@ impl MemoryController { interface_chip, memory: TracingMemory::new(&mem_config, range_checker.clone(), memory_bus), /* it is expected that the memory will be * set later */ - offline_memory: Arc::new(Mutex::new(OfflineMemory::new( - AddressMap::from_mem_config(&mem_config), - CHUNK, - memory_bus, - range_checker.clone(), - mem_config, - ))), access_adapters: AccessAdapterInventory::new( range_checker.clone(), memory_bus, @@ -352,8 +332,6 @@ impl MemoryController { if self.timestamp() > INITIAL_TIMESTAMP + 1 { panic!("Cannot set initial memory after first timestamp"); } - let mut offline_memory = self.offline_memory.lock().unwrap(); - offline_memory.set_initial_memory(memory.clone(), self.mem_config); self.memory = TracingMemory::new( &self.mem_config, @@ -484,69 +462,6 @@ impl MemoryController { self.memory.timestamp() } - pub fn offline_memory(&self) -> &Arc>> { - &self.offline_memory - } - - fn replay_access_log(&mut self) { - unimplemented!(); - // let log = mem::take(&mut self.memory.log); - // if log.is_empty() { - // // Online memory logs may be empty, but offline memory may be replayed from external - // sources. // In these cases, we skip the calls to replay access logs because - // `set_log_capacity` would // panic. - // tracing::debug!("skipping replay_access_log"); - // return; - // } - - // let mut offline_memory = self.offline_memory.lock().unwrap(); - // offline_memory.set_log_capacity(log.len()); - - // for entry in log { - // Self::replay_access( - // entry, - // &mut offline_memory, - // &mut self.interface_chip, - // &mut self.access_adapters, - // ); - // } - } - - /// Low-level API to replay a single memory access log entry and populate the [OfflineMemory], - /// [MemoryInterface], and `AccessAdapterInventory`. - pub fn replay_access( - entry: MemoryLogEntry, - offline_memory: &mut OfflineMemory, - interface_chip: &mut MemoryInterface, - adapter_records: &mut AccessAdapterInventory, - ) { - match entry { - MemoryLogEntry::Read { - address_space, - pointer, - len, - } => { - if address_space != 0 { - interface_chip.touch_range(address_space, pointer, len as u32); - } - offline_memory.read(address_space, pointer, len, adapter_records); - } - MemoryLogEntry::Write { - address_space, - pointer, - data, - } => { - if address_space != 0 { - interface_chip.touch_range(address_space, pointer, data.len() as u32); - } - offline_memory.write(address_space, pointer, data, adapter_records); - } - MemoryLogEntry::IncrementTimestampBy(amount) => { - offline_memory.increment_timestamp_by(amount); - } - }; - } - /// Returns the final memory state if persistent. pub fn finalize(&mut self, hasher: Option<&mut H>) where @@ -555,15 +470,13 @@ impl MemoryController { if self.final_state.is_some() { return; } - - self.replay_access_log(); - let mut offline_memory = self.offline_memory.lock().unwrap(); + todo!(); match &mut self.interface_chip { MemoryInterface::Volatile { boundary_chip } => { - let final_memory = offline_memory.finalize::<1>(&mut self.access_adapters); - boundary_chip.finalize(final_memory); - self.final_state = Some(FinalState::Volatile(VolatileFinalState::default())); + // let final_memory = offline_memory.finalize::<1>(&mut self.access_adapters); + // boundary_chip.finalize(final_memory); + // self.final_state = Some(FinalState::Volatile(VolatileFinalState::default())); } MemoryInterface::Persistent { merkle_chip, @@ -571,22 +484,23 @@ impl MemoryController { initial_memory, } => { let hasher = hasher.unwrap(); - let final_partition = offline_memory.finalize::(&mut self.access_adapters); - - boundary_chip.finalize(initial_memory, &final_partition, hasher); - let final_memory_values = final_partition - .into_par_iter() - .map(|(key, value)| (key, value.values)) - .collect(); - let initial_node = MemoryNode::tree_from_memory( - merkle_chip.air.memory_dimensions, - initial_memory, - hasher, - ); - merkle_chip.finalize(&initial_node, &final_memory_values, hasher); - self.final_state = Some(FinalState::Persistent(PersistentFinalState { - final_memory: final_memory_values.clone(), - })); + // let final_partition = offline_memory.finalize::(&mut + // self.access_adapters); + + // boundary_chip.finalize(initial_memory, &final_partition, hasher); + // let final_memory_values = final_partition + // .into_par_iter() + // .map(|(key, value)| (key, value.values)) + // .collect(); + // let initial_node = MemoryNode::tree_from_memory( + // merkle_chip.air.memory_dimensions, + // initial_memory, + // hasher, + // ); + // merkle_chip.finalize(&initial_node, &final_memory_values, hasher); + // self.final_state = Some(FinalState::Persistent(PersistentFinalState { + // final_memory: final_memory_values.clone(), + // })); } }; } @@ -749,49 +663,6 @@ pub struct MemoryAuxColsFactory<'a, T> { // NOTE[jpw]: The `make_*_aux_cols` functions should be thread-safe so they can be used in // parallelized trace generation. impl MemoryAuxColsFactory<'_, F> { - pub fn generate_read_aux(&self, read: &MemoryRecord, buffer: &mut MemoryReadAuxCols) { - assert!( - !read.address_space.is_zero(), - "cannot make `MemoryReadAuxCols` for address space 0" - ); - self.generate_base_aux(read, &mut buffer.base); - } - - // TODO: revisit deleting this - pub fn generate_read_or_immediate_aux( - &self, - read: &MemoryRecord, - buffer: &mut MemoryReadOrImmediateAuxCols, - ) { - IsZeroSubAir.generate_subrow( - read.address_space, - (&mut buffer.is_zero_aux, &mut buffer.is_immediate), - ); - self.generate_base_aux(read, &mut buffer.base); - } - - // TODO: revisit deleting this - pub fn generate_write_aux( - &self, - write: &MemoryRecord, - buffer: &mut MemoryWriteAuxCols, - ) { - buffer - .prev_data - .copy_from_slice(write.prev_data_slice().unwrap()); - self.generate_base_aux(write, &mut buffer.base); - } - - // TODO: revisit deleting this - pub fn generate_base_aux(&self, record: &MemoryRecord, buffer: &mut MemoryBaseAuxCols) { - buffer.prev_timestamp = F::from_canonical_u32(record.prev_timestamp); - self.generate_timestamp_lt( - record.prev_timestamp, - record.timestamp, - &mut buffer.timestamp_lt_aux, - ); - } - /// Fill the trace assuming `prev_timestamp` is already provided in `buffer`. pub fn fill_from_prev(&self, timestamp: u32, buffer: &mut MemoryBaseAuxCols) { let prev_timestamp = buffer.prev_timestamp.as_canonical_u32(); @@ -814,33 +685,6 @@ impl MemoryAuxColsFactory<'_, F> { ); } - // TODO: revisit deleting this - /// In general, prefer `generate_read_aux` which writes in-place rather than this function. - pub fn make_read_aux_cols(&self, read: &MemoryRecord) -> MemoryReadAuxCols { - assert!( - !read.address_space.is_zero(), - "cannot make `MemoryReadAuxCols` for address space 0" - ); - MemoryReadAuxCols::new( - read.prev_timestamp, - self.generate_timestamp_lt_cols(read.prev_timestamp, read.timestamp), - ) - } - - // TODO: revisit deleting this - /// In general, prefer `generate_write_aux` which writes in-place rather than this function. - pub fn make_write_aux_cols( - &self, - write: &MemoryRecord, - ) -> MemoryWriteAuxCols { - let prev_data = write.prev_data_slice().unwrap(); - MemoryWriteAuxCols::new( - prev_data.try_into().unwrap(), - F::from_canonical_u32(write.prev_timestamp), - self.generate_timestamp_lt_cols(write.prev_timestamp, write.timestamp), - ) - } - fn generate_timestamp_lt_cols( &self, prev_timestamp: u32, diff --git a/crates/vm/src/system/memory/mod.rs b/crates/vm/src/system/memory/mod.rs index ac6a7d85cf..2e14cc9c86 100644 --- a/crates/vm/src/system/memory/mod.rs +++ b/crates/vm/src/system/memory/mod.rs @@ -3,7 +3,6 @@ use openvm_circuit_primitives_derive::AlignedBorrow; mod adapter; mod controller; pub mod merkle; -mod offline; pub mod offline_checker; pub mod online; pub mod paged_vec; @@ -14,7 +13,7 @@ pub mod tree; mod volatile; pub use controller::*; -pub use offline::*; +pub use online::INITIAL_TIMESTAMP; pub use paged_vec::*; #[derive(PartialEq, Copy, Clone, Debug, Eq)] diff --git a/crates/vm/src/system/memory/offline.rs b/crates/vm/src/system/memory/offline.rs deleted file mode 100644 index 0be8d5214b..0000000000 --- a/crates/vm/src/system/memory/offline.rs +++ /dev/null @@ -1,1080 +0,0 @@ -use std::{array, cmp::max}; - -use openvm_circuit_primitives::{ - assert_less_than::AssertLtSubAir, var_range::SharedVariableRangeCheckerChip, -}; -use openvm_stark_backend::p3_field::PrimeField32; -use rustc_hash::FxHashSet; - -use super::{AddressMap, PagedVec, SharedMemoryHelper, PAGE_SIZE}; -use crate::{ - arch::MemoryConfig, - system::memory::{ - adapter::{AccessAdapterInventory, AccessAdapterRecord, AccessAdapterRecordKind}, - offline_checker::{MemoryBridge, MemoryBus}, - MemoryAuxColsFactory, MemoryImage, RecordId, TimestampedEquipartition, TimestampedValues, - }, -}; - -pub const INITIAL_TIMESTAMP: u32 = 0; - -#[repr(C)] -#[derive(Clone, Default, PartialEq, Eq, Debug)] -struct BlockData { - pointer: u32, - timestamp: u32, - size: usize, -} - -struct BlockMap { - /// Block ids. 0 is a special value standing for the default block. - id: AddressMap, - /// The place where non-default blocks are stored. - storage: Vec, - initial_block_size: usize, -} - -impl BlockMap { - pub fn from_mem_config(mem_config: &MemoryConfig, initial_block_size: usize) -> Self { - assert!(initial_block_size.is_power_of_two()); - Self { - id: AddressMap::from_mem_config(mem_config), - storage: vec![], - initial_block_size, - } - } - - fn initial_block_data(pointer: u32, initial_block_size: usize) -> BlockData { - let aligned_pointer = (pointer / initial_block_size as u32) * initial_block_size as u32; - BlockData { - pointer: aligned_pointer, - size: initial_block_size, - timestamp: INITIAL_TIMESTAMP, - } - } - - pub fn get_without_adding(&self, address: (u32, u32)) -> BlockData { - let idx = unsafe { self.id.get::(address) }; - if idx == 0 { - Self::initial_block_data(address.1, self.initial_block_size) - } else { - self.storage[idx - 1].clone() - } - } - - pub fn get(&mut self, (address_space, pointer): (u32, u32)) -> &BlockData { - let idx = unsafe { self.id.get::((address_space, pointer)) }; - if idx == 0 { - // `initial_block_size` is a power of two, as asserted in `from_mem_config`. - let pointer = pointer & !(self.initial_block_size as u32 - 1); - self.set_range( - (address_space, pointer), - self.initial_block_size, - Self::initial_block_data(pointer, self.initial_block_size), - ); - self.storage.last().unwrap() - } else { - &self.storage[idx - 1] - } - } - - pub fn get_mut(&mut self, (address_space, pointer): (u32, u32)) -> &mut BlockData { - let idx = unsafe { self.id.get::((address_space, pointer)) }; - if idx == 0 { - let pointer = pointer - pointer % self.initial_block_size as u32; - self.set_range( - (address_space, pointer), - self.initial_block_size, - Self::initial_block_data(pointer, self.initial_block_size), - ); - self.storage.last_mut().unwrap() - } else { - &mut self.storage[idx - 1] - } - } - - pub fn set_range( - &mut self, - (address_space, pointer): (u32, u32), - len: usize, - block: BlockData, - ) { - self.storage.push(block); - for i in 0..len { - unsafe { - self.id - .insert((address_space, pointer + i as u32), self.storage.len()); - } - } - } - - pub fn items(&self) -> impl Iterator + '_ { - self.id - .paged_vecs - .iter() - .enumerate() - .flat_map(move |(as_idx, paged_vec)| { - paged_vec.iter::().map(move |(ptr_idx, x)| { - ((as_idx as u32 + self.id.as_offset, ptr_idx as u32), x) - }) - }) - .filter(|(_, idx)| *idx > 0) - .map(|(address, idx)| (address, &self.storage[idx - 1])) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct MemoryRecord { - pub address_space: T, - pub pointer: T, - pub timestamp: u32, - pub prev_timestamp: u32, - data: Vec, - /// None if a read. - prev_data: Option>, -} - -impl MemoryRecord { - pub fn data_slice(&self) -> &[T] { - self.data.as_slice() - } - - pub fn prev_data_slice(&self) -> Option<&[T]> { - self.prev_data.as_deref() - } -} - -impl MemoryRecord { - pub fn data_at(&self, index: usize) -> T { - self.data[index] - } -} - -pub struct OfflineMemory { - block_data: BlockMap, - data: Vec>, - as_offset: u32, - timestamp: u32, - timestamp_max_bits: usize, - - memory_bus: MemoryBus, - range_checker: SharedVariableRangeCheckerChip, - - log: Vec>>, -} - -impl OfflineMemory { - /// Creates a new partition with the given initial block size. - /// - /// Panics if the initial block size is not a power of two. - pub fn new( - initial_memory: MemoryImage, - initial_block_size: usize, - memory_bus: MemoryBus, - range_checker: SharedVariableRangeCheckerChip, - config: MemoryConfig, - ) -> Self { - assert_eq!(initial_memory.as_offset, config.as_offset); - Self { - block_data: BlockMap::from_mem_config(&config, initial_block_size), - data: initial_memory.paged_vecs, - as_offset: config.as_offset, - timestamp: INITIAL_TIMESTAMP + 1, - timestamp_max_bits: config.clk_max_bits, - memory_bus, - range_checker, - log: vec![], - } - } - - pub fn set_initial_memory(&mut self, initial_memory: MemoryImage, config: MemoryConfig) { - assert_eq!(self.timestamp, INITIAL_TIMESTAMP + 1); - assert_eq!(initial_memory.as_offset, config.as_offset); - self.as_offset = config.as_offset; - self.data = initial_memory.paged_vecs; - } - - pub(super) fn set_log_capacity(&mut self, access_capacity: usize) { - assert!(self.log.is_empty()); - self.log = Vec::with_capacity(access_capacity); - } - - pub fn memory_bridge(&self) -> MemoryBridge { - MemoryBridge::new( - self.memory_bus, - self.timestamp_max_bits, - self.range_checker.bus(), - ) - } - - pub fn timestamp(&self) -> u32 { - self.timestamp - } - - /// Increments the current timestamp by one and returns the new value. - pub fn increment_timestamp(&mut self) { - self.increment_timestamp_by(1) - } - - /// Increments the current timestamp by a specified delta and returns the new value. - pub fn increment_timestamp_by(&mut self, delta: u32) { - self.log.push(None); - self.timestamp += delta; - } - - /// Writes an array of values to the memory at the specified address space and start index. - pub fn write( - &mut self, - address_space: u32, - pointer: u32, - values: Vec, - records: &mut AccessAdapterInventory, - ) { - let len = values.len(); - assert!(len.is_power_of_two()); - assert_ne!(address_space, 0); - - let prev_timestamp = self.access_updating_timestamp(address_space, pointer, len, records); - - debug_assert!(prev_timestamp < self.timestamp); - - todo!(); - // let pointer = pointer as usize; - // let prev_data = self.data[(address_space - self.as_offset) as usize] - // .set_range(pointer..pointer + len, &values); - - // let record = MemoryRecord { - // address_space: F::from_canonical_u32(address_space), - // pointer: F::from_canonical_usize(pointer), - // timestamp: self.timestamp, - // prev_timestamp, - // data: values, - // prev_data: Some(prev_data), - // }; - // self.log.push(Some(record)); - // self.timestamp += 1; - } - - /// Reads an array of values from the memory at the specified address space and start index. - pub fn read( - &mut self, - address_space: u32, - pointer: u32, - len: usize, - adapter_records: &mut AccessAdapterInventory, - ) { - assert!(len.is_power_of_two()); - if address_space == 0 { - let pointer = F::from_canonical_u32(pointer); - self.log.push(Some(MemoryRecord { - address_space: F::ZERO, - pointer, - timestamp: self.timestamp, - prev_timestamp: 0, - data: vec![pointer], - prev_data: None, - })); - self.timestamp += 1; - return; - } - - let prev_timestamp = - self.access_updating_timestamp(address_space, pointer, len, adapter_records); - - debug_assert!(prev_timestamp < self.timestamp); - - let values = self.range_vec(address_space, pointer, len); - - self.log.push(Some(MemoryRecord { - address_space: F::from_canonical_u32(address_space), - pointer: F::from_canonical_u32(pointer), - timestamp: self.timestamp, - prev_timestamp, - data: values, - prev_data: None, - })); - self.timestamp += 1; - } - - pub fn record_by_id(&self, id: RecordId) -> &MemoryRecord { - self.log[id.0].as_ref().unwrap() - } - - pub fn finalize( - &mut self, - adapter_records: &mut AccessAdapterInventory, - ) -> TimestampedEquipartition { - // First make sure the partition we maintain in self.block_data is an equipartition. - // Grab all aligned pointers that need to be re-accessed. - let to_access: FxHashSet<_> = self - .block_data - .items() - .map(|((address_space, pointer), _)| (address_space, (pointer / N as u32) * N as u32)) - .collect(); - - for &(address_space, pointer) in to_access.iter() { - let block = self.block_data.get((address_space, pointer)); - if block.pointer != pointer || block.size != N { - self.access(address_space, pointer, N, adapter_records); - } - } - - let mut equipartition = TimestampedEquipartition::::new(); - for (address_space, pointer) in to_access { - let block = self.block_data.get((address_space, pointer)); - - debug_assert_eq!(block.pointer % N as u32, 0); - debug_assert_eq!(block.size, N); - - equipartition.insert( - (address_space, pointer / N as u32), - TimestampedValues { - timestamp: block.timestamp, - values: self.range_array::(address_space, pointer), - }, - ); - } - equipartition - } - - // Modifies the partition to ensure that there is a block starting at (address_space, query). - fn split_to_make_boundary( - &mut self, - address_space: u32, - query: u32, - records: &mut AccessAdapterInventory, - ) { - let lim = (self.data[(address_space - self.as_offset) as usize].bytes_capacity()) as u32; - if query == lim { - return; - } - assert!(query < lim); - let original_block = self.block_containing(address_space, query); - if original_block.pointer == query { - return; - } - - let data = self.range_vec(address_space, original_block.pointer, original_block.size); - - let timestamp = original_block.timestamp; - - let mut cur_ptr = original_block.pointer; - let mut cur_size = original_block.size; - todo!() - // while cur_size > 0 { - // // Split. - // records.add_record(AccessAdapterRecord { - // timestamp, - // address_space: F::from_canonical_u32(address_space), - // start_index: F::from_canonical_u32(cur_ptr), - // data: data[(cur_ptr - original_block.pointer) as usize - // ..(cur_ptr - original_block.pointer) as usize + cur_size] - // .to_vec(), - // kind: AccessAdapterRecordKind::Split, - // }); - - // let half_size = cur_size / 2; - // let half_size_u32 = half_size as u32; - // let mid_ptr = cur_ptr + half_size_u32; - - // if query <= mid_ptr { - // // The right is finalized; add it to the partition. - // let block = BlockData { - // pointer: mid_ptr, - // size: half_size, - // timestamp, - // }; - // self.block_data - // .set_range(&(address_space, mid_ptr), half_size, block); - // } - // if query >= cur_ptr + half_size_u32 { - // // The left is finalized; add it to the partition. - // let block = BlockData { - // pointer: cur_ptr, - // size: half_size, - // timestamp, - // }; - // self.block_data - // .set_range(&(address_space, cur_ptr), half_size, block); - // } - // if mid_ptr <= query { - // cur_ptr = mid_ptr; - // } - // if cur_ptr == query { - // break; - // } - // cur_size = half_size; - // } - } - - fn access_updating_timestamp( - &mut self, - address_space: u32, - pointer: u32, - size: usize, - records: &mut AccessAdapterInventory, - ) -> u32 { - self.access(address_space, pointer, size, records); - - let mut prev_timestamp = None; - - let mut i = 0; - while i < size as u32 { - let block = self.block_data.get_mut((address_space, pointer + i)); - debug_assert!(i == 0 || prev_timestamp == Some(block.timestamp)); - prev_timestamp = Some(block.timestamp); - block.timestamp = self.timestamp; - i = block.pointer + block.size as u32; - } - prev_timestamp.unwrap() - } - - fn access( - &mut self, - address_space: u32, - pointer: u32, - size: usize, - records: &mut AccessAdapterInventory, - ) { - self.split_to_make_boundary(address_space, pointer, records); - self.split_to_make_boundary(address_space, pointer + size as u32, records); - - let block_data = self.block_containing(address_space, pointer); - - if block_data.pointer == pointer && block_data.size == size { - return; - } - assert!(size > 1); - - // Now recursively access left and right blocks to ensure they are in the partition. - let half_size = size / 2; - self.access(address_space, pointer, half_size, records); - self.access( - address_space, - pointer + half_size as u32, - half_size, - records, - ); - - self.merge_block_with_next(address_space, pointer, records); - } - - /// Merges the two adjacent blocks starting at (address_space, pointer). - /// - /// Panics if there is no block starting at (address_space, pointer) or if the two blocks - /// do not have the same size. - fn merge_block_with_next( - &mut self, - address_space: u32, - pointer: u32, - records: &mut AccessAdapterInventory, - ) { - let left_block = self.block_data.get((address_space, pointer)); - - let left_timestamp = left_block.timestamp; - let size = left_block.size; - - let right_timestamp = self - .block_data - .get((address_space, pointer + size as u32)) - .timestamp; - - let timestamp = max(left_timestamp, right_timestamp); - self.block_data.set_range( - (address_space, pointer), - 2 * size, - BlockData { - pointer, - size: 2 * size, - timestamp, - }, - ); - records.add_record(AccessAdapterRecord { - timestamp, - address_space: F::from_canonical_u32(address_space), - start_index: F::from_canonical_u32(pointer), - data: self.range_vec(address_space, pointer, 2 * size), - kind: AccessAdapterRecordKind::Merge { - left_timestamp, - right_timestamp, - }, - }); - } - - fn block_containing(&mut self, address_space: u32, pointer: u32) -> BlockData { - self.block_data.get_without_adding((address_space, pointer)) - } - - pub fn get(&self, address_space: u32, pointer: u32) -> F { - self.data[(address_space - self.as_offset) as usize].get(pointer as usize) - } - - fn range_array(&self, address_space: u32, pointer: u32) -> [F; N] { - array::from_fn(|i| self.get(address_space, pointer + i as u32)) - } - - fn range_vec(&self, _address_space: u32, _pointer: u32, _len: usize) -> Vec { - unimplemented!("to remove") - } - - pub fn aux_cols_factory(&self) -> MemoryAuxColsFactory { - let range_bus = self.range_checker.bus(); - MemoryAuxColsFactory { - range_checker: self.range_checker.as_ref(), - timestamp_lt_air: AssertLtSubAir::new(range_bus, self.timestamp_max_bits), - _marker: Default::default(), - } - } - - // just for unit testing - #[cfg(test)] - fn last_record(&self) -> &MemoryRecord { - self.log.last().unwrap().as_ref().unwrap() - } -} - -#[cfg(test)] -mod tests { - use openvm_circuit_primitives::var_range::{ - SharedVariableRangeCheckerChip, VariableRangeCheckerBus, - }; - use openvm_stark_backend::p3_field::FieldAlgebra; - use openvm_stark_sdk::p3_baby_bear::BabyBear; - - use super::{BlockData, MemoryRecord, OfflineMemory}; - use crate::{ - arch::MemoryConfig, - system::memory::{ - adapter::{AccessAdapterInventory, AccessAdapterRecord, AccessAdapterRecordKind}, - offline_checker::MemoryBus, - paged_vec::AddressMap, - MemoryImage, TimestampedValues, - }, - }; - - macro_rules! bb { - ($x:expr) => { - BabyBear::from_canonical_u32($x) - }; - } - - macro_rules! bba { - [$($x:expr),*] => { - [$(BabyBear::from_canonical_u32($x)),*] - } - } - - macro_rules! bbvec { - [$($x:expr),*] => { - vec![$(BabyBear::from_canonical_u32($x)),*] - } - } - - fn setup_test( - initial_memory: MemoryImage, - initial_block_size: usize, - ) -> (OfflineMemory, AccessAdapterInventory) { - let memory_bus = MemoryBus::new(0); - let range_checker = - SharedVariableRangeCheckerChip::new(VariableRangeCheckerBus::new(1, 29)); - let mem_config = MemoryConfig { - as_offset: initial_memory.as_offset, - ..Default::default() - }; - let memory = OfflineMemory::new( - initial_memory, - initial_block_size, - memory_bus, - range_checker.clone(), - mem_config, - ); - let access_adapter_inventory = AccessAdapterInventory::new( - range_checker, - memory_bus, - mem_config.clk_max_bits, - mem_config.max_access_adapter_n, - ); - (memory, access_adapter_inventory) - } - - #[test] - fn test_partition() { - let initial_memory = AddressMap::new(0, 1, 16); - let (mut memory, _) = setup_test(initial_memory, 8); - assert_eq!( - memory.block_containing(1, 13), - BlockData { - pointer: 8, - size: 8, - timestamp: 0, - } - ); - - assert_eq!( - memory.block_containing(1, 8), - BlockData { - pointer: 8, - size: 8, - timestamp: 0, - } - ); - - assert_eq!( - memory.block_containing(1, 15), - BlockData { - pointer: 8, - size: 8, - timestamp: 0, - } - ); - - assert_eq!( - memory.block_containing(1, 16), - BlockData { - pointer: 16, - size: 8, - timestamp: 0, - } - ); - } - - #[test] - fn test_write_read_initial_block_len_1() { - let (mut memory, mut access_adapters) = setup_test(MemoryImage::default(), 1); - let address_space = 1; - - memory.write(address_space, 0, bbvec![1, 2, 3, 4], &mut access_adapters); - - memory.read(address_space, 0, 2, &mut access_adapters); - let read_record = memory.last_record(); - assert_eq!(read_record.data, bba![1, 2]); - - memory.write(address_space, 2, bbvec![100], &mut access_adapters); - - memory.read(address_space, 0, 4, &mut access_adapters); - let read_record = memory.last_record(); - assert_eq!(read_record.data, bba![1, 2, 100, 4]); - } - - #[test] - fn test_records_initial_block_len_1() { - let (mut memory, mut adapter_records) = setup_test(MemoryImage::default(), 1); - - memory.write(1, 0, bbvec![1, 2, 3, 4], &mut adapter_records); - - // Above write first causes merge of [0:1] and [1:2] into [0:2]. - assert_eq!( - adapter_records.records_for_n(2)[0], - AccessAdapterRecord { - timestamp: 0, - address_space: bb!(1), - start_index: bb!(0), - data: bbvec![0, 0], - kind: AccessAdapterRecordKind::Merge { - left_timestamp: 0, - right_timestamp: 0, - }, - } - ); - // then merge [2:3] and [3:4] into [2:4]. - assert_eq!( - adapter_records.records_for_n(2)[1], - AccessAdapterRecord { - timestamp: 0, - address_space: bb!(1), - start_index: bb!(2), - data: bbvec![0, 0], - kind: AccessAdapterRecordKind::Merge { - left_timestamp: 0, - right_timestamp: 0, - }, - } - ); - // then merge [0:2] and [2:4] into [0:4]. - assert_eq!( - adapter_records.records_for_n(4)[0], - AccessAdapterRecord { - timestamp: 0, - address_space: bb!(1), - start_index: bb!(0), - data: bbvec![0, 0, 0, 0], - kind: AccessAdapterRecordKind::Merge { - left_timestamp: 0, - right_timestamp: 0, - }, - } - ); - // At time 1 we write [0:4]. - let write_record = memory.last_record(); - assert_eq!( - write_record, - &MemoryRecord { - address_space: bb!(1), - pointer: bb!(0), - timestamp: 1, - prev_timestamp: 0, - data: bbvec![1, 2, 3, 4], - prev_data: Some(bbvec![0, 0, 0, 0]), - } - ); - assert_eq!(memory.timestamp(), 2); - assert_eq!(adapter_records.total_records(), 3); - - memory.read(1, 0, 4, &mut adapter_records); - let read_record = memory.last_record(); - // At time 2 we read [0:4]. - assert_eq!(adapter_records.total_records(), 3); - assert_eq!( - read_record, - &MemoryRecord { - address_space: bb!(1), - pointer: bb!(0), - timestamp: 2, - prev_timestamp: 1, - data: bbvec![1, 2, 3, 4], - prev_data: None, - } - ); - assert_eq!(memory.timestamp(), 3); - - memory.write(1, 0, bbvec![10, 11], &mut adapter_records); - let write_record = memory.last_record(); - // write causes split [0:4] into [0:2] and [2:4] (to prepare for write to [0:2]). - assert_eq!(adapter_records.total_records(), 4); - assert_eq!( - adapter_records.records_for_n(4).last().unwrap(), - &AccessAdapterRecord { - timestamp: 2, - address_space: bb!(1), - start_index: bb!(0), - data: bbvec![1, 2, 3, 4], - kind: AccessAdapterRecordKind::Split, - } - ); - - // At time 3 we write [10, 11] into [0, 2]. - assert_eq!( - write_record, - &MemoryRecord { - address_space: bb!(1), - pointer: bb!(0), - timestamp: 3, - prev_timestamp: 2, - data: bbvec![10, 11], - prev_data: Some(bbvec![1, 2]), - } - ); - - memory.read(1, 0, 4, &mut adapter_records); - let read_record = memory.last_record(); - assert_eq!(adapter_records.total_records(), 5); - assert_eq!( - adapter_records.records_for_n(4).last().unwrap(), - &AccessAdapterRecord { - timestamp: 3, - address_space: bb!(1), - start_index: bb!(0), - data: bbvec![10, 11, 3, 4], - kind: AccessAdapterRecordKind::Merge { - left_timestamp: 3, - right_timestamp: 2 - }, - } - ); - // At time 9 we read [0:4]. - assert_eq!( - read_record, - &MemoryRecord { - address_space: bb!(1), - pointer: bb!(0), - timestamp: 4, - prev_timestamp: 3, - data: bbvec![10, 11, 3, 4], - prev_data: None, - } - ); - } - - #[test] - fn test_records_initial_block_len_8() { - let (mut memory, mut adapter_records) = setup_test(MemoryImage::default(), 8); - - memory.write(1, 0, bbvec![1, 2, 3, 4], &mut adapter_records); - let write_record = memory.last_record(); - - // Above write first causes split of [0:8] into [0:4] and [4:8]. - assert_eq!(adapter_records.total_records(), 1); - assert_eq!( - adapter_records.records_for_n(8).last().unwrap(), - &AccessAdapterRecord { - timestamp: 0, - address_space: bb!(1), - start_index: bb!(0), - data: bbvec![0, 0, 0, 0, 0, 0, 0, 0], - kind: AccessAdapterRecordKind::Split, - } - ); - // At time 1 we write [0:4]. - assert_eq!( - write_record, - &MemoryRecord { - address_space: bb!(1), - pointer: bb!(0), - timestamp: 1, - prev_timestamp: 0, - data: bbvec![1, 2, 3, 4], - prev_data: Some(bbvec![0, 0, 0, 0]), - } - ); - assert_eq!(memory.timestamp(), 2); - - memory.read(1, 0, 4, &mut adapter_records); - let read_record = memory.last_record(); - // At time 2 we read [0:4]. - assert_eq!(adapter_records.total_records(), 1); - assert_eq!( - read_record, - &MemoryRecord { - address_space: bb!(1), - pointer: bb!(0), - timestamp: 2, - prev_timestamp: 1, - data: bbvec![1, 2, 3, 4], - prev_data: None, - } - ); - assert_eq!(memory.timestamp(), 3); - - memory.write(1, 0, bbvec![10, 11], &mut adapter_records); - let write_record = memory.last_record(); - // write causes split [0:4] into [0:2] and [2:4] (to prepare for write to [0:2]). - assert_eq!(adapter_records.total_records(), 2); - assert_eq!( - adapter_records.records_for_n(4).last().unwrap(), - &AccessAdapterRecord { - timestamp: 2, - address_space: bb!(1), - start_index: bb!(0), - data: bbvec![1, 2, 3, 4], - kind: AccessAdapterRecordKind::Split, - } - ); - - // At time 3 we write [10, 11] into [0, 2]. - assert_eq!( - write_record, - &MemoryRecord { - address_space: bb!(1), - pointer: bb!(0), - timestamp: 3, - prev_timestamp: 2, - data: bbvec![10, 11], - prev_data: Some(bbvec![1, 2]), - } - ); - - memory.read(1, 0, 4, &mut adapter_records); - let read_record = memory.last_record(); - assert_eq!(adapter_records.total_records(), 3); - assert_eq!( - adapter_records.records_for_n(4).last().unwrap(), - &AccessAdapterRecord { - timestamp: 3, - address_space: bb!(1), - start_index: bb!(0), - data: bbvec![10, 11, 3, 4], - kind: AccessAdapterRecordKind::Merge { - left_timestamp: 3, - right_timestamp: 2 - }, - } - ); - // At time 9 we read [0:4]. - assert_eq!( - read_record, - &MemoryRecord { - address_space: bb!(1), - pointer: bb!(0), - timestamp: 4, - prev_timestamp: 3, - data: bbvec![10, 11, 3, 4], - prev_data: None, - } - ); - } - - #[test] - fn test_get_initial_block_len_1() { - let (mut memory, mut adapter_records) = setup_test(MemoryImage::default(), 1); - - memory.write(2, 0, bbvec![4, 3, 2, 1], &mut adapter_records); - - assert_eq!(memory.get(2, 0), BabyBear::from_canonical_u32(4)); - assert_eq!(memory.get(2, 1), BabyBear::from_canonical_u32(3)); - assert_eq!(memory.get(2, 2), BabyBear::from_canonical_u32(2)); - assert_eq!(memory.get(2, 3), BabyBear::from_canonical_u32(1)); - assert_eq!(memory.get(2, 5), BabyBear::ZERO); - - assert_eq!(memory.get(1, 0), BabyBear::ZERO); - } - - #[test] - fn test_get_initial_block_len_8() { - let (mut memory, mut adapter_records) = setup_test(MemoryImage::default(), 8); - - memory.write(2, 0, bbvec![4, 3, 2, 1], &mut adapter_records); - - assert_eq!(memory.get(2, 0), BabyBear::from_canonical_u32(4)); - assert_eq!(memory.get(2, 1), BabyBear::from_canonical_u32(3)); - assert_eq!(memory.get(2, 2), BabyBear::from_canonical_u32(2)); - assert_eq!(memory.get(2, 3), BabyBear::from_canonical_u32(1)); - assert_eq!(memory.get(2, 5), BabyBear::ZERO); - assert_eq!(memory.get(2, 9), BabyBear::ZERO); - assert_eq!(memory.get(1, 0), BabyBear::ZERO); - } - - #[test] - fn test_finalize_empty() { - let (mut memory, mut adapter_records) = setup_test(MemoryImage::default(), 4); - - let memory = memory.finalize::<4>(&mut adapter_records); - assert_eq!(memory.len(), 0); - assert_eq!(adapter_records.total_records(), 0); - } - - #[test] - fn test_finalize_block_len_8() { - let (mut memory, mut adapter_records) = setup_test(MemoryImage::default(), 8); - // Make block 0:4 in address space 1 active. - memory.write(1, 0, bbvec![1, 2, 3, 4], &mut adapter_records); - - // Make block 16:32 in address space 1 active. - memory.write( - 1, - 16, - bbvec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - &mut adapter_records, - ); - - // Make block 64:72 in address space 2 active. - memory.write(2, 64, bbvec![8, 7, 6, 5, 4, 3, 2, 1], &mut adapter_records); - - let num_records_before_finalize = adapter_records.total_records(); - - // Finalize to a partition of size 8. - let final_memory = memory.finalize::<8>(&mut adapter_records); - assert_eq!(final_memory.len(), 4); - assert_eq!( - final_memory.get(&(1, 0)), - Some(&TimestampedValues { - values: bba![1, 2, 3, 4, 0, 0, 0, 0], - timestamp: 1, - }) - ); - // start_index = 16 corresponds to label = 2 - assert_eq!( - final_memory.get(&(1, 2)), - Some(&TimestampedValues { - values: bba![1, 1, 1, 1, 1, 1, 1, 1], - timestamp: 2, - }) - ); - // start_index = 24 corresponds to label = 3 - assert_eq!( - final_memory.get(&(1, 3)), - Some(&TimestampedValues { - values: bba![1, 1, 1, 1, 1, 1, 1, 1], - timestamp: 2, - }) - ); - // start_index = 64 corresponds to label = 8 - assert_eq!( - final_memory.get(&(2, 8)), - Some(&TimestampedValues { - values: bba![8, 7, 6, 5, 4, 3, 2, 1], - timestamp: 3, - }) - ); - - // We need to do 1 + 1 + 0 = 2 adapters. - assert_eq!( - adapter_records.total_records() - num_records_before_finalize, - 2 - ); - } - - #[test] - fn test_write_read_initial_block_len_8_initial_memory() { - type F = BabyBear; - - // Initialize initial memory with blocks at indices 0 and 2 - let mut initial_memory = MemoryImage::default(); - for i in 0..8 { - unsafe { - initial_memory.insert((1, i), F::from_canonical_u32(i + 1)); - initial_memory.insert((1, 16 + i), F::from_canonical_u32(i + 1)); - } - } - - let (mut memory, mut adapter_records) = setup_test(initial_memory, 8); - - // Verify initial state of block 0 (pointers 0–8) - memory.read(1, 0, 8, &mut adapter_records); - let initial_read_record_0 = memory.last_record(); - assert_eq!(initial_read_record_0.data, bbvec![1, 2, 3, 4, 5, 6, 7, 8]); - - // Verify initial state of block 2 (pointers 16–24) - memory.read(1, 16, 8, &mut adapter_records); - let initial_read_record_2 = memory.last_record(); - assert_eq!(initial_read_record_2.data, bbvec![1, 2, 3, 4, 5, 6, 7, 8]); - - // Test: Write a partial block to block 0 (pointer 0) and read back partially and fully - memory.write(1, 0, bbvec![9, 9, 9, 9], &mut adapter_records); - memory.read(1, 0, 2, &mut adapter_records); - let partial_read_record = memory.last_record(); - assert_eq!(partial_read_record.data, bbvec![9, 9]); - - memory.read(1, 0, 8, &mut adapter_records); - let full_read_record_0 = memory.last_record(); - assert_eq!(full_read_record_0.data, bbvec![9, 9, 9, 9, 5, 6, 7, 8]); - - // Test: Write a single element to pointer 2 and verify read in different lengths - memory.write(1, 2, bbvec![100], &mut adapter_records); - memory.read(1, 1, 4, &mut adapter_records); - let read_record_4 = memory.last_record(); - assert_eq!(read_record_4.data, bbvec![9, 100, 9, 5]); - - memory.read(1, 2, 8, &mut adapter_records); - let full_read_record_2 = memory.last_record(); - assert_eq!(full_read_record_2.data, bba![100, 9, 5, 6, 7, 8, 0, 0]); - - // Test: Write and read at the last pointer in block 2 (pointer 23, part of key (1, 2)) - memory.write(1, 23, bbvec![77], &mut adapter_records); - memory.read(1, 23, 2, &mut adapter_records); - let boundary_read_record = memory.last_record(); - assert_eq!(boundary_read_record.data, bba![77, 0]); // Last byte modified, ensuring boundary check - - // Test: Reading from an uninitialized block (should default to 0) - memory.read(1, 10, 4, &mut adapter_records); - let default_read_record = memory.last_record(); - assert_eq!(default_read_record.data, bba![0, 0, 0, 0]); - - memory.read(1, 100, 4, &mut adapter_records); - let default_read_record = memory.last_record(); - assert_eq!(default_read_record.data, bba![0, 0, 0, 0]); - - // Test: Overwrite entire memory pointer 16–24 and verify - memory.write( - 1, - 16, - bbvec![50, 50, 50, 50, 50, 50, 50, 50], - &mut adapter_records, - ); - memory.read(1, 16, 8, &mut adapter_records); - let overwrite_read_record = memory.last_record(); - assert_eq!( - overwrite_read_record.data, - bba![50, 50, 50, 50, 50, 50, 50, 50] - ); // Verify entire block overwrite - } -} diff --git a/crates/vm/src/system/memory/online.rs b/crates/vm/src/system/memory/online.rs index 1ac72bb83e..2dbf96391b 100644 --- a/crates/vm/src/system/memory/online.rs +++ b/crates/vm/src/system/memory/online.rs @@ -12,10 +12,9 @@ use super::{ paged_vec::{AddressMap, PAGE_SIZE}, Address, MemoryAddress, PagedVec, }; -use crate::{ - arch::MemoryConfig, - system::memory::{offline::INITIAL_TIMESTAMP, MemoryImage, RecordId}, -}; +use crate::{arch::MemoryConfig, system::memory::MemoryImage}; + +pub const INITIAL_TIMESTAMP: u32 = 0; /// API for guest memory conforming to OpenVM ISA pub trait GuestMemory { diff --git a/crates/vm/src/system/memory/persistent.rs b/crates/vm/src/system/memory/persistent.rs index c2ce24a98a..0163555dd6 100644 --- a/crates/vm/src/system/memory/persistent.rs +++ b/crates/vm/src/system/memory/persistent.rs @@ -19,12 +19,12 @@ use openvm_stark_backend::{ }; use rustc_hash::FxHashSet; -use super::merkle::SerialReceiver; +use super::{merkle::SerialReceiver, online::INITIAL_TIMESTAMP}; use crate::{ arch::hasher::Hasher, system::memory::{ dimensions::MemoryDimensions, offline_checker::MemoryBus, MemoryAddress, MemoryImage, - TimestampedEquipartition, INITIAL_TIMESTAMP, + TimestampedEquipartition, }, }; diff --git a/crates/vm/src/system/native_adapter/mod.rs b/crates/vm/src/system/native_adapter/mod.rs index 8be14277c4..cc3e31ed2f 100644 --- a/crates/vm/src/system/native_adapter/mod.rs +++ b/crates/vm/src/system/native_adapter/mod.rs @@ -6,8 +6,7 @@ use std::{ use openvm_circuit::{ arch::{ AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge, - ExecutionBus, ExecutionState, MinimalInstruction, Result, VmAdapterAir, VmAdapterChip, - VmAdapterInterface, + ExecutionBus, ExecutionState, MinimalInstruction, Result, VmAdapterAir, VmAdapterInterface, }, system::{ memory::{ @@ -30,7 +29,7 @@ use serde_big_array::BigArray; use super::memory::{online::TracingMemory, MemoryAuxColsFactory}; use crate::{ arch::{AdapterExecutorE1, AdapterTraceStep}, - system::memory::{online::GuestMemory, OfflineMemory, RecordId}, + system::memory::{online::GuestMemory, RecordId}, }; #[repr(C)] diff --git a/extensions/keccak256/circuit/src/extension.rs b/extensions/keccak256/circuit/src/extension.rs index d24681fb55..86ddc5802c 100644 --- a/extensions/keccak256/circuit/src/extension.rs +++ b/extensions/keccak256/circuit/src/extension.rs @@ -84,7 +84,6 @@ impl VmExtension for Keccak256 { inventory.add_periphery_chip(chip.clone()); chip }; - let offline_memory = builder.system_base().offline_memory(); let address_bits = builder.system_config().memory_config.pointer_max_bits; let keccak_chip = KeccakVmChip::new( diff --git a/extensions/rv32im/circuit/src/base_alu/tests.rs b/extensions/rv32im/circuit/src/base_alu/tests.rs index 313bd83f52..521fca0c9e 100644 --- a/extensions/rv32im/circuit/src/base_alu/tests.rs +++ b/extensions/rv32im/circuit/src/base_alu/tests.rs @@ -5,7 +5,7 @@ use openvm_circuit::{ testing::{ test_adapter::TestAdapterAir, TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS, }, - AdapterExecutorE1, AdapterTraceStep, NewVmChipWrapper, VmAirWrapper, VmChipWrapper, + AdapterExecutorE1, AdapterTraceStep, NewVmChipWrapper, VmAirWrapper, }, system::memory::{ online::{GuestMemory, TracingMemory}, diff --git a/extensions/rv32im/circuit/src/branch_eq/tests.rs b/extensions/rv32im/circuit/src/branch_eq/tests.rs index 3097f4ba17..df1489e073 100644 --- a/extensions/rv32im/circuit/src/branch_eq/tests.rs +++ b/extensions/rv32im/circuit/src/branch_eq/tests.rs @@ -2,8 +2,8 @@ use std::{array, borrow::BorrowMut}; use openvm_circuit::arch::{ testing::{memory::gen_pointer, TestAdapterChip, VmChipTestBuilder}, - BasicAdapterInterface, ExecutionBridge, ImmInstruction, InstructionExecutor, VmAdapterChip, - VmAirWrapper, VmChipWrapper, VmCoreChip, + BasicAdapterInterface, ExecutionBridge, ImmInstruction, InstructionExecutor, VmAirWrapper, + VmCoreChip, }; use openvm_instructions::{ instruction::Instruction, diff --git a/extensions/rv32im/circuit/src/branch_lt/tests.rs b/extensions/rv32im/circuit/src/branch_lt/tests.rs index 205d029dc5..e5fcb9bd22 100644 --- a/extensions/rv32im/circuit/src/branch_lt/tests.rs +++ b/extensions/rv32im/circuit/src/branch_lt/tests.rs @@ -3,8 +3,8 @@ use std::borrow::BorrowMut; use openvm_circuit::{ arch::{ testing::{memory::gen_pointer, TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - BasicAdapterInterface, ExecutionBridge, ImmInstruction, InstructionExecutor, VmAdapterChip, - VmAirWrapper, VmChipWrapper, VmCoreChip, + BasicAdapterInterface, ExecutionBridge, ImmInstruction, InstructionExecutor, VmAirWrapper, + VmCoreChip, }, utils::{generate_long_number, i32_to_f}, }; diff --git a/extensions/rv32im/circuit/src/divrem/tests.rs b/extensions/rv32im/circuit/src/divrem/tests.rs index 393b2d678a..1548769d5a 100644 --- a/extensions/rv32im/circuit/src/divrem/tests.rs +++ b/extensions/rv32im/circuit/src/divrem/tests.rs @@ -6,7 +6,7 @@ use openvm_circuit::{ memory::gen_pointer, TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS, RANGE_TUPLE_CHECKER_BUS, }, - ExecutionBridge, InstructionExecutor, VmAdapterChip, VmAirWrapper, VmChipWrapper, + ExecutionBridge, InstructionExecutor, VmAirWrapper, }, utils::generate_long_number, }; diff --git a/extensions/rv32im/circuit/src/extension.rs b/extensions/rv32im/circuit/src/extension.rs index a802419c7b..d9dd57dc4c 100644 --- a/extensions/rv32im/circuit/src/extension.rs +++ b/extensions/rv32im/circuit/src/extension.rs @@ -569,7 +569,6 @@ impl VmExtension for Rv32Io { program_bus, memory_bridge, } = builder.system_port(); - let offline_memory = builder.system_base().offline_memory(); let bitwise_lu_chip = if let Some(&chip) = builder .find_chip::>() @@ -593,7 +592,6 @@ impl VmExtension for Rv32Io { ), Rv32HintStoreStep::new( bitwise_lu_chip, - offline_memory.clone(), builder.system_config().memory_config.pointer_max_bits, Rv32HintStoreOpcode::CLASS_OFFSET, ), diff --git a/extensions/rv32im/circuit/src/hintstore/mod.rs b/extensions/rv32im/circuit/src/hintstore/mod.rs index c679aa27ca..b6c456a1f6 100644 --- a/extensions/rv32im/circuit/src/hintstore/mod.rs +++ b/extensions/rv32im/circuit/src/hintstore/mod.rs @@ -13,7 +13,7 @@ use openvm_circuit::{ memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, online::{GuestMemory, TracingMemory}, - MemoryAddress, MemoryAuxColsFactory, MemoryController, OfflineMemory, RecordId, + MemoryAddress, MemoryAuxColsFactory, MemoryController, RecordId, }, program::ProgramBus, }, @@ -286,7 +286,6 @@ pub struct Rv32HintStoreRecord { pub struct Rv32HintStoreStep { pointer_max_bits: usize, offset: usize, - offline_memory: Arc>>, pub streams: OnceLock>>>, bitwise_lookup_chip: SharedBitwiseOperationLookupChip, } @@ -294,14 +293,12 @@ pub struct Rv32HintStoreStep { impl Rv32HintStoreStep { pub fn new( bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - offline_memory: Arc>>, pointer_max_bits: usize, offset: usize, ) -> Self { Self { pointer_max_bits, offset, - offline_memory, streams: OnceLock::new(), bitwise_lookup_chip, } @@ -398,7 +395,7 @@ where for word_index in 0..(num_words as usize) { let offset = *trace_offset + word_index * width; - let mut row: &mut Rv32HintStoreCols = trace[offset..offset + width].borrow_mut(); + let row: &mut Rv32HintStoreCols = trace[offset..offset + width].borrow_mut(); if word_index != 0 { row.is_buffer = F::ONE; diff --git a/extensions/rv32im/circuit/src/hintstore/tests.rs b/extensions/rv32im/circuit/src/hintstore/tests.rs index 69bfdbee84..d76017a3a7 100644 --- a/extensions/rv32im/circuit/src/hintstore/tests.rs +++ b/extensions/rv32im/circuit/src/hintstore/tests.rs @@ -141,12 +141,7 @@ fn rand_hintstore_test() { 0, tester.address_bits(), ), - Rv32HintStoreStep::new( - bitwise_chip.clone(), - tester.offline_memory_mutex_arc(), - tester.address_bits(), - 0, - ), + Rv32HintStoreStep::new(bitwise_chip.clone(), tester.address_bits(), 0), MAX_INS_CAPACITY, tester.memory_helper(), ); @@ -197,12 +192,7 @@ fn run_negative_hintstore_test( 0, tester.address_bits(), ), - Rv32HintStoreStep::new( - bitwise_chip.clone(), - tester.offline_memory_mutex_arc(), - tester.address_bits(), - 0, - ), + Rv32HintStoreStep::new(bitwise_chip.clone(), tester.address_bits(), 0), MAX_INS_CAPACITY, tester.memory_helper(), ); @@ -259,12 +249,7 @@ fn execute_roundtrip_sanity_test() { 0, tester.address_bits(), ), - Rv32HintStoreStep::new( - bitwise_chip.clone(), - tester.offline_memory_mutex_arc(), - tester.address_bits(), - 0, - ), + Rv32HintStoreStep::new(bitwise_chip.clone(), tester.address_bits(), 0), MAX_INS_CAPACITY, tester.memory_helper(), ); diff --git a/extensions/rv32im/circuit/src/jalr/tests.rs b/extensions/rv32im/circuit/src/jalr/tests.rs index 5430b8de47..95831a3928 100644 --- a/extensions/rv32im/circuit/src/jalr/tests.rs +++ b/extensions/rv32im/circuit/src/jalr/tests.rs @@ -2,7 +2,7 @@ use std::{array, borrow::BorrowMut}; use openvm_circuit::arch::{ testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - VmAdapterChip, VmAirWrapper, + VmAirWrapper, }; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, diff --git a/extensions/rv32im/circuit/src/less_than/tests.rs b/extensions/rv32im/circuit/src/less_than/tests.rs index fe3f13aca6..c22d6a17c5 100644 --- a/extensions/rv32im/circuit/src/less_than/tests.rs +++ b/extensions/rv32im/circuit/src/less_than/tests.rs @@ -3,7 +3,7 @@ use std::borrow::BorrowMut; use openvm_circuit::{ arch::{ testing::{TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - NewVmChipWrapper, VmAirWrapper, VmChipWrapper, + NewVmChipWrapper, VmAirWrapper, }, utils::{generate_long_number, i32_to_f}, }; diff --git a/extensions/rv32im/circuit/src/loadstore/tests.rs b/extensions/rv32im/circuit/src/loadstore/tests.rs index 1eb53b156d..0dc4399b4b 100644 --- a/extensions/rv32im/circuit/src/loadstore/tests.rs +++ b/extensions/rv32im/circuit/src/loadstore/tests.rs @@ -3,7 +3,7 @@ use std::{array, borrow::BorrowMut}; use openvm_circuit::{ arch::{ testing::{memory::gen_pointer, VmChipTestBuilder}, - VmAdapterChip, VmAirWrapper, + VmAirWrapper, }, utils::u32_into_limbs, }; diff --git a/extensions/rv32im/circuit/src/mul/tests.rs b/extensions/rv32im/circuit/src/mul/tests.rs index d477420462..378e883deb 100644 --- a/extensions/rv32im/circuit/src/mul/tests.rs +++ b/extensions/rv32im/circuit/src/mul/tests.rs @@ -3,7 +3,7 @@ use std::borrow::BorrowMut; use openvm_circuit::{ arch::{ testing::{TestAdapterChip, VmChipTestBuilder, RANGE_TUPLE_CHECKER_BUS}, - ExecutionBridge, VmAdapterChip, VmAirWrapper, VmChipWrapper, + ExecutionBridge, VmAirWrapper, }, utils::generate_long_number, }; diff --git a/extensions/rv32im/circuit/src/mulh/tests.rs b/extensions/rv32im/circuit/src/mulh/tests.rs index ea2dcca24b..d082ba65b9 100644 --- a/extensions/rv32im/circuit/src/mulh/tests.rs +++ b/extensions/rv32im/circuit/src/mulh/tests.rs @@ -6,7 +6,7 @@ use openvm_circuit::{ memory::gen_pointer, TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS, RANGE_TUPLE_CHECKER_BUS, }, - ExecutionBridge, InstructionExecutor, VmAdapterChip, VmAirWrapper, VmChipWrapper, + ExecutionBridge, InstructionExecutor, VmAirWrapper, }, utils::generate_long_number, }; diff --git a/extensions/rv32im/circuit/src/shift/tests.rs b/extensions/rv32im/circuit/src/shift/tests.rs index aaaf786bc6..e9f48fc1ab 100644 --- a/extensions/rv32im/circuit/src/shift/tests.rs +++ b/extensions/rv32im/circuit/src/shift/tests.rs @@ -3,7 +3,7 @@ use std::{array, borrow::BorrowMut}; use openvm_circuit::{ arch::{ testing::{TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - NewVmChipWrapper, VmAirWrapper, VmChipWrapper, + NewVmChipWrapper, VmAirWrapper, }, utils::generate_long_number, }; From d12a24dbff235472bde7f2fadc9fcd951cd248b1 Mon Sep 17 00:00:00 2001 From: Arayi Khalatyan <127004086+arayikhalatyan@users.noreply.github.com> Date: Wed, 7 May 2025 11:05:52 -0400 Subject: [PATCH 14/49] feat: make rv32im testing uniform accross all the chips (#1630) Made the rv32im tests pass and made all the testing files to have the same testing interface. Deleted the `test_adapter`. Kept all the test cases unchanged. The only commented test case remaining is the `store` test to the address space 4, which is failing because currently memory accesses with block size 4 are not supported with the address space 4. All the test files have 3 types of tests: Positive, Negative, and Sanity tests. All the test files have 2 helper functions: `create_test_chip`, `set_and_execute`. An important thing to notice about negative tests when expecting an interaction fail (aka ChallangePhase error) is that ther might be an imbalance created for the wrong reasons. For example, there might be an imbalance on the range checker bus created by the interactions: [send 1] (sent from the chip_air) [receive 2] (the execution did `add_count(2)` at some point) This is not a "valid" fail since 1 is still in the range of the range checker. Because of this a manual check is needed for all the negative checks. To see all the imbalances occurred during a test remove the 'disable_debug_builder();' line from the `run_negative_test` function and run the test. I am 95% sure that I wen through all the negative tests and checked that the imbalances occurred are correct. The `test_adapter` tried to address this issue by getting rid of interaction imbalances on the memory bus. But even with the `test _adapter` a manual check was necessary. To solve this I suggest that we somehow keep all the interactions that occur during the test and automatically check that actually an invalid interaction has happened on a specified bus. Resolves INT-3975 --------- Co-authored-by: Ayush Shukla --- Cargo.lock | 18 +- crates/vm/src/arch/testing/mod.rs | 3 - crates/vm/src/arch/testing/test_adapter.rs | 103 --- extensions/rv32im/circuit/Cargo.toml | 2 + extensions/rv32im/circuit/src/auipc/tests.rs | 176 ++-- .../rv32im/circuit/src/base_alu/tests.rs | 750 ++++++---------- .../rv32im/circuit/src/branch_eq/tests.rs | 422 +++++---- .../rv32im/circuit/src/branch_lt/tests.rs | 781 ++++++++--------- extensions/rv32im/circuit/src/divrem/tests.rs | 798 +++++++++--------- .../rv32im/circuit/src/hintstore/tests.rs | 125 +-- .../rv32im/circuit/src/jal_lui/tests.rs | 193 ++--- extensions/rv32im/circuit/src/jalr/tests.rs | 405 ++++----- .../rv32im/circuit/src/less_than/tests.rs | 664 +++++++-------- .../circuit/src/load_sign_extend/tests.rs | 204 ++--- .../rv32im/circuit/src/loadstore/tests.rs | 349 +++----- extensions/rv32im/circuit/src/mul/tests.rs | 262 +++--- extensions/rv32im/circuit/src/mulh/tests.rs | 564 ++++++------- extensions/rv32im/circuit/src/shift/tests.rs | 560 ++++++------ extensions/rv32im/circuit/src/test_utils.rs | 13 +- 19 files changed, 2729 insertions(+), 3663 deletions(-) delete mode 100644 crates/vm/src/arch/testing/test_adapter.rs diff --git a/Cargo.lock b/Cargo.lock index a6692d798e..942d786eb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4781,6 +4781,7 @@ dependencies = [ "serde", "serde-big-array", "strum", + "test-case", ] [[package]] @@ -4948,6 +4949,7 @@ dependencies = [ [[package]] name = "openvm-stark-backend" version = "1.0.0" +source = "git+https://github.com/openvm-org/stark-backend.git?tag=v1.0.0#884f8e6aabf72bde00dc51f1f1121277bff73b1e" dependencies = [ "bitcode", "cfg-if", @@ -4975,6 +4977,7 @@ dependencies = [ [[package]] name = "openvm-stark-sdk" version = "1.0.0" +source = "git+https://github.com/openvm-org/stark-backend.git?tag=v1.0.0#884f8e6aabf72bde00dc51f1f1121277bff73b1e" dependencies = [ "derivative", "derive_more 0.99.19", @@ -4991,7 +4994,6 @@ dependencies = [ "p3-fri", "p3-goldilocks", "p3-keccak", - "p3-koala-bear", "p3-merkle-tree", "p3-poseidon", "p3-poseidon2", @@ -5270,20 +5272,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "p3-koala-bear" -version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=1ba4e5c#1ba4e5c40417f4f7aae86bcca56b6484b4b2490b" -dependencies = [ - "p3-field", - "p3-mds", - "p3-monty-31", - "p3-poseidon2", - "p3-symmetric", - "rand", - "serde", -] - [[package]] name = "p3-matrix" version = "0.1.0" diff --git a/crates/vm/src/arch/testing/mod.rs b/crates/vm/src/arch/testing/mod.rs index 15d1dbf9c9..0bc8ccdd5f 100644 --- a/crates/vm/src/arch/testing/mod.rs +++ b/crates/vm/src/arch/testing/mod.rs @@ -40,12 +40,9 @@ use crate::{ pub mod execution; pub mod memory; pub mod program; -// TODO[jpw]: delete or fix -pub mod test_adapter; pub use execution::ExecutionTester; pub use memory::MemoryTester; -pub use test_adapter::TestAdapterChip; pub const EXECUTION_BUS: BusIndex = 0; pub const MEMORY_BUS: BusIndex = 1; diff --git a/crates/vm/src/arch/testing/test_adapter.rs b/crates/vm/src/arch/testing/test_adapter.rs deleted file mode 100644 index f004a0a2f7..0000000000 --- a/crates/vm/src/arch/testing/test_adapter.rs +++ /dev/null @@ -1,103 +0,0 @@ -use std::{ - borrow::{Borrow, BorrowMut}, - collections::VecDeque, - fmt::Debug, -}; - -use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::instruction::Instruction; -use openvm_stark_backend::{ - interaction::InteractionBuilder, - p3_air::BaseAir, - p3_field::{Field, FieldAlgebra, PrimeField32}, -}; -use serde::{Deserialize, Serialize}; - -use crate::{ - arch::{ - AdapterAirContext, AdapterRuntimeContext, DynAdapterInterface, DynArray, ExecutionBridge, - ExecutionState, MinimalInstruction, Result, VmAdapterAir, - }, - system::memory::MemoryController, -}; - -// Replaces A: VmAdapterChip while testing VmCoreChip functionality, as it has no -// constraints and thus cannot cause a failure. -pub struct TestAdapterChip { - /// List of the return values of `preprocess` this chip should provide on each sequential call. - pub prank_reads: VecDeque>, - /// List of `pc_inc` to use in `postprocess` on each sequential call. - /// Defaults to `4` if not provided. - pub prank_pc_inc: VecDeque>, - - pub air: TestAdapterAir, -} - -impl TestAdapterChip { - pub fn new( - prank_reads: Vec>, - prank_pc_inc: Vec>, - execution_bridge: ExecutionBridge, - ) -> Self { - Self { - prank_reads: prank_reads.into(), - prank_pc_inc: prank_pc_inc.into(), - air: TestAdapterAir { execution_bridge }, - } - } -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct TestAdapterRecord { - pub from_pc: u32, - pub operands: [T; 7], -} - -#[derive(Clone, Copy, Debug)] -pub struct TestAdapterAir { - pub execution_bridge: ExecutionBridge, -} - -#[repr(C)] -#[derive(AlignedBorrow)] -pub struct TestAdapterCols { - pub from_pc: T, - pub operands: [T; 7], -} - -impl BaseAir for TestAdapterAir { - fn width(&self) -> usize { - TestAdapterCols::::width() - } -} - -impl VmAdapterAir for TestAdapterAir { - type Interface = DynAdapterInterface; - - fn eval( - &self, - builder: &mut AB, - local: &[AB::Var], - ctx: AdapterAirContext, - ) { - let processed_instruction: MinimalInstruction = ctx.instruction.into(); - let cols: &TestAdapterCols = local.borrow(); - self.execution_bridge - .execute_and_increment_or_set_pc( - processed_instruction.opcode, - cols.operands.to_vec(), - ExecutionState { - pc: cols.from_pc.into(), - timestamp: AB::Expr::ONE, - }, - AB::Expr::ZERO, - (4, ctx.to_pc), - ) - .eval(builder, processed_instruction.is_valid); - } - - fn get_from_pc(&self, local: &[AB::Var]) -> AB::Var { - let cols: &TestAdapterCols = local.borrow(); - cols.from_pc - } -} diff --git a/extensions/rv32im/circuit/Cargo.toml b/extensions/rv32im/circuit/Cargo.toml index 8b20385104..6e71db40db 100644 --- a/extensions/rv32im/circuit/Cargo.toml +++ b/extensions/rv32im/circuit/Cargo.toml @@ -21,6 +21,8 @@ derive-new.workspace = true derive_more = { workspace = true, features = ["from"] } rand.workspace = true eyre.workspace = true +test-case.workspace = true + # for div_rem: num-bigint.workspace = true num-integer.workspace = true diff --git a/extensions/rv32im/circuit/src/auipc/tests.rs b/extensions/rv32im/circuit/src/auipc/tests.rs index 2ac4a0d30b..42f1cd1468 100644 --- a/extensions/rv32im/circuit/src/auipc/tests.rs +++ b/extensions/rv32im/circuit/src/auipc/tests.rs @@ -1,33 +1,36 @@ use std::borrow::BorrowMut; -use openvm_circuit::arch::{testing::VmChipTestBuilder, VmAirWrapper}; +use openvm_circuit::arch::{ + testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, + VmAirWrapper, +}; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, }; use openvm_instructions::{instruction::Instruction, program::PC_BITS, LocalOpcode}; use openvm_rv32im_transpiler::Rv32AuipcOpcode::{self, *}; use openvm_stark_backend::{ - interaction::BusIndex, + p3_air::BaseAir, p3_field::{FieldAlgebra, PrimeField32}, - p3_matrix::{dense::RowMajorMatrix, Matrix}, + p3_matrix::{ + dense::{DenseMatrix, RowMajorMatrix}, + Matrix, + }, utils::disable_debug_builder, - verifier::VerificationError, - Chip, ChipUsageGetter, }; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; use super::{run_auipc, Rv32AuipcChip, Rv32AuipcCoreAir, Rv32AuipcCoreCols, Rv32AuipcStep}; -use crate::adapters::{ - Rv32RdWriteAdapterAir, Rv32RdWriteAdapterCols, Rv32RdWriteAdapterStep, RV32_CELL_BITS, - RV32_REGISTER_NUM_LIMBS, +use crate::{ + adapters::{ + Rv32RdWriteAdapterAir, Rv32RdWriteAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + }, + test_utils::get_verification_error, }; -const ADAPTER_WIDTH: usize = size_of::>(); const IMM_BITS: usize = 24; -const BITWISE_OP_LOOKUP_BUS: BusIndex = 9; const MAX_INS_CAPACITY: usize = 128; - type F = BabyBear; fn create_test_chip( @@ -69,9 +72,7 @@ fn set_and_execute( initial_pc.unwrap_or(rng.gen_range(0..(1 << PC_BITS))), ); let initial_pc = tester.execution.last_from_pc().as_canonical_u32(); - let rd_data = run_auipc(opcode, initial_pc, imm as u32); - assert_eq!(rd_data.map(F::from_canonical_u8), tester.read::<4>(1, a)); } @@ -85,11 +86,10 @@ fn set_and_execute( #[test] fn rand_auipc_test() { let mut rng = create_seeded_rng(); - let mut tester = VmChipTestBuilder::default(); let (mut chip, bitwise_chip) = create_test_chip(&tester); - let num_tests: usize = 1; + let num_tests: usize = 100; for _ in 0..num_tests { set_and_execute(&mut tester, &mut chip, &mut rng, AUIPC, None, None); } @@ -102,18 +102,22 @@ fn rand_auipc_test() { // NEGATIVE TESTS // // Given a fake trace of a single operation, setup a chip and run the test. We replace -// the write part of the trace and check that the core chip throws the expected error. -// A dummy adaptor is used so memory interactions don't indirectly cause false passes. +// part of the trace and check that the chip throws the expected error. ////////////////////////////////////////////////////////////////////////////////////// +#[derive(Clone, Copy, Default, PartialEq)] +struct AuipcPrankValues { + pub rd_data: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, + pub imm_limbs: Option<[u32; RV32_REGISTER_NUM_LIMBS - 1]>, + pub pc_limbs: Option<[u32; RV32_REGISTER_NUM_LIMBS - 2]>, +} + fn run_negative_auipc_test( opcode: Rv32AuipcOpcode, initial_imm: Option, initial_pc: Option, - rd_data: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, - imm_limbs: Option<[u32; RV32_REGISTER_NUM_LIMBS - 1]>, - pc_limbs: Option<[u32; RV32_REGISTER_NUM_LIMBS - 2]>, - expected_error: VerificationError, + prank_vals: AuipcPrankValues, + interaction_error: bool, ) { let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); @@ -128,39 +132,32 @@ fn run_negative_auipc_test( initial_pc, ); - let tester = tester.build(); - - let auipc_trace_width = chip.trace_width(); - let air = chip.air(); - let mut chip_input = chip.generate_air_proof_input(); - let auipc_trace = chip_input.raw.common_main.as_mut().unwrap(); - { - let mut trace_row = auipc_trace.row_slice(0).to_vec(); - - let (_, core_row) = trace_row.split_at_mut(ADAPTER_WIDTH); - + let adapter_width = BaseAir::::width(&chip.air.adapter); + let modify_trace = |trace: &mut DenseMatrix| { + let mut trace_row = trace.row_slice(0).to_vec(); + let (_, core_row) = trace_row.split_at_mut(adapter_width); let core_cols: &mut Rv32AuipcCoreCols = core_row.borrow_mut(); - if let Some(data) = rd_data { + if let Some(data) = prank_vals.rd_data { core_cols.rd_data = data.map(F::from_canonical_u32); } - - if let Some(data) = imm_limbs { + if let Some(data) = prank_vals.imm_limbs { core_cols.imm_limbs = data.map(F::from_canonical_u32); } - - if let Some(data) = pc_limbs { + if let Some(data) = prank_vals.pc_limbs { core_cols.pc_limbs = data.map(F::from_canonical_u32); } - *auipc_trace = RowMajorMatrix::new(trace_row, auipc_trace_width); - } + *trace = RowMajorMatrix::new(trace_row, trace.width()); + }; + disable_debug_builder(); let tester = tester - .load_air_proof_input((air, chip_input)) + .build() + .load_and_prank_trace(chip, modify_trace) .load(bitwise_chip) .finalize(); - tester.simple_test_with_expected_error(expected_error); + tester.simple_test_with_expected_error(get_verification_error(interaction_error)); } #[test] @@ -169,47 +166,53 @@ fn invalid_limb_negative_tests() { AUIPC, Some(9722891), None, - None, - Some([107, 46, 81]), - None, - VerificationError::OodEvaluationMismatch, + AuipcPrankValues { + imm_limbs: Some([107, 46, 81]), + ..Default::default() + }, + false, ); run_negative_auipc_test( AUIPC, Some(0), Some(2110400), - Some([194, 51, 32, 240]), - None, - Some([51, 32]), - VerificationError::ChallengePhaseError, + AuipcPrankValues { + rd_data: Some([194, 51, 32, 240]), + pc_limbs: Some([51, 32]), + ..Default::default() + }, + true, ); run_negative_auipc_test( AUIPC, None, None, - None, - None, - Some([206, 166]), - VerificationError::OodEvaluationMismatch, + AuipcPrankValues { + pc_limbs: Some([206, 166]), + ..Default::default() + }, + false, ); run_negative_auipc_test( AUIPC, None, None, - Some([30, 92, 82, 132]), - None, - None, - VerificationError::OodEvaluationMismatch, + AuipcPrankValues { + rd_data: Some([30, 92, 82, 132]), + ..Default::default() + }, + false, ); - run_negative_auipc_test( AUIPC, None, Some(876487877), - Some([197, 202, 49, 70]), - Some([166, 243, 17]), - Some([36, 62]), - VerificationError::ChallengePhaseError, + AuipcPrankValues { + rd_data: Some([197, 202, 49, 70]), + imm_limbs: Some([166, 243, 17]), + pc_limbs: Some([36, 62]), + }, + true, ); } @@ -219,37 +222,42 @@ fn overflow_negative_tests() { AUIPC, Some(256264), None, - None, - Some([3592, 219, 3]), - None, - VerificationError::OodEvaluationMismatch, + AuipcPrankValues { + imm_limbs: Some([3592, 219, 3]), + ..Default::default() + }, + false, ); run_negative_auipc_test( AUIPC, None, None, - None, - None, - Some([0, 0]), - VerificationError::OodEvaluationMismatch, + AuipcPrankValues { + pc_limbs: Some([0, 0]), + ..Default::default() + }, + false, ); run_negative_auipc_test( AUIPC, Some(255), None, - None, - Some([F::NEG_ONE.as_canonical_u32(), 1, 0]), - None, - VerificationError::ChallengePhaseError, + AuipcPrankValues { + imm_limbs: Some([F::NEG_ONE.as_canonical_u32(), 1, 0]), + ..Default::default() + }, + true, ); run_negative_auipc_test( AUIPC, Some(0), Some(255), - Some([F::NEG_ONE.as_canonical_u32(), 1, 0, 0]), - Some([0, 0, 0]), - Some([1, 0]), - VerificationError::ChallengePhaseError, + AuipcPrankValues { + rd_data: Some([F::NEG_ONE.as_canonical_u32(), 1, 0, 0]), + imm_limbs: Some([0, 0, 0]), + pc_limbs: Some([1, 0]), + }, + true, ); } @@ -259,18 +267,6 @@ fn overflow_negative_tests() { /// Ensure that solve functions produce the correct results. /////////////////////////////////////////////////////////////////////////////////////// -#[test] -fn execute_roundtrip_sanity_test() { - let mut rng = create_seeded_rng(); - let mut tester = VmChipTestBuilder::default(); - let (mut chip, _) = create_test_chip(&tester); - - let num_tests: usize = 100; - for _ in 0..num_tests { - set_and_execute(&mut tester, &mut chip, &mut rng, AUIPC, None, None); - } -} - #[test] fn run_auipc_sanity_test() { let opcode = AUIPC; diff --git a/extensions/rv32im/circuit/src/base_alu/tests.rs b/extensions/rv32im/circuit/src/base_alu/tests.rs index 521fca0c9e..3d9456fa26 100644 --- a/extensions/rv32im/circuit/src/base_alu/tests.rs +++ b/extensions/rv32im/circuit/src/base_alu/tests.rs @@ -1,27 +1,14 @@ -use std::borrow::BorrowMut; - -use openvm_circuit::{ - arch::{ - testing::{ - test_adapter::TestAdapterAir, TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS, - }, - AdapterExecutorE1, AdapterTraceStep, NewVmChipWrapper, VmAirWrapper, - }, - system::memory::{ - online::{GuestMemory, TracingMemory}, - MemoryAuxColsFactory, - }, - utils::generate_long_number, +use std::{array, borrow::BorrowMut}; + +use openvm_circuit::arch::{ + testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, + VmAirWrapper, }; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, }; -use openvm_instructions::{ - instruction::Instruction, - riscv::{RV32_IMM_AS, RV32_REGISTER_AS}, - LocalOpcode, -}; -use openvm_rv32im_transpiler::BaseAluOpcode; +use openvm_instructions::LocalOpcode; +use openvm_rv32im_transpiler::BaseAluOpcode::{self, *}; use openvm_stark_backend::{ p3_air::BaseAir, p3_field::{FieldAlgebra, PrimeField32}, @@ -30,24 +17,23 @@ use openvm_stark_backend::{ Matrix, }, utils::disable_debug_builder, - verifier::VerificationError, - ChipUsageGetter, }; -use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; -use rand::Rng; +use openvm_stark_sdk::{config::setup_tracing, p3_baby_bear::BabyBear, utils::create_seeded_rng}; +use rand::{rngs::StdRng, Rng}; +use test_case::test_case; -use super::{core::run_alu, BaseAluCoreAir, BaseAluStep, Rv32BaseAluChip, Rv32BaseAluStep}; +use super::{core::run_alu, BaseAluCoreAir, Rv32BaseAluChip, Rv32BaseAluStep}; use crate::{ adapters::{ - tracing_read, tracing_read_imm, Rv32BaseAluAdapterAir, Rv32BaseAluAdapterCols, - Rv32BaseAluAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + Rv32BaseAluAdapterAir, Rv32BaseAluAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, }, base_alu::BaseAluCoreCols, - test_utils::{generate_rv32_is_type_immediate, rv32_rand_write_register_or_imm}, + test_utils::{ + generate_rv32_is_type_immediate, get_verification_error, rv32_rand_write_register_or_imm, + }, }; const MAX_INS_CAPACITY: usize = 128; - type F = BabyBear; fn create_test_chip( @@ -79,6 +65,46 @@ fn create_test_chip( (chip, bitwise_chip) } + +fn set_and_execute( + tester: &mut VmChipTestBuilder, + chip: &mut Rv32BaseAluChip, + rng: &mut StdRng, + opcode: BaseAluOpcode, + b: Option<[u8; RV32_REGISTER_NUM_LIMBS]>, + is_imm: Option, + c: Option<[u8; RV32_REGISTER_NUM_LIMBS]>, +) { + let b = b.unwrap_or(array::from_fn(|_| rng.gen_range(0..=u8::MAX))); + let (c_imm, c) = if is_imm.unwrap_or(rng.gen_bool(0.5)) { + let (imm, c) = if let Some(c) = c { + ((u32::from_le_bytes(c) & 0xFFFFFF) as usize, c) + } else { + generate_rv32_is_type_immediate(rng) + }; + (Some(imm), c) + } else { + ( + None, + c.unwrap_or(array::from_fn(|_| rng.gen_range(0..=u8::MAX))), + ) + }; + + let (instruction, rd) = rv32_rand_write_register_or_imm( + tester, + b, + c, + c_imm, + opcode.global_opcode().as_usize(), + rng, + ); + tester.execute(chip, &instruction); + + let a = run_alu::(opcode, &b, &c) + .map(F::from_canonical_u8); + assert_eq!(a, tester.read::(1, rd)) +} + ////////////////////////////////////////////////////////////////////////////////////// // POSITIVE TESTS // @@ -86,220 +112,211 @@ fn create_test_chip( // passes all constraints. ////////////////////////////////////////////////////////////////////////////////////// -fn run_rv32_alu_rand_test(opcode: BaseAluOpcode, num_ops: usize) { +#[test_case(ADD, 100)] +#[test_case(SUB, 100)] +#[test_case(XOR, 100)] +#[test_case(OR, 100)] +#[test_case(AND, 100)] +fn rand_rv32_alu_test(opcode: BaseAluOpcode, num_ops: usize) { + setup_tracing(); let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); let (mut chip, bitwise_chip) = create_test_chip(&tester); for _ in 0..num_ops { - let b = generate_long_number::(&mut rng) - .map(|x| x as u8); - let (c_imm, c) = if rng.gen_bool(0.5) { - ( - None, - generate_long_number::(&mut rng) - .map(|x| x as u8), - ) - } else { - let (imm, c) = generate_rv32_is_type_immediate(&mut rng); - (Some(imm), c) - }; - - let (instruction, rd) = rv32_rand_write_register_or_imm( - &mut tester, - b, - c, - c_imm, - opcode.global_opcode().as_usize(), - &mut rng, - ); - tester.execute(&mut chip, &instruction); - - let a = run_alu::(opcode, &b, &c) - .map(F::from_canonical_u8); - assert_eq!(a, tester.read::(1, rd)) + set_and_execute(&mut tester, &mut chip, &mut rng, opcode, None, None, None); } let tester = tester.build().load(chip).load(bitwise_chip).finalize(); tester.simple_test().expect("Verification failed"); } +////////////////////////////////////////////////////////////////////////////////////// +// NEGATIVE TESTS +// +// Given a fake trace of a single operation, setup a chip and run the test. We replace +// part of the trace and check that the chip throws the expected error. +////////////////////////////////////////////////////////////////////////////////////// + +#[allow(clippy::too_many_arguments)] +fn run_negative_alu_test( + opcode: BaseAluOpcode, + prank_a: [u32; RV32_REGISTER_NUM_LIMBS], + b: [u8; RV32_REGISTER_NUM_LIMBS], + c: [u8; RV32_REGISTER_NUM_LIMBS], + prank_c: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, + prank_opcode_flags: Option<[bool; 5]>, + is_imm: Option, + interaction_error: bool, +) { + let mut rng = create_seeded_rng(); + let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); + let (mut chip, bitwise_chip) = create_test_chip(&tester); + + set_and_execute( + &mut tester, + &mut chip, + &mut rng, + opcode, + Some(b), + is_imm, + Some(c), + ); + + let adapter_width = BaseAir::::width(&chip.air.adapter); + let modify_trace = |trace: &mut DenseMatrix| { + let mut values = trace.row_slice(0).to_vec(); + let cols: &mut BaseAluCoreCols = + values.split_at_mut(adapter_width).1.borrow_mut(); + cols.a = prank_a.map(F::from_canonical_u32); + if let Some(prank_c) = prank_c { + cols.c = prank_c.map(F::from_canonical_u32); + } + if let Some(prank_opcode_flags) = prank_opcode_flags { + cols.opcode_add_flag = F::from_bool(prank_opcode_flags[0]); + cols.opcode_and_flag = F::from_bool(prank_opcode_flags[1]); + cols.opcode_or_flag = F::from_bool(prank_opcode_flags[2]); + cols.opcode_sub_flag = F::from_bool(prank_opcode_flags[3]); + cols.opcode_xor_flag = F::from_bool(prank_opcode_flags[4]); + } + *trace = RowMajorMatrix::new(values, trace.width()); + }; + + disable_debug_builder(); + let tester = tester + .build() + .load_and_prank_trace(chip, modify_trace) + .load(bitwise_chip) + .finalize(); + tester.simple_test_with_expected_error(get_verification_error(interaction_error)); +} + #[test] -fn rv32_alu_add_rand_test() { - run_rv32_alu_rand_test(BaseAluOpcode::ADD, 100); +fn rv32_alu_add_wrong_negative_test() { + run_negative_alu_test( + ADD, + [246, 0, 0, 0], + [250, 0, 0, 0], + [250, 0, 0, 0], + None, + None, + None, + false, + ); } #[test] -fn rv32_alu_sub_rand_test() { - run_rv32_alu_rand_test(BaseAluOpcode::SUB, 100); +fn rv32_alu_add_out_of_range_negative_test() { + run_negative_alu_test( + ADD, + [500, 0, 0, 0], + [250, 0, 0, 0], + [250, 0, 0, 0], + None, + None, + None, + true, + ); +} + +#[test] +fn rv32_alu_sub_wrong_negative_test() { + run_negative_alu_test( + SUB, + [255, 0, 0, 0], + [1, 0, 0, 0], + [2, 0, 0, 0], + None, + None, + None, + false, + ); } #[test] -fn rv32_alu_xor_rand_test() { - run_rv32_alu_rand_test(BaseAluOpcode::XOR, 100); +fn rv32_alu_sub_out_of_range_negative_test() { + run_negative_alu_test( + SUB, + [F::NEG_ONE.as_canonical_u32(), 0, 0, 0], + [1, 0, 0, 0], + [2, 0, 0, 0], + None, + None, + None, + true, + ); } #[test] -fn rv32_alu_or_rand_test() { - run_rv32_alu_rand_test(BaseAluOpcode::OR, 100); +fn rv32_alu_xor_wrong_negative_test() { + run_negative_alu_test( + XOR, + [255, 255, 255, 255], + [0, 0, 1, 0], + [255, 255, 255, 255], + None, + None, + None, + true, + ); } #[test] -fn rv32_alu_and_rand_test() { - run_rv32_alu_rand_test(BaseAluOpcode::AND, 100); +fn rv32_alu_or_wrong_negative_test() { + run_negative_alu_test( + OR, + [255, 255, 255, 255], + [255, 255, 255, 254], + [0, 0, 0, 0], + None, + None, + None, + true, + ); } -////////////////////////////////////////////////////////////////////////////////////// -// NEGATIVE TESTS -// -// Given a fake trace of a single operation, setup a chip and run the test. We replace -// the write part of the trace and check that the core chip throws the expected error. -// A dummy adapter is used so memory interactions don't indirectly cause false passes. -////////////////////////////////////////////////////////////////////////////////////// +#[test] +fn rv32_alu_and_wrong_negative_test() { + run_negative_alu_test( + AND, + [255, 255, 255, 255], + [0, 0, 1, 0], + [0, 0, 0, 0], + None, + None, + None, + true, + ); +} -// type Rv32BaseAluTestChip = NewVmChipWrapper< -// F, -// VmAirWrapper>, -// BaseAluStep, -// >; - -// // TODO: FIX NEGATIVE TESTS - -// #[allow(clippy::too_many_arguments)] -// fn run_rv32_alu_negative_test( -// opcode: BaseAluOpcode, -// a: [u32; RV32_REGISTER_NUM_LIMBS], -// b: [u32; RV32_REGISTER_NUM_LIMBS], -// c: [u32; RV32_REGISTER_NUM_LIMBS], -// interaction_error: bool, -// ) { -// let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); -// let (mut chip, bitwise_chip) = create_test_chip(&tester); -// let mut chip = Rv32BaseAluTestChip::::new( -// TestAdapterChip::new( -// vec![[b.map(F::from_canonical_u32), c.map(F::from_canonical_u32)].concat()], -// vec![None], -// ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), -// ), -// BaseAluStep::new(bitwise_chip.clone(), BaseAluOpcode::CLASS_OFFSET), -// tester.offline_memory_mutex_arc(), -// ); - -// tester.execute( -// &mut chip, -// &Instruction::from_usize(opcode.global_opcode(), [0, 0, 0, 1, 1]), -// ); - -// let trace_width = chip.trace_width(); -// let adapter_width = Rv32BaseAluAdapterCols::::width(); - -// if (opcode == BaseAluOpcode::ADD || opcode == BaseAluOpcode::SUB) -// && a.iter().all(|&a_val| a_val < (1 << RV32_CELL_BITS)) -// { -// bitwise_chip.clear(); -// for a_val in a { -// bitwise_chip.request_xor(a_val, a_val); -// } -// } - -// let modify_trace = |trace: &mut DenseMatrix| { -// let mut values = trace.row_slice(0).to_vec(); -// let cols: &mut BaseAluCoreCols = -// values.split_at_mut(adapter_width).1.borrow_mut(); -// cols.a = a.map(F::from_canonical_u32); -// *trace = RowMajorMatrix::new(values, trace_width); -// }; - -// disable_debug_builder(); -// let tester = tester -// .build() -// .load_and_prank_trace(chip, modify_trace) -// .load(bitwise_chip) -// .finalize(); -// tester.simple_test_with_expected_error(if interaction_error { -// VerificationError::ChallengePhaseError -// } else { -// VerificationError::OodEvaluationMismatch -// }); -// } - -// #[test] -// fn rv32_alu_add_wrong_negative_test() { -// run_rv32_alu_negative_test( -// BaseAluOpcode::ADD, -// [246, 0, 0, 0], -// [250, 0, 0, 0], -// [250, 0, 0, 0], -// false, -// ); -// } - -// #[test] -// fn rv32_alu_add_out_of_range_negative_test() { -// run_rv32_alu_negative_test( -// BaseAluOpcode::ADD, -// [500, 0, 0, 0], -// [250, 0, 0, 0], -// [250, 0, 0, 0], -// true, -// ); -// } - -// #[test] -// fn rv32_alu_sub_wrong_negative_test() { -// run_rv32_alu_negative_test( -// BaseAluOpcode::SUB, -// [255, 0, 0, 0], -// [1, 0, 0, 0], -// [2, 0, 0, 0], -// false, -// ); -// } - -// #[test] -// fn rv32_alu_sub_out_of_range_negative_test() { -// run_rv32_alu_negative_test( -// BaseAluOpcode::SUB, -// [F::NEG_ONE.as_canonical_u32(), 0, 0, 0], -// [1, 0, 0, 0], -// [2, 0, 0, 0], -// true, -// ); -// } - -// #[test] -// fn rv32_alu_xor_wrong_negative_test() { -// run_rv32_alu_negative_test( -// BaseAluOpcode::XOR, -// [255, 255, 255, 255], -// [0, 0, 1, 0], -// [255, 255, 255, 255], -// true, -// ); -// } - -// #[test] -// fn rv32_alu_or_wrong_negative_test() { -// run_rv32_alu_negative_test( -// BaseAluOpcode::OR, -// [255, 255, 255, 255], -// [255, 255, 255, 254], -// [0, 0, 0, 0], -// true, -// ); -// } - -// #[test] -// fn rv32_alu_and_wrong_negative_test() { -// run_rv32_alu_negative_test( -// BaseAluOpcode::AND, -// [255, 255, 255, 255], -// [0, 0, 1, 0], -// [0, 0, 0, 0], -// true, -// ); -// } +#[test] +fn rv32_alu_adapter_unconstrained_imm_limb_test() { + run_negative_alu_test( + ADD, + [255, 7, 0, 0], + [0, 0, 0, 0], + [255, 7, 0, 0], + Some([511, 6, 0, 0]), + None, + Some(true), + true, + ); +} + +#[test] +fn rv32_alu_adapter_unconstrained_rs2_read_test() { + run_negative_alu_test( + ADD, + [2, 2, 2, 2], + [1, 1, 1, 1], + [1, 1, 1, 1], + None, + Some([false, false, false, false, false]), + Some(false), + false, + ); +} /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS @@ -312,7 +329,7 @@ fn run_add_sanity_test() { let x: [u8; RV32_REGISTER_NUM_LIMBS] = [229, 33, 29, 111]; let y: [u8; RV32_REGISTER_NUM_LIMBS] = [50, 171, 44, 194]; let z: [u8; RV32_REGISTER_NUM_LIMBS] = [23, 205, 73, 49]; - let result = run_alu::(BaseAluOpcode::ADD, &x, &y); + let result = run_alu::(ADD, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], result[i]) } @@ -323,7 +340,7 @@ fn run_sub_sanity_test() { let x: [u8; RV32_REGISTER_NUM_LIMBS] = [229, 33, 29, 111]; let y: [u8; RV32_REGISTER_NUM_LIMBS] = [50, 171, 44, 194]; let z: [u8; RV32_REGISTER_NUM_LIMBS] = [179, 118, 240, 172]; - let result = run_alu::(BaseAluOpcode::SUB, &x, &y); + let result = run_alu::(SUB, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], result[i]) } @@ -334,7 +351,7 @@ fn run_xor_sanity_test() { let x: [u8; RV32_REGISTER_NUM_LIMBS] = [229, 33, 29, 111]; let y: [u8; RV32_REGISTER_NUM_LIMBS] = [50, 171, 44, 194]; let z: [u8; RV32_REGISTER_NUM_LIMBS] = [215, 138, 49, 173]; - let result = run_alu::(BaseAluOpcode::XOR, &x, &y); + let result = run_alu::(XOR, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], result[i]) } @@ -345,7 +362,7 @@ fn run_or_sanity_test() { let x: [u8; RV32_REGISTER_NUM_LIMBS] = [229, 33, 29, 111]; let y: [u8; RV32_REGISTER_NUM_LIMBS] = [50, 171, 44, 194]; let z: [u8; RV32_REGISTER_NUM_LIMBS] = [247, 171, 61, 239]; - let result = run_alu::(BaseAluOpcode::OR, &x, &y); + let result = run_alu::(OR, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], result[i]) } @@ -356,301 +373,8 @@ fn run_and_sanity_test() { let x: [u8; RV32_REGISTER_NUM_LIMBS] = [229, 33, 29, 111]; let y: [u8; RV32_REGISTER_NUM_LIMBS] = [50, 171, 44, 194]; let z: [u8; RV32_REGISTER_NUM_LIMBS] = [32, 33, 12, 66]; - let result = run_alu::(BaseAluOpcode::AND, &x, &y); + let result = run_alu::(AND, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], result[i]) } } - -////////////////////////////////////////////////////////////////////////////////////// -// ADAPTER TESTS -// -// Ensure that the adapter is correct. -////////////////////////////////////////////////////////////////////////////////////// - -// A pranking chip where `preprocess` can have `rs2` limbs that overflow. -#[derive(derive_new::new)] -struct Rv32BaseAluAdapterTestStep(Rv32BaseAluAdapterStep); - -impl AdapterTraceStep for Rv32BaseAluAdapterTestStep -where - F: PrimeField32, -{ - const WIDTH: usize = - as AdapterTraceStep>::WIDTH; - type ReadData = as AdapterTraceStep>::ReadData; - type WriteData = - as AdapterTraceStep>::WriteData; - type TraceContext<'a> = - as AdapterTraceStep>::TraceContext<'a>; - - #[inline(always)] - fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { - as AdapterTraceStep>::start( - pc, - memory, - adapter_row, - ); - } - - #[inline(always)] - fn read( - &self, - memory: &mut TracingMemory, - instruction: &Instruction, - adapter_row: &mut [F], - ) -> Self::ReadData { - let &Instruction { b, c, d, e, .. } = instruction; - - debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - debug_assert!( - e.as_canonical_u32() == RV32_REGISTER_AS || e.as_canonical_u32() == RV32_IMM_AS - ); - - let adapter_row: &mut Rv32BaseAluAdapterCols = adapter_row.borrow_mut(); - - adapter_row.rs1_ptr = b; - let rs1 = tracing_read( - memory, - d.as_canonical_u32(), - b.as_canonical_u32(), - &mut adapter_row.reads_aux[0], - ); - - let rs2 = if e.as_canonical_u32() == RV32_REGISTER_AS { - adapter_row.rs2_as = e; - adapter_row.rs2 = c; - - tracing_read( - memory, - e.as_canonical_u32(), - c.as_canonical_u32(), - &mut adapter_row.reads_aux[1], - ) - } else { - adapter_row.rs2_as = e; - - // Here we use values that can overflow - let c_u32 = c.as_canonical_u32(); - let mask1 = (1 << 9) - 1; // Allowing overflow - let mask2 = (1 << 3) - 2; // Allowing overflow - - let rs2 = [ - (c_u32 & mask1) as u8, - ((c_u32 >> 8) & mask2) as u8, - (c_u32 >> 16) as u8, - (c_u32 >> 16) as u8, - ]; - - tracing_read_imm(memory, c.as_canonical_u32(), &mut adapter_row.rs2); - rs2 - }; - - (rs1, rs2) - } - - #[inline(always)] - fn write( - &self, - memory: &mut TracingMemory, - instruction: &Instruction, - adapter_row: &mut [F], - data: &Self::WriteData, - ) { - as AdapterTraceStep>::write( - &self.0, - memory, - instruction, - adapter_row, - data, - ); - } - - #[inline(always)] - fn fill_trace_row( - &self, - mem_helper: &MemoryAuxColsFactory, - bitwise_lookup_chip: Self::TraceContext<'_>, - adapter_row: &mut [F], - ) { - as AdapterTraceStep>::fill_trace_row( - &self.0, - mem_helper, - bitwise_lookup_chip, - adapter_row, - ); - } -} - -impl AdapterExecutorE1 for Rv32BaseAluAdapterTestStep -where - F: PrimeField32, -{ - type ReadData = as AdapterExecutorE1>::ReadData; - type WriteData = as AdapterExecutorE1>::WriteData; - - #[inline(always)] - fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData - where - Mem: GuestMemory, - { - let &Instruction { b, c, d, e, .. } = instruction; - - debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - debug_assert!( - e.as_canonical_u32() == RV32_REGISTER_AS || e.as_canonical_u32() == RV32_IMM_AS - ); - - let rs1: [u8; RV32_REGISTER_NUM_LIMBS] = - unsafe { memory.read(d.as_canonical_u32(), b.as_canonical_u32()) }; - - let rs2 = if e.as_canonical_u32() == RV32_REGISTER_AS { - let rs2: [u8; RV32_REGISTER_NUM_LIMBS] = - unsafe { memory.read(e.as_canonical_u32(), c.as_canonical_u32()) }; - rs2 - } else { - // Here we use values that can overflow - let imm = c.as_canonical_u32(); - - debug_assert_eq!(imm >> 24, 0); - - let mask1 = (1 << 9) - 1; // Allowing overflow - let mask2 = (1 << 3) - 2; // Allowing overflow - - let mut imm_le = [ - (imm & mask1) as u8, - ((imm >> 8) & mask2) as u8, - (imm >> 16) as u8, - (imm >> 16) as u8, - ]; - imm_le[3] = imm_le[2]; - imm_le - }; - - (rs1, rs2) - } - - #[inline(always)] - fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) - where - Mem: GuestMemory, - { - as AdapterExecutorE1>::write( - &self.0, - memory, - instruction, - data, - ); - } -} - -// #[test] -// fn rv32_alu_adapter_unconstrained_imm_limb_test() { -// let mut rng = create_seeded_rng(); -// let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); -// let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - -// let mut tester = VmChipTestBuilder::default(); - -// let mut chip = NewVmChipWrapper::::new( -// VmAirWrapper::new( -// Rv32BaseAluAdapterAir::new( -// tester.execution_bridge(), -// tester.memory_bridge(), -// bitwise_bus, -// ), -// BaseAluCoreAir::::new( -// bitwise_bus, -// BaseAluOpcode::CLASS_OFFSET, -// ), -// ), -// BaseAluStep::new( -// Rv32BaseAluAdapterTestStep(Rv32BaseAluAdapterStep::new()), -// bitwise_chip.clone(), -// BaseAluOpcode::CLASS_OFFSET, -// ), -// MAX_INS_CAPACITY, -// tester.memory_helper(), -// ); - -// let b = [0, 0, 0, 0]; -// let (c_imm, c) = { -// let imm = (1 << 11) - 1; -// let fake_c: [u32; 4] = [(1 << 9) - 1, (1 << 3) - 2, 0, 0]; -// let fake_c = fake_c.map(|x| x as u8); -// (Some(imm), fake_c) -// }; - -// let (instruction, _rd) = rv32_rand_write_register_or_imm( -// &mut tester, -// b, -// c, -// c_imm, -// BaseAluOpcode::ADD.global_opcode().as_usize(), -// &mut rng, -// ); -// tester.execute(&mut chip, &instruction); - -// disable_debug_builder(); -// let tester = tester.build().load(chip).load(bitwise_chip).finalize(); -// tester.simple_test_with_expected_error(VerificationError::ChallengePhaseError); -// } - -#[test] -fn rv32_alu_adapter_unconstrained_rs2_read_test() { - let mut rng = create_seeded_rng(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - - let mut tester = VmChipTestBuilder::default(); - let mut chip = Rv32BaseAluChip::new( - VmAirWrapper::new( - Rv32BaseAluAdapterAir::new( - tester.execution_bridge(), - tester.memory_bridge(), - bitwise_bus, - ), - BaseAluCoreAir::new(bitwise_bus, BaseAluOpcode::CLASS_OFFSET), - ), - Rv32BaseAluStep::new( - Rv32BaseAluAdapterStep::new(), - bitwise_chip.clone(), - BaseAluOpcode::CLASS_OFFSET, - ), - MAX_INS_CAPACITY, - tester.memory_helper(), - ); - - let b = [1, 1, 1, 1]; - let c = [1, 1, 1, 1]; - let (instruction, _rd) = rv32_rand_write_register_or_imm( - &mut tester, - b, - c, - None, - BaseAluOpcode::ADD.global_opcode().as_usize(), - &mut rng, - ); - tester.execute(&mut chip, &instruction); - - let trace_width = chip.trace_width(); - let adapter_width = BaseAir::::width(&chip.air.adapter); - - let modify_trace = |trace: &mut DenseMatrix| { - let mut values = trace.row_slice(0).to_vec(); - let mut dummy_values = values.clone(); - let cols: &mut BaseAluCoreCols = - dummy_values.split_at_mut(adapter_width).1.borrow_mut(); - cols.opcode_add_flag = F::ZERO; - values.extend(dummy_values); - *trace = RowMajorMatrix::new(values, trace_width); - }; - - disable_debug_builder(); - let tester = tester - .build() - .load_and_prank_trace(chip, modify_trace) - .load(bitwise_chip) - .finalize(); - tester.simple_test_with_expected_error(VerificationError::OodEvaluationMismatch); -} diff --git a/extensions/rv32im/circuit/src/branch_eq/tests.rs b/extensions/rv32im/circuit/src/branch_eq/tests.rs index df1489e073..33d4566315 100644 --- a/extensions/rv32im/circuit/src/branch_eq/tests.rs +++ b/extensions/rv32im/circuit/src/branch_eq/tests.rs @@ -1,9 +1,8 @@ use std::{array, borrow::BorrowMut}; use openvm_circuit::arch::{ - testing::{memory::gen_pointer, TestAdapterChip, VmChipTestBuilder}, - BasicAdapterInterface, ExecutionBridge, ImmInstruction, InstructionExecutor, VmAirWrapper, - VmCoreChip, + testing::{memory::gen_pointer, VmChipTestBuilder}, + InstructionExecutor, VmAirWrapper, }; use openvm_instructions::{ instruction::Instruction, @@ -19,11 +18,10 @@ use openvm_stark_backend::{ Matrix, }, utils::disable_debug_builder, - verifier::VerificationError, - ChipUsageGetter, }; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; +use test_case::test_case; use super::{ core::{run_eq, BranchEqualStep}, @@ -33,29 +31,50 @@ use crate::{ adapters::{ Rv32BranchAdapterAir, Rv32BranchAdapterStep, RV32_REGISTER_NUM_LIMBS, RV_B_TYPE_IMM_BITS, }, + test_utils::get_verification_error, BranchEqualCoreAir, }; type F = BabyBear; const MAX_INS_CAPACITY: usize = 128; +const ABS_MAX_IMM: i32 = 1 << (RV_B_TYPE_IMM_BITS - 1); -////////////////////////////////////////////////////////////////////////////////////// -// POSITIVE TESTS -// -// Randomly generate computations and execute, ensuring that the generated trace -// passes all constraints. -////////////////////////////////////////////////////////////////////////////////////// +fn create_test_chip(tester: &mut VmChipTestBuilder) -> Rv32BranchEqualChip { + let chip = Rv32BranchEqualChip::::new( + VmAirWrapper::new( + Rv32BranchAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), + BranchEqualCoreAir::new(BranchEqualOpcode::CLASS_OFFSET, DEFAULT_PC_STEP), + ), + BranchEqualStep::new( + Rv32BranchAdapterStep::new(), + BranchEqualOpcode::CLASS_OFFSET, + DEFAULT_PC_STEP, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), + ); + + chip +} #[allow(clippy::too_many_arguments)] -fn run_rv32_branch_eq_rand_execute>( +fn set_and_execute>( tester: &mut VmChipTestBuilder, chip: &mut E, - opcode: BranchEqualOpcode, - a: [u8; RV32_REGISTER_NUM_LIMBS], - b: [u8; RV32_REGISTER_NUM_LIMBS], - imm: i32, rng: &mut StdRng, + opcode: BranchEqualOpcode, + a: Option<[u8; RV32_REGISTER_NUM_LIMBS]>, + b: Option<[u8; RV32_REGISTER_NUM_LIMBS]>, + imm: Option, ) { + let a = a.unwrap_or(array::from_fn(|_| rng.gen_range(0..=u8::MAX))); + let b = b.unwrap_or(if rng.gen_bool(0.5) { + a + } else { + array::from_fn(|_| rng.gen_range(0..=u8::MAX)) + }); + + let imm = imm.unwrap_or(rng.gen_range((-ABS_MAX_IMM)..ABS_MAX_IMM)); let rs1 = gen_pointer(rng, 4); let rs2 = gen_pointer(rng, 4); tester.write::(1, rs1, a.map(F::from_canonical_u8)); @@ -83,190 +102,170 @@ fn run_rv32_branch_eq_rand_execute>( assert_eq!(to_pc, from_pc + pc_inc); } -fn run_rv32_branch_eq_rand_test(opcode: BranchEqualOpcode, num_ops: usize) { - let mut rng = create_seeded_rng(); - const ABS_MAX_BRANCH: i32 = 1 << (RV_B_TYPE_IMM_BITS - 1); +////////////////////////////////////////////////////////////////////////////////////// +// POSITIVE TESTS +// +// Randomly generate computations and execute, ensuring that the generated trace +// passes all constraints. +////////////////////////////////////////////////////////////////////////////////////// +#[test_case(BranchEqualOpcode::BEQ, 100)] +#[test_case(BranchEqualOpcode::BNE, 100)] +fn rand_rv32_branch_eq_test(opcode: BranchEqualOpcode, num_ops: usize) { + let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - - let mut chip = Rv32BranchEqualChip::::new( - VmAirWrapper::new( - Rv32BranchAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), - BranchEqualCoreAir::new(BranchEqualOpcode::CLASS_OFFSET, DEFAULT_PC_STEP), - ), - BranchEqualStep::new( - Rv32BranchAdapterStep::new(), - BranchEqualOpcode::CLASS_OFFSET, - DEFAULT_PC_STEP, - ), - MAX_INS_CAPACITY, - tester.memory_helper(), - ); + let mut chip = create_test_chip(&mut tester); for _ in 0..num_ops { - let a = array::from_fn(|_| rng.gen_range(0..u8::MAX)); - let b = if rng.gen_bool(0.5) { - a - } else { - array::from_fn(|_| rng.gen_range(0..u8::MAX)) - }; - let imm = rng.gen_range((-ABS_MAX_BRANCH)..ABS_MAX_BRANCH); - run_rv32_branch_eq_rand_execute(&mut tester, &mut chip, opcode, a, b, imm, &mut rng); + set_and_execute(&mut tester, &mut chip, &mut rng, opcode, None, None, None); } let tester = tester.build().load(chip).finalize(); tester.simple_test().expect("Verification failed"); } +////////////////////////////////////////////////////////////////////////////////////// +// NEGATIVE TESTS +// +// Given a fake trace of a single operation, setup a chip and run the test. We replace +// part of the trace and check that the chip throws the expected error. +////////////////////////////////////////////////////////////////////////////////////// + +#[allow(clippy::too_many_arguments)] +fn run_negative_branch_eq_test( + opcode: BranchEqualOpcode, + a: [u8; RV32_REGISTER_NUM_LIMBS], + b: [u8; RV32_REGISTER_NUM_LIMBS], + prank_cmp_result: Option, + prank_diff_inv_marker: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, + interaction_error: bool, +) { + let imm = 16i32; + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let mut chip = create_test_chip(&mut tester); + + set_and_execute( + &mut tester, + &mut chip, + &mut rng, + opcode, + Some(a), + Some(b), + Some(imm), + ); + + let adapter_width = BaseAir::::width(&chip.air.adapter); + let modify_trace = |trace: &mut DenseMatrix| { + let mut values = trace.row_slice(0).to_vec(); + let cols: &mut BranchEqualCoreCols = + values.split_at_mut(adapter_width).1.borrow_mut(); + if let Some(cmp_result) = prank_cmp_result { + cols.cmp_result = F::from_bool(cmp_result); + } + if let Some(diff_inv_marker) = prank_diff_inv_marker { + cols.diff_inv_marker = diff_inv_marker.map(F::from_canonical_u32); + } + *trace = RowMajorMatrix::new(values, trace.width()); + }; + + disable_debug_builder(); + let tester = tester + .build() + .load_and_prank_trace(chip, modify_trace) + .finalize(); + tester.simple_test_with_expected_error(get_verification_error(interaction_error)); +} + #[test] -fn rv32_beq_rand_test() { - run_rv32_branch_eq_rand_test(BranchEqualOpcode::BEQ, 100); +fn rv32_beq_wrong_cmp_negative_test() { + run_negative_branch_eq_test( + BranchEqualOpcode::BEQ, + [0, 0, 7, 0], + [0, 0, 0, 7], + Some(true), + None, + false, + ); + + run_negative_branch_eq_test( + BranchEqualOpcode::BEQ, + [0, 0, 7, 0], + [0, 0, 7, 0], + Some(false), + None, + false, + ); } #[test] -fn rv32_bne_rand_test() { - run_rv32_branch_eq_rand_test(BranchEqualOpcode::BNE, 100); +fn rv32_beq_zero_inv_marker_negative_test() { + run_negative_branch_eq_test( + BranchEqualOpcode::BEQ, + [0, 0, 7, 0], + [0, 0, 0, 7], + Some(true), + Some([0, 0, 0, 0]), + false, + ); } -////////////////////////////////////////////////////////////////////////////////////// -// NEGATIVE TESTS -// -// Given a fake trace of a single operation, setup a chip and run the test. We replace -// the write part of the trace and check that the core chip throws the expected error. -// A dummy adapter is used so memory interactions don't indirectly cause false passes. -////////////////////////////////////////////////////////////////////////////////////// +#[test] +fn rv32_beq_invalid_inv_marker_negative_test() { + run_negative_branch_eq_test( + BranchEqualOpcode::BEQ, + [0, 0, 7, 0], + [0, 0, 7, 0], + Some(false), + Some([0, 0, 1, 0]), + false, + ); +} + +#[test] +fn rv32_bne_wrong_cmp_negative_test() { + run_negative_branch_eq_test( + BranchEqualOpcode::BNE, + [0, 0, 7, 0], + [0, 0, 0, 7], + Some(false), + None, + false, + ); -// type Rv32BranchEqualTestChip = -// VmChipWrapper, BranchEqualStep>; - -// #[allow(clippy::too_many_arguments)] -// fn run_rv32_beq_negative_test( -// opcode: BranchEqualOpcode, -// a: [u32; RV32_REGISTER_NUM_LIMBS], -// b: [u32; RV32_REGISTER_NUM_LIMBS], -// cmp_result: bool, -// diff_inv_marker: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, -// ) { -// let imm = 16u32; -// let mut tester = VmChipTestBuilder::default(); -// let mut chip = Rv32BranchEqualTestChip::::new( -// TestAdapterChip::new( -// vec![[a.map(F::from_canonical_u32), b.map(F::from_canonical_u32)].concat()], -// vec![if cmp_result { Some(imm) } else { None }], -// ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), -// ), -// BranchEqualStep::new(BranchEqualOpcode::CLASS_OFFSET, 4), -// tester.offline_memory_mutex_arc(), -// ); - -// tester.execute( -// &mut chip, -// &Instruction::from_usize(opcode.global_opcode(), [0, 0, imm as usize, 1, 1]), -// ); - -// let trace_width = chip.trace_width(); -// let adapter_width = BaseAir::::width(chip.adapter.air()); - -// let modify_trace = |trace: &mut DenseMatrix| { -// let mut values = trace.row_slice(0).to_vec(); -// let cols: &mut BranchEqualCoreCols = -// values.split_at_mut(adapter_width).1.borrow_mut(); -// cols.cmp_result = F::from_bool(cmp_result); -// if let Some(diff_inv_marker) = diff_inv_marker { -// cols.diff_inv_marker = diff_inv_marker.map(F::from_canonical_u32); -// } -// *trace = RowMajorMatrix::new(values, trace_width); -// }; - -// disable_debug_builder(); -// let tester = tester -// .build() -// .load_and_prank_trace(chip, modify_trace) -// .finalize(); -// tester.simple_test_with_expected_error(VerificationError::OodEvaluationMismatch); -// } - -// #[test] -// fn rv32_beq_wrong_cmp_negative_test() { -// run_rv32_beq_negative_test( -// BranchEqualOpcode::BEQ, -// [0, 0, 7, 0], -// [0, 0, 0, 7], -// true, -// None, -// ); - -// run_rv32_beq_negative_test( -// BranchEqualOpcode::BEQ, -// [0, 0, 7, 0], -// [0, 0, 7, 0], -// false, -// None, -// ); -// } - -// #[test] -// fn rv32_beq_zero_inv_marker_negative_test() { -// run_rv32_beq_negative_test( -// BranchEqualOpcode::BEQ, -// [0, 0, 7, 0], -// [0, 0, 0, 7], -// true, -// Some([0, 0, 0, 0]), -// ); -// } - -// #[test] -// fn rv32_beq_invalid_inv_marker_negative_test() { -// run_rv32_beq_negative_test( -// BranchEqualOpcode::BEQ, -// [0, 0, 7, 0], -// [0, 0, 7, 0], -// false, -// Some([0, 0, 1, 0]), -// ); -// } - -// #[test] -// fn rv32_bne_wrong_cmp_negative_test() { -// run_rv32_beq_negative_test( -// BranchEqualOpcode::BNE, -// [0, 0, 7, 0], -// [0, 0, 0, 7], -// false, -// None, -// ); - -// run_rv32_beq_negative_test( -// BranchEqualOpcode::BNE, -// [0, 0, 7, 0], -// [0, 0, 7, 0], -// true, -// None, -// ); -// } - -// #[test] -// fn rv32_bne_zero_inv_marker_negative_test() { -// run_rv32_beq_negative_test( -// BranchEqualOpcode::BNE, -// [0, 0, 7, 0], -// [0, 0, 0, 7], -// false, -// Some([0, 0, 0, 0]), -// ); -// } - -// #[test] -// fn rv32_bne_invalid_inv_marker_negative_test() { -// run_rv32_beq_negative_test( -// BranchEqualOpcode::BNE, -// [0, 0, 7, 0], -// [0, 0, 7, 0], -// true, -// Some([0, 0, 1, 0]), -// ); -// } + run_negative_branch_eq_test( + BranchEqualOpcode::BNE, + [0, 0, 7, 0], + [0, 0, 7, 0], + Some(true), + None, + false, + ); +} + +#[test] +fn rv32_bne_zero_inv_marker_negative_test() { + run_negative_branch_eq_test( + BranchEqualOpcode::BNE, + [0, 0, 7, 0], + [0, 0, 0, 7], + Some(false), + Some([0, 0, 0, 0]), + false, + ); +} + +#[test] +fn rv32_bne_invalid_inv_marker_negative_test() { + run_negative_branch_eq_test( + BranchEqualOpcode::BNE, + [0, 0, 7, 0], + [0, 0, 7, 0], + Some(true), + Some([0, 0, 1, 0]), + false, + ); +} /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS @@ -274,35 +273,34 @@ fn rv32_bne_rand_test() { /// Ensure that solve functions produce the correct results. /////////////////////////////////////////////////////////////////////////////////////// -// #[test] -// fn execute_pc_increment_sanity_test() { -// let core = BranchEqualStep::::new(BranchEqualOpcode::CLASS_OFFSET, -// 4); - -// let mut instruction = Instruction:: { -// opcode: BranchEqualOpcode::BEQ.global_opcode(), -// c: F::from_canonical_u8(8), -// ..Default::default() -// }; -// let x: [F; RV32_REGISTER_NUM_LIMBS] = [19, 4, 1790, 60].map(F::from_canonical_u32); -// let y: [F; RV32_REGISTER_NUM_LIMBS] = [19, 32, 1804, 60].map(F::from_canonical_u32); - -// let result = as VmCoreChip< -// F, -// BasicAdapterInterface, 2, 0, RV32_REGISTER_NUM_LIMBS, 0>, -// >>::execute_instruction(&core, &instruction, 0, [x, y]); -// let (output, _) = result.expect("execute_instruction failed"); -// assert!(output.to_pc.is_none()); - -// instruction.opcode = BranchEqualOpcode::BNE.global_opcode(); -// let result = as VmCoreChip< -// F, -// BasicAdapterInterface, 2, 0, RV32_REGISTER_NUM_LIMBS, 0>, -// >>::execute_instruction(&core, &instruction, 0, [x, y]); -// let (output, _) = result.expect("execute_instruction failed"); -// assert!(output.to_pc.is_some()); -// assert_eq!(output.to_pc.unwrap(), 8); -// } +#[test] +fn execute_roundtrip_sanity_test() { + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let mut chip = create_test_chip(&mut tester); + + let x = [19, 4, 179, 60]; + let y = [19, 32, 180, 60]; + set_and_execute( + &mut tester, + &mut chip, + &mut rng, + BranchEqualOpcode::BEQ, + Some(x), + Some(y), + Some(8), + ); + + set_and_execute( + &mut tester, + &mut chip, + &mut rng, + BranchEqualOpcode::BNE, + Some(x), + Some(y), + Some(8), + ); +} #[test] fn run_eq_sanity_test() { diff --git a/extensions/rv32im/circuit/src/branch_lt/tests.rs b/extensions/rv32im/circuit/src/branch_lt/tests.rs index e5fcb9bd22..3c7670f641 100644 --- a/extensions/rv32im/circuit/src/branch_lt/tests.rs +++ b/extensions/rv32im/circuit/src/branch_lt/tests.rs @@ -1,12 +1,11 @@ -use std::borrow::BorrowMut; +use std::{array, borrow::BorrowMut}; use openvm_circuit::{ arch::{ - testing::{memory::gen_pointer, TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - BasicAdapterInterface, ExecutionBridge, ImmInstruction, InstructionExecutor, VmAirWrapper, - VmCoreChip, + testing::{memory::gen_pointer, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, + InstructionExecutor, VmAirWrapper, }, - utils::{generate_long_number, i32_to_f}, + utils::i32_to_f, }; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, @@ -21,8 +20,6 @@ use openvm_stark_backend::{ Matrix, }, utils::disable_debug_builder, - verifier::VerificationError, - ChipUsageGetter, }; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; @@ -37,29 +34,58 @@ use crate::{ RV_B_TYPE_IMM_BITS, }, branch_lt::BranchLessThanCoreCols, + test_utils::get_verification_error, BranchLessThanCoreAir, }; +use test_case::test_case; type F = BabyBear; const MAX_INS_CAPACITY: usize = 128; +const ABS_MAX_IMM: i32 = 1 << (RV_B_TYPE_IMM_BITS - 1); -////////////////////////////////////////////////////////////////////////////////////// -// POSITIVE TESTS -// -// Randomly generate computations and execute, ensuring that the generated trace -// passes all constraints. -////////////////////////////////////////////////////////////////////////////////////// +fn create_test_chip( + tester: &mut VmChipTestBuilder, +) -> ( + Rv32BranchLessThanChip, + SharedBitwiseOperationLookupChip, +) { + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + let chip = Rv32BranchLessThanChip::::new( + VmAirWrapper::new( + Rv32BranchAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), + BranchLessThanCoreAir::new(bitwise_bus, BranchLessThanOpcode::CLASS_OFFSET), + ), + BranchLessThanStep::new( + Rv32BranchAdapterStep::new(), + bitwise_chip.clone(), + BranchLessThanOpcode::CLASS_OFFSET, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), + ); + + (chip, bitwise_chip) +} #[allow(clippy::too_many_arguments)] -fn run_rv32_branch_lt_rand_execute>( +fn set_and_execute>( tester: &mut VmChipTestBuilder, chip: &mut E, - opcode: BranchLessThanOpcode, - a: [u8; RV32_REGISTER_NUM_LIMBS], - b: [u8; RV32_REGISTER_NUM_LIMBS], - imm: i32, rng: &mut StdRng, + opcode: BranchLessThanOpcode, + a: Option<[u8; RV32_REGISTER_NUM_LIMBS]>, + b: Option<[u8; RV32_REGISTER_NUM_LIMBS]>, + imm: Option, ) { + let a = a.unwrap_or(array::from_fn(|_| rng.gen_range(0..=u8::MAX))); + let b = b.unwrap_or(if rng.gen_bool(0.5) { + a + } else { + array::from_fn(|_| rng.gen_range(0..=u8::MAX)) + }); + + let imm = imm.unwrap_or(rng.gen_range((-ABS_MAX_IMM)..ABS_MAX_IMM)); let rs1 = gen_pointer(rng, 4); let rs2 = gen_pointer(rng, 4); tester.write::(1, rs1, a.map(F::from_canonical_u8)); @@ -86,411 +112,327 @@ fn run_rv32_branch_lt_rand_execute>( assert_eq!(to_pc, from_pc + pc_inc); } -fn run_rv32_branch_lt_rand_test(opcode: BranchLessThanOpcode, num_ops: usize) { - let mut rng = create_seeded_rng(); - const ABS_MAX_BRANCH: i32 = 1 << (RV_B_TYPE_IMM_BITS - 1); - - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); +////////////////////////////////////////////////////////////////////////////////////// +// POSITIVE TESTS +// +// Randomly generate computations and execute, ensuring that the generated trace +// passes all constraints. +////////////////////////////////////////////////////////////////////////////////////// +#[test_case(BranchLessThanOpcode::BLT, 100)] +#[test_case(BranchLessThanOpcode::BLTU, 100)] +#[test_case(BranchLessThanOpcode::BGE, 100)] +#[test_case(BranchLessThanOpcode::BGEU, 100)] +fn rand_branch_lt_test(opcode: BranchLessThanOpcode, num_ops: usize) { + let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - - let mut chip = Rv32BranchLessThanChip::::new( - VmAirWrapper::new( - Rv32BranchAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), - BranchLessThanCoreAir::new(bitwise_bus, BranchLessThanOpcode::CLASS_OFFSET), - ), - BranchLessThanStep::new( - Rv32BranchAdapterStep::new(), - bitwise_chip.clone(), - BranchLessThanOpcode::CLASS_OFFSET, - ), - MAX_INS_CAPACITY, - tester.memory_helper(), - ); + let (mut chip, bitwise_chip) = create_test_chip(&mut tester); for _ in 0..num_ops { - // TODO(ayush): directly generate u8 - let a = generate_long_number::(&mut rng); - let b = if rng.gen_bool(0.5) { - a - } else { - generate_long_number::(&mut rng) - }; - let a = a.map(|x| x as u8); - let b = b.map(|x| x as u8); - let imm = rng.gen_range((-ABS_MAX_BRANCH)..ABS_MAX_BRANCH); - run_rv32_branch_lt_rand_execute(&mut tester, &mut chip, opcode, a, b, imm, &mut rng); + set_and_execute(&mut tester, &mut chip, &mut rng, opcode, None, None, None); } // Test special case where b = c - run_rv32_branch_lt_rand_execute( + set_and_execute( &mut tester, &mut chip, - opcode, - [101, 128, 202, 255], - [101, 128, 202, 255], - 24, &mut rng, + opcode, + Some([101, 128, 202, 255]), + Some([101, 128, 202, 255]), + Some(24), ); - run_rv32_branch_lt_rand_execute( + set_and_execute( &mut tester, &mut chip, - opcode, - [36, 0, 0, 0], - [36, 0, 0, 0], - 24, &mut rng, + opcode, + Some([36, 0, 0, 0]), + Some([36, 0, 0, 0]), + Some(24), ); let tester = tester.build().load(chip).load(bitwise_chip).finalize(); tester.simple_test().expect("Verification failed"); } +////////////////////////////////////////////////////////////////////////////////////// +// NEGATIVE TESTS +// +// Given a fake trace of a single operation, setup a chip and run the test. We replace +// part of the trace and check that the chip throws the expected error. +////////////////////////////////////////////////////////////////////////////////////// + +#[derive(Clone, Copy, Default, PartialEq)] +struct BranchLessThanPrankValues { + pub a_msb: Option, + pub b_msb: Option, + pub diff_marker: Option<[u32; NUM_LIMBS]>, + pub diff_val: Option, +} + +#[allow(clippy::too_many_arguments)] +fn run_negative_branch_lt_test( + opcode: BranchLessThanOpcode, + a: [u8; RV32_REGISTER_NUM_LIMBS], + b: [u8; RV32_REGISTER_NUM_LIMBS], + prank_cmp_result: bool, + prank_vals: BranchLessThanPrankValues, + interaction_error: bool, +) { + let imm = 16i32; + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let (mut chip, bitwise_chip) = create_test_chip(&mut tester); + + set_and_execute( + &mut tester, + &mut chip, + &mut rng, + opcode, + Some(a), + Some(b), + Some(imm), + ); + + let adapter_width = BaseAir::::width(&chip.air.adapter); + let ge_opcode = opcode == BranchLessThanOpcode::BGE || opcode == BranchLessThanOpcode::BGEU; + + let modify_trace = |trace: &mut DenseMatrix| { + let mut values = trace.row_slice(0).to_vec(); + let cols: &mut BranchLessThanCoreCols = + values.split_at_mut(adapter_width).1.borrow_mut(); + + if let Some(a_msb) = prank_vals.a_msb { + cols.a_msb_f = i32_to_f(a_msb); + } + if let Some(b_msb) = prank_vals.b_msb { + cols.b_msb_f = i32_to_f(b_msb); + } + if let Some(diff_marker) = prank_vals.diff_marker { + cols.diff_marker = diff_marker.map(F::from_canonical_u32); + } + if let Some(diff_val) = prank_vals.diff_val { + cols.diff_val = F::from_canonical_u32(diff_val); + } + cols.cmp_result = F::from_bool(prank_cmp_result); + cols.cmp_lt = F::from_bool(ge_opcode ^ prank_cmp_result); + + *trace = RowMajorMatrix::new(values, trace.width()); + }; + + disable_debug_builder(); + let tester = tester + .build() + .load_and_prank_trace(chip, modify_trace) + .load(bitwise_chip) + .finalize(); + tester.simple_test_with_expected_error(get_verification_error(interaction_error)); +} + #[test] -fn rv32_blt_rand_test() { - run_rv32_branch_lt_rand_test(BranchLessThanOpcode::BLT, 10); +fn rv32_blt_wrong_lt_cmp_negative_test() { + let a = [145, 34, 25, 205]; + let b = [73, 35, 25, 205]; + let prank_vals = Default::default(); + run_negative_branch_lt_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, false); } #[test] -fn rv32_bltu_rand_test() { - run_rv32_branch_lt_rand_test(BranchLessThanOpcode::BLTU, 12); +fn rv32_blt_wrong_ge_cmp_negative_test() { + let a = [73, 35, 25, 205]; + let b = [145, 34, 25, 205]; + let prank_vals = Default::default(); + run_negative_branch_lt_test(BranchLessThanOpcode::BLT, a, b, true, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BLTU, a, b, true, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BGE, a, b, false, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BGEU, a, b, false, prank_vals, false); } #[test] -fn rv32_bge_rand_test() { - run_rv32_branch_lt_rand_test(BranchLessThanOpcode::BGE, 12); +fn rv32_blt_wrong_eq_cmp_negative_test() { + let a = [73, 35, 25, 205]; + let b = [73, 35, 25, 205]; + let prank_vals = Default::default(); + run_negative_branch_lt_test(BranchLessThanOpcode::BLT, a, b, true, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BLTU, a, b, true, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BGE, a, b, false, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BGEU, a, b, false, prank_vals, false); } #[test] -fn rv32_bgeu_rand_test() { - run_rv32_branch_lt_rand_test(BranchLessThanOpcode::BGEU, 12); +fn rv32_blt_fake_diff_val_negative_test() { + let a = [145, 34, 25, 205]; + let b = [73, 35, 25, 205]; + let prank_vals = BranchLessThanPrankValues { + diff_val: Some(F::NEG_ONE.as_canonical_u32()), + ..Default::default() + }; + run_negative_branch_lt_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, true); + run_negative_branch_lt_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, true); + run_negative_branch_lt_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, true); + run_negative_branch_lt_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, true); } -////////////////////////////////////////////////////////////////////////////////////// -// NEGATIVE TESTS -// -// Given a fake trace of a single operation, setup a chip and run the test. We replace -// the write part of the trace and check that the core chip throws the expected error. -// A dummy adapter is used so memory interactions don't indirectly cause false passes. -////////////////////////////////////////////////////////////////////////////////////// +#[test] +fn rv32_blt_zero_diff_val_negative_test() { + let a = [145, 34, 25, 205]; + let b = [73, 35, 25, 205]; + let prank_vals = BranchLessThanPrankValues { + diff_marker: Some([0, 0, 1, 0]), + diff_val: Some(0), + ..Default::default() + }; + run_negative_branch_lt_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, true); + run_negative_branch_lt_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, true); + run_negative_branch_lt_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, true); + run_negative_branch_lt_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, true); +} + +#[test] +fn rv32_blt_fake_diff_marker_negative_test() { + let a = [145, 34, 25, 205]; + let b = [73, 35, 25, 205]; + let prank_vals = BranchLessThanPrankValues { + diff_marker: Some([1, 0, 0, 0]), + diff_val: Some(72), + ..Default::default() + }; + run_negative_branch_lt_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, false); +} -// type Rv32BranchLessThanTestChip = VmChipWrapper< -// F, -// TestAdapterChip, -// BranchLessThanStep, -// >; - -// #[derive(Clone, Copy, Default, PartialEq)] -// struct BranchLessThanPrankValues { -// pub a_msb: Option, -// pub b_msb: Option, -// pub diff_marker: Option<[u32; NUM_LIMBS]>, -// pub diff_val: Option, -// } - -// #[allow(clippy::too_many_arguments)] -// fn run_rv32_blt_negative_test( -// opcode: BranchLessThanOpcode, -// a: [u32; RV32_REGISTER_NUM_LIMBS], -// b: [u32; RV32_REGISTER_NUM_LIMBS], -// cmp_result: bool, -// prank_vals: BranchLessThanPrankValues, -// interaction_error: bool, -// ) { -// let imm = 16u32; -// let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); -// let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - -// let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); -// let mut chip = Rv32BranchLessThanTestChip::::new( -// TestAdapterChip::new( -// vec![[a.map(F::from_canonical_u32), b.map(F::from_canonical_u32)].concat()], -// vec![if cmp_result { Some(imm) } else { None }], -// ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), -// ), -// BranchLessThanStep::new(bitwise_chip.clone(), BranchLessThanOpcode::CLASS_OFFSET), -// tester.offline_memory_mutex_arc(), -// ); - -// tester.execute( -// &mut chip, -// &Instruction::from_usize(opcode.global_opcode(), [0, 0, imm as usize, 1, 1]), -// ); - -// let trace_width = chip.trace_width(); -// let adapter_width = BaseAir::::width(chip.adapter.air()); -// let ge_opcode = opcode == BranchLessThanOpcode::BGE || opcode == BranchLessThanOpcode::BGEU; -// let (_, _, a_sign, b_sign) = run_cmp::(opcode, &a, -// &b); - -// if prank_vals != BranchLessThanPrankValues::default() { -// debug_assert!(prank_vals.diff_val.is_some()); -// let a_msb = prank_vals.a_msb.unwrap_or( -// a[RV32_REGISTER_NUM_LIMBS - 1] as i32 - if a_sign { 1 << RV32_CELL_BITS } else { 0 }, -// ); -// let b_msb = prank_vals.b_msb.unwrap_or( -// b[RV32_REGISTER_NUM_LIMBS - 1] as i32 - if b_sign { 1 << RV32_CELL_BITS } else { 0 }, -// ); -// let signed_offset = match opcode { -// BranchLessThanOpcode::BLT | BranchLessThanOpcode::BGE => 1 << (RV32_CELL_BITS - 1), -// _ => 0, -// }; - -// bitwise_chip.clear(); -// bitwise_chip.request_range( -// (a_msb + signed_offset) as u8 as u32, -// (b_msb + signed_offset) as u8 as u32, -// ); - -// let diff_val = prank_vals -// .diff_val -// .unwrap() -// .clamp(0, (1 << RV32_CELL_BITS) - 1); -// if diff_val > 0 { -// bitwise_chip.request_range(diff_val - 1, 0); -// } -// } - -// let modify_trace = |trace: &mut DenseMatrix| { -// let mut values = trace.row_slice(0).to_vec(); -// let cols: &mut BranchLessThanCoreCols = -// values.split_at_mut(adapter_width).1.borrow_mut(); - -// if let Some(a_msb) = prank_vals.a_msb { -// cols.a_msb_f = i32_to_f(a_msb); -// } -// if let Some(b_msb) = prank_vals.b_msb { -// cols.b_msb_f = i32_to_f(b_msb); -// } -// if let Some(diff_marker) = prank_vals.diff_marker { -// cols.diff_marker = diff_marker.map(F::from_canonical_u32); -// } -// if let Some(diff_val) = prank_vals.diff_val { -// cols.diff_val = F::from_canonical_u32(diff_val); -// } -// cols.cmp_result = F::from_bool(cmp_result); -// cols.cmp_lt = F::from_bool(ge_opcode ^ cmp_result); - -// *trace = RowMajorMatrix::new(values, trace_width); -// }; - -// disable_debug_builder(); -// let tester = tester -// .build() -// .load_and_prank_trace(chip, modify_trace) -// .load(bitwise_chip) -// .finalize(); -// tester.simple_test_with_expected_error(if interaction_error { -// VerificationError::ChallengePhaseError -// } else { -// VerificationError::OodEvaluationMismatch -// }); -// } - -// #[test] -// fn rv32_blt_wrong_lt_cmp_negative_test() { -// let a = [145, 34, 25, 205]; -// let b = [73, 35, 25, 205]; -// let prank_vals = Default::default(); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, false); -// } - -// #[test] -// fn rv32_blt_wrong_ge_cmp_negative_test() { -// let a = [73, 35, 25, 205]; -// let b = [145, 34, 25, 205]; -// let prank_vals = Default::default(); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, true, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, true, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, false, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, false, prank_vals, false); -// } - -// #[test] -// fn rv32_blt_wrong_eq_cmp_negative_test() { -// let a = [73, 35, 25, 205]; -// let b = [73, 35, 25, 205]; -// let prank_vals = Default::default(); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, true, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, true, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, false, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, false, prank_vals, false); -// } - -// #[test] -// fn rv32_blt_fake_diff_val_negative_test() { -// let a = [145, 34, 25, 205]; -// let b = [73, 35, 25, 205]; -// let prank_vals = BranchLessThanPrankValues { -// diff_val: Some(F::NEG_ONE.as_canonical_u32()), -// ..Default::default() -// }; -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, true); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, true); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, true); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, true); -// } - -// #[test] -// fn rv32_blt_zero_diff_val_negative_test() { -// let a = [145, 34, 25, 205]; -// let b = [73, 35, 25, 205]; -// let prank_vals = BranchLessThanPrankValues { -// diff_marker: Some([0, 0, 1, 0]), -// diff_val: Some(0), -// ..Default::default() -// }; -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, true); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, true); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, true); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, true); -// } - -// #[test] -// fn rv32_blt_fake_diff_marker_negative_test() { -// let a = [145, 34, 25, 205]; -// let b = [73, 35, 25, 205]; -// let prank_vals = BranchLessThanPrankValues { -// diff_marker: Some([1, 0, 0, 0]), -// diff_val: Some(72), -// ..Default::default() -// }; -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, false); -// } - -// #[test] -// fn rv32_blt_zero_diff_marker_negative_test() { -// let a = [145, 34, 25, 205]; -// let b = [73, 35, 25, 205]; -// let prank_vals = BranchLessThanPrankValues { -// diff_marker: Some([0, 0, 0, 0]), -// diff_val: Some(0), -// ..Default::default() -// }; -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, false); -// } - -// #[test] -// fn rv32_blt_signed_wrong_a_msb_negative_test() { -// let a = [145, 34, 25, 205]; -// let b = [73, 35, 25, 205]; -// let prank_vals = BranchLessThanPrankValues { -// a_msb: Some(206), -// diff_marker: Some([0, 0, 0, 1]), -// diff_val: Some(1), -// ..Default::default() -// }; -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, false); -// } - -// #[test] -// fn rv32_blt_signed_wrong_a_msb_sign_negative_test() { -// let a = [145, 34, 25, 205]; -// let b = [73, 35, 25, 205]; -// let prank_vals = BranchLessThanPrankValues { -// a_msb: Some(205), -// diff_marker: Some([0, 0, 0, 1]), -// diff_val: Some(256), -// ..Default::default() -// }; -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, true); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, true); -// } - -// #[test] -// fn rv32_blt_signed_wrong_b_msb_negative_test() { -// let a = [145, 36, 25, 205]; -// let b = [73, 35, 25, 205]; -// let prank_vals = BranchLessThanPrankValues { -// b_msb: Some(206), -// diff_marker: Some([0, 0, 0, 1]), -// diff_val: Some(1), -// ..Default::default() -// }; -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, true, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, false, prank_vals, false); -// } - -// #[test] -// fn rv32_blt_signed_wrong_b_msb_sign_negative_test() { -// let a = [145, 36, 25, 205]; -// let b = [73, 35, 25, 205]; -// let prank_vals = BranchLessThanPrankValues { -// b_msb: Some(205), -// diff_marker: Some([0, 0, 0, 1]), -// diff_val: Some(256), -// ..Default::default() -// }; -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLT, a, b, true, prank_vals, true); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGE, a, b, false, prank_vals, true); -// } - -// #[test] -// fn rv32_blt_unsigned_wrong_a_msb_negative_test() { -// let a = [145, 36, 25, 205]; -// let b = [73, 35, 25, 205]; -// let prank_vals = BranchLessThanPrankValues { -// a_msb: Some(204), -// diff_marker: Some([0, 0, 0, 1]), -// diff_val: Some(1), -// ..Default::default() -// }; -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, true, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, false, prank_vals, false); -// } - -// #[test] -// fn rv32_blt_unsigned_wrong_a_msb_sign_negative_test() { -// let a = [145, 36, 25, 205]; -// let b = [73, 35, 25, 205]; -// let prank_vals = BranchLessThanPrankValues { -// a_msb: Some(-51), -// diff_marker: Some([0, 0, 0, 1]), -// diff_val: Some(256), -// ..Default::default() -// }; -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, true, prank_vals, true); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, false, prank_vals, true); -// } - -// #[test] -// fn rv32_blt_unsigned_wrong_b_msb_negative_test() { -// let a = [145, 34, 25, 205]; -// let b = [73, 35, 25, 205]; -// let prank_vals = BranchLessThanPrankValues { -// b_msb: Some(206), -// diff_marker: Some([0, 0, 0, 1]), -// diff_val: Some(1), -// ..Default::default() -// }; -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, false); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, false); -// } - -// #[test] -// fn rv32_blt_unsigned_wrong_b_msb_sign_negative_test() { -// let a = [145, 34, 25, 205]; -// let b = [73, 35, 25, 205]; -// let prank_vals = BranchLessThanPrankValues { -// b_msb: Some(-51), -// diff_marker: Some([0, 0, 0, 1]), -// diff_val: Some(256), -// ..Default::default() -// }; -// run_rv32_blt_negative_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, true); -// run_rv32_blt_negative_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, true); -// } +#[test] +fn rv32_blt_zero_diff_marker_negative_test() { + let a = [145, 34, 25, 205]; + let b = [73, 35, 25, 205]; + let prank_vals = BranchLessThanPrankValues { + diff_marker: Some([0, 0, 0, 0]), + diff_val: Some(0), + ..Default::default() + }; + run_negative_branch_lt_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, false); +} + +#[test] +fn rv32_blt_signed_wrong_a_msb_negative_test() { + let a = [145, 34, 25, 205]; + let b = [73, 35, 25, 205]; + let prank_vals = BranchLessThanPrankValues { + a_msb: Some(206), + diff_marker: Some([0, 0, 0, 1]), + diff_val: Some(1), + ..Default::default() + }; + run_negative_branch_lt_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, false); +} + +#[test] +fn rv32_blt_signed_wrong_a_msb_sign_negative_test() { + let a = [145, 34, 25, 205]; + let b = [73, 35, 25, 205]; + let prank_vals = BranchLessThanPrankValues { + a_msb: Some(205), + diff_marker: Some([0, 0, 0, 1]), + diff_val: Some(256), + ..Default::default() + }; + run_negative_branch_lt_test(BranchLessThanOpcode::BLT, a, b, false, prank_vals, true); + run_negative_branch_lt_test(BranchLessThanOpcode::BGE, a, b, true, prank_vals, true); +} + +#[test] +fn rv32_blt_signed_wrong_b_msb_negative_test() { + let a = [145, 36, 25, 205]; + let b = [73, 35, 25, 205]; + let prank_vals = BranchLessThanPrankValues { + b_msb: Some(206), + diff_marker: Some([0, 0, 0, 1]), + diff_val: Some(1), + ..Default::default() + }; + run_negative_branch_lt_test(BranchLessThanOpcode::BLT, a, b, true, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BGE, a, b, false, prank_vals, false); +} + +#[test] +fn rv32_blt_signed_wrong_b_msb_sign_negative_test() { + let a = [145, 36, 25, 205]; + let b = [73, 35, 25, 205]; + let prank_vals = BranchLessThanPrankValues { + b_msb: Some(205), + diff_marker: Some([0, 0, 0, 1]), + diff_val: Some(256), + ..Default::default() + }; + run_negative_branch_lt_test(BranchLessThanOpcode::BLT, a, b, true, prank_vals, true); + run_negative_branch_lt_test(BranchLessThanOpcode::BGE, a, b, false, prank_vals, true); +} + +#[test] +fn rv32_blt_unsigned_wrong_a_msb_negative_test() { + let a = [145, 36, 25, 205]; + let b = [73, 35, 25, 205]; + let prank_vals = BranchLessThanPrankValues { + a_msb: Some(204), + diff_marker: Some([0, 0, 0, 1]), + diff_val: Some(1), + ..Default::default() + }; + run_negative_branch_lt_test(BranchLessThanOpcode::BLTU, a, b, true, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BGEU, a, b, false, prank_vals, false); +} + +#[test] +fn rv32_blt_unsigned_wrong_a_msb_sign_negative_test() { + let a = [145, 36, 25, 205]; + let b = [73, 35, 25, 205]; + let prank_vals = BranchLessThanPrankValues { + a_msb: Some(-51), + diff_marker: Some([0, 0, 0, 1]), + diff_val: Some(256), + ..Default::default() + }; + run_negative_branch_lt_test(BranchLessThanOpcode::BLTU, a, b, true, prank_vals, true); + run_negative_branch_lt_test(BranchLessThanOpcode::BGEU, a, b, false, prank_vals, true); +} + +#[test] +fn rv32_blt_unsigned_wrong_b_msb_negative_test() { + let a = [145, 34, 25, 205]; + let b = [73, 35, 25, 205]; + let prank_vals = BranchLessThanPrankValues { + b_msb: Some(206), + diff_marker: Some([0, 0, 0, 1]), + diff_val: Some(1), + ..Default::default() + }; + run_negative_branch_lt_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, false); + run_negative_branch_lt_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, false); +} + +#[test] +fn rv32_blt_unsigned_wrong_b_msb_sign_negative_test() { + let a = [145, 34, 25, 205]; + let b = [73, 35, 25, 205]; + let prank_vals = BranchLessThanPrankValues { + b_msb: Some(-51), + diff_marker: Some([0, 0, 0, 1]), + diff_val: Some(256), + ..Default::default() + }; + run_negative_branch_lt_test(BranchLessThanOpcode::BLTU, a, b, false, prank_vals, true); + run_negative_branch_lt_test(BranchLessThanOpcode::BGEU, a, b, true, prank_vals, true); +} /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS @@ -498,38 +440,33 @@ fn rv32_bgeu_rand_test() { /// Ensure that solve functions produce the correct results. /////////////////////////////////////////////////////////////////////////////////////// -// #[test] -// fn execute_pc_increment_sanity_test() { -// let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); -// let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); -// let core = BranchLessThanStep::::new( -// bitwise_chip, -// BranchLessThanOpcode::CLASS_OFFSET, -// ); - -// let mut instruction = Instruction:: { -// opcode: BranchLessThanOpcode::BLT.global_opcode(), -// c: F::from_canonical_u8(8), -// ..Default::default() -// }; -// let x: [F; RV32_REGISTER_NUM_LIMBS] = [145, 34, 25, 205].map(F::from_canonical_u32); - -// let result = as VmCoreChip< -// F, -// BasicAdapterInterface, 2, 0, RV32_REGISTER_NUM_LIMBS, 0>, -// >>::execute_instruction(&core, &instruction, 0, [x, x]); -// let (output, _) = result.expect("execute_instruction failed"); -// assert!(output.to_pc.is_none()); - -// instruction.opcode = BranchLessThanOpcode::BGE.global_opcode(); -// let result = as VmCoreChip< -// F, -// BasicAdapterInterface, 2, 0, RV32_REGISTER_NUM_LIMBS, 0>, -// >>::execute_instruction(&core, &instruction, 0, [x, x]); -// let (output, _) = result.expect("execute_instruction failed"); -// assert!(output.to_pc.is_some()); -// assert_eq!(output.to_pc.unwrap(), 8); -// } +#[test] +fn execute_roundtrip_sanity_test() { + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let (mut chip, _) = create_test_chip(&mut tester); + + let x = [145, 34, 25, 205]; + set_and_execute( + &mut tester, + &mut chip, + &mut rng, + BranchLessThanOpcode::BLT, + Some(x), + Some(x), + Some(8), + ); + + set_and_execute( + &mut tester, + &mut chip, + &mut rng, + BranchLessThanOpcode::BGE, + Some(x), + Some(x), + Some(8), + ); +} #[test] fn run_cmp_unsigned_sanity_test() { diff --git a/extensions/rv32im/circuit/src/divrem/tests.rs b/extensions/rv32im/circuit/src/divrem/tests.rs index 1548769d5a..178b1bc87c 100644 --- a/extensions/rv32im/circuit/src/divrem/tests.rs +++ b/extensions/rv32im/circuit/src/divrem/tests.rs @@ -3,10 +3,9 @@ use std::{array, borrow::BorrowMut}; use openvm_circuit::{ arch::{ testing::{ - memory::gen_pointer, TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS, - RANGE_TUPLE_CHECKER_BUS, + memory::gen_pointer, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS, RANGE_TUPLE_CHECKER_BUS, }, - ExecutionBridge, InstructionExecutor, VmAirWrapper, + InstructionExecutor, VmAirWrapper, }, utils::generate_long_number, }; @@ -24,11 +23,10 @@ use openvm_stark_backend::{ Matrix, }, utils::disable_debug_builder, - verifier::VerificationError, - ChipUsageGetter, }; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; +use test_case::test_case; use super::core::run_divrem; use crate::{ @@ -37,18 +35,14 @@ use crate::{ run_mul_carries, run_sltu_diff_idx, DivRemCoreCols, DivRemCoreSpecialCase, DivRemStep, Rv32DivRemChip, }, - DivRemCoreAir, Rv32DivRemStep, + test_utils::get_verification_error, + DivRemCoreAir, }; type F = BabyBear; const MAX_INS_CAPACITY: usize = 128; - -////////////////////////////////////////////////////////////////////////////////////// -// POSITIVE TESTS -// -// Randomly generate computations and execute, ensuring that the generated trace -// passes all constraints. -////////////////////////////////////////////////////////////////////////////////////// +// the max number of limbs we currently support MUL for is 32 (i.e. for U256s) +const MAX_NUM_LIMBS: u32 = 32; fn limb_sra( x: [u32; NUM_LIMBS], @@ -59,15 +53,58 @@ fn limb_sra( array::from_fn(|i| if i + shift < NUM_LIMBS { x[i] } else { ext }) } +fn create_test_chip( + tester: &mut VmChipTestBuilder, +) -> ( + Rv32DivRemChip, + SharedBitwiseOperationLookupChip, + SharedRangeTupleCheckerChip<2>, +) { + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let range_tuple_bus = RangeTupleCheckerBus::new( + RANGE_TUPLE_CHECKER_BUS, + [1 << RV32_CELL_BITS, MAX_NUM_LIMBS * (1 << RV32_CELL_BITS)], + ); + + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + let range_tuple_chip = SharedRangeTupleCheckerChip::new(range_tuple_bus); + + let chip = Rv32DivRemChip::::new( + VmAirWrapper::new( + Rv32MultAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), + DivRemCoreAir::new(bitwise_bus, range_tuple_bus, DivRemOpcode::CLASS_OFFSET), + ), + DivRemStep::new( + Rv32MultAdapterStep::new(), + bitwise_chip.clone(), + range_tuple_chip.clone(), + DivRemOpcode::CLASS_OFFSET, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), + ); + + (chip, bitwise_chip, range_tuple_chip) +} + #[allow(clippy::too_many_arguments)] -fn run_rv32_divrem_rand_write_execute>( - opcode: DivRemOpcode, +fn set_and_execute>( tester: &mut VmChipTestBuilder, chip: &mut E, - b: [u32; RV32_REGISTER_NUM_LIMBS], - c: [u32; RV32_REGISTER_NUM_LIMBS], rng: &mut StdRng, + opcode: DivRemOpcode, + b: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, + c: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, ) { + let b = b.unwrap_or(generate_long_number::< + RV32_REGISTER_NUM_LIMBS, + RV32_CELL_BITS, + >(rng)); + let c = c.unwrap_or(limb_sra::( + generate_long_number::(rng), + rng.gen_range(0..(RV32_REGISTER_NUM_LIMBS - 1)), + )); + let rs1 = gen_pointer(rng, 4); let rs2 = gen_pointer(rng, 4); let rd = gen_pointer(rng, 4); @@ -91,466 +128,383 @@ fn run_rv32_divrem_rand_write_execute>( ); } -fn run_rv32_divrem_rand_test(opcode: DivRemOpcode, num_ops: usize) { - // the max number of limbs we currently support MUL for is 32 (i.e. for U256s) - const MAX_NUM_LIMBS: u32 = 32; - let mut rng = create_seeded_rng(); - - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let range_tuple_bus = RangeTupleCheckerBus::new( - RANGE_TUPLE_CHECKER_BUS, - [1 << RV32_CELL_BITS, MAX_NUM_LIMBS * (1 << RV32_CELL_BITS)], - ); - - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let range_tuple_checker = SharedRangeTupleCheckerChip::new(range_tuple_bus); +////////////////////////////////////////////////////////////////////////////////////// +// POSITIVE TESTS +// +// Randomly generate computations and execute, ensuring that the generated trace +// passes all constraints. +////////////////////////////////////////////////////////////////////////////////////// +#[test_case(DivRemOpcode::DIV, 100)] +#[test_case(DivRemOpcode::DIVU, 100)] +#[test_case(DivRemOpcode::REM, 100)] +#[test_case(DivRemOpcode::REMU, 100)] +fn rand_divrem_test(opcode: DivRemOpcode, num_ops: usize) { + let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - - let mut chip = Rv32DivRemChip::::new( - VmAirWrapper::new( - Rv32MultAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), - DivRemCoreAir::new(bitwise_bus, range_tuple_bus, DivRemOpcode::CLASS_OFFSET), - ), - DivRemStep::new( - Rv32MultAdapterStep::new(), - bitwise_chip.clone(), - range_tuple_checker.clone(), - DivRemOpcode::CLASS_OFFSET, - ), - MAX_INS_CAPACITY, - tester.memory_helper(), - ); + let (mut chip, bitwise_chip, range_tuple_chip) = create_test_chip(&mut tester); for _ in 0..num_ops { - let b = generate_long_number::(&mut rng); - let leading_zeros = rng.gen_range(0..(RV32_REGISTER_NUM_LIMBS - 1)); - let c = limb_sra::( - generate_long_number::(&mut rng), - leading_zeros, - ); - run_rv32_divrem_rand_write_execute(opcode, &mut tester, &mut chip, b, c, &mut rng); + set_and_execute(&mut tester, &mut chip, &mut rng, opcode, None, None); } // Test special cases in addition to random cases (i.e. zero divisor with b > 0, // zero divisor with b < 0, r = 0 (3 cases), and signed overflow). - run_rv32_divrem_rand_write_execute( - opcode, + set_and_execute( &mut tester, &mut chip, - [98, 188, 163, 127], - [0, 0, 0, 0], &mut rng, - ); - run_rv32_divrem_rand_write_execute( opcode, + Some([98, 188, 163, 127]), + Some([0, 0, 0, 0]), + ); + set_and_execute( &mut tester, &mut chip, - [98, 188, 163, 229], - [0, 0, 0, 0], &mut rng, - ); - run_rv32_divrem_rand_write_execute( opcode, + Some([98, 188, 163, 229]), + Some([0, 0, 0, 0]), + ); + set_and_execute( &mut tester, &mut chip, - [0, 0, 0, 128], - [0, 1, 0, 0], &mut rng, - ); - run_rv32_divrem_rand_write_execute( opcode, + Some([0, 0, 0, 128]), + Some([0, 1, 0, 0]), + ); + set_and_execute( &mut tester, &mut chip, - [0, 0, 0, 127], - [0, 1, 0, 0], &mut rng, - ); - run_rv32_divrem_rand_write_execute( opcode, + Some([0, 0, 0, 127]), + Some([0, 1, 0, 0]), + ); + set_and_execute( &mut tester, &mut chip, - [0, 0, 0, 0], - [0, 0, 0, 0], &mut rng, + opcode, + Some([0, 0, 0, 0]), + Some([0, 0, 0, 0]), ); - run_rv32_divrem_rand_write_execute( + set_and_execute( + &mut tester, + &mut chip, + &mut rng, opcode, + Some([0, 0, 0, 0]), + Some([0, 0, 0, 0]), + ); + set_and_execute( &mut tester, &mut chip, - [0, 0, 0, 128], - [255, 255, 255, 255], &mut rng, + opcode, + Some([0, 0, 0, 128]), + Some([255, 255, 255, 255]), ); let tester = tester .build() .load(chip) .load(bitwise_chip) - .load(range_tuple_checker) + .load(range_tuple_chip) .finalize(); tester.simple_test().expect("Verification failed"); } +////////////////////////////////////////////////////////////////////////////////////// +// NEGATIVE TESTS +// +// Given a fake trace of a single operation, setup a chip and run the test. We replace +// part of the trace and check that the chip throws the expected error. +////////////////////////////////////////////////////////////////////////////////////// + +#[derive(Default, Clone, Copy)] +struct DivRemPrankValues { + pub q: Option<[u32; NUM_LIMBS]>, + pub r: Option<[u32; NUM_LIMBS]>, + pub r_prime: Option<[u32; NUM_LIMBS]>, + pub diff_val: Option, + pub zero_divisor: Option, + pub r_zero: Option, +} + +fn run_negative_divrem_test( + signed: bool, + b: [u32; RV32_REGISTER_NUM_LIMBS], + c: [u32; RV32_REGISTER_NUM_LIMBS], + prank_vals: DivRemPrankValues, + interaction_error: bool, +) { + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let (mut chip, bitwise_chip, range_tuple_chip) = create_test_chip(&mut tester); + + let (div_opcode, rem_opcode) = if signed { + (DivRemOpcode::DIV, DivRemOpcode::REM) + } else { + (DivRemOpcode::DIVU, DivRemOpcode::REMU) + }; + + set_and_execute( + &mut tester, + &mut chip, + &mut rng, + div_opcode, + Some(b), + Some(c), + ); + + set_and_execute( + &mut tester, + &mut chip, + &mut rng, + rem_opcode, + Some(b), + Some(c), + ); + + let adapter_width = BaseAir::::width(&chip.air.adapter); + let modify_trace = |trace: &mut DenseMatrix| { + let mut values = trace.row_slice(0).to_vec(); + let cols: &mut DivRemCoreCols = + values.split_at_mut(adapter_width).1.borrow_mut(); + + if let Some(q) = prank_vals.q { + cols.q = q.map(F::from_canonical_u32); + } + if let Some(r) = prank_vals.r { + cols.r = r.map(F::from_canonical_u32); + let r_sum = r.iter().sum::(); + cols.r_sum_inv = F::from_canonical_u32(r_sum) + .try_inverse() + .unwrap_or(F::ZERO); + } + if let Some(r_prime) = prank_vals.r_prime { + cols.r_prime = r_prime.map(F::from_canonical_u32); + cols.r_inv = cols + .r_prime + .map(|r| (r - F::from_canonical_u32(256)).inverse()); + } + if let Some(diff_val) = prank_vals.diff_val { + cols.lt_diff = F::from_canonical_u32(diff_val); + } + if let Some(zero_divisor) = prank_vals.zero_divisor { + cols.zero_divisor = F::from_bool(zero_divisor); + } + if let Some(r_zero) = prank_vals.r_zero { + cols.r_zero = F::from_bool(r_zero); + } + + *trace = RowMajorMatrix::new(values, trace.width()); + }; + + disable_debug_builder(); + let tester = tester + .build() + .load_and_prank_trace(chip, modify_trace) + .load(bitwise_chip) + .load(range_tuple_chip) + .finalize(); + tester.simple_test_with_expected_error(get_verification_error(interaction_error)); +} + #[test] -fn rv32_div_rand_test() { - run_rv32_divrem_rand_test(DivRemOpcode::DIV, 100); +fn rv32_divrem_unsigned_wrong_q_negative_test() { + let b: [u32; RV32_REGISTER_NUM_LIMBS] = [98, 188, 163, 229]; + let c: [u32; RV32_REGISTER_NUM_LIMBS] = [123, 34, 0, 0]; + let prank_vals = DivRemPrankValues { + q: Some([245, 168, 7, 0]), + ..Default::default() + }; + run_negative_divrem_test(false, b, c, prank_vals, true); } #[test] -fn rv32_divu_rand_test() { - run_rv32_divrem_rand_test(DivRemOpcode::DIVU, 100); +fn rv32_divrem_unsigned_wrong_r_negative_test() { + let b: [u32; RV32_REGISTER_NUM_LIMBS] = [98, 188, 163, 229]; + let c: [u32; RV32_REGISTER_NUM_LIMBS] = [123, 34, 0, 0]; + let prank_vals = DivRemPrankValues { + r: Some([171, 3, 0, 0]), + r_prime: Some([171, 3, 0, 0]), + diff_val: Some(31), + ..Default::default() + }; + run_negative_divrem_test(false, b, c, prank_vals, true); } #[test] -fn rv32_rem_rand_test() { - run_rv32_divrem_rand_test(DivRemOpcode::REM, 100); +fn rv32_divrem_unsigned_high_mult_negative_test() { + let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; + let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 2, 0, 0]; + let prank_vals = DivRemPrankValues { + q: Some([128, 0, 0, 1]), + ..Default::default() + }; + run_negative_divrem_test(false, b, c, prank_vals, true); } #[test] -fn rv32_remu_rand_test() { - run_rv32_divrem_rand_test(DivRemOpcode::REMU, 100); +fn rv32_divrem_unsigned_zero_divisor_wrong_r_negative_test() { + let b: [u32; RV32_REGISTER_NUM_LIMBS] = [254, 255, 255, 255]; + let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; + let prank_vals = DivRemPrankValues { + r: Some([255, 255, 255, 255]), + r_prime: Some([255, 255, 255, 255]), + diff_val: Some(255), + ..Default::default() + }; + run_negative_divrem_test(false, b, c, prank_vals, true); } -////////////////////////////////////////////////////////////////////////////////////// -// NEGATIVE TESTS -// -// Given a fake trace of a single operation, setup a chip and run the test. We replace -// the write part of the trace and check that the core chip throws the expected error. -// A dummy adapter is used so memory interactions don't indirectly cause false passes. -////////////////////////////////////////////////////////////////////////////////////// +#[test] +fn rv32_divrem_signed_wrong_q_negative_test() { + let b: [u32; RV32_REGISTER_NUM_LIMBS] = [98, 188, 163, 229]; + let c: [u32; RV32_REGISTER_NUM_LIMBS] = [123, 34, 0, 0]; + let prank_vals = DivRemPrankValues { + q: Some([74, 61, 255, 255]), + ..Default::default() + }; + run_negative_divrem_test(true, b, c, prank_vals, true); +} -// type Rv32DivRemTestChip = -// VmChipWrapper, DivRemStep>; - -// #[derive(Default, Clone, Copy)] -// struct DivRemPrankValues { -// pub q: Option<[u32; NUM_LIMBS]>, -// pub r: Option<[u32; NUM_LIMBS]>, -// pub r_prime: Option<[u32; NUM_LIMBS]>, -// pub diff_val: Option, -// pub zero_divisor: Option, -// pub r_zero: Option, -// } - -// fn run_rv32_divrem_negative_test( -// signed: bool, -// b: [u32; RV32_REGISTER_NUM_LIMBS], -// c: [u32; RV32_REGISTER_NUM_LIMBS], -// prank_vals: &DivRemPrankValues, -// interaction_error: bool, -// ) { -// // the max number of limbs we currently support MUL for is 32 (i.e. for U256s) -// const MAX_NUM_LIMBS: u32 = 32; -// let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); -// let range_tuple_bus = RangeTupleCheckerBus::new( -// RANGE_TUPLE_CHECKER_BUS, -// [1 << RV32_CELL_BITS, MAX_NUM_LIMBS * (1 << RV32_CELL_BITS)], -// ); - -// let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); -// let range_tuple_chip = SharedRangeTupleCheckerChip::new(range_tuple_bus); - -// let mut tester = VmChipTestBuilder::default(); -// let mut chip = Rv32DivRemTestChip::::new( -// TestAdapterChip::new( -// vec![[b.map(F::from_canonical_u32), c.map(F::from_canonical_u32)].concat(); 2], -// vec![None], -// ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), -// ), -// DivRemStep::new( -// bitwise_chip.clone(), -// range_tuple_chip.clone(), -// DivRemOpcode::CLASS_OFFSET, -// ), -// tester.offline_memory_mutex_arc(), -// ); - -// let (div_opcode, rem_opcode) = if signed { -// (DivRemOpcode::DIV, DivRemOpcode::REM) -// } else { -// (DivRemOpcode::DIVU, DivRemOpcode::REMU) -// }; -// tester.execute( -// &mut chip, -// &Instruction::from_usize(div_opcode.global_opcode(), [0, 0, 0, 1, 1]), -// ); -// tester.execute( -// &mut chip, -// &Instruction::from_usize(rem_opcode.global_opcode(), [0, 0, 0, 1, 1]), -// ); - -// let (q, r, b_sign, c_sign, q_sign, case) = -// run_divrem::(signed, &b, &c); -// let q = prank_vals.q.unwrap_or(q); -// let r = prank_vals.r.unwrap_or(r); -// let carries = -// run_mul_carries::(signed, &c, &q, &r, q_sign); - -// range_tuple_chip.clear(); -// for i in 0..RV32_REGISTER_NUM_LIMBS { -// range_tuple_chip.add_count(&[q[i], carries[i]]); -// range_tuple_chip.add_count(&[r[i], carries[i + RV32_REGISTER_NUM_LIMBS]]); -// } - -// if let Some(diff_val) = prank_vals.diff_val { -// bitwise_chip.clear(); -// if signed { -// let b_sign_mask = if b_sign { 1 << (RV32_CELL_BITS - 1) } else { 0 }; -// let c_sign_mask = if c_sign { 1 << (RV32_CELL_BITS - 1) } else { 0 }; -// bitwise_chip.request_range( -// (b[RV32_REGISTER_NUM_LIMBS - 1] - b_sign_mask) << 1, -// (c[RV32_REGISTER_NUM_LIMBS - 1] - c_sign_mask) << 1, -// ); -// } -// if case == DivRemCoreSpecialCase::None { -// bitwise_chip.request_range(diff_val - 1, 0); -// } -// } - -// let trace_width = chip.trace_width(); -// let adapter_width = BaseAir::::width(chip.adapter.air()); - -// let modify_trace = |trace: &mut DenseMatrix| { -// let mut values = trace.row_slice(0).to_vec(); -// let cols: &mut DivRemCoreCols = -// values.split_at_mut(adapter_width).1.borrow_mut(); - -// if let Some(q) = prank_vals.q { -// cols.q = q.map(F::from_canonical_u32); -// } -// if let Some(r) = prank_vals.r { -// cols.r = r.map(F::from_canonical_u32); -// let r_sum = r.iter().sum::(); -// cols.r_sum_inv = F::from_canonical_u32(r_sum) -// .try_inverse() -// .unwrap_or(F::ZERO); -// } -// if let Some(r_prime) = prank_vals.r_prime { -// cols.r_prime = r_prime.map(F::from_canonical_u32); -// cols.r_inv = cols -// .r_prime -// .map(|r| (r - F::from_canonical_u32(256)).inverse()); -// } -// if let Some(diff_val) = prank_vals.diff_val { -// cols.lt_diff = F::from_canonical_u32(diff_val); -// } -// if let Some(zero_divisor) = prank_vals.zero_divisor { -// cols.zero_divisor = F::from_bool(zero_divisor); -// } -// if let Some(r_zero) = prank_vals.r_zero { -// cols.r_zero = F::from_bool(r_zero); -// } - -// *trace = RowMajorMatrix::new(values, trace_width); -// }; - -// disable_debug_builder(); -// let tester = tester -// .build() -// .load_and_prank_trace(chip, modify_trace) -// .load(bitwise_chip) -// .load(range_tuple_chip) -// .finalize(); -// tester.simple_test_with_expected_error(if interaction_error { -// VerificationError::ChallengePhaseError -// } else { -// VerificationError::OodEvaluationMismatch -// }); -// } - -// #[test] -// fn rv32_divrem_unsigned_wrong_q_negative_test() { -// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [98, 188, 163, 229]; -// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [123, 34, 0, 0]; -// let prank_vals = DivRemPrankValues { -// q: Some([245, 168, 7, 0]), -// ..Default::default() -// }; -// run_rv32_divrem_negative_test(false, b, c, &prank_vals, true); -// } - -// #[test] -// fn rv32_divrem_unsigned_wrong_r_negative_test() { -// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [98, 188, 163, 229]; -// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [123, 34, 0, 0]; -// let prank_vals = DivRemPrankValues { -// r: Some([171, 3, 0, 0]), -// r_prime: Some([171, 3, 0, 0]), -// diff_val: Some(31), -// ..Default::default() -// }; -// run_rv32_divrem_negative_test(false, b, c, &prank_vals, true); -// } - -// #[test] -// fn rv32_divrem_unsigned_high_mult_negative_test() { -// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; -// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 2, 0, 0]; -// let prank_vals = DivRemPrankValues { -// q: Some([128, 0, 0, 1]), -// ..Default::default() -// }; -// run_rv32_divrem_negative_test(false, b, c, &prank_vals, true); -// } - -// #[test] -// fn rv32_divrem_unsigned_zero_divisor_wrong_r_negative_test() { -// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [254, 255, 255, 255]; -// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; -// let prank_vals = DivRemPrankValues { -// r: Some([255, 255, 255, 255]), -// r_prime: Some([255, 255, 255, 255]), -// diff_val: Some(255), -// ..Default::default() -// }; -// run_rv32_divrem_negative_test(false, b, c, &prank_vals, true); -// } - -// #[test] -// fn rv32_divrem_signed_wrong_q_negative_test() { -// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [98, 188, 163, 229]; -// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [123, 34, 0, 0]; -// let prank_vals = DivRemPrankValues { -// q: Some([74, 61, 255, 255]), -// ..Default::default() -// }; -// run_rv32_divrem_negative_test(true, b, c, &prank_vals, true); -// } - -// #[test] -// fn rv32_divrem_signed_wrong_r_negative_test() { -// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [98, 188, 163, 229]; -// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [123, 34, 0, 0]; -// let prank_vals = DivRemPrankValues { -// r: Some([212, 241, 255, 255]), -// r_prime: Some([44, 14, 0, 0]), -// diff_val: Some(20), -// ..Default::default() -// }; -// run_rv32_divrem_negative_test(true, b, c, &prank_vals, true); -// } - -// #[test] -// fn rv32_divrem_signed_high_mult_negative_test() { -// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 255]; -// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 255]; -// let prank_vals = DivRemPrankValues { -// q: Some([1, 0, 0, 1]), -// ..Default::default() -// }; -// run_rv32_divrem_negative_test(true, b, c, &prank_vals, true); -// } - -// #[test] -// fn rv32_divrem_signed_r_wrong_sign_negative_test() { -// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; -// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 0, 0, 0]; -// let prank_vals = DivRemPrankValues { -// q: Some([31, 5, 0, 0]), -// r: Some([242, 255, 255, 255]), -// r_prime: Some([242, 255, 255, 255]), -// diff_val: Some(192), -// ..Default::default() -// }; -// run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); -// } - -// #[test] -// fn rv32_divrem_signed_r_wrong_prime_negative_test() { -// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; -// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 0, 0, 0]; -// let prank_vals = DivRemPrankValues { -// q: Some([31, 5, 0, 0]), -// r: Some([242, 255, 255, 255]), -// r_prime: Some([14, 0, 0, 0]), -// diff_val: Some(36), -// ..Default::default() -// }; -// run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); -// } - -// #[test] -// fn rv32_divrem_signed_zero_divisor_wrong_r_negative_test() { -// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [254, 255, 255, 255]; -// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; -// let prank_vals = DivRemPrankValues { -// r: Some([255, 255, 255, 255]), -// r_prime: Some([1, 0, 0, 0]), -// diff_val: Some(1), -// ..Default::default() -// }; -// run_rv32_divrem_negative_test(true, b, c, &prank_vals, true); -// } - -// #[test] -// fn rv32_divrem_false_zero_divisor_flag_negative_test() { -// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; -// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 0, 0, 0]; -// let prank_vals = DivRemPrankValues { -// q: Some([29, 5, 0, 0]), -// r: Some([86, 0, 0, 0]), -// r_prime: Some([86, 0, 0, 0]), -// diff_val: Some(36), -// zero_divisor: Some(true), -// ..Default::default() -// }; -// run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); -// run_rv32_divrem_negative_test(false, b, c, &prank_vals, false); -// } - -// #[test] -// fn rv32_divrem_false_r_zero_flag_negative_test() { -// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; -// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 0, 0, 0]; -// let prank_vals = DivRemPrankValues { -// q: Some([29, 5, 0, 0]), -// r: Some([86, 0, 0, 0]), -// r_prime: Some([86, 0, 0, 0]), -// diff_val: Some(36), -// r_zero: Some(true), -// ..Default::default() -// }; -// run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); -// run_rv32_divrem_negative_test(false, b, c, &prank_vals, false); -// } - -// #[test] -// fn rv32_divrem_unset_zero_divisor_flag_negative_test() { -// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; -// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; -// let prank_vals = DivRemPrankValues { -// zero_divisor: Some(false), -// ..Default::default() -// }; -// run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); -// run_rv32_divrem_negative_test(false, b, c, &prank_vals, false); -// } - -// #[test] -// fn rv32_divrem_wrong_r_zero_flag_negative_test() { -// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; -// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; -// let prank_vals = DivRemPrankValues { -// zero_divisor: Some(false), -// r_zero: Some(true), -// ..Default::default() -// }; -// run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); -// run_rv32_divrem_negative_test(false, b, c, &prank_vals, false); -// } - -// #[test] -// fn rv32_divrem_unset_r_zero_flag_negative_test() { -// let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; -// let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; -// let prank_vals = DivRemPrankValues { -// r_zero: Some(false), -// ..Default::default() -// }; -// run_rv32_divrem_negative_test(true, b, c, &prank_vals, false); -// run_rv32_divrem_negative_test(false, b, c, &prank_vals, false); -// } +#[test] +fn rv32_divrem_signed_wrong_r_negative_test() { + let b: [u32; RV32_REGISTER_NUM_LIMBS] = [98, 188, 163, 229]; + let c: [u32; RV32_REGISTER_NUM_LIMBS] = [123, 34, 0, 0]; + let prank_vals = DivRemPrankValues { + r: Some([212, 241, 255, 255]), + r_prime: Some([44, 14, 0, 0]), + diff_val: Some(20), + ..Default::default() + }; + run_negative_divrem_test(true, b, c, prank_vals, true); +} + +#[test] +fn rv32_divrem_signed_high_mult_negative_test() { + let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 255]; + let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 255]; + let prank_vals = DivRemPrankValues { + q: Some([1, 0, 0, 1]), + ..Default::default() + }; + run_negative_divrem_test(true, b, c, prank_vals, true); +} + +#[test] +fn rv32_divrem_signed_r_wrong_sign_negative_test() { + let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; + let c: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 0, 0, 0]; + let prank_vals = DivRemPrankValues { + q: Some([31, 5, 0, 0]), + r: Some([242, 255, 255, 255]), + r_prime: Some([242, 255, 255, 255]), + diff_val: Some(192), + ..Default::default() + }; + run_negative_divrem_test(true, b, c, prank_vals, false); +} + +#[test] +fn rv32_divrem_signed_r_wrong_prime_negative_test() { + let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; + let c: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 0, 0, 0]; + let prank_vals = DivRemPrankValues { + q: Some([31, 5, 0, 0]), + r: Some([242, 255, 255, 255]), + r_prime: Some([14, 0, 0, 0]), + diff_val: Some(36), + ..Default::default() + }; + run_negative_divrem_test(true, b, c, prank_vals, false); +} + +#[test] +fn rv32_divrem_signed_zero_divisor_wrong_r_negative_test() { + let b: [u32; RV32_REGISTER_NUM_LIMBS] = [254, 255, 255, 255]; + let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; + let prank_vals = DivRemPrankValues { + r: Some([255, 255, 255, 255]), + r_prime: Some([1, 0, 0, 0]), + diff_val: Some(1), + ..Default::default() + }; + run_negative_divrem_test(true, b, c, prank_vals, true); +} + +#[test] +fn rv32_divrem_false_zero_divisor_flag_negative_test() { + let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; + let c: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 0, 0, 0]; + let prank_vals = DivRemPrankValues { + q: Some([29, 5, 0, 0]), + r: Some([86, 0, 0, 0]), + r_prime: Some([86, 0, 0, 0]), + diff_val: Some(36), + zero_divisor: Some(true), + ..Default::default() + }; + run_negative_divrem_test(true, b, c, prank_vals, false); + run_negative_divrem_test(false, b, c, prank_vals, false); +} + +#[test] +fn rv32_divrem_false_r_zero_flag_negative_test() { + let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; + let c: [u32; RV32_REGISTER_NUM_LIMBS] = [50, 0, 0, 0]; + let prank_vals = DivRemPrankValues { + q: Some([29, 5, 0, 0]), + r: Some([86, 0, 0, 0]), + r_prime: Some([86, 0, 0, 0]), + diff_val: Some(36), + r_zero: Some(true), + ..Default::default() + }; + run_negative_divrem_test(true, b, c, prank_vals, false); + run_negative_divrem_test(false, b, c, prank_vals, false); +} + +#[test] +fn rv32_divrem_unset_zero_divisor_flag_negative_test() { + let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; + let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; + let prank_vals = DivRemPrankValues { + zero_divisor: Some(false), + ..Default::default() + }; + run_negative_divrem_test(true, b, c, prank_vals, false); + run_negative_divrem_test(false, b, c, prank_vals, false); +} + +#[test] +fn rv32_divrem_wrong_r_zero_flag_negative_test() { + let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; + let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 0]; + let prank_vals = DivRemPrankValues { + zero_divisor: Some(false), + r_zero: Some(true), + ..Default::default() + }; + run_negative_divrem_test(true, b, c, prank_vals, false); + run_negative_divrem_test(false, b, c, prank_vals, false); +} + +#[test] +fn rv32_divrem_unset_r_zero_flag_negative_test() { + let b: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; + let c: [u32; RV32_REGISTER_NUM_LIMBS] = [0, 0, 1, 0]; + let prank_vals = DivRemPrankValues { + r_zero: Some(false), + ..Default::default() + }; + run_negative_divrem_test(true, b, c, prank_vals, false); + run_negative_divrem_test(false, b, c, prank_vals, false); +} /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS diff --git a/extensions/rv32im/circuit/src/hintstore/tests.rs b/extensions/rv32im/circuit/src/hintstore/tests.rs index d76017a3a7..5af467be37 100644 --- a/extensions/rv32im/circuit/src/hintstore/tests.rs +++ b/extensions/rv32im/circuit/src/hintstore/tests.rs @@ -8,8 +8,9 @@ use openvm_circuit::arch::{ testing::{memory::gen_pointer, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, ExecutionBridge, Streams, }; -use openvm_circuit_primitives::bitwise_op_lookup::{ - BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, +use openvm_circuit_primitives::{ + bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, + var_range::SharedVariableRangeCheckerChip, }; use openvm_instructions::{ instruction::Instruction, @@ -24,16 +25,41 @@ use openvm_stark_backend::{ Matrix, }, utils::disable_debug_builder, - verifier::VerificationError, }; use openvm_stark_sdk::{config::setup_tracing, p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; use super::{Rv32HintStoreAir, Rv32HintStoreChip, Rv32HintStoreCols, Rv32HintStoreStep}; -use crate::adapters::decompose; +use crate::{adapters::decompose, test_utils::get_verification_error}; type F = BabyBear; -const MAX_INS_CAPACITY: usize = 128; +const MAX_INS_CAPACITY: usize = 1024; + +fn create_test_chip( + tester: &mut VmChipTestBuilder, +) -> ( + Rv32HintStoreChip, + SharedBitwiseOperationLookupChip, +) { + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + + let mut chip = Rv32HintStoreChip::::new( + Rv32HintStoreAir::new( + ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), + tester.memory_bridge(), + bitwise_chip.bus(), + 0, + tester.address_bits(), + ), + Rv32HintStoreStep::new(bitwise_chip.clone(), tester.address_bits(), 0), + MAX_INS_CAPACITY, + tester.memory_helper(), + ); + chip.step + .set_streams(Arc::new(Mutex::new(Streams::default()))); + (chip, bitwise_chip) +} fn set_and_execute( tester: &mut VmChipTestBuilder, @@ -122,34 +148,16 @@ fn set_and_execute_buffer( /// Randomly generate computations and execute, ensuring that the generated trace /// passes all constraints. /////////////////////////////////////////////////////////////////////////////////////// + #[test] fn rand_hintstore_test() { setup_tracing(); let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - - let range_checker_chip = tester.memory_controller().range_checker.clone(); - - let mut chip = Rv32HintStoreChip::::new( - Rv32HintStoreAir::new( - ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), - tester.memory_bridge(), - bitwise_chip.bus(), - 0, - tester.address_bits(), - ), - Rv32HintStoreStep::new(bitwise_chip.clone(), tester.address_bits(), 0), - MAX_INS_CAPACITY, - tester.memory_helper(), - ); - chip.step - .set_streams(Arc::new(Mutex::new(Streams::default()))); - - let num_tests: usize = 8; - for _ in 0..num_tests { + let (mut chip, bitwise_chip) = create_test_chip(&mut tester); + let num_ops: usize = 100; + for _ in 0..num_ops { if rng.gen_bool(0.5) { set_and_execute(&mut tester, &mut chip, &mut rng, HINT_STOREW); } else { @@ -157,7 +165,6 @@ fn rand_hintstore_test() { } } - drop(range_checker_chip); let tester = tester.build().load(chip).load(bitwise_chip).finalize(); tester.simple_test().expect("Verification failed"); } @@ -166,68 +173,44 @@ fn rand_hintstore_test() { // NEGATIVE TESTS // // Given a fake trace of a single operation, setup a chip and run the test. We replace -// the write part of the trace and check that the core chip throws the expected error. -// A dummy adaptor is used so memory interactions don't indirectly cause false passes. +// part of the trace and check that the chip throws the expected error. ////////////////////////////////////////////////////////////////////////////////////// #[allow(clippy::too_many_arguments)] fn run_negative_hintstore_test( opcode: Rv32HintStoreOpcode, - data: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, - expected_error: VerificationError, + prank_data: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, + interaction_error: bool, ) { let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - - let range_checker_chip = tester.memory_controller().range_checker.clone(); - - let mut chip = Rv32HintStoreChip::::new( - Rv32HintStoreAir::new( - ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), - tester.memory_bridge(), - bitwise_chip.bus(), - 0, - tester.address_bits(), - ), - Rv32HintStoreStep::new(bitwise_chip.clone(), tester.address_bits(), 0), - MAX_INS_CAPACITY, - tester.memory_helper(), - ); - chip.step - .set_streams(Arc::new(Mutex::new(Streams::default()))); + let (mut chip, bitwise_chip) = create_test_chip(&mut tester); set_and_execute(&mut tester, &mut chip, &mut rng, opcode); let modify_trace = |trace: &mut DenseMatrix| { let mut trace_row = trace.row_slice(0).to_vec(); let cols: &mut Rv32HintStoreCols = trace_row.as_mut_slice().borrow_mut(); - if let Some(data) = data { + if let Some(data) = prank_data { cols.data = data.map(F::from_canonical_u32); } *trace = RowMajorMatrix::new(trace_row, trace.width()); }; - drop(range_checker_chip); disable_debug_builder(); let tester = tester .build() .load_and_prank_trace(chip, modify_trace) .load(bitwise_chip) .finalize(); - tester.simple_test_with_expected_error(expected_error); + tester.simple_test_with_expected_error(get_verification_error(interaction_error)); } #[test] fn negative_hintstore_tests() { - run_negative_hintstore_test( - HINT_STOREW, - Some([92, 187, 45, 280]), - VerificationError::ChallengePhaseError, - ); + run_negative_hintstore_test(HINT_STOREW, Some([92, 187, 45, 280]), true); } + /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS /// @@ -238,26 +221,10 @@ fn execute_roundtrip_sanity_test() { let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - - let mut chip = Rv32HintStoreChip::::new( - Rv32HintStoreAir::new( - ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), - tester.memory_bridge(), - bitwise_chip.bus(), - 0, - tester.address_bits(), - ), - Rv32HintStoreStep::new(bitwise_chip.clone(), tester.address_bits(), 0), - MAX_INS_CAPACITY, - tester.memory_helper(), - ); - chip.step - .set_streams(Arc::new(Mutex::new(Streams::default()))); + let (mut chip, _) = create_test_chip(&mut tester); - let num_tests: usize = 100; - for _ in 0..num_tests { + let num_ops: usize = 10; + for _ in 0..num_ops { set_and_execute(&mut tester, &mut chip, &mut rng, HINT_STOREW); } } diff --git a/extensions/rv32im/circuit/src/jal_lui/tests.rs b/extensions/rv32im/circuit/src/jal_lui/tests.rs index 8cf33625fb..ec09769612 100644 --- a/extensions/rv32im/circuit/src/jal_lui/tests.rs +++ b/extensions/rv32im/circuit/src/jal_lui/tests.rs @@ -10,14 +10,17 @@ use openvm_circuit_primitives::bitwise_op_lookup::{ use openvm_instructions::{instruction::Instruction, program::PC_BITS, LocalOpcode}; use openvm_rv32im_transpiler::Rv32JalLuiOpcode::{self, *}; use openvm_stark_backend::{ + p3_air::BaseAir, p3_field::{FieldAlgebra, PrimeField32}, - p3_matrix::{dense::RowMajorMatrix, Matrix}, + p3_matrix::{ + dense::{DenseMatrix, RowMajorMatrix}, + Matrix, + }, utils::disable_debug_builder, - verifier::VerificationError, - Chip, ChipUsageGetter, }; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; +use test_case::test_case; use super::{run_jal_lui, Rv32JalLuiChip, Rv32JalLuiCoreAir, Rv32JalLuiStep}; use crate::{ @@ -27,12 +30,12 @@ use crate::{ RV_IS_TYPE_IMM_BITS, }, jal_lui::Rv32JalLuiCoreCols, + test_utils::get_verification_error, }; const IMM_BITS: usize = 20; const LIMB_MAX: u32 = (1 << RV32_CELL_BITS) - 1; -const MAX_INS_CAPACITY: usize = 256; -const ADAPTER_WIDTH: usize = size_of::>(); +const MAX_INS_CAPACITY: usize = 128; type F = BabyBear; @@ -111,17 +114,15 @@ fn set_and_execute( /// passes all constraints. /////////////////////////////////////////////////////////////////////////////////////// -#[test] -fn rand_jal_lui_test() { +#[test_case(JAL, 100)] +#[test_case(LUI, 100)] +fn rand_jal_lui_test(opcode: Rv32JalLuiOpcode, num_ops: usize) { let mut rng = create_seeded_rng(); - let mut tester = VmChipTestBuilder::default(); let (mut chip, bitwise_chip) = create_test_chip(&tester); - let num_tests: usize = 100; - for _ in 0..num_tests { - set_and_execute(&mut tester, &mut chip, &mut rng, JAL, None, None); - set_and_execute(&mut tester, &mut chip, &mut rng, LUI, None, None); + for _ in 0..num_ops { + set_and_execute(&mut tester, &mut chip, &mut rng, opcode, None, None); } let tester = tester.build().load(chip).load(bitwise_chip).finalize(); @@ -131,24 +132,27 @@ fn rand_jal_lui_test() { // NEGATIVE TESTS // // Given a fake trace of a single operation, setup a chip and run the test. We replace -// the write part of the trace and check that the core chip throws the expected error. -// A dummy adaptor is used so memory interactions don't indirectly cause false passes. +// part of the trace and check that the chip throws the expected error. ////////////////////////////////////////////////////////////////////////////////////// +#[derive(Clone, Copy, Default, PartialEq)] +struct JalLuiPrankValues { + pub rd_data: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, + pub imm: Option, + pub is_jal: Option, + pub is_lui: Option, + pub needs_write: Option, +} + #[allow(clippy::too_many_arguments)] fn run_negative_jal_lui_test( opcode: Rv32JalLuiOpcode, initial_imm: Option, initial_pc: Option, - rd_data: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, - imm: Option, - is_jal: Option, - is_lui: Option, - needs_write: Option, - expected_error: VerificationError, + prank_vals: JalLuiPrankValues, + interaction_error: bool, ) { let mut rng = create_seeded_rng(); - let mut tester = VmChipTestBuilder::default(); let (mut chip, bitwise_chip) = create_test_chip(&tester); @@ -161,51 +165,43 @@ fn run_negative_jal_lui_test( initial_pc, ); - let tester = tester.build(); - - let jal_lui_trace_width = chip.trace_width(); - let air = chip.air(); - let mut chip_input = chip.generate_air_proof_input(); - let jal_lui_trace = chip_input.raw.common_main.as_mut().unwrap(); - { - let mut trace_row = jal_lui_trace.row_slice(0).to_vec(); - - let (adapter_row, core_row) = trace_row.split_at_mut(ADAPTER_WIDTH); - + let adapter_width = BaseAir::::width(&chip.air.adapter); + let modify_trace = |trace: &mut DenseMatrix| { + let mut trace_row = trace.row_slice(0).to_vec(); + let (adapter_row, core_row) = trace_row.split_at_mut(adapter_width); let adapter_cols: &mut Rv32CondRdWriteAdapterCols = adapter_row.borrow_mut(); let core_cols: &mut Rv32JalLuiCoreCols = core_row.borrow_mut(); - if let Some(data) = rd_data { + if let Some(data) = prank_vals.rd_data { core_cols.rd_data = data.map(F::from_canonical_u32); } - - if let Some(imm) = imm { + if let Some(imm) = prank_vals.imm { core_cols.imm = if imm < 0 { F::NEG_ONE * F::from_canonical_u32((-imm) as u32) } else { F::from_canonical_u32(imm as u32) }; } - if let Some(is_jal) = is_jal { + if let Some(is_jal) = prank_vals.is_jal { core_cols.is_jal = F::from_bool(is_jal); } - if let Some(is_lui) = is_lui { + if let Some(is_lui) = prank_vals.is_lui { core_cols.is_lui = F::from_bool(is_lui); } - - if let Some(needs_write) = needs_write { + if let Some(needs_write) = prank_vals.needs_write { adapter_cols.needs_write = F::from_bool(needs_write); } - *jal_lui_trace = RowMajorMatrix::new(trace_row, jal_lui_trace_width); - } + *trace = RowMajorMatrix::new(trace_row, trace.width()); + }; disable_debug_builder(); let tester = tester - .load_air_proof_input((air, chip_input)) + .build() + .load_and_prank_trace(chip, modify_trace) .load(bitwise_chip) .finalize(); - tester.simple_test_with_expected_error(expected_error); + tester.simple_test_with_expected_error(get_verification_error(interaction_error)); } #[test] @@ -214,34 +210,35 @@ fn opcode_flag_negative_test() { JAL, None, None, - None, - None, - Some(false), - Some(true), - None, - VerificationError::OodEvaluationMismatch, + JalLuiPrankValues { + is_jal: Some(false), + is_lui: Some(true), + ..Default::default() + }, + false, ); run_negative_jal_lui_test( JAL, None, None, - None, - None, - Some(false), - Some(false), - Some(false), - VerificationError::ChallengePhaseError, + JalLuiPrankValues { + is_jal: Some(false), + is_lui: Some(false), + needs_write: Some(false), + ..Default::default() + }, + true, ); run_negative_jal_lui_test( LUI, None, None, - None, - None, - Some(true), - Some(false), - None, - VerificationError::OodEvaluationMismatch, + JalLuiPrankValues { + is_jal: Some(true), + is_lui: Some(false), + ..Default::default() + }, + false, ); } @@ -251,67 +248,61 @@ fn overflow_negative_tests() { JAL, None, None, - Some([LIMB_MAX, LIMB_MAX, LIMB_MAX, LIMB_MAX]), - None, - None, - None, - None, - VerificationError::OodEvaluationMismatch, + JalLuiPrankValues { + rd_data: Some([LIMB_MAX, LIMB_MAX, LIMB_MAX, LIMB_MAX]), + ..Default::default() + }, + false, ); run_negative_jal_lui_test( LUI, None, None, - Some([LIMB_MAX, LIMB_MAX, LIMB_MAX, LIMB_MAX]), - None, - None, - None, - None, - VerificationError::OodEvaluationMismatch, + JalLuiPrankValues { + rd_data: Some([LIMB_MAX, LIMB_MAX, LIMB_MAX, LIMB_MAX]), + ..Default::default() + }, + false, ); run_negative_jal_lui_test( LUI, None, None, - Some([0, LIMB_MAX, LIMB_MAX, LIMB_MAX + 1]), - None, - None, - None, - None, - VerificationError::OodEvaluationMismatch, + JalLuiPrankValues { + rd_data: Some([0, LIMB_MAX, LIMB_MAX, LIMB_MAX + 1]), + ..Default::default() + }, + false, ); run_negative_jal_lui_test( LUI, None, None, - None, - Some(-1), - None, - None, - None, - VerificationError::OodEvaluationMismatch, + JalLuiPrankValues { + imm: Some(-1), + ..Default::default() + }, + false, ); run_negative_jal_lui_test( LUI, None, None, - None, - Some(-28), - None, - None, - None, - VerificationError::OodEvaluationMismatch, + JalLuiPrankValues { + imm: Some(-28), + ..Default::default() + }, + false, ); run_negative_jal_lui_test( JAL, None, Some(251), - Some([F::NEG_ONE.as_canonical_u32(), 1, 0, 0]), - None, - None, - None, - None, - VerificationError::ChallengePhaseError, + JalLuiPrankValues { + rd_data: Some([F::NEG_ONE.as_canonical_u32(), 1, 0, 0]), + ..Default::default() + }, + true, ); } @@ -320,19 +311,13 @@ fn overflow_negative_tests() { /// /// Ensure that solve functions produce the correct results. /////////////////////////////////////////////////////////////////////////////////////// + #[test] fn execute_roundtrip_sanity_test() { let mut rng = create_seeded_rng(); - let mut tester = VmChipTestBuilder::default(); let (mut chip, _) = create_test_chip(&tester); - let num_tests: usize = 10; - for _ in 0..num_tests { - set_and_execute(&mut tester, &mut chip, &mut rng, JAL, None, None); - set_and_execute(&mut tester, &mut chip, &mut rng, LUI, None, None); - } - set_and_execute( &mut tester, &mut chip, diff --git a/extensions/rv32im/circuit/src/jalr/tests.rs b/extensions/rv32im/circuit/src/jalr/tests.rs index 95831a3928..c7d54b525d 100644 --- a/extensions/rv32im/circuit/src/jalr/tests.rs +++ b/extensions/rv32im/circuit/src/jalr/tests.rs @@ -12,10 +12,11 @@ use openvm_rv32im_transpiler::Rv32JalrOpcode::{self, *}; use openvm_stark_backend::{ p3_air::BaseAir, p3_field::{FieldAlgebra, PrimeField32}, - p3_matrix::{dense::RowMajorMatrix, Matrix}, + p3_matrix::{ + dense::{DenseMatrix, RowMajorMatrix}, + Matrix, + }, utils::disable_debug_builder, - verifier::VerificationError, - Chip, ChipUsageGetter, }; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; @@ -26,6 +27,7 @@ use crate::{ compose, Rv32JalrAdapterAir, Rv32JalrAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, }, jalr::{run_jalr, Rv32JalrChip, Rv32JalrCoreCols, Rv32JalrStep}, + test_utils::get_verification_error, }; const IMM_BITS: usize = 16; @@ -37,6 +39,33 @@ fn into_limbs(num: u32) -> [u32; 4] { array::from_fn(|i| (num >> (8 * i)) & 255) } +fn create_test_chip( + tester: &mut VmChipTestBuilder, +) -> ( + Rv32JalrChip, + SharedBitwiseOperationLookupChip, +) { + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + let range_checker_chip = tester.memory_controller().range_checker.clone(); + + let chip = Rv32JalrChip::::new( + VmAirWrapper::new( + Rv32JalrAdapterAir::new(tester.memory_bridge(), tester.execution_bridge()), + Rv32JalrCoreAir::new(bitwise_bus, range_checker_chip.bus()), + ), + Rv32JalrStep::new( + Rv32JalrAdapterStep::new(), + bitwise_chip.clone(), + range_checker_chip.clone(), + ), + MAX_INS_CAPACITY, + tester.memory_helper(), + ); + + (chip, bitwise_chip) +} + #[allow(clippy::too_many_arguments)] fn set_and_execute( tester: &mut VmChipTestBuilder, @@ -97,27 +126,11 @@ fn set_and_execute( #[test] fn rand_jalr_test() { let mut rng = create_seeded_rng(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().range_checker.clone(); + let (mut chip, bitwise_chip) = create_test_chip(&mut tester); - let mut chip = Rv32JalrChip::::new( - VmAirWrapper::new( - Rv32JalrAdapterAir::new(tester.memory_bridge(), tester.execution_bridge()), - Rv32JalrCoreAir::new(bitwise_bus, range_checker_chip.bus()), - ), - Rv32JalrStep::new( - Rv32JalrAdapterStep::new(), - bitwise_chip.clone(), - range_checker_chip.clone(), - ), - MAX_INS_CAPACITY, - tester.memory_helper(), - ); - - let num_tests: usize = 100; - for _ in 0..num_tests { + let num_ops = 100; + for _ in 0..num_ops { set_and_execute( &mut tester, &mut chip, @@ -130,226 +143,166 @@ fn rand_jalr_test() { ); } - drop(range_checker_chip); - // let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - // tester.simple_test().expect("Verification failed"); + let tester = tester.build().load(chip).load(bitwise_chip).finalize(); + tester.simple_test().expect("Verification failed"); } ////////////////////////////////////////////////////////////////////////////////////// // NEGATIVE TESTS // // Given a fake trace of a single operation, setup a chip and run the test. We replace -// the write part of the trace and check that the core chip throws the expected error. -// A dummy adaptor is used so memory interactions don't indirectly cause false passes. +// part of the trace and check that the chip throws the expected error. ////////////////////////////////////////////////////////////////////////////////////// -// #[allow(clippy::too_many_arguments)] -// fn run_negative_jalr_test( -// opcode: Rv32JalrOpcode, -// initial_pc: Option, -// initial_rs1: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, -// initial_imm: Option, -// initial_imm_sign: Option, -// rd_data: Option<[u32; RV32_REGISTER_NUM_LIMBS - 1]>, -// rs1_data: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, -// to_pc_least_sig_bit: Option, -// to_pc_limbs: Option<[u32; 2]>, -// imm_sign: Option, -// expected_error: VerificationError, -// ) { -// let mut rng = create_seeded_rng(); -// let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); -// let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); -// let mut tester = VmChipTestBuilder::default(); -// let range_checker_chip = tester.memory_controller().range_checker.clone(); - -// let adapter = Rv32JalrAdapterStep::::new( -// tester.execution_bus(), -// tester.program_bus(), -// tester.memory_bridge(), -// ); -// let adapter_width = BaseAir::::width(adapter.air()); -// let inner = Rv32JalrStep::new(bitwise_chip.clone(), range_checker_chip.clone()); -// let mut chip = Rv32JalrChip::::new(adapter, inner, tester.offline_memory_mutex_arc()); - -// set_and_execute( -// &mut tester, -// &mut chip, -// &mut rng, -// opcode, -// initial_imm, -// initial_imm_sign, -// initial_pc, -// initial_rs1, -// ); - -// let tester = tester.build(); - -// let jalr_trace_width = chip.trace_width(); -// let air = chip.air(); -// let mut chip_input = chip.generate_air_proof_input(); -// let jalr_trace = chip_input.raw.common_main.as_mut().unwrap(); -// { -// let mut trace_row = jalr_trace.row_slice(0).to_vec(); - -// let (_, core_row) = trace_row.split_at_mut(adapter_width); - -// let core_cols: &mut Rv32JalrCoreCols = core_row.borrow_mut(); - -// if let Some(data) = rd_data { -// core_cols.rd_data = data.map(F::from_canonical_u32); -// } - -// if let Some(data) = rs1_data { -// core_cols.rs1_data = data.map(F::from_canonical_u32); -// } - -// if let Some(data) = to_pc_least_sig_bit { -// core_cols.to_pc_least_sig_bit = F::from_canonical_u32(data); -// } - -// if let Some(data) = to_pc_limbs { -// core_cols.to_pc_limbs = data.map(F::from_canonical_u32); -// } - -// if let Some(data) = imm_sign { -// core_cols.imm_sign = F::from_canonical_u32(data); -// } - -// *jalr_trace = RowMajorMatrix::new(trace_row, jalr_trace_width); -// } - -// drop(range_checker_chip); -// disable_debug_builder(); -// let tester = tester -// .load_air_proof_input((air, chip_input)) -// .load(bitwise_chip) -// .finalize(); -// tester.simple_test_with_expected_error(expected_error); -// } - -// #[test] -// fn invalid_cols_negative_tests() { -// run_negative_jalr_test( -// JALR, -// None, -// None, -// Some(15362), -// Some(0), -// None, -// None, -// None, -// None, -// Some(1), -// VerificationError::OodEvaluationMismatch, -// ); - -// run_negative_jalr_test( -// JALR, -// None, -// None, -// Some(15362), -// Some(1), -// None, -// None, -// None, -// None, -// Some(0), -// VerificationError::OodEvaluationMismatch, -// ); - -// run_negative_jalr_test( -// JALR, -// None, -// Some([23, 154, 67, 28]), -// Some(42512), -// Some(1), -// None, -// None, -// Some(0), -// None, -// None, -// VerificationError::OodEvaluationMismatch, -// ); -// } - -// #[test] -// fn overflow_negative_tests() { -// run_negative_jalr_test( -// JALR, -// Some(251), -// None, -// None, -// None, -// Some([1, 0, 0]), -// None, -// None, -// None, -// None, -// VerificationError::ChallengePhaseError, -// ); - -// run_negative_jalr_test( -// JALR, -// None, -// Some([0, 0, 0, 0]), -// Some((1 << 15) - 2), -// Some(0), -// None, -// None, -// None, -// Some([ -// (F::NEG_ONE * F::from_canonical_u32((1 << 14) + 1)).as_canonical_u32(), -// 1, -// ]), -// None, -// VerificationError::ChallengePhaseError, -// ); -// } - -/////////////////////////////////////////////////////////////////////////////////////// -/// SANITY TESTS -/// -/// Ensure that solve functions produce the correct results. -/////////////////////////////////////////////////////////////////////////////////////// +#[derive(Clone, Copy, Default, PartialEq)] +struct JalrPrankValues { + pub rd_data: Option<[u32; RV32_REGISTER_NUM_LIMBS - 1]>, + pub rs1_data: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, + pub to_pc_least_sig_bit: Option, + pub to_pc_limbs: Option<[u32; 2]>, + pub imm_sign: Option, +} -#[test] -fn execute_roundtrip_sanity_test() { +#[allow(clippy::too_many_arguments)] +fn run_negative_jalr_test( + opcode: Rv32JalrOpcode, + initial_pc: Option, + initial_rs1: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, + initial_imm: Option, + initial_imm_sign: Option, + prank_vals: JalrPrankValues, + interaction_error: bool, +) { let mut rng = create_seeded_rng(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().range_checker.clone(); - let mut chip = Rv32JalrChip::::new( - VmAirWrapper::new( - Rv32JalrAdapterAir::new(tester.memory_bridge(), tester.execution_bridge()), - Rv32JalrCoreAir::new(bitwise_bus, range_checker_chip.bus()), - ), - Rv32JalrStep::new( - Rv32JalrAdapterStep::new(), - bitwise_chip.clone(), - range_checker_chip.clone(), - ), - MAX_INS_CAPACITY, - tester.memory_helper(), + let (mut chip, bitwise_chip) = create_test_chip(&mut tester); + + set_and_execute( + &mut tester, + &mut chip, + &mut rng, + opcode, + initial_imm, + initial_imm_sign, + initial_pc, + initial_rs1, ); - let num_tests: usize = 10; - for _ in 0..num_tests { - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - JALR, - None, - None, - None, - None, - ); - } + let adapter_width = BaseAir::::width(&chip.air.adapter); + let modify_trace = |trace: &mut DenseMatrix| { + let mut trace_row = trace.row_slice(0).to_vec(); + let (_, core_row) = trace_row.split_at_mut(adapter_width); + let core_cols: &mut Rv32JalrCoreCols = core_row.borrow_mut(); + + if let Some(data) = prank_vals.rd_data { + core_cols.rd_data = data.map(F::from_canonical_u32); + } + if let Some(data) = prank_vals.rs1_data { + core_cols.rs1_data = data.map(F::from_canonical_u32); + } + if let Some(data) = prank_vals.to_pc_least_sig_bit { + core_cols.to_pc_least_sig_bit = F::from_canonical_u32(data); + } + if let Some(data) = prank_vals.to_pc_limbs { + core_cols.to_pc_limbs = data.map(F::from_canonical_u32); + } + if let Some(data) = prank_vals.imm_sign { + core_cols.imm_sign = F::from_canonical_u32(data); + } + + *trace = RowMajorMatrix::new(trace_row, trace.width()); + }; + + disable_debug_builder(); + let tester = tester + .build() + .load_and_prank_trace(chip, modify_trace) + .load(bitwise_chip) + .finalize(); + tester.simple_test_with_expected_error(get_verification_error(interaction_error)); } +#[test] +fn invalid_cols_negative_tests() { + run_negative_jalr_test( + JALR, + None, + None, + Some(15362), + Some(0), + JalrPrankValues { + imm_sign: Some(1), + ..Default::default() + }, + false, + ); + + run_negative_jalr_test( + JALR, + None, + None, + Some(15362), + Some(1), + JalrPrankValues { + imm_sign: Some(0), + ..Default::default() + }, + false, + ); + + run_negative_jalr_test( + JALR, + None, + Some([23, 154, 67, 28]), + Some(42512), + Some(1), + JalrPrankValues { + to_pc_least_sig_bit: Some(0), + ..Default::default() + }, + false, + ); +} + +#[test] +fn overflow_negative_tests() { + run_negative_jalr_test( + JALR, + Some(251), + None, + None, + None, + JalrPrankValues { + rd_data: Some([1, 0, 0]), + ..Default::default() + }, + true, + ); + + run_negative_jalr_test( + JALR, + None, + Some([0, 0, 0, 0]), + Some((1 << 15) - 2), + Some(0), + JalrPrankValues { + to_pc_limbs: Some([ + (F::NEG_ONE * F::from_canonical_u32((1 << 14) + 1)).as_canonical_u32(), + 1, + ]), + ..Default::default() + }, + true, + ); +} + +/////////////////////////////////////////////////////////////////////////////////////// +/// SANITY TESTS +/// +/// Ensure that solve functions produce the correct results. +/////////////////////////////////////////////////////////////////////////////////////// + #[test] fn run_jalr_sanity_test() { let opcode = JALR; diff --git a/extensions/rv32im/circuit/src/less_than/tests.rs b/extensions/rv32im/circuit/src/less_than/tests.rs index c22d6a17c5..52ac6f67c4 100644 --- a/extensions/rv32im/circuit/src/less_than/tests.rs +++ b/extensions/rv32im/circuit/src/less_than/tests.rs @@ -1,17 +1,17 @@ -use std::borrow::BorrowMut; +use std::{array, borrow::BorrowMut}; use openvm_circuit::{ arch::{ - testing::{TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - NewVmChipWrapper, VmAirWrapper, + testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, + InstructionExecutor, VmAirWrapper, }, - utils::{generate_long_number, i32_to_f}, + utils::i32_to_f, }; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, }; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; -use openvm_rv32im_transpiler::LessThanOpcode; +use openvm_instructions::LocalOpcode; +use openvm_rv32im_transpiler::LessThanOpcode::{self, *}; use openvm_stark_backend::{ p3_air::BaseAir, p3_field::{FieldAlgebra, PrimeField32}, @@ -20,21 +20,20 @@ use openvm_stark_backend::{ Matrix, }, utils::disable_debug_builder, - verifier::VerificationError, - ChipUsageGetter, }; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; -use rand::Rng; +use rand::{rngs::StdRng, Rng}; +use test_case::test_case; -use super::{ - core::run_less_than, LessThanCoreAir, LessThanStep, Rv32LessThanChip, Rv32LessThanStep, -}; +use super::{core::run_less_than, LessThanCoreAir, LessThanStep, Rv32LessThanChip}; use crate::{ adapters::{ Rv32BaseAluAdapterAir, Rv32BaseAluAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, }, less_than::LessThanCoreCols, - test_utils::{generate_rv32_is_type_immediate, rv32_rand_write_register_or_imm}, + test_utils::{ + generate_rv32_is_type_immediate, get_verification_error, rv32_rand_write_register_or_imm, + }, }; type F = BabyBear; @@ -69,6 +68,48 @@ fn create_test_chip( (chip, bitwise_chip) } + +#[allow(clippy::too_many_arguments)] +fn set_and_execute>( + tester: &mut VmChipTestBuilder, + chip: &mut E, + rng: &mut StdRng, + opcode: LessThanOpcode, + b: Option<[u8; RV32_REGISTER_NUM_LIMBS]>, + is_imm: Option, + c: Option<[u8; RV32_REGISTER_NUM_LIMBS]>, +) { + let b = b.unwrap_or(array::from_fn(|_| rng.gen_range(0..=u8::MAX))); + let (c_imm, c) = if is_imm.unwrap_or(rng.gen_bool(0.5)) { + let (imm, c) = if let Some(c) = c { + ((u32::from_le_bytes(c) & 0xFFFFFF) as usize, c) + } else { + generate_rv32_is_type_immediate(rng) + }; + (Some(imm), c) + } else { + ( + None, + c.unwrap_or(array::from_fn(|_| rng.gen_range(0..=u8::MAX))), + ) + }; + + let (instruction, rd) = rv32_rand_write_register_or_imm( + tester, + b, + c, + c_imm, + opcode.global_opcode().as_usize(), + rng, + ); + tester.execute(chip, &instruction); + + let (cmp, _, _, _) = run_less_than::(opcode, &b, &c); + let mut a = [F::ZERO; RV32_REGISTER_NUM_LIMBS]; + a[0] = F::from_bool(cmp); + assert_eq!(a, tester.read::(1, rd)); +} + ////////////////////////////////////////////////////////////////////////////////////// // POSITIVE TESTS // @@ -76,364 +117,295 @@ fn create_test_chip( // passes all constraints. ////////////////////////////////////////////////////////////////////////////////////// +#[test_case(SLT, 100)] +#[test_case(SLTU, 100)] fn run_rv32_lt_rand_test(opcode: LessThanOpcode, num_ops: usize) { let mut rng = create_seeded_rng(); - let mut tester = VmChipTestBuilder::default(); let (mut chip, bitwise_chip) = create_test_chip(&tester); - for _ in 0..num_ops { - let b = generate_long_number::(&mut rng) - .map(|x| x as u8); - let (c_imm, c) = if rng.gen_bool(0.5) { - ( - None, - generate_long_number::(&mut rng) - .map(|x| x as u8), - ) - } else { - let (imm, c) = generate_rv32_is_type_immediate(&mut rng); - (Some(imm), c) - }; - let (instruction, rd) = rv32_rand_write_register_or_imm( - &mut tester, - b, - c, - c_imm, - opcode.global_opcode().as_usize(), - &mut rng, - ); - tester.execute(&mut chip, &instruction); - - let (cmp, _, _, _) = - run_less_than::(opcode, &b, &c); - let mut a = [F::ZERO; RV32_REGISTER_NUM_LIMBS]; - a[0] = F::from_bool(cmp); - assert_eq!(a, tester.read::(1, rd)); + for _ in 0..num_ops { + set_and_execute(&mut tester, &mut chip, &mut rng, opcode, None, None, None); } // Test special case where b = c let b = [101, 128, 202, 255]; - let (instruction, _) = rv32_rand_write_register_or_imm( + set_and_execute( &mut tester, - b, - b, - None, - opcode.global_opcode().as_usize(), + &mut chip, &mut rng, + opcode, + Some(b), + Some(false), + Some(b), ); - tester.execute(&mut chip, &instruction); let b = [36, 0, 0, 0]; - let (instruction, _) = rv32_rand_write_register_or_imm( + set_and_execute( &mut tester, - b, - b, - Some(36), - opcode.global_opcode().as_usize(), + &mut chip, &mut rng, + opcode, + Some(b), + Some(true), + Some(b), ); - tester.execute(&mut chip, &instruction); let tester = tester.build().load(chip).load(bitwise_chip).finalize(); tester.simple_test().expect("Verification failed"); } +////////////////////////////////////////////////////////////////////////////////////// +// NEGATIVE TESTS +// +// Given a fake trace of a single operation, setup a chip and run the test. We replace +// part of the trace and check that the chip throws the expected error. +////////////////////////////////////////////////////////////////////////////////////// + +#[derive(Clone, Copy, Default, PartialEq)] +struct LessThanPrankValues { + pub b_msb: Option, + pub c_msb: Option, + pub diff_marker: Option<[u32; NUM_LIMBS]>, + pub diff_val: Option, +} + +#[allow(clippy::too_many_arguments)] +fn run_negative_less_than_test( + opcode: LessThanOpcode, + b: [u8; RV32_REGISTER_NUM_LIMBS], + c: [u8; RV32_REGISTER_NUM_LIMBS], + prank_cmp_result: bool, + prank_vals: LessThanPrankValues, + interaction_error: bool, +) { + let mut rng = create_seeded_rng(); + let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); + let (mut chip, bitwise_chip) = create_test_chip(&tester); + + set_and_execute( + &mut tester, + &mut chip, + &mut rng, + opcode, + Some(b), + Some(false), + Some(c), + ); + + let adapter_width = BaseAir::::width(&chip.air.adapter); + let modify_trace = |trace: &mut DenseMatrix| { + let mut values = trace.row_slice(0).to_vec(); + let cols: &mut LessThanCoreCols = + values.split_at_mut(adapter_width).1.borrow_mut(); + + if let Some(b_msb) = prank_vals.b_msb { + cols.b_msb_f = i32_to_f(b_msb); + } + if let Some(c_msb) = prank_vals.c_msb { + cols.c_msb_f = i32_to_f(c_msb); + } + if let Some(diff_marker) = prank_vals.diff_marker { + cols.diff_marker = diff_marker.map(F::from_canonical_u32); + } + if let Some(diff_val) = prank_vals.diff_val { + cols.diff_val = F::from_canonical_u32(diff_val); + } + cols.cmp_result = F::from_bool(prank_cmp_result); + + *trace = RowMajorMatrix::new(values, trace.width()); + }; + + disable_debug_builder(); + let tester = tester + .build() + .load_and_prank_trace(chip, modify_trace) + .load(bitwise_chip) + .finalize(); + tester.simple_test_with_expected_error(get_verification_error(interaction_error)); +} + #[test] -fn rv32_slt_rand_test() { - run_rv32_lt_rand_test(LessThanOpcode::SLT, 100); +fn rv32_lt_wrong_false_cmp_negative_test() { + let b = [145, 34, 25, 205]; + let c = [73, 35, 25, 205]; + let prank_vals = Default::default(); + run_negative_less_than_test(SLT, b, c, false, prank_vals, false); + run_negative_less_than_test(SLTU, b, c, false, prank_vals, false); } #[test] -fn rv32_sltu_rand_test() { - run_rv32_lt_rand_test(LessThanOpcode::SLTU, 100); +fn rv32_lt_wrong_true_cmp_negative_test() { + let b = [73, 35, 25, 205]; + let c = [145, 34, 25, 205]; + let prank_vals = Default::default(); + run_negative_less_than_test(SLT, b, c, true, prank_vals, false); + run_negative_less_than_test(SLTU, b, c, true, prank_vals, false); } -////////////////////////////////////////////////////////////////////////////////////// -// NEGATIVE TESTS -// -// Given a fake trace of a single operation, setup a chip and run the test. We replace -// the write part of the trace and check that the core chip throws the expected error. -// A dummy adapter is used so memory interactions don't indirectly cause false passes. -////////////////////////////////////////////////////////////////////////////////////// +#[test] +fn rv32_lt_wrong_eq_negative_test() { + let b = [73, 35, 25, 205]; + let c = [73, 35, 25, 205]; + let prank_vals = Default::default(); + run_negative_less_than_test(SLT, b, c, true, prank_vals, false); + run_negative_less_than_test(SLTU, b, c, true, prank_vals, false); +} + +#[test] +fn rv32_lt_fake_diff_val_negative_test() { + let b = [145, 34, 25, 205]; + let c = [73, 35, 25, 205]; + let prank_vals = LessThanPrankValues { + diff_val: Some(F::NEG_ONE.as_canonical_u32()), + ..Default::default() + }; + run_negative_less_than_test(SLT, b, c, false, prank_vals, true); + run_negative_less_than_test(SLTU, b, c, false, prank_vals, true); +} + +#[test] +fn rv32_lt_zero_diff_val_negative_test() { + let b = [145, 34, 25, 205]; + let c = [73, 35, 25, 205]; + let prank_vals = LessThanPrankValues { + diff_marker: Some([0, 0, 1, 0]), + diff_val: Some(0), + ..Default::default() + }; + run_negative_less_than_test(SLT, b, c, false, prank_vals, true); + run_negative_less_than_test(SLTU, b, c, false, prank_vals, true); +} + +#[test] +fn rv32_lt_fake_diff_marker_negative_test() { + let b = [145, 34, 25, 205]; + let c = [73, 35, 25, 205]; + let prank_vals = LessThanPrankValues { + diff_marker: Some([1, 0, 0, 0]), + diff_val: Some(72), + ..Default::default() + }; + run_negative_less_than_test(SLT, b, c, false, prank_vals, false); + run_negative_less_than_test(SLTU, b, c, false, prank_vals, false); +} + +#[test] +fn rv32_lt_zero_diff_marker_negative_test() { + let b = [145, 34, 25, 205]; + let c = [73, 35, 25, 205]; + let prank_vals = LessThanPrankValues { + diff_marker: Some([0, 0, 0, 0]), + diff_val: Some(0), + ..Default::default() + }; + run_negative_less_than_test(SLT, b, c, false, prank_vals, false); + run_negative_less_than_test(SLTU, b, c, false, prank_vals, false); +} + +#[test] +fn rv32_slt_wrong_b_msb_negative_test() { + let b = [145, 34, 25, 205]; + let c = [73, 35, 25, 205]; + let prank_vals = LessThanPrankValues { + b_msb: Some(206), + diff_marker: Some([0, 0, 0, 1]), + diff_val: Some(1), + ..Default::default() + }; + run_negative_less_than_test(SLT, b, c, false, prank_vals, false); +} -// type Rv32LessThanTestChip = -// VmChipWrapper, LessThanStep>; - -// #[derive(Clone, Copy, Default, PartialEq)] -// struct LessThanPrankValues { -// pub b_msb: Option, -// pub c_msb: Option, -// pub diff_marker: Option<[u32; NUM_LIMBS]>, -// pub diff_val: Option, -// } - -// #[allow(clippy::too_many_arguments)] -// fn run_rv32_lt_negative_test( -// opcode: LessThanOpcode, -// b: [u8; RV32_REGISTER_NUM_LIMBS], -// c: [u8; RV32_REGISTER_NUM_LIMBS], -// cmp_result: bool, -// prank_vals: LessThanPrankValues, -// interaction_error: bool, -// ) { -// let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); -// let (mut chip, bitwise_chip) = create_test_chip(&tester); -// tester.execute( -// &mut chip, -// &Instruction::from_usize(opcode.global_opcode(), [0, 0, 0, 1, 1]), -// ); - -// let trace_width = chip.trace_width(); -// let adapter_width = BaseAir::::width(&chip.air.adapter); -// let (_, _, b_sign, c_sign) = -// run_less_than::(opcode, &b, &c); - -// if prank_vals != LessThanPrankValues::default() { -// debug_assert!(prank_vals.diff_val.is_some()); -// let b_msb = prank_vals.b_msb.unwrap_or( -// b[RV32_REGISTER_NUM_LIMBS - 1] as i32 - if b_sign { 1 << RV32_CELL_BITS } else { 0 }, -// ); -// let c_msb = prank_vals.c_msb.unwrap_or( -// c[RV32_REGISTER_NUM_LIMBS - 1] as i32 - if c_sign { 1 << RV32_CELL_BITS } else { 0 }, -// ); -// let sign_offset = if opcode == LessThanOpcode::SLT { -// 1 << (RV32_CELL_BITS - 1) -// } else { -// 0 -// }; - -// bitwise_chip.clear(); -// bitwise_chip.request_range( -// (b_msb + sign_offset) as u8 as u32, -// (c_msb + sign_offset) as u8 as u32, -// ); - -// let diff_val = prank_vals -// .diff_val -// .unwrap() -// .clamp(0, (1 << RV32_CELL_BITS) - 1); -// if diff_val > 0 { -// bitwise_chip.request_range(diff_val - 1, 0); -// } -// }; - -// let modify_trace = |trace: &mut DenseMatrix| { -// let mut values = trace.row_slice(0).to_vec(); -// let cols: &mut LessThanCoreCols = -// values.split_at_mut(adapter_width).1.borrow_mut(); - -// if let Some(b_msb) = prank_vals.b_msb { -// cols.b_msb_f = i32_to_f(b_msb); -// } -// if let Some(c_msb) = prank_vals.c_msb { -// cols.c_msb_f = i32_to_f(c_msb); -// } -// if let Some(diff_marker) = prank_vals.diff_marker { -// cols.diff_marker = diff_marker.map(F::from_canonical_u32); -// } -// if let Some(diff_val) = prank_vals.diff_val { -// cols.diff_val = F::from_canonical_u32(diff_val); -// } -// cols.cmp_result = F::from_bool(cmp_result); - -// *trace = RowMajorMatrix::new(values, trace_width); -// }; - -// disable_debug_builder(); -// let tester = tester -// .build() -// .load_and_prank_trace(chip, modify_trace) -// .load(bitwise_chip) -// .finalize(); -// tester.simple_test_with_expected_error(if interaction_error { -// VerificationError::ChallengePhaseError -// } else { -// VerificationError::OodEvaluationMismatch -// }); -// } - -// #[test] -// fn rv32_lt_wrong_false_cmp_negative_test() { -// let b = [145, 34, 25, 205]; -// let c = [73, 35, 25, 205]; -// let prank_vals = Default::default(); -// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, false); -// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, false); -// } - -// #[test] -// fn rv32_lt_wrong_true_cmp_negative_test() { -// let b = [73, 35, 25, 205]; -// let c = [145, 34, 25, 205]; -// let prank_vals = Default::default(); -// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, true, prank_vals, false); -// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, true, prank_vals, false); -// } - -// #[test] -// fn rv32_lt_wrong_eq_negative_test() { -// let b = [73, 35, 25, 205]; -// let c = [73, 35, 25, 205]; -// let prank_vals = Default::default(); -// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, true, prank_vals, false); -// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, true, prank_vals, false); -// } - -// #[test] -// fn rv32_lt_fake_diff_val_negative_test() { -// let b = [145, 34, 25, 205]; -// let c = [73, 35, 25, 205]; -// let prank_vals = LessThanPrankValues { -// diff_val: Some(F::NEG_ONE.as_canonical_u32()), -// ..Default::default() -// }; -// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, true); -// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, true); -// } - -// #[test] -// fn rv32_lt_zero_diff_val_negative_test() { -// let b = [145, 34, 25, 205]; -// let c = [73, 35, 25, 205]; -// let prank_vals = LessThanPrankValues { -// diff_marker: Some([0, 0, 1, 0]), -// diff_val: Some(0), -// ..Default::default() -// }; -// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, true); -// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, true); -// } - -// #[test] -// fn rv32_lt_fake_diff_marker_negative_test() { -// let b = [145, 34, 25, 205]; -// let c = [73, 35, 25, 205]; -// let prank_vals = LessThanPrankValues { -// diff_marker: Some([1, 0, 0, 0]), -// diff_val: Some(72), -// ..Default::default() -// }; -// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, false); -// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, false); -// } - -// #[test] -// fn rv32_lt_zero_diff_marker_negative_test() { -// let b = [145, 34, 25, 205]; -// let c = [73, 35, 25, 205]; -// let prank_vals = LessThanPrankValues { -// diff_marker: Some([0, 0, 0, 0]), -// diff_val: Some(0), -// ..Default::default() -// }; -// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, false); -// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, false); -// } - -// #[test] -// fn rv32_slt_wrong_b_msb_negative_test() { -// let b = [145, 34, 25, 205]; -// let c = [73, 35, 25, 205]; -// let prank_vals = LessThanPrankValues { -// b_msb: Some(206), -// diff_marker: Some([0, 0, 0, 1]), -// diff_val: Some(1), -// ..Default::default() -// }; -// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, false); -// } - -// #[test] -// fn rv32_slt_wrong_b_msb_sign_negative_test() { -// let b = [145, 34, 25, 205]; -// let c = [73, 35, 25, 205]; -// let prank_vals = LessThanPrankValues { -// b_msb: Some(205), -// diff_marker: Some([0, 0, 0, 1]), -// diff_val: Some(256), -// ..Default::default() -// }; -// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, false, prank_vals, true); -// } - -// #[test] -// fn rv32_slt_wrong_c_msb_negative_test() { -// let b = [145, 36, 25, 205]; -// let c = [73, 35, 25, 205]; -// let prank_vals = LessThanPrankValues { -// c_msb: Some(204), -// diff_marker: Some([0, 0, 0, 1]), -// diff_val: Some(1), -// ..Default::default() -// }; -// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, true, prank_vals, false); -// } - -// #[test] -// fn rv32_slt_wrong_c_msb_sign_negative_test() { -// let b = [145, 36, 25, 205]; -// let c = [73, 35, 25, 205]; -// let prank_vals = LessThanPrankValues { -// c_msb: Some(205), -// diff_marker: Some([0, 0, 0, 1]), -// diff_val: Some(256), -// ..Default::default() -// }; -// run_rv32_lt_negative_test(LessThanOpcode::SLT, b, c, true, prank_vals, true); -// } - -// #[test] -// fn rv32_sltu_wrong_b_msb_negative_test() { -// let b = [145, 36, 25, 205]; -// let c = [73, 35, 25, 205]; -// let prank_vals = LessThanPrankValues { -// b_msb: Some(204), -// diff_marker: Some([0, 0, 0, 1]), -// diff_val: Some(1), -// ..Default::default() -// }; -// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, true, prank_vals, false); -// } - -// #[test] -// fn rv32_sltu_wrong_b_msb_sign_negative_test() { -// let b = [145, 36, 25, 205]; -// let c = [73, 35, 25, 205]; -// let prank_vals = LessThanPrankValues { -// b_msb: Some(-51), -// diff_marker: Some([0, 0, 0, 1]), -// diff_val: Some(256), -// ..Default::default() -// }; -// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, true, prank_vals, true); -// } - -// #[test] -// fn rv32_sltu_wrong_c_msb_negative_test() { -// let b = [145, 34, 25, 205]; -// let c = [73, 35, 25, 205]; -// let prank_vals = LessThanPrankValues { -// c_msb: Some(204), -// diff_marker: Some([0, 0, 0, 1]), -// diff_val: Some(1), -// ..Default::default() -// }; -// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, false); -// } - -// #[test] -// fn rv32_sltu_wrong_c_msb_sign_negative_test() { -// let b = [145, 34, 25, 205]; -// let c = [73, 35, 25, 205]; -// let prank_vals = LessThanPrankValues { -// c_msb: Some(-51), -// diff_marker: Some([0, 0, 0, 1]), -// diff_val: Some(256), -// ..Default::default() -// }; -// run_rv32_lt_negative_test(LessThanOpcode::SLTU, b, c, false, prank_vals, true); -// } +#[test] +fn rv32_slt_wrong_b_msb_sign_negative_test() { + let b = [145, 34, 25, 205]; + let c = [73, 35, 25, 205]; + let prank_vals = LessThanPrankValues { + b_msb: Some(205), + diff_marker: Some([0, 0, 0, 1]), + diff_val: Some(256), + ..Default::default() + }; + run_negative_less_than_test(SLT, b, c, false, prank_vals, true); +} + +#[test] +fn rv32_slt_wrong_c_msb_negative_test() { + let b = [145, 36, 25, 205]; + let c = [73, 35, 25, 205]; + let prank_vals = LessThanPrankValues { + c_msb: Some(204), + diff_marker: Some([0, 0, 0, 1]), + diff_val: Some(1), + ..Default::default() + }; + run_negative_less_than_test(SLT, b, c, true, prank_vals, false); +} + +#[test] +fn rv32_slt_wrong_c_msb_sign_negative_test() { + let b = [145, 36, 25, 205]; + let c = [73, 35, 25, 205]; + let prank_vals = LessThanPrankValues { + c_msb: Some(205), + diff_marker: Some([0, 0, 0, 1]), + diff_val: Some(256), + ..Default::default() + }; + run_negative_less_than_test(SLT, b, c, true, prank_vals, true); +} + +#[test] +fn rv32_sltu_wrong_b_msb_negative_test() { + let b = [145, 36, 25, 205]; + let c = [73, 35, 25, 205]; + let prank_vals = LessThanPrankValues { + b_msb: Some(204), + diff_marker: Some([0, 0, 0, 1]), + diff_val: Some(1), + ..Default::default() + }; + run_negative_less_than_test(SLTU, b, c, true, prank_vals, false); +} + +#[test] +fn rv32_sltu_wrong_b_msb_sign_negative_test() { + let b = [145, 36, 25, 205]; + let c = [73, 35, 25, 205]; + let prank_vals = LessThanPrankValues { + b_msb: Some(-51), + diff_marker: Some([0, 0, 0, 1]), + diff_val: Some(256), + ..Default::default() + }; + run_negative_less_than_test(SLTU, b, c, true, prank_vals, true); +} + +#[test] +fn rv32_sltu_wrong_c_msb_negative_test() { + let b = [145, 34, 25, 205]; + let c = [73, 35, 25, 205]; + let prank_vals = LessThanPrankValues { + c_msb: Some(204), + diff_marker: Some([0, 0, 0, 1]), + diff_val: Some(1), + ..Default::default() + }; + run_negative_less_than_test(SLTU, b, c, false, prank_vals, false); +} + +#[test] +fn rv32_sltu_wrong_c_msb_sign_negative_test() { + let b = [145, 34, 25, 205]; + let c = [73, 35, 25, 205]; + let prank_vals = LessThanPrankValues { + c_msb: Some(-51), + diff_marker: Some([0, 0, 0, 1]), + diff_val: Some(256), + ..Default::default() + }; + run_negative_less_than_test(SLTU, b, c, false, prank_vals, true); +} /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS @@ -446,7 +418,7 @@ fn run_sltu_sanity_test() { let x: [u8; RV32_REGISTER_NUM_LIMBS] = [145, 34, 25, 205]; let y: [u8; RV32_REGISTER_NUM_LIMBS] = [73, 35, 25, 205]; let (cmp_result, diff_idx, x_sign, y_sign) = - run_less_than::(LessThanOpcode::SLTU, &x, &y); + run_less_than::(SLTU, &x, &y); assert!(cmp_result); assert_eq!(diff_idx, 1); assert!(!x_sign); // unsigned @@ -458,7 +430,7 @@ fn run_slt_same_sign_sanity_test() { let x: [u8; RV32_REGISTER_NUM_LIMBS] = [145, 34, 25, 205]; let y: [u8; RV32_REGISTER_NUM_LIMBS] = [73, 35, 25, 205]; let (cmp_result, diff_idx, x_sign, y_sign) = - run_less_than::(LessThanOpcode::SLT, &x, &y); + run_less_than::(SLT, &x, &y); assert!(cmp_result); assert_eq!(diff_idx, 1); assert!(x_sign); // negative @@ -470,7 +442,7 @@ fn run_slt_diff_sign_sanity_test() { let x: [u8; RV32_REGISTER_NUM_LIMBS] = [45, 35, 25, 55]; let y: [u8; RV32_REGISTER_NUM_LIMBS] = [173, 34, 25, 205]; let (cmp_result, diff_idx, x_sign, y_sign) = - run_less_than::(LessThanOpcode::SLT, &x, &y); + run_less_than::(SLT, &x, &y); assert!(!cmp_result); assert_eq!(diff_idx, 3); assert!(!x_sign); // positive @@ -481,7 +453,7 @@ fn run_slt_diff_sign_sanity_test() { fn run_less_than_equal_sanity_test() { let x: [u8; RV32_REGISTER_NUM_LIMBS] = [45, 35, 25, 55]; let (cmp_result, diff_idx, x_sign, y_sign) = - run_less_than::(LessThanOpcode::SLT, &x, &x); + run_less_than::(SLT, &x, &x); assert!(!cmp_result); assert_eq!(diff_idx, RV32_REGISTER_NUM_LIMBS); assert!(!x_sign); // positive diff --git a/extensions/rv32im/circuit/src/load_sign_extend/tests.rs b/extensions/rv32im/circuit/src/load_sign_extend/tests.rs index 658884f99c..a09edec6b6 100644 --- a/extensions/rv32im/circuit/src/load_sign_extend/tests.rs +++ b/extensions/rv32im/circuit/src/load_sign_extend/tests.rs @@ -14,10 +14,10 @@ use openvm_stark_backend::{ Matrix, }, utils::disable_debug_builder, - verifier::VerificationError, }; use openvm_stark_sdk::{config::setup_tracing, p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; +use test_case::test_case; use super::{run_write_data_sign_extend, LoadSignExtendCoreAir}; use crate::{ @@ -26,11 +26,12 @@ use crate::{ RV32_REGISTER_NUM_LIMBS, }, load_sign_extend::LoadSignExtendCoreCols, + test_utils::get_verification_error, LoadSignExtendStep, Rv32LoadSignExtendChip, }; const IMM_BITS: usize = 16; -const MAX_INS_CAPACITY: usize = 256; +const MAX_INS_CAPACITY: usize = 128; type F = BabyBear; @@ -38,6 +39,29 @@ fn into_limbs(num: u32) -> [u32; array::from_fn(|i| (num >> (LIMB_BITS * i)) & ((1 << LIMB_BITS) - 1)) } +fn create_test_chip(tester: &mut VmChipTestBuilder) -> Rv32LoadSignExtendChip { + let range_checker_chip = tester.memory_controller().range_checker.clone(); + let chip = Rv32LoadSignExtendChip::::new( + VmAirWrapper::new( + Rv32LoadStoreAdapterAir::new( + tester.memory_bridge(), + tester.execution_bridge(), + range_checker_chip.bus(), + tester.address_bits(), + ), + LoadSignExtendCoreAir::new(range_checker_chip.bus()), + ), + LoadSignExtendStep::new( + Rv32LoadStoreAdapterStep::new(tester.address_bits()), + range_checker_chip.clone(), + ), + MAX_INS_CAPACITY, + tester.memory_helper(), + ); + + chip +} + #[allow(clippy::too_many_arguments)] fn set_and_execute( tester: &mut VmChipTestBuilder, @@ -121,49 +145,20 @@ fn set_and_execute( /// Randomly generate computations and execute, ensuring that the generated trace /// passes all constraints. /////////////////////////////////////////////////////////////////////////////////////// -#[test] -fn rand_load_sign_extend_test() { +#[test_case(LOADB, 100)] +#[test_case(LOADH, 100)] +fn rand_load_sign_extend_test(opcode: Rv32LoadStoreOpcode, num_ops: usize) { setup_tracing(); let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().range_checker.clone(); - - let mut chip = Rv32LoadSignExtendChip::::new( - VmAirWrapper::new( - Rv32LoadStoreAdapterAir::new( - tester.memory_bridge(), - tester.execution_bridge(), - range_checker_chip.bus(), - tester.address_bits(), - ), - LoadSignExtendCoreAir::new(range_checker_chip.bus()), - ), - LoadSignExtendStep::new( - Rv32LoadStoreAdapterStep::new(tester.address_bits()), - range_checker_chip, - ), - MAX_INS_CAPACITY, - tester.memory_helper(), - ); - - let num_tests: usize = 1; - for _ in 0..num_tests { - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - LOADB, - None, - None, - None, - None, - ); + let mut chip = create_test_chip(&mut tester); + for _ in 0..num_ops { set_and_execute( &mut tester, &mut chip, &mut rng, - LOADH, + opcode, None, None, None, @@ -179,45 +174,29 @@ fn rand_load_sign_extend_test() { // NEGATIVE TESTS // // Given a fake trace of a single operation, setup a chip and run the test. We replace -// the write part of the trace and check that the core chip throws the expected error. -// A dummy adaptor is used so memory interactions don't indirectly cause false passes. +// part of the trace and check that the chip throws the expected error. ////////////////////////////////////////////////////////////////////////////////////// -#[allow(clippy::too_many_arguments)] -fn run_negative_loadstore_test( - opcode: Rv32LoadStoreOpcode, - read_data: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, +#[derive(Clone, Copy, Default, PartialEq)] +struct LoadSignExtPrankValues { data_most_sig_bit: Option, shift_most_sig_bit: Option, opcode_flags: Option<[bool; 3]>, +} + +#[allow(clippy::too_many_arguments)] +fn run_negative_load_sign_extend_test( + opcode: Rv32LoadStoreOpcode, + read_data: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, rs1: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, imm: Option, imm_sign: Option, - expected_error: VerificationError, + prank_vals: LoadSignExtPrankValues, + interaction_error: bool, ) { let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().range_checker.clone(); - - let mut chip = Rv32LoadSignExtendChip::::new( - VmAirWrapper::new( - Rv32LoadStoreAdapterAir::new( - tester.memory_bridge(), - tester.execution_bridge(), - range_checker_chip.bus(), - tester.address_bits(), - ), - LoadSignExtendCoreAir::new(range_checker_chip.bus()), - ), - LoadSignExtendStep::new( - Rv32LoadStoreAdapterStep::new(tester.address_bits()), - range_checker_chip, - ), - MAX_INS_CAPACITY, - tester.memory_helper(), - ); - - let adapter_width = BaseAir::::width(&chip.air.adapter); + let mut chip = create_test_chip(&mut tester); set_and_execute( &mut tester, @@ -230,30 +209,28 @@ fn run_negative_loadstore_test( imm_sign, ); + let adapter_width = BaseAir::::width(&chip.air.adapter); let modify_trace = |trace: &mut DenseMatrix| { let mut trace_row = trace.row_slice(0).to_vec(); - let (_, core_row) = trace_row.split_at_mut(adapter_width); let core_cols: &mut LoadSignExtendCoreCols = core_row.borrow_mut(); - if let Some(shifted_read_data) = read_data { core_cols.shifted_read_data = shifted_read_data.map(F::from_canonical_u32); } - - if let Some(data_most_sig_bit) = data_most_sig_bit { + if let Some(data_most_sig_bit) = prank_vals.data_most_sig_bit { core_cols.data_most_sig_bit = F::from_canonical_u32(data_most_sig_bit); } - if let Some(shift_most_sig_bit) = shift_most_sig_bit { + if let Some(shift_most_sig_bit) = prank_vals.shift_most_sig_bit { core_cols.shift_most_sig_bit = F::from_canonical_u32(shift_most_sig_bit); } - - if let Some(opcode_flags) = opcode_flags { + if let Some(opcode_flags) = prank_vals.opcode_flags { core_cols.opcode_loadb_flag0 = F::from_bool(opcode_flags[0]); core_cols.opcode_loadb_flag1 = F::from_bool(opcode_flags[1]); core_cols.opcode_loadh_flag = F::from_bool(opcode_flags[2]); } + *trace = RowMajorMatrix::new(trace_row, trace.width()); }; @@ -262,45 +239,48 @@ fn run_negative_loadstore_test( .build() .load_and_prank_trace(chip, modify_trace) .finalize(); - tester.simple_test_with_expected_error(expected_error); + tester.simple_test_with_expected_error(get_verification_error(interaction_error)); } #[test] fn loadstore_negative_tests() { - run_negative_loadstore_test( + run_negative_load_sign_extend_test( LOADB, Some([233, 187, 145, 238]), - Some(0), None, None, None, - None, - None, - VerificationError::ChallengePhaseError, + LoadSignExtPrankValues { + data_most_sig_bit: Some(0), + ..Default::default() + }, + true, ); - run_negative_loadstore_test( + run_negative_load_sign_extend_test( LOADH, None, - None, - Some(0), - None, Some([202, 109, 183, 26]), Some(31212), None, - VerificationError::ChallengePhaseError, + LoadSignExtPrankValues { + shift_most_sig_bit: Some(0), + ..Default::default() + }, + true, ); - run_negative_loadstore_test( + run_negative_load_sign_extend_test( LOADB, None, - None, - None, - Some([true, false, false]), Some([250, 132, 77, 5]), Some(47741), None, - VerificationError::ChallengePhaseError, + LoadSignExtPrankValues { + opcode_flags: Some([true, false, false]), + ..Default::default() + }, + true, ); } @@ -309,54 +289,6 @@ fn loadstore_negative_tests() { /// /// Ensure that solve functions produce the correct results. /////////////////////////////////////////////////////////////////////////////////////// -#[test] -fn execute_roundtrip_sanity_test() { - let mut rng = create_seeded_rng(); - let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().range_checker.clone(); - - let mut chip = Rv32LoadSignExtendChip::::new( - VmAirWrapper::new( - Rv32LoadStoreAdapterAir::new( - tester.memory_bridge(), - tester.execution_bridge(), - range_checker_chip.bus(), - tester.address_bits(), - ), - LoadSignExtendCoreAir::new(range_checker_chip.bus()), - ), - LoadSignExtendStep::new( - Rv32LoadStoreAdapterStep::new(tester.address_bits()), - range_checker_chip, - ), - MAX_INS_CAPACITY, - tester.memory_helper(), - ); - - let num_tests: usize = 10; - for _ in 0..num_tests { - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - LOADB, - None, - None, - None, - None, - ); - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - LOADH, - None, - None, - None, - None, - ); - } -} #[test] fn solve_loadh_extend_sign_sanity_test() { diff --git a/extensions/rv32im/circuit/src/loadstore/tests.rs b/extensions/rv32im/circuit/src/loadstore/tests.rs index 0dc4399b4b..72821c2e37 100644 --- a/extensions/rv32im/circuit/src/loadstore/tests.rs +++ b/extensions/rv32im/circuit/src/loadstore/tests.rs @@ -7,6 +7,7 @@ use openvm_circuit::{ }, utils::u32_into_limbs, }; + use openvm_instructions::{instruction::Instruction, LocalOpcode}; use openvm_rv32im_transpiler::Rv32LoadStoreOpcode::{self, *}; use openvm_stark_backend::{ @@ -17,10 +18,10 @@ use openvm_stark_backend::{ Matrix, }, utils::disable_debug_builder, - verifier::VerificationError, }; use openvm_stark_sdk::{config::setup_tracing, p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, seq::SliceRandom, Rng}; +use test_case::test_case; use super::{run_write_data, LoadStoreCoreAir, LoadStoreStep, Rv32LoadStoreChip}; use crate::{ @@ -29,13 +30,38 @@ use crate::{ RV32_REGISTER_NUM_LIMBS, }, loadstore::LoadStoreCoreCols, + test_utils::get_verification_error, }; const IMM_BITS: usize = 16; -const MAX_INS_CAPACITY: usize = 1024; +const MAX_INS_CAPACITY: usize = 128; type F = BabyBear; +fn create_test_chip(tester: &mut VmChipTestBuilder) -> Rv32LoadStoreChip { + let range_checker_chip = tester.memory_controller().range_checker.clone(); + let chip = Rv32LoadStoreChip::::new( + VmAirWrapper::new( + Rv32LoadStoreAdapterAir::new( + tester.memory_bridge(), + tester.execution_bridge(), + range_checker_chip.bus(), + tester.address_bits(), + ), + LoadStoreCoreAir::new(Rv32LoadStoreOpcode::CLASS_OFFSET), + ), + LoadStoreStep::new( + Rv32LoadStoreAdapterStep::new(tester.address_bits()), + range_checker_chip.clone(), + Rv32LoadStoreOpcode::CLASS_OFFSET, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), + ); + + chip +} + #[allow(clippy::too_many_arguments)] fn set_and_execute( tester: &mut VmChipTestBuilder, @@ -143,89 +169,24 @@ fn set_and_execute( /// Randomly generate computations and execute, ensuring that the generated trace /// passes all constraints. /////////////////////////////////////////////////////////////////////////////////////// -#[test] -fn rand_loadstore_test() { +#[test_case(LOADW, 100)] +#[test_case(LOADBU, 100)] +#[test_case(LOADHU, 100)] +#[test_case(STOREW, 100)] +#[test_case(STOREB, 100)] +#[test_case(STOREH, 100)] +fn rand_loadstore_test(opcode: Rv32LoadStoreOpcode, num_ops: usize) { setup_tracing(); let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().range_checker.clone(); + let mut chip = create_test_chip(&mut tester); - let mut chip = Rv32LoadStoreChip::::new( - VmAirWrapper::new( - Rv32LoadStoreAdapterAir::new( - tester.memory_bridge(), - tester.execution_bridge(), - range_checker_chip.bus(), - tester.address_bits(), - ), - LoadStoreCoreAir::new(Rv32LoadStoreOpcode::CLASS_OFFSET), - ), - LoadStoreStep::new( - Rv32LoadStoreAdapterStep::new(tester.address_bits()), - range_checker_chip.clone(), - Rv32LoadStoreOpcode::CLASS_OFFSET, - ), - MAX_INS_CAPACITY, - tester.memory_helper(), - ); - - let num_tests: usize = 100; - for _ in 0..num_tests { - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - LOADW, - None, - None, - None, - None, - ); - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - LOADBU, - None, - None, - None, - None, - ); + for _ in 0..num_ops { set_and_execute( &mut tester, &mut chip, &mut rng, - LOADHU, - None, - None, - None, - None, - ); - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - STOREW, - None, - None, - None, - None, - ); - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - STOREB, - None, - None, - None, - None, - ); - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - STOREH, + opcode, None, None, None, @@ -233,7 +194,6 @@ fn rand_loadstore_test() { ); } - drop(range_checker_chip); let tester = tester.build().load(chip).finalize(); tester.simple_test().expect("Verification failed"); } @@ -242,48 +202,31 @@ fn rand_loadstore_test() { // NEGATIVE TESTS // // Given a fake trace of a single operation, setup a chip and run the test. We replace -// the write part of the trace and check that the core chip throws the expected error. -// A dummy adaptor is used so memory interactions don't indirectly cause false passes. +// part of the trace and check that the chip throws the expected error. ////////////////////////////////////////////////////////////////////////////////////// -#[allow(clippy::too_many_arguments)] -fn run_negative_loadstore_test( - opcode: Rv32LoadStoreOpcode, +#[derive(Clone, Copy, Default, PartialEq)] +struct LoadStorePrankValues { read_data: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, prev_data: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, write_data: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, flags: Option<[u32; 4]>, is_load: Option, +} + +#[allow(clippy::too_many_arguments)] +fn run_negative_loadstore_test( + opcode: Rv32LoadStoreOpcode, rs1: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, imm: Option, imm_sign: Option, mem_as: Option, - expected_error: VerificationError, + prank_vals: LoadStorePrankValues, + interaction_error: bool, ) { let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().range_checker.clone(); - - let mut chip = Rv32LoadStoreChip::::new( - VmAirWrapper::new( - Rv32LoadStoreAdapterAir::new( - tester.memory_bridge(), - tester.execution_bridge(), - range_checker_chip.bus(), - tester.address_bits(), - ), - LoadStoreCoreAir::new(Rv32LoadStoreOpcode::CLASS_OFFSET), - ), - LoadStoreStep::new( - Rv32LoadStoreAdapterStep::new(tester.address_bits()), - range_checker_chip.clone(), - Rv32LoadStoreOpcode::CLASS_OFFSET, - ), - MAX_INS_CAPACITY, - tester.memory_helper(), - ); - - let adapter_width = BaseAir::::width(&chip.air.adapter); + let mut chip = create_test_chip(&mut tester); set_and_execute( &mut tester, @@ -296,35 +239,38 @@ fn run_negative_loadstore_test( mem_as, ); + let adapter_width = BaseAir::::width(&chip.air.adapter); + let modify_trace = |trace: &mut DenseMatrix| { let mut trace_row = trace.row_slice(0).to_vec(); let (_, core_row) = trace_row.split_at_mut(adapter_width); let core_cols: &mut LoadStoreCoreCols = core_row.borrow_mut(); - if let Some(read_data) = read_data { + + if let Some(read_data) = prank_vals.read_data { core_cols.read_data = read_data.map(F::from_canonical_u32); } - if let Some(prev_data) = prev_data { + if let Some(prev_data) = prank_vals.prev_data { core_cols.prev_data = prev_data.map(F::from_canonical_u32); } - if let Some(write_data) = write_data { + if let Some(write_data) = prank_vals.write_data { core_cols.write_data = write_data.map(F::from_canonical_u32); } - if let Some(flags) = flags { + if let Some(flags) = prank_vals.flags { core_cols.flags = flags.map(F::from_canonical_u32); } - if let Some(is_load) = is_load { + if let Some(is_load) = prank_vals.is_load { core_cols.is_load = F::from_bool(is_load); } + *trace = RowMajorMatrix::new(trace_row, trace.width()); }; - drop(range_checker_chip); disable_debug_builder(); let tester = tester .build() .load_and_prank_trace(chip, modify_trace) .finalize(); - tester.simple_test_with_expected_error(expected_error); + tester.simple_test_with_expected_error(get_verification_error(interaction_error)); } #[test] @@ -335,40 +281,38 @@ fn negative_wrong_opcode_tests() { None, None, None, - Some(false), - None, - None, - None, - None, - VerificationError::OodEvaluationMismatch, + LoadStorePrankValues { + is_load: Some(false), + ..Default::default() + }, + false, ); run_negative_loadstore_test( LOADBU, - None, - None, - None, - Some([0, 0, 0, 2]), - None, Some([4, 0, 0, 0]), Some(1), None, None, - VerificationError::OodEvaluationMismatch, + LoadStorePrankValues { + flags: Some([0, 0, 0, 2]), + ..Default::default() + }, + false, ); run_negative_loadstore_test( STOREH, - None, - None, - None, - Some([1, 0, 1, 0]), - Some(true), Some([11, 169, 76, 28]), Some(37121), None, None, - VerificationError::OodEvaluationMismatch, + LoadStorePrankValues { + flags: Some([1, 0, 1, 0]), + is_load: Some(true), + ..Default::default() + }, + false, ); } @@ -376,30 +320,34 @@ fn negative_wrong_opcode_tests() { fn negative_write_data_tests() { run_negative_loadstore_test( LOADHU, - Some([175, 33, 198, 250]), - Some([90, 121, 64, 205]), - Some([175, 33, 0, 0]), - Some([0, 2, 0, 0]), - Some(true), Some([13, 11, 156, 23]), Some(43641), None, None, - VerificationError::ChallengePhaseError, + LoadStorePrankValues { + read_data: Some([175, 33, 198, 250]), + prev_data: Some([90, 121, 64, 205]), + write_data: Some([175, 33, 0, 0]), + flags: Some([0, 2, 0, 0]), + is_load: Some(true), + }, + true, ); run_negative_loadstore_test( STOREB, - Some([175, 33, 198, 250]), - Some([90, 121, 64, 205]), - Some([175, 121, 64, 205]), - Some([0, 0, 1, 1]), - None, Some([45, 123, 87, 24]), Some(28122), Some(0), None, - VerificationError::OodEvaluationMismatch, + LoadStorePrankValues { + read_data: Some([175, 33, 198, 250]), + prev_data: Some([90, 121, 64, 205]), + write_data: Some([175, 121, 64, 205]), + flags: Some([0, 0, 1, 1]), + is_load: None, + }, + false, ); } @@ -410,13 +358,9 @@ fn negative_wrong_address_space_tests() { None, None, None, - None, - None, - None, - None, - None, Some(3), - VerificationError::OodEvaluationMismatch, + LoadStorePrankValues::default(), + false, ); // TODO(ayush): add back // run_negative_loadstore_test( @@ -424,26 +368,19 @@ fn negative_wrong_address_space_tests() { // None, // None, // None, - // None, - // None, - // None, - // None, - // None, // Some(4), - // VerificationError::OodEvaluationMismatch, + // LoadStorePrankValues::default(), + // false, // ); + run_negative_loadstore_test( STOREW, None, None, None, - None, - None, - None, - None, - None, Some(1), - VerificationError::OodEvaluationMismatch, + LoadStorePrankValues::default(), + false, ); } @@ -452,96 +389,6 @@ fn negative_wrong_address_space_tests() { /// /// Ensure that solve functions produce the correct results. /////////////////////////////////////////////////////////////////////////////////////// -#[test] -fn execute_roundtrip_sanity_test() { - let mut rng = create_seeded_rng(); - let mut tester = VmChipTestBuilder::default(); - let range_checker_chip = tester.memory_controller().range_checker.clone(); - - let mut chip = Rv32LoadStoreChip::::new( - VmAirWrapper::new( - Rv32LoadStoreAdapterAir::new( - tester.memory_bridge(), - tester.execution_bridge(), - range_checker_chip.bus(), - tester.address_bits(), - ), - LoadStoreCoreAir::new(Rv32LoadStoreOpcode::CLASS_OFFSET), - ), - LoadStoreStep::new( - Rv32LoadStoreAdapterStep::new(tester.address_bits()), - range_checker_chip.clone(), - Rv32LoadStoreOpcode::CLASS_OFFSET, - ), - MAX_INS_CAPACITY, - tester.memory_helper(), - ); - - let num_tests: usize = 100; - for _ in 0..num_tests { - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - LOADW, - None, - None, - None, - None, - ); - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - LOADBU, - None, - None, - None, - None, - ); - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - LOADHU, - None, - None, - None, - None, - ); - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - STOREW, - None, - None, - None, - None, - ); - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - STOREB, - None, - None, - None, - None, - ); - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - STOREH, - None, - None, - None, - None, - ); - } -} - #[test] fn run_loadw_storew_sanity_test() { let read_data = [138, 45, 202, 76].map(F::from_canonical_u32); diff --git a/extensions/rv32im/circuit/src/mul/tests.rs b/extensions/rv32im/circuit/src/mul/tests.rs index 378e883deb..e21195e088 100644 --- a/extensions/rv32im/circuit/src/mul/tests.rs +++ b/extensions/rv32im/circuit/src/mul/tests.rs @@ -1,15 +1,12 @@ -use std::borrow::BorrowMut; +use std::{array, borrow::BorrowMut}; -use openvm_circuit::{ - arch::{ - testing::{TestAdapterChip, VmChipTestBuilder, RANGE_TUPLE_CHECKER_BUS}, - ExecutionBridge, VmAirWrapper, - }, - utils::generate_long_number, +use openvm_circuit::arch::{ + testing::{VmChipTestBuilder, RANGE_TUPLE_CHECKER_BUS}, + InstructionExecutor, VmAirWrapper, }; use openvm_circuit_primitives::range_tuple::{RangeTupleCheckerBus, SharedRangeTupleCheckerChip}; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; -use openvm_rv32im_transpiler::MulOpcode; +use openvm_instructions::LocalOpcode; +use openvm_rv32im_transpiler::MulOpcode::{self, MUL}; use openvm_stark_backend::{ p3_air::BaseAir, p3_field::FieldAlgebra, @@ -18,44 +15,33 @@ use openvm_stark_backend::{ Matrix, }, utils::disable_debug_builder, - verifier::VerificationError, - ChipUsageGetter, }; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; +use rand::{rngs::StdRng, Rng}; use super::core::run_mul; use crate::{ adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, mul::{MultiplicationCoreCols, MultiplicationStep, Rv32MultiplicationChip}, - test_utils::rv32_rand_write_register_or_imm, + test_utils::{get_verification_error, rv32_rand_write_register_or_imm}, MultiplicationCoreAir, }; const MAX_INS_CAPACITY: usize = 128; - +// the max number of limbs we currently support MUL for is 32 (i.e. for U256s) +const MAX_NUM_LIMBS: u32 = 32; type F = BabyBear; -////////////////////////////////////////////////////////////////////////////////////// -// POSITIVE TESTS -// -// Randomly generate computations and execute, ensuring that the generated trace -// passes all constraints. -////////////////////////////////////////////////////////////////////////////////////// - -fn run_rv32_mul_rand_test(num_ops: usize) { - // the max number of limbs we currently support MUL for is 32 (i.e. for U256s) - const MAX_NUM_LIMBS: u32 = 32; - let mut rng = create_seeded_rng(); - +fn create_test_chip( + tester: &mut VmChipTestBuilder, +) -> (Rv32MultiplicationChip, SharedRangeTupleCheckerChip<2>) { let range_tuple_bus = RangeTupleCheckerBus::new( RANGE_TUPLE_CHECKER_BUS, [1 << RV32_CELL_BITS, MAX_NUM_LIMBS * (1 << RV32_CELL_BITS)], ); let range_tuple_checker = SharedRangeTupleCheckerChip::new(range_tuple_bus); - let mut tester = VmChipTestBuilder::default(); - - let mut chip = Rv32MultiplicationChip::::new( + let chip = Rv32MultiplicationChip::::new( VmAirWrapper::new( Rv32MultAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), MultiplicationCoreAir::new(range_tuple_bus, MulOpcode::CLASS_OFFSET), @@ -69,28 +55,50 @@ fn run_rv32_mul_rand_test(num_ops: usize) { tester.memory_helper(), ); + (chip, range_tuple_checker) +} + +#[allow(clippy::too_many_arguments)] +fn set_and_execute>( + tester: &mut VmChipTestBuilder, + chip: &mut E, + rng: &mut StdRng, + opcode: MulOpcode, + b: Option<[u8; RV32_REGISTER_NUM_LIMBS]>, + c: Option<[u8; RV32_REGISTER_NUM_LIMBS]>, +) { + let b = b.unwrap_or(array::from_fn(|_| rng.gen_range(0..=u8::MAX))); + let c = c.unwrap_or(array::from_fn(|_| rng.gen_range(0..=u8::MAX))); + + let (mut instruction, rd) = + rv32_rand_write_register_or_imm(tester, b, c, None, opcode.global_opcode().as_usize(), rng); + + instruction.e = F::ZERO; + tester.execute(chip, &instruction); + + let (a, _) = run_mul::(&b, &c); + assert_eq!( + a.map(F::from_canonical_u8), + tester.read::(1, rd) + ) +} + +////////////////////////////////////////////////////////////////////////////////////// +// POSITIVE TESTS +// +// Randomly generate computations and execute, ensuring that the generated trace +// passes all constraints. +////////////////////////////////////////////////////////////////////////////////////// + +#[test] +fn run_rv32_mul_rand_test() { + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + + let (mut chip, range_tuple_checker) = create_test_chip(&mut tester); + let num_ops = 100; for _ in 0..num_ops { - let b = generate_long_number::(&mut rng) - .map(|x| x as u8); - let c = generate_long_number::(&mut rng) - .map(|x| x as u8); - - let (mut instruction, rd) = rv32_rand_write_register_or_imm( - &mut tester, - b, - c, - None, - MulOpcode::MUL.global_opcode().as_usize(), - &mut rng, - ); - instruction.e = F::ZERO; - tester.execute(&mut chip, &instruction); - - let (a, _) = run_mul::(&b, &c); - assert_eq!( - a.map(F::from_canonical_u8), - tester.read::(1, rd) - ) + set_and_execute(&mut tester, &mut chip, &mut rng, MUL, None, None); } let tester = tester @@ -101,110 +109,70 @@ fn run_rv32_mul_rand_test(num_ops: usize) { tester.simple_test().expect("Verification failed"); } -#[test] -fn rv32_mul_rand_test() { - run_rv32_mul_rand_test(1); -} - ////////////////////////////////////////////////////////////////////////////////////// // NEGATIVE TESTS // // Given a fake trace of a single operation, setup a chip and run the test. We replace -// the write part of the trace and check that the core chip throws the expected error. -// A dummy adapter is used so memory interactions don't indirectly cause false passes. +// part of the trace and check that the chip throws the expected error. ////////////////////////////////////////////////////////////////////////////////////// -// type Rv32MultiplicationTestChip = VmChipWrapper< -// F, -// TestAdapterChip, -// MultiplicationStep, -// >; - -// #[allow(clippy::too_many_arguments)] -// fn run_rv32_mul_negative_test( -// a: [u8; RV32_REGISTER_NUM_LIMBS], -// b: [u8; RV32_REGISTER_NUM_LIMBS], -// c: [u8; RV32_REGISTER_NUM_LIMBS], -// is_valid: bool, -// interaction_error: bool, -// ) { -// const MAX_NUM_LIMBS: u32 = 32; -// let range_tuple_bus = RangeTupleCheckerBus::new( -// RANGE_TUPLE_CHECKER_BUS, -// [1 << RV32_CELL_BITS, MAX_NUM_LIMBS * (1 << RV32_CELL_BITS)], -// ); -// let range_tuple_chip = SharedRangeTupleCheckerChip::new(range_tuple_bus); - -// let mut tester = VmChipTestBuilder::default(); -// let mut chip = Rv32MultiplicationTestChip::::new( -// TestAdapterChip::new( -// vec![[b.map(F::from_canonical_u8), c.map(F::from_canonical_u8)].concat()], -// vec![None], -// ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), -// ), -// MultiplicationStep::new(range_tuple_chip.clone(), MulOpcode::CLASS_OFFSET), -// tester.offline_memory_mutex_arc(), -// ); - -// tester.execute( -// &mut chip, -// &Instruction::from_usize(MulOpcode::MUL.global_opcode(), [0, 0, 0, 1, 0]), -// ); - -// let trace_width = chip.trace_width(); -// let adapter_width = BaseAir::::width(chip.adapter.air()); -// let (_, carry) = run_mul::(&b, &c); - -// range_tuple_chip.clear(); -// if is_valid { -// for (a, carry) in a.iter().zip(carry.iter()) { -// range_tuple_chip.add_count(&[*a as u32, *carry]); -// } -// } - -// let modify_trace = |trace: &mut DenseMatrix| { -// let mut values = trace.row_slice(0).to_vec(); -// let cols: &mut MultiplicationCoreCols = -// values.split_at_mut(adapter_width).1.borrow_mut(); -// cols.a = a.map(F::from_canonical_u8); -// cols.is_valid = F::from_bool(is_valid); -// *trace = RowMajorMatrix::new(values, trace_width); -// }; - -// disable_debug_builder(); -// let tester = tester -// .build() -// .load_and_prank_trace(chip, modify_trace) -// .load(range_tuple_chip) -// .finalize(); -// tester.simple_test_with_expected_error(if interaction_error { -// VerificationError::ChallengePhaseError -// } else { -// VerificationError::OodEvaluationMismatch -// }); -// } - -// #[test] -// fn rv32_mul_wrong_negative_test() { -// run_rv32_mul_negative_test( -// [63, 247, 125, 234], -// [51, 109, 78, 142], -// [197, 85, 150, 32], -// true, -// true, -// ); -// } - -// #[test] -// fn rv32_mul_is_valid_false_negative_test() { -// run_rv32_mul_negative_test( -// [63, 247, 125, 234], -// [51, 109, 78, 142], -// [197, 85, 150, 32], -// false, -// true, -// ); -// } +#[allow(clippy::too_many_arguments)] +fn run_negative_mul_test( + opcode: MulOpcode, + prank_a: [u32; RV32_REGISTER_NUM_LIMBS], + b: [u8; RV32_REGISTER_NUM_LIMBS], + c: [u8; RV32_REGISTER_NUM_LIMBS], + prank_is_valid: bool, + interaction_error: bool, +) { + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let (mut chip, range_tuple_chip) = create_test_chip(&mut tester); + + set_and_execute(&mut tester, &mut chip, &mut rng, opcode, Some(b), Some(c)); + + let adapter_width = BaseAir::::width(&chip.air.adapter); + let modify_trace = |trace: &mut DenseMatrix| { + let mut values = trace.row_slice(0).to_vec(); + let cols: &mut MultiplicationCoreCols = + values.split_at_mut(adapter_width).1.borrow_mut(); + cols.a = prank_a.map(F::from_canonical_u32); + cols.is_valid = F::from_bool(prank_is_valid); + *trace = RowMajorMatrix::new(values, trace.width()); + }; + + disable_debug_builder(); + let tester = tester + .build() + .load_and_prank_trace(chip, modify_trace) + .load(range_tuple_chip) + .finalize(); + tester.simple_test_with_expected_error(get_verification_error(interaction_error)); +} + +#[test] +fn rv32_mul_wrong_negative_test() { + run_negative_mul_test( + MUL, + [63, 247, 125, 234], + [51, 109, 78, 142], + [197, 85, 150, 32], + true, + true, + ); +} + +#[test] +fn rv32_mul_is_valid_false_negative_test() { + run_negative_mul_test( + MUL, + [63, 247, 125, 234], + [51, 109, 78, 142], + [197, 85, 150, 32], + false, + true, + ); +} /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS diff --git a/extensions/rv32im/circuit/src/mulh/tests.rs b/extensions/rv32im/circuit/src/mulh/tests.rs index d082ba65b9..3c64ea0f70 100644 --- a/extensions/rv32im/circuit/src/mulh/tests.rs +++ b/extensions/rv32im/circuit/src/mulh/tests.rs @@ -1,12 +1,18 @@ use std::borrow::BorrowMut; +use super::core::run_mulh; +use crate::{ + adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, + mulh::{MulHCoreCols, MulHStep, Rv32MulHChip}, + test_utils::get_verification_error, + MulHCoreAir, +}; use openvm_circuit::{ arch::{ testing::{ - memory::gen_pointer, TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS, - RANGE_TUPLE_CHECKER_BUS, + memory::gen_pointer, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS, RANGE_TUPLE_CHECKER_BUS, }, - ExecutionBridge, InstructionExecutor, VmAirWrapper, + InstructionExecutor, VmAirWrapper, }, utils::generate_long_number, }; @@ -15,7 +21,7 @@ use openvm_circuit_primitives::{ range_tuple::{RangeTupleCheckerBus, SharedRangeTupleCheckerChip}, }; use openvm_instructions::{instruction::Instruction, LocalOpcode}; -use openvm_rv32im_transpiler::MulHOpcode; +use openvm_rv32im_transpiler::MulHOpcode::{self, *}; use openvm_stark_backend::{ p3_air::BaseAir, p3_field::FieldAlgebra, @@ -24,39 +30,67 @@ use openvm_stark_backend::{ Matrix, }, utils::disable_debug_builder, - verifier::VerificationError, - ChipUsageGetter, }; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::rngs::StdRng; - -use super::core::run_mulh; -use crate::{ - adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, - mulh::{MulHCoreCols, MulHStep, Rv32MulHChip}, - MulHCoreAir, -}; +use test_case::test_case; const MAX_INS_CAPACITY: usize = 128; - +// the max number of limbs we currently support MUL for is 32 (i.e. for U256s) +const MAX_NUM_LIMBS: u32 = 32; type F = BabyBear; -////////////////////////////////////////////////////////////////////////////////////// -// POSITIVE TESTS -// -// Randomly generate computations and execute, ensuring that the generated trace -// passes all constraints. -////////////////////////////////////////////////////////////////////////////////////// +fn create_test_chip( + tester: &mut VmChipTestBuilder, +) -> ( + Rv32MulHChip, + SharedBitwiseOperationLookupChip, + SharedRangeTupleCheckerChip<2>, +) { + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let range_tuple_bus = RangeTupleCheckerBus::new( + RANGE_TUPLE_CHECKER_BUS, + [1 << RV32_CELL_BITS, MAX_NUM_LIMBS * (1 << RV32_CELL_BITS)], + ); + + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + let range_tuple_checker = SharedRangeTupleCheckerChip::new(range_tuple_bus); + + let chip = Rv32MulHChip::::new( + VmAirWrapper::new( + Rv32MultAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), + MulHCoreAir::new(bitwise_bus, range_tuple_bus), + ), + MulHStep::new( + Rv32MultAdapterStep::new(), + bitwise_chip.clone(), + range_tuple_checker.clone(), + ), + MAX_INS_CAPACITY, + tester.memory_helper(), + ); + + (chip, bitwise_chip, range_tuple_checker) +} #[allow(clippy::too_many_arguments)] -fn run_rv32_mulh_rand_write_execute>( - opcode: MulHOpcode, +fn set_and_execute>( tester: &mut VmChipTestBuilder, chip: &mut E, - b: [u32; RV32_REGISTER_NUM_LIMBS], - c: [u32; RV32_REGISTER_NUM_LIMBS], rng: &mut StdRng, + opcode: MulHOpcode, + b: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, + c: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, ) { + let b = b.unwrap_or(generate_long_number::< + RV32_REGISTER_NUM_LIMBS, + RV32_CELL_BITS, + >(rng)); + let c = c.unwrap_or(generate_long_number::< + RV32_REGISTER_NUM_LIMBS, + RV32_CELL_BITS, + >(rng)); + let rs1 = gen_pointer(rng, 4); let rs2 = gen_pointer(rng, 4); let rd = gen_pointer(rng, 4); @@ -64,52 +98,35 @@ fn run_rv32_mulh_rand_write_execute>( tester.write::(1, rs1, b.map(F::from_canonical_u32)); tester.write::(1, rs2, c.map(F::from_canonical_u32)); - let (a, _, _, _, _) = run_mulh::(opcode, &b, &c); tester.execute( chip, &Instruction::from_usize(opcode.global_opcode(), [rd, rs1, rs2, 1, 0]), ); + let (a, _, _, _, _) = run_mulh::(opcode, &b, &c); assert_eq!( a.map(F::from_canonical_u32), tester.read::(1, rd) ); } +////////////////////////////////////////////////////////////////////////////////////// +// POSITIVE TESTS +// +// Randomly generate computations and execute, ensuring that the generated trace +// passes all constraints. +////////////////////////////////////////////////////////////////////////////////////// + +#[test_case(MULH, 100)] +#[test_case(MULHSU, 100)] +#[test_case(MULHU, 100)] fn run_rv32_mulh_rand_test(opcode: MulHOpcode, num_ops: usize) { - // the max number of limbs we currently support MUL for is 32 (i.e. for U256s) - const MAX_NUM_LIMBS: u32 = 32; let mut rng = create_seeded_rng(); - - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let range_tuple_bus = RangeTupleCheckerBus::new( - RANGE_TUPLE_CHECKER_BUS, - [1 << RV32_CELL_BITS, MAX_NUM_LIMBS * (1 << RV32_CELL_BITS)], - ); - - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let range_tuple_checker = SharedRangeTupleCheckerChip::new(range_tuple_bus); - let mut tester = VmChipTestBuilder::default(); - - let mut chip = Rv32MulHChip::::new( - VmAirWrapper::new( - Rv32MultAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), - MulHCoreAir::new(bitwise_bus, range_tuple_bus), - ), - MulHStep::new( - Rv32MultAdapterStep::new(), - bitwise_chip.clone(), - range_tuple_checker.clone(), - ), - MAX_INS_CAPACITY, - tester.memory_helper(), - ); + let (mut chip, bitwise_chip, range_tuple_checker) = create_test_chip(&mut tester); for _ in 0..num_ops { - let b = generate_long_number::(&mut rng); - let c = generate_long_number::(&mut rng); - run_rv32_mulh_rand_write_execute(opcode, &mut tester, &mut chip, b, c, &mut rng); + set_and_execute(&mut tester, &mut chip, &mut rng, opcode, None, None); } let tester = tester @@ -121,258 +138,205 @@ fn run_rv32_mulh_rand_test(opcode: MulHOpcode, num_ops: usize) { tester.simple_test().expect("Verification failed"); } +////////////////////////////////////////////////////////////////////////////////////// +// NEGATIVE TESTS +// +// Given a fake trace of a single operation, setup a chip and run the test. We replace +// part of the trace and check that the chip throws the expected error. +////////////////////////////////////////////////////////////////////////////////////// + +#[allow(clippy::too_many_arguments)] +fn run_negative_mulh_test( + opcode: MulHOpcode, + prank_a: [u32; RV32_REGISTER_NUM_LIMBS], + b: [u32; RV32_REGISTER_NUM_LIMBS], + c: [u32; RV32_REGISTER_NUM_LIMBS], + prank_a_mul: [u32; RV32_REGISTER_NUM_LIMBS], + prank_b_ext: u32, + prank_c_ext: u32, + interaction_error: bool, +) { + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let (mut chip, bitwise_chip, range_tuple_chip) = create_test_chip(&mut tester); + + set_and_execute(&mut tester, &mut chip, &mut rng, opcode, Some(b), Some(c)); + + let adapter_width = BaseAir::::width(&chip.air.adapter); + let modify_trace = |trace: &mut DenseMatrix| { + let mut values = trace.row_slice(0).to_vec(); + let cols: &mut MulHCoreCols = + values.split_at_mut(adapter_width).1.borrow_mut(); + cols.a = prank_a.map(F::from_canonical_u32); + cols.a_mul = prank_a_mul.map(F::from_canonical_u32); + cols.b_ext = F::from_canonical_u32(prank_b_ext); + cols.c_ext = F::from_canonical_u32(prank_c_ext); + *trace = RowMajorMatrix::new(values, trace.width()); + }; + + disable_debug_builder(); + let tester = tester + .build() + .load_and_prank_trace(chip, modify_trace) + .load(bitwise_chip) + .load(range_tuple_chip) + .finalize(); + tester.simple_test_with_expected_error(get_verification_error(interaction_error)); +} + #[test] -fn rv32_mulh_rand_test() { - run_rv32_mulh_rand_test(MulHOpcode::MULH, 100); +fn rv32_mulh_wrong_a_mul_negative_test() { + run_negative_mulh_test( + MULH, + [130, 9, 135, 241], + [197, 85, 150, 32], + [51, 109, 78, 142], + [63, 247, 125, 234], + 0, + 255, + true, + ); } #[test] -fn rv32_mulhsu_rand_test() { - run_rv32_mulh_rand_test(MulHOpcode::MULHSU, 100); +fn rv32_mulh_wrong_a_negative_test() { + run_negative_mulh_test( + MULH, + [130, 9, 135, 242], + [197, 85, 150, 32], + [51, 109, 78, 142], + [63, 247, 125, 232], + 0, + 255, + true, + ); } #[test] -fn rv32_mulhu_rand_test() { - run_rv32_mulh_rand_test(MulHOpcode::MULHU, 100); +fn rv32_mulh_wrong_ext_negative_test() { + run_negative_mulh_test( + MULH, + [1, 0, 0, 0], + [0, 0, 0, 128], + [2, 0, 0, 0], + [0, 0, 0, 0], + 0, + 0, + true, + ); } -////////////////////////////////////////////////////////////////////////////////////// -// NEGATIVE TESTS -// -// Given a fake trace of a single operation, setup a chip and run the test. We replace -// the write part of the trace and check that the core chip throws the expected error. -// A dummy adapter is used so memory interactions don't indirectly cause false passes. -////////////////////////////////////////////////////////////////////////////////////// +#[test] +fn rv32_mulh_invalid_ext_negative_test() { + run_negative_mulh_test( + MULH, + [3, 2, 2, 2], + [0, 0, 0, 128], + [2, 0, 0, 0], + [0, 0, 0, 0], + 1, + 0, + false, + ); +} + +#[test] +fn rv32_mulhsu_wrong_a_mul_negative_test() { + run_negative_mulh_test( + MULHSU, + [174, 40, 246, 202], + [197, 85, 150, 160], + [51, 109, 78, 142], + [63, 247, 125, 105], + 255, + 0, + true, + ); +} + +#[test] +fn rv32_mulhsu_wrong_a_negative_test() { + run_negative_mulh_test( + MULHSU, + [174, 40, 246, 201], + [197, 85, 150, 160], + [51, 109, 78, 142], + [63, 247, 125, 104], + 255, + 0, + true, + ); +} + +#[test] +fn rv32_mulhsu_wrong_b_ext_negative_test() { + run_negative_mulh_test( + MULHSU, + [1, 0, 0, 0], + [0, 0, 0, 128], + [2, 0, 0, 0], + [0, 0, 0, 0], + 0, + 0, + true, + ); +} -// type Rv32MulHTestChip = -// VmChipWrapper, MulHStep>; - -// #[allow(clippy::too_many_arguments)] -// fn run_rv32_mulh_negative_test( -// opcode: MulHOpcode, -// a: [u32; RV32_REGISTER_NUM_LIMBS], -// b: [u32; RV32_REGISTER_NUM_LIMBS], -// c: [u32; RV32_REGISTER_NUM_LIMBS], -// a_mul: [u32; RV32_REGISTER_NUM_LIMBS], -// b_ext: u32, -// c_ext: u32, -// interaction_error: bool, -// ) { -// const MAX_NUM_LIMBS: u32 = 32; -// let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); -// let range_tuple_bus = RangeTupleCheckerBus::new( -// RANGE_TUPLE_CHECKER_BUS, -// [1 << RV32_CELL_BITS, MAX_NUM_LIMBS * (1 << RV32_CELL_BITS)], -// ); - -// let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); -// let range_tuple_chip = SharedRangeTupleCheckerChip::new(range_tuple_bus); - -// let mut tester = VmChipTestBuilder::default(); -// let mut chip = Rv32MulHTestChip::::new( -// TestAdapterChip::new( -// vec![[b.map(F::from_canonical_u32), c.map(F::from_canonical_u32)].concat()], -// vec![None], -// ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), -// ), -// MulHStep::new(bitwise_chip.clone(), range_tuple_chip.clone()), -// tester.offline_memory_mutex_arc(), -// ); - -// tester.execute( -// &mut chip, -// &Instruction::from_usize(opcode.global_opcode(), [0, 0, 0, 1, 0]), -// ); - -// let trace_width = chip.trace_width(); -// let adapter_width = BaseAir::::width(chip.adapter.air()); -// let (_, _, carry, _, _) = run_mulh::(opcode, &b, -// &c); - -// range_tuple_chip.clear(); -// for i in 0..RV32_REGISTER_NUM_LIMBS { -// range_tuple_chip.add_count(&[a_mul[i], carry[i]]); -// range_tuple_chip.add_count(&[a[i], carry[RV32_REGISTER_NUM_LIMBS + i]]); -// } - -// let modify_trace = |trace: &mut DenseMatrix| { -// let mut values = trace.row_slice(0).to_vec(); -// let cols: &mut MulHCoreCols = -// values.split_at_mut(adapter_width).1.borrow_mut(); -// cols.a = a.map(F::from_canonical_u32); -// cols.a_mul = a_mul.map(F::from_canonical_u32); -// cols.b_ext = F::from_canonical_u32(b_ext); -// cols.c_ext = F::from_canonical_u32(c_ext); -// *trace = RowMajorMatrix::new(values, trace_width); -// }; - -// disable_debug_builder(); -// let tester = tester -// .build() -// .load_and_prank_trace(chip, modify_trace) -// .load(bitwise_chip) -// .load(range_tuple_chip) -// .finalize(); -// tester.simple_test_with_expected_error(if interaction_error { -// VerificationError::ChallengePhaseError -// } else { -// VerificationError::OodEvaluationMismatch -// }); -// } - -// #[test] -// fn rv32_mulh_wrong_a_mul_negative_test() { -// run_rv32_mulh_negative_test( -// MulHOpcode::MULH, -// [130, 9, 135, 241], -// [197, 85, 150, 32], -// [51, 109, 78, 142], -// [63, 247, 125, 234], -// 0, -// 255, -// true, -// ); -// } - -// #[test] -// fn rv32_mulh_wrong_a_negative_test() { -// run_rv32_mulh_negative_test( -// MulHOpcode::MULH, -// [130, 9, 135, 242], -// [197, 85, 150, 32], -// [51, 109, 78, 142], -// [63, 247, 125, 232], -// 0, -// 255, -// true, -// ); -// } - -// #[test] -// fn rv32_mulh_wrong_ext_negative_test() { -// run_rv32_mulh_negative_test( -// MulHOpcode::MULH, -// [1, 0, 0, 0], -// [0, 0, 0, 128], -// [2, 0, 0, 0], -// [0, 0, 0, 0], -// 0, -// 0, -// true, -// ); -// } - -// #[test] -// fn rv32_mulh_invalid_ext_negative_test() { -// run_rv32_mulh_negative_test( -// MulHOpcode::MULH, -// [3, 2, 2, 2], -// [0, 0, 0, 128], -// [2, 0, 0, 0], -// [0, 0, 0, 0], -// 1, -// 0, -// false, -// ); -// } - -// #[test] -// fn rv32_mulhsu_wrong_a_mul_negative_test() { -// run_rv32_mulh_negative_test( -// MulHOpcode::MULHSU, -// [174, 40, 246, 202], -// [197, 85, 150, 160], -// [51, 109, 78, 142], -// [63, 247, 125, 105], -// 255, -// 0, -// true, -// ); -// } - -// #[test] -// fn rv32_mulhsu_wrong_a_negative_test() { -// run_rv32_mulh_negative_test( -// MulHOpcode::MULHSU, -// [174, 40, 246, 201], -// [197, 85, 150, 160], -// [51, 109, 78, 142], -// [63, 247, 125, 104], -// 255, -// 0, -// true, -// ); -// } - -// #[test] -// fn rv32_mulhsu_wrong_b_ext_negative_test() { -// run_rv32_mulh_negative_test( -// MulHOpcode::MULHSU, -// [1, 0, 0, 0], -// [0, 0, 0, 128], -// [2, 0, 0, 0], -// [0, 0, 0, 0], -// 0, -// 0, -// true, -// ); -// } - -// #[test] -// fn rv32_mulhsu_wrong_c_ext_negative_test() { -// run_rv32_mulh_negative_test( -// MulHOpcode::MULHSU, -// [0, 0, 0, 64], -// [0, 0, 0, 128], -// [0, 0, 0, 128], -// [0, 0, 0, 0], -// 255, -// 255, -// false, -// ); -// } - -// #[test] -// fn rv32_mulhu_wrong_a_mul_negative_test() { -// run_rv32_mulh_negative_test( -// MulHOpcode::MULHU, -// [130, 9, 135, 241], -// [197, 85, 150, 32], -// [51, 109, 78, 142], -// [63, 247, 125, 234], -// 0, -// 0, -// true, -// ); -// } - -// #[test] -// fn rv32_mulhu_wrong_a_negative_test() { -// run_rv32_mulh_negative_test( -// MulHOpcode::MULHU, -// [130, 9, 135, 240], -// [197, 85, 150, 32], -// [51, 109, 78, 142], -// [63, 247, 125, 232], -// 0, -// 0, -// true, -// ); -// } - -// #[test] -// fn rv32_mulhu_wrong_ext_negative_test() { -// run_rv32_mulh_negative_test( -// MulHOpcode::MULHU, -// [255, 255, 255, 255], -// [0, 0, 0, 128], -// [2, 0, 0, 0], -// [0, 0, 0, 0], -// 255, -// 0, -// false, -// ); -// } +#[test] +fn rv32_mulhsu_wrong_c_ext_negative_test() { + run_negative_mulh_test( + MULHSU, + [0, 0, 0, 64], + [0, 0, 0, 128], + [0, 0, 0, 128], + [0, 0, 0, 0], + 255, + 255, + false, + ); +} + +#[test] +fn rv32_mulhu_wrong_a_mul_negative_test() { + run_negative_mulh_test( + MULHU, + [130, 9, 135, 241], + [197, 85, 150, 32], + [51, 109, 78, 142], + [63, 247, 125, 234], + 0, + 0, + true, + ); +} + +#[test] +fn rv32_mulhu_wrong_a_negative_test() { + run_negative_mulh_test( + MULHU, + [130, 9, 135, 240], + [197, 85, 150, 32], + [51, 109, 78, 142], + [63, 247, 125, 232], + 0, + 0, + true, + ); +} + +#[test] +fn rv32_mulhu_wrong_ext_negative_test() { + run_negative_mulh_test( + MULHU, + [255, 255, 255, 255], + [0, 0, 0, 128], + [2, 0, 0, 0], + [0, 0, 0, 0], + 255, + 0, + false, + ); +} /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS @@ -389,7 +353,7 @@ fn run_mulh_sanity_test() { let c: [u32; RV32_REGISTER_NUM_LIMBS] = [303, 375, 449, 463]; let c_mul: [u32; RV32_REGISTER_NUM_LIMBS] = [39, 100, 126, 205]; let (res, res_mul, carry, x_ext, y_ext) = - run_mulh::(MulHOpcode::MULH, &x, &y); + run_mulh::(MULH, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], res[i]); assert_eq!(z_mul[i], res_mul[i]); @@ -409,7 +373,7 @@ fn run_mulhu_sanity_test() { let c: [u32; RV32_REGISTER_NUM_LIMBS] = [107, 93, 18, 0]; let c_mul: [u32; RV32_REGISTER_NUM_LIMBS] = [39, 100, 126, 205]; let (res, res_mul, carry, x_ext, y_ext) = - run_mulh::(MulHOpcode::MULHU, &x, &y); + run_mulh::(MULHU, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], res[i]); assert_eq!(z_mul[i], res_mul[i]); @@ -429,7 +393,7 @@ fn run_mulhsu_pos_sanity_test() { let c: [u32; RV32_REGISTER_NUM_LIMBS] = [107, 93, 18, 0]; let c_mul: [u32; RV32_REGISTER_NUM_LIMBS] = [39, 100, 126, 205]; let (res, res_mul, carry, x_ext, y_ext) = - run_mulh::(MulHOpcode::MULHSU, &x, &y); + run_mulh::(MULHSU, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], res[i]); assert_eq!(z_mul[i], res_mul[i]); @@ -449,7 +413,7 @@ fn run_mulhsu_neg_sanity_test() { let c: [u32; RV32_REGISTER_NUM_LIMBS] = [212, 292, 326, 379]; let c_mul: [u32; RV32_REGISTER_NUM_LIMBS] = [39, 100, 126, 231]; let (res, res_mul, carry, x_ext, y_ext) = - run_mulh::(MulHOpcode::MULHSU, &x, &y); + run_mulh::(MULHSU, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], res[i]); assert_eq!(z_mul[i], res_mul[i]); diff --git a/extensions/rv32im/circuit/src/shift/tests.rs b/extensions/rv32im/circuit/src/shift/tests.rs index e9f48fc1ab..7b92556ccd 100644 --- a/extensions/rv32im/circuit/src/shift/tests.rs +++ b/extensions/rv32im/circuit/src/shift/tests.rs @@ -1,17 +1,14 @@ use std::{array, borrow::BorrowMut}; -use openvm_circuit::{ - arch::{ - testing::{TestAdapterChip, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - NewVmChipWrapper, VmAirWrapper, - }, - utils::generate_long_number, +use openvm_circuit::arch::{ + testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, + InstructionExecutor, VmAirWrapper, }; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, }; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; -use openvm_rv32im_transpiler::ShiftOpcode; +use openvm_instructions::LocalOpcode; +use openvm_rv32im_transpiler::ShiftOpcode::{self, *}; use openvm_stark_backend::{ p3_air::BaseAir, p3_field::FieldAlgebra, @@ -20,20 +17,19 @@ use openvm_stark_backend::{ Matrix, }, utils::disable_debug_builder, - verifier::VerificationError, - ChipUsageGetter, }; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; -use rand::Rng; +use rand::{rngs::StdRng, Rng}; +use test_case::test_case; -use super::{ - core::run_shift, Rv32ShiftChip, Rv32ShiftStep, ShiftCoreAir, ShiftCoreCols, ShiftStep, -}; +use super::{core::run_shift, Rv32ShiftChip, ShiftCoreAir, ShiftCoreCols, ShiftStep}; use crate::{ adapters::{ Rv32BaseAluAdapterAir, Rv32BaseAluAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, }, - test_utils::{generate_rv32_is_type_immediate, rv32_rand_write_register_or_imm}, + test_utils::{ + generate_rv32_is_type_immediate, get_verification_error, rv32_rand_write_register_or_imm, + }, }; type F = BabyBear; @@ -74,309 +70,287 @@ fn create_test_chip( (chip, bitwise_chip) } +#[allow(clippy::too_many_arguments)] +fn set_and_execute>( + tester: &mut VmChipTestBuilder, + chip: &mut E, + rng: &mut StdRng, + opcode: ShiftOpcode, + b: Option<[u8; RV32_REGISTER_NUM_LIMBS]>, + is_imm: Option, + c: Option<[u8; RV32_REGISTER_NUM_LIMBS]>, +) { + let b = b.unwrap_or(array::from_fn(|_| rng.gen_range(0..=u8::MAX))); + let (c_imm, c) = if is_imm.unwrap_or(rng.gen_bool(0.5)) { + let (imm, c) = if let Some(c) = c { + ((u32::from_le_bytes(c) & 0xFFFFFF) as usize, c) + } else { + generate_rv32_is_type_immediate(rng) + }; + (Some(imm), c) + } else { + ( + None, + c.unwrap_or(array::from_fn(|_| rng.gen_range(0..=u8::MAX))), + ) + }; + let (instruction, rd) = rv32_rand_write_register_or_imm( + tester, + b, + c, + c_imm, + opcode.global_opcode().as_usize(), + rng, + ); + tester.execute(chip, &instruction); + + let (a, _, _) = run_shift::(opcode, &b, &c); + assert_eq!( + a.map(F::from_canonical_u8), + tester.read::(1, rd) + ) +} + ////////////////////////////////////////////////////////////////////////////////////// // POSITIVE TESTS // // Randomly generate computations and execute, ensuring that the generated trace // passes all constraints. ////////////////////////////////////////////////////////////////////////////////////// - +#[test_case(SLL, 100)] +#[test_case(SRL, 100)] +#[test_case(SRA, 100)] fn run_rv32_shift_rand_test(opcode: ShiftOpcode, num_ops: usize) { let mut rng = create_seeded_rng(); - let mut tester = VmChipTestBuilder::default(); let (mut chip, bitwise_chip) = create_test_chip(&tester); for _ in 0..num_ops { - let b = generate_long_number::(&mut rng) - .map(|x| x as u8); - let (c_imm, c) = if rng.gen_bool(0.5) { - ( - None, - generate_long_number::(&mut rng) - .map(|x| x as u8), - ) - } else { - let (imm, c) = generate_rv32_is_type_immediate(&mut rng); - (Some(imm), c) - }; - - let (instruction, rd) = rv32_rand_write_register_or_imm( - &mut tester, - b, - c, - c_imm, - opcode.global_opcode().as_usize(), - &mut rng, - ); - tester.execute(&mut chip, &instruction); - - let (a, _, _) = run_shift::(opcode, &b, &c); - assert_eq!( - a.map(F::from_canonical_u8), - tester.read::(1, rd) - ) + set_and_execute(&mut tester, &mut chip, &mut rng, opcode, None, None, None); } let tester = tester.build().load(chip).load(bitwise_chip).finalize(); tester.simple_test().expect("Verification failed"); } +////////////////////////////////////////////////////////////////////////////////////// +// NEGATIVE TESTS +// +// Given a fake trace of a single operation, setup a chip and run the test. We replace +// part of the trace and check that the chip throws the expected error. +////////////////////////////////////////////////////////////////////////////////////// + +#[derive(Clone, Copy, Default, PartialEq)] +struct ShiftPrankValues { + pub bit_shift: Option, + pub bit_multiplier_left: Option, + pub bit_multiplier_right: Option, + pub b_sign: Option, + pub bit_shift_marker: Option<[u32; LIMB_BITS]>, + pub limb_shift_marker: Option<[u32; NUM_LIMBS]>, + pub bit_shift_carry: Option<[u32; NUM_LIMBS]>, +} + +#[allow(clippy::too_many_arguments)] +fn run_negative_shift_test( + opcode: ShiftOpcode, + prank_a: [u32; RV32_REGISTER_NUM_LIMBS], + b: [u8; RV32_REGISTER_NUM_LIMBS], + c: [u8; RV32_REGISTER_NUM_LIMBS], + prank_vals: ShiftPrankValues, + interaction_error: bool, +) { + let mut rng = create_seeded_rng(); + let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); + let (mut chip, bitwise_chip) = create_test_chip(&tester); + + set_and_execute( + &mut tester, + &mut chip, + &mut rng, + opcode, + Some(b), + Some(false), + Some(c), + ); + + let adapter_width = BaseAir::::width(&chip.air.adapter); + let modify_trace = |trace: &mut DenseMatrix| { + let mut values = trace.row_slice(0).to_vec(); + let cols: &mut ShiftCoreCols = + values.split_at_mut(adapter_width).1.borrow_mut(); + + cols.a = prank_a.map(F::from_canonical_u32); + if let Some(bit_multiplier_left) = prank_vals.bit_multiplier_left { + cols.bit_multiplier_left = F::from_canonical_u32(bit_multiplier_left); + } + if let Some(bit_multiplier_right) = prank_vals.bit_multiplier_right { + cols.bit_multiplier_right = F::from_canonical_u32(bit_multiplier_right); + } + if let Some(b_sign) = prank_vals.b_sign { + cols.b_sign = F::from_canonical_u32(b_sign); + } + if let Some(bit_shift_marker) = prank_vals.bit_shift_marker { + cols.bit_shift_marker = bit_shift_marker.map(F::from_canonical_u32); + } + if let Some(limb_shift_marker) = prank_vals.limb_shift_marker { + cols.limb_shift_marker = limb_shift_marker.map(F::from_canonical_u32); + } + if let Some(bit_shift_carry) = prank_vals.bit_shift_carry { + cols.bit_shift_carry = bit_shift_carry.map(F::from_canonical_u32); + } + + *trace = RowMajorMatrix::new(values, trace.width()); + }; + + disable_debug_builder(); + let tester = tester + .build() + .load_and_prank_trace(chip, modify_trace) + .load(bitwise_chip) + .finalize(); + tester.simple_test_with_expected_error(get_verification_error(interaction_error)); +} + #[test] -fn rv32_shift_sll_rand_test() { - run_rv32_shift_rand_test(ShiftOpcode::SLL, 100); +fn rv32_shift_wrong_negative_test() { + let a = [1, 0, 0, 0]; + let b = [1, 0, 0, 0]; + let c = [1, 0, 0, 0]; + let prank_vals = Default::default(); + run_negative_shift_test(SLL, a, b, c, prank_vals, false); + run_negative_shift_test(SRL, a, b, c, prank_vals, false); + run_negative_shift_test(SRA, a, b, c, prank_vals, false); } #[test] -fn rv32_shift_srl_rand_test() { - run_rv32_shift_rand_test(ShiftOpcode::SRL, 100); +fn rv32_sll_wrong_bit_shift_negative_test() { + let a = [0, 4, 4, 4]; + let b = [1, 1, 1, 1]; + let c = [9, 10, 100, 0]; + let prank_vals = ShiftPrankValues { + bit_shift: Some(2), + bit_multiplier_left: Some(4), + bit_shift_marker: Some([0, 0, 1, 0, 0, 0, 0, 0]), + ..Default::default() + }; + run_negative_shift_test(SLL, a, b, c, prank_vals, true); } #[test] -fn rv32_shift_sra_rand_test() { - run_rv32_shift_rand_test(ShiftOpcode::SRA, 100); +fn rv32_sll_wrong_limb_shift_negative_test() { + let a = [0, 0, 2, 2]; + let b = [1, 1, 1, 1]; + let c = [9, 0, 0, 0]; + let prank_vals = ShiftPrankValues { + limb_shift_marker: Some([0, 0, 1, 0]), + ..Default::default() + }; + run_negative_shift_test(SLL, a, b, c, prank_vals, true); } -////////////////////////////////////////////////////////////////////////////////////// -// NEGATIVE TESTS -// -// Given a fake trace of a single operation, setup a chip and run the test. We replace -// the write part of the trace and check that the core chip throws the expected error. -// A dummy adapter is used so memory interactions don't indirectly cause false passes. -////////////////////////////////////////////////////////////////////////////////////// +#[test] +fn rv32_sll_wrong_bit_carry_negative_test() { + let a = [0, 510, 510, 510]; + let b = [255, 255, 255, 255]; + let c = [9, 0, 0, 0]; + let prank_vals = ShiftPrankValues { + bit_shift_carry: Some([0, 0, 0, 0]), + ..Default::default() + }; + run_negative_shift_test(SLL, a, b, c, prank_vals, true); +} + +#[test] +fn rv32_sll_wrong_bit_mult_side_negative_test() { + let a = [128, 128, 128, 0]; + let b = [1, 1, 1, 1]; + let c = [9, 0, 0, 0]; + let prank_vals = ShiftPrankValues { + bit_multiplier_left: Some(0), + bit_multiplier_right: Some(1), + ..Default::default() + }; + run_negative_shift_test(SLL, a, b, c, prank_vals, false); +} + +#[test] +fn rv32_srl_wrong_bit_shift_negative_test() { + let a = [0, 0, 32, 0]; + let b = [0, 0, 0, 128]; + let c = [9, 0, 0, 0]; + let prank_vals = ShiftPrankValues { + bit_shift: Some(2), + bit_multiplier_left: Some(4), + bit_shift_marker: Some([0, 0, 1, 0, 0, 0, 0, 0]), + ..Default::default() + }; + run_negative_shift_test(SRL, a, b, c, prank_vals, false); +} + +#[test] +fn rv32_srl_wrong_limb_shift_negative_test() { + let a = [0, 64, 0, 0]; + let b = [0, 0, 0, 128]; + let c = [9, 0, 0, 0]; + let prank_vals = ShiftPrankValues { + limb_shift_marker: Some([0, 1, 0, 0]), + ..Default::default() + }; + run_negative_shift_test(SRL, a, b, c, prank_vals, false); +} -// type Rv32ShiftTestChip = -// VmChipWrapper, ShiftStep>; - -// #[derive(Clone, Copy, Default, PartialEq)] -// struct ShiftPrankValues { -// pub bit_shift: Option, -// pub bit_multiplier_left: Option, -// pub bit_multiplier_right: Option, -// pub b_sign: Option, -// pub bit_shift_marker: Option<[u32; LIMB_BITS]>, -// pub limb_shift_marker: Option<[u32; NUM_LIMBS]>, -// pub bit_shift_carry: Option<[u32; NUM_LIMBS]>, -// } - -// #[allow(clippy::too_many_arguments)] -// fn run_rv32_shift_negative_test( -// opcode: ShiftOpcode, -// a: [u32; RV32_REGISTER_NUM_LIMBS], -// b: [u32; RV32_REGISTER_NUM_LIMBS], -// c: [u32; RV32_REGISTER_NUM_LIMBS], -// prank_vals: ShiftPrankValues, -// interaction_error: bool, -// ) { -// let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); -// let (mut chip, bitwise_chip) = create_test_chip(&tester); -// let range_checker_chip = tester.range_checker(); - -// tester.execute( -// &mut chip, -// &Instruction::from_usize(opcode.global_opcode(), [0, 0, 0, 1, 1]), -// ); - -// let bit_shift = prank_vals -// .bit_shift -// .unwrap_or(c[0] % (RV32_CELL_BITS as u32)); -// let bit_shift_carry = prank_vals -// .bit_shift_carry -// .unwrap_or(array::from_fn(|i| match opcode { -// ShiftOpcode::SLL => b[i] >> ((RV32_CELL_BITS as u32) - bit_shift), -// _ => b[i] % (1 << bit_shift), -// })); - -// range_checker_chip.clear(); -// range_checker_chip.add_count(bit_shift, RV32_CELL_BITS.ilog2() as usize); -// for (a_val, carry_val) in a.iter().zip(bit_shift_carry.iter()) { -// range_checker_chip.add_count(*a_val, RV32_CELL_BITS); -// range_checker_chip.add_count(*carry_val, bit_shift as usize); -// } - -// let trace_width = chip.trace_width(); -// let adapter_width = BaseAir::::width(&chip.air.adapter); - -// let modify_trace = |trace: &mut DenseMatrix| { -// let mut values = trace.row_slice(0).to_vec(); -// let cols: &mut ShiftCoreCols = -// values.split_at_mut(adapter_width).1.borrow_mut(); - -// cols.a = a.map(F::from_canonical_u32); -// if let Some(bit_multiplier_left) = prank_vals.bit_multiplier_left { -// cols.bit_multiplier_left = F::from_canonical_u32(bit_multiplier_left); -// } -// if let Some(bit_multiplier_right) = prank_vals.bit_multiplier_right { -// cols.bit_multiplier_right = F::from_canonical_u32(bit_multiplier_right); -// } -// if let Some(b_sign) = prank_vals.b_sign { -// cols.b_sign = F::from_canonical_u32(b_sign); -// } -// if let Some(bit_shift_marker) = prank_vals.bit_shift_marker { -// cols.bit_shift_marker = bit_shift_marker.map(F::from_canonical_u32); -// } -// if let Some(limb_shift_marker) = prank_vals.limb_shift_marker { -// cols.limb_shift_marker = limb_shift_marker.map(F::from_canonical_u32); -// } -// if let Some(bit_shift_carry) = prank_vals.bit_shift_carry { -// cols.bit_shift_carry = bit_shift_carry.map(F::from_canonical_u32); -// } - -// *trace = RowMajorMatrix::new(values, trace_width); -// }; - -// disable_debug_builder(); -// let tester = tester -// .build() -// .load_and_prank_trace(chip, modify_trace) -// .load(bitwise_chip) -// .finalize(); -// tester.simple_test_with_expected_error(if interaction_error { -// VerificationError::ChallengePhaseError -// } else { -// VerificationError::OodEvaluationMismatch -// }); -// } - -// #[test] -// fn rv32_shift_wrong_negative_test() { -// let a = [1, 0, 0, 0]; -// let b = [1, 0, 0, 0]; -// let c = [1, 0, 0, 0]; -// let prank_vals = Default::default(); -// run_rv32_shift_negative_test(ShiftOpcode::SLL, a, b, c, prank_vals, false); -// run_rv32_shift_negative_test(ShiftOpcode::SRL, a, b, c, prank_vals, false); -// run_rv32_shift_negative_test(ShiftOpcode::SRA, a, b, c, prank_vals, false); -// } - -// #[test] -// fn rv32_sll_wrong_bit_shift_negative_test() { -// let a = [0, 4, 4, 4]; -// let b = [1, 1, 1, 1]; -// let c = [9, 10, 100, 0]; -// let prank_vals = ShiftPrankValues { -// bit_shift: Some(2), -// bit_multiplier_left: Some(4), -// bit_shift_marker: Some([0, 0, 1, 0, 0, 0, 0, 0]), -// ..Default::default() -// }; -// run_rv32_shift_negative_test(ShiftOpcode::SLL, a, b, c, prank_vals, true); -// } - -// #[test] -// fn rv32_sll_wrong_limb_shift_negative_test() { -// let a = [0, 0, 2, 2]; -// let b = [1, 1, 1, 1]; -// let c = [9, 0, 0, 0]; -// let prank_vals = ShiftPrankValues { -// limb_shift_marker: Some([0, 0, 1, 0]), -// ..Default::default() -// }; -// run_rv32_shift_negative_test(ShiftOpcode::SLL, a, b, c, prank_vals, true); -// } - -// #[test] -// fn rv32_sll_wrong_bit_carry_negative_test() { -// let a = [0, 510, 510, 510]; -// let b = [255, 255, 255, 255]; -// let c = [9, 0, 0, 0]; -// let prank_vals = ShiftPrankValues { -// bit_shift_carry: Some([0, 0, 0, 0]), -// ..Default::default() -// }; -// run_rv32_shift_negative_test(ShiftOpcode::SLL, a, b, c, prank_vals, true); -// } - -// #[test] -// fn rv32_sll_wrong_bit_mult_side_negative_test() { -// let a = [128, 128, 128, 0]; -// let b = [1, 1, 1, 1]; -// let c = [9, 0, 0, 0]; -// let prank_vals = ShiftPrankValues { -// bit_multiplier_left: Some(0), -// bit_multiplier_right: Some(1), -// ..Default::default() -// }; -// run_rv32_shift_negative_test(ShiftOpcode::SLL, a, b, c, prank_vals, false); -// } - -// #[test] -// fn rv32_srl_wrong_bit_shift_negative_test() { -// let a = [0, 0, 32, 0]; -// let b = [0, 0, 0, 128]; -// let c = [9, 0, 0, 0]; -// let prank_vals = ShiftPrankValues { -// bit_shift: Some(2), -// bit_multiplier_left: Some(4), -// bit_shift_marker: Some([0, 0, 1, 0, 0, 0, 0, 0]), -// ..Default::default() -// }; -// run_rv32_shift_negative_test(ShiftOpcode::SRL, a, b, c, prank_vals, false); -// } - -// #[test] -// fn rv32_srl_wrong_limb_shift_negative_test() { -// let a = [0, 64, 0, 0]; -// let b = [0, 0, 0, 128]; -// let c = [9, 0, 0, 0]; -// let prank_vals = ShiftPrankValues { -// limb_shift_marker: Some([0, 1, 0, 0]), -// ..Default::default() -// }; -// run_rv32_shift_negative_test(ShiftOpcode::SRL, a, b, c, prank_vals, false); -// } - -// #[test] -// fn rv32_srx_wrong_bit_mult_side_negative_test() { -// let a = [0, 0, 0, 0]; -// let b = [0, 0, 0, 128]; -// let c = [9, 0, 0, 0]; -// let prank_vals = ShiftPrankValues { -// bit_multiplier_left: Some(1), -// bit_multiplier_right: Some(0), -// ..Default::default() -// }; -// run_rv32_shift_negative_test(ShiftOpcode::SRL, a, b, c, prank_vals, false); -// run_rv32_shift_negative_test(ShiftOpcode::SRA, a, b, c, prank_vals, false); -// } - -// #[test] -// fn rv32_sra_wrong_bit_shift_negative_test() { -// let a = [0, 0, 224, 255]; -// let b = [0, 0, 0, 128]; -// let c = [9, 0, 0, 0]; -// let prank_vals = ShiftPrankValues { -// bit_shift: Some(2), -// bit_multiplier_left: Some(4), -// bit_shift_marker: Some([0, 0, 1, 0, 0, 0, 0, 0]), -// ..Default::default() -// }; -// run_rv32_shift_negative_test(ShiftOpcode::SRA, a, b, c, prank_vals, false); -// } - -// #[test] -// fn rv32_sra_wrong_limb_shift_negative_test() { -// let a = [0, 192, 255, 255]; -// let b = [0, 0, 0, 128]; -// let c = [9, 0, 0, 0]; -// let prank_vals = ShiftPrankValues { -// limb_shift_marker: Some([0, 1, 0, 0]), -// ..Default::default() -// }; -// run_rv32_shift_negative_test(ShiftOpcode::SRA, a, b, c, prank_vals, false); -// } - -// #[test] -// fn rv32_sra_wrong_sign_negative_test() { -// let a = [0, 0, 64, 0]; -// let b = [0, 0, 0, 128]; -// let c = [9, 0, 0, 0]; -// let prank_vals = ShiftPrankValues { -// b_sign: Some(0), -// ..Default::default() -// }; -// run_rv32_shift_negative_test(ShiftOpcode::SRA, a, b, c, prank_vals, true); -// } +#[test] +fn rv32_srx_wrong_bit_mult_side_negative_test() { + let a = [0, 0, 0, 0]; + let b = [0, 0, 0, 128]; + let c = [9, 0, 0, 0]; + let prank_vals = ShiftPrankValues { + bit_multiplier_left: Some(1), + bit_multiplier_right: Some(0), + ..Default::default() + }; + run_negative_shift_test(SRL, a, b, c, prank_vals, false); + run_negative_shift_test(SRA, a, b, c, prank_vals, false); +} + +#[test] +fn rv32_sra_wrong_bit_shift_negative_test() { + let a = [0, 0, 224, 255]; + let b = [0, 0, 0, 128]; + let c = [9, 0, 0, 0]; + let prank_vals = ShiftPrankValues { + bit_shift: Some(2), + bit_multiplier_left: Some(4), + bit_shift_marker: Some([0, 0, 1, 0, 0, 0, 0, 0]), + ..Default::default() + }; + run_negative_shift_test(SRA, a, b, c, prank_vals, false); +} + +#[test] +fn rv32_sra_wrong_limb_shift_negative_test() { + let a = [0, 192, 255, 255]; + let b = [0, 0, 0, 128]; + let c = [9, 0, 0, 0]; + let prank_vals = ShiftPrankValues { + limb_shift_marker: Some([0, 1, 0, 0]), + ..Default::default() + }; + run_negative_shift_test(SRA, a, b, c, prank_vals, false); +} + +#[test] +fn rv32_sra_wrong_sign_negative_test() { + let a = [0, 0, 64, 0]; + let b = [0, 0, 0, 128]; + let c = [9, 0, 0, 0]; + let prank_vals = ShiftPrankValues { + b_sign: Some(0), + ..Default::default() + }; + run_negative_shift_test(SRA, a, b, c, prank_vals, true); +} /////////////////////////////////////////////////////////////////////////////////////// /// SANITY TESTS @@ -390,7 +364,7 @@ fn run_sll_sanity_test() { let y: [u8; RV32_REGISTER_NUM_LIMBS] = [91, 0, 100, 0]; let z: [u8; RV32_REGISTER_NUM_LIMBS] = [0, 0, 0, 104]; let (result, limb_shift, bit_shift) = - run_shift::(ShiftOpcode::SLL, &x, &y); + run_shift::(SLL, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], result[i]) } @@ -405,7 +379,7 @@ fn run_srl_sanity_test() { let y: [u8; RV32_REGISTER_NUM_LIMBS] = [49, 190, 190, 190]; let z: [u8; RV32_REGISTER_NUM_LIMBS] = [110, 100, 0, 0]; let (result, limb_shift, bit_shift) = - run_shift::(ShiftOpcode::SRL, &x, &y); + run_shift::(SRL, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], result[i]) } @@ -420,7 +394,7 @@ fn run_sra_sanity_test() { let y: [u8; RV32_REGISTER_NUM_LIMBS] = [113, 20, 50, 80]; let z: [u8; RV32_REGISTER_NUM_LIMBS] = [110, 228, 255, 255]; let (result, limb_shift, bit_shift) = - run_shift::(ShiftOpcode::SRA, &x, &y); + run_shift::(SRA, &x, &y); for i in 0..RV32_REGISTER_NUM_LIMBS { assert_eq!(z[i], result[i]) } diff --git a/extensions/rv32im/circuit/src/test_utils.rs b/extensions/rv32im/circuit/src/test_utils.rs index 62fd55cf59..f018b0d845 100644 --- a/extensions/rv32im/circuit/src/test_utils.rs +++ b/extensions/rv32im/circuit/src/test_utils.rs @@ -1,6 +1,6 @@ use openvm_circuit::arch::testing::{memory::gen_pointer, VmChipTestBuilder}; use openvm_instructions::{instruction::Instruction, VmOpcode}; -use openvm_stark_backend::p3_field::FieldAlgebra; +use openvm_stark_backend::{p3_field::FieldAlgebra, verifier::VerificationError}; use openvm_stark_sdk::p3_baby_bear::BabyBear; use rand::{rngs::StdRng, Rng}; @@ -52,3 +52,14 @@ pub fn generate_rv32_is_type_immediate(rng: &mut StdRng) -> (usize, [u8; RV32_RE ], ) } + +/// Returns the corresponding verification error based on whether +/// an interaction error or a constraint error is expected +#[cfg_attr(all(feature = "test-utils", not(test)), allow(dead_code))] +pub fn get_verification_error(is_interaction_error: bool) -> VerificationError { + if is_interaction_error { + VerificationError::ChallengePhaseError + } else { + VerificationError::OodEvaluationMismatch + } +} From 5506f0643388622e16d4399a8d3c49197c727490 Mon Sep 17 00:00:00 2001 From: Arayi Khalatyan <127004086+arayikhalatyan@users.noreply.github.com> Date: Wed, 7 May 2025 12:06:09 -0400 Subject: [PATCH 15/49] fix: divrem tests (#1633) Fixed an error in divrem negative tests. The trace pranking was done incorrectly. 2 instructions were being called (so the trace had height 2) each time but only one of the rows was being modified. Changed it so only one instruction is called each time Also, made the setup_tracing the default --- crates/vm/src/arch/testing/mod.rs | 1 + .../rv32im/circuit/src/base_alu/tests.rs | 3 +- extensions/rv32im/circuit/src/divrem/tests.rs | 100 +++++++++--------- .../rv32im/circuit/src/hintstore/tests.rs | 8 +- .../circuit/src/load_sign_extend/tests.rs | 3 +- .../rv32im/circuit/src/loadstore/tests.rs | 3 +- 6 files changed, 56 insertions(+), 62 deletions(-) diff --git a/crates/vm/src/arch/testing/mod.rs b/crates/vm/src/arch/testing/mod.rs index 0bc8ccdd5f..4d94e96e44 100644 --- a/crates/vm/src/arch/testing/mod.rs +++ b/crates/vm/src/arch/testing/mod.rs @@ -248,6 +248,7 @@ impl VmChipTestBuilder { impl Default for VmChipTestBuilder { fn default() -> Self { + setup_tracing_with_log_level(Level::INFO); let mem_config = MemoryConfig::default(); let range_checker = SharedVariableRangeCheckerChip::new(VariableRangeCheckerBus::new( RANGE_CHECKER_BUS, diff --git a/extensions/rv32im/circuit/src/base_alu/tests.rs b/extensions/rv32im/circuit/src/base_alu/tests.rs index 3d9456fa26..194d536aeb 100644 --- a/extensions/rv32im/circuit/src/base_alu/tests.rs +++ b/extensions/rv32im/circuit/src/base_alu/tests.rs @@ -18,7 +18,7 @@ use openvm_stark_backend::{ }, utils::disable_debug_builder, }; -use openvm_stark_sdk::{config::setup_tracing, p3_baby_bear::BabyBear, utils::create_seeded_rng}; +use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; use test_case::test_case; @@ -118,7 +118,6 @@ fn set_and_execute( #[test_case(OR, 100)] #[test_case(AND, 100)] fn rand_rv32_alu_test(opcode: BaseAluOpcode, num_ops: usize) { - setup_tracing(); let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); diff --git a/extensions/rv32im/circuit/src/divrem/tests.rs b/extensions/rv32im/circuit/src/divrem/tests.rs index 178b1bc87c..da775724d2 100644 --- a/extensions/rv32im/circuit/src/divrem/tests.rs +++ b/extensions/rv32im/circuit/src/divrem/tests.rs @@ -14,7 +14,7 @@ use openvm_circuit_primitives::{ range_tuple::{RangeTupleCheckerBus, SharedRangeTupleCheckerChip}, }; use openvm_instructions::{instruction::Instruction, LocalOpcode}; -use openvm_rv32im_transpiler::DivRemOpcode; +use openvm_rv32im_transpiler::DivRemOpcode::{self, *}; use openvm_stark_backend::{ p3_air::BaseAir, p3_field::{Field, FieldAlgebra}, @@ -112,8 +112,8 @@ fn set_and_execute>( tester.write::(1, rs1, b.map(F::from_canonical_u32)); tester.write::(1, rs2, c.map(F::from_canonical_u32)); - let is_div = opcode == DivRemOpcode::DIV || opcode == DivRemOpcode::DIVU; - let is_signed = opcode == DivRemOpcode::DIV || opcode == DivRemOpcode::REM; + let is_div = opcode == DIV || opcode == DIVU; + let is_signed = opcode == DIV || opcode == REM; let (q, r, _, _, _, _) = run_divrem::(is_signed, &b, &c); @@ -135,10 +135,10 @@ fn set_and_execute>( // passes all constraints. ////////////////////////////////////////////////////////////////////////////////////// -#[test_case(DivRemOpcode::DIV, 100)] -#[test_case(DivRemOpcode::DIVU, 100)] -#[test_case(DivRemOpcode::REM, 100)] -#[test_case(DivRemOpcode::REMU, 100)] +#[test_case(DIV, 100)] +#[test_case(DIVU, 100)] +#[test_case(REM, 100)] +#[test_case(REMU, 100)] fn rand_divrem_test(opcode: DivRemOpcode, num_ops: usize) { let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); @@ -234,7 +234,7 @@ struct DivRemPrankValues { } fn run_negative_divrem_test( - signed: bool, + opcode: DivRemOpcode, b: [u32; RV32_REGISTER_NUM_LIMBS], c: [u32; RV32_REGISTER_NUM_LIMBS], prank_vals: DivRemPrankValues, @@ -244,29 +244,7 @@ fn run_negative_divrem_test( let mut tester = VmChipTestBuilder::default(); let (mut chip, bitwise_chip, range_tuple_chip) = create_test_chip(&mut tester); - let (div_opcode, rem_opcode) = if signed { - (DivRemOpcode::DIV, DivRemOpcode::REM) - } else { - (DivRemOpcode::DIVU, DivRemOpcode::REMU) - }; - - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - div_opcode, - Some(b), - Some(c), - ); - - set_and_execute( - &mut tester, - &mut chip, - &mut rng, - rem_opcode, - Some(b), - Some(c), - ); + set_and_execute(&mut tester, &mut chip, &mut rng, opcode, Some(b), Some(c)); let adapter_width = BaseAir::::width(&chip.air.adapter); let modify_trace = |trace: &mut DenseMatrix| { @@ -321,7 +299,8 @@ fn rv32_divrem_unsigned_wrong_q_negative_test() { q: Some([245, 168, 7, 0]), ..Default::default() }; - run_negative_divrem_test(false, b, c, prank_vals, true); + run_negative_divrem_test(DIVU, b, c, prank_vals, true); + run_negative_divrem_test(REMU, b, c, prank_vals, true); } #[test] @@ -334,7 +313,8 @@ fn rv32_divrem_unsigned_wrong_r_negative_test() { diff_val: Some(31), ..Default::default() }; - run_negative_divrem_test(false, b, c, prank_vals, true); + run_negative_divrem_test(DIVU, b, c, prank_vals, true); + run_negative_divrem_test(REMU, b, c, prank_vals, true); } #[test] @@ -345,7 +325,8 @@ fn rv32_divrem_unsigned_high_mult_negative_test() { q: Some([128, 0, 0, 1]), ..Default::default() }; - run_negative_divrem_test(false, b, c, prank_vals, true); + run_negative_divrem_test(DIVU, b, c, prank_vals, true); + run_negative_divrem_test(REMU, b, c, prank_vals, true); } #[test] @@ -358,7 +339,8 @@ fn rv32_divrem_unsigned_zero_divisor_wrong_r_negative_test() { diff_val: Some(255), ..Default::default() }; - run_negative_divrem_test(false, b, c, prank_vals, true); + run_negative_divrem_test(DIVU, b, c, prank_vals, true); + run_negative_divrem_test(REMU, b, c, prank_vals, true); } #[test] @@ -369,7 +351,8 @@ fn rv32_divrem_signed_wrong_q_negative_test() { q: Some([74, 61, 255, 255]), ..Default::default() }; - run_negative_divrem_test(true, b, c, prank_vals, true); + run_negative_divrem_test(DIV, b, c, prank_vals, true); + run_negative_divrem_test(REM, b, c, prank_vals, true); } #[test] @@ -382,7 +365,8 @@ fn rv32_divrem_signed_wrong_r_negative_test() { diff_val: Some(20), ..Default::default() }; - run_negative_divrem_test(true, b, c, prank_vals, true); + run_negative_divrem_test(DIV, b, c, prank_vals, true); + run_negative_divrem_test(REM, b, c, prank_vals, true); } #[test] @@ -393,7 +377,8 @@ fn rv32_divrem_signed_high_mult_negative_test() { q: Some([1, 0, 0, 1]), ..Default::default() }; - run_negative_divrem_test(true, b, c, prank_vals, true); + run_negative_divrem_test(DIV, b, c, prank_vals, true); + run_negative_divrem_test(REM, b, c, prank_vals, true); } #[test] @@ -407,7 +392,8 @@ fn rv32_divrem_signed_r_wrong_sign_negative_test() { diff_val: Some(192), ..Default::default() }; - run_negative_divrem_test(true, b, c, prank_vals, false); + run_negative_divrem_test(DIV, b, c, prank_vals, false); + run_negative_divrem_test(REM, b, c, prank_vals, false); } #[test] @@ -421,7 +407,8 @@ fn rv32_divrem_signed_r_wrong_prime_negative_test() { diff_val: Some(36), ..Default::default() }; - run_negative_divrem_test(true, b, c, prank_vals, false); + run_negative_divrem_test(DIV, b, c, prank_vals, false); + run_negative_divrem_test(REM, b, c, prank_vals, false); } #[test] @@ -434,7 +421,8 @@ fn rv32_divrem_signed_zero_divisor_wrong_r_negative_test() { diff_val: Some(1), ..Default::default() }; - run_negative_divrem_test(true, b, c, prank_vals, true); + run_negative_divrem_test(DIV, b, c, prank_vals, true); + run_negative_divrem_test(REM, b, c, prank_vals, true); } #[test] @@ -449,8 +437,10 @@ fn rv32_divrem_false_zero_divisor_flag_negative_test() { zero_divisor: Some(true), ..Default::default() }; - run_negative_divrem_test(true, b, c, prank_vals, false); - run_negative_divrem_test(false, b, c, prank_vals, false); + run_negative_divrem_test(DIVU, b, c, prank_vals, false); + run_negative_divrem_test(REMU, b, c, prank_vals, false); + run_negative_divrem_test(DIV, b, c, prank_vals, false); + run_negative_divrem_test(REM, b, c, prank_vals, false); } #[test] @@ -465,8 +455,10 @@ fn rv32_divrem_false_r_zero_flag_negative_test() { r_zero: Some(true), ..Default::default() }; - run_negative_divrem_test(true, b, c, prank_vals, false); - run_negative_divrem_test(false, b, c, prank_vals, false); + run_negative_divrem_test(DIVU, b, c, prank_vals, false); + run_negative_divrem_test(REMU, b, c, prank_vals, false); + run_negative_divrem_test(DIV, b, c, prank_vals, false); + run_negative_divrem_test(REM, b, c, prank_vals, false); } #[test] @@ -477,8 +469,10 @@ fn rv32_divrem_unset_zero_divisor_flag_negative_test() { zero_divisor: Some(false), ..Default::default() }; - run_negative_divrem_test(true, b, c, prank_vals, false); - run_negative_divrem_test(false, b, c, prank_vals, false); + run_negative_divrem_test(DIVU, b, c, prank_vals, false); + run_negative_divrem_test(REMU, b, c, prank_vals, false); + run_negative_divrem_test(DIV, b, c, prank_vals, false); + run_negative_divrem_test(REM, b, c, prank_vals, false); } #[test] @@ -490,8 +484,10 @@ fn rv32_divrem_wrong_r_zero_flag_negative_test() { r_zero: Some(true), ..Default::default() }; - run_negative_divrem_test(true, b, c, prank_vals, false); - run_negative_divrem_test(false, b, c, prank_vals, false); + run_negative_divrem_test(DIVU, b, c, prank_vals, false); + run_negative_divrem_test(REMU, b, c, prank_vals, false); + run_negative_divrem_test(DIV, b, c, prank_vals, false); + run_negative_divrem_test(REM, b, c, prank_vals, false); } #[test] @@ -502,8 +498,10 @@ fn rv32_divrem_unset_r_zero_flag_negative_test() { r_zero: Some(false), ..Default::default() }; - run_negative_divrem_test(true, b, c, prank_vals, false); - run_negative_divrem_test(false, b, c, prank_vals, false); + run_negative_divrem_test(DIVU, b, c, prank_vals, false); + run_negative_divrem_test(REMU, b, c, prank_vals, false); + run_negative_divrem_test(DIV, b, c, prank_vals, false); + run_negative_divrem_test(REM, b, c, prank_vals, false); } /////////////////////////////////////////////////////////////////////////////////////// diff --git a/extensions/rv32im/circuit/src/hintstore/tests.rs b/extensions/rv32im/circuit/src/hintstore/tests.rs index 5af467be37..c56bfe185d 100644 --- a/extensions/rv32im/circuit/src/hintstore/tests.rs +++ b/extensions/rv32im/circuit/src/hintstore/tests.rs @@ -8,9 +8,8 @@ use openvm_circuit::arch::{ testing::{memory::gen_pointer, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, ExecutionBridge, Streams, }; -use openvm_circuit_primitives::{ - bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, - var_range::SharedVariableRangeCheckerChip, +use openvm_circuit_primitives::bitwise_op_lookup::{ + BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, }; use openvm_instructions::{ instruction::Instruction, @@ -26,7 +25,7 @@ use openvm_stark_backend::{ }, utils::disable_debug_builder, }; -use openvm_stark_sdk::{config::setup_tracing, p3_baby_bear::BabyBear, utils::create_seeded_rng}; +use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; use super::{Rv32HintStoreAir, Rv32HintStoreChip, Rv32HintStoreCols, Rv32HintStoreStep}; @@ -151,7 +150,6 @@ fn set_and_execute_buffer( #[test] fn rand_hintstore_test() { - setup_tracing(); let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); diff --git a/extensions/rv32im/circuit/src/load_sign_extend/tests.rs b/extensions/rv32im/circuit/src/load_sign_extend/tests.rs index a09edec6b6..32474eb494 100644 --- a/extensions/rv32im/circuit/src/load_sign_extend/tests.rs +++ b/extensions/rv32im/circuit/src/load_sign_extend/tests.rs @@ -15,7 +15,7 @@ use openvm_stark_backend::{ }, utils::disable_debug_builder, }; -use openvm_stark_sdk::{config::setup_tracing, p3_baby_bear::BabyBear, utils::create_seeded_rng}; +use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; use test_case::test_case; @@ -148,7 +148,6 @@ fn set_and_execute( #[test_case(LOADB, 100)] #[test_case(LOADH, 100)] fn rand_load_sign_extend_test(opcode: Rv32LoadStoreOpcode, num_ops: usize) { - setup_tracing(); let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); diff --git a/extensions/rv32im/circuit/src/loadstore/tests.rs b/extensions/rv32im/circuit/src/loadstore/tests.rs index 72821c2e37..58b36f16ae 100644 --- a/extensions/rv32im/circuit/src/loadstore/tests.rs +++ b/extensions/rv32im/circuit/src/loadstore/tests.rs @@ -19,7 +19,7 @@ use openvm_stark_backend::{ }, utils::disable_debug_builder, }; -use openvm_stark_sdk::{config::setup_tracing, p3_baby_bear::BabyBear, utils::create_seeded_rng}; +use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, seq::SliceRandom, Rng}; use test_case::test_case; @@ -176,7 +176,6 @@ fn set_and_execute( #[test_case(STOREB, 100)] #[test_case(STOREH, 100)] fn rand_loadstore_test(opcode: Rv32LoadStoreOpcode, num_ops: usize) { - setup_tracing(); let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); let mut chip = create_test_chip(&mut tester); From 9695af7a4dac8f5e9ecf783a6e2e201c3ec77ee7 Mon Sep 17 00:00:00 2001 From: Arayi Khalatyan <127004086+arayikhalatyan@users.noreply.github.com> Date: Mon, 12 May 2025 14:41:30 -0400 Subject: [PATCH 16/49] feat: bigint circuit to compile (#1639) Implemented e1 and e3 for HeapBranch, Heap, and VecHeap adapters. Updated the Bigint circuit correspondingly. Had to make some changes in the interfaces of rv32im Steps. In particular - Changed Reads type `([u8; N], [u8; N])` into `Into<[[u8;N];2]>` and Writes type `[u8; N]` into `From<[[u8;N];1]>`. This change corresponds to what we used to do with the previous integration API in order to make the interfaces to match. - Got rid of TraceAdapterContext in a lot of places. This is because the same Step can be using different AdapterSteps that require different TraceContexts. Or even the AdapterStep might require a `TraceContext` that the Step doesn't have. The easy solution was to implement AdapterSteps in a similar way as in the previous integration API. That is, added the necessary fields to the AdapterStep structs. I am thinking maybe deleting the `TraceContext` from the interface makes sense. I am not sure if there is a better way to do this Important Note: the tests don't run right now because a lot of the read/write operations are done in address space 2 with block size 32 but currently only block size 4 is supported by the memory. Resolves INT-3980 --- Cargo.lock | 1 + crates/vm/src/arch/testing/mod.rs | 6 +- extensions/bigint/circuit/Cargo.toml | 1 + extensions/bigint/circuit/src/extension.rs | 158 ++++-- extensions/bigint/circuit/src/lib.rs | 98 +++- extensions/bigint/circuit/src/tests.rs | 507 +++++++++--------- extensions/rv32-adapters/src/heap.rs | 206 +++---- extensions/rv32-adapters/src/heap_branch.rs | 232 ++++---- extensions/rv32-adapters/src/lib.rs | 8 +- extensions/rv32-adapters/src/vec_heap.rs | 434 +++++++-------- extensions/rv32im/circuit/Cargo.toml | 2 +- extensions/rv32im/circuit/src/adapters/alu.rs | 30 +- .../rv32im/circuit/src/adapters/branch.rs | 9 +- extensions/rv32im/circuit/src/adapters/mod.rs | 61 +-- extensions/rv32im/circuit/src/adapters/mul.rs | 17 +- .../rv32im/circuit/src/base_alu/core.rs | 30 +- .../rv32im/circuit/src/base_alu/tests.rs | 2 +- .../rv32im/circuit/src/branch_eq/core.rs | 24 +- .../rv32im/circuit/src/branch_lt/core.rs | 16 +- extensions/rv32im/circuit/src/divrem/core.rs | 24 +- extensions/rv32im/circuit/src/extension.rs | 6 +- .../rv32im/circuit/src/less_than/core.rs | 32 +- .../rv32im/circuit/src/less_than/tests.rs | 2 +- .../rv32im/circuit/src/loadstore/tests.rs | 2 +- extensions/rv32im/circuit/src/mul/core.rs | 23 +- extensions/rv32im/circuit/src/mulh/core.rs | 24 +- extensions/rv32im/circuit/src/shift/core.rs | 30 +- extensions/rv32im/circuit/src/shift/tests.rs | 2 +- 28 files changed, 988 insertions(+), 999 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 942d786eb9..f56c362d30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4143,6 +4143,7 @@ dependencies = [ "openvm-stark-sdk", "rand", "serde", + "test-case", ] [[package]] diff --git a/crates/vm/src/arch/testing/mod.rs b/crates/vm/src/arch/testing/mod.rs index 4d94e96e44..edd46c3df1 100644 --- a/crates/vm/src/arch/testing/mod.rs +++ b/crates/vm/src/arch/testing/mod.rs @@ -140,7 +140,11 @@ impl VmChipTestBuilder { pointer: usize, writes: Vec<[F; NUM_LIMBS]>, ) { - self.write(1usize, register, [F::from_canonical_usize(pointer)]); + self.write( + 1usize, + register, + pointer.to_le_bytes().map(F::from_canonical_u8), + ); for (i, &write) in writes.iter().enumerate() { self.write(2usize, pointer + i * NUM_LIMBS, write); } diff --git a/extensions/bigint/circuit/Cargo.toml b/extensions/bigint/circuit/Cargo.toml index 09d68a9d1b..7d133ff151 100644 --- a/extensions/bigint/circuit/Cargo.toml +++ b/extensions/bigint/circuit/Cargo.toml @@ -29,6 +29,7 @@ serde.workspace = true openvm-stark-sdk = { workspace = true } openvm-circuit = { workspace = true, features = ["test-utils"] } openvm-rv32-adapters = { workspace = true, features = ["test-utils"] } +test-case.workspace = true [features] default = ["parallel", "jemalloc"] diff --git a/extensions/bigint/circuit/src/extension.rs b/extensions/bigint/circuit/src/extension.rs index b9eeeafd99..12f161c0b8 100644 --- a/extensions/bigint/circuit/src/extension.rs +++ b/extensions/bigint/circuit/src/extension.rs @@ -5,11 +5,12 @@ use openvm_bigint_transpiler::{ }; use openvm_circuit::{ arch::{ - SystemConfig, SystemPort, VmExtension, VmInventory, VmInventoryBuilder, VmInventoryError, + ExecutionBridge, SystemConfig, SystemPort, VmExtension, VmInventory, VmInventoryBuilder, + VmInventoryError, }, system::phantom::PhantomChip, }; -use openvm_circuit_derive::{AnyEnum, InstructionExecutor, VmConfig}; +use openvm_circuit_derive::{AnyEnum, InsExecutorE1, InstructionExecutor, VmConfig}; use openvm_circuit_primitives::{ bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, range_tuple::{RangeTupleCheckerBus, SharedRangeTupleCheckerChip}, @@ -25,6 +26,9 @@ use serde::{Deserialize, Serialize}; use crate::*; +// TODO: this should be decided after e2 execution +const MAX_INS_CAPACITY: usize = 1 << 22; + #[derive(Clone, Debug, VmConfig, derive_new::new, Serialize, Deserialize)] pub struct Int256Rv32Config { #[system] @@ -69,7 +73,7 @@ fn default_range_tuple_checker_sizes() -> [u32; 2] { [1 << 8, 32 * (1 << 8)] } -#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)] +#[derive(ChipUsageGetter, Chip, InstructionExecutor, InsExecutorE1, From, AnyEnum)] pub enum Int256Executor { BaseAlu256(Rv32BaseAlu256Chip), LessThan256(Rv32LessThan256Chip), @@ -101,6 +105,8 @@ impl VmExtension for Int256 { program_bus, memory_bridge, } = builder.system_port(); + let execution_bridge = ExecutionBridge::new(execution_bus, program_bus); + let range_checker_chip = builder.system_base().range_checker_chip.clone(); let bitwise_lu_chip = if let Some(&chip) = builder .find_chip::>() @@ -113,8 +119,8 @@ impl VmExtension for Int256 { inventory.add_periphery_chip(chip.clone()); chip }; - let offline_memory = builder.system_base().offline_memory(); - let address_bits = builder.system_config().memory_config.pointer_max_bits; + // let offline_memory = builder.system_base().offline_memory(); + let pointer_max_bits = builder.system_config().memory_config.pointer_max_bits; let range_tuple_chip = if let Some(chip) = builder .find_chip::>() @@ -133,66 +139,97 @@ impl VmExtension for Int256 { }; let base_alu_chip = Rv32BaseAlu256Chip::new( - Rv32HeapAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - address_bits, + VmAirWrapper::new( + Rv32HeapAdapterAir::new( + execution_bridge, + memory_bridge, + bitwise_lu_chip.bus(), + pointer_max_bits, + ), + BaseAluCoreAir::new(bitwise_lu_chip.bus(), Rv32BaseAlu256Opcode::CLASS_OFFSET), + ), + Rv32BaseAlu256Step::new( + Rv32HeapAdapterStep::new(pointer_max_bits, bitwise_lu_chip.clone()), bitwise_lu_chip.clone(), + Rv32BaseAlu256Opcode::CLASS_OFFSET, ), - BaseAluCoreChip::new(bitwise_lu_chip.clone(), Rv32BaseAlu256Opcode::CLASS_OFFSET), - offline_memory.clone(), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); + inventory.add_executor( base_alu_chip, Rv32BaseAlu256Opcode::iter().map(|x| x.global_opcode()), )?; let less_than_chip = Rv32LessThan256Chip::new( - Rv32HeapAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - address_bits, + VmAirWrapper::new( + Rv32HeapAdapterAir::new( + execution_bridge, + memory_bridge, + bitwise_lu_chip.bus(), + pointer_max_bits, + ), + LessThanCoreAir::new(bitwise_lu_chip.bus(), Rv32LessThan256Opcode::CLASS_OFFSET), + ), + Rv32LessThan256Step::new( + Rv32HeapAdapterStep::new(pointer_max_bits, bitwise_lu_chip.clone()), bitwise_lu_chip.clone(), + Rv32LessThan256Opcode::CLASS_OFFSET, ), - LessThanCoreChip::new(bitwise_lu_chip.clone(), Rv32LessThan256Opcode::CLASS_OFFSET), - offline_memory.clone(), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); + inventory.add_executor( less_than_chip, Rv32LessThan256Opcode::iter().map(|x| x.global_opcode()), )?; let branch_equal_chip = Rv32BranchEqual256Chip::new( - Rv32HeapBranchAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - address_bits, - bitwise_lu_chip.clone(), + VmAirWrapper::new( + Rv32HeapBranchAdapterAir::new( + execution_bridge, + memory_bridge, + bitwise_lu_chip.bus(), + pointer_max_bits, + ), + BranchEqualCoreAir::new(Rv32BranchEqual256Opcode::CLASS_OFFSET, DEFAULT_PC_STEP), + ), + Rv32BranchEqual256Step::new( + Rv32HeapBranchAdapterStep::new(pointer_max_bits, bitwise_lu_chip.clone()), + Rv32BranchEqual256Opcode::CLASS_OFFSET, + DEFAULT_PC_STEP, ), - BranchEqualCoreChip::new(Rv32BranchEqual256Opcode::CLASS_OFFSET, DEFAULT_PC_STEP), - offline_memory.clone(), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); + inventory.add_executor( branch_equal_chip, Rv32BranchEqual256Opcode::iter().map(|x| x.global_opcode()), )?; let branch_less_than_chip = Rv32BranchLessThan256Chip::new( - Rv32HeapBranchAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - address_bits, - bitwise_lu_chip.clone(), + VmAirWrapper::new( + Rv32HeapBranchAdapterAir::new( + execution_bridge, + memory_bridge, + bitwise_lu_chip.bus(), + pointer_max_bits, + ), + BranchLessThanCoreAir::new( + bitwise_lu_chip.bus(), + Rv32BranchLessThan256Opcode::CLASS_OFFSET, + ), ), - BranchLessThanCoreChip::new( + Rv32BranchLessThan256Step::new( + Rv32HeapBranchAdapterStep::new(pointer_max_bits, bitwise_lu_chip.clone()), bitwise_lu_chip.clone(), Rv32BranchLessThan256Opcode::CLASS_OFFSET, ), - offline_memory.clone(), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor( branch_less_than_chip, @@ -200,36 +237,53 @@ impl VmExtension for Int256 { )?; let multiplication_chip = Rv32Multiplication256Chip::new( - Rv32HeapAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - address_bits, - bitwise_lu_chip.clone(), + VmAirWrapper::new( + Rv32HeapAdapterAir::new( + execution_bridge, + memory_bridge, + bitwise_lu_chip.bus(), + pointer_max_bits, + ), + MultiplicationCoreAir::new(*range_tuple_chip.bus(), Rv32Mul256Opcode::CLASS_OFFSET), + ), + Rv32Multiplication256Step::new( + Rv32HeapAdapterStep::new(pointer_max_bits, bitwise_lu_chip.clone()), + range_tuple_chip.clone(), + Rv32Mul256Opcode::CLASS_OFFSET, ), - MultiplicationCoreChip::new(range_tuple_chip, Rv32Mul256Opcode::CLASS_OFFSET), - offline_memory.clone(), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); + inventory.add_executor( multiplication_chip, Rv32Mul256Opcode::iter().map(|x| x.global_opcode()), )?; let shift_chip = Rv32Shift256Chip::new( - Rv32HeapAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - address_bits, - bitwise_lu_chip.clone(), + VmAirWrapper::new( + Rv32HeapAdapterAir::new( + execution_bridge, + memory_bridge, + bitwise_lu_chip.bus(), + pointer_max_bits, + ), + ShiftCoreAir::new( + bitwise_lu_chip.bus(), + range_checker_chip.bus(), + Rv32Shift256Opcode::CLASS_OFFSET, + ), ), - ShiftCoreChip::new( + Rv32Shift256Step::new( + Rv32HeapAdapterStep::new(pointer_max_bits, bitwise_lu_chip.clone()), bitwise_lu_chip.clone(), - range_checker_chip, + range_checker_chip.clone(), Rv32Shift256Opcode::CLASS_OFFSET, ), - offline_memory.clone(), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); + inventory.add_executor( shift_chip, Rv32Shift256Opcode::iter().map(|x| x.global_opcode()), diff --git a/extensions/bigint/circuit/src/lib.rs b/extensions/bigint/circuit/src/lib.rs index 295ef73db2..ba971f27a5 100644 --- a/extensions/bigint/circuit/src/lib.rs +++ b/extensions/bigint/circuit/src/lib.rs @@ -1,9 +1,15 @@ -use openvm_circuit::{self, arch::VmChipWrapper}; -use openvm_rv32_adapters::{Rv32HeapAdapterChip, Rv32HeapBranchAdapterChip}; +use openvm_circuit::{ + self, + arch::{NewVmChipWrapper, VmAirWrapper}, +}; +use openvm_rv32_adapters::{ + Rv32HeapAdapterAir, Rv32HeapAdapterStep, Rv32HeapBranchAdapterAir, Rv32HeapBranchAdapterStep, +}; use openvm_rv32im_circuit::{ adapters::{INT256_NUM_LIMBS, RV32_CELL_BITS}, - BaseAluCoreChip, BranchEqualCoreChip, BranchLessThanCoreChip, LessThanCoreChip, - MultiplicationCoreChip, ShiftCoreChip, + BaseAluCoreAir, BaseAluStep, BranchEqualCoreAir, BranchEqualStep, BranchLessThanCoreAir, + BranchLessThanStep, LessThanCoreAir, LessThanStep, MultiplicationCoreAir, MultiplicationStep, + ShiftCoreAir, ShiftStep, }; mod extension; @@ -12,38 +18,74 @@ pub use extension::*; #[cfg(test)] mod tests; -pub type Rv32BaseAlu256Chip = VmChipWrapper< - F, - Rv32HeapAdapterChip, - BaseAluCoreChip, +/// BaseAlu256 +pub type Rv32BaseAlu256Air = VmAirWrapper< + Rv32HeapAdapterAir<2, INT256_NUM_LIMBS, INT256_NUM_LIMBS>, + BaseAluCoreAir, >; +pub type Rv32BaseAlu256Step = BaseAluStep< + Rv32HeapAdapterStep<2, INT256_NUM_LIMBS, INT256_NUM_LIMBS>, + INT256_NUM_LIMBS, + RV32_CELL_BITS, +>; +pub type Rv32BaseAlu256Chip = NewVmChipWrapper; -pub type Rv32LessThan256Chip = VmChipWrapper< - F, - Rv32HeapAdapterChip, - LessThanCoreChip, +/// LessThan256 +pub type Rv32LessThan256Air = VmAirWrapper< + Rv32HeapAdapterAir<2, INT256_NUM_LIMBS, INT256_NUM_LIMBS>, + LessThanCoreAir, +>; +pub type Rv32LessThan256Step = LessThanStep< + Rv32HeapAdapterStep<2, INT256_NUM_LIMBS, INT256_NUM_LIMBS>, + INT256_NUM_LIMBS, + RV32_CELL_BITS, >; +pub type Rv32LessThan256Chip = NewVmChipWrapper; -pub type Rv32Multiplication256Chip = VmChipWrapper< - F, - Rv32HeapAdapterChip, - MultiplicationCoreChip, +/// Multiplication256 +pub type Rv32Multiplication256Air = VmAirWrapper< + Rv32HeapAdapterAir<2, INT256_NUM_LIMBS, INT256_NUM_LIMBS>, + MultiplicationCoreAir, >; +pub type Rv32Multiplication256Step = MultiplicationStep< + Rv32HeapAdapterStep<2, INT256_NUM_LIMBS, INT256_NUM_LIMBS>, + INT256_NUM_LIMBS, + RV32_CELL_BITS, +>; +pub type Rv32Multiplication256Chip = + NewVmChipWrapper; -pub type Rv32Shift256Chip = VmChipWrapper< - F, - Rv32HeapAdapterChip, - ShiftCoreChip, +/// Shift256 +pub type Rv32Shift256Air = VmAirWrapper< + Rv32HeapAdapterAir<2, INT256_NUM_LIMBS, INT256_NUM_LIMBS>, + ShiftCoreAir, +>; +pub type Rv32Shift256Step = ShiftStep< + Rv32HeapAdapterStep<2, INT256_NUM_LIMBS, INT256_NUM_LIMBS>, + INT256_NUM_LIMBS, + RV32_CELL_BITS, >; +pub type Rv32Shift256Chip = NewVmChipWrapper; -pub type Rv32BranchEqual256Chip = VmChipWrapper< - F, - Rv32HeapBranchAdapterChip, - BranchEqualCoreChip, +/// BranchEqual256 +pub type Rv32BranchEqual256Air = VmAirWrapper< + Rv32HeapBranchAdapterAir<2, INT256_NUM_LIMBS>, + BranchEqualCoreAir, >; +pub type Rv32BranchEqual256Step = + BranchEqualStep, INT256_NUM_LIMBS>; +pub type Rv32BranchEqual256Chip = + NewVmChipWrapper; -pub type Rv32BranchLessThan256Chip = VmChipWrapper< - F, - Rv32HeapBranchAdapterChip, - BranchLessThanCoreChip, +/// BranchLessThan256 +pub type Rv32BranchLessThan256Air = VmAirWrapper< + Rv32HeapBranchAdapterAir<2, INT256_NUM_LIMBS>, + BranchLessThanCoreAir, +>; +pub type Rv32BranchLessThan256Step = BranchLessThanStep< + Rv32HeapBranchAdapterStep<2, INT256_NUM_LIMBS>, + INT256_NUM_LIMBS, + RV32_CELL_BITS, >; +pub type Rv32BranchLessThan256Chip = + NewVmChipWrapper; diff --git a/extensions/bigint/circuit/src/tests.rs b/extensions/bigint/circuit/src/tests.rs index 0e26352410..8ae7a30894 100644 --- a/extensions/bigint/circuit/src/tests.rs +++ b/extensions/bigint/circuit/src/tests.rs @@ -5,7 +5,7 @@ use openvm_bigint_transpiler::{ use openvm_circuit::{ arch::{ testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS, RANGE_TUPLE_CHECKER_BUS}, - InstructionExecutor, + InstructionExecutor, VmAirWrapper, }, utils::generate_long_number, }; @@ -13,22 +13,32 @@ use openvm_circuit_primitives::{ bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, range_tuple::{RangeTupleCheckerBus, SharedRangeTupleCheckerChip}, }; -use openvm_instructions::{program::PC_BITS, riscv::RV32_CELL_BITS, LocalOpcode}; +use openvm_instructions::{ + program::{DEFAULT_PC_STEP, PC_BITS}, + riscv::RV32_CELL_BITS, + LocalOpcode, +}; use openvm_rv32_adapters::{ - rv32_heap_branch_default, rv32_write_heap_default, Rv32HeapAdapterChip, - Rv32HeapBranchAdapterChip, + rv32_heap_branch_default, rv32_write_heap_default, Rv32HeapAdapterAir, Rv32HeapAdapterStep, + Rv32HeapBranchAdapterAir, Rv32HeapBranchAdapterStep, }; use openvm_rv32im_circuit::{ adapters::{INT256_NUM_LIMBS, RV_B_TYPE_IMM_BITS}, - BaseAluCoreChip, BranchEqualCoreChip, BranchLessThanCoreChip, LessThanCoreChip, - MultiplicationCoreChip, ShiftCoreChip, + BaseAluCoreAir, BranchEqualCoreAir, BranchLessThanCoreAir, LessThanCoreAir, + MultiplicationCoreAir, ShiftCoreAir, }; use openvm_rv32im_transpiler::{ - BaseAluOpcode, BranchEqualOpcode, BranchLessThanOpcode, LessThanOpcode, ShiftOpcode, + BaseAluOpcode, BranchEqualOpcode, BranchLessThanOpcode, LessThanOpcode, MulOpcode, ShiftOpcode, }; use openvm_stark_backend::p3_field::{FieldAlgebra, PrimeField32}; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; -use rand::Rng; +use rand::{rngs::StdRng, Rng}; +use test_case::test_case; + +use crate::{ + Rv32BaseAlu256Step, Rv32BranchEqual256Step, Rv32BranchLessThan256Step, Rv32LessThan256Step, + Rv32Multiplication256Step, Rv32Shift256Step, +}; use super::{ Rv32BaseAlu256Chip, Rv32BranchEqual256Chip, Rv32BranchLessThan256Chip, Rv32LessThan256Chip, @@ -36,148 +46,144 @@ use super::{ }; type F = BabyBear; +const MAX_INS_CAPACITY: usize = 128; +const ABS_MAX_BRANCH: i32 = 1 << (RV_B_TYPE_IMM_BITS - 1); #[allow(clippy::type_complexity)] -fn run_int_256_rand_execute>( - opcode: usize, - num_ops: usize, - executor: &mut E, +fn set_and_execute_rand>( tester: &mut VmChipTestBuilder, + chip: &mut E, + rng: &mut StdRng, + opcode: usize, branch_fn: Option bool>, ) { - const ABS_MAX_BRANCH: i32 = 1 << (RV_B_TYPE_IMM_BITS - 1); - - let mut rng = create_seeded_rng(); let branch = branch_fn.is_some(); - for _ in 0..num_ops { - let b = generate_long_number::(&mut rng); - let c = generate_long_number::(&mut rng); - if branch { - let imm = rng.gen_range((-ABS_MAX_BRANCH)..ABS_MAX_BRANCH); - let instruction = rv32_heap_branch_default( - tester, - vec![b.map(F::from_canonical_u32)], - vec![c.map(F::from_canonical_u32)], - imm as isize, - opcode, - ); - - tester.execute_with_pc( - executor, - &instruction, - rng.gen_range((ABS_MAX_BRANCH as u32)..(1 << (PC_BITS - 1))), - ); - - let cmp_result = branch_fn.unwrap()(opcode, &b, &c); - let from_pc = tester.execution.last_from_pc().as_canonical_u32() as i32; - let to_pc = tester.execution.last_to_pc().as_canonical_u32() as i32; - assert_eq!(to_pc, from_pc + if cmp_result { imm } else { 4 }); - } else { - let instruction = rv32_write_heap_default( - tester, - vec![b.map(F::from_canonical_u32)], - vec![c.map(F::from_canonical_u32)], - opcode, - ); - tester.execute(executor, &instruction); - } + let b = generate_long_number::(rng); + let c = generate_long_number::(rng); + if branch { + let imm = rng.gen_range((-ABS_MAX_BRANCH)..ABS_MAX_BRANCH); + let instruction = rv32_heap_branch_default( + tester, + vec![b.map(F::from_canonical_u32)], + vec![c.map(F::from_canonical_u32)], + imm as isize, + opcode, + ); + + tester.execute_with_pc( + chip, + &instruction, + rng.gen_range((ABS_MAX_BRANCH as u32)..(1 << (PC_BITS - 1))), + ); + + let cmp_result = branch_fn.unwrap()(opcode, &b, &c); + let from_pc = tester.execution.last_from_pc().as_canonical_u32() as i32; + let to_pc = tester.execution.last_to_pc().as_canonical_u32() as i32; + assert_eq!(to_pc, from_pc + if cmp_result { imm } else { 4 }); + } else { + let instruction = rv32_write_heap_default( + tester, + vec![b.map(F::from_canonical_u32)], + vec![c.map(F::from_canonical_u32)], + opcode, + ); + tester.execute(chip, &instruction); } } +#[test_case(BaseAluOpcode::ADD, 24)] +#[test_case(BaseAluOpcode::SUB, 24)] +#[test_case(BaseAluOpcode::XOR, 24)] +#[test_case(BaseAluOpcode::OR, 24)] +#[test_case(BaseAluOpcode::AND, 24)] fn run_alu_256_rand_test(opcode: BaseAluOpcode, num_ops: usize) { + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let offset = Rv32BaseAlu256Opcode::CLASS_OFFSET; + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let mut tester = VmChipTestBuilder::default(); - let mut chip = Rv32BaseAlu256Chip::::new( - Rv32HeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), + VmAirWrapper::new( + Rv32HeapAdapterAir::new( + tester.execution_bridge(), + tester.memory_bridge(), + bitwise_bus, + tester.address_bits(), + ), + BaseAluCoreAir::new(bitwise_bus, offset), + ), + Rv32BaseAlu256Step::new( + Rv32HeapAdapterStep::new(tester.address_bits(), bitwise_chip.clone()), bitwise_chip.clone(), + offset, ), - BaseAluCoreChip::new(bitwise_chip.clone(), Rv32BaseAlu256Opcode::CLASS_OFFSET), - tester.offline_memory_mutex_arc(), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - run_int_256_rand_execute( - opcode.local_usize() + Rv32BaseAlu256Opcode::CLASS_OFFSET, - num_ops, - &mut chip, - &mut tester, - None, - ); + for _ in 0..num_ops { + set_and_execute_rand( + &mut tester, + &mut chip, + &mut rng, + opcode.local_usize() + offset, + None, + ); + } let tester = tester.build().load(chip).load(bitwise_chip).finalize(); tester.simple_test().expect("Verification failed"); } -#[test] -fn alu_256_add_rand_test() { - run_alu_256_rand_test(BaseAluOpcode::ADD, 24); -} - -#[test] -fn alu_256_sub_rand_test() { - run_alu_256_rand_test(BaseAluOpcode::SUB, 24); -} - -#[test] -fn alu_256_xor_rand_test() { - run_alu_256_rand_test(BaseAluOpcode::XOR, 24); -} - -#[test] -fn alu_256_or_rand_test() { - run_alu_256_rand_test(BaseAluOpcode::OR, 24); -} - -#[test] -fn alu_256_and_rand_test() { - run_alu_256_rand_test(BaseAluOpcode::AND, 24); -} - +#[test_case(LessThanOpcode::SLT, 24)] +#[test_case(LessThanOpcode::SLTU, 24)] fn run_lt_256_rand_test(opcode: LessThanOpcode, num_ops: usize) { + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let offset = Rv32LessThan256Opcode::CLASS_OFFSET; + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - - let mut tester = VmChipTestBuilder::default(); let mut chip = Rv32LessThan256Chip::::new( - Rv32HeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), + VmAirWrapper::new( + Rv32HeapAdapterAir::new( + tester.execution_bridge(), + tester.memory_bridge(), + bitwise_bus, + tester.address_bits(), + ), + LessThanCoreAir::new(bitwise_bus, offset), + ), + Rv32LessThan256Step::new( + Rv32HeapAdapterStep::new(tester.address_bits(), bitwise_chip.clone()), bitwise_chip.clone(), + offset, ), - LessThanCoreChip::new(bitwise_chip.clone(), Rv32LessThan256Opcode::CLASS_OFFSET), - tester.offline_memory_mutex_arc(), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - run_int_256_rand_execute( - opcode.local_usize() + Rv32LessThan256Opcode::CLASS_OFFSET, - num_ops, - &mut chip, - &mut tester, - None, - ); + for _ in 0..num_ops { + set_and_execute_rand( + &mut tester, + &mut chip, + &mut rng, + opcode.local_usize() + offset, + None, + ); + } let tester = tester.build().load(chip).load(bitwise_chip).finalize(); tester.simple_test().expect("Verification failed"); } -#[test] -fn lt_256_slt_rand_test() { - run_lt_256_rand_test(LessThanOpcode::SLT, 24); -} - -#[test] -fn lt_256_sltu_rand_test() { - run_lt_256_rand_test(LessThanOpcode::SLTU, 24); -} +#[test_case(MulOpcode::MUL, 24)] +fn run_mul_256_rand_test(opcode: MulOpcode, num_ops: usize) { + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let offset = Rv32Mul256Opcode::CLASS_OFFSET; -fn run_mul_256_rand_test(num_ops: usize) { let range_tuple_bus = RangeTupleCheckerBus::new( RANGE_TUPLE_CHECKER_BUS, [ @@ -185,105 +191,120 @@ fn run_mul_256_rand_test(num_ops: usize) { (INT256_NUM_LIMBS * (1 << RV32_CELL_BITS)) as u32, ], ); - let range_tuple_checker = SharedRangeTupleCheckerChip::new(range_tuple_bus); + let range_tuple_chip = SharedRangeTupleCheckerChip::new(range_tuple_bus); let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let mut tester = VmChipTestBuilder::default(); let mut chip = Rv32Multiplication256Chip::::new( - Rv32HeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), + VmAirWrapper::new( + Rv32HeapAdapterAir::new( + tester.execution_bridge(), + tester.memory_bridge(), + bitwise_bus, + tester.address_bits(), + ), + MultiplicationCoreAir::new(range_tuple_bus, offset), ), - MultiplicationCoreChip::new(range_tuple_checker.clone(), Rv32Mul256Opcode::CLASS_OFFSET), - tester.offline_memory_mutex_arc(), + Rv32Multiplication256Step::new( + Rv32HeapAdapterStep::new(tester.address_bits(), bitwise_chip.clone()), + range_tuple_chip.clone(), + offset, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - run_int_256_rand_execute( - Rv32Mul256Opcode::CLASS_OFFSET, - num_ops, - &mut chip, - &mut tester, - None, - ); + for _ in 0..num_ops { + set_and_execute_rand( + &mut tester, + &mut chip, + &mut rng, + opcode.local_usize() + offset, + None, + ); + } let tester = tester .build() .load(chip) - .load(range_tuple_checker) + .load(range_tuple_chip) .load(bitwise_chip) .finalize(); tester.simple_test().expect("Verification failed"); } -#[test] -fn mul_256_rand_test() { - run_mul_256_rand_test(24); -} - +#[test_case(ShiftOpcode::SLL, 24)] +#[test_case(ShiftOpcode::SRL, 24)] +#[test_case(ShiftOpcode::SRA, 24)] fn run_shift_256_rand_test(opcode: ShiftOpcode, num_ops: usize) { + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let offset = Rv32Shift256Opcode::CLASS_OFFSET; + + let range_checker_chip = tester.range_checker(); let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let mut tester = VmChipTestBuilder::default(); let mut chip = Rv32Shift256Chip::::new( - Rv32HeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), + VmAirWrapper::new( + Rv32HeapAdapterAir::new( + tester.execution_bridge(), + tester.memory_bridge(), + bitwise_bus, + tester.address_bits(), + ), + ShiftCoreAir::new(bitwise_bus, range_checker_chip.bus(), offset), ), - ShiftCoreChip::new( + Rv32Shift256Step::new( + Rv32HeapAdapterStep::new(tester.address_bits(), bitwise_chip.clone()), bitwise_chip.clone(), - tester.memory_controller().borrow().range_checker.clone(), - Rv32Shift256Opcode::CLASS_OFFSET, + range_checker_chip.clone(), + offset, ), - tester.offline_memory_mutex_arc(), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - run_int_256_rand_execute( - opcode.local_usize() + Rv32Shift256Opcode::CLASS_OFFSET, - num_ops, - &mut chip, - &mut tester, - None, - ); + for _ in 0..num_ops { + set_and_execute_rand( + &mut tester, + &mut chip, + &mut rng, + opcode.local_usize() + offset, + None, + ); + } + + drop(range_checker_chip); let tester = tester.build().load(chip).load(bitwise_chip).finalize(); tester.simple_test().expect("Verification failed"); } -#[test] -fn shift_256_sll_rand_test() { - run_shift_256_rand_test(ShiftOpcode::SLL, 24); -} - -#[test] -fn shift_256_srl_rand_test() { - run_shift_256_rand_test(ShiftOpcode::SRL, 24); -} - -#[test] -fn shift_256_sra_rand_test() { - run_shift_256_rand_test(ShiftOpcode::SRA, 24); -} - +#[test_case(BranchEqualOpcode::BEQ, 24)] +#[test_case(BranchEqualOpcode::BNE, 24)] fn run_beq_256_rand_test(opcode: BranchEqualOpcode, num_ops: usize) { + let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); + let offset = Rv32BranchEqual256Opcode::CLASS_OFFSET; + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let mut chip = Rv32BranchEqual256Chip::::new( - Rv32HeapBranchAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), + VmAirWrapper::new( + Rv32HeapBranchAdapterAir::new( + tester.execution_bridge(), + tester.memory_bridge(), + bitwise_bus, + tester.address_bits(), + ), + BranchEqualCoreAir::new(offset, DEFAULT_PC_STEP), + ), + Rv32BranchEqual256Step::new( + Rv32HeapBranchAdapterStep::new(tester.address_bits(), bitwise_chip.clone()), + offset, + DEFAULT_PC_STEP, ), - BranchEqualCoreChip::new(Rv32BranchEqual256Opcode::CLASS_OFFSET, 4), - tester.offline_memory_mutex_arc(), + MAX_INS_CAPACITY, + tester.memory_helper(), ); let branch_fn = |opcode: usize, x: &[u32; INT256_NUM_LIMBS], y: &[u32; INT256_NUM_LIMBS]| { @@ -294,93 +315,79 @@ fn run_beq_256_rand_test(opcode: BranchEqualOpcode, num_ops: usize) { == BranchEqualOpcode::BNE.local_usize() + Rv32BranchEqual256Opcode::CLASS_OFFSET) }; - run_int_256_rand_execute( - opcode.local_usize() + Rv32BranchEqual256Opcode::CLASS_OFFSET, - num_ops, - &mut chip, - &mut tester, - Some(branch_fn), - ); + for _ in 0..num_ops { + set_and_execute_rand( + &mut tester, + &mut chip, + &mut rng, + opcode.local_usize() + offset, + Some(branch_fn), + ); + } let tester = tester.build().load(chip).load(bitwise_chip).finalize(); tester.simple_test().expect("Verification failed"); } -#[test] -fn beq_256_beq_rand_test() { - run_beq_256_rand_test(BranchEqualOpcode::BEQ, 24); -} - -#[test] -fn beq_256_bne_rand_test() { - run_beq_256_rand_test(BranchEqualOpcode::BNE, 24); -} - +#[test_case(BranchLessThanOpcode::BLT, 24)] +#[test_case(BranchLessThanOpcode::BLTU, 24)] +#[test_case(BranchLessThanOpcode::BGE, 24)] +#[test_case(BranchLessThanOpcode::BGEU, 24)] fn run_blt_256_rand_test(opcode: BranchLessThanOpcode, num_ops: usize) { + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let offset = Rv32BranchLessThan256Opcode::CLASS_OFFSET; + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let mut tester = VmChipTestBuilder::default(); let mut chip = Rv32BranchLessThan256Chip::::new( - Rv32HeapBranchAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), + VmAirWrapper::new( + Rv32HeapBranchAdapterAir::new( + tester.execution_bridge(), + tester.memory_bridge(), + bitwise_bus, + tester.address_bits(), + ), + BranchLessThanCoreAir::new(bitwise_bus, offset), ), - BranchLessThanCoreChip::new( + Rv32BranchLessThan256Step::new( + Rv32HeapBranchAdapterStep::new(tester.address_bits(), bitwise_chip.clone()), bitwise_chip.clone(), - Rv32BranchLessThan256Opcode::CLASS_OFFSET, + offset, ), - tester.offline_memory_mutex_arc(), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - - let branch_fn = |opcode: usize, x: &[u32; INT256_NUM_LIMBS], y: &[u32; INT256_NUM_LIMBS]| { - let opcode = - BranchLessThanOpcode::from_usize(opcode - Rv32BranchLessThan256Opcode::CLASS_OFFSET); - let (is_ge, is_signed) = match opcode { - BranchLessThanOpcode::BLT => (false, true), - BranchLessThanOpcode::BLTU => (false, false), - BranchLessThanOpcode::BGE => (true, true), - BranchLessThanOpcode::BGEU => (true, false), - }; - let x_sign = x[INT256_NUM_LIMBS - 1] >> (RV32_CELL_BITS - 1) != 0 && is_signed; - let y_sign = y[INT256_NUM_LIMBS - 1] >> (RV32_CELL_BITS - 1) != 0 && is_signed; - for (x, y) in x.iter().rev().zip(y.iter().rev()) { - if x != y { - return (x < y) ^ x_sign ^ y_sign ^ is_ge; + let branch_fn = + |opcode: usize, x: &[u32; INT256_NUM_LIMBS], y: &[u32; INT256_NUM_LIMBS]| -> bool { + let opcode = BranchLessThanOpcode::from_usize( + opcode - Rv32BranchLessThan256Opcode::CLASS_OFFSET, + ); + let (is_ge, is_signed) = match opcode { + BranchLessThanOpcode::BLT => (false, true), + BranchLessThanOpcode::BLTU => (false, false), + BranchLessThanOpcode::BGE => (true, true), + BranchLessThanOpcode::BGEU => (true, false), + }; + let x_sign = x[INT256_NUM_LIMBS - 1] >> (RV32_CELL_BITS - 1) != 0 && is_signed; + let y_sign = y[INT256_NUM_LIMBS - 1] >> (RV32_CELL_BITS - 1) != 0 && is_signed; + for (x, y) in x.iter().rev().zip(y.iter().rev()) { + if x != y { + return (x < y) ^ x_sign ^ y_sign ^ is_ge; + } } - } - is_ge - }; + is_ge + }; - run_int_256_rand_execute( - opcode.local_usize() + Rv32BranchLessThan256Opcode::CLASS_OFFSET, - num_ops, - &mut chip, - &mut tester, - Some(branch_fn), - ); + for _ in 0..num_ops { + set_and_execute_rand( + &mut tester, + &mut chip, + &mut rng, + opcode.local_usize() + offset, + Some(branch_fn), + ); + } let tester = tester.build().load(chip).load(bitwise_chip).finalize(); tester.simple_test().expect("Verification failed"); } - -#[test] -fn blt_256_blt_rand_test() { - run_blt_256_rand_test(BranchLessThanOpcode::BLT, 24); -} - -#[test] -fn blt_256_bltu_rand_test() { - run_blt_256_rand_test(BranchLessThanOpcode::BLTU, 24); -} - -#[test] -fn blt_256_bge_rand_test() { - run_blt_256_rand_test(BranchLessThanOpcode::BGE, 24); -} - -#[test] -fn blt_256_bgeu_rand_test() { - run_blt_256_rand_test(BranchLessThanOpcode::BGEU, 24); -} diff --git a/extensions/rv32-adapters/src/heap.rs b/extensions/rv32-adapters/src/heap.rs index 8217f6833b..cf7e54ddef 100644 --- a/extensions/rv32-adapters/src/heap.rs +++ b/extensions/rv32-adapters/src/heap.rs @@ -1,18 +1,14 @@ -use std::{ - array::{self, from_fn}, - borrow::Borrow, - marker::PhantomData, -}; +use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge, - ExecutionBus, ExecutionState, MinimalInstruction, Result, VmAdapterAir, VmAdapterChip, - VmAdapterInterface, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, + ExecutionBridge, MinimalInstruction, VmAdapterAir, }, - system::{ - memory::{offline_checker::MemoryBridge, MemoryController, OfflineMemory}, - program::ProgramBus, + system::memory::{ + offline_checker::MemoryBridge, + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, }; use openvm_circuit_primitives::bitwise_op_lookup::{ @@ -20,20 +16,15 @@ use openvm_circuit_primitives::bitwise_op_lookup::{ }; use openvm_instructions::{ instruction::Instruction, - program::DEFAULT_PC_STEP, - riscv::{RV32_CELL_BITS, RV32_MEMORY_AS, RV32_REGISTER_AS, RV32_REGISTER_NUM_LIMBS}, + riscv::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, }; -use openvm_rv32im_circuit::adapters::{read_rv32_register, tmp_convert_to_u8s}; use openvm_stark_backend::{ interaction::InteractionBuilder, p3_air::BaseAir, p3_field::{Field, PrimeField32}, }; -use super::{ - vec_heap_generate_trace_row_impl, Rv32VecHeapAdapterAir, Rv32VecHeapAdapterCols, - Rv32VecHeapReadRecord, Rv32VecHeapWriteRecord, -}; +use crate::{RV32VecHeapAdapterStep, Rv32VecHeapAdapterAir, Rv32VecHeapAdapterCols}; /// This adapter reads from NUM_READS <= 2 pointers and writes to 1 pointer. /// * The data is read from the heap (address space 2), and the pointers are read from registers @@ -101,139 +92,100 @@ impl< } } -pub struct Rv32HeapAdapterChip< - F: Field, +pub struct Rv32HeapAdapterStep< const NUM_READS: usize, const READ_SIZE: usize, const WRITE_SIZE: usize, -> { - pub air: Rv32HeapAdapterAir, - pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - _marker: PhantomData, -} +>(RV32VecHeapAdapterStep); -impl - Rv32HeapAdapterChip +impl + Rv32HeapAdapterStep { pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - address_bits: usize, + pointer_max_bits: usize, bitwise_lookup_chip: SharedBitwiseOperationLookupChip, ) -> Self { assert!(NUM_READS <= 2); assert!( - RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - address_bits < RV32_CELL_BITS, - "address_bits={address_bits} needs to be large enough for high limb range check" + RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - pointer_max_bits < RV32_CELL_BITS, + "pointer_max_bits={pointer_max_bits} needs to be large enough for high limb range check" ); - Self { - air: Rv32HeapAdapterAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - bus: bitwise_lookup_chip.bus(), - address_bits, - }, + Rv32HeapAdapterStep(RV32VecHeapAdapterStep::new( + pointer_max_bits, bitwise_lookup_chip, - _marker: PhantomData, - } + )) } } -impl - VmAdapterChip for Rv32HeapAdapterChip +impl< + F: PrimeField32, + CTX, + const NUM_READS: usize, + const READ_SIZE: usize, + const WRITE_SIZE: usize, + > AdapterTraceStep for Rv32HeapAdapterStep +where + F: PrimeField32, { - type ReadRecord = Rv32VecHeapReadRecord; - type WriteRecord = Rv32VecHeapWriteRecord<1, WRITE_SIZE>; - type Air = Rv32HeapAdapterAir; - type Interface = - BasicAdapterInterface, NUM_READS, 1, READ_SIZE, WRITE_SIZE>; - - fn preprocess( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { - let Instruction { a, b, c, d, e, .. } = *instruction; - - debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - debug_assert_eq!(e.as_canonical_u32(), RV32_MEMORY_AS); - - let mut rs_vals = [0; NUM_READS]; - let rs_records: [_; NUM_READS] = from_fn(|i| { - let addr = if i == 0 { b } else { c }; - let (record, val) = read_rv32_register(memory, d, addr); - rs_vals[i] = val; - record - }); - let (rd_record, rd_val) = read_rv32_register(memory, d, a); - - let read_records = rs_vals.map(|address| { - debug_assert!(address as usize + READ_SIZE - 1 < (1 << self.air.address_bits)); - [memory.read::(e, F::from_canonical_u32(address))] - }); - let read_data = read_records.map(|r| r[0].1.map(F::from_canonical_u8)); - - let record = Rv32VecHeapReadRecord { - rs: rs_records, - rd: rd_record, - rd_val: F::from_canonical_u32(rd_val), - reads: read_records.map(|r| array::from_fn(|i| r[i].0)), - }; - - Ok((read_data, record)) + const WIDTH: usize = + Rv32VecHeapAdapterCols::::width(); + type ReadData = [[u8; READ_SIZE]; NUM_READS]; + type WriteData = [[u8; WRITE_SIZE]; 1]; + + type TraceContext<'a> = (); + + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let adapter_cols: &mut Rv32VecHeapAdapterCols = + adapter_row.borrow_mut(); + adapter_cols.from_state.pc = F::from_canonical_u32(pc); + adapter_cols.from_state.timestamp = F::from_canonical_u32(memory.timestamp); } - fn postprocess( - &mut self, - memory: &mut MemoryController, + fn read( + &self, + memory: &mut TracingMemory, instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - let e = instruction.e; - let writes = [memory - .write(e, read_record.rd_val, &tmp_convert_to_u8s(output.writes[0])) - .0]; - - let timestamp_delta = memory.timestamp() - from_state.timestamp; - debug_assert!( - timestamp_delta == 6, - "timestamp delta is {}, expected 6", - timestamp_delta - ); - - Ok(( - ExecutionState { - pc: from_state.pc + DEFAULT_PC_STEP, - timestamp: memory.timestamp(), - }, - Self::WriteRecord { from_state, writes }, - )) + adapter_row: &mut [F], + ) -> Self::ReadData { + let read_data = AdapterTraceStep::::read(&self.0, memory, instruction, adapter_row); + read_data.map(|r| r[0]) } - fn generate_trace_row( + fn write( &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, + memory: &mut TracingMemory, + instruction: &Instruction, + adapter_row: &mut [F], + data: &Self::WriteData, ) { - vec_heap_generate_trace_row_impl( - row_slice, - &read_record, - &write_record, - self.bitwise_lookup_chip.clone(), - self.air.address_bits, - memory, - ); + AdapterTraceStep::::write(&self.0, memory, instruction, adapter_row, data); + } + + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, ctx: (), adapter_row: &mut [F]) { + AdapterTraceStep::::fill_trace_row(&self.0, mem_helper, ctx, adapter_row); + } +} + +impl + AdapterExecutorE1 for Rv32HeapAdapterStep +{ + type ReadData = [[u8; READ_SIZE]; NUM_READS]; + type WriteData = [[u8; WRITE_SIZE]; 1]; + + #[inline(always)] + fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData + where + Mem: GuestMemory, + { + let read_data = AdapterExecutorE1::::read(&self.0, memory, instruction); + read_data.map(|r| r[0]) } - fn air(&self) -> &Self::Air { - &self.air + #[inline(always)] + fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) + where + Mem: GuestMemory, + { + AdapterExecutorE1::::write(&self.0, memory, instruction, data); } } diff --git a/extensions/rv32-adapters/src/heap_branch.rs b/extensions/rv32-adapters/src/heap_branch.rs index fc4067e84a..a80c6c214f 100644 --- a/extensions/rv32-adapters/src/heap_branch.rs +++ b/extensions/rv32-adapters/src/heap_branch.rs @@ -2,22 +2,18 @@ use std::{ array::from_fn, borrow::{Borrow, BorrowMut}, iter::once, - marker::PhantomData, }; use itertools::izip; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge, - ExecutionBus, ExecutionState, ImmInstruction, Result, VmAdapterAir, VmAdapterChip, - VmAdapterInterface, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, + ExecutionBridge, ExecutionState, ImmInstruction, VmAdapterAir, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadAuxCols}, - MemoryAddress, MemoryController, OfflineMemory, RecordId, - }, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryReadAuxCols}, + online::{GuestMemory, TracingMemory}, + MemoryAddress, MemoryAuxColsFactory, }, }; use openvm_circuit_primitives::bitwise_op_lookup::{ @@ -30,15 +26,13 @@ use openvm_instructions::{ riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}, }; use openvm_rv32im_circuit::adapters::{ - read_rv32_register, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + memory_read, new_read_rv32_register, tracing_read, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, }; use openvm_stark_backend::{ interaction::InteractionBuilder, p3_air::BaseAir, p3_field::{Field, FieldAlgebra, PrimeField32}, }; -use serde::{Deserialize, Serialize}; -use serde_big_array::BigArray; /// This adapter reads from NUM_READS <= 2 pointers. /// * The data is read from the heap (address space 2), and the pointers are read from registers @@ -170,158 +164,148 @@ impl VmA } } -pub struct Rv32HeapBranchAdapterChip { - pub air: Rv32HeapBranchAdapterAir, +pub struct Rv32HeapBranchAdapterStep { + pub pointer_max_bits: usize, + // TODO(arayi): use reference to bitwise lookup chip with lifetimes instead pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - _marker: PhantomData, } -impl - Rv32HeapBranchAdapterChip +impl + Rv32HeapBranchAdapterStep { pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - address_bits: usize, + pointer_max_bits: usize, bitwise_lookup_chip: SharedBitwiseOperationLookupChip, ) -> Self { assert!(NUM_READS <= 2); assert!( - RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - address_bits < RV32_CELL_BITS, - "address_bits={address_bits} needs to be large enough for high limb range check" + RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - pointer_max_bits < RV32_CELL_BITS, + "pointer_max_bits={pointer_max_bits} needs to be large enough for high limb range check" ); Self { - air: Rv32HeapBranchAdapterAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - bus: bitwise_lookup_chip.bus(), - address_bits, - }, + pointer_max_bits, bitwise_lookup_chip, - _marker: PhantomData, } } } +impl AdapterExecutorE1 + for Rv32HeapBranchAdapterStep +{ + type ReadData = [[u8; READ_SIZE]; NUM_READS]; + type WriteData = (); -#[repr(C)] -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Rv32HeapBranchReadRecord { - #[serde(with = "BigArray")] - pub rs_reads: [RecordId; NUM_READS], - #[serde(with = "BigArray")] - pub heap_reads: [RecordId; NUM_READS], + fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData + where + Mem: GuestMemory, + { + let Instruction { a, b, d, e, .. } = *instruction; + + let d = d.as_canonical_u32(); + let e = e.as_canonical_u32(); + debug_assert_eq!(d, RV32_REGISTER_AS); + debug_assert_eq!(e, RV32_MEMORY_AS); + + // Read register values + let rs_vals = from_fn(|i| { + let addr = if i == 0 { a } else { b }; + new_read_rv32_register(memory, d, addr.as_canonical_u32()) + }); + + // Read memory values + rs_vals.map(|address| { + assert!(address as usize + READ_SIZE - 1 < (1 << self.pointer_max_bits)); + memory_read(memory, e, address) + }) + } + + fn write(&self, _memory: &mut Mem, _instruction: &Instruction, _data: &Self::WriteData) + where + Mem: GuestMemory, + { + // This function intentionally does nothing + } } -impl VmAdapterChip - for Rv32HeapBranchAdapterChip +impl AdapterTraceStep + for Rv32HeapBranchAdapterStep +where + F: PrimeField32, { - type ReadRecord = Rv32HeapBranchReadRecord; - type WriteRecord = ExecutionState; - type Air = Rv32HeapBranchAdapterAir; - type Interface = BasicAdapterInterface, NUM_READS, 0, READ_SIZE, 0>; - - fn preprocess( - &mut self, - memory: &mut MemoryController, + const WIDTH: usize = Rv32HeapBranchAdapterCols::::width(); + type ReadData = [[u8; READ_SIZE]; NUM_READS]; + type WriteData = (); + type TraceContext<'a> = (); + + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let cols: &mut Rv32HeapBranchAdapterCols<_, NUM_READS, READ_SIZE> = + adapter_row.borrow_mut(); + cols.from_state.pc = F::from_canonical_u32(pc); + cols.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + } + + fn read( + &self, + memory: &mut TracingMemory, instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { + adapter_row: &mut [F], + ) -> Self::ReadData { let Instruction { a, b, d, e, .. } = *instruction; - debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - debug_assert_eq!(e.as_canonical_u32(), RV32_MEMORY_AS); + let d = d.as_canonical_u32(); + let e = e.as_canonical_u32(); + debug_assert_eq!(d, RV32_REGISTER_AS); + debug_assert_eq!(e, RV32_MEMORY_AS); - let mut rs_vals = [0; NUM_READS]; - let rs_records: [_; NUM_READS] = from_fn(|i| { - let addr = if i == 0 { a } else { b }; - let (record, val) = read_rv32_register(memory, d, addr); - rs_vals[i] = val; - record - }); + let cols: &mut Rv32HeapBranchAdapterCols<_, NUM_READS, READ_SIZE> = + adapter_row.borrow_mut(); - let heap_records = rs_vals.map(|address| { - assert!(address as usize + READ_SIZE - 1 < (1 << self.air.address_bits)); - memory.read::(e, F::from_canonical_u32(address)) + // Read register values + let rs_vals: [_; NUM_READS] = from_fn(|i| { + let addr = if i == 0 { a } else { b }; + cols.rs_ptr[i] = addr; + let rs_val = tracing_read(memory, e, addr.as_canonical_u32(), &mut cols.rs_read_aux[i]); + cols.rs_val[i] = rs_val.map(F::from_canonical_u8); + u32::from_le_bytes(rs_val) }); - let record = Rv32HeapBranchReadRecord { - rs_reads: rs_records, - heap_reads: heap_records.map(|r| r.0), - }; - Ok((heap_records.map(|r| r.1.map(F::from_canonical_u8)), record)) + // Read memory values + from_fn(|i| { + assert!(rs_vals[i] as usize + READ_SIZE - 1 < (1 << self.pointer_max_bits)); + tracing_read(memory, e, rs_vals[i], &mut cols.heap_read_aux[i]) + }) } - fn postprocess( - &mut self, - memory: &mut MemoryController, + fn write( + &self, + _memory: &mut TracingMemory, _instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - _read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - let timestamp_delta = memory.timestamp() - from_state.timestamp; - debug_assert!( - timestamp_delta == 4, - "timestamp delta is {}, expected 4", - timestamp_delta - ); - - Ok(( - ExecutionState { - pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), - timestamp: memory.timestamp(), - }, - from_state, - )) + _adapter_row: &mut [F], + _data: &Self::WriteData, + ) { + // This function intentionally does nothing } - fn generate_trace_row( + fn fill_trace_row( &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, + _mem_helper: &MemoryAuxColsFactory, + _ctx: (), + adapter_row: &mut [F], ) { - let aux_cols_factory = memory.aux_cols_factory(); - let row_slice: &mut Rv32HeapBranchAdapterCols<_, NUM_READS, READ_SIZE> = - row_slice.borrow_mut(); - row_slice.from_state = write_record.map(F::from_canonical_u32); - - let rs_reads = read_record.rs_reads.map(|r| memory.record_by_id(r)); - - for (i, rs_read) in rs_reads.iter().enumerate() { - row_slice.rs_ptr[i] = rs_read.pointer; - row_slice.rs_val[i].copy_from_slice(rs_read.data_slice()); - aux_cols_factory.generate_read_aux(rs_read, &mut row_slice.rs_read_aux[i]); - } - - for (i, heap_read) in read_record.heap_reads.iter().enumerate() { - let record = memory.record_by_id(*heap_read); - aux_cols_factory.generate_read_aux(record, &mut row_slice.heap_read_aux[i]); - } + let cols: &mut Rv32HeapBranchAdapterCols = + adapter_row.borrow_mut(); // Range checks: - let need_range_check: Vec = rs_reads + let need_range_check: Vec = cols + .rs_val .iter() - .map(|record| { - record - .data_at(RV32_REGISTER_NUM_LIMBS - 1) - .as_canonical_u32() - }) + .map(|&val| val[RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32()) .chain(once(0)) // in case NUM_READS is odd .collect(); - debug_assert!(self.air.address_bits <= RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS); - let limb_shift_bits = RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.air.address_bits; + debug_assert!(self.pointer_max_bits <= RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS); + let limb_shift_bits = RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.pointer_max_bits; for pair in need_range_check.chunks_exact(2) { self.bitwise_lookup_chip .request_range(pair[0] << limb_shift_bits, pair[1] << limb_shift_bits); } } - - fn air(&self) -> &Self::Air { - &self.air - } } diff --git a/extensions/rv32-adapters/src/lib.rs b/extensions/rv32-adapters/src/lib.rs index d84c82f617..c194f3e8ca 100644 --- a/extensions/rv32-adapters/src/lib.rs +++ b/extensions/rv32-adapters/src/lib.rs @@ -1,14 +1,14 @@ -mod eq_mod; +// mod eq_mod; mod heap; mod heap_branch; mod vec_heap; -mod vec_heap_two_reads; +// mod vec_heap_two_reads; -pub use eq_mod::*; +// pub use eq_mod::*; pub use heap::*; pub use heap_branch::*; pub use vec_heap::*; -pub use vec_heap_two_reads::*; +// pub use vec_heap_two_reads::*; #[cfg(any(test, feature = "test-utils"))] mod test_utils; diff --git a/extensions/rv32-adapters/src/vec_heap.rs b/extensions/rv32-adapters/src/vec_heap.rs index 0a2766e29a..4d430b3fc1 100644 --- a/extensions/rv32-adapters/src/vec_heap.rs +++ b/extensions/rv32-adapters/src/vec_heap.rs @@ -2,21 +2,18 @@ use std::{ array::from_fn, borrow::{Borrow, BorrowMut}, iter::{once, zip}, - marker::PhantomData, }; use itertools::izip; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, ExecutionBridge, ExecutionBus, ExecutionState, - Result, VecHeapAdapterInterface, VmAdapterAir, VmAdapterChip, VmAdapterInterface, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ExecutionBridge, ExecutionState, + VecHeapAdapterInterface, VmAdapterAir, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, - MemoryAddress, MemoryController, OfflineMemory, RecordId, - }, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, + online::{GuestMemory, TracingMemory}, + MemoryAddress, MemoryAuxColsFactory, }, }; use openvm_circuit_primitives::bitwise_op_lookup::{ @@ -29,16 +26,14 @@ use openvm_instructions::{ riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}, }; use openvm_rv32im_circuit::adapters::{ - abstract_compose, read_rv32_register, tmp_convert_to_u8s, RV32_CELL_BITS, - RV32_REGISTER_NUM_LIMBS, + abstract_compose, memory_read, memory_write, new_read_rv32_register, tracing_read, + tracing_write, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, }; use openvm_stark_backend::{ interaction::InteractionBuilder, p3_air::BaseAir, p3_field::{Field, FieldAlgebra, PrimeField32}, }; -use serde::{Deserialize, Serialize}; -use serde_with::serde_as; /// This adapter reads from R (R <= 2) pointers and writes to 1 pointer. /// * The data is read from the heap (address space 2), and the pointers are read from registers @@ -47,87 +42,6 @@ use serde_with::serde_as; /// starting from the addresses in `rs[0]` (and `rs[1]` if `R = 2`). /// * Writes take the form of `BLOCKS_PER_WRITE` consecutive writes of size `WRITE_SIZE` to the /// heap, starting from the address in `rd`. -#[derive(Clone)] -pub struct Rv32VecHeapAdapterChip< - F: Field, - const NUM_READS: usize, - const BLOCKS_PER_READ: usize, - const BLOCKS_PER_WRITE: usize, - const READ_SIZE: usize, - const WRITE_SIZE: usize, -> { - pub air: - Rv32VecHeapAdapterAir, - pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - _marker: PhantomData, -} - -impl< - F: PrimeField32, - const NUM_READS: usize, - const BLOCKS_PER_READ: usize, - const BLOCKS_PER_WRITE: usize, - const READ_SIZE: usize, - const WRITE_SIZE: usize, - > - Rv32VecHeapAdapterChip -{ - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - address_bits: usize, - bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - ) -> Self { - assert!(NUM_READS <= 2); - assert!( - RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - address_bits < RV32_CELL_BITS, - "address_bits={address_bits} needs to be large enough for high limb range check" - ); - Self { - air: Rv32VecHeapAdapterAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - bus: bitwise_lookup_chip.bus(), - address_bits, - }, - bitwise_lookup_chip, - _marker: PhantomData, - } - } -} - -#[repr(C)] -#[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -#[serde(bound = "F: Field")] -pub struct Rv32VecHeapReadRecord< - F: Field, - const NUM_READS: usize, - const BLOCKS_PER_READ: usize, - const READ_SIZE: usize, -> { - /// Read register value from address space e=1 - #[serde_as(as = "[_; NUM_READS]")] - pub rs: [RecordId; NUM_READS], - /// Read register value from address space d=1 - pub rd: RecordId, - - pub rd_val: F, - - #[serde_as(as = "[[_; BLOCKS_PER_READ]; NUM_READS]")] - pub reads: [[RecordId; BLOCKS_PER_READ]; NUM_READS], -} - -#[repr(C)] -#[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct Rv32VecHeapWriteRecord { - pub from_state: ExecutionState, - #[serde_as(as = "[_; BLOCKS_PER_WRITE]")] - pub writes: [RecordId; BLOCKS_PER_WRITE], -} - #[repr(C)] #[derive(AlignedBorrow)] pub struct Rv32VecHeapAdapterCols< @@ -347,204 +261,230 @@ impl< } } +#[derive(derive_new::new)] +pub struct RV32VecHeapAdapterStep< + const NUM_READS: usize, + const BLOCKS_PER_READ: usize, + const BLOCKS_PER_WRITE: usize, + const READ_SIZE: usize, + const WRITE_SIZE: usize, +> { + pointer_max_bits: usize, + // TODO(arayi): use reference to bitwise lookup chip with lifetimes instead + pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, +} + impl< F: PrimeField32, + CTX, const NUM_READS: usize, const BLOCKS_PER_READ: usize, const BLOCKS_PER_WRITE: usize, const READ_SIZE: usize, const WRITE_SIZE: usize, - > VmAdapterChip - for Rv32VecHeapAdapterChip< - F, - NUM_READS, - BLOCKS_PER_READ, - BLOCKS_PER_WRITE, - READ_SIZE, - WRITE_SIZE, - > + > AdapterTraceStep + for RV32VecHeapAdapterStep { - type ReadRecord = Rv32VecHeapReadRecord; - type WriteRecord = Rv32VecHeapWriteRecord; - type Air = - Rv32VecHeapAdapterAir; - type Interface = VecHeapAdapterInterface< + const WIDTH: usize = Rv32VecHeapAdapterCols::< F, NUM_READS, BLOCKS_PER_READ, BLOCKS_PER_WRITE, READ_SIZE, WRITE_SIZE, - >; + >::width(); + type ReadData = [[[u8; READ_SIZE]; BLOCKS_PER_READ]; NUM_READS]; + type WriteData = [[u8; WRITE_SIZE]; BLOCKS_PER_WRITE]; + type TraceContext<'a> = (); - fn preprocess( - &mut self, - memory: &mut MemoryController, + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let adapter_cols: &mut Rv32VecHeapAdapterCols< + F, + NUM_READS, + BLOCKS_PER_READ, + BLOCKS_PER_WRITE, + READ_SIZE, + WRITE_SIZE, + > = adapter_row.borrow_mut(); + adapter_cols.from_state.pc = F::from_canonical_u32(pc); + adapter_cols.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + } + + fn read( + &self, + memory: &mut TracingMemory, instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { - let Instruction { a, b, c, d, e, .. } = *instruction; + adapter_row: &mut [F], + ) -> Self::ReadData { + let Instruction { b, c, d, e, .. } = *instruction; - debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - debug_assert_eq!(e.as_canonical_u32(), RV32_MEMORY_AS); + let e = e.as_canonical_u32(); + let d = d.as_canonical_u32(); + debug_assert_eq!(d, RV32_REGISTER_AS); + debug_assert_eq!(e, RV32_MEMORY_AS); + + let cols: &mut Rv32VecHeapAdapterCols< + F, + NUM_READS, + BLOCKS_PER_READ, + BLOCKS_PER_WRITE, + READ_SIZE, + WRITE_SIZE, + > = adapter_row.borrow_mut(); // Read register values - let mut rs_vals = [0; NUM_READS]; - let rs_records: [_; NUM_READS] = from_fn(|i| { + let rs_vals: [_; NUM_READS] = from_fn(|i| { let addr = if i == 0 { b } else { c }; - let (record, val) = read_rv32_register(memory, d, addr); - rs_vals[i] = val; - record + cols.rs_ptr[i] = addr; + let rs_val = tracing_read(memory, e, addr.as_canonical_u32(), &mut cols.rs_read_aux[i]); + cols.rs_val[i] = rs_val.map(F::from_canonical_u8); + u32::from_le_bytes(rs_val) }); - let (rd_record, rd_val) = read_rv32_register(memory, d, a); // Read memory values - let read_records = rs_vals.map(|address| { + from_fn(|i| { assert!( - address as usize + READ_SIZE * BLOCKS_PER_READ - 1 < (1 << self.air.address_bits) + rs_vals[i] as usize + READ_SIZE * BLOCKS_PER_READ - 1 + < (1 << self.pointer_max_bits) ); - from_fn(|i| { - memory.read::( + from_fn(|j| { + tracing_read( + memory, e, - F::from_canonical_u32(address + (i * READ_SIZE) as u32), + rs_vals[i] + (j * READ_SIZE) as u32, + &mut cols.reads_aux[i][j], ) }) - }); - let read_data = read_records.map(|r| r.map(|x| x.1.map(F::from_canonical_u8))); - assert!(rd_val as usize + WRITE_SIZE * BLOCKS_PER_WRITE - 1 < (1 << self.air.address_bits)); - - let record = Rv32VecHeapReadRecord { - rs: rs_records, - rd: rd_record, - rd_val: F::from_canonical_u32(rd_val), - reads: read_records.map(|r| r.map(|x| x.0)), - }; - - Ok((read_data, record)) + }) } - fn postprocess( - &mut self, - memory: &mut MemoryController, + fn write( + &self, + memory: &mut openvm_circuit::system::memory::online::TracingMemory, instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - let e = instruction.e; - let mut i = 0; - let writes = output.writes.map(|write| { - let (record_id, _) = memory.write( + adapter_row: &mut [F], + data: &Self::WriteData, + ) { + let Instruction { a, d, e, .. } = *instruction; + + let e = e.as_canonical_u32(); + let cols: &mut Rv32VecHeapAdapterCols< + F, + NUM_READS, + BLOCKS_PER_READ, + BLOCKS_PER_WRITE, + READ_SIZE, + WRITE_SIZE, + > = adapter_row.borrow_mut(); + + cols.rd_ptr = a; + let rd_val = tracing_read( + memory, + d.as_canonical_u32(), + a.as_canonical_u32(), + &mut cols.rd_read_aux, + ); + cols.rd_val = rd_val.map(F::from_canonical_u8); + + let rd_val = u32::from_le_bytes(rd_val); + assert!(rd_val as usize + WRITE_SIZE * BLOCKS_PER_WRITE - 1 < (1 << self.pointer_max_bits)); + + for i in 0..BLOCKS_PER_WRITE { + tracing_write( + memory, e, - read_record.rd_val + F::from_canonical_u32((i * WRITE_SIZE) as u32), - &tmp_convert_to_u8s(write), + rd_val + (i * WRITE_SIZE) as u32, + &data[i], + &mut cols.writes_aux[i], ); - i += 1; - record_id - }); - - Ok(( - ExecutionState { - pc: from_state.pc + DEFAULT_PC_STEP, - timestamp: memory.timestamp(), - }, - Self::WriteRecord { from_state, writes }, - )) + } } - fn generate_trace_row( + fn fill_trace_row( &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, + _mem_helper: &MemoryAuxColsFactory, + _ctx: (), + adapter_row: &mut [F], ) { - vec_heap_generate_trace_row_impl( - row_slice, - &read_record, - &write_record, - self.bitwise_lookup_chip.clone(), - self.air.address_bits, - memory, - ) - } + let cols: &mut Rv32VecHeapAdapterCols< + F, + NUM_READS, + BLOCKS_PER_READ, + BLOCKS_PER_WRITE, + READ_SIZE, + WRITE_SIZE, + > = adapter_row.borrow_mut(); - fn air(&self) -> &Self::Air { - &self.air + // Range checks: + let need_range_check: Vec = cols + .rs_val + .iter() + .chain(std::iter::repeat_n(&cols.rd_val, 2)) + .map(|&val| val[RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32()) + .collect(); + debug_assert!(self.pointer_max_bits <= RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS); + let limb_shift_bits = RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.pointer_max_bits; + for pair in need_range_check.chunks_exact(2) { + self.bitwise_lookup_chip + .request_range(pair[0] << limb_shift_bits, pair[1] << limb_shift_bits); + } } } -pub(super) fn vec_heap_generate_trace_row_impl< - F: PrimeField32, - const NUM_READS: usize, - const BLOCKS_PER_READ: usize, - const BLOCKS_PER_WRITE: usize, - const READ_SIZE: usize, - const WRITE_SIZE: usize, ->( - row_slice: &mut [F], - read_record: &Rv32VecHeapReadRecord, - write_record: &Rv32VecHeapWriteRecord, - bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - address_bits: usize, - memory: &OfflineMemory, -) { - let aux_cols_factory = memory.aux_cols_factory(); - let row_slice: &mut Rv32VecHeapAdapterCols< - F, - NUM_READS, - BLOCKS_PER_READ, - BLOCKS_PER_WRITE, - READ_SIZE, - WRITE_SIZE, - > = row_slice.borrow_mut(); - row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); - - let rd = memory.record_by_id(read_record.rd); - let rs = read_record - .rs - .into_iter() - .map(|r| memory.record_by_id(r)) - .collect::>(); - - row_slice.rd_ptr = rd.pointer; - row_slice.rd_val.copy_from_slice(rd.data_slice()); - - for (i, r) in rs.iter().enumerate() { - row_slice.rs_ptr[i] = r.pointer; - row_slice.rs_val[i].copy_from_slice(r.data_slice()); - aux_cols_factory.generate_read_aux(r, &mut row_slice.rs_read_aux[i]); - } +impl< + F: PrimeField32, + const NUM_READS: usize, + const BLOCKS_PER_READ: usize, + const BLOCKS_PER_WRITE: usize, + const READ_SIZE: usize, + const WRITE_SIZE: usize, + > AdapterExecutorE1 + for RV32VecHeapAdapterStep +{ + type ReadData = [[[u8; READ_SIZE]; BLOCKS_PER_READ]; NUM_READS]; + type WriteData = [[u8; WRITE_SIZE]; BLOCKS_PER_WRITE]; - aux_cols_factory.generate_read_aux(rd, &mut row_slice.rd_read_aux); + fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData + where + Mem: GuestMemory, + { + let Instruction { b, c, d, e, .. } = *instruction; - for (i, reads) in read_record.reads.iter().enumerate() { - for (j, &x) in reads.iter().enumerate() { - let record = memory.record_by_id(x); - aux_cols_factory.generate_read_aux(record, &mut row_slice.reads_aux[i][j]); - } - } + let d = d.as_canonical_u32(); + let e = e.as_canonical_u32(); + debug_assert_eq!(d, RV32_REGISTER_AS); + debug_assert_eq!(e, RV32_MEMORY_AS); - for (i, &w) in write_record.writes.iter().enumerate() { - let record = memory.record_by_id(w); - aux_cols_factory.generate_write_aux(record, &mut row_slice.writes_aux[i]); - } + // Read register values + let rs_vals = from_fn(|i| { + let addr = if i == 0 { b } else { c }; + new_read_rv32_register(memory, d, addr.as_canonical_u32()) + }); - // Range checks: - let need_range_check: Vec = rs - .iter() - .chain(std::iter::repeat_n(&rd, 2)) - .map(|record| { - record - .data_at(RV32_REGISTER_NUM_LIMBS - 1) - .as_canonical_u32() + // Read memory values + rs_vals.map(|address| { + assert!( + address as usize + READ_SIZE * BLOCKS_PER_READ - 1 < (1 << self.pointer_max_bits) + ); + from_fn(|i| memory_read(memory, e, address + (i * READ_SIZE) as u32)) }) - .collect(); - debug_assert!(address_bits <= RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS); - let limb_shift_bits = RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - address_bits; - for pair in need_range_check.chunks_exact(2) { - bitwise_lookup_chip.request_range(pair[0] << limb_shift_bits, pair[1] << limb_shift_bits); + } + + fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) + where + Mem: GuestMemory, + { + let Instruction { a, d, e, .. } = *instruction; + let rd_val = new_read_rv32_register(memory, d.as_canonical_u32(), a.as_canonical_u32()); + assert!(rd_val as usize + WRITE_SIZE * BLOCKS_PER_WRITE - 1 < (1 << self.pointer_max_bits)); + + for i in 0..BLOCKS_PER_WRITE { + memory_write( + memory, + e.as_canonical_u32(), + rd_val + (i * WRITE_SIZE) as u32, + &data[i], + ); + } } } diff --git a/extensions/rv32im/circuit/Cargo.toml b/extensions/rv32im/circuit/Cargo.toml index 6e71db40db..0e28dd3093 100644 --- a/extensions/rv32im/circuit/Cargo.toml +++ b/extensions/rv32im/circuit/Cargo.toml @@ -21,7 +21,6 @@ derive-new.workspace = true derive_more = { workspace = true, features = ["from"] } rand.workspace = true eyre.workspace = true -test-case.workspace = true # for div_rem: num-bigint.workspace = true @@ -32,6 +31,7 @@ serde-big-array.workspace = true [dev-dependencies] openvm-stark-sdk = { workspace = true } openvm-circuit = { workspace = true, features = ["test-utils"] } +test-case.workspace = true [features] default = ["parallel", "jemalloc"] diff --git a/extensions/rv32im/circuit/src/adapters/alu.rs b/extensions/rv32im/circuit/src/adapters/alu.rs index f8322c143e..654d101469 100644 --- a/extensions/rv32im/circuit/src/adapters/alu.rs +++ b/extensions/rv32im/circuit/src/adapters/alu.rs @@ -12,7 +12,7 @@ use openvm_circuit::{ }, }; use openvm_circuit_primitives::{ - bitwise_op_lookup::{BitwiseOperationLookupBus, BitwiseOperationLookupChip}, + bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, utils::not, }; use openvm_circuit_primitives_derive::AlignedBorrow; @@ -161,15 +161,18 @@ impl VmAdapterAir for Rv32BaseAluAdapterAir { } #[derive(derive_new::new)] -pub struct Rv32BaseAluAdapterStep; +pub struct Rv32BaseAluAdapterStep { + // TODO(arayi): use reference to bitwise lookup chip with lifetimes instead + pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, +} impl AdapterTraceStep for Rv32BaseAluAdapterStep { const WIDTH: usize = size_of::>(); - type ReadData = ([u8; RV32_REGISTER_NUM_LIMBS], [u8; RV32_REGISTER_NUM_LIMBS]); - type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; - type TraceContext<'a> = &'a BitwiseOperationLookupChip; + type ReadData = [[u8; RV32_REGISTER_NUM_LIMBS]; 2]; + type WriteData = [[u8; RV32_REGISTER_NUM_LIMBS]; 1]; + type TraceContext<'a> = (); #[inline(always)] fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { @@ -218,7 +221,7 @@ impl AdapterTraceStep tracing_read_imm(memory, c.as_canonical_u32(), &mut adapter_row.rs2) }; - (rs1, rs2) + [rs1, rs2] } #[inline(always)] @@ -240,7 +243,7 @@ impl AdapterTraceStep memory, RV32_REGISTER_AS, a.as_canonical_u32(), - data, + &data[0], &mut adapter_row.writes_aux, ); } @@ -249,7 +252,7 @@ impl AdapterTraceStep fn fill_trace_row( &self, mem_helper: &MemoryAuxColsFactory, - bitwise_lookup_chip: &BitwiseOperationLookupChip, + _ctx: (), adapter_row: &mut [F], ) { let adapter_row: &mut Rv32BaseAluAdapterCols = adapter_row.borrow_mut(); @@ -264,7 +267,8 @@ impl AdapterTraceStep } else { let rs2_imm = adapter_row.rs2.as_canonical_u32(); let mask = (1 << RV32_CELL_BITS) - 1; - bitwise_lookup_chip.request_range(rs2_imm & mask, (rs2_imm >> 8) & mask); + self.bitwise_lookup_chip + .request_range(rs2_imm & mask, (rs2_imm >> 8) & mask); } timestamp += 1; @@ -277,8 +281,8 @@ where F: PrimeField32, { // TODO(ayush): directly use u32 - type ReadData = ([u8; RV32_REGISTER_NUM_LIMBS], [u8; RV32_REGISTER_NUM_LIMBS]); - type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + type ReadData = [[u8; RV32_REGISTER_NUM_LIMBS]; 2]; + type WriteData = [[u8; RV32_REGISTER_NUM_LIMBS]; 1]; #[inline(always)] fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData @@ -307,7 +311,7 @@ where imm_le }; - (rs1, rs2) + [rs1, rs2] } #[inline(always)] @@ -319,6 +323,6 @@ where debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - memory_write(memory, d.as_canonical_u32(), a.as_canonical_u32(), rd); + memory_write(memory, d.as_canonical_u32(), a.as_canonical_u32(), &rd[0]); } } diff --git a/extensions/rv32im/circuit/src/adapters/branch.rs b/extensions/rv32im/circuit/src/adapters/branch.rs index 25f55bd93d..6fb6ba00c8 100644 --- a/extensions/rv32im/circuit/src/adapters/branch.rs +++ b/extensions/rv32im/circuit/src/adapters/branch.rs @@ -20,7 +20,6 @@ use openvm_stark_backend::{ p3_air::BaseAir, p3_field::{Field, FieldAlgebra, PrimeField32}, }; -use serde::{Deserialize, Serialize}; use super::RV32_REGISTER_NUM_LIMBS; use crate::adapters::{memory_read, tracing_read}; @@ -115,7 +114,7 @@ where F: PrimeField32, { const WIDTH: usize = size_of::>(); - type ReadData = ([u8; RV32_REGISTER_NUM_LIMBS], [u8; RV32_REGISTER_NUM_LIMBS]); + type ReadData = [[u8; RV32_REGISTER_NUM_LIMBS]; 2]; type WriteData = (); type TraceContext<'a> = (); @@ -155,7 +154,7 @@ where &mut adapter_row.reads_aux[1], ); - (rs1, rs2) + [rs1, rs2] } #[inline(always)] @@ -191,7 +190,7 @@ where F: PrimeField32, { // TODO(ayush): directly use u32 - type ReadData = ([u8; RV32_REGISTER_NUM_LIMBS], [u8; RV32_REGISTER_NUM_LIMBS]); + type ReadData = [[u8; RV32_REGISTER_NUM_LIMBS]; 2]; type WriteData = (); #[inline(always)] @@ -209,7 +208,7 @@ where let rs2: [u8; RV32_REGISTER_NUM_LIMBS] = memory_read(memory, RV32_REGISTER_AS, b.as_canonical_u32()); - (rs1, rs2) + [rs1, rs2] } #[inline(always)] diff --git a/extensions/rv32im/circuit/src/adapters/mod.rs b/extensions/rv32im/circuit/src/adapters/mod.rs index bcf906e7fd..f1a29d376b 100644 --- a/extensions/rv32im/circuit/src/adapters/mod.rs +++ b/extensions/rv32im/circuit/src/adapters/mod.rs @@ -53,7 +53,7 @@ pub fn decompose(value: u32) -> [F; RV32_REGISTER_NUM_LIMBS] { } #[inline(always)] -pub fn memory_read(memory: &Mem, address_space: u32, ptr: u32) -> [u8; RV32_REGISTER_NUM_LIMBS] +pub fn memory_read(memory: &Mem, address_space: u32, ptr: u32) -> [u8; N] where Mem: GuestMemory, { @@ -67,15 +67,15 @@ where // SAFETY: // - address space `RV32_REGISTER_AS` and `RV32_MEMORY_AS` will always have cell type `u8` and // minimum alignment of `RV32_REGISTER_NUM_LIMBS` - unsafe { memory.read::(address_space, ptr) } + unsafe { memory.read::(address_space, ptr) } } #[inline(always)] -pub fn memory_write( +pub fn memory_write( memory: &mut Mem, address_space: u32, ptr: u32, - data: &[u8; RV32_REGISTER_NUM_LIMBS], + data: &[u8; N], ) where Mem: GuestMemory, { @@ -89,18 +89,18 @@ pub fn memory_write( // SAFETY: // - address space `RV32_REGISTER_AS` and `RV32_MEMORY_AS` will always have cell type `u8` and // minimum alignment of `RV32_REGISTER_NUM_LIMBS` - unsafe { memory.write::(address_space, ptr, data) } + unsafe { memory.write::(address_space, ptr, data) } } /// Atomic read operation which increments the timestamp by 1. /// Returns `(t_prev, [ptr:4]_{address_space})` where `t_prev` is the timestamp of the last memory /// access. #[inline(always)] -pub fn timed_read( +pub fn timed_read( memory: &mut TracingMemory, address_space: u32, ptr: u32, -) -> (u32, [u8; RV32_REGISTER_NUM_LIMBS]) { +) -> (u32, [u8; N]) { debug_assert!( address_space == RV32_REGISTER_AS || address_space == RV32_MEMORY_AS @@ -110,18 +110,16 @@ pub fn timed_read( // SAFETY: // - address space `RV32_REGISTER_AS` and `RV32_MEMORY_AS` will always have cell type `u8` and // minimum alignment of `RV32_REGISTER_NUM_LIMBS` - unsafe { - memory.read::(address_space, ptr) - } + unsafe { memory.read::(address_space, ptr) } } #[inline(always)] -pub fn timed_write( +pub fn timed_write( memory: &mut TracingMemory, address_space: u32, ptr: u32, - val: &[u8; RV32_REGISTER_NUM_LIMBS], -) -> (u32, [u8; RV32_REGISTER_NUM_LIMBS]) { + data: &[u8; N], +) -> (u32, [u8; N]) { // TODO(ayush): should this allow public values address space debug_assert!( address_space == RV32_REGISTER_AS @@ -130,27 +128,21 @@ pub fn timed_write( ); // SAFETY: - // - address space `RV32_REGISTER_AS` and `RV32_MEMORY_ASwill always have cell type `u8` and + // - address space `RV32_REGISTER_AS` and `RV32_MEMORY_AS` will always have cell type `u8` and // minimum alignment of `RV32_REGISTER_NUM_LIMBS` - unsafe { - memory.write::( - address_space, - ptr, - val, - ) - } + unsafe { memory.write::(address_space, ptr, data) } } /// Reads register value at `reg_ptr` from memory and records the memory access in mutable buffer. /// Trace generation relevant to this memory access can be done fully from the recorded buffer. #[inline(always)] -pub fn tracing_read( +pub fn tracing_read( memory: &mut TracingMemory, address_space: u32, ptr: u32, aux_cols: &mut MemoryReadAuxCols, /* TODO[jpw]: switch to raw u8 * buffer */ -) -> [u8; RV32_REGISTER_NUM_LIMBS] +) -> [u8; N] where F: PrimeField32, { @@ -162,18 +154,18 @@ where /// Writes `reg_ptr, reg_val` into memory and records the memory access in mutable buffer. /// Trace generation relevant to this memory access can be done fully from the recorded buffer. #[inline(always)] -pub fn tracing_write( +pub fn tracing_write( memory: &mut TracingMemory, address_space: u32, ptr: u32, - val: &[u8; RV32_REGISTER_NUM_LIMBS], - aux_cols: &mut MemoryWriteAuxCols, /* TODO[jpw]: switch to raw - * u8 - * buffer */ + data: &[u8; N], + aux_cols: &mut MemoryWriteAuxCols, /* TODO[jpw]: switch to raw + * u8 + * buffer */ ) where F: PrimeField32, { - let (t_prev, data_prev) = timed_write(memory, address_space, ptr, val); + let (t_prev, data_prev) = timed_write(memory, address_space, ptr, data); aux_cols.set_prev( F::from_canonical_u32(t_prev), data_prev.map(F::from_canonical_u8), @@ -182,16 +174,16 @@ pub fn tracing_write( // TODO(ayush): this is bad but not sure how to avoid #[inline(always)] -pub fn tracing_write_with_base_aux( +pub fn tracing_write_with_base_aux( memory: &mut TracingMemory, address_space: u32, ptr: u32, - val: &[u8; RV32_REGISTER_NUM_LIMBS], + data: &[u8; N], base_aux_cols: &mut MemoryBaseAuxCols, ) where F: PrimeField32, { - let (t_prev, _) = timed_write(memory, address_space, ptr, val); + let (t_prev, _) = timed_write(memory, address_space, ptr, data); base_aux_cols.set_prev(F::from_canonical_u32(t_prev)); } @@ -231,6 +223,11 @@ pub fn read_rv32_register( (record.0, val) } +#[inline(always)] +pub fn new_read_rv32_register(memory: &Mem, address_space: u32, ptr: u32) -> u32 { + u32::from_le_bytes(memory_read(memory, address_space, ptr)) +} + /// Peeks at the value of a register without updating the memory state or incrementing the /// timestamp. pub fn unsafe_read_rv32_register(memory: &MemoryController, pointer: F) -> u32 { diff --git a/extensions/rv32im/circuit/src/adapters/mul.rs b/extensions/rv32im/circuit/src/adapters/mul.rs index 4b937459f2..9259f5549b 100644 --- a/extensions/rv32im/circuit/src/adapters/mul.rs +++ b/extensions/rv32im/circuit/src/adapters/mul.rs @@ -20,7 +20,6 @@ use openvm_stark_backend::{ p3_air::BaseAir, p3_field::{Field, FieldAlgebra, PrimeField32}, }; -use serde::{Deserialize, Serialize}; use super::{tracing_write, RV32_REGISTER_NUM_LIMBS}; use crate::adapters::{memory_read, memory_write, tracing_read}; @@ -132,8 +131,8 @@ where F: PrimeField32, { const WIDTH: usize = size_of::>(); - type ReadData = ([u8; RV32_REGISTER_NUM_LIMBS], [u8; RV32_REGISTER_NUM_LIMBS]); - type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + type ReadData = [[u8; RV32_REGISTER_NUM_LIMBS]; 2]; + type WriteData = [[u8; RV32_REGISTER_NUM_LIMBS]; 1]; type TraceContext<'a> = (); #[inline(always)] @@ -171,7 +170,7 @@ where &mut adapter_row.reads_aux[1], ); - (rs1, rs2) + [rs1, rs2] } #[inline(always)] @@ -193,7 +192,7 @@ where memory, RV32_REGISTER_AS, a.as_canonical_u32(), - data, + &data[0], &mut adapter_row.writes_aux, ) } @@ -224,8 +223,8 @@ where F: PrimeField32, { // TODO(ayush): directly use u32 - type ReadData = ([u8; RV32_REGISTER_NUM_LIMBS], [u8; RV32_REGISTER_NUM_LIMBS]); - type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + type ReadData = [[u8; RV32_REGISTER_NUM_LIMBS]; 2]; + type WriteData = [[u8; RV32_REGISTER_NUM_LIMBS]; 1]; #[inline(always)] fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData @@ -241,7 +240,7 @@ where let rs2: [u8; RV32_REGISTER_NUM_LIMBS] = memory_read(memory, RV32_REGISTER_AS, c.as_canonical_u32()); - (rs1, rs2) + [rs1, rs2] } #[inline(always)] @@ -253,6 +252,6 @@ where debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - memory_write(memory, RV32_REGISTER_AS, a.as_canonical_u32(), rd); + memory_write(memory, RV32_REGISTER_AS, a.as_canonical_u32(), &rd[0]); } } diff --git a/extensions/rv32im/circuit/src/base_alu/core.rs b/extensions/rv32im/circuit/src/base_alu/core.rs index 21281996e2..aa25a05309 100644 --- a/extensions/rv32im/circuit/src/base_alu/core.rs +++ b/extensions/rv32im/circuit/src/base_alu/core.rs @@ -15,9 +15,7 @@ use openvm_circuit::{ }, }; use openvm_circuit_primitives::{ - bitwise_op_lookup::{ - BitwiseOperationLookupBus, BitwiseOperationLookupChip, SharedBitwiseOperationLookupChip, - }, + bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, utils::not, }; use openvm_circuit_primitives_derive::AlignedBorrow; @@ -200,9 +198,9 @@ where + for<'a> AdapterTraceStep< F, CTX, - ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), - WriteData = [u8; NUM_LIMBS], - TraceContext<'a> = &'a BitwiseOperationLookupChip, + ReadData: Into<[[u8; NUM_LIMBS]; 2]>, + WriteData: From<[[u8; NUM_LIMBS]; 1]>, + TraceContext<'a> = (), >, { fn get_opcode_name(&self, opcode: usize) -> String { @@ -221,12 +219,15 @@ where let local_opcode = BaseAluOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); - let (rs1, rs2) = self.adapter.read(state.memory, instruction, adapter_row); + let [rs1, rs2] = self + .adapter + .read(state.memory, instruction, adapter_row) + .into(); let rd = run_alu::(local_opcode, &rs1, &rs2); @@ -241,7 +242,7 @@ where core_row.opcode_and_flag = F::from_bool(local_opcode == BaseAluOpcode::AND); self.adapter - .write(state.memory, instruction, adapter_row, &rd); + .write(state.memory, instruction, adapter_row, &[rd].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); @@ -253,8 +254,7 @@ where fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - self.adapter - .fill_trace_row(mem_helper, self.bitwise_lookup_chip.as_ref(), adapter_row); + self.adapter.fill_trace_row(mem_helper, (), adapter_row); let core_row: &mut BaseAluCoreCols = core_row.borrow_mut(); @@ -279,8 +279,8 @@ where A: 'static + for<'a> AdapterExecutorE1< F, - ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), - WriteData = [u8; NUM_LIMBS], + ReadData: Into<[[u8; NUM_LIMBS]; 2]>, + WriteData: From<[[u8; NUM_LIMBS]; 1]>, >, { fn execute_e1( @@ -295,9 +295,9 @@ where let local_opcode = BaseAluOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let (rs1, rs2) = self.adapter.read(state.memory, instruction); + let [rs1, rs2] = self.adapter.read(state.memory, instruction).into(); let rd = run_alu::(local_opcode, &rs1, &rs2); - self.adapter.write(state.memory, instruction, &rd); + self.adapter.write(state.memory, instruction, &[rd].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); diff --git a/extensions/rv32im/circuit/src/base_alu/tests.rs b/extensions/rv32im/circuit/src/base_alu/tests.rs index 194d536aeb..ff4ddbaae4 100644 --- a/extensions/rv32im/circuit/src/base_alu/tests.rs +++ b/extensions/rv32im/circuit/src/base_alu/tests.rs @@ -55,7 +55,7 @@ fn create_test_chip( BaseAluCoreAir::new(bitwise_bus, BaseAluOpcode::CLASS_OFFSET), ), Rv32BaseAluStep::new( - Rv32BaseAluAdapterStep::new(), + Rv32BaseAluAdapterStep::new(bitwise_chip.clone()), bitwise_chip.clone(), BaseAluOpcode::CLASS_OFFSET, ), diff --git a/extensions/rv32im/circuit/src/branch_eq/core.rs b/extensions/rv32im/circuit/src/branch_eq/core.rs index ba464dbd9b..3f375b31ab 100644 --- a/extensions/rv32im/circuit/src/branch_eq/core.rs +++ b/extensions/rv32im/circuit/src/branch_eq/core.rs @@ -13,9 +13,12 @@ use openvm_circuit::{ MemoryAuxColsFactory, }, }; -use openvm_circuit_primitives::utils::not; +use openvm_circuit_primitives::{ + bitwise_op_lookup::{BitwiseOperationLookupChip, SharedBitwiseOperationLookupChip}, + utils::not, +}; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; +use openvm_instructions::{instruction::Instruction, riscv::RV32_CELL_BITS, LocalOpcode}; use openvm_rv32im_transpiler::BranchEqualOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -23,8 +26,6 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, rap::BaseAirWithPublicValues, }; -use serde::{Deserialize, Serialize}; -use serde_big_array::BigArray; use strum::IntoEnumIterator; #[repr(C)] @@ -140,7 +141,6 @@ where } } -#[derive(Debug)] pub struct BranchEqualStep { adapter: A, pub offset: usize, @@ -164,7 +164,7 @@ where + for<'a> AdapterTraceStep< F, CTX, - ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), + ReadData: Into<[[u8; NUM_LIMBS]; 2]>, WriteData = (), TraceContext<'a> = (), >, @@ -185,12 +185,15 @@ where let branch_eq_opcode = BranchEqualOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); - let (rs1, rs2) = self.adapter.read(state.memory, instruction, adapter_row); + let [rs1, rs2] = self + .adapter + .read(state.memory, instruction, adapter_row) + .into(); let (cmp_result, diff_idx, diff_inv_val) = run_eq(branch_eq_opcode, &rs1, &rs2); @@ -225,8 +228,7 @@ where impl StepExecutorE1 for BranchEqualStep where F: PrimeField32, - A: 'static - + for<'a> AdapterExecutorE1, + A: 'static + for<'a> AdapterExecutorE1, WriteData = ()>, { fn execute_e1( &mut self, @@ -240,7 +242,7 @@ where let branch_eq_opcode = BranchEqualOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let (rs1, rs2) = self.adapter.read(state.memory, instruction); + let [rs1, rs2] = self.adapter.read(state.memory, instruction).into(); // TODO(ayush): probably don't need the other values let (cmp_result, _, _) = run_eq::(branch_eq_opcode, &rs1, &rs2); diff --git a/extensions/rv32im/circuit/src/branch_lt/core.rs b/extensions/rv32im/circuit/src/branch_lt/core.rs index 6f15ec80af..30d287731f 100644 --- a/extensions/rv32im/circuit/src/branch_lt/core.rs +++ b/extensions/rv32im/circuit/src/branch_lt/core.rs @@ -26,8 +26,6 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, rap::BaseAirWithPublicValues, }; -use serde::{Deserialize, Serialize}; -use serde_big_array::BigArray; use strum::IntoEnumIterator; #[repr(C)] @@ -223,7 +221,7 @@ where + for<'a> AdapterTraceStep< F, CTX, - ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), + ReadData: Into<[[u8; NUM_LIMBS]; 2]>, WriteData = (), TraceContext<'a> = (), >, @@ -247,12 +245,15 @@ where let blt_opcode = BranchLessThanOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); - let (rs1, rs2) = self.adapter.read(state.memory, instruction, adapter_row); + let [rs1, rs2] = self + .adapter + .read(state.memory, instruction, adapter_row) + .into(); let (cmp_result, diff_idx, a_sign, b_sign) = run_cmp::(blt_opcode, &rs1, &rs2); @@ -355,8 +356,7 @@ impl StepExecutorE1 for BranchLessThanStep where F: PrimeField32, - A: 'static - + for<'a> AdapterExecutorE1, + A: 'static + for<'a> AdapterExecutorE1, WriteData = ()>, { fn execute_e1( &mut self, @@ -370,7 +370,7 @@ where let blt_opcode = BranchLessThanOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let (rs1, rs2) = self.adapter.read(state.memory, instruction); + let [rs1, rs2] = self.adapter.read(state.memory, instruction).into(); // TODO(ayush): probably don't need the other values let (cmp_result, _, _, _) = run_cmp::(blt_opcode, &rs1, &rs2); diff --git a/extensions/rv32im/circuit/src/divrem/core.rs b/extensions/rv32im/circuit/src/divrem/core.rs index 8e38e6aabc..a66048b05c 100644 --- a/extensions/rv32im/circuit/src/divrem/core.rs +++ b/extensions/rv32im/circuit/src/divrem/core.rs @@ -29,8 +29,7 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, rap::BaseAirWithPublicValues, }; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_big_array::BigArray; + use strum::IntoEnumIterator; #[repr(C)] @@ -401,8 +400,8 @@ where + for<'a> AdapterTraceStep< F, CTX, - ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), - WriteData = [u8; NUM_LIMBS], + ReadData: Into<[[u8; NUM_LIMBS]; 2]>, + WriteData: From<[[u8; NUM_LIMBS]; 1]>, TraceContext<'a> = (), >, { @@ -425,12 +424,15 @@ where let is_signed = divrem_opcode == DivRemOpcode::DIV || divrem_opcode == DivRemOpcode::REM; let is_div = divrem_opcode == DivRemOpcode::DIV || divrem_opcode == DivRemOpcode::DIVU; - let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); - let (rs1, rs2) = self.adapter.read(state.memory, instruction, adapter_row); + let [rs1, rs2] = self + .adapter + .read(state.memory, instruction, adapter_row) + .into(); let b = rs1.map(u32::from); let c = rs2.map(u32::from); @@ -514,7 +516,7 @@ where }; self.adapter - .write(state.memory, instruction, adapter_row, &rd); + .write(state.memory, instruction, adapter_row, &[rd].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); @@ -537,8 +539,8 @@ where A: 'static + for<'a> AdapterExecutorE1< F, - ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), - WriteData = [u8; NUM_LIMBS], + ReadData: Into<[[u8; NUM_LIMBS]; 2]>, + WriteData: From<[[u8; NUM_LIMBS]; 1]>, >, { fn execute_e1( @@ -554,7 +556,7 @@ where // Determine opcode and operation type let divrem_opcode = DivRemOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let (rs1, rs2) = self.adapter.read(state.memory, instruction); + let [rs1, rs2] = self.adapter.read(state.memory, instruction).into(); let rs1 = rs1.map(u32::from); let rs2 = rs2.map(u32::from); @@ -571,7 +573,7 @@ where r.map(|x| x as u8) }; - self.adapter.write(state.memory, instruction, &rd); + self.adapter.write(state.memory, instruction, &[rd].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); diff --git a/extensions/rv32im/circuit/src/extension.rs b/extensions/rv32im/circuit/src/extension.rs index d9dd57dc4c..c2a6a973c9 100644 --- a/extensions/rv32im/circuit/src/extension.rs +++ b/extensions/rv32im/circuit/src/extension.rs @@ -225,7 +225,7 @@ impl VmExtension for Rv32I { BaseAluCoreAir::new(bitwise_lu_chip.bus(), BaseAluOpcode::CLASS_OFFSET), ), Rv32BaseAluStep::new( - Rv32BaseAluAdapterStep::new(), + Rv32BaseAluAdapterStep::new(bitwise_lu_chip.clone()), bitwise_lu_chip.clone(), BaseAluOpcode::CLASS_OFFSET, ), @@ -247,7 +247,7 @@ impl VmExtension for Rv32I { LessThanCoreAir::new(bitwise_lu_chip.bus(), LessThanOpcode::CLASS_OFFSET), ), LessThanStep::new( - Rv32BaseAluAdapterStep::new(), + Rv32BaseAluAdapterStep::new(bitwise_lu_chip.clone()), bitwise_lu_chip.clone(), LessThanOpcode::CLASS_OFFSET, ), @@ -270,7 +270,7 @@ impl VmExtension for Rv32I { ), ), ShiftStep::new( - Rv32BaseAluAdapterStep::new(), + Rv32BaseAluAdapterStep::new(bitwise_lu_chip.clone()), bitwise_lu_chip.clone(), range_checker.clone(), ShiftOpcode::CLASS_OFFSET, diff --git a/extensions/rv32im/circuit/src/less_than/core.rs b/extensions/rv32im/circuit/src/less_than/core.rs index 5d63f03652..0ad602d63d 100644 --- a/extensions/rv32im/circuit/src/less_than/core.rs +++ b/extensions/rv32im/circuit/src/less_than/core.rs @@ -14,9 +14,7 @@ use openvm_circuit::{ }, }; use openvm_circuit_primitives::{ - bitwise_op_lookup::{ - BitwiseOperationLookupBus, BitwiseOperationLookupChip, SharedBitwiseOperationLookupChip, - }, + bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, utils::not, }; use openvm_circuit_primitives_derive::AlignedBorrow; @@ -28,8 +26,6 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, rap::BaseAirWithPublicValues, }; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_big_array::BigArray; use strum::IntoEnumIterator; #[repr(C)] @@ -199,9 +195,9 @@ where + for<'a> AdapterTraceStep< F, CTX, - ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), - WriteData = [u8; NUM_LIMBS], - TraceContext<'a> = &'a BitwiseOperationLookupChip, + ReadData: Into<[[u8; NUM_LIMBS]; 2]>, + WriteData: From<[[u8; NUM_LIMBS]; 1]>, + TraceContext<'a> = (), >, { fn get_opcode_name(&self, opcode: usize) -> String { @@ -222,12 +218,15 @@ where let local_opcode = LessThanOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); - let (rs1, rs2) = self.adapter.read(state.memory, instruction, adapter_row); + let [rs1, rs2] = self + .adapter + .read(state.memory, instruction, adapter_row) + .into(); let (cmp_result, _, _, _) = run_less_than::(local_opcode, &rs1, &rs2); @@ -241,7 +240,7 @@ where output[0] = cmp_result as u8; self.adapter - .write(state.memory, instruction, adapter_row, &output); + .write(state.memory, instruction, adapter_row, &[output].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); @@ -253,8 +252,7 @@ where fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - self.adapter - .fill_trace_row(mem_helper, self.bitwise_lookup_chip.as_ref(), adapter_row); + self.adapter.fill_trace_row(mem_helper, (), adapter_row); let core_row: &mut LessThanCoreCols<_, NUM_LIMBS, LIMB_BITS> = core_row.borrow_mut(); @@ -333,8 +331,8 @@ where A: 'static + for<'a> AdapterExecutorE1< F, - ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), - WriteData = [u8; NUM_LIMBS], + ReadData: Into<[[u8; NUM_LIMBS]; 2]>, + WriteData: From<[[u8; NUM_LIMBS]; 1]>, >, { fn execute_e1( @@ -349,7 +347,7 @@ where let less_than_opcode = LessThanOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let (rs1, rs2) = self.adapter.read(state.memory, instruction); + let [rs1, rs2] = self.adapter.read(state.memory, instruction).into(); // Run the comparison let (cmp_result, _, _, _) = @@ -357,7 +355,7 @@ where let mut rd = [0u8; NUM_LIMBS]; rd[0] = cmp_result as u8; - self.adapter.write(state.memory, instruction, &rd); + self.adapter.write(state.memory, instruction, &[rd].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); diff --git a/extensions/rv32im/circuit/src/less_than/tests.rs b/extensions/rv32im/circuit/src/less_than/tests.rs index 52ac6f67c4..373c2920f7 100644 --- a/extensions/rv32im/circuit/src/less_than/tests.rs +++ b/extensions/rv32im/circuit/src/less_than/tests.rs @@ -58,7 +58,7 @@ fn create_test_chip( LessThanCoreAir::new(bitwise_bus, LessThanOpcode::CLASS_OFFSET), ), LessThanStep::new( - Rv32BaseAluAdapterStep::new(), + Rv32BaseAluAdapterStep::new(bitwise_chip.clone()), bitwise_chip.clone(), LessThanOpcode::CLASS_OFFSET, ), diff --git a/extensions/rv32im/circuit/src/loadstore/tests.rs b/extensions/rv32im/circuit/src/loadstore/tests.rs index 58b36f16ae..d7c758515e 100644 --- a/extensions/rv32im/circuit/src/loadstore/tests.rs +++ b/extensions/rv32im/circuit/src/loadstore/tests.rs @@ -39,7 +39,7 @@ const MAX_INS_CAPACITY: usize = 128; type F = BabyBear; fn create_test_chip(tester: &mut VmChipTestBuilder) -> Rv32LoadStoreChip { - let range_checker_chip = tester.memory_controller().range_checker.clone(); + let range_checker_chip = tester.range_checker(); let chip = Rv32LoadStoreChip::::new( VmAirWrapper::new( Rv32LoadStoreAdapterAir::new( diff --git a/extensions/rv32im/circuit/src/mul/core.rs b/extensions/rv32im/circuit/src/mul/core.rs index fb4469fdaf..efa115aa54 100644 --- a/extensions/rv32im/circuit/src/mul/core.rs +++ b/extensions/rv32im/circuit/src/mul/core.rs @@ -23,8 +23,6 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, rap::BaseAirWithPublicValues, }; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_big_array::BigArray; #[repr(C)] #[derive(AlignedBorrow)] @@ -160,8 +158,8 @@ where + for<'a> AdapterTraceStep< F, CTX, - ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), - WriteData = [u8; NUM_LIMBS], + ReadData: Into<[[u8; NUM_LIMBS]; 2]>, + WriteData: From<[[u8; NUM_LIMBS]; 1]>, TraceContext<'a> = (), >, { @@ -184,12 +182,15 @@ where MulOpcode::MUL ); - let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); - let (rs1, rs2) = self.adapter.read(state.memory, instruction, adapter_row); + let [rs1, rs2] = self + .adapter + .read(state.memory, instruction, adapter_row) + .into(); let (a, carry) = run_mul::(&rs1, &rs2); @@ -206,7 +207,7 @@ where // TODO(ayush): avoid this conversion self.adapter - .write(state.memory, instruction, adapter_row, &a); + .write(state.memory, instruction, adapter_row, &[a].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); @@ -229,8 +230,8 @@ where A: 'static + for<'a> AdapterExecutorE1< F, - ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), - WriteData = [u8; NUM_LIMBS], + ReadData: Into<[[u8; NUM_LIMBS]; 2]>, + WriteData: From<[[u8; NUM_LIMBS]; 1]>, >, { fn execute_e1( @@ -250,11 +251,11 @@ where MulOpcode::MUL ); - let (rs1, rs2) = self.adapter.read(state.memory, instruction); + let [rs1, rs2] = self.adapter.read(state.memory, instruction).into(); let (rd, _) = run_mul::(&rs1, &rs2); - self.adapter.write(state.memory, instruction, &rd); + self.adapter.write(state.memory, instruction, &[rd].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); diff --git a/extensions/rv32im/circuit/src/mulh/core.rs b/extensions/rv32im/circuit/src/mulh/core.rs index 62672f05dc..555a70bbd7 100644 --- a/extensions/rv32im/circuit/src/mulh/core.rs +++ b/extensions/rv32im/circuit/src/mulh/core.rs @@ -26,8 +26,7 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, rap::BaseAirWithPublicValues, }; -use serde::{Deserialize, Serialize}; -use serde_big_array::BigArray; + use strum::IntoEnumIterator; #[repr(C)] @@ -231,8 +230,8 @@ where + for<'a> AdapterTraceStep< F, CTX, - ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), - WriteData = [u8; NUM_LIMBS], + ReadData: Into<[[u8; NUM_LIMBS]; 2]>, + WriteData: From<[[u8; NUM_LIMBS]; 1]>, TraceContext<'a> = (), >, { @@ -255,12 +254,15 @@ where let mulh_opcode = MulHOpcode::from_usize(opcode.local_opcode_idx(MulHOpcode::CLASS_OFFSET)); - let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); - let (rs1, rs2) = self.adapter.read(state.memory, instruction, adapter_row); + let [rs1, rs2] = self + .adapter + .read(state.memory, instruction, adapter_row) + .into(); let b = rs1.map(u32::from); let c = rs2.map(u32::from); @@ -296,7 +298,7 @@ where // TODO(ayush): avoid this conversion let a = a.map(|x| x as u8); self.adapter - .write(state.memory, instruction, adapter_row, &a); + .write(state.memory, instruction, adapter_row, &[a].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); @@ -319,8 +321,8 @@ where A: 'static + for<'a> AdapterExecutorE1< F, - ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), - WriteData = [u8; NUM_LIMBS], + ReadData: Into<[[u8; NUM_LIMBS]; 2]>, + WriteData: From<[[u8; NUM_LIMBS]; 1]>, >, { fn execute_e1( @@ -335,14 +337,14 @@ where let mulh_opcode = MulHOpcode::from_usize(opcode.local_opcode_idx(MulHOpcode::CLASS_OFFSET)); - let (rs1, rs2) = self.adapter.read(state.memory, instruction); + let [rs1, rs2] = self.adapter.read(state.memory, instruction).into(); let rs1 = rs1.map(u32::from); let rs2 = rs2.map(u32::from); let (rd, _, _, _, _) = run_mulh::(mulh_opcode, &rs1, &rs2); let rd = rd.map(|x| x as u8); - self.adapter.write(state.memory, instruction, &rd); + self.adapter.write(state.memory, instruction, &[rd].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); diff --git a/extensions/rv32im/circuit/src/shift/core.rs b/extensions/rv32im/circuit/src/shift/core.rs index c31ff76b9b..ba68e61a05 100644 --- a/extensions/rv32im/circuit/src/shift/core.rs +++ b/extensions/rv32im/circuit/src/shift/core.rs @@ -14,9 +14,7 @@ use openvm_circuit::{ }, }; use openvm_circuit_primitives::{ - bitwise_op_lookup::{ - BitwiseOperationLookupBus, BitwiseOperationLookupChip, SharedBitwiseOperationLookupChip, - }, + bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, utils::not, var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus}, }; @@ -275,9 +273,9 @@ where + for<'a> AdapterTraceStep< F, CTX, - ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), - WriteData = [u8; NUM_LIMBS], - TraceContext<'a> = &'a BitwiseOperationLookupChip, + ReadData: Into<[[u8; NUM_LIMBS]; 2]>, + WriteData: From<[[u8; NUM_LIMBS]; 1]>, + TraceContext<'a> = (), >, { fn get_opcode_name(&self, opcode: usize) -> String { @@ -296,12 +294,15 @@ where let local_opcode = ShiftOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); - let (rs1, rs2) = self.adapter.read(state.memory, instruction, adapter_row); + let [rs1, rs2] = self + .adapter + .read(state.memory, instruction, adapter_row) + .into(); let (output, limb_shift, bit_shift) = run_shift::(local_opcode, &rs1, &rs2); @@ -316,7 +317,7 @@ where core_row.limb_shift_marker[0] = F::from_canonical_usize(limb_shift); self.adapter - .write(state.memory, instruction, adapter_row, &output); + .write(state.memory, instruction, adapter_row, &[output].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); @@ -328,8 +329,7 @@ where fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - self.adapter - .fill_trace_row(mem_helper, self.bitwise_lookup_chip.as_ref(), adapter_row); + self.adapter.fill_trace_row(mem_helper, (), adapter_row); let core_row: &mut ShiftCoreCols = core_row.borrow_mut(); @@ -393,8 +393,8 @@ where A: 'static + for<'a> AdapterExecutorE1< F, - ReadData = ([u8; NUM_LIMBS], [u8; NUM_LIMBS]), - WriteData = [u8; NUM_LIMBS], + ReadData: Into<[[u8; NUM_LIMBS]; 2]>, + WriteData: From<[[u8; NUM_LIMBS]; 1]>, >, { fn execute_e1( @@ -409,11 +409,11 @@ where let shift_opcode = ShiftOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let (rs1, rs2) = self.adapter.read(state.memory, instruction); + let [rs1, rs2] = self.adapter.read(state.memory, instruction).into(); let (rd, _, _) = run_shift::(shift_opcode, &rs1, &rs2); - self.adapter.write(state.memory, instruction, &rd); + self.adapter.write(state.memory, instruction, &[rd].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); diff --git a/extensions/rv32im/circuit/src/shift/tests.rs b/extensions/rv32im/circuit/src/shift/tests.rs index 7b92556ccd..fd111e2342 100644 --- a/extensions/rv32im/circuit/src/shift/tests.rs +++ b/extensions/rv32im/circuit/src/shift/tests.rs @@ -58,7 +58,7 @@ fn create_test_chip( ), ), ShiftStep::new( - Rv32BaseAluAdapterStep::new(), + Rv32BaseAluAdapterStep::new(bitwise_chip.clone()), bitwise_chip.clone(), tester.range_checker().clone(), ShiftOpcode::CLASS_OFFSET, From 917457734d01a9b92f17130f49742c687eadaed3 Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Tue, 13 May 2025 14:23:07 -0400 Subject: [PATCH 17/49] feat(new-execution): add codspeed execution benchmark (#1643) Closes INT-4013 --- .github/workflows/benchmarks-execute.yml | 31 ++++++++ Cargo.lock | 90 ++++++++++++++++++------ benchmarks/execute/Cargo.toml | 10 +-- 3 files changed, 105 insertions(+), 26 deletions(-) diff --git a/.github/workflows/benchmarks-execute.yml b/.github/workflows/benchmarks-execute.yml index 741ccdb0f1..034d3e9954 100644 --- a/.github/workflows/benchmarks-execute.yml +++ b/.github/workflows/benchmarks-execute.yml @@ -110,3 +110,34 @@ jobs: echo -e "\nBenchmark Summary:" cat "$SUMMARY_FILE" fi + + codspeed-benchmarks: + runs-on: + - runs-on=${{ github.run_id }} + - runner=8cpu-linux-x64 + steps: + - uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + + - name: Install cargo-binstall + uses: cargo-bins/cargo-binstall@main + + - name: Install codspeed + run: cargo binstall --no-confirm --force cargo-codspeed + + - name: Build benchmarks + working-directory: benchmarks/execute + run: cargo codspeed build + + - name: Run benchmarks with CodSpeed + uses: CodSpeedHQ/action@v3 + with: + working-directory: benchmarks/execute + run: cargo codspeed run + token: ${{ secrets.CODSPEED_TOKEN }} diff --git a/Cargo.lock b/Cargo.lock index f56c362d30..1e74d992af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1444,12 +1444,75 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "codspeed" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f4cce9c27c49c4f101fffeebb1826f41a9df2e7498b7cd4d95c0658b796c6c" +dependencies = [ + "colored", + "libc", + "serde", + "serde_json", + "uuid", +] + +[[package]] +name = "codspeed-divan-compat" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8620a09dfaf37b3c45f982c4b65bd8f9b0203944da3ffa705c0fcae6b84655ff" +dependencies = [ + "codspeed", + "codspeed-divan-compat-macros", + "codspeed-divan-compat-walltime", +] + +[[package]] +name = "codspeed-divan-compat-macros" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30fe872bc4214626b35d3a1706a905d0243503bb6ba3bb7be2fc59083d5d680c" +dependencies = [ + "divan-macros", + "itertools 0.14.0", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "codspeed-divan-compat-walltime" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104caa97b36d4092d89e24e4b103b40ede1edab03c0372d19e14a33f9393132b" +dependencies = [ + "cfg-if", + "clap", + "codspeed", + "condtype", + "divan-macros", + "libc", + "regex-lite", +] + [[package]] name = "colorchoice" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + [[package]] name = "condtype" version = "1.3.0" @@ -1951,25 +2014,11 @@ dependencies = [ "syn 2.0.98", ] -[[package]] -name = "divan" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a405457ec78b8fe08b0e32b4a3570ab5dff6dd16eb9e76a5ee0a9d9cbd898933" -dependencies = [ - "cfg-if", - "clap", - "condtype", - "divan-macros", - "libc", - "regex-lite", -] - [[package]] name = "divan-macros" -version = "0.1.21" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9556bc800956545d6420a640173e5ba7dfa82f38d3ea5a167eb555bc69ac3323" +checksum = "8dc51d98e636f5e3b0759a39257458b22619cac7e96d932da6eeb052891bb67c" dependencies = [ "proc-macro2", "quote", @@ -4055,19 +4104,15 @@ dependencies = [ name = "openvm-benchmarks-execute" version = "1.1.1-rc.0" dependencies = [ - "cargo-openvm", "clap", + "codspeed-divan-compat", "criterion", "derive_more 1.0.0", - "divan", "eyre", "openvm-benchmarks-utils", "openvm-circuit", - "openvm-keccak256-circuit", - "openvm-keccak256-transpiler", "openvm-rv32im-circuit", "openvm-rv32im-transpiler", - "openvm-sdk", "openvm-stark-sdk", "openvm-transpiler", "tracing", @@ -7648,6 +7693,9 @@ name = "uuid" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c1f41ffb7cf259f1ecc2876861a17e7142e63ead296f671f81f6ae85903e0d6" +dependencies = [ + "getrandom 0.3.1", +] [[package]] name = "valuable" diff --git a/benchmarks/execute/Cargo.toml b/benchmarks/execute/Cargo.toml index 8a77dc4a5f..db554f5489 100644 --- a/benchmarks/execute/Cargo.toml +++ b/benchmarks/execute/Cargo.toml @@ -9,15 +9,15 @@ license.workspace = true [dependencies] openvm-benchmarks-utils.workspace = true -cargo-openvm.workspace = true +# cargo-openvm.workspace = true openvm-circuit.workspace = true -openvm-sdk.workspace = true +# openvm-sdk.workspace = true openvm-stark-sdk.workspace = true openvm-transpiler.workspace = true openvm-rv32im-circuit.workspace = true openvm-rv32im-transpiler.workspace = true -openvm-keccak256-circuit.workspace = true -openvm-keccak256-transpiler.workspace = true +# openvm-keccak256-circuit.workspace = true +# openvm-keccak256-transpiler.workspace = true clap = { version = "4.5.9", features = ["derive", "env"] } eyre.workspace = true @@ -28,7 +28,7 @@ tracing-subscriber = { version = "0.3.17", features = ["std", "env-filter"] } [dev-dependencies] criterion = { version = "0.5", features = ["html_reports"] } -divan = { version = "0.1.21" } +divan = { package = "codspeed-divan-compat", version = "*" } [features] default = ["jemalloc"] From 169d5bb30206fb6d2a3449445fdba9f8c0c35c8e Mon Sep 17 00:00:00 2001 From: Golovanov399 Date: Wed, 14 May 2025 00:34:22 +0300 Subject: [PATCH 18/49] feat: memory access adapters, boundary chips, merkle chip E3 (#1640) Resolves INT-3801. - Added memory access adapters. To improve: * Allocate the trace buffer once before filling it as opposed to pushing to `Vec` how it's done now, * Maybe not call `get_f` too often (although I don't know how to avoid it normally). - Added volatile and persistent boundary chips tracegen, - Added merkle chip tracegen as described [here](https://docs.google.com/document/d/12cH7ZYRFWHgflpPzOILb7bg5XExdyWOL4vwrQ9HFGkQ/edit?tab=t.0#heading=h.hrg0oexxgu9). To improve: * Parallelize at least something, * Maybe support passing this struct between segments. - `VmChipTestBuilder` now has `::default_persistent`, so all tests in `extensions/rv32im/circuit` pass both with volatile and persistent memory interface. --- Cargo.lock | 2222 ++++++++++++----- crates/vm/src/arch/execution_control.rs | 9 +- crates/vm/src/arch/segment.rs | 3 +- crates/vm/src/arch/testing/memory/mod.rs | 21 - crates/vm/src/arch/testing/mod.rs | 96 +- crates/vm/src/system/memory/adapter/mod.rs | 152 +- crates/vm/src/system/memory/controller/mod.rs | 248 +- crates/vm/src/system/memory/merkle/mod.rs | 17 +- .../vm/src/system/memory/merkle/tests/mod.rs | 15 +- crates/vm/src/system/memory/merkle/trace.rs | 171 +- crates/vm/src/system/memory/merkle/tree.rs | 228 ++ crates/vm/src/system/memory/online.rs | 197 +- crates/vm/src/system/memory/persistent.rs | 54 +- .../rv32im/circuit/src/branch_lt/tests.rs | 2 +- .../rv32im/circuit/src/loadstore/tests.rs | 1 - extensions/rv32im/circuit/src/mulh/tests.rs | 15 +- 16 files changed, 2368 insertions(+), 1083 deletions(-) create mode 100644 crates/vm/src/system/memory/merkle/tree.rs diff --git a/Cargo.lock b/Cargo.lock index 1e74d992af..0bb10df310 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -58,7 +58,7 @@ dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -76,28 +76,61 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "alloy-eip2124" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "741bdd7499908b3aa0b159bba11e71c8cddd009a2c2eb7a06e825f1ec87900a5" +dependencies = [ + "alloy-primitives 1.1.0", + "alloy-rlp", + "crc", + "serde", + "thiserror 2.0.12", +] + [[package]] name = "alloy-eip2930" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" +checksum = "7b82752a889170df67bbb36d42ca63c531eb16274f0d7299ae2a680facba17bd" dependencies = [ - "alloy-primitives 0.8.25", + "alloy-primitives 1.1.0", "alloy-rlp", "serde", ] [[package]] name = "alloy-eip7702" -version = "0.4.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c986539255fb839d1533c128e190e557e52ff652c9ef62939e233a81dd93f7e" +checksum = "804cefe429015b4244966c006d25bda5545fa9db5990e9c9079faf255052f50a" dependencies = [ - "alloy-primitives 0.8.25", + "alloy-primitives 1.1.0", "alloy-rlp", - "derive_more 1.0.0", "k256", "serde", + "thiserror 2.0.12", +] + +[[package]] +name = "alloy-eips" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "609515c1955b33af3d78d26357540f68c5551a90ef58fd53def04f2aa074ec43" +dependencies = [ + "alloy-eip2124", + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives 1.1.0", + "alloy-rlp", + "alloy-serde", + "auto_impl", + "c-kzg", + "derive_more 2.0.1", + "either", + "serde", + "sha2 0.10.9", ] [[package]] @@ -121,7 +154,7 @@ dependencies = [ "bytes", "cfg-if", "const-hex", - "derive_more 0.99.19", + "derive_more 0.99.20", "hex-literal", "itoa", "ruint", @@ -138,7 +171,7 @@ dependencies = [ "bytes", "cfg-if", "const-hex", - "derive_more 0.99.19", + "derive_more 0.99.20", "hex-literal", "itoa", "ruint", @@ -157,14 +190,41 @@ dependencies = [ "const-hex", "derive_more 2.0.1", "foldhash", - "hashbrown 0.15.2", - "indexmap 2.7.1", + "hashbrown 0.15.3", + "indexmap 2.9.0", "itoa", "k256", "keccak-asm", "paste", "proptest", - "rand", + "rand 0.8.5", + "ruint", + "rustc-hash 2.1.1", + "serde", + "sha3", + "tiny-keccak", +] + +[[package]] +name = "alloy-primitives" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a12fe11d0b8118e551c29e1a67ccb6d01cc07ef08086df30f07487146de6fa1" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more 2.0.1", + "foldhash", + "hashbrown 0.15.3", + "indexmap 2.9.0", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.9.1", "ruint", "rustc-hash 2.1.1", "serde", @@ -191,7 +251,18 @@ checksum = "a40e1ef334153322fd878d07e86af7a529bcb86b2439525920a88eba87bcf943" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "alloy-serde" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4dba6ff08916bc0a9cbba121ce21f67c0b554c39cf174bc7b9df6c651bd3c3b" +dependencies = [ + "alloy-primitives 1.1.0", + "serde", + "serde_json", ] [[package]] @@ -205,7 +276,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -218,11 +289,11 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.7.1", + "indexmap 2.9.0", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "syn-solidity", "tiny-keccak", ] @@ -241,7 +312,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.98", + "syn 2.0.101", "syn-solidity", ] @@ -252,7 +323,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d162f8524adfdfb0e4bd0505c734c985f3e2474eb022af32eef0d52a4f3935c" dependencies = [ "serde", - "winnow 0.7.3", + "winnow 0.7.9", ] [[package]] @@ -350,9 +421,18 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.96" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] [[package]] name = "ariadne" @@ -364,6 +444,51 @@ dependencies = [ "yansi 0.5.1", ] +[[package]] +name = "ark-bls12-381" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df4dcc01ff89867cd86b0da835f23c3f02738353aaee7dde7495af71363b8d5" +dependencies = [ + "ark-ec", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" +dependencies = [ + "ark-ec", + "ark-ff 0.5.0", + "ark-r1cs-std", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash", + "ark-ff 0.5.0", + "ark-poly", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.3", + "itertools 0.13.0", + "num-bigint 0.4.6", + "num-integer", + "num-traits", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.3.0" @@ -402,6 +527,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "educe", + "itertools 0.13.0", + "num-bigint 0.4.6", + "num-traits", + "paste", + "zeroize", +] + [[package]] name = "ark-ff-asm" version = "0.3.0" @@ -422,6 +567,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn 2.0.101", +] + [[package]] name = "ark-ff-macros" version = "0.3.0" @@ -447,6 +602,63 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "ark-poly" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" +dependencies = [ + "ahash", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.3", +] + +[[package]] +name = "ark-r1cs-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941551ef1df4c7a401de7068758db6503598e6f01850bdb2cfdb614a1f9dbea1" +dependencies = [ + "ark-ec", + "ark-ff 0.5.0", + "ark-relations", + "ark-std 0.5.0", + "educe", + "num-bigint 0.4.6", + "num-integer", + "num-traits", + "tracing", +] + +[[package]] +name = "ark-relations" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec46ddc93e7af44bcab5230937635b06fb5744464dd6a7e7b083e80ebd274384" +dependencies = [ + "ark-ff 0.5.0", + "ark-std 0.5.0", + "tracing", + "tracing-subscriber 0.2.25", +] + [[package]] name = "ark-serialize" version = "0.3.0" @@ -468,6 +680,30 @@ dependencies = [ "num-bigint 0.4.6", ] +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-serialize-derive", + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "num-bigint 0.4.6", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "ark-std" version = "0.3.0" @@ -475,7 +711,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" dependencies = [ "num-traits", - "rand", + "rand 0.8.5", ] [[package]] @@ -485,7 +721,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand 0.8.5", ] [[package]] @@ -511,13 +757,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.86" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -529,6 +775,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "aurora-engine-modexp" version = "1.2.0" @@ -541,13 +793,13 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12882f59de5360c748c4cbf569a042d5fb0eb515f7bea9c1f470b47f6ffbd73" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -558,9 +810,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-config" -version = "1.5.18" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90aff65e86db5fe300752551c1b015ef72b708ac54bded8ef43d0d53cb7cb0b1" +checksum = "b6fcc63c9860579e4cb396239570e979376e70aab79e496621748a09913f8b36" dependencies = [ "aws-credential-types", "aws-runtime", @@ -568,7 +820,7 @@ dependencies = [ "aws-sdk-ssooidc", "aws-sdk-sts", "aws-smithy-async", - "aws-smithy-http 0.61.1", + "aws-smithy-http", "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", @@ -577,7 +829,7 @@ dependencies = [ "bytes", "fastrand", "hex", - "http 0.2.12", + "http 1.3.1", "ring", "time", "tokio", @@ -588,9 +840,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" +checksum = "687bc16bc431a8533fe0097c7f0182874767f920989d7260950172ae8e3c4465" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -598,17 +850,40 @@ dependencies = [ "zeroize", ] +[[package]] +name = "aws-lc-rs" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b756939cb2f8dc900aa6dcd505e6e2428e9cae7ff7b028c49e3946efa70878" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa9b6986f250236c27e5a204062434a773a13243d2ffc2955f37bdba4c5c6a1" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "aws-runtime" -version = "1.5.5" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76dd04d39cc12844c0994f2c9c5a6f5184c22e9188ec1ff723de41910a21dcad" +checksum = "6c4063282c69991e57faab9e5cb21ae557e59f5b0fb285c196335243df8dc25c" dependencies = [ "aws-credential-types", "aws-sigv4", "aws-smithy-async", "aws-smithy-eventstream", - "aws-smithy-http 0.60.12", + "aws-smithy-http", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -617,7 +892,6 @@ dependencies = [ "fastrand", "http 0.2.12", "http-body 0.4.6", - "once_cell", "percent-encoding", "pin-project-lite", "tracing", @@ -626,9 +900,9 @@ dependencies = [ [[package]] name = "aws-sdk-s3" -version = "1.78.0" +version = "1.83.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038614b6cf7dd68d9a7b5b39563d04337eb3678d1d4173e356e927b0356158a" +checksum = "51384750334005f40e1a334b0d54eca822a77eacdcf3c50fdf38f583c5eee7a2" dependencies = [ "aws-credential-types", "aws-runtime", @@ -636,7 +910,7 @@ dependencies = [ "aws-smithy-async", "aws-smithy-checksums", "aws-smithy-eventstream", - "aws-smithy-http 0.61.1", + "aws-smithy-http", "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", @@ -648,32 +922,34 @@ dependencies = [ "hex", "hmac", "http 0.2.12", + "http 1.3.1", "http-body 0.4.6", "lru", "once_cell", "percent-encoding", "regex-lite", - "sha2", + "sha2 0.10.9", "tracing", "url", ] [[package]] name = "aws-sdk-sso" -version = "1.61.0" +version = "1.65.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e65ff295979977039a25f5a0bf067a64bc5e6aa38f3cef4037cf42516265553c" +checksum = "8efec445fb78df585327094fcef4cad895b154b58711e504db7a93c41aa27151" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", - "aws-smithy-http 0.61.1", + "aws-smithy-http", "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", "aws-types", "bytes", + "fastrand", "http 0.2.12", "once_cell", "regex-lite", @@ -682,20 +958,21 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.62.0" +version = "1.66.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91430a60f754f235688387b75ee798ef00cfd09709a582be2b7525ebb5306d4f" +checksum = "5e49cca619c10e7b002dc8e66928ceed66ab7f56c1a3be86c5437bf2d8d89bba" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", - "aws-smithy-http 0.61.1", + "aws-smithy-http", "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", "aws-types", "bytes", + "fastrand", "http 0.2.12", "once_cell", "regex-lite", @@ -704,14 +981,14 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.62.0" +version = "1.66.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9276e139d39fff5a0b0c984fc2d30f970f9a202da67234f948fda02e5bea1dbe" +checksum = "7420479eac0a53f776cc8f0d493841ffe58ad9d9783f3947be7265784471b47a" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", - "aws-smithy-http 0.61.1", + "aws-smithy-http", "aws-smithy-json", "aws-smithy-query", "aws-smithy-runtime", @@ -719,6 +996,7 @@ dependencies = [ "aws-smithy-types", "aws-smithy-xml", "aws-types", + "fastrand", "http 0.2.12", "once_cell", "regex-lite", @@ -727,13 +1005,13 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.2.9" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bfe75fad52793ce6dec0dc3d4b1f388f038b5eb866c8d4d7f3a8e21b5ea5051" +checksum = "3503af839bd8751d0bdc5a46b9cac93a003a353e635b0c12cf2376b5b53e41ea" dependencies = [ "aws-credential-types", "aws-smithy-eventstream", - "aws-smithy-http 0.60.12", + "aws-smithy-http", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", @@ -742,12 +1020,11 @@ dependencies = [ "hex", "hmac", "http 0.2.12", - "http 1.2.0", - "once_cell", + "http 1.3.1", "p256 0.11.1", "percent-encoding", "ring", - "sha2", + "sha2 0.10.9", "subtle", "time", "tracing", @@ -756,9 +1033,9 @@ dependencies = [ [[package]] name = "aws-smithy-async" -version = "1.2.4" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa59d1327d8b5053c54bf2eaae63bf629ba9e904434d0835a28ed3c0ed0a614e" +checksum = "1e190749ea56f8c42bf15dd76c65e14f8f765233e6df9b0506d9d934ebef867c" dependencies = [ "futures-util", "pin-project-lite", @@ -767,11 +1044,11 @@ dependencies = [ [[package]] name = "aws-smithy-checksums" -version = "0.63.0" +version = "0.63.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2dc8d842d872529355c72632de49ef8c5a2949a4472f10e802f28cf925770c" +checksum = "b65d21e1ba6f2cdec92044f904356a19f5ad86961acf015741106cdfafd747c0" dependencies = [ - "aws-smithy-http 0.60.12", + "aws-smithy-http", "aws-smithy-types", "bytes", "crc32c", @@ -783,15 +1060,15 @@ dependencies = [ "md-5", "pin-project-lite", "sha1", - "sha2", + "sha2 0.10.9", "tracing", ] [[package]] name = "aws-smithy-eventstream" -version = "0.60.7" +version = "0.60.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "461e5e02f9864cba17cff30f007c2e37ade94d01e87cdb5204e44a84e6d38c17" +checksum = "7c45d3dddac16c5c59d553ece225a88870cf81b7b813c9cc17b78cf4685eac7a" dependencies = [ "aws-smithy-types", "bytes", @@ -800,18 +1077,19 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.60.12" +version = "0.62.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7809c27ad8da6a6a68c454e651d4962479e81472aa19ae99e59f9aba1f9713cc" +checksum = "99335bec6cdc50a346fda1437f9fefe33abf8c99060739a546a16457f2862ca9" dependencies = [ + "aws-smithy-eventstream", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", "bytes-utils", "futures-core", "http 0.2.12", + "http 1.3.1", "http-body 0.4.6", - "once_cell", "percent-encoding", "pin-project-lite", "pin-utils", @@ -819,35 +1097,51 @@ dependencies = [ ] [[package]] -name = "aws-smithy-http" -version = "0.61.1" +name = "aws-smithy-http-client" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f276f21c7921fe902826618d1423ae5bf74cf8c1b8472aee8434f3dfd31824" +checksum = "8aff1159006441d02e57204bf57a1b890ba68bedb6904ffd2873c1c4c11c546b" dependencies = [ - "aws-smithy-eventstream", + "aws-smithy-async", "aws-smithy-runtime-api", "aws-smithy-types", - "bytes", - "bytes-utils", - "futures-core", + "h2 0.4.9", "http 0.2.12", + "http 1.3.1", "http-body 0.4.6", - "once_cell", - "percent-encoding", + "hyper 0.14.32", + "hyper 1.6.0", + "hyper-rustls 0.24.2", + "hyper-rustls 0.27.5", + "hyper-util", "pin-project-lite", - "pin-utils", + "rustls 0.21.12", + "rustls 0.23.26", + "rustls-native-certs 0.8.1", + "rustls-pki-types", + "tokio", + "tower", "tracing", ] [[package]] name = "aws-smithy-json" -version = "0.61.2" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "623a51127f24c30776c8b374295f2df78d92517386f77ba30773f15a30ce1422" +checksum = "92144e45819cae7dc62af23eac5a038a58aa544432d2102609654376a900bd07" dependencies = [ "aws-smithy-types", ] +[[package]] +name = "aws-smithy-observability" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9364d5989ac4dd918e5cc4c4bdcc61c9be17dcd2586ea7f69e348fc7c6cab393" +dependencies = [ + "aws-smithy-runtime-api", +] + [[package]] name = "aws-smithy-query" version = "0.60.7" @@ -860,42 +1154,39 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.7.8" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d526a12d9ed61fadefda24abe2e682892ba288c2018bcb38b1b4c111d13f6d92" +checksum = "14302f06d1d5b7d333fd819943075b13d27c7700b414f574c3c35859bfb55d5e" dependencies = [ "aws-smithy-async", - "aws-smithy-http 0.60.12", + "aws-smithy-http", + "aws-smithy-http-client", + "aws-smithy-observability", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", "fastrand", - "h2", "http 0.2.12", + "http 1.3.1", "http-body 0.4.6", "http-body 1.0.1", - "httparse", - "hyper", - "hyper-rustls", - "once_cell", "pin-project-lite", "pin-utils", - "rustls", "tokio", "tracing", ] [[package]] name = "aws-smithy-runtime-api" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92165296a47a812b267b4f41032ff8069ab7ff783696d217f0994a0d7ab585cd" +checksum = "a1e5d9e3a80a18afa109391fb5ad09c3daf887b516c6fd805a157c6ea7994a57" dependencies = [ "aws-smithy-async", "aws-smithy-types", "bytes", "http 0.2.12", - "http 1.2.0", + "http 1.3.1", "pin-project-lite", "tokio", "tracing", @@ -904,16 +1195,16 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.13" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7b8a53819e42f10d0821f56da995e1470b199686a1809168db6ca485665f042" +checksum = "40076bd09fadbc12d5e026ae080d0930defa606856186e31d83ccc6a255eeaf3" dependencies = [ "base64-simd", "bytes", "bytes-utils", "futures-core", "http 0.2.12", - "http 1.2.0", + "http 1.3.1", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -939,9 +1230,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.3.5" +version = "1.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbd0a668309ec1f66c0f6bda4840dd6d4796ae26d699ebc266d7cc95c6d040f" +checksum = "8a322fec39e4df22777ed3ad8ea868ac2f94cd15e1a55f6ee8d8d6305057689a" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -1009,9 +1300,9 @@ dependencies = [ [[package]] name = "base64ct" -version = "1.6.0" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" [[package]] name = "bincode" @@ -1022,13 +1313,45 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags 2.9.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.101", + "which", +] + [[package]] name = "bit-set" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ - "bit-vec", + "bit-vec 0.6.3", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec 0.8.0", ] [[package]] @@ -1037,11 +1360,17 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitcode" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18c1406a27371b2f76232a2259df6ab607b91b5a0a7476a7729ff590df5a969a" +checksum = "cf300f4aa6e66f3bdff11f1236a88c622fe47ea814524792240b4d554d9858ee" dependencies = [ "arrayvec", "bitcode_derive", @@ -1058,7 +1387,23 @@ checksum = "42b6b4cb608b8282dc3b53d0f4c9ab404655d562674c682db7e6c0458cc83c23" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "bitcoin-io" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" + +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative", ] [[package]] @@ -1069,9 +1414,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "bitvec" @@ -1107,16 +1452,24 @@ dependencies = [ [[package]] name = "blake3" -version = "1.6.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1230237285e3e10cde447185e8975408ae24deaa67205ce684805c25bc0c7937" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq 0.3.1", - "memmap2", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", ] [[package]] @@ -1137,7 +1490,7 @@ dependencies = [ "ff 0.12.1", "group 0.12.1", "pairing 0.22.0", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -1155,9 +1508,9 @@ dependencies = [ [[package]] name = "bon" -version = "3.3.2" +version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7acc34ff59877422326db7d6f2d845a582b16396b6b08194942bf34c6528ab" +checksum = "ced38439e7a86a4761f7f7d5ded5ff009135939ecb464a24452eaa4c1696af7d" dependencies = [ "bon-macros", "rustversion", @@ -1165,9 +1518,9 @@ dependencies = [ [[package]] name = "bon-macros" -version = "3.3.2" +version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4159dd617a7fbc9be6a692fe69dc2954f8e6bb6bb5e4d7578467441390d77fd0" +checksum = "0ce61d2d3844c6b8d31b2353d9f66cf5e632b3e9549583fe3cac2f4f6136725e" dependencies = [ "darling", "ident_case", @@ -1175,7 +1528,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -1202,15 +1555,15 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byte-slice-cast" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "bytemuck" -version = "1.21.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" [[package]] name = "byteorder" @@ -1220,9 +1573,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" dependencies = [ "serde", ] @@ -1259,10 +1612,11 @@ dependencies = [ [[package]] name = "c-kzg" -version = "1.0.3" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0307f72feab3300336fb803a57134159f6e20139af1357f36c54cb90d8e8928" +checksum = "4e7e3c397401eb76228c89561cf22f85f41c95aa799ee9d860de3ea1cbc728fc" dependencies = [ + "arbitrary", "blst", "cc", "glob", @@ -1302,7 +1656,7 @@ dependencies = [ "target-lexicon", "tempfile", "tokio", - "toml 0.8.20", + "toml 0.8.22", "tracing", "vergen", ] @@ -1324,7 +1678,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver 1.0.25", + "semver 1.0.26", "serde", "serde_json", "thiserror 1.0.69", @@ -1338,15 +1692,24 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.14" +version = "1.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" +checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" dependencies = [ "jobserver", "libc", "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -1355,15 +1718,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.39" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -1403,11 +1766,22 @@ dependencies = [ "inout", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" -version = "4.5.30" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" dependencies = [ "clap_builder", "clap_derive", @@ -1415,9 +1789,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.30" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" dependencies = [ "anstream", "anstyle", @@ -1428,14 +1802,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.28" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -1592,6 +1966,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -1706,9 +2090,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -1760,7 +2144,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -1772,7 +2156,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -1789,9 +2173,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", @@ -1799,27 +2183,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -1834,9 +2218,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", "zeroize", @@ -1844,9 +2228,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", "serde", @@ -1871,7 +2255,7 @@ checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -1882,20 +2266,42 @@ checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "derive-where" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e73f2692d4bd3cac41dca28934a39894200c9fabf49586d77d0e5954af1d7902" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] name = "derive_more" -version = "0.99.19" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -1924,7 +2330,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "unicode-xid", ] @@ -1936,7 +2342,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "unicode-xid", ] @@ -1955,7 +2361,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "const-oid", "crypto-common", "subtle", @@ -2011,7 +2417,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -2022,7 +2428,7 @@ checksum = "8dc51d98e636f5e3b0759a39257458b22619cac7e96d932da6eeb052891bb67c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -2037,12 +2443,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" -[[package]] -name = "dyn-clone" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feeef44e73baff3a26d371801df019877a9866a8c493d315ab00177843314f35" - [[package]] name = "ecdsa" version = "0.14.8" @@ -2061,7 +2461,7 @@ version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der 0.7.9", + "der 0.7.10", "digest 0.10.7", "elliptic-curve 0.13.8", "rfc6979 0.4.0", @@ -2069,11 +2469,23 @@ dependencies = [ "spki 0.7.3", ] +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elf" @@ -2095,7 +2507,7 @@ dependencies = [ "generic-array", "group 0.12.1", "pkcs8 0.9.0", - "rand_core", + "rand_core 0.6.4", "sec1 0.3.0", "subtle", "zeroize", @@ -2110,11 +2522,11 @@ dependencies = [ "base16ct 0.2.0", "crypto-bigint 0.5.5", "digest 0.10.7", - "ff 0.13.0", + "ff 0.13.1", "generic-array", "group 0.13.0", "pkcs8 0.10.2", - "rand_core", + "rand_core 0.6.4", "sec1 0.7.3", "subtle", "zeroize", @@ -2156,6 +2568,26 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "enum_dispatch" version = "0.3.13" @@ -2165,7 +2597,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -2176,7 +2608,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -2190,9 +2622,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", @@ -2208,9 +2640,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -2280,7 +2712,7 @@ dependencies = [ "k256", "num_enum", "open-fastrlp", - "rand", + "rand 0.8.5", "rlp", "serde", "serde_json", @@ -2300,7 +2732,7 @@ dependencies = [ "chrono", "ethers-core", "reqwest", - "semver 1.0.25", + "semver 1.0.26", "serde", "serde_json", "thiserror 1.0.69", @@ -2327,10 +2759,10 @@ dependencies = [ "path-slash", "rayon", "regex", - "semver 1.0.25", + "semver 1.0.26", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "solang-parser", "svm-rs", "svm-rs-builds", @@ -2387,31 +2819,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" dependencies = [ "bitvec", - "rand_core", + "rand_core 0.6.4", "subtle", ] [[package]] name = "ff" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ "bitvec", "byteorder", "ff_derive", - "rand_core", + "rand_core 0.6.4", "subtle", ] [[package]] name = "ff_derive" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f54704be45ed286151c5e11531316eaef5b8f5af7d597b806fdb8af108d84a" +checksum = "f10d12652036b0e99197587c6ba87a8fc3031986499973c030d8b44fcc151b60" dependencies = [ "addchain", - "cfg-if", "num-bigint 0.3.3", "num-integer", "num-traits", @@ -2429,7 +2860,7 @@ dependencies = [ "atomic", "pear", "serde", - "toml 0.8.20", + "toml 0.8.22", "uncased", "version_check", ] @@ -2441,7 +2872,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand", + "rand 0.8.5", "rustc-hex", "static_assertions", ] @@ -2454,9 +2885,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", @@ -2470,9 +2901,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "forge-fmt" @@ -2519,7 +2950,7 @@ dependencies = [ "regex", "reqwest", "revm-primitives 1.3.0", - "semver 1.0.25", + "semver 1.0.26", "serde", "serde_json", "serde_regex", @@ -2540,6 +2971,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "funty" version = "2.0.0" @@ -2575,7 +3012,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -2625,9 +3062,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", @@ -2636,26 +3073,26 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets 0.52.6", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] name = "getset" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded738faa0e88d3abc9d1a13cb11adc2073c400969eeb8793cf7132589959fc" +checksum = "f3586f256131df87204eb733da72e3d3eb4f343c639f4b7be279ac7c48baeafe" dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -2670,7 +3107,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "libc", "libgit2-sys", "log", @@ -2679,9 +3116,9 @@ dependencies = [ [[package]] name = "glam" -version = "0.30.0" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17fcdf9683c406c2fc4d124afd29c0d595e22210d633cbdb8695ba9935ab1dc6" +checksum = "6b46b9ca4690308844c644e7c634d68792467260e051c8543e0c7871662b3ba7" [[package]] name = "glob" @@ -2710,7 +3147,7 @@ checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ "ff 0.12.1", "memuse", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -2720,8 +3157,8 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff 0.13.0", - "rand_core", + "ff 0.13.1", + "rand_core 0.6.4", "subtle", ] @@ -2737,7 +3174,26 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.7.1", + "indexmap 2.9.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.3.1", + "indexmap 2.9.0", "slab", "tokio", "tokio-util", @@ -2746,9 +3202,9 @@ dependencies = [ [[package]] name = "half" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", @@ -2771,14 +3227,14 @@ checksum = "62f0ca78d12ac5c893f286d7cdfe3869290305ab8cac376e2592cdc8396da102" dependencies = [ "blake2b_simd", "crossbeam", - "ff 0.13.0", + "ff 0.13.1", "group 0.13.0", "halo2curves-axiom", "itertools 0.11.0", "maybe-rayon", "pairing 0.23.0", - "rand", - "rand_core", + "rand 0.8.5", + "rand_core 0.6.4", "rayon", "rustc-hash 1.1.0", "sha3", @@ -2799,7 +3255,7 @@ dependencies = [ "num-integer", "num-traits", "poseidon-primitives", - "rand_chacha", + "rand_chacha 0.3.1", "rayon", "rustc-hash 1.1.0", "serde", @@ -2817,9 +3273,9 @@ dependencies = [ "num-bigint 0.4.6", "num-integer", "num-traits", - "rand", - "rand_chacha", - "rand_core", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_core 0.6.4", "rayon", "serde", "serde_json", @@ -2836,7 +3292,7 @@ dependencies = [ "ff 0.12.1", "group 0.12.1", "pasta_curves 0.4.1", - "rand_core", + "rand_core 0.6.4", "rayon", ] @@ -2848,7 +3304,7 @@ checksum = "b756596082144af6e57105a20403b7b80fe9dccd085700b74fae3af523b74dba" dependencies = [ "blake2", "digest 0.10.7", - "ff 0.13.0", + "ff 0.13.1", "group 0.13.0", "halo2derive", "hex", @@ -2858,12 +3314,12 @@ dependencies = [ "num-traits", "pairing 0.23.0", "paste", - "rand", - "rand_core", + "rand 0.8.5", + "rand_core 0.6.4", "rayon", "serde", "serde_arrays", - "sha2", + "sha2 0.10.9", "static_assertions", "subtle", "unroll", @@ -2877,7 +3333,7 @@ checksum = "dd8309e4638b4f1bcf6613d72265a84074d26034c35edc5d605b5688e580b8b8" dependencies = [ "blake2b_simd", "digest 0.10.7", - "ff 0.13.0", + "ff 0.13.1", "group 0.13.0", "hex", "lazy_static", @@ -2886,12 +3342,12 @@ dependencies = [ "pairing 0.23.0", "pasta_curves 0.5.1", "paste", - "rand", - "rand_core", + "rand 0.8.5", + "rand_core 0.6.4", "rayon", "serde", "serde_arrays", - "sha2", + "sha2 0.10.9", "static_assertions", "subtle", "unroll", @@ -2929,9 +3385,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ "allocator-api2", "equivalent", @@ -2953,9 +3409,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hermit-abi" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" [[package]] name = "hex" @@ -2966,6 +3422,15 @@ dependencies = [ "serde", ] +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex-literal" version = "0.4.1" @@ -3003,9 +3468,9 @@ dependencies = [ [[package]] name = "http" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -3030,27 +3495,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.2.0", + "http 1.3.1", ] [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", - "http 1.2.0", + "futures-core", + "http 1.3.1", "http-body 1.0.1", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -3068,17 +3533,37 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.9", + "http 1.3.1", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", "tokio", - "tower-service", - "tracing", "want", ] @@ -3090,24 +3575,63 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper", + "hyper 0.14.32", "log", - "rustls", - "rustls-native-certs", + "rustls 0.21.12", + "rustls-native-certs 0.6.3", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http 1.3.1", + "hyper 1.6.0", + "hyper-util", + "rustls 0.23.26", + "rustls-native-certs 0.8.1", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.2", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "hyper 1.6.0", + "libc", + "pin-project-lite", + "socket2", "tokio", - "tokio-rustls", + "tower-service", + "tracing", ] [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -3162,9 +3686,9 @@ dependencies = [ [[package]] name = "icu_locid_transform_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" @@ -3186,9 +3710,9 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" @@ -3207,9 +3731,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" @@ -3236,7 +3760,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -3301,7 +3825,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -3323,12 +3847,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.3", "serde", ] @@ -3355,11 +3879,11 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "is-terminal" -version = "0.4.15" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ - "hermit-abi 0.4.0", + "hermit-abi 0.5.0", "libc", "windows-sys 0.59.0", ] @@ -3388,6 +3912,24 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -3399,16 +3941,17 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.2", "libc", ] @@ -3432,7 +3975,7 @@ dependencies = [ "bls12_381", "ff 0.12.1", "group 0.12.1", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -3446,7 +3989,7 @@ dependencies = [ "ecdsa 0.16.9", "elliptic-curve 0.13.8", "once_cell", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -3475,7 +4018,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" dependencies = [ "ascii-canvas", - "bit-set", + "bit-set 0.5.3", "ena", "itertools 0.11.0", "lalrpop-util", @@ -3507,11 +4050,17 @@ dependencies = [ "spin", ] +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" -version = "0.2.169" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libgit2-sys" @@ -3525,17 +4074,27 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + [[package]] name = "libm" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72" [[package]] name = "libmimalloc-sys" -version = "0.1.39" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44" +checksum = "ec9d6fac27761dabcd4ee73571cdb06b7022dc99089acbe5435691edffaac0f4" dependencies = [ "cc", "libc", @@ -3547,15 +4106,61 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "libc", ] +[[package]] +name = "libsecp256k1" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79019718125edc905a079a70cfa5f3820bc76139fc91d6f9abc27ea2a887139" +dependencies = [ + "arrayref", + "base64 0.22.1", + "digest 0.9.0", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde", + "sha2 0.9.9", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + [[package]] name = "libz-sys" -version = "1.1.21" +version = "1.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9b68e50e6e0b26f672573834882eb57759f6db9b3be2ea3c35c91188bb4eaa" +checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" dependencies = [ "cc", "libc", @@ -3583,9 +4188,9 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "lock_api" @@ -3605,9 +4210,9 @@ checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" [[package]] name = "log" -version = "0.4.25" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "lru" @@ -3615,7 +4220,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.3", ] [[package]] @@ -3626,7 +4231,7 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -3681,9 +4286,9 @@ checksum = "3d97bbf43eb4f088f8ca469930cde17fa036207c9a5e02ccc5107c4e8b17c964" [[package]] name = "metrics" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884adb57038347dfbaf2d5065887b6cf4312330dc8e94bc30a1a839bd79d3261" +checksum = "3045b4193fbdc5b5681f32f11070da9be3609f189a79f3390706d42587f46bb5" dependencies = [ "ahash", "portable-atomic", @@ -3695,7 +4300,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62a6a1f7141f1d9bc7a886b87536bbfc97752e08b369e1e0453a9acfab5f5da4" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "itoa", "lockfree-object-pool", "metrics", @@ -3703,7 +4308,7 @@ dependencies = [ "once_cell", "tracing", "tracing-core", - "tracing-subscriber", + "tracing-subscriber 0.3.19", ] [[package]] @@ -3716,7 +4321,7 @@ dependencies = [ "crossbeam-epoch", "crossbeam-utils", "hashbrown 0.14.5", - "indexmap 2.7.1", + "indexmap 2.9.0", "metrics", "num_cpus", "ordered-float", @@ -3727,9 +4332,9 @@ dependencies = [ [[package]] name = "mimalloc" -version = "0.1.43" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633" +checksum = "995942f432bbb4822a7e9c3faa87a695185b0d09273ba85f097b54f4e458f2af" dependencies = [ "libmimalloc-sys", ] @@ -3740,11 +4345,17 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" -version = "0.8.4" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] @@ -3775,6 +4386,16 @@ dependencies = [ "smallvec", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -3818,7 +4439,7 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", - "rand", + "rand 0.8.5", "serde", ] @@ -3916,7 +4537,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -3943,7 +4564,7 @@ dependencies = [ "num-bigint 0.4.6", "num-integer", "num-traits", - "rand", + "rand 0.8.5", ] [[package]] @@ -3957,15 +4578,21 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.3" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "oorandom" -version = "11.1.4" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "opaque-debug" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "open-fastrlp" @@ -4033,7 +4660,7 @@ dependencies = [ "openvm-rv32im-circuit", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "serde", "serde-big-array", "serde_with", @@ -4046,7 +4673,7 @@ version = "0.1.0" dependencies = [ "openvm-macros-common", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4067,7 +4694,7 @@ version = "1.1.1-rc.0" dependencies = [ "openvm-macros-common", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4116,7 +4743,7 @@ dependencies = [ "openvm-stark-sdk", "openvm-transpiler", "tracing", - "tracing-subscriber", + "tracing-subscriber 0.3.19", ] [[package]] @@ -4148,7 +4775,7 @@ dependencies = [ "openvm-stark-backend", "openvm-stark-sdk", "openvm-transpiler", - "rand_chacha", + "rand_chacha 0.3.1", "serde", "tiny-keccak", "tokio", @@ -4166,7 +4793,7 @@ dependencies = [ "openvm-transpiler", "tempfile", "tracing", - "tracing-subscriber", + "tracing-subscriber 0.3.19", ] [[package]] @@ -4186,7 +4813,7 @@ dependencies = [ "openvm-rv32im-transpiler", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "serde", "test-case", ] @@ -4270,7 +4897,7 @@ dependencies = [ "openvm-stark-backend", "openvm-stark-sdk", "p3-baby-bear", - "rand", + "rand 0.8.5", "rustc-hash 2.1.1", "serde", "serde-big-array", @@ -4286,7 +4913,7 @@ version = "1.1.1-rc.0" dependencies = [ "itertools 0.14.0", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4300,7 +4927,7 @@ dependencies = [ "openvm-circuit-primitives-derive", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "test-case", "tracing", ] @@ -4311,7 +4938,7 @@ version = "1.1.1-rc.0" dependencies = [ "itertools 0.14.0", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4334,7 +4961,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4363,7 +4990,7 @@ dependencies = [ "openvm-rv32im-circuit", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "serde", "serde_with", "strum", @@ -4420,7 +5047,7 @@ version = "1.1.1-rc.0" dependencies = [ "openvm-macros-common", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4460,7 +5087,7 @@ dependencies = [ "quote", "strum", "strum_macros", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4481,7 +5108,7 @@ dependencies = [ "openvm-stark-backend", "openvm-stark-sdk", "p3-keccak-air", - "rand", + "rand 0.8.5", "serde", "serde-big-array", "strum", @@ -4529,7 +5156,7 @@ dependencies = [ name = "openvm-macros-common" version = "1.1.1-rc.0" dependencies = [ - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4546,7 +5173,7 @@ dependencies = [ "openvm-pairing-guest", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "serde", "serde_with", "tracing", @@ -4570,7 +5197,7 @@ dependencies = [ "openvm-rv32im-circuit", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "serde", "serde-big-array", "static_assertions", @@ -4596,7 +5223,7 @@ dependencies = [ "openvm-stark-backend", "openvm-stark-sdk", "p3-symmetric", - "rand", + "rand 0.8.5", "serde", "snark-verifier-sdk", "strum", @@ -4609,7 +5236,7 @@ name = "openvm-native-compiler-derive" version = "1.1.1-rc.0" dependencies = [ "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4633,7 +5260,7 @@ dependencies = [ "p3-fri", "p3-merkle-tree", "p3-symmetric", - "rand", + "rand 0.8.5", "serde", "serde_json", "serde_with", @@ -4677,7 +5304,7 @@ dependencies = [ "openvm-rv32im-circuit", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "serde", "strum", ] @@ -4702,7 +5329,7 @@ dependencies = [ "openvm-ecc-sw-macros", "openvm-platform", "openvm-rv32im-guest", - "rand", + "rand 0.8.5", "serde", "strum_macros", "subtle", @@ -4730,7 +5357,7 @@ dependencies = [ "openvm-stark-sdk", "openvm-toolchain-tests", "openvm-transpiler", - "rand", + "rand 0.8.5", ] [[package]] @@ -4752,7 +5379,7 @@ version = "1.1.1-rc.0" dependencies = [ "critical-section", "embedded-alloc", - "getrandom 0.2.15", + "getrandom 0.2.16", "libm", "openvm-custom-insn", "openvm-rv32im-guest", @@ -4770,7 +5397,7 @@ dependencies = [ "p3-poseidon2", "p3-poseidon2-air", "p3-symmetric", - "rand", + "rand 0.8.5", "zkhash", ] @@ -4800,7 +5427,7 @@ dependencies = [ "openvm-rv32im-circuit", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "serde", "serde-big-array", "serde_with", @@ -4823,7 +5450,7 @@ dependencies = [ "openvm-rv32im-transpiler", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "serde", "serde-big-array", "strum", @@ -4930,8 +5557,8 @@ dependencies = [ "openvm-circuit-primitives", "openvm-stark-backend", "openvm-stark-sdk", - "rand", - "sha2", + "rand 0.8.5", + "sha2 0.10.9", ] [[package]] @@ -4950,9 +5577,9 @@ dependencies = [ "openvm-sha256-transpiler", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "serde", - "sha2", + "sha2 0.10.9", "strum", ] @@ -4961,7 +5588,7 @@ name = "openvm-sha256-guest" version = "1.1.1-rc.0" dependencies = [ "openvm-platform", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -5026,8 +5653,8 @@ version = "1.0.0" source = "git+https://github.com/openvm-org/stark-backend.git?tag=v1.0.0#884f8e6aabf72bde00dc51f1f1121277bff73b1e" dependencies = [ "derivative", - "derive_more 0.99.19", - "ff 0.13.0", + "derive_more 0.99.20", + "ff 0.13.1", "itertools 0.14.0", "metrics", "metrics-tracing-context", @@ -5044,14 +5671,14 @@ dependencies = [ "p3-poseidon", "p3-poseidon2", "p3-symmetric", - "rand", + "rand 0.8.5", "serde", "serde_json", "static_assertions", - "toml 0.8.20", + "toml 0.8.22", "tracing", "tracing-forest", - "tracing-subscriber", + "tracing-subscriber 0.3.19", "zkhash", ] @@ -5129,7 +5756,7 @@ checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ "ecdsa 0.14.8", "elliptic-curve 0.12.3", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -5138,7 +5765,10 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ + "ecdsa 0.16.9", "elliptic-curve 0.13.8", + "primeorder", + "sha2 0.10.9", ] [[package]] @@ -5160,7 +5790,7 @@ dependencies = [ "p3-monty-31", "p3-poseidon2", "p3-symmetric", - "rand", + "rand 0.8.5", "serde", ] @@ -5179,13 +5809,13 @@ name = "p3-bn254-fr" version = "0.1.0" source = "git+https://github.com/Plonky3/Plonky3.git?rev=1ba4e5c#1ba4e5c40417f4f7aae86bcca56b6484b4b2490b" dependencies = [ - "ff 0.13.0", + "ff 0.13.1", "halo2curves", "num-bigint 0.4.6", "p3-field", "p3-poseidon2", "p3-symmetric", - "rand", + "rand 0.8.5", "serde", ] @@ -5240,7 +5870,7 @@ dependencies = [ "nums", "p3-maybe-rayon", "p3-util", - "rand", + "rand 0.8.5", "serde", "tracing", ] @@ -5259,7 +5889,7 @@ dependencies = [ "p3-matrix", "p3-maybe-rayon", "p3-util", - "rand", + "rand 0.8.5", "serde", "tracing", ] @@ -5277,7 +5907,7 @@ dependencies = [ "p3-poseidon2", "p3-symmetric", "p3-util", - "rand", + "rand 0.8.5", "serde", ] @@ -5314,7 +5944,7 @@ dependencies = [ "p3-matrix", "p3-maybe-rayon", "p3-util", - "rand", + "rand 0.8.5", "tracing", ] @@ -5327,7 +5957,7 @@ dependencies = [ "p3-field", "p3-maybe-rayon", "p3-util", - "rand", + "rand 0.8.5", "serde", "tracing", "transpose", @@ -5352,7 +5982,7 @@ dependencies = [ "p3-matrix", "p3-symmetric", "p3-util", - "rand", + "rand 0.8.5", ] [[package]] @@ -5367,7 +5997,7 @@ dependencies = [ "p3-maybe-rayon", "p3-symmetric", "p3-util", - "rand", + "rand 0.8.5", "serde", "tracing", ] @@ -5387,7 +6017,7 @@ dependencies = [ "p3-poseidon2", "p3-symmetric", "p3-util", - "rand", + "rand 0.8.5", "serde", "tracing", "transpose", @@ -5401,7 +6031,7 @@ dependencies = [ "p3-field", "p3-mds", "p3-symmetric", - "rand", + "rand 0.8.5", ] [[package]] @@ -5413,7 +6043,7 @@ dependencies = [ "p3-field", "p3-mds", "p3-symmetric", - "rand", + "rand 0.8.5", ] [[package]] @@ -5427,7 +6057,7 @@ dependencies = [ "p3-maybe-rayon", "p3-poseidon2", "p3-util", - "rand", + "rand 0.8.5", "tikv-jemallocator", "tracing", ] @@ -5511,7 +6141,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -5544,7 +6174,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" dependencies = [ "base64ct", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -5558,7 +6188,7 @@ dependencies = [ "ff 0.12.1", "group 0.12.1", "lazy_static", - "rand", + "rand 0.8.5", "static_assertions", "subtle", ] @@ -5570,10 +6200,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095" dependencies = [ "blake2b_simd", - "ff 0.13.0", + "ff 0.13.1", "group 0.13.0", "lazy_static", - "rand", + "rand 0.8.5", "static_assertions", "subtle", ] @@ -5599,7 +6229,7 @@ dependencies = [ "digest 0.10.7", "hmac", "password-hash", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -5622,7 +6252,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -5633,12 +6263,12 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.15" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" +checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" dependencies = [ "memchr", - "thiserror 2.0.11", + "thiserror 2.0.12", "ucd-trie", ] @@ -5649,7 +6279,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.7.1", + "indexmap 2.9.0", ] [[package]] @@ -5669,7 +6299,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", - "rand", + "rand 0.8.5", ] [[package]] @@ -5682,7 +6312,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -5722,15 +6352,15 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.9", + "der 0.7.10", "spki 0.7.3", ] [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plotters" @@ -5762,9 +6392,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" [[package]] name = "poseidon-primitives" @@ -5773,10 +6403,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e4aaeda7a092e21165cc5f0cbc738e72a46f31c03c3cbd87b71ceae9d2d93bc" dependencies = [ "bitvec", - "ff 0.13.0", + "ff 0.13.1", "lazy_static", "log", - "rand", + "rand 0.8.5", "rand_xorshift", "thiserror 1.0.69", ] @@ -5789,11 +6419,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy", + "zerocopy 0.8.25", ] [[package]] @@ -5804,12 +6434,21 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettyplease" -version = "0.2.29" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" +checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve 0.13.8", ] [[package]] @@ -5828,11 +6467,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "toml_edit 0.22.24", + "toml_edit 0.22.26", ] [[package]] @@ -5854,14 +6493,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -5874,24 +6513,24 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "version_check", "yansi 1.0.1", ] [[package]] name = "proptest" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ - "bit-set", - "bit-vec", - "bitflags 2.8.0", + "bit-set 0.8.0", + "bit-vec 0.8.0", + "bitflags 2.9.0", "lazy_static", "num-traits", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "rand_xorshift", "regex-syntax 0.8.5", "rusty-fork", @@ -5922,13 +6561,19 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "radium" version = "0.7.0" @@ -5952,8 +6597,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "serde", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", "serde", ] @@ -5964,7 +6620,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -5973,7 +6639,17 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.2", + "serde", ] [[package]] @@ -5982,16 +6658,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] [[package]] name = "raw-cpuid" -version = "11.4.0" +version = "11.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529468c1335c1c03919960dfefdb1b3648858c20d7ec2d0663e728e4a717efbc" +checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -6020,7 +6696,7 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -6029,7 +6705,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "libredox", "thiserror 1.0.69", ] @@ -6079,94 +6755,210 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] -name = "regex-syntax" -version = "0.8.5" +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-rustls 0.24.2", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls 0.21.12", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-rustls 0.24.1", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "revm" +version = "22.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5378e95ffe5c8377002dafeb6f7d370a55517cef7d6d6c16fc552253af3b123" +dependencies = [ + "revm-bytecode", + "revm-context", + "revm-context-interface", + "revm-database", + "revm-database-interface", + "revm-handler", + "revm-inspector", + "revm-interpreter", + "revm-precompile", + "revm-primitives 18.0.0", + "revm-state", +] + +[[package]] +name = "revm-bytecode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63e138d520c5c5bc25ecc82506e9e4e6e85a811809fc5251c594378dccabfc6" +dependencies = [ + "bitvec", + "phf", + "revm-primitives 18.0.0", + "serde", +] + +[[package]] +name = "revm-context" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9765628dfea4f3686aa8f2a72471c52801e6b38b601939ac16965f49bac66580" +dependencies = [ + "cfg-if", + "derive-where", + "revm-bytecode", + "revm-context-interface", + "revm-database-interface", + "revm-primitives 18.0.0", + "revm-state", + "serde", +] + +[[package]] +name = "revm-context-interface" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d74335aa1f14222cc4d3be1f62a029cc7dc03819cc8d080ff17b7e1d76375f" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "auto_impl", + "revm-database-interface", + "revm-primitives 18.0.0", + "revm-state", + "serde", +] + +[[package]] +name = "revm-database" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5c80c5a2fd605f2119ee32a63fb3be941fb6a81ced8cdb3397abca28317224" +dependencies = [ + "alloy-eips", + "revm-bytecode", + "revm-database-interface", + "revm-primitives 18.0.0", + "revm-state", + "serde", +] + +[[package]] +name = "revm-database-interface" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "a0e4dfbc734b1ea67b5e8f8b3c7dc4283e2210d978cdaf6c7a45e97be5ea53b3" +dependencies = [ + "auto_impl", + "revm-primitives 18.0.0", + "revm-state", + "serde", +] [[package]] -name = "reqwest" -version = "0.11.27" +name = "revm-handler" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "8676379521c7bf179c31b685c5126ce7800eab5844122aef3231b97026d41a10" dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper", - "hyper-rustls", - "ipnet", - "js-sys", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls", - "rustls-pemfile", + "auto_impl", + "revm-bytecode", + "revm-context", + "revm-context-interface", + "revm-database-interface", + "revm-interpreter", + "revm-precompile", + "revm-primitives 18.0.0", + "revm-state", "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio", - "tokio-rustls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", - "winreg", ] [[package]] -name = "revm" -version = "18.0.0" +name = "revm-inspector" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15689a3c6a8d14b647b4666f2e236ef47b5a5133cdfd423f545947986fff7013" +checksum = "bfed4ecf999a3f6ae776ae2d160478c5dca986a8c2d02168e04066b1e34c789e" dependencies = [ "auto_impl", - "cfg-if", - "dyn-clone", + "revm-context", + "revm-database-interface", + "revm-handler", "revm-interpreter", - "revm-precompile", + "revm-primitives 18.0.0", + "revm-state", "serde", "serde_json", ] [[package]] name = "revm-interpreter" -version = "14.0.0" +version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e3f11d0fed049a4a10f79820c59113a79b38aed4ebec786a79d5c667bfeb51" +checksum = "feb20260342003cfb791536e678ef5bbea1bfd1f8178b170e8885ff821985473" dependencies = [ - "revm-primitives 14.0.0", + "revm-bytecode", + "revm-context-interface", + "revm-primitives 18.0.0", "serde", ] [[package]] name = "revm-precompile" -version = "15.0.0" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e381060af24b750069a2b2d2c54bba273d84e8f5f9e8026fc9262298e26cc336" +checksum = "418e95eba68c9806c74f3e36cd5d2259170b61e90ac608b17ff8c435038ddace" dependencies = [ + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", "aurora-engine-modexp", "blst", "c-kzg", "cfg-if", "k256", + "libsecp256k1", "once_cell", - "revm-primitives 14.0.0", + "p256 0.13.2", + "revm-primitives 18.0.0", "ripemd", "secp256k1", - "sha2", - "substrate-bn", + "sha2 0.10.9", ] [[package]] @@ -6178,7 +6970,7 @@ dependencies = [ "alloy-primitives 0.4.2", "alloy-rlp", "auto_impl", - "bitflags 2.8.0", + "bitflags 2.9.0", "bitvec", "enumn", "hashbrown 0.14.5", @@ -6187,21 +6979,24 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "14.0.0" +version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3702f132bb484f4f0d0ca4f6fbde3c82cfd745041abbedd6eda67730e1868ef0" +checksum = "1fc2283ff87358ec7501956c5dd8724a6c2be959c619c4861395ae5e0054575f" dependencies = [ - "alloy-eip2930", - "alloy-eip7702", - "alloy-primitives 0.8.25", - "auto_impl", - "bitflags 2.8.0", - "bitvec", - "c-kzg", - "cfg-if", - "dyn-clone", + "alloy-primitives 1.1.0", "enumn", - "hex", + "serde", +] + +[[package]] +name = "revm-state" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09dd121f6e66d75ab111fb51b4712f129511569bc3e41e6067ae760861418bd8" +dependencies = [ + "bitflags 2.9.0", + "revm-bytecode", + "revm-primitives 18.0.0", "serde", ] @@ -6228,13 +7023,13 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -6295,9 +7090,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.12.4" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ef8fb1dd8de3870cb8400d51b4c2023854bbafd5431a3ac7e7317243e22d2f" +checksum = "78a46eb779843b2c4f21fac5773e25d6d5b7c8f0922876c91541790d2ca27eef" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", @@ -6311,7 +7106,8 @@ dependencies = [ "parity-scale-codec", "primitive-types", "proptest", - "rand", + "rand 0.8.5", + "rand 0.9.1", "rlp", "ruint-macro", "serde", @@ -6364,7 +7160,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.25", + "semver 1.0.26", ] [[package]] @@ -6373,7 +7169,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -6386,7 +7182,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys 0.9.4", @@ -6401,10 +7197,24 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.23.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.103.1", + "subtle", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.6.3" @@ -6414,7 +7224,19 @@ dependencies = [ "openssl-probe", "rustls-pemfile", "schannel", - "security-framework", + "security-framework 2.11.1", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.2.0", ] [[package]] @@ -6426,6 +7248,12 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pki-types" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -6436,11 +7264,23 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.103.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "rusty-fork" @@ -6456,9 +7296,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -6490,7 +7330,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -6539,7 +7379,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct 0.2.0", - "der 0.7.9", + "der 0.7.10", "generic-array", "pkcs8 0.10.2", "subtle", @@ -6548,11 +7388,12 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.29.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" dependencies = [ - "rand", + "bitcoin_hashes", + "rand 0.8.5", "secp256k1-sys", ] @@ -6571,8 +7412,21 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.8.0", - "core-foundation", + "bitflags 2.9.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags 2.9.0", + "core-foundation 0.10.0", "core-foundation-sys", "libc", "security-framework-sys", @@ -6599,9 +7453,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" dependencies = [ "serde", ] @@ -6617,9 +7471,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -6644,22 +7498,22 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "serde_json" -version = "1.0.139" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "itoa", "memchr", "ryu", @@ -6707,7 +7561,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.7.1", + "indexmap 2.9.0", "serde", "serde_derive", "serde_json", @@ -6724,7 +7578,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -6740,9 +7594,22 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -6786,9 +7653,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] @@ -6800,7 +7667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ "digest 0.10.7", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -6810,7 +7677,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -6836,15 +7703,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "snark-verifier" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28e4c4ed1edca41687fe2d8a09ba30badb0a5cc7fa56dd1159d62aeab7c99ace" +checksum = "4d798d8ce8e29b8820ecc1028ac44cc4fc0f0296728af6fe6a0c4db05782c0a4" dependencies = [ "halo2-base", "halo2-ecc", @@ -6855,7 +7722,7 @@ dependencies = [ "num-integer", "num-traits", "pairing 0.23.0", - "rand", + "rand 0.8.5", "revm", "ruint", "serde", @@ -6864,9 +7731,9 @@ dependencies = [ [[package]] name = "snark-verifier-sdk" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "babff70ce6292fce03f692d68569f76b8f6710dbac7be7fe5f32c915909c9065" +checksum = "a338d065044702bf751e87cf353daac63e2fc4c53a3e323cbcd98c603ee6e66c" dependencies = [ "bincode", "ethereum-types", @@ -6878,8 +7745,8 @@ dependencies = [ "num-bigint 0.4.6", "num-integer", "num-traits", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "serde", "serde_json", "snark-verifier", @@ -6887,9 +7754,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -6932,7 +7799,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der 0.7.9", + "der 0.7.10", ] [[package]] @@ -6990,20 +7857,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.98", -] - -[[package]] -name = "substrate-bn" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" -dependencies = [ - "byteorder", - "crunchy", - "lazy_static", - "rand", - "rustc-hex", + "syn 2.0.101", ] [[package]] @@ -7036,10 +7890,10 @@ dependencies = [ "hex", "once_cell", "reqwest", - "semver 1.0.25", + "semver 1.0.26", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "thiserror 1.0.69", "url", "zip", @@ -7053,7 +7907,7 @@ checksum = "aa64b5e8eecd3a8af7cfc311e29db31a268a62d5953233d3e8243ec77a71c4e3" dependencies = [ "build_const", "hex", - "semver 1.0.25", + "semver 1.0.26", "serde_json", "svm-rs", ] @@ -7071,9 +7925,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.98" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -7089,7 +7943,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -7100,13 +7954,13 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -7116,7 +7970,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -7144,15 +7998,14 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.17.1" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ - "cfg-if", "fastrand", - "getrandom 0.3.1", + "getrandom 0.3.2", "once_cell", - "rustix 0.38.44", + "rustix 1.0.7", "windows-sys 0.59.0", ] @@ -7195,7 +8048,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -7206,7 +8059,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "test-case-core", ] @@ -7218,7 +8071,7 @@ checksum = "e7f46083d221181166e5b6f6b1e5f1d499f3a76888826e6cb1d057554157cd0f" dependencies = [ "env_logger", "test-log-macros", - "tracing-subscriber", + "tracing-subscriber 0.3.19", ] [[package]] @@ -7229,7 +8082,7 @@ checksum = "888d0c3c6db53c0fdab160d2ed5e12ba745383d3e85813f2ea0f2b1475ab553f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -7243,11 +8096,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.11", + "thiserror-impl 2.0.12", ] [[package]] @@ -7258,18 +8111,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "thiserror-impl" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -7313,9 +8166,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.37" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -7330,15 +8183,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.19" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -7398,7 +8251,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -7407,15 +8260,25 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls 0.23.26", "tokio", ] [[package]] name = "tokio-util" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -7430,7 +8293,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", @@ -7439,21 +8302,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.24", + "toml_edit 0.22.26", ] [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] @@ -7464,7 +8327,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", @@ -7473,17 +8336,40 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.7.3", + "toml_write", + "winnow 0.7.9", ] +[[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -7509,7 +8395,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -7532,7 +8418,7 @@ dependencies = [ "smallvec", "thiserror 1.0.69", "tracing", - "tracing-subscriber", + "tracing-subscriber 0.3.19", ] [[package]] @@ -7546,6 +8432,15 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.19" @@ -7621,9 +8516,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-width" @@ -7690,7 +8585,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.13.2" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c1f41ffb7cf259f1ecc2876861a17e7142e63ead296f671f81f6ae85903e0d6" dependencies = [ @@ -7770,9 +8665,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -7799,7 +8694,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "wasm-bindgen-shared", ] @@ -7834,7 +8729,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -7864,6 +8759,18 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + [[package]] name = "winapi" version = "0.3.9" @@ -7897,11 +8804,61 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.52.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-targets 0.52.6", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", ] [[package]] @@ -8063,9 +9020,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.3" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" +checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" dependencies = [ "memchr", ] @@ -8082,11 +9039,11 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -8148,7 +9105,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "synstructure", ] @@ -8158,8 +9115,16 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive 0.8.25", ] [[package]] @@ -8170,27 +9135,38 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "synstructure", ] @@ -8211,7 +9187,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -8233,7 +9209,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -8275,9 +9251,9 @@ dependencies = [ "jubjub", "lazy_static", "pasta_curves 0.5.1", - "rand", + "rand 0.8.5", "serde", - "sha2", + "sha2 0.10.9", "sha3", "subtle", ] diff --git a/crates/vm/src/arch/execution_control.rs b/crates/vm/src/arch/execution_control.rs index 3fe18c6961..fd0db751e9 100644 --- a/crates/vm/src/arch/execution_control.rs +++ b/crates/vm/src/arch/execution_control.rs @@ -58,7 +58,7 @@ where fn execute_instruction( &mut self, vm_state: &mut ExecutionSegmentState, - // instruction: &Instruction, + instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> where @@ -156,14 +156,13 @@ where fn execute_instruction( &mut self, state: &mut ExecutionSegmentState, - // instruction: &Instruction, + instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> where F: PrimeField32, { let timestamp = chip_complex.memory_controller().timestamp(); - let (instruction, _) = chip_complex.base.program_chip.get_instruction(state.pc)?; let &Instruction { opcode, .. } = instruction; @@ -239,14 +238,12 @@ where fn execute_instruction( &mut self, state: &mut ExecutionSegmentState, - // instruction: &Instruction, + instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> where F: PrimeField32, { - let (instruction, _) = chip_complex.base.program_chip.get_instruction(state.pc)?; - let &Instruction { opcode, .. } = instruction; if let Some(executor) = chip_complex.inventory.get_mut_executor(&opcode) { diff --git a/crates/vm/src/arch/segment.rs b/crates/vm/src/arch/segment.rs index fd9d481988..f84ae78bc7 100644 --- a/crates/vm/src/arch/segment.rs +++ b/crates/vm/src/arch/segment.rs @@ -215,8 +215,9 @@ where *prev_backtrace = trace.cloned(); // Execute the instruction using the control implementation + // TODO(AG): maybe avoid cloning the instruction? self.control - .execute_instruction(state, &mut self.chip_complex)?; + .execute_instruction(state, &instruction.clone(), &mut self.chip_complex)?; // Update metrics if enabled #[cfg(feature = "bench-metrics")] diff --git a/crates/vm/src/arch/testing/memory/mod.rs b/crates/vm/src/arch/testing/memory/mod.rs index 6fc5caadcd..247ca3970d 100644 --- a/crates/vm/src/arch/testing/memory/mod.rs +++ b/crates/vm/src/arch/testing/memory/mod.rs @@ -5,8 +5,6 @@ use openvm_circuit::system::memory::MemoryController; use openvm_stark_backend::p3_field::PrimeField32; use rand::Rng; -use crate::system::memory::INITIAL_TIMESTAMP; - pub mod air; /// A dummy testing chip that will add unconstrained messages into the [MemoryBus]. @@ -100,25 +98,6 @@ impl MemoryTester { .unwrap() .send(addr_space as u32, ptr as u32, &data, t); } - - /// Fills in dummy memory chips to balance the memory bus with the initial and final boundary - /// messages. Taking a volatile memory approach: any touched address will be initialized with - /// zero. - pub fn finalize(&mut self) { - let memory = &self.controller.memory; - // TODO[jpw]: assuming the last block size matches initial block size, after adding - // adapters, fix this - for ((addr_space, ptr), metadata) in memory.touched_blocks() { - let block_size = metadata.block_size as usize; - let chip = self.chip_for_block.get_mut(&block_size).unwrap(); - let mut data = F::zero_vec(block_size); - chip.send(addr_space, ptr, &data, INITIAL_TIMESTAMP); - for (i, v) in data.iter_mut().enumerate() { - *v = memory.data().get_f(addr_space, ptr + i as u32); - } - chip.receive(addr_space, ptr, &data, metadata.timestamp); - } - } } pub fn gen_pointer(rng: &mut R, len: usize) -> usize diff --git a/crates/vm/src/arch/testing/mod.rs b/crates/vm/src/arch/testing/mod.rs index edd46c3df1..254c1096a0 100644 --- a/crates/vm/src/arch/testing/mod.rs +++ b/crates/vm/src/arch/testing/mod.rs @@ -1,11 +1,19 @@ +use std::{ + borrow::Borrow, + iter::zip, + rc::Rc, + sync::{Arc, Mutex}, +}; + use openvm_circuit_primitives::var_range::{ SharedVariableRangeCheckerChip, VariableRangeCheckerBus, }; use openvm_instructions::instruction::Instruction; +use openvm_poseidon2_air::Poseidon2Config; use openvm_stark_backend::{ config::{StarkGenericConfig, Val}, engine::VerificationData, - interaction::BusIndex, + interaction::{BusIndex, PermutationCheckBus}, p3_field::PrimeField32, p3_matrix::dense::{DenseMatrix, RowMajorMatrix}, prover::types::AirProofInput, @@ -30,9 +38,11 @@ use crate::{ arch::{ExecutionState, MemoryConfig}, system::{ memory::{ + interface::MemoryInterface, offline_checker::{MemoryBridge, MemoryBus}, MemoryController, SharedMemoryHelper, }, + poseidon2::Poseidon2PeripheryChip, program::ProgramBus, }, }; @@ -250,6 +260,31 @@ impl VmChipTestBuilder { } } +impl VmChipTestBuilder { + pub fn default_persistent() -> Self { + let mem_config = MemoryConfig::default(); + let range_checker = SharedVariableRangeCheckerChip::new(VariableRangeCheckerBus::new( + RANGE_CHECKER_BUS, + mem_config.decomp, + )); + let memory_controller = MemoryController::with_persistent_memory( + MemoryBus::new(MEMORY_BUS), + mem_config, + range_checker, + PermutationCheckBus::new(MEMORY_MERKLE_BUS), + PermutationCheckBus::new(POSEIDON2_DIRECT_BUS), + ); + Self { + memory: MemoryTester::new(memory_controller), + execution: ExecutionTester::new(ExecutionBus::new(EXECUTION_BUS)), + program: ProgramTester::new(ProgramBus::new(READ_INSTRUCTION_BUS)), + rng: StdRng::seed_from_u64(0), + default_register: 0, + default_pointer: 0, + } + } +} + impl Default for VmChipTestBuilder { fn default() -> Self { setup_tracing_with_log_level(Level::INFO); @@ -304,27 +339,48 @@ where } pub fn finalize(mut self) -> Self { - if let Some(mut memory_tester) = self.memory.take() { + if let Some(memory_tester) = self.memory.take() { // Balance memory boundaries - memory_tester.finalize(); - let memory_controller = memory_tester.controller; + let mut memory_controller = memory_tester.controller; let range_checker = memory_controller.range_checker.clone(); - drop(memory_controller); - // dummy memory interactions: - for mem_chip in memory_tester.chip_for_block.into_values() { - self = self.load(mem_chip); - } - { - // todo: boundary and adapter stuff - // let airs = memory_controller.borrow().airs(); - // let air_proof_inputs = Rc::try_unwrap(memory_controller) - // .unwrap_or_else(|_| panic!("Memory controller was not dropped")) - // .into_inner() - // .generate_air_proof_inputs(); - // self.air_proof_inputs.extend( - // zip(airs, air_proof_inputs).filter(|(_, input)| input.main_trace_height() > - // 0), ); - } + match &memory_controller.interface_chip { + MemoryInterface::Volatile { .. } => { + memory_controller.finalize(None::<&mut Poseidon2PeripheryChip>>); + // dummy memory interactions: + for mem_chip in memory_tester.chip_for_block.into_values() { + self = self.load(mem_chip); + } + { + let airs = memory_controller.borrow().airs(); + let air_proof_inputs = memory_controller.generate_air_proof_inputs(); + self.air_proof_inputs.extend( + zip(airs, air_proof_inputs) + .filter(|(_, input)| input.main_trace_height() > 0), + ); + } + } + MemoryInterface::Persistent { .. } => { + let mut poseidon_chip = Poseidon2PeripheryChip::new( + Poseidon2Config::default(), + POSEIDON2_DIRECT_BUS, + 3, + ); + memory_controller.finalize(Some(&mut poseidon_chip)); + // dummy memory interactions: + for mem_chip in memory_tester.chip_for_block.into_values() { + self = self.load(mem_chip); + } + { + let airs = memory_controller.borrow().airs(); + let air_proof_inputs = memory_controller.generate_air_proof_inputs(); + self.air_proof_inputs.extend( + zip(airs, air_proof_inputs) + .filter(|(_, input)| input.main_trace_height() > 0), + ); + } + self = self.load(poseidon_chip); + } + }; self = self.load(range_checker); // this must be last because other trace generation // mutates its state } diff --git a/crates/vm/src/system/memory/adapter/mod.rs b/crates/vm/src/system/memory/adapter/mod.rs index e2e0ab9ff1..821657e1d2 100644 --- a/crates/vm/src/system/memory/adapter/mod.rs +++ b/crates/vm/src/system/memory/adapter/mod.rs @@ -1,11 +1,10 @@ -use std::{borrow::BorrowMut, cmp::max, io::Cursor, sync::Arc}; +use std::{borrow::BorrowMut, io::Cursor, sync::Arc}; pub use air::*; pub use columns::*; use enum_dispatch::enum_dispatch; use openvm_circuit_primitives::{ - is_less_than::IsLtSubAir, utils::next_power_of_two_or_zero, - var_range::SharedVariableRangeCheckerChip, TraceSubRowGenerator, + is_less_than::IsLtSubAir, var_range::SharedVariableRangeCheckerChip, TraceSubRowGenerator, }; use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; use openvm_stark_backend::{ @@ -13,8 +12,7 @@ use openvm_stark_backend::{ p3_air::BaseAir, p3_commit::PolynomialSpace, p3_field::PrimeField32, - p3_matrix::dense::RowMajorMatrix, - p3_maybe_rayon::prelude::*, + p3_matrix::{dense::RowMajorMatrix, Matrix}, p3_util::log2_strict_usize, prover::types::AirProofInput, AirRef, Chip, ChipUsageGetter, @@ -32,7 +30,7 @@ pub struct AccessAdapterInventory { air_names: Vec, } -impl AccessAdapterInventory { +impl AccessAdapterInventory { pub fn new( range_checker: SharedVariableRangeCheckerChip, memory_bus: MemoryBus, @@ -80,6 +78,13 @@ impl AccessAdapterInventory { } } + pub fn set_trace(&mut self, index: usize, trace: Vec, width: usize) + where + F: PrimeField32, + { + self.chips[index].set_trace(RowMajorMatrix::new(trace, width)); + } + #[cfg(test)] pub fn records_for_n(&self, n: usize) -> &[AccessAdapterRecord] { let idx = log2_strict_usize(n) - 1; @@ -134,7 +139,10 @@ impl AccessAdapterInventory { memory_bus: MemoryBus, clk_max_bits: usize, max_access_adapter_n: usize, - ) -> Option> { + ) -> Option> + where + F: Clone + Send + Sync, + { if N <= max_access_adapter_n { Some(GenericAccessAdapterChip::new::( range_checker, @@ -204,6 +212,9 @@ pub trait GenericAccessAdapterChipTrait { fn add_record(&mut self, record: AccessAdapterRecord); fn n(&self) -> usize; fn generate_trace(self) -> RowMajorMatrix + where + F: PrimeField32; + fn set_trace(&mut self, trace: RowMajorMatrix) where F: PrimeField32; @@ -238,7 +249,7 @@ enum GenericAccessAdapterChip { N32(AccessAdapterChip), } -impl GenericAccessAdapterChip { +impl GenericAccessAdapterChip { fn new( range_checker: SharedVariableRangeCheckerChip, memory_bus: MemoryBus, @@ -273,9 +284,11 @@ pub struct AccessAdapterChip { air: AccessAdapterAir, range_checker: SharedVariableRangeCheckerChip, pub records: Vec>, + trace: RowMajorMatrix, overridden_height: Option, } -impl AccessAdapterChip { + +impl AccessAdapterChip { pub fn new( range_checker: SharedVariableRangeCheckerChip, memory_bus: MemoryBus, @@ -286,6 +299,7 @@ impl AccessAdapterChip { air: AccessAdapterAir:: { memory_bus, lt_air }, range_checker, records: vec![], + trace: RowMajorMatrix::new(Vec::new(), 0), overridden_height: None, } } @@ -304,48 +318,59 @@ impl GenericAccessAdapterChipTrait for AccessAdapterChip::width(&self.air); - let height = if let Some(oh) = self.overridden_height { - assert!( - oh >= self.records.len(), - "Overridden height is less than the required height" - ); - oh - } else { - self.records.len() - }; - let height = next_power_of_two_or_zero(height); - let mut values = F::zero_vec(height * width); - - values - .par_chunks_mut(width) - .zip(self.records.into_par_iter()) - .for_each(|(row, record)| { - let row: &mut AccessAdapterCols = row.borrow_mut(); - - row.is_valid = F::ONE; - row.values = record.data.try_into().unwrap(); - row.address = MemoryAddress::new(record.address_space, record.start_index); - - let (left_timestamp, right_timestamp) = match record.kind { - AccessAdapterRecordKind::Split => (record.timestamp, record.timestamp), - AccessAdapterRecordKind::Merge { - left_timestamp, - right_timestamp, - } => (left_timestamp, right_timestamp), - }; - debug_assert_eq!(max(left_timestamp, right_timestamp), record.timestamp); - - row.left_timestamp = F::from_canonical_u32(left_timestamp); - row.right_timestamp = F::from_canonical_u32(right_timestamp); - row.is_split = F::from_bool(record.kind == AccessAdapterRecordKind::Split); - - self.air.lt_air.generate_subrow( - (self.range_checker.as_ref(), left_timestamp, right_timestamp), - (&mut row.lt_aux, &mut row.is_right_larger), - ); - }); - RowMajorMatrix::new(values, width) + let mut trace = self.trace; + let height = trace.height(); + trace.pad_to_height(height.next_power_of_two(), F::ZERO); + trace + // TODO(AG): everything related to the calculated trace height + // needs to be in memory controller, who owns these traces. + + // let width = BaseAir::::width(&self.air); + // let height = if let Some(oh) = self.overridden_height { + // assert!( + // oh >= self.records.len(), + // "Overridden height is less than the required height" + // ); + // oh + // } else { + // self.records.len() + // }; + // let height = next_power_of_two_or_zero(height); + // let mut values = F::zero_vec(height * width); + + // values + // .par_chunks_mut(width) + // .zip(self.records.into_par_iter()) + // .for_each(|(row, record)| { + // let row: &mut AccessAdapterCols = row.borrow_mut(); + + // row.is_valid = F::ONE; + // row.values = record.data.try_into().unwrap(); + // row.address = MemoryAddress::new(record.address_space, record.start_index); + + // let (left_timestamp, right_timestamp) = match record.kind { + // AccessAdapterRecordKind::Split => (record.timestamp, record.timestamp), + // AccessAdapterRecordKind::Merge { + // left_timestamp, + // right_timestamp, + // } => (left_timestamp, right_timestamp), + // }; + // debug_assert_eq!(max(left_timestamp, right_timestamp), record.timestamp); + + // row.left_timestamp = F::from_canonical_u32(left_timestamp); + // row.right_timestamp = F::from_canonical_u32(right_timestamp); + // row.is_split = F::from_bool(record.kind == AccessAdapterRecordKind::Split); + + // self.air.lt_air.generate_subrow( + // (self.range_checker.as_ref(), left_timestamp, right_timestamp), + // (&mut row.lt_aux, &mut row.is_right_larger), + // ); + // }); + // RowMajorMatrix::new(values, width) + } + + fn set_trace(&mut self, trace: RowMajorMatrix) { + self.trace = trace; } fn execute_split( @@ -364,15 +389,19 @@ impl GenericAccessAdapterChipTrait for AccessAdapterChip GenericAccessAdapterChipTrait for AccessAdapterChip AdapterInventoryTraceCursor { let index = get_chip_index(block_size); let begin = self.cursors[index].position() as usize; let end = begin + self.widths[index]; + self.cursors[index].get_mut().resize(end, F::ZERO); self.cursors[index].set_position(end as u64); &mut self.cursors[index].get_mut()[begin..end] } + + pub fn extract_trace(&mut self, index: usize) -> Vec { + std::mem::replace(&mut self.cursors[index], Cursor::new(Vec::new())).into_inner() + } + + pub fn width(&self, index: usize) -> usize { + self.widths[index] + } } diff --git a/crates/vm/src/system/memory/controller/mod.rs b/crates/vm/src/system/memory/controller/mod.rs index 0b37974e76..aee7f23bf0 100644 --- a/crates/vm/src/system/memory/controller/mod.rs +++ b/crates/vm/src/system/memory/controller/mod.rs @@ -1,14 +1,8 @@ -use std::{ - collections::BTreeMap, - iter, - marker::PhantomData, - sync::{Arc, Mutex}, -}; +use std::{collections::BTreeMap, iter, marker::PhantomData}; use getset::{Getters, MutGetters}; use openvm_circuit_primitives::{ assert_less_than::{AssertLtSubAir, LessThanAuxCols}, - is_zero::IsZeroSubAir, utils::next_power_of_two_or_zero, var_range::{ SharedVariableRangeCheckerChip, VariableRangeCheckerBus, VariableRangeCheckerChip, @@ -29,9 +23,10 @@ use serde::{Deserialize, Serialize}; use self::interface::MemoryInterface; use super::{ - online::{GuestMemory, INITIAL_TIMESTAMP}, + online::INITIAL_TIMESTAMP, paged_vec::{AddressMap, PAGE_SIZE}, volatile::VolatileBoundaryChip, + MemoryAddress, }; use crate::{ arch::{hasher::HasherChip, MemoryConfig}, @@ -39,13 +34,9 @@ use crate::{ adapter::AccessAdapterInventory, dimensions::MemoryDimensions, merkle::{MemoryMerkleChip, SerialReceiver}, - offline_checker::{ - MemoryBaseAuxCols, MemoryBridge, MemoryBus, MemoryReadAuxCols, - MemoryReadOrImmediateAuxCols, MemoryWriteAuxCols, AUX_LEN, - }, - online::{MemoryLogEntry, TracingMemory}, + offline_checker::{MemoryBaseAuxCols, MemoryBridge, MemoryBus, AUX_LEN}, + online::{AccessMetadata, TracingMemory}, persistent::PersistentBoundaryChip, - tree::MemoryNode, }, }; @@ -100,25 +91,6 @@ pub struct MemoryController { // addr_space -> Memory data structure pub memory: TracingMemory, pub access_adapters: AccessAdapterInventory, - // Filled during finalization. - final_state: Option>, -} - -#[allow(clippy::large_enum_variant)] -#[derive(Debug)] -enum FinalState { - Volatile(VolatileFinalState), - #[allow(dead_code)] - Persistent(PersistentFinalState), -} -#[derive(Debug, Default)] -struct VolatileFinalState { - _marker: PhantomData, -} -#[allow(dead_code)] -#[derive(Debug)] -struct PersistentFinalState { - final_memory: Equipartition, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -240,7 +212,7 @@ impl MemoryController { range_checker.clone(), ), }, - memory: TracingMemory::new(&mem_config, range_checker.clone(), memory_bus), + memory: TracingMemory::new(&mem_config, range_checker.clone(), memory_bus, 1), access_adapters: AccessAdapterInventory::new( range_checker.clone(), memory_bus, @@ -249,7 +221,6 @@ impl MemoryController { ), range_checker, range_checker_bus, - final_state: None, } } @@ -284,8 +255,8 @@ impl MemoryController { memory_bus, mem_config, interface_chip, - memory: TracingMemory::new(&mem_config, range_checker.clone(), memory_bus), /* it is expected that the memory will be - * set later */ + memory: TracingMemory::new(&mem_config, range_checker.clone(), memory_bus, CHUNK), /* it is expected that the memory will be + * set later */ access_adapters: AccessAdapterInventory::new( range_checker.clone(), memory_bus, @@ -294,7 +265,6 @@ impl MemoryController { ), range_checker, range_checker_bus, - final_state: None, } } @@ -337,6 +307,7 @@ impl MemoryController { &self.mem_config, self.range_checker.clone(), self.memory_bus, + CHUNK, ) .with_image(memory.clone(), self.mem_config.access_capacity); @@ -462,47 +433,198 @@ impl MemoryController { self.memory.timestamp() } + /// Returns the equipartition of the touched blocks. + /// Has side effects (namely setting the traces for the access adapters). + fn touched_blocks_to_equipartition( + &mut self, + touched_blocks: Vec<((u32, u32), AccessMetadata)>, + ) -> TimestampedEquipartition { + let mut current_values = [F::ZERO; CHUNK]; + let mut current_cnt = 0; + let mut current_address = MemoryAddress::new(0, 0); + let mut current_timestamps = vec![0; CHUNK]; + let mut final_memory = TimestampedEquipartition::::new(); + for ((addr_space, ptr), metadata) in touched_blocks { + let AccessMetadata { + timestamp, + block_size, + } = metadata; + if current_cnt > 0 + && (current_address.address_space != addr_space + || current_address.pointer + CHUNK as u32 <= ptr) + { + let min_block_size = + self.memory.min_block_size[current_address.address_space as usize] as usize; + current_values[current_cnt..].fill(F::ZERO); + current_timestamps[(current_cnt / min_block_size)..].fill(INITIAL_TIMESTAMP); + self.memory.execute_merges::( + current_address, + min_block_size, + ¤t_values, + ¤t_timestamps, + ); + final_memory.insert( + (current_address.address_space, current_address.pointer), + TimestampedValues { + timestamp: *current_timestamps + .iter() + .take(current_cnt.div_ceil(min_block_size)) + .max() + .unwrap(), + values: current_values, + }, + ); + current_cnt = 0; + } + let min_block_size = self.memory.min_block_size[addr_space as usize] as usize; + if current_cnt == 0 { + let rem = ptr & (CHUNK as u32 - 1); + if rem != 0 { + current_values[..(rem as usize)].fill(F::ZERO); + current_address = MemoryAddress::new(addr_space, ptr - rem); + } else { + current_address = MemoryAddress::new(addr_space, ptr); + } + } else { + let offset = (ptr - current_address.pointer) as usize; + current_values[current_cnt..offset].fill(F::ZERO); + current_timestamps[(current_cnt / min_block_size)..(offset / min_block_size)] + .fill(INITIAL_TIMESTAMP); + current_cnt = offset; + } + debug_assert!(block_size >= min_block_size as u32); + debug_assert!(ptr % min_block_size as u32 == 0); + + let values = (0..block_size) + .map(|i| self.memory.data.get_f::(addr_space, ptr + i)) + .collect::>(); + self.memory.execute_splits::( + MemoryAddress::new(addr_space, ptr), + min_block_size.min(CHUNK), + &values, + metadata.timestamp, + ); + if INITIAL_MERGES { + debug_assert_eq!(CHUNK, 1); + let initial_values = vec![F::ZERO; min_block_size]; + let initial_timestamps = vec![INITIAL_TIMESTAMP; min_block_size / CHUNK]; + for i in (0..block_size).step_by(min_block_size) { + self.memory.execute_merges::( + MemoryAddress::new(addr_space, ptr + i), + CHUNK, + &initial_values, + &initial_timestamps, + ); + } + } + for i in 0..block_size { + current_values[current_cnt] = values[i as usize]; + if current_cnt & (min_block_size - 1) == 0 { + current_timestamps[current_cnt / min_block_size] = timestamp; + } + current_cnt += 1; + if current_cnt == CHUNK { + self.memory.execute_merges::( + current_address, + min_block_size, + ¤t_values, + ¤t_timestamps, + ); + final_memory.insert( + (current_address.address_space, current_address.pointer), + TimestampedValues { + timestamp: *current_timestamps + .iter() + .take(current_cnt.div_ceil(min_block_size)) + .max() + .unwrap(), + values: current_values, + }, + ); + current_address.pointer += current_cnt as u32; + current_cnt = 0; + } + } + } + if current_cnt > 0 { + let min_block_size = + self.memory.min_block_size[current_address.address_space as usize] as usize; + current_values[current_cnt..].fill(F::ZERO); + current_timestamps[(current_cnt / min_block_size)..].fill(INITIAL_TIMESTAMP); + self.memory.execute_merges::( + current_address, + min_block_size, + ¤t_values, + ¤t_timestamps, + ); + final_memory.insert( + (current_address.address_space, current_address.pointer), + TimestampedValues { + timestamp: *current_timestamps + .iter() + .take(current_cnt.div_ceil(min_block_size)) + .max() + .unwrap(), + values: current_values, + }, + ); + } + + for i in 0..self.access_adapters.num_access_adapters() { + let width = self.memory.adapter_inventory_trace_cursor.width(i); + self.access_adapters.set_trace( + i, + self.memory.adapter_inventory_trace_cursor.extract_trace(i), + width, + ); + } + + final_memory + } + /// Returns the final memory state if persistent. + #[allow(clippy::assertions_on_constants)] pub fn finalize(&mut self, hasher: Option<&mut H>) where H: HasherChip + Sync + for<'a> SerialReceiver<&'a [F]>, { - if self.final_state.is_some() { - return; + let touched_blocks = self.memory.touched_blocks().collect::>(); + + let mut final_memory_volatile = None; + let mut final_memory_persistent = None; + + match &self.interface_chip { + MemoryInterface::Volatile { .. } => { + final_memory_volatile = + Some(self.touched_blocks_to_equipartition::<1, true>(touched_blocks)); + } + MemoryInterface::Persistent { .. } => { + final_memory_persistent = + Some(self.touched_blocks_to_equipartition::(touched_blocks)); + } } - todo!(); match &mut self.interface_chip { MemoryInterface::Volatile { boundary_chip } => { - // let final_memory = offline_memory.finalize::<1>(&mut self.access_adapters); - // boundary_chip.finalize(final_memory); - // self.final_state = Some(FinalState::Volatile(VolatileFinalState::default())); + let final_memory = final_memory_volatile.unwrap(); + boundary_chip.finalize(final_memory); } MemoryInterface::Persistent { - merkle_chip, boundary_chip, + merkle_chip, initial_memory, } => { + let final_memory = final_memory_persistent.unwrap(); + let hasher = hasher.unwrap(); - // let final_partition = offline_memory.finalize::(&mut - // self.access_adapters); - - // boundary_chip.finalize(initial_memory, &final_partition, hasher); - // let final_memory_values = final_partition - // .into_par_iter() - // .map(|(key, value)| (key, value.values)) - // .collect(); - // let initial_node = MemoryNode::tree_from_memory( - // merkle_chip.air.memory_dimensions, - // initial_memory, - // hasher, - // ); - // merkle_chip.finalize(&initial_node, &final_memory_values, hasher); - // self.final_state = Some(FinalState::Persistent(PersistentFinalState { - // final_memory: final_memory_values.clone(), - // })); + boundary_chip.finalize(initial_memory, &final_memory, hasher); + let final_memory_values = final_memory + .into_par_iter() + .map(|(key, value)| (key, value.values)) + .collect(); + merkle_chip.finalize(initial_memory.clone(), &final_memory_values, hasher); } - }; + } } pub fn generate_air_proof_inputs(self) -> Vec> diff --git a/crates/vm/src/system/memory/merkle/mod.rs b/crates/vm/src/system/memory/merkle/mod.rs index 74f8951bc4..e9c7c49e8c 100644 --- a/crates/vm/src/system/memory/merkle/mod.rs +++ b/crates/vm/src/system/memory/merkle/mod.rs @@ -1,10 +1,11 @@ use openvm_stark_backend::{interaction::PermutationCheckBus, p3_field::PrimeField32}; use rustc_hash::FxHashSet; -use super::controller::dimensions::MemoryDimensions; +use super::{controller::dimensions::MemoryDimensions, Equipartition, MemoryImage}; mod air; mod columns; mod trace; +mod tree; pub use air::*; pub use columns::*; @@ -78,3 +79,17 @@ impl MemoryMerkleChip { } } } + +fn memory_to_partition( + memory: &MemoryImage, +) -> Equipartition { + let mut memory_partition = Equipartition::new(); + for ((address_space, pointer), value) in memory.items() { + let label = (address_space, pointer / N as u32); + let chunk = memory_partition + .entry(label) + .or_insert_with(|| [F::default(); N]); + chunk[(pointer % N as u32) as usize] = value; + } + memory_partition +} diff --git a/crates/vm/src/system/memory/merkle/tests/mod.rs b/crates/vm/src/system/memory/merkle/tests/mod.rs index 98ba089ad2..65474093e3 100644 --- a/crates/vm/src/system/memory/merkle/tests/mod.rs +++ b/crates/vm/src/system/memory/merkle/tests/mod.rs @@ -19,6 +19,7 @@ use openvm_stark_sdk::{ }; use rand::RngCore; +use super::memory_to_partition; use crate::{ arch::testing::{MEMORY_MERKLE_BUS, POSEIDON2_DIRECT_BUS}, system::memory::{ @@ -180,20 +181,6 @@ fn test( .expect("Verification failed"); } -fn memory_to_partition( - memory: &MemoryImage, -) -> Equipartition { - let mut memory_partition = Equipartition::new(); - for ((address_space, pointer), value) in memory.items() { - let label = (address_space, pointer / N as u32); - let chunk = memory_partition - .entry(label) - .or_insert_with(|| [F::default(); N]); - chunk[(pointer % N as u32) as usize] = value; - } - memory_partition -} - fn random_test( height: usize, max_value: u32, diff --git a/crates/vm/src/system/memory/merkle/trace.rs b/crates/vm/src/system/memory/merkle/trace.rs index 52609f259a..5a5dfc35e0 100644 --- a/crates/vm/src/system/memory/merkle/trace.rs +++ b/crates/vm/src/system/memory/merkle/trace.rs @@ -1,6 +1,5 @@ use std::{ borrow::BorrowMut, - cmp::Reverse, sync::{atomic::AtomicU32, Arc}, }; @@ -11,16 +10,13 @@ use openvm_stark_backend::{ prover::types::AirProofInput, AirRef, Chip, ChipUsageGetter, }; -use rustc_hash::FxHashSet; use crate::{ arch::hasher::HasherChip, system::{ memory::{ - controller::dimensions::MemoryDimensions, - merkle::{FinalState, MemoryMerkleChip, MemoryMerkleCols}, - tree::MemoryNode::{self, NonLeaf}, - Equipartition, + merkle::{tree::MerkleTree, FinalState, MemoryMerkleChip, MemoryMerkleCols}, + Equipartition, MemoryImage, }, poseidon2::{ Poseidon2PeripheryBaseChip, Poseidon2PeripheryChip, PERIPHERY_POSEIDON2_WIDTH, @@ -31,37 +27,13 @@ use crate::{ impl MemoryMerkleChip { pub fn finalize( &mut self, - initial_tree: &MemoryNode, + initial_memory: MemoryImage, final_memory: &Equipartition, hasher: &mut impl HasherChip, ) { assert!(self.final_state.is_none(), "Merkle chip already finalized"); - // there needs to be a touched node with `height_section` = 0 - // shouldn't be a leaf because - // trace generation will expect an interaction from MemoryInterfaceChip in that case - if self.touched_nodes.len() == 1 { - self.touch_node(1, 0, 0); - } - - let mut rows = vec![]; - let mut tree_helper = TreeHelper { - memory_dimensions: self.air.memory_dimensions, - final_memory, - touched_nodes: &self.touched_nodes, - trace_rows: &mut rows, - }; - let final_tree = tree_helper.recur( - self.air.memory_dimensions.overall_height(), - initial_tree, - 0, - 0, - hasher, - ); - self.final_state = Some(FinalState { - rows, - init_root: initial_tree.hash(), - final_root: final_tree.hash(), - }); + let mut tree = MerkleTree::from_memory(initial_memory, &self.air.memory_dimensions, hasher); + self.final_state = Some(tree.finalize(hasher, final_memory, &self.air.memory_dimensions)); } } @@ -85,7 +57,8 @@ where } = self.final_state.unwrap(); // important that this sort be stable, // because we need the initial root to be first and the final root to be second - rows.sort_by_key(|row| Reverse(row.parent_height)); + rows.reverse(); + rows.swap(0, 1); let width = MemoryMerkleCols::, CHUNK>::width(); let mut height = rows.len().next_power_of_two(); @@ -122,136 +95,6 @@ impl ChipUsageGetter for MemoryMerkleChip { - memory_dimensions: MemoryDimensions, - final_memory: &'a Equipartition, - touched_nodes: &'a FxHashSet<(usize, u32, u32)>, - trace_rows: &'a mut Vec>, -} - -impl TreeHelper<'_, CHUNK, F> { - fn recur( - &mut self, - height: usize, - initial_node: &MemoryNode, - as_label: u32, - address_label: u32, - hasher: &mut impl HasherChip, - ) -> MemoryNode { - if height == 0 { - let address_space = as_label + self.memory_dimensions.as_offset; - let leaf_values = *self - .final_memory - .get(&(address_space, address_label)) - .unwrap_or(&[F::ZERO; CHUNK]); - MemoryNode::new_leaf(hasher.hash(&leaf_values)) - } else if let NonLeaf { - left: initial_left_node, - right: initial_right_node, - .. - } = initial_node.clone() - { - // Tell the hasher about this hash. - hasher.compress_and_record(&initial_left_node.hash(), &initial_right_node.hash()); - - let is_as_section = height > self.memory_dimensions.address_height; - - let (left_as_label, right_as_label) = if is_as_section { - (2 * as_label, 2 * as_label + 1) - } else { - (as_label, as_label) - }; - let (left_address_label, right_address_label) = if is_as_section { - (address_label, address_label) - } else { - (2 * address_label, 2 * address_label + 1) - }; - - let left_is_final = - !self - .touched_nodes - .contains(&(height - 1, left_as_label, left_address_label)); - - let final_left_node = if left_is_final { - initial_left_node - } else { - Arc::new(self.recur( - height - 1, - &initial_left_node, - left_as_label, - left_address_label, - hasher, - )) - }; - - let right_is_final = - !self - .touched_nodes - .contains(&(height - 1, right_as_label, right_address_label)); - - let final_right_node = if right_is_final { - initial_right_node - } else { - Arc::new(self.recur( - height - 1, - &initial_right_node, - right_as_label, - right_address_label, - hasher, - )) - }; - - let final_node = MemoryNode::new_nonleaf(final_left_node, final_right_node, hasher); - self.add_trace_row(height, as_label, address_label, initial_node, None); - self.add_trace_row( - height, - as_label, - address_label, - &final_node, - Some([left_is_final, right_is_final]), - ); - final_node - } else { - panic!("Leaf {:?} found at nonzero height {}", initial_node, height); - } - } - - /// Expects `node` to be NonLeaf - fn add_trace_row( - &mut self, - parent_height: usize, - as_label: u32, - address_label: u32, - node: &MemoryNode, - direction_changes: Option<[bool; 2]>, - ) { - let [left_direction_change, right_direction_change] = - direction_changes.unwrap_or([false; 2]); - let cols = if let NonLeaf { hash, left, right } = node { - MemoryMerkleCols { - expand_direction: if direction_changes.is_none() { - F::ONE - } else { - F::NEG_ONE - }, - height_section: F::from_bool(parent_height > self.memory_dimensions.address_height), - parent_height: F::from_canonical_usize(parent_height), - is_root: F::from_bool(parent_height == self.memory_dimensions.overall_height()), - parent_as_label: F::from_canonical_u32(as_label), - parent_address_label: F::from_canonical_u32(address_label), - parent_hash: *hash, - left_child_hash: left.hash(), - right_child_hash: right.hash(), - left_direction_different: F::from_bool(left_direction_change), - right_direction_different: F::from_bool(right_direction_change), - } - } else { - panic!("trace_rows expects node = {:?} to be NonLeaf", node); - }; - self.trace_rows.push(cols); - } -} - pub trait SerialReceiver { fn receive(&mut self, msg: T); } diff --git a/crates/vm/src/system/memory/merkle/tree.rs b/crates/vm/src/system/memory/merkle/tree.rs new file mode 100644 index 0000000000..74f3d54730 --- /dev/null +++ b/crates/vm/src/system/memory/merkle/tree.rs @@ -0,0 +1,228 @@ +use openvm_stark_backend::p3_field::PrimeField32; +use rustc_hash::FxHashMap; + +use super::{memory_to_partition, FinalState, MemoryMerkleCols}; +use crate::{ + arch::hasher::HasherChip, + system::memory::{dimensions::MemoryDimensions, AddressMap, Equipartition, PAGE_SIZE}, +}; + +#[derive(Debug)] +pub struct MerkleTree { + /// Height of the tree -- the root is the only node at height `height`, + /// and the leaves are at height `0`. + height: usize, + /// Nodes corresponding to all zeroes. + zero_nodes: Vec<[F; CHUNK]>, + /// Nodes in the tree that have ever been touched. + nodes: FxHashMap, +} + +impl MerkleTree { + pub fn new(height: usize, hasher: &impl HasherChip) -> Self { + Self { + height, + zero_nodes: (0..height + 1) + .scan(hasher.hash(&[F::ZERO; CHUNK]), |acc, _| { + let result = Some(*acc); + *acc = hasher.compress(acc, acc); + result + }) + .collect(), + nodes: FxHashMap::default(), + } + } + + /// Shared logic for both from_memory and finalize. + fn process_layers( + &mut self, + layer: Vec<(u64, [F; CHUNK])>, + md: &MemoryDimensions, + mut rows: Option<&mut Vec>>, + mut compress: CompressFn, + ) where + CompressFn: FnMut(&[F; CHUNK], &[F; CHUNK]) -> [F; CHUNK], + { + let mut layer = layer + .into_iter() + .map(|(index, values)| (index, values, self.get_node(index))) + .collect::>(); + for height in 1..=self.height { + let mut i = 0; + let mut new_layer = Vec::new(); + while i < layer.len() { + let (index, values, old_values) = layer[i]; + let par_index = index >> 1; + i += 1; + + let par_old_values = self.get_node(par_index); + + // Lowest `label_section_height` bits of `par_index` are the address label, + // The remaining highest are the address space label. + let label_section_height = md.address_height.saturating_sub(height); + let parent_address_label = (par_index & ((1 << label_section_height) - 1)) as u32; + let parent_as_label = + ((par_index & !(1 << (self.height - height))) >> label_section_height) as u32; + + self.nodes.insert(index, values); + + if i < layer.len() && layer[i].0 == index ^ 1 { + // sibling found + let (_, sibling_values, sibling_old_values) = layer[i]; + i += 1; + let combined = compress(&values, &sibling_values); + + // Only record rows if requested + if let Some(rows) = rows.as_deref_mut() { + rows.push(MemoryMerkleCols { + expand_direction: F::ONE, + height_section: F::from_bool(height > md.address_height), + parent_height: F::from_canonical_usize(height), + is_root: F::from_bool(height == md.overall_height()), + parent_as_label: F::from_canonical_u32(parent_as_label), + parent_address_label: F::from_canonical_u32(parent_address_label), + parent_hash: self.get_node(par_index), + left_child_hash: old_values, + right_child_hash: sibling_old_values, + left_direction_different: F::ZERO, + right_direction_different: F::ZERO, + }); + rows.push(MemoryMerkleCols { + expand_direction: F::NEG_ONE, + height_section: F::from_bool(height > md.address_height), + parent_height: F::from_canonical_usize(height), + is_root: F::from_bool(height == md.overall_height()), + parent_as_label: F::from_canonical_u32(parent_as_label), + parent_address_label: F::from_canonical_u32(parent_address_label), + parent_hash: combined, + left_child_hash: values, + right_child_hash: sibling_values, + left_direction_different: F::ZERO, + right_direction_different: F::ZERO, + }); + // This is a hacky way to say "and we also want to record the old values" + compress(&old_values, &sibling_old_values); + } + + self.nodes.insert(index ^ 1, sibling_values); + new_layer.push((par_index, combined, par_old_values)); + } else { + // no sibling found + let sibling_values = self.get_node(index ^ 1); + let is_left = index % 2 == 0; + let (left, right) = if is_left { + (values, sibling_values) + } else { + (sibling_values, values) + }; + let combined = compress(&left, &right); + + if let Some(rows) = rows.as_deref_mut() { + rows.push(MemoryMerkleCols { + expand_direction: F::ONE, + height_section: F::from_bool(height > md.address_height), + parent_height: F::from_canonical_usize(height), + is_root: F::from_bool(height == md.overall_height()), + parent_as_label: F::from_canonical_u32(parent_as_label), + parent_address_label: F::from_canonical_u32(parent_address_label), + parent_hash: self.get_node(par_index), + left_child_hash: if is_left { old_values } else { left }, + right_child_hash: if is_left { right } else { old_values }, + left_direction_different: F::ZERO, + right_direction_different: F::ZERO, + }); + rows.push(MemoryMerkleCols { + expand_direction: F::NEG_ONE, + height_section: F::from_bool(height > md.address_height), + parent_height: F::from_canonical_usize(height), + is_root: F::from_bool(height == md.overall_height()), + parent_as_label: F::from_canonical_u32(parent_as_label), + parent_address_label: F::from_canonical_u32(parent_address_label), + parent_hash: combined, + left_child_hash: left, + right_child_hash: right, + left_direction_different: F::from_bool(!is_left), + right_direction_different: F::from_bool(is_left), + }); + // This is a hacky way to say "and we also want to record the old values" + if is_left { + compress(&old_values, &right); + } else { + compress(&left, &old_values); + } + } + + new_layer.push((par_index, combined, par_old_values)); + } + } + layer = new_layer; + } + if !layer.is_empty() { + assert_eq!(layer.len(), 1); + self.nodes.insert(layer[0].0, layer[0].1); + } + } + + pub fn from_memory( + initial_memory: AddressMap, + md: &MemoryDimensions, + hasher: &impl HasherChip, + ) -> Self { + let mut tree = Self::new(md.overall_height(), hasher); + let layer: Vec<_> = memory_to_partition(&initial_memory) + .iter() + .map(|((addr_sp, ptr), v)| { + ( + (1 << tree.height) + md.label_to_index((*addr_sp, *ptr)), + hasher.hash(v), + ) + }) + .collect(); + tree.process_layers(layer, md, None, |left, right| hasher.compress(left, right)); + tree + } + + pub fn finalize( + &mut self, + hasher: &mut impl HasherChip, + touched: &Equipartition, + md: &MemoryDimensions, + ) -> FinalState { + let init_root = self.get_node(1); + let layer: Vec<_> = touched + .iter() + .map(|((addr_sp, ptr), v)| { + ( + (1 << self.height) + md.label_to_index((*addr_sp, *ptr / CHUNK as u32)), + hasher.hash(v), + ) + }) + .collect(); + let mut rows = Vec::with_capacity(if touched.is_empty() { + 0 + } else { + layer + .iter() + .zip(layer.iter().skip(1)) + .fold(md.overall_height(), |acc, ((lhs, _), (rhs, _))| { + acc + (lhs ^ rhs).ilog2() as usize + }) + }); + self.process_layers(layer, md, Some(&mut rows), |left, right| { + hasher.compress_and_record(left, right) + }); + let final_root = self.get_node(1); + FinalState { + rows, + init_root, + final_root, + } + } + + fn get_node(&self, index: u64) -> [F; CHUNK] { + self.nodes + .get(&index) + .cloned() + .unwrap_or(self.zero_nodes[self.height - index.ilog2() as usize]) + } +} diff --git a/crates/vm/src/system/memory/online.rs b/crates/vm/src/system/memory/online.rs index 2dbf96391b..eec3156ba2 100644 --- a/crates/vm/src/system/memory/online.rs +++ b/crates/vm/src/system/memory/online.rs @@ -94,7 +94,7 @@ pub struct AccessMetadata { impl AccessMetadata { /// A marker indicating that the element is a part of a larger block which starts earlier. - const OCCUPIED: u32 = u32::MAX; + pub const OCCUPIED: u32 = u32::MAX; } /// Online memory that stores additional information for trace generation purposes. @@ -102,6 +102,8 @@ impl AccessMetadata { #[derive(Getters)] pub struct TracingMemory { pub timestamp: u32, + /// The initial block size -- this depends on the type of boundary chip. + initial_block_size: usize, /// The underlying data memory, with memory cells typed by address space: see [AddressMap]. // TODO: make generic in GuestMemory #[getset(get = "pub")] @@ -122,6 +124,7 @@ impl TracingMemory { mem_config: &MemoryConfig, range_checker: SharedVariableRangeCheckerChip, memory_bus: MemoryBus, + initial_block_size: usize, ) -> Self { assert_eq!(mem_config.as_offset, 1); let num_cells = 1usize << mem_config.pointer_max_bits; // max cells per address space @@ -147,6 +150,7 @@ impl TracingMemory { meta, min_block_size, timestamp: INITIAL_TIMESTAMP + 1, + initial_block_size, access_adapter_inventory: AccessAdapterInventory::new( range_checker, memory_bus, @@ -186,13 +190,25 @@ impl TracingMemory { ); } - fn execute_splits( + pub(crate) fn execute_splits( &mut self, address: MemoryAddress, + align: usize, values: &[F], timestamp: u32, ) { - let mut size = ALIGN; + if UPDATE_META { + for i in 0..(values.len() / align) { + self.set_meta_block( + address.address_space as usize, + address.pointer as usize + i * align, + align, + align, + timestamp, + ); + } + } + let mut size = align; let MemoryAddress { address_space, pointer, @@ -203,9 +219,9 @@ impl TracingMemory { self.access_adapter_inventory.execute_split( MemoryAddress { address_space, - pointer: pointer + (i * size) as u32, + pointer: pointer + i as u32, }, - &values[i * size..(i + 1) * size], + &values[i..i + size], timestamp, self.adapter_inventory_trace_cursor.get_row_slice(size), ); @@ -213,13 +229,23 @@ impl TracingMemory { } } - fn execute_merges( + pub(crate) fn execute_merges( &mut self, address: MemoryAddress, + align: usize, values: &[F], timestamps: &[u32], ) { - let mut size = ALIGN; + if UPDATE_META { + self.set_meta_block( + address.address_space as usize, + address.pointer as usize, + align, + values.len(), + *timestamps.iter().max().unwrap(), + ); + } + let mut size = align; let MemoryAddress { address_space, pointer, @@ -227,20 +253,20 @@ impl TracingMemory { while size < values.len() { size *= 2; for i in (0..values.len()).step_by(size) { - let left_timestamp = timestamps[(i / ALIGN)..((i + size / 2) / ALIGN)] + let left_timestamp = timestamps[(i / align)..((i + size / 2) / align)] .iter() .max() .unwrap(); - let right_timestamp = timestamps[(i + size / 2 / ALIGN)..((i + size) / ALIGN)] + let right_timestamp = timestamps[((i + size / 2) / align)..((i + size) / align)] .iter() .max() .unwrap(); self.access_adapter_inventory.execute_merge( MemoryAddress { address_space, - pointer: pointer + (i * size) as u32, + pointer: pointer + i as u32, }, - &values[i * size..(i + 1) * size], + &values[i..i + size], *left_timestamp, *right_timestamp, self.adapter_inventory_trace_cursor.get_row_slice(size), @@ -249,22 +275,52 @@ impl TracingMemory { } } + /// Updates the metadata with the given block. + fn set_meta_block( + &mut self, + address_space: usize, + pointer: usize, + align: usize, + block_size: usize, + timestamp: u32, + ) { + let ptr = pointer / align; + let meta = unsafe { self.meta.get_unchecked_mut(address_space) }; + meta.set( + ptr * size_of::(), + &AccessMetadata { + timestamp, + block_size: block_size as u32, + }, + ); + for i in 1..(block_size / align) { + meta.set( + (ptr + i) * size_of::(), + &AccessMetadata { + timestamp, + block_size: AccessMetadata::OCCUPIED, + }, + ); + } + } + /// Returns the timestamp of the previous access to `[pointer:BLOCK_SIZE]_{address_space}`. /// If we need to split/merge/initialize something for this, we first do all the necessary /// actions. In the end of this process, we have this segment intact in our `meta`. /// /// Caller must ensure alignment (e.g. via `assert_alignment`) prior to calling this function. - fn prev_access_time( + fn prev_access_time( &mut self, address_space: usize, pointer: usize, + align: usize, ) -> u32 { let size = size_of::(); - let seg_size = ALIGN * size; - let num_segs = BLOCK_SIZE / ALIGN; + let seg_size = align * size; + let num_segs = BLOCK_SIZE / align; - let begin = pointer / ALIGN; - let end = begin + BLOCK_SIZE / ALIGN; + let begin = pointer / align; + let end = begin + BLOCK_SIZE / align; let mut prev_ts = INITIAL_TIMESTAMP; let mut block_timestamps = vec![INITIAL_TIMESTAMP; num_segs]; @@ -281,32 +337,39 @@ impl TracingMemory { break false; } else if current_metadata.block_size == 0 { // Initialize - self.meta[address_space].set( - cur_ptr * size_of::(), - &AccessMetadata { - timestamp: INITIAL_TIMESTAMP, - block_size: ALIGN as u32, - }, + if self.initial_block_size > align { + cur_ptr -= cur_ptr % (self.initial_block_size / align); + } + self.set_meta_block( + address_space, + cur_ptr * align, + align, + self.initial_block_size, + INITIAL_TIMESTAMP, ); + current_metadata = + AccessMetadata::new(INITIAL_TIMESTAMP, self.initial_block_size as u32); } prev_ts = prev_ts.max(current_metadata.timestamp); while current_metadata.block_size == AccessMetadata::OCCUPIED { cur_ptr -= 1; - current_metadata = - self.meta[address_space].get::(cur_ptr * seg_size); + current_metadata = self.meta[address_space] + .get::(cur_ptr * size_of::()); } block_timestamps[cur_ptr.saturating_sub(begin) - ..((cur_ptr + current_metadata.block_size as usize).min(end) - begin)] + ..((cur_ptr + (current_metadata.block_size as usize) / align).min(end) - begin)] .fill(current_metadata.timestamp); - // Split - let address = MemoryAddress::new(address_space as u32, (cur_ptr * seg_size) as u32); - let values = (0..current_metadata.block_size as usize) - .map(|i| { - self.data - .get_f(address.address_space, address.pointer + (i as u32)) - }) - .collect::>(); - self.execute_splits::(address, &values, self.timestamp); + if current_metadata.block_size > align as u32 { + // Split + let address = MemoryAddress::new(address_space as u32, (cur_ptr * seg_size) as u32); + let values = (0..current_metadata.block_size as usize) + .map(|i| { + self.data + .get_f(address.address_space, address.pointer + (i as u32)) + }) + .collect::>(); + self.execute_splits::(address, align, &values, current_metadata.timestamp); + } cur_ptr += current_metadata.block_size as usize; }; if need_to_merge { @@ -314,8 +377,9 @@ impl TracingMemory { let values = (0..BLOCK_SIZE) .map(|i| self.data.get_f(address_space as u32, (pointer + i) as u32)) .collect::>(); - self.execute_merges::( + self.execute_merges::( MemoryAddress::new(address_space as u32, pointer as u32), + align, &values, &block_timestamps, ); @@ -354,29 +418,18 @@ impl TracingMemory { T: Copy + Debug, { self.assert_alignment(BLOCK_SIZE, ALIGN, address_space, pointer); - let values = self.data.read(address_space, pointer); + let t_prev = + self.prev_access_time::(address_space as usize, pointer as usize, ALIGN); let t_curr = self.timestamp; self.timestamp += 1; - // Handle timestamp and block size: - let access_idx = (pointer as usize / ALIGN) * size_of::(); - // TODO: this is wrong and must be replaced with normal logic - // let t_prev = { - // // TODO: address space should be checked elsewhere - // let meta = unsafe { self.meta.get_unchecked_mut(address_space as usize) }; - // let AccessMetadata { - // timestamp: t_prev, - // mut block_size, - // } = meta.replace(access_idx, &AccessMetadata::new(t_curr, BLOCK_SIZE as u32)); - // // TODO: mark as touched - // if block_size == 0 { - // block_size = BLOCK_SIZE as u32; - // } - // t_prev - // }; - let t_prev = - self.prev_access_time::(address_space as usize, pointer as usize); - let meta = unsafe { self.meta.get_unchecked_mut(address_space as usize) }; - meta.set(access_idx, &AccessMetadata::new(t_curr, BLOCK_SIZE as u32)); + let values = self.data.read(address_space, pointer); + self.set_meta_block( + address_space as usize, + pointer as usize, + ALIGN, + BLOCK_SIZE, + t_curr, + ); (t_prev, values) } @@ -413,29 +466,18 @@ impl TracingMemory { T: Copy + Debug, { self.assert_alignment(BLOCK_SIZE, ALIGN, address_space, pointer); + let t_prev = + self.prev_access_time::(address_space as usize, pointer as usize, ALIGN); let values_prev = self.data.replace(address_space, pointer, values); let t_curr = self.timestamp; self.timestamp += 1; - // Handle timestamp and block size: - let access_idx = (pointer as usize / ALIGN) * size_of::(); - // TODO: this is wrong and must be replaced with normal logic - // let t_prev = { - // // TODO: address space should be checked elsewhere - // let meta = unsafe { self.meta.get_unchecked_mut(address_space as usize) }; - // let AccessMetadata { - // timestamp: t_prev, - // mut block_size, - // } = meta.replace(access_idx, &AccessMetadata::new(t_curr, BLOCK_SIZE as u32)); - // // TODO: mark as touched - // if block_size == 0 { - // block_size = BLOCK_SIZE as u32; - // } - // t_prev - // }; - let t_prev = - self.prev_access_time::(address_space as usize, pointer as usize); - let meta = unsafe { self.meta.get_unchecked_mut(address_space as usize) }; - meta.set(access_idx, &AccessMetadata::new(t_curr, BLOCK_SIZE as u32)); + self.set_meta_block( + address_space as usize, + pointer as usize, + ALIGN, + BLOCK_SIZE, + t_curr, + ); (t_prev, values_prev) } @@ -465,7 +507,8 @@ impl TracingMemory { .flat_map(move |(addr_space, (page, &align))| { page.iter::() .filter_map(move |(idx, metadata)| { - (metadata.block_size != 0) + (metadata.block_size != 0 + && metadata.block_size != AccessMetadata::OCCUPIED) .then_some(((addr_space as u32, idx as u32 * align), metadata)) }) }) diff --git a/crates/vm/src/system/memory/persistent.rs b/crates/vm/src/system/memory/persistent.rs index 0163555dd6..3391e7f9e0 100644 --- a/crates/vm/src/system/memory/persistent.rs +++ b/crates/vm/src/system/memory/persistent.rs @@ -19,7 +19,7 @@ use openvm_stark_backend::{ }; use rustc_hash::FxHashSet; -use super::{merkle::SerialReceiver, online::INITIAL_TIMESTAMP}; +use super::{merkle::SerialReceiver, online::INITIAL_TIMESTAMP, TimestampedValues}; use crate::{ arch::hasher::Hasher, system::memory::{ @@ -201,42 +201,36 @@ impl PersistentBoundaryChip { pub fn finalize( &mut self, initial_memory: &MemoryImage, + // Only touched stuff final_memory: &TimestampedEquipartition, hasher: &mut H, ) where H: Hasher + Sync + for<'a> SerialReceiver<&'a [F]>, { - match &mut self.touched_labels { - TouchedLabels::Running(touched_labels) => { - let final_touched_labels: Vec<_> = touched_labels - .par_iter() - .map(|&(address_space, label)| { - let pointer = label * CHUNK as u32; - let init_values = array::from_fn(|i| unsafe { - initial_memory.get((address_space, pointer + i as u32)) - }); - let initial_hash = hasher.hash(&init_values); - let timestamped_values = final_memory.get(&(address_space, label)).unwrap(); - let final_hash = hasher.hash(×tamped_values.values); - FinalTouchedLabel { - address_space, - label, - init_values, - final_values: timestamped_values.values, - init_hash: initial_hash, - final_hash, - final_timestamp: timestamped_values.timestamp, - } - }) - .collect(); - for l in &final_touched_labels { - hasher.receive(&l.init_values); - hasher.receive(&l.final_values); + let final_touched_labels: Vec<_> = final_memory + .par_iter() + .map(|(&(addr_space, ptr), &ts_values)| { + let TimestampedValues { timestamp, values } = ts_values; + let init_values = + array::from_fn(|i| initial_memory.get_f::(addr_space, ptr + i as u32)); + let initial_hash = hasher.hash(&init_values); + let final_hash = hasher.hash(&values); + FinalTouchedLabel { + address_space: addr_space, + label: ptr / CHUNK as u32, + init_values, + final_values: values, + init_hash: initial_hash, + final_hash, + final_timestamp: timestamp, } - self.touched_labels = TouchedLabels::Final(final_touched_labels); - } - _ => panic!("Cannot finalize after finalization"), + }) + .collect(); + for l in &final_touched_labels { + hasher.receive(&l.init_values); + hasher.receive(&l.final_values); } + self.touched_labels = TouchedLabels::Final(final_touched_labels); } } diff --git a/extensions/rv32im/circuit/src/branch_lt/tests.rs b/extensions/rv32im/circuit/src/branch_lt/tests.rs index 3c7670f641..2c84f4b1ea 100644 --- a/extensions/rv32im/circuit/src/branch_lt/tests.rs +++ b/extensions/rv32im/circuit/src/branch_lt/tests.rs @@ -23,6 +23,7 @@ use openvm_stark_backend::{ }; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; +use test_case::test_case; use super::{ core::{run_cmp, BranchLessThanStep}, @@ -37,7 +38,6 @@ use crate::{ test_utils::get_verification_error, BranchLessThanCoreAir, }; -use test_case::test_case; type F = BabyBear; const MAX_INS_CAPACITY: usize = 128; diff --git a/extensions/rv32im/circuit/src/loadstore/tests.rs b/extensions/rv32im/circuit/src/loadstore/tests.rs index d7c758515e..1e83a3f62f 100644 --- a/extensions/rv32im/circuit/src/loadstore/tests.rs +++ b/extensions/rv32im/circuit/src/loadstore/tests.rs @@ -7,7 +7,6 @@ use openvm_circuit::{ }, utils::u32_into_limbs, }; - use openvm_instructions::{instruction::Instruction, LocalOpcode}; use openvm_rv32im_transpiler::Rv32LoadStoreOpcode::{self, *}; use openvm_stark_backend::{ diff --git a/extensions/rv32im/circuit/src/mulh/tests.rs b/extensions/rv32im/circuit/src/mulh/tests.rs index 3c64ea0f70..54bff347c1 100644 --- a/extensions/rv32im/circuit/src/mulh/tests.rs +++ b/extensions/rv32im/circuit/src/mulh/tests.rs @@ -1,12 +1,5 @@ use std::borrow::BorrowMut; -use super::core::run_mulh; -use crate::{ - adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, - mulh::{MulHCoreCols, MulHStep, Rv32MulHChip}, - test_utils::get_verification_error, - MulHCoreAir, -}; use openvm_circuit::{ arch::{ testing::{ @@ -35,6 +28,14 @@ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::rngs::StdRng; use test_case::test_case; +use super::core::run_mulh; +use crate::{ + adapters::{Rv32MultAdapterAir, Rv32MultAdapterStep, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}, + mulh::{MulHCoreCols, MulHStep, Rv32MulHChip}, + test_utils::get_verification_error, + MulHCoreAir, +}; + const MAX_INS_CAPACITY: usize = 128; // the max number of limbs we currently support MUL for is 32 (i.e. for U256s) const MAX_NUM_LIMBS: u32 = 32; From 65ab533fbdde451400268fc1d26b43d5e429b864 Mon Sep 17 00:00:00 2001 From: Xinding Wei Date: Tue, 13 May 2025 18:23:13 -0700 Subject: [PATCH 19/49] fix: Cargo lock & format (#1650) `cargo` complains that `uuid` has a conflict checksum. --- Cargo.lock | 15 ++++++++++++--- extensions/bigint/circuit/src/tests.rs | 9 ++++----- extensions/rv32im/circuit/src/divrem/core.rs | 1 - extensions/rv32im/circuit/src/mulh/core.rs | 1 - 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0bb10df310..3b73d08d99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1818,6 +1818,15 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + [[package]] name = "codspeed" version = "2.10.1" @@ -1853,7 +1862,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -8587,9 +8596,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" name = "uuid" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1f41ffb7cf259f1ecc2876861a17e7142e63ead296f671f81f6ae85903e0d6" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.3.1", + "getrandom 0.3.2", ] [[package]] diff --git a/extensions/bigint/circuit/src/tests.rs b/extensions/bigint/circuit/src/tests.rs index 8ae7a30894..abaf29345c 100644 --- a/extensions/bigint/circuit/src/tests.rs +++ b/extensions/bigint/circuit/src/tests.rs @@ -35,15 +35,14 @@ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; use test_case::test_case; -use crate::{ - Rv32BaseAlu256Step, Rv32BranchEqual256Step, Rv32BranchLessThan256Step, Rv32LessThan256Step, - Rv32Multiplication256Step, Rv32Shift256Step, -}; - use super::{ Rv32BaseAlu256Chip, Rv32BranchEqual256Chip, Rv32BranchLessThan256Chip, Rv32LessThan256Chip, Rv32Multiplication256Chip, Rv32Shift256Chip, }; +use crate::{ + Rv32BaseAlu256Step, Rv32BranchEqual256Step, Rv32BranchLessThan256Step, Rv32LessThan256Step, + Rv32Multiplication256Step, Rv32Shift256Step, +}; type F = BabyBear; const MAX_INS_CAPACITY: usize = 128; diff --git a/extensions/rv32im/circuit/src/divrem/core.rs b/extensions/rv32im/circuit/src/divrem/core.rs index a66048b05c..c8cc31ece2 100644 --- a/extensions/rv32im/circuit/src/divrem/core.rs +++ b/extensions/rv32im/circuit/src/divrem/core.rs @@ -29,7 +29,6 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, rap::BaseAirWithPublicValues, }; - use strum::IntoEnumIterator; #[repr(C)] diff --git a/extensions/rv32im/circuit/src/mulh/core.rs b/extensions/rv32im/circuit/src/mulh/core.rs index 555a70bbd7..b1ca02ee21 100644 --- a/extensions/rv32im/circuit/src/mulh/core.rs +++ b/extensions/rv32im/circuit/src/mulh/core.rs @@ -26,7 +26,6 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, rap::BaseAirWithPublicValues, }; - use strum::IntoEnumIterator; #[repr(C)] From 8c334451a68f9cb9bf5ce83cece17026b3bea703 Mon Sep 17 00:00:00 2001 From: Golovanov399 Date: Wed, 14 May 2025 23:57:05 +0300 Subject: [PATCH 20/49] fix: new block initialization in memory access adapters (#1651) I used to handle creating new blocks in a wrong way when `align > initial_block_size`, now I hopefully do it right. Also added persistent base alu tests, although nothing changed for the persistent case, and added a dummy access in all of them that used to fail. --- crates/vm/src/system/memory/online.rs | 32 ++++++++++++------- .../rv32im/circuit/src/base_alu/tests.rs | 31 ++++++++++++++++++ 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/crates/vm/src/system/memory/online.rs b/crates/vm/src/system/memory/online.rs index eec3156ba2..a01e5e4f2c 100644 --- a/crates/vm/src/system/memory/online.rs +++ b/crates/vm/src/system/memory/online.rs @@ -337,18 +337,28 @@ impl TracingMemory { break false; } else if current_metadata.block_size == 0 { // Initialize - if self.initial_block_size > align { + if self.initial_block_size < align { + // Only happens in volatile, so empty initial memory + self.set_meta_block( + address_space, + cur_ptr * align, + align, + align, + INITIAL_TIMESTAMP, + ); + current_metadata = AccessMetadata::new(INITIAL_TIMESTAMP, align as u32); + } else { cur_ptr -= cur_ptr % (self.initial_block_size / align); + self.set_meta_block( + address_space, + cur_ptr * align, + align, + self.initial_block_size, + INITIAL_TIMESTAMP, + ); + current_metadata = + AccessMetadata::new(INITIAL_TIMESTAMP, self.initial_block_size as u32); } - self.set_meta_block( - address_space, - cur_ptr * align, - align, - self.initial_block_size, - INITIAL_TIMESTAMP, - ); - current_metadata = - AccessMetadata::new(INITIAL_TIMESTAMP, self.initial_block_size as u32); } prev_ts = prev_ts.max(current_metadata.timestamp); while current_metadata.block_size == AccessMetadata::OCCUPIED { @@ -370,7 +380,7 @@ impl TracingMemory { .collect::>(); self.execute_splits::(address, align, &values, current_metadata.timestamp); } - cur_ptr += current_metadata.block_size as usize; + cur_ptr += current_metadata.block_size as usize / align; }; if need_to_merge { // Merge diff --git a/extensions/rv32im/circuit/src/base_alu/tests.rs b/extensions/rv32im/circuit/src/base_alu/tests.rs index ff4ddbaae4..813eb34435 100644 --- a/extensions/rv32im/circuit/src/base_alu/tests.rs +++ b/extensions/rv32im/circuit/src/base_alu/tests.rs @@ -123,6 +123,37 @@ fn rand_rv32_alu_test(opcode: BaseAluOpcode, num_ops: usize) { let mut tester = VmChipTestBuilder::default(); let (mut chip, bitwise_chip) = create_test_chip(&tester); + // TODO(AG): make a more meaningful test for memory accesses + tester.write(2, 1024, [F::ONE; 4]); + tester.write(2, 1028, [F::ONE; 4]); + let sm = tester.read(2, 1024); + assert_eq!(sm, [F::ONE; 8]); + + for _ in 0..num_ops { + set_and_execute(&mut tester, &mut chip, &mut rng, opcode, None, None, None); + } + + let tester = tester.build().load(chip).load(bitwise_chip).finalize(); + tester.simple_test().expect("Verification failed"); +} + +#[test_case(ADD, 100)] +#[test_case(SUB, 100)] +#[test_case(XOR, 100)] +#[test_case(OR, 100)] +#[test_case(AND, 100)] +fn rand_rv32_alu_test_persistent(opcode: BaseAluOpcode, num_ops: usize) { + let mut rng = create_seeded_rng(); + + let mut tester = VmChipTestBuilder::default_persistent(); + let (mut chip, bitwise_chip) = create_test_chip(&tester); + + // TODO(AG): make a more meaningful test for memory accesses + tester.write(2, 1024, [F::ONE; 4]); + tester.write(2, 1028, [F::ONE; 4]); + let sm = tester.read(2, 1024); + assert_eq!(sm, [F::ONE; 8]); + for _ in 0..num_ops { set_and_execute(&mut tester, &mut chip, &mut rng, opcode, None, None, None); } From 164eb0e5c3c2b14bba00f7303b57569634c50c25 Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Thu, 15 May 2025 10:31:31 -0400 Subject: [PATCH 21/49] feat(new-execution): trigger codspeed ci on branch pushes (#1654) --- .github/workflows/benchmarks-execute.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks-execute.yml b/.github/workflows/benchmarks-execute.yml index 034d3e9954..6b043cc558 100644 --- a/.github/workflows/benchmarks-execute.yml +++ b/.github/workflows/benchmarks-execute.yml @@ -2,7 +2,8 @@ name: "benchmarks-execute" on: push: - branches: ["main"] + # TODO(ayush): remove after feat/new-execution is merged + branches: ["main", "feat/new-execution"] pull_request: types: [opened, synchronize, reopened, labeled] branches: ["**"] From 327fe86f0453b84092da69459f44eebb2b8bc6ae Mon Sep 17 00:00:00 2001 From: Golovanov399 Date: Thu, 15 May 2025 17:37:35 +0300 Subject: [PATCH 22/49] feat: new execution remove redundant controller memory e1 (#1653) This resolves INT-4012 by not using memory controller's memory in E1 execution. --- crates/vm/src/arch/execution_control.rs | 15 +++++++-------- crates/vm/src/arch/segment.rs | 13 +++++++++---- crates/vm/src/arch/vm.rs | 14 ++++++++++---- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/crates/vm/src/arch/execution_control.rs b/crates/vm/src/arch/execution_control.rs index fd0db751e9..ecb4d0ffdd 100644 --- a/crates/vm/src/arch/execution_control.rs +++ b/crates/vm/src/arch/execution_control.rs @@ -55,9 +55,9 @@ where /// Execute a single instruction // TODO(ayush): change instruction to Instruction / PInstruction - fn execute_instruction( + fn execute_instruction( &mut self, - vm_state: &mut ExecutionSegmentState, + vm_state: &mut ExecutionSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> @@ -153,9 +153,9 @@ where } /// Execute a single instruction - fn execute_instruction( + fn execute_instruction( &mut self, - state: &mut ExecutionSegmentState, + state: &mut ExecutionSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> @@ -235,9 +235,9 @@ where } /// Execute a single instruction - fn execute_instruction( + fn execute_instruction( &mut self, - state: &mut ExecutionSegmentState, + state: &mut ExecutionSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> @@ -247,10 +247,9 @@ where let &Instruction { opcode, .. } = instruction; if let Some(executor) = chip_complex.inventory.get_mut_executor(&opcode) { - let memory_controller = &mut chip_complex.base.memory_controller; let vm_state = VmStateMut { pc: &mut state.pc, - memory: &mut memory_controller.memory.data, + memory: state.memory.as_mut().unwrap(), ctx: &mut (), }; executor.execute_e1(vm_state, instruction)?; diff --git a/crates/vm/src/arch/segment.rs b/crates/vm/src/arch/segment.rs index f84ae78bc7..034236c833 100644 --- a/crates/vm/src/arch/segment.rs +++ b/crates/vm/src/arch/segment.rs @@ -60,7 +60,8 @@ pub type TracegenVmSegmentExecutor = VmSegmentExecutor, TracegenCtx, TracegenExecutionControl>; #[derive(derive_new::new)] -pub struct ExecutionSegmentState { +pub struct ExecutionSegmentState { + pub memory: Option, pub pc: u32, pub exit_code: u32, pub is_terminated: bool, @@ -122,13 +123,17 @@ where } /// Stopping is triggered by should_stop() or if VM is terminated - pub fn execute_from_pc(&mut self, pc: u32) -> Result { + pub fn execute_from_pc( + &mut self, + pc: u32, + memory: Option, + ) -> Result, ExecutionError> { let mut prev_backtrace: Option = None; // Call the pre-execution hook self.control.on_segment_start(pc, &mut self.chip_complex); - let mut state = ExecutionSegmentState::new(pc, 0, false); + let mut state = ExecutionSegmentState::new(memory, pc, 0, false); loop { // Fetch, decode and execute single instruction let terminated_exit_code = self.execute_instruction(&mut state, &mut prev_backtrace)?; @@ -155,7 +160,7 @@ where // TODO(ayush): clean this up, separate to smaller functions fn execute_instruction( &mut self, - state: &mut ExecutionSegmentState, + state: &mut ExecutionSegmentState, prev_backtrace: &mut Option, ) -> Result, ExecutionError> { let pc = state.pc; diff --git a/crates/vm/src/arch/vm.rs b/crates/vm/src/arch/vm.rs index 2743768a37..f94cdc9900 100644 --- a/crates/vm/src/arch/vm.rs +++ b/crates/vm/src/arch/vm.rs @@ -246,7 +246,9 @@ where if let Some(overridden_heights) = self.overridden_heights.as_ref() { segment.set_override_trace_heights(overridden_heights.clone()); } - let state = metrics_span("execute_time_ms", || segment.execute_from_pc(from_state.pc))?; + let state = metrics_span("execute_time_ms", || { + segment.execute_from_pc(from_state.pc, None) + })?; if state.is_terminated { return Ok(VmExecutorOneSegmentResult { @@ -339,7 +341,7 @@ where // TODO(ayush): avoid clones exe.program.clone(), state.input, - Some(state.memory), + None, self.trace_height_constraints.clone(), exe.fn_bounds.clone(), ); @@ -348,7 +350,9 @@ where segment.metrics = state.metrics; } - let exec_state = metrics_span("execute_time_ms", || segment.execute_from_pc(state.pc))?; + let exec_state = metrics_span("execute_time_ms", || { + segment.execute_from_pc(state.pc, Some(state.memory)) + })?; if exec_state.is_terminated { // Check exit code for the final segment @@ -561,7 +565,9 @@ where if let Some(overridden_heights) = self.overridden_heights.as_ref() { segment.set_override_trace_heights(overridden_heights.clone()); } - metrics_span("execute_time_ms", || segment.execute_from_pc(exe.pc_start))?; + metrics_span("execute_time_ms", || { + segment.execute_from_pc(exe.pc_start, None) + })?; Ok(segment) } } From d603ad09a68cecca409bfca6b6940bb3ae836eb8 Mon Sep 17 00:00:00 2001 From: Arayi Khalatyan <127004086+arayikhalatyan@users.noreply.github.com> Date: Thu, 15 May 2025 15:51:36 -0400 Subject: [PATCH 23/49] feat: mod builder rewrite (#1644) implemented e1 and e3 for `VecHeapTwoReads` and `eq_mod` rv32 adapters. Implemented e1 and e3 for mod-builder. Updated the `algebra` and `ecc` extensions accordingly. Deleted all the pairing chips All the tests successfully run. Also, added back the address space 4 loadstore tests. Resolves INT-3914 --- Cargo.lock | 1 + crates/circuits/mod-builder/src/core_chip.rs | 305 +++--- crates/vm/src/arch/integration_api.rs | 39 +- crates/vm/src/arch/testing/mod.rs | 13 +- extensions/algebra/circuit/Cargo.toml | 1 + .../algebra/circuit/src/fp2_chip/addsub.rs | 227 +++-- .../algebra/circuit/src/fp2_chip/mod.rs | 14 + .../algebra/circuit/src/fp2_chip/muldiv.rs | 240 +++-- .../algebra/circuit/src/fp2_extension.rs | 66 +- .../circuit/src/modular_chip/addsub.rs | 78 +- .../algebra/circuit/src/modular_chip/is_eq.rs | 275 +++--- .../algebra/circuit/src/modular_chip/mod.rs | 45 +- .../circuit/src/modular_chip/muldiv.rs | 79 +- .../algebra/circuit/src/modular_chip/tests.rs | 888 +++++++++--------- .../algebra/circuit/src/modular_extension.rs | 148 ++- extensions/bigint/circuit/src/extension.rs | 2 +- .../circuit/src/weierstrass_chip/add_ne.rs | 76 +- .../circuit/src/weierstrass_chip/double.rs | 76 +- .../ecc/circuit/src/weierstrass_chip/mod.rs | 120 +-- .../ecc/circuit/src/weierstrass_chip/tests.rs | 91 +- .../ecc/circuit/src/weierstrass_extension.rs | 87 +- .../pairing/circuit/src/fp12_chip/add.rs | 20 - .../pairing/circuit/src/fp12_chip/mod.rs | 10 - .../pairing/circuit/src/fp12_chip/mul.rs | 175 ---- .../pairing/circuit/src/fp12_chip/sub.rs | 20 - .../pairing/circuit/src/fp12_chip/tests.rs | 271 ------ extensions/pairing/circuit/src/lib.rs | 4 - .../src/pairing_chip/line/d_type/mod.rs | 8 - .../line/d_type/mul_013_by_013.rs | 101 -- .../pairing_chip/line/d_type/mul_by_01234.rs | 113 --- .../src/pairing_chip/line/d_type/tests.rs | 287 ------ .../src/pairing_chip/line/evaluate_line.rs | 102 -- .../src/pairing_chip/line/m_type/mod.rs | 8 - .../line/m_type/mul_023_by_023.rs | 101 -- .../pairing_chip/line/m_type/mul_by_02345.rs | 113 --- .../src/pairing_chip/line/m_type/tests.rs | 217 ----- .../circuit/src/pairing_chip/line/mod.rs | 7 - .../miller_double_and_add_step.rs | 215 ----- .../src/pairing_chip/miller_double_step.rs | 253 ----- .../pairing/circuit/src/pairing_chip/mod.rs | 8 - .../pairing/circuit/src/pairing_extension.rs | 13 +- extensions/pairing/transpiler/src/lib.rs | 64 +- extensions/rv32-adapters/src/eq_mod.rs | 311 +++--- extensions/rv32-adapters/src/heap.rs | 6 +- extensions/rv32-adapters/src/heap_branch.rs | 37 +- extensions/rv32-adapters/src/lib.rs | 8 +- extensions/rv32-adapters/src/vec_heap.rs | 79 +- .../rv32-adapters/src/vec_heap_two_reads.rs | 504 +++++----- .../rv32im/circuit/src/adapters/loadstore.rs | 42 +- .../rv32im/circuit/src/loadstore/tests.rs | 59 +- 50 files changed, 2165 insertions(+), 3862 deletions(-) delete mode 100644 extensions/pairing/circuit/src/fp12_chip/add.rs delete mode 100644 extensions/pairing/circuit/src/fp12_chip/mod.rs delete mode 100644 extensions/pairing/circuit/src/fp12_chip/mul.rs delete mode 100644 extensions/pairing/circuit/src/fp12_chip/sub.rs delete mode 100644 extensions/pairing/circuit/src/fp12_chip/tests.rs delete mode 100644 extensions/pairing/circuit/src/pairing_chip/line/d_type/mod.rs delete mode 100644 extensions/pairing/circuit/src/pairing_chip/line/d_type/mul_013_by_013.rs delete mode 100644 extensions/pairing/circuit/src/pairing_chip/line/d_type/mul_by_01234.rs delete mode 100644 extensions/pairing/circuit/src/pairing_chip/line/d_type/tests.rs delete mode 100644 extensions/pairing/circuit/src/pairing_chip/line/evaluate_line.rs delete mode 100644 extensions/pairing/circuit/src/pairing_chip/line/m_type/mod.rs delete mode 100644 extensions/pairing/circuit/src/pairing_chip/line/m_type/mul_023_by_023.rs delete mode 100644 extensions/pairing/circuit/src/pairing_chip/line/m_type/mul_by_02345.rs delete mode 100644 extensions/pairing/circuit/src/pairing_chip/line/m_type/tests.rs delete mode 100644 extensions/pairing/circuit/src/pairing_chip/line/mod.rs delete mode 100644 extensions/pairing/circuit/src/pairing_chip/miller_double_and_add_step.rs delete mode 100644 extensions/pairing/circuit/src/pairing_chip/miller_double_step.rs delete mode 100644 extensions/pairing/circuit/src/pairing_chip/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 3b73d08d99..b2eba1c80d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4674,6 +4674,7 @@ dependencies = [ "serde-big-array", "serde_with", "strum", + "test-case", ] [[package]] diff --git a/crates/circuits/mod-builder/src/core_chip.rs b/crates/circuits/mod-builder/src/core_chip.rs index 30e9c65dbb..4fd037fedb 100644 --- a/crates/circuits/mod-builder/src/core_chip.rs +++ b/crates/circuits/mod-builder/src/core_chip.rs @@ -1,24 +1,28 @@ use itertools::Itertools; use num_bigint::BigUint; use num_traits::Zero; -use openvm_circuit::arch::{ - AdapterAirContext, AdapterRuntimeContext, DynAdapterInterface, DynArray, MinimalInstruction, - Result, VmAdapterInterface, VmCoreAir, VmCoreChip, +use openvm_circuit::{ + arch::{ + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, DynAdapterInterface, DynArray, + MinimalInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, + VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, + }, }; use openvm_circuit_primitives::{ var_range::SharedVariableRangeCheckerChip, SubAir, TraceSubRowGenerator, }; -use openvm_instructions::instruction::Instruction; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP}; use openvm_stark_backend::{ interaction::InteractionBuilder, p3_air::BaseAir, p3_field::{Field, FieldAlgebra, PrimeField32}, - p3_matrix::{dense::RowMajorMatrix, Matrix}, rap::BaseAirWithPublicValues, }; use openvm_stark_sdk::p3_baby_bear::BabyBear; -use serde::{Deserialize, Serialize}; -use serde_with::{serde_as, DisplayFromStr}; use crate::{ utils::{biguint_to_limbs_vec, limbs_to_biguint}, @@ -165,27 +169,21 @@ where } } -#[serde_as] -#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] -pub struct FieldExpressionRecord { - #[serde_as(as = "Vec")] - pub inputs: Vec, - pub flags: Vec, -} - -pub struct FieldExpressionCoreChip { - pub air: FieldExpressionCoreAir, +// TODO(arayi): use lifetimes and references for fields +pub struct FieldExpressionStep { + adapter: A, + pub expr: FieldExpr, + pub offset: usize, + pub local_opcode_idx: Vec, + pub opcode_flag_idx: Vec, pub range_checker: SharedVariableRangeCheckerChip, - pub name: String, - - /// Whether to finalize the trace. True if all-zero rows don't satisfy the constraints (e.g. - /// there is int_add) pub should_finalize: bool, } -impl FieldExpressionCoreChip { +impl FieldExpressionStep { pub fn new( + adapter: A, expr: FieldExpr, offset: usize, local_opcode_idx: Vec, @@ -194,145 +192,192 @@ impl FieldExpressionCoreChip { name: &str, should_finalize: bool, ) -> Self { - let air = FieldExpressionCoreAir::new(expr, offset, local_opcode_idx, opcode_flag_idx); + let opcode_flag_idx = if opcode_flag_idx.is_empty() && expr.needs_setup() { + // single op chip that needs setup, so there is only one default flag, must be 0. + vec![0] + } else { + // multi ops chip or no-setup chip, use as is. + opcode_flag_idx + }; + assert_eq!(opcode_flag_idx.len(), local_opcode_idx.len() - 1); tracing::info!( - "FieldExpressionCoreChip: opcode={name}, main_width={}", - BaseAir::::width(&air) + "FieldExpressionCoreStep: opcode={name}, main_width={}", + BaseAir::::width(&expr) ); Self { - air, + adapter, + expr, + offset, + local_opcode_idx, + opcode_flag_idx, range_checker, name: name.to_string(), should_finalize, } } + pub fn num_inputs(&self) -> usize { + self.expr.builder.num_input + } + + pub fn num_vars(&self) -> usize { + self.expr.builder.num_variables + } + + pub fn num_flags(&self) -> usize { + self.expr.builder.num_flags + } - pub fn expr(&self) -> &FieldExpr { - &self.air.expr + pub fn output_indices(&self) -> &[usize] { + &self.expr.builder.output_indices } } -impl VmCoreChip for FieldExpressionCoreChip +impl TraceStep for FieldExpressionStep where - I: VmAdapterInterface, - I::Reads: Into>, - AdapterRuntimeContext: From>>, + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData: Into>, + WriteData: From>, + TraceContext<'a> = (), + >, { - type Record = FieldExpressionRecord; - type Air = FieldExpressionCoreAir; + fn get_opcode_name(&self, _opcode: usize) -> String { + self.name.clone() + } - fn execute_instruction( - &self, + fn execute( + &mut self, + state: VmStateMut, CTX>, instruction: &Instruction, - _from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { - let field_element_limbs = self.air.expr.canonical_num_limbs(); - let limb_bits = self.air.expr.canonical_limb_bits(); - let data: DynArray<_> = reads.into(); - let data = data.0; - assert_eq!(data.len(), self.air.num_inputs() * field_element_limbs); - let data_u32: Vec = data.iter().map(|x| x.as_canonical_u32()).collect(); - - let mut inputs = vec![]; - for i in 0..self.air.num_inputs() { - let start = i * field_element_limbs; - let end = start + field_element_limbs; - let limb_slice = &data_u32[start..end]; - let input = limbs_to_biguint(limb_slice, limb_bits); - inputs.push(input); - } + trace: &mut [F], + trace_offset: &mut usize, + width: usize, + ) -> Result<()> { + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let (adapter_row, core_row) = row_slice.split_at_mut(A::WIDTH); - let Instruction { opcode, .. } = instruction; - let local_opcode_idx = opcode.local_opcode_idx(self.air.offset); - let mut flags = vec![]; - - // If the chip doesn't need setup, (right now) it must be single op chip and thus no flag is - // needed. Otherwise, there is a flag for each opcode and will be derived by - // is_valid - sum(flags). - if self.expr().needs_setup() { - flags = vec![false; self.air.num_flags()]; - self.air - .opcode_flag_idx - .iter() - .enumerate() - .for_each(|(i, &flag_idx)| { - flags[flag_idx] = local_opcode_idx == self.air.local_opcode_idx[i] - }); - } + A::start(*state.pc, state.memory, adapter_row); - let vars = self.air.expr.execute(inputs.clone(), flags.clone()); - assert_eq!(vars.len(), self.air.num_vars()); + let data: DynArray<_> = self + .adapter + .read(state.memory, instruction, adapter_row) + .into(); - let outputs: Vec = self - .air - .output_indices() - .iter() - .map(|&i| vars[i].clone()) - .collect(); - let writes: Vec = outputs - .iter() - .map(|x| biguint_to_limbs_vec(x.clone(), limb_bits, field_element_limbs)) - .concat() - .into_iter() - .map(|x| F::from_canonical_u32(x)) - .collect(); + let (writes, inputs, flags) = run_field_expression(self, &data, instruction); - let ctx = AdapterRuntimeContext::<_, DynAdapterInterface<_>>::without_pc(writes); - Ok((ctx.into(), FieldExpressionRecord { inputs, flags })) - } + // TODO(arayi): Should move this to fill_trace_row + self.expr + .generate_subrow((self.range_checker.as_ref(), inputs, flags), core_row); - fn get_opcode_name(&self, _opcode: usize) -> String { - self.name.clone() - } + self.adapter + .write(state.memory, instruction, adapter_row, &writes.into()); - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - self.air.expr.generate_subrow( - (self.range_checker.as_ref(), record.inputs, record.flags), - row_slice, - ); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *trace_offset += width; + Ok(()) } - fn air(&self) -> &Self::Air { - &self.air + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row: &mut [F]) { + let (adapter_row, _) = row.split_at_mut(A::WIDTH); + self.adapter.fill_trace_row(mem_helper, (), adapter_row); } - fn finalize(&self, trace: &mut RowMajorMatrix, num_records: usize) { - if !self.should_finalize || num_records == 0 { - return; - } - - let core_width = >::width(&self.air); - let adapter_width = trace.width() - core_width; - let dummy_row = self.generate_dummy_trace_row(adapter_width, core_width); - for row in trace.rows_mut().skip(num_records) { - row.copy_from_slice(&dummy_row); - } - } -} - -impl FieldExpressionCoreChip { // We will be setting is_valid = 0. That forces all flags be 0 (otherwise setup will be -1). // We generate a dummy row with all flags set to 0, then we set is_valid = 0. - fn generate_dummy_trace_row( - &self, - adapter_width: usize, - core_width: usize, - ) -> Vec { - let record = FieldExpressionRecord { - inputs: vec![BigUint::zero(); self.air.num_inputs()], - flags: vec![false; self.air.num_flags()], - }; - let mut row = vec![F::ZERO; adapter_width + core_width]; - let core_row = &mut row[adapter_width..]; + fn fill_dummy_trace_row(&self, _mem_helper: &MemoryAuxColsFactory, row: &mut [F]) { + let inputs: Vec = vec![BigUint::zero(); self.num_inputs()]; + let flags: Vec = vec![false; self.num_flags()]; + let core_row = &mut row[A::WIDTH..]; // We **do not** want this trace row to update the range checker // so we must create a temporary range checker let tmp_range_checker = SharedVariableRangeCheckerChip::new(self.range_checker.bus()); - self.air.expr.generate_subrow( - (tmp_range_checker.as_ref(), record.inputs, record.flags), - core_row, - ); + self.expr + .generate_subrow((tmp_range_checker.as_ref(), inputs, flags), core_row); core_row[0] = F::ZERO; // is_valid = 0 - row } } + +impl StepExecutorE1 for FieldExpressionStep +where + F: PrimeField32, + A: 'static + + for<'a> AdapterExecutorE1>, WriteData: From>>, +{ + fn execute_e1( + &mut self, + state: VmStateMut, + instruction: &Instruction, + ) -> Result<()> + where + Mem: GuestMemory, + { + let data: DynArray<_> = self.adapter.read(state.memory, instruction).into(); + + let writes = run_field_expression(self, &data, instruction).0; + self.adapter + .write(state.memory, instruction, &writes.into()); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + Ok(()) + } +} + +fn run_field_expression( + step: &FieldExpressionStep, + data: &DynArray, + instruction: &Instruction, +) -> (DynArray, Vec, Vec) { + let field_element_limbs = step.expr.canonical_num_limbs(); + let limb_bits = step.expr.canonical_limb_bits(); + + let data = data.0.iter().map(|&x| x as u32).collect_vec(); + + assert_eq!(data.len(), step.num_inputs() * field_element_limbs); + + let mut inputs = Vec::with_capacity(step.num_inputs()); + for i in 0..step.num_inputs() { + let start = i * field_element_limbs; + let end = start + field_element_limbs; + let limb_slice = &data[start..end]; + let input = limbs_to_biguint(limb_slice, limb_bits); + inputs.push(input); + } + + let Instruction { opcode, .. } = instruction; + let local_opcode_idx = opcode.local_opcode_idx(step.offset); + let mut flags = vec![]; + + // If the chip doesn't need setup, (right now) it must be single op chip and thus no flag is + // needed. Otherwise, there is a flag for each opcode and will be derived by + // is_valid - sum(flags). + if step.expr.needs_setup() { + flags = vec![false; step.num_flags()]; + step.opcode_flag_idx + .iter() + .enumerate() + .for_each(|(i, &flag_idx)| { + flags[flag_idx] = local_opcode_idx == step.local_opcode_idx[i] + }); + } + + let vars = step.expr.execute(inputs.clone(), flags.clone()); + assert_eq!(vars.len(), step.num_vars()); + + let outputs: Vec = step + .output_indices() + .iter() + .map(|&i| vars[i].clone()) + .collect(); + let writes: DynArray<_> = outputs + .iter() + .map(|x| biguint_to_limbs_vec(x.clone(), limb_bits, field_element_limbs)) + .concat() + .into_iter() + .map(|x| x as u8) + .collect::>() + .into(); + + (writes, inputs, flags) +} diff --git a/crates/vm/src/arch/integration_api.rs b/crates/vm/src/arch/integration_api.rs index dbb18c4c41..c1754abc8e 100644 --- a/crates/vm/src/arch/integration_api.rs +++ b/crates/vm/src/arch/integration_api.rs @@ -172,14 +172,26 @@ pub trait TraceStep { /// [`TraceStep::execute`], so the `trace` should already contain context necessary to /// fill in the rest of it. // TODO(ayush): come up with a better abstraction for chips that fill a dynamic number of rows - fn fill_trace(&self, mem_helper: &MemoryAuxColsFactory, trace: &mut [F], width: usize) - where + fn fill_trace( + &self, + mem_helper: &MemoryAuxColsFactory, + trace: &mut [F], + width: usize, + rows_used: usize, + ) where Self: Send + Sync, F: Send + Sync, { - trace.par_chunks_exact_mut(width).for_each(|row_slice| { - self.fill_trace_row(mem_helper, row_slice); - }); + trace[..rows_used * width] + .par_chunks_exact_mut(width) + .for_each(|row_slice| { + self.fill_trace_row(mem_helper, row_slice); + }); + trace[rows_used * width..] + .par_chunks_exact_mut(width) + .for_each(|row_slice| { + self.fill_dummy_trace_row(mem_helper, row_slice); + }); } /// Populates `row_slice`. This function will always be called after @@ -192,6 +204,13 @@ pub trait TraceStep { unreachable!("fill_trace_row is not implemented") } + /// Populates `row_slice`. This function will be called on dummy rows. + /// By default the trace is padded with empty (all 0) rows to make the height a power of 2. + /// + /// The provided `row_slice` will have length equal to the width of the AIR. + fn fill_dummy_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + // By default, the row is filled with zeroes + } /// Returns a list of public values to publish. fn generate_public_values(&self) -> Vec { vec![] @@ -292,14 +311,8 @@ where assert!(height.checked_mul(self.width).unwrap() <= self.trace_buffer.len()); self.trace_buffer.truncate(height * self.width); let mem_helper = self.mem_helper.as_borrowed(); - // This zip only goes through used rows. - // TODO: check if zero-init assumption changes - // The padding(=dummy) rows between rows_used..height are ASSUMED to be filled with zeros. - self.step.fill_trace( - &mem_helper, - &mut self.trace_buffer[..rows_used * self.width], - self.width, - ); + self.step + .fill_trace(&mem_helper, &mut self.trace_buffer, self.width, rows_used); drop(self.mem_helper); let trace = RowMajorMatrix::new(self.trace_buffer, self.width); // self.inner.finalize(&mut trace, num_records); diff --git a/crates/vm/src/arch/testing/mod.rs b/crates/vm/src/arch/testing/mod.rs index 254c1096a0..bdd5691bce 100644 --- a/crates/vm/src/arch/testing/mod.rs +++ b/crates/vm/src/arch/testing/mod.rs @@ -155,8 +155,17 @@ impl VmChipTestBuilder { register, pointer.to_le_bytes().map(F::from_canonical_u8), ); - for (i, &write) in writes.iter().enumerate() { - self.write(2usize, pointer + i * NUM_LIMBS, write); + if NUM_LIMBS.is_power_of_two() { + for (i, &write) in writes.iter().enumerate() { + self.write(2usize, pointer + i * NUM_LIMBS, write); + } + } else { + for (i, &write) in writes.iter().enumerate() { + let ptr = pointer + i * NUM_LIMBS; + for j in (0..NUM_LIMBS).step_by(4) { + self.write::<4>(2usize, ptr + j, write[j..j + 4].try_into().unwrap()); + } + } } } diff --git a/extensions/algebra/circuit/Cargo.toml b/extensions/algebra/circuit/Cargo.toml index 7949fb0946..fdaab36ff3 100644 --- a/extensions/algebra/circuit/Cargo.toml +++ b/extensions/algebra/circuit/Cargo.toml @@ -37,3 +37,4 @@ openvm-mod-circuit-builder = { workspace = true, features = ["test-utils"] } openvm-circuit = { workspace = true, features = ["test-utils"] } openvm-rv32-adapters = { workspace = true, features = ["test-utils"] } openvm-pairing-guest = { workspace = true, features = ["halo2curves"] } +test-case = {workspace = true} \ No newline at end of file diff --git a/extensions/algebra/circuit/src/fp2_chip/addsub.rs b/extensions/algebra/circuit/src/fp2_chip/addsub.rs index 4eca1ad102..5d337a902e 100644 --- a/extensions/algebra/circuit/src/fp2_chip/addsub.rs +++ b/extensions/algebra/circuit/src/fp2_chip/addsub.rs @@ -1,62 +1,28 @@ -use std::{ - cell::RefCell, - rc::Rc, - sync::{Arc, Mutex}, -}; +use std::{cell::RefCell, rc::Rc}; use openvm_algebra_transpiler::Fp2Opcode; -use openvm_circuit::{arch::VmChipWrapper, system::memory::OfflineMemory}; -use openvm_circuit_derive::InstructionExecutor; -use openvm_circuit_primitives::var_range::{ - SharedVariableRangeCheckerChip, VariableRangeCheckerBus, +use openvm_circuit::{ + arch::ExecutionBridge, + system::memory::{offline_checker::MemoryBridge, SharedMemoryHelper}, +}; + +use openvm_circuit_derive::{InsExecutorE1, InstructionExecutor}; +use openvm_circuit_primitives::{ + bitwise_op_lookup::SharedBitwiseOperationLookupChip, + var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus}, + Chip, ChipUsageGetter, }; -use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; + +use openvm_instructions::riscv::RV32_CELL_BITS; use openvm_mod_circuit_builder::{ - ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreChip, + ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreAir, }; -use openvm_rv32_adapters::Rv32VecHeapAdapterChip; +use openvm_rv32_adapters::{Rv32VecHeapAdapterAir, Rv32VecHeapAdapterStep}; use openvm_stark_backend::p3_field::PrimeField32; use crate::Fp2; -// Input: Fp2 * 2 -// Output: Fp2 -#[derive(Chip, ChipUsageGetter, InstructionExecutor)] -pub struct Fp2AddSubChip( - pub VmChipWrapper< - F, - Rv32VecHeapAdapterChip, - FieldExpressionCoreChip, - >, -); - -impl - Fp2AddSubChip -{ - pub fn new( - adapter: Rv32VecHeapAdapterChip, - config: ExprBuilderConfig, - offset: usize, - range_checker: SharedVariableRangeCheckerChip, - offline_memory: Arc>>, - ) -> Self { - let (expr, is_add_flag, is_sub_flag) = fp2_addsub_expr(config, range_checker.bus()); - let core = FieldExpressionCoreChip::new( - expr, - offset, - vec![ - Fp2Opcode::ADD as usize, - Fp2Opcode::SUB as usize, - Fp2Opcode::SETUP_ADDSUB as usize, - ], - vec![is_add_flag, is_sub_flag], - range_checker, - "Fp2AddSub", - false, - ); - Self(VmChipWrapper::new(adapter, core, offline_memory)) - } -} +use super::{Fp2Air, Fp2Chip, Fp2Step}; pub fn fp2_addsub_expr( config: ExprBuilderConfig, @@ -85,11 +51,73 @@ pub fn fp2_addsub_expr( ) } +// Input: Fp2 * 2 +// Output: Fp2 +#[derive(Chip, ChipUsageGetter, InstructionExecutor, InsExecutorE1)] +pub struct Fp2AddSubChip( + pub Fp2Chip, +); + +impl + Fp2AddSubChip +{ + pub fn new( + execution_bridge: ExecutionBridge, + memory_bridge: MemoryBridge, + mem_helper: SharedMemoryHelper, + pointer_max_bits: usize, + config: ExprBuilderConfig, + offset: usize, + bitwise_lookup_chip: SharedBitwiseOperationLookupChip, + range_checker: SharedVariableRangeCheckerChip, + height: usize, + ) -> Self { + let (expr, is_add_flag, is_sub_flag) = fp2_addsub_expr(config, range_checker.bus()); + + let local_opcode_idx = vec![ + Fp2Opcode::ADD as usize, + Fp2Opcode::SUB as usize, + Fp2Opcode::SETUP_ADDSUB as usize, + ]; + let opcode_flag_idx = vec![is_add_flag, is_sub_flag]; + let air = Fp2Air::new( + Rv32VecHeapAdapterAir::new( + execution_bridge, + memory_bridge, + bitwise_lookup_chip.bus(), + pointer_max_bits, + ), + FieldExpressionCoreAir::new( + expr.clone(), + offset, + local_opcode_idx.clone(), + opcode_flag_idx.clone(), + ), + ); + + let step = Fp2Step::new( + Rv32VecHeapAdapterStep::new(pointer_max_bits, bitwise_lookup_chip), + expr, + offset, + local_opcode_idx, + opcode_flag_idx, + range_checker, + "Fp2AddSub", + false, + ); + Self(Fp2Chip::new(air, step, height, mem_helper)) + } + pub fn expr(&self) -> &FieldExpr { + &self.0.step.expr + } +} + #[cfg(test)] mod tests { use halo2curves_axiom::{bn256::Fq2, ff::Field}; use itertools::Itertools; + use num_bigint::BigUint; use openvm_algebra_transpiler::Fp2Opcode; use openvm_circuit::arch::testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}; use openvm_circuit_primitives::bitwise_op_lookup::{ @@ -101,52 +129,30 @@ mod tests { ExprBuilderConfig, }; use openvm_pairing_guest::bn254::BN254_MODULUS; - use openvm_rv32_adapters::{rv32_write_heap_default, Rv32VecHeapAdapterChip}; + use openvm_rv32_adapters::rv32_write_heap_default; use openvm_stark_backend::p3_field::FieldAlgebra; - use openvm_stark_sdk::p3_baby_bear::BabyBear; - use rand::{rngs::StdRng, SeedableRng}; + use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use super::Fp2AddSubChip; const NUM_LIMBS: usize = 32; const LIMB_BITS: usize = 8; + const MAX_INS_CAPACITY: usize = 128; + const OFFSET: usize = Fp2Opcode::CLASS_OFFSET; type F = BabyBear; - #[test] - fn test_fp2_addsub() { - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let modulus = BN254_MODULUS.clone(); - let config = ExprBuilderConfig { - modulus: modulus.clone(), - num_limbs: NUM_LIMBS, - limb_bits: LIMB_BITS, - }; - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let adapter = Rv32VecHeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), - ); - let mut chip = Fp2AddSubChip::new( - adapter, - config, - Fp2Opcode::CLASS_OFFSET, - tester.range_checker(), - tester.offline_memory_mutex_arc(), - ); - - let mut rng = StdRng::seed_from_u64(42); + fn set_and_execute_rand( + tester: &mut VmChipTestBuilder, + chip: &mut Fp2AddSubChip, + modulus: &BigUint, + ) { + let mut rng = create_seeded_rng(); let x = Fq2::random(&mut rng); let y = Fq2::random(&mut rng); let inputs = [x.c0, x.c1, y.c0, y.c1].map(bn254_fq_to_biguint); let expected_sum = bn254_fq2_to_biguint_vec(x + y); let r_sum = chip - .0 - .core .expr() .execute_with_output(inputs.to_vec(), vec![true, false]); assert_eq!(r_sum.len(), 2); @@ -155,8 +161,6 @@ mod tests { let expected_sub = bn254_fq2_to_biguint_vec(x - y); let r_sub = chip - .0 - .core .expr() .execute_with_output(inputs.to_vec(), vec![false, true]); assert_eq!(r_sub.len(), 2); @@ -177,30 +181,57 @@ mod tests { .map(BabyBear::from_canonical_u32) }) .collect_vec(); - let modulus = - biguint_to_limbs::(modulus, LIMB_BITS).map(BabyBear::from_canonical_u32); + let modulus = biguint_to_limbs::(modulus.clone(), LIMB_BITS) + .map(BabyBear::from_canonical_u32); let zero = [BabyBear::ZERO; NUM_LIMBS]; let setup_instruction = rv32_write_heap_default( - &mut tester, + tester, vec![modulus, zero], vec![zero; 2], - chip.0.core.air.offset + Fp2Opcode::SETUP_ADDSUB as usize, + OFFSET + Fp2Opcode::SETUP_ADDSUB as usize, ); let instruction1 = rv32_write_heap_default( - &mut tester, + tester, x_limbs.clone(), y_limbs.clone(), - chip.0.core.air.offset + Fp2Opcode::ADD as usize, + OFFSET + Fp2Opcode::ADD as usize, ); - let instruction2 = rv32_write_heap_default( - &mut tester, - x_limbs, - y_limbs, - chip.0.core.air.offset + Fp2Opcode::SUB as usize, + let instruction2 = + rv32_write_heap_default(tester, x_limbs, y_limbs, OFFSET + Fp2Opcode::SUB as usize); + + tester.execute(chip, &setup_instruction); + tester.execute(chip, &instruction1); + tester.execute(chip, &instruction2); + } + + #[test] + fn test_fp2_addsub() { + let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); + let modulus = BN254_MODULUS.clone(); + let config = ExprBuilderConfig { + modulus: modulus.clone(), + num_limbs: NUM_LIMBS, + limb_bits: LIMB_BITS, + }; + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + + let mut chip = Fp2AddSubChip::new( + tester.execution_bridge(), + tester.memory_bridge(), + tester.memory_helper(), + tester.address_bits(), + config, + OFFSET, + bitwise_chip.clone(), + tester.range_checker(), + MAX_INS_CAPACITY, ); - tester.execute(&mut chip, &setup_instruction); - tester.execute(&mut chip, &instruction1); - tester.execute(&mut chip, &instruction2); + + let num_ops = 10; + for _ in 0..num_ops { + set_and_execute_rand(&mut tester, &mut chip, &modulus); + } let tester = tester.build().load(chip).load(bitwise_chip).finalize(); tester.simple_test().expect("Verification failed"); } diff --git a/extensions/algebra/circuit/src/fp2_chip/mod.rs b/extensions/algebra/circuit/src/fp2_chip/mod.rs index cd316fd70c..a5bba6d9c0 100644 --- a/extensions/algebra/circuit/src/fp2_chip/mod.rs +++ b/extensions/algebra/circuit/src/fp2_chip/mod.rs @@ -3,3 +3,17 @@ pub use addsub::*; mod muldiv; pub use muldiv::*; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; +use openvm_mod_circuit_builder::{FieldExpressionCoreAir, FieldExpressionStep}; +use openvm_rv32_adapters::{Rv32VecHeapAdapterStep, Rv32VecHeapAdapterAir}; + +pub(crate) type Fp2Air = VmAirWrapper< + Rv32VecHeapAdapterAir<2, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE>, + FieldExpressionCoreAir, +>; + +pub(crate) type Fp2Step = + FieldExpressionStep>; + +pub(crate) type Fp2Chip = + NewVmChipWrapper, Fp2Step>; diff --git a/extensions/algebra/circuit/src/fp2_chip/muldiv.rs b/extensions/algebra/circuit/src/fp2_chip/muldiv.rs index 83ef9565f3..0683649ec6 100644 --- a/extensions/algebra/circuit/src/fp2_chip/muldiv.rs +++ b/extensions/algebra/circuit/src/fp2_chip/muldiv.rs @@ -1,62 +1,28 @@ -use std::{ - cell::RefCell, - rc::Rc, - sync::{Arc, Mutex}, -}; +use std::{cell::RefCell, rc::Rc}; use openvm_algebra_transpiler::Fp2Opcode; -use openvm_circuit::{arch::VmChipWrapper, system::memory::OfflineMemory}; -use openvm_circuit_derive::InstructionExecutor; -use openvm_circuit_primitives::var_range::{ - SharedVariableRangeCheckerChip, VariableRangeCheckerBus, +use openvm_circuit::{ + arch::ExecutionBridge, + system::memory::{offline_checker::MemoryBridge, SharedMemoryHelper}, +}; + +use openvm_circuit_derive::{InsExecutorE1, InstructionExecutor}; +use openvm_circuit_primitives::{ + bitwise_op_lookup::SharedBitwiseOperationLookupChip, + var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus}, + Chip, ChipUsageGetter, }; -use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; + +use openvm_instructions::riscv::RV32_CELL_BITS; use openvm_mod_circuit_builder::{ - ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreChip, SymbolicExpr, + ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreAir, SymbolicExpr, }; -use openvm_rv32_adapters::Rv32VecHeapAdapterChip; +use openvm_rv32_adapters::{Rv32VecHeapAdapterAir, Rv32VecHeapAdapterStep}; use openvm_stark_backend::p3_field::PrimeField32; use crate::Fp2; -// Input: Fp2 * 2 -// Output: Fp2 -#[derive(Chip, ChipUsageGetter, InstructionExecutor)] -pub struct Fp2MulDivChip( - pub VmChipWrapper< - F, - Rv32VecHeapAdapterChip, - FieldExpressionCoreChip, - >, -); - -impl - Fp2MulDivChip -{ - pub fn new( - adapter: Rv32VecHeapAdapterChip, - config: ExprBuilderConfig, - offset: usize, - range_checker: SharedVariableRangeCheckerChip, - offline_memory: Arc>>, - ) -> Self { - let (expr, is_mul_flag, is_div_flag) = fp2_muldiv_expr(config, range_checker.bus()); - let core = FieldExpressionCoreChip::new( - expr, - offset, - vec![ - Fp2Opcode::MUL as usize, - Fp2Opcode::DIV as usize, - Fp2Opcode::SETUP_MULDIV as usize, - ], - vec![is_mul_flag, is_div_flag], - range_checker, - "Fp2MulDiv", - false, - ); - Self(VmChipWrapper::new(adapter, core, offline_memory)) - } -} +use super::{Fp2Air, Fp2Chip, Fp2Step}; pub fn fp2_muldiv_expr( config: ExprBuilderConfig, @@ -124,11 +90,74 @@ pub fn fp2_muldiv_expr( ) } +// Input: Fp2 * 2 +// Output: Fp2 +#[derive(Chip, ChipUsageGetter, InstructionExecutor, InsExecutorE1)] +pub struct Fp2MulDivChip( + pub Fp2Chip, +); + +impl + Fp2MulDivChip +{ + pub fn new( + execution_bridge: ExecutionBridge, + memory_bridge: MemoryBridge, + mem_helper: SharedMemoryHelper, + pointer_max_bits: usize, + config: ExprBuilderConfig, + offset: usize, + bitwise_lookup_chip: SharedBitwiseOperationLookupChip, + range_checker: SharedVariableRangeCheckerChip, + height: usize, + ) -> Self { + let (expr, is_mul_flag, is_div_flag) = fp2_muldiv_expr(config, range_checker.bus()); + + let local_opcode_idx = vec![ + Fp2Opcode::MUL as usize, + Fp2Opcode::DIV as usize, + Fp2Opcode::SETUP_MULDIV as usize, + ]; + let opcode_flag_idx = vec![is_mul_flag, is_div_flag]; + let air = Fp2Air::new( + Rv32VecHeapAdapterAir::new( + execution_bridge, + memory_bridge, + bitwise_lookup_chip.bus(), + pointer_max_bits, + ), + FieldExpressionCoreAir::new( + expr.clone(), + offset, + local_opcode_idx.clone(), + opcode_flag_idx.clone(), + ), + ); + + let step = Fp2Step::new( + Rv32VecHeapAdapterStep::new(pointer_max_bits, bitwise_lookup_chip), + expr, + offset, + local_opcode_idx, + opcode_flag_idx, + range_checker, + "Fp2MulDiv", + false, + ); + Self(Fp2Chip::new(air, step, height, mem_helper)) + } + + pub fn expr(&self) -> &FieldExpr { + &self.0.step.expr + } +} + #[cfg(test)] mod tests { use halo2curves_axiom::{bn256::Fq2, ff::Field}; use itertools::Itertools; + use num_bigint::BigUint; use openvm_algebra_transpiler::Fp2Opcode; use openvm_circuit::arch::testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}; use openvm_circuit_primitives::bitwise_op_lookup::{ @@ -140,57 +169,30 @@ mod tests { ExprBuilderConfig, }; use openvm_pairing_guest::bn254::BN254_MODULUS; - use openvm_rv32_adapters::{rv32_write_heap_default, Rv32VecHeapAdapterChip}; + use openvm_rv32_adapters::rv32_write_heap_default; use openvm_stark_backend::p3_field::FieldAlgebra; - use openvm_stark_sdk::p3_baby_bear::BabyBear; - use rand::{rngs::StdRng, SeedableRng}; + use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; - use super::Fp2MulDivChip; + use crate::fp2_chip::Fp2MulDivChip; const NUM_LIMBS: usize = 32; const LIMB_BITS: usize = 8; + const OFFSET: usize = Fp2Opcode::CLASS_OFFSET; + const MAX_INS_CAPACITY: usize = 128; type F = BabyBear; - #[test] - fn test_fp2_muldiv() { - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let modulus = BN254_MODULUS.clone(); - let config = ExprBuilderConfig { - modulus: modulus.clone(), - num_limbs: NUM_LIMBS, - limb_bits: LIMB_BITS, - }; - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let adapter = Rv32VecHeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), - ); - let mut chip = Fp2MulDivChip::new( - adapter, - config, - Fp2Opcode::CLASS_OFFSET, - tester.range_checker(), - tester.offline_memory_mutex_arc(), - ); - assert_eq!( - chip.0.core.expr().builder.num_variables, - 2, - "Fp2MulDiv should only introduce new z Fp2 variable (2 Fp var)" - ); - - let mut rng = StdRng::seed_from_u64(42); + fn set_and_execute_rand( + tester: &mut VmChipTestBuilder, + chip: &mut Fp2MulDivChip, + modulus: &BigUint, + ) { + let mut rng = create_seeded_rng(); let x = Fq2::random(&mut rng); let y = Fq2::random(&mut rng); let inputs = [x.c0, x.c1, y.c0, y.c1].map(bn254_fq_to_biguint); let expected_mul = bn254_fq2_to_biguint_vec(x * y); let r_mul = chip - .0 - .core .expr() .execute_with_output(inputs.to_vec(), vec![true, false]); assert_eq!(r_mul.len(), 2); @@ -199,8 +201,6 @@ mod tests { let expected_div = bn254_fq2_to_biguint_vec(x * y.invert().unwrap()); let r_div = chip - .0 - .core .expr() .execute_with_output(inputs.to_vec(), vec![false, true]); assert_eq!(r_div.len(), 2); @@ -221,30 +221,62 @@ mod tests { .map(BabyBear::from_canonical_u32) }) .collect_vec(); - let modulus = - biguint_to_limbs::(modulus, LIMB_BITS).map(BabyBear::from_canonical_u32); + let modulus = biguint_to_limbs::(modulus.clone(), LIMB_BITS) + .map(BabyBear::from_canonical_u32); let zero = [BabyBear::ZERO; NUM_LIMBS]; let setup_instruction = rv32_write_heap_default( - &mut tester, + tester, vec![modulus, zero], vec![zero; 2], - chip.0.core.air.offset + Fp2Opcode::SETUP_MULDIV as usize, + OFFSET + Fp2Opcode::SETUP_MULDIV as usize, ); let instruction1 = rv32_write_heap_default( - &mut tester, + tester, x_limbs.clone(), y_limbs.clone(), - chip.0.core.air.offset + Fp2Opcode::MUL as usize, + OFFSET + Fp2Opcode::MUL as usize, ); - let instruction2 = rv32_write_heap_default( - &mut tester, - x_limbs, - y_limbs, - chip.0.core.air.offset + Fp2Opcode::DIV as usize, + let instruction2 = + rv32_write_heap_default(tester, x_limbs, y_limbs, OFFSET + Fp2Opcode::DIV as usize); + tester.execute(chip, &setup_instruction); + tester.execute(chip, &instruction1); + tester.execute(chip, &instruction2); + } + + #[test] + fn test_fp2_muldiv() { + let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); + let modulus = BN254_MODULUS.clone(); + let config = ExprBuilderConfig { + modulus: modulus.clone(), + num_limbs: NUM_LIMBS, + limb_bits: LIMB_BITS, + }; + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + + let mut chip = Fp2MulDivChip::new( + tester.execution_bridge(), + tester.memory_bridge(), + tester.memory_helper(), + tester.address_bits(), + config, + OFFSET, + bitwise_chip.clone(), + tester.range_checker(), + MAX_INS_CAPACITY, ); - tester.execute(&mut chip, &setup_instruction); - tester.execute(&mut chip, &instruction1); - tester.execute(&mut chip, &instruction2); + assert_eq!( + chip.expr().builder.num_variables, + 2, + "Fp2MulDiv should only introduce new z Fp2 variable (2 Fp var)" + ); + + let num_ops = 10; + for _ in 0..num_ops { + set_and_execute_rand(&mut tester, &mut chip, &modulus); + } + let tester = tester.build().load(chip).load(bitwise_chip).finalize(); tester.simple_test().expect("Verification failed"); } diff --git a/extensions/algebra/circuit/src/fp2_extension.rs b/extensions/algebra/circuit/src/fp2_extension.rs index 940ec4c864..6fa87a0943 100644 --- a/extensions/algebra/circuit/src/fp2_extension.rs +++ b/extensions/algebra/circuit/src/fp2_extension.rs @@ -2,17 +2,18 @@ use derive_more::derive::From; use num_bigint::BigUint; use openvm_algebra_transpiler::Fp2Opcode; use openvm_circuit::{ - arch::{SystemPort, VmExtension, VmInventory, VmInventoryBuilder, VmInventoryError}, + arch::{ + ExecutionBridge, SystemPort, VmExtension, VmInventory, VmInventoryBuilder, VmInventoryError, + }, system::phantom::PhantomChip, }; -use openvm_circuit_derive::{AnyEnum, InstructionExecutor}; +use openvm_circuit_derive::{AnyEnum, InsExecutorE1, InstructionExecutor}; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, }; use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; use openvm_instructions::{LocalOpcode, VmOpcode}; use openvm_mod_circuit_builder::ExprBuilderConfig; -use openvm_rv32_adapters::Rv32VecHeapAdapterChip; use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr}; @@ -20,6 +21,9 @@ use strum::EnumCount; use crate::fp2_chip::{Fp2AddSubChip, Fp2MulDivChip}; +// TODO: this should be decided after e2 execution +const MAX_INS_CAPACITY: usize = 1 << 22; + #[serde_as] #[derive(Clone, Debug, derive_new::new, Serialize, Deserialize)] pub struct Fp2Extension { @@ -27,7 +31,7 @@ pub struct Fp2Extension { pub supported_modulus: Vec, } -#[derive(ChipUsageGetter, Chip, InstructionExecutor, AnyEnum, From)] +#[derive(ChipUsageGetter, Chip, InstructionExecutor, InsExecutorE1, AnyEnum, From)] pub enum Fp2ExtensionExecutor { // 32 limbs prime Fp2AddSubRv32_32(Fp2AddSubChip), @@ -58,6 +62,11 @@ impl VmExtension for Fp2Extension { program_bus, memory_bridge, } = builder.system_port(); + + let execution_bridge = ExecutionBridge::new(execution_bus, program_bus); + let range_checker = builder.system_base().range_checker_chip.clone(); + let pointer_max_bits = builder.system_config().memory_config.pointer_max_bits; + let bitwise_lu_chip = if let Some(&chip) = builder .find_chip::>() .first() @@ -69,9 +78,6 @@ impl VmExtension for Fp2Extension { inventory.add_periphery_chip(chip.clone()); chip }; - let offline_memory = builder.system_base().offline_memory(); - let range_checker = builder.system_base().range_checker_chip.clone(); - let address_bits = builder.system_config().memory_config.pointer_max_bits; let addsub_opcodes = (Fp2Opcode::ADD as usize)..=(Fp2Opcode::SETUP_ADDSUB as usize); let muldiv_opcodes = (Fp2Opcode::MUL as usize)..=(Fp2Opcode::SETUP_MULDIV as usize); @@ -91,28 +97,18 @@ impl VmExtension for Fp2Extension { num_limbs: 48, limb_bits: 8, }; - let adapter_chip_32 = Rv32VecHeapAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - address_bits, - bitwise_lu_chip.clone(), - ); - let adapter_chip_48 = Rv32VecHeapAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - address_bits, - bitwise_lu_chip.clone(), - ); if bytes <= 32 { let addsub_chip = Fp2AddSubChip::new( - adapter_chip_32.clone(), + execution_bridge.clone(), + memory_bridge.clone(), + builder.system_base().memory_controller.helper(), + pointer_max_bits, config32.clone(), start_offset, + bitwise_lu_chip.clone(), range_checker.clone(), - offline_memory.clone(), + MAX_INS_CAPACITY, ); inventory.add_executor( Fp2ExtensionExecutor::Fp2AddSubRv32_32(addsub_chip), @@ -121,11 +117,15 @@ impl VmExtension for Fp2Extension { .map(|x| VmOpcode::from_usize(x + start_offset)), )?; let muldiv_chip = Fp2MulDivChip::new( - adapter_chip_32.clone(), + execution_bridge.clone(), + memory_bridge.clone(), + builder.system_base().memory_controller.helper(), + pointer_max_bits, config32.clone(), start_offset, + bitwise_lu_chip.clone(), range_checker.clone(), - offline_memory.clone(), + MAX_INS_CAPACITY, ); inventory.add_executor( Fp2ExtensionExecutor::Fp2MulDivRv32_32(muldiv_chip), @@ -135,11 +135,15 @@ impl VmExtension for Fp2Extension { )?; } else if bytes <= 48 { let addsub_chip = Fp2AddSubChip::new( - adapter_chip_48.clone(), + execution_bridge.clone(), + memory_bridge.clone(), + builder.system_base().memory_controller.helper(), + pointer_max_bits, config48.clone(), start_offset, + bitwise_lu_chip.clone(), range_checker.clone(), - offline_memory.clone(), + MAX_INS_CAPACITY, ); inventory.add_executor( Fp2ExtensionExecutor::Fp2AddSubRv32_48(addsub_chip), @@ -148,11 +152,15 @@ impl VmExtension for Fp2Extension { .map(|x| VmOpcode::from_usize(x + start_offset)), )?; let muldiv_chip = Fp2MulDivChip::new( - adapter_chip_48.clone(), + execution_bridge.clone(), + memory_bridge.clone(), + builder.system_base().memory_controller.helper(), + pointer_max_bits, config48.clone(), start_offset, + bitwise_lu_chip.clone(), range_checker.clone(), - offline_memory.clone(), + MAX_INS_CAPACITY, ); inventory.add_executor( Fp2ExtensionExecutor::Fp2MulDivRv32_48(muldiv_chip), diff --git a/extensions/algebra/circuit/src/modular_chip/addsub.rs b/extensions/algebra/circuit/src/modular_chip/addsub.rs index 34bede150f..fd02bf1b1f 100644 --- a/extensions/algebra/circuit/src/modular_chip/addsub.rs +++ b/extensions/algebra/circuit/src/modular_chip/addsub.rs @@ -1,22 +1,25 @@ -use std::{ - cell::RefCell, - rc::Rc, - sync::{Arc, Mutex}, -}; +use std::{cell::RefCell, rc::Rc}; use openvm_algebra_transpiler::Rv32ModularArithmeticOpcode; -use openvm_circuit::{arch::VmChipWrapper, system::memory::OfflineMemory}; -use openvm_circuit_derive::InstructionExecutor; -use openvm_circuit_primitives::var_range::{ - SharedVariableRangeCheckerChip, VariableRangeCheckerBus, +use openvm_circuit::{ + arch::ExecutionBridge, + system::memory::{offline_checker::MemoryBridge, SharedMemoryHelper}, +}; +use openvm_circuit_derive::{InsExecutorE1, InstructionExecutor}; +use openvm_circuit_primitives::{ + bitwise_op_lookup::SharedBitwiseOperationLookupChip, + var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus}, + Chip, ChipUsageGetter, }; -use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; +use openvm_instructions::riscv::RV32_CELL_BITS; use openvm_mod_circuit_builder::{ - ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreChip, FieldVariable, + ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreAir, FieldVariable, }; -use openvm_rv32_adapters::Rv32VecHeapAdapterChip; +use openvm_rv32_adapters::{Rv32VecHeapAdapterStep, Rv32VecHeapAdapterAir}; use openvm_stark_backend::p3_field::PrimeField32; +use super::{ModularAir, ModularChip, ModularStep}; + pub fn addsub_expr( config: ExprBuilderConfig, range_bus: VariableRangeCheckerBus, @@ -43,39 +46,58 @@ pub fn addsub_expr( ) } -#[derive(Chip, ChipUsageGetter, InstructionExecutor)] +#[derive(Chip, ChipUsageGetter, InstructionExecutor, InsExecutorE1)] pub struct ModularAddSubChip( - pub VmChipWrapper< - F, - Rv32VecHeapAdapterChip, - FieldExpressionCoreChip, - >, + pub ModularChip, ); impl ModularAddSubChip { pub fn new( - adapter: Rv32VecHeapAdapterChip, + execution_bridge: ExecutionBridge, + memory_bridge: MemoryBridge, + mem_helper: SharedMemoryHelper, + pointer_max_bits: usize, config: ExprBuilderConfig, offset: usize, + bitwise_lookup_chip: SharedBitwiseOperationLookupChip, range_checker: SharedVariableRangeCheckerChip, - offline_memory: Arc>>, + height: usize, ) -> Self { let (expr, is_add_flag, is_sub_flag) = addsub_expr(config, range_checker.bus()); - let core = FieldExpressionCoreChip::new( + + let local_opcode_idx = vec![ + Rv32ModularArithmeticOpcode::ADD as usize, + Rv32ModularArithmeticOpcode::SUB as usize, + Rv32ModularArithmeticOpcode::SETUP_ADDSUB as usize, + ]; + let opcode_flag_idx = vec![is_add_flag, is_sub_flag]; + let air = ModularAir::new( + Rv32VecHeapAdapterAir::new( + execution_bridge, + memory_bridge, + bitwise_lookup_chip.bus(), + pointer_max_bits, + ), + FieldExpressionCoreAir::new( + expr.clone(), + offset, + local_opcode_idx.clone(), + opcode_flag_idx.clone(), + ), + ); + + let step = ModularStep::new( + Rv32VecHeapAdapterStep::new(pointer_max_bits, bitwise_lookup_chip), expr, offset, - vec![ - Rv32ModularArithmeticOpcode::ADD as usize, - Rv32ModularArithmeticOpcode::SUB as usize, - Rv32ModularArithmeticOpcode::SETUP_ADDSUB as usize, - ], - vec![is_add_flag, is_sub_flag], + local_opcode_idx, + opcode_flag_idx, range_checker, "ModularAddSub", false, ); - Self(VmChipWrapper::new(adapter, core, offline_memory)) + Self(ModularChip::new(air, step, height, mem_helper)) } } diff --git a/extensions/algebra/circuit/src/modular_chip/is_eq.rs b/extensions/algebra/circuit/src/modular_chip/is_eq.rs index 57187d0ce2..5b1c03430b 100644 --- a/extensions/algebra/circuit/src/modular_chip/is_eq.rs +++ b/extensions/algebra/circuit/src/modular_chip/is_eq.rs @@ -7,10 +7,13 @@ use num_bigint::BigUint; use openvm_algebra_transpiler::Rv32ModularArithmeticOpcode; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, InsExecutorE1, MinimalInstruction, Result, - VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, + StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ bigint::utils::big_uint_to_limbs, @@ -19,21 +22,20 @@ use openvm_circuit_primitives::{ SubAir, TraceSubRowGenerator, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; use openvm_stark_backend::{ interaction::InteractionBuilder, p3_air::{AirBuilder, BaseAir}, p3_field::{Field, FieldAlgebra, PrimeField32}, rap::BaseAirWithPublicValues, }; -use serde::{Deserialize, Serialize}; -use serde_big_array::BigArray; + // Given two numbers b and c, we want to prove that a) b == c or b != c, depending on // result of cmp_result and b) b, c < N for some modulus N that is passed into the AIR // at runtime (i.e. when chip is instantiated). #[repr(C)] -#[derive(AlignedBorrow)] +#[derive(AlignedBorrow, Debug)] pub struct ModularIsEqualCoreCols { pub is_valid: T, pub is_setup: T, @@ -280,171 +282,188 @@ where } } -#[repr(C)] -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct ModularIsEqualCoreRecord { - #[serde(with = "BigArray")] - pub b: [T; READ_LIMBS], - #[serde(with = "BigArray")] - pub c: [T; READ_LIMBS], - pub cmp_result: T, - #[serde(with = "BigArray")] - pub eq_marker: [T; READ_LIMBS], - pub b_diff_idx: usize, - pub c_diff_idx: usize, - pub is_setup: bool, -} - -pub struct ModularIsEqualCoreChip< +#[derive(derive_new::new)] +pub struct ModularIsEqualStep< + A, const READ_LIMBS: usize, const WRITE_LIMBS: usize, const LIMB_BITS: usize, > { - pub air: ModularIsEqualCoreAir, + adapter: A, + pub modulus_limbs: [u8; READ_LIMBS], + pub offset: usize, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, } -impl - ModularIsEqualCoreChip -{ - pub fn new( - modulus: BigUint, - bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - offset: usize, - ) -> Self { - Self { - air: ModularIsEqualCoreAir::new(modulus, bitwise_lookup_chip.bus(), offset), - bitwise_lookup_chip, - } - } -} - -impl< - F: PrimeField32, - I: VmAdapterInterface, - const READ_LIMBS: usize, - const WRITE_LIMBS: usize, - const LIMB_BITS: usize, - > VmCoreChip for ModularIsEqualCoreChip +impl + TraceStep for ModularIsEqualStep where - I::Reads: Into<[[F; READ_LIMBS]; 2]>, - I::Writes: From<[[F; WRITE_LIMBS]; 1]>, + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData: Into<[[u8; READ_LIMBS]; 2]>, + WriteData: From<[u8; WRITE_LIMBS]>, + TraceContext<'a> = (), + >, { - type Record = ModularIsEqualCoreRecord; - type Air = ModularIsEqualCoreAir; - - #[allow(clippy::type_complexity)] - fn execute_instruction( - &self, + fn execute( + &mut self, + state: VmStateMut, CTX>, instruction: &Instruction, - _from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { - let data: [[F; READ_LIMBS]; 2] = reads.into(); - let b = data[0].map(|x| x.as_canonical_u32()); - let c = data[1].map(|y| y.as_canonical_u32()); - let (b_cmp, b_diff_idx) = run_unsigned_less_than::(&b, &self.air.modulus_limbs); - let (c_cmp, c_diff_idx) = run_unsigned_less_than::(&c, &self.air.modulus_limbs); - let is_setup = instruction.opcode.local_opcode_idx(self.air.offset) + trace: &mut [F], + trace_offset: &mut usize, + width: usize, + ) -> Result<()> { + let Instruction { opcode, .. } = instruction; + + let local_opcode = + Rv32ModularArithmeticOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + matches!( + local_opcode, + Rv32ModularArithmeticOpcode::IS_EQ | Rv32ModularArithmeticOpcode::SETUP_ISEQ + ); + + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let (adapter_row, core_row) = row_slice.split_at_mut(A::WIDTH); + + let cols: &mut ModularIsEqualCoreCols = core_row.borrow_mut(); + + A::start(*state.pc, state.memory, adapter_row); + let [b, c] = self + .adapter + .read(state.memory, instruction, adapter_row) + .into(); + + cols.b = b.map(F::from_canonical_u8); + cols.c = c.map(F::from_canonical_u8); + + let (b_cmp, _) = run_unsigned_less_than::(&b, &self.modulus_limbs); + let (c_cmp, _) = run_unsigned_less_than::(&c, &self.modulus_limbs); + let is_setup = instruction.opcode.local_opcode_idx(self.offset) == Rv32ModularArithmeticOpcode::SETUP_ISEQ as usize; + cols.is_setup = F::from_bool(is_setup); + if !is_setup { - assert!(b_cmp, "{:?} >= {:?}", b, self.air.modulus_limbs); - } - assert!(c_cmp, "{:?} >= {:?}", c, self.air.modulus_limbs); - if !is_setup { - self.bitwise_lookup_chip.request_range( - self.air.modulus_limbs[b_diff_idx] - b[b_diff_idx] - 1, - self.air.modulus_limbs[c_diff_idx] - c[c_diff_idx] - 1, - ); + assert!(b_cmp, "{:?} >= {:?}", b, self.modulus_limbs); } + assert!(c_cmp, "{:?} >= {:?}", c, self.modulus_limbs); - let mut eq_marker = [F::ZERO; READ_LIMBS]; - let mut cmp_result = F::ZERO; - self.air - .subair - .generate_subrow((&data[0], &data[1]), (&mut eq_marker, &mut cmp_result)); - - let mut writes = [F::ZERO; WRITE_LIMBS]; - writes[0] = cmp_result; - - let output = AdapterRuntimeContext::without_pc([writes]); - let record = ModularIsEqualCoreRecord { - is_setup, - b: data[0], - c: data[1], - cmp_result, - eq_marker, - b_diff_idx, - c_diff_idx, - }; + let mut write_data = [0u8; WRITE_LIMBS]; + write_data[0] = (b == c) as u8; + self.adapter + .write(state.memory, instruction, adapter_row, &write_data.into()); - Ok((output, record)) - } + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *trace_offset += width; - fn get_opcode_name(&self, opcode: usize) -> String { - format!( - "{:?}", - Rv32ModularArithmeticOpcode::from_usize(opcode - self.air.offset) - ) + Ok(()) } - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - let row_slice: &mut ModularIsEqualCoreCols<_, READ_LIMBS> = row_slice.borrow_mut(); - row_slice.is_valid = F::ONE; - row_slice.is_setup = F::from_bool(record.is_setup); - row_slice.b = record.b; - row_slice.c = record.c; - row_slice.cmp_result = record.cmp_result; + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, core_row) = row_slice.split_at_mut(A::WIDTH); + self.adapter.fill_trace_row(mem_helper, (), adapter_row); + let cols: &mut ModularIsEqualCoreCols = core_row.borrow_mut(); - row_slice.eq_marker = record.eq_marker; - - if !record.is_setup { - row_slice.b_lt_diff = F::from_canonical_u32(self.air.modulus_limbs[record.b_diff_idx]) - - record.b[record.b_diff_idx]; + cols.is_valid = F::ONE; + let sub_air = IsEqArraySubAir::; + sub_air.generate_subrow( + (&cols.b, &cols.c), + (&mut cols.eq_marker, &mut cols.cmp_result), + ); + let b = cols.b.map(|x| x.as_canonical_u32() as u8); + let c = cols.c.map(|x| x.as_canonical_u32() as u8); + let (_, b_diff_idx) = run_unsigned_less_than::(&b, &self.modulus_limbs); + let (_, c_diff_idx) = run_unsigned_less_than::(&c, &self.modulus_limbs); + + if cols.is_setup != F::ONE { + cols.b_lt_diff = + F::from_canonical_u8(self.modulus_limbs[b_diff_idx]) - cols.b[b_diff_idx]; + self.bitwise_lookup_chip.request_range( + (self.modulus_limbs[b_diff_idx] - b[b_diff_idx] - 1) as u32, + (self.modulus_limbs[c_diff_idx] - c[c_diff_idx] - 1) as u32, + ); } - row_slice.c_lt_diff = F::from_canonical_u32(self.air.modulus_limbs[record.c_diff_idx]) - - record.c[record.c_diff_idx]; - row_slice.c_lt_mark = if record.b_diff_idx == record.c_diff_idx { + cols.c_lt_diff = F::from_canonical_u8(self.modulus_limbs[c_diff_idx]) - cols.c[c_diff_idx]; + cols.c_lt_mark = if b_diff_idx == c_diff_idx { F::ONE } else { F::from_canonical_u8(2) }; - row_slice.lt_marker = from_fn(|i| { - if i == record.b_diff_idx { + cols.lt_marker = from_fn(|i| { + if i == b_diff_idx { F::ONE - } else if i == record.c_diff_idx { - row_slice.c_lt_mark + } else if i == c_diff_idx { + cols.c_lt_mark } else { F::ZERO } }); } - fn air(&self) -> &Self::Air { - &self.air + fn get_opcode_name(&self, opcode: usize) -> String { + format!( + "{:?}", + Rv32ModularArithmeticOpcode::from_usize(opcode - self.offset) + ) } } -impl - InsExecutorE1 for ModularIsEqualCoreChip +impl + StepExecutorE1 for ModularIsEqualStep where - Mem: GuestMemory, F: PrimeField32, + A: 'static + + for<'a> AdapterExecutorE1< + F, + ReadData: Into<[[u8; READ_LIMBS]; 2]>, + WriteData: From<[u8; WRITE_LIMBS]>, + >, { - fn execute_e1( + fn execute_e1( &mut self, - _state: &mut VmExecutionState, - _instruction: &Instruction, - ) -> Result<()> { - todo!("Implement execute_e1") + state: VmStateMut, + instruction: &Instruction, + ) -> Result<()> + where + Mem: GuestMemory, + { + let Instruction { opcode, .. } = instruction; + + let local_opcode = + Rv32ModularArithmeticOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + matches!( + local_opcode, + Rv32ModularArithmeticOpcode::IS_EQ | Rv32ModularArithmeticOpcode::SETUP_ISEQ + ); + + let [b, c] = self.adapter.read(state.memory, instruction).into(); + let (b_cmp, _) = run_unsigned_less_than::(&b, &self.modulus_limbs); + let (c_cmp, _) = run_unsigned_less_than::(&c, &self.modulus_limbs); + let is_setup = instruction.opcode.local_opcode_idx(self.offset) + == Rv32ModularArithmeticOpcode::SETUP_ISEQ as usize; + + if !is_setup { + assert!(b_cmp, "{:?} >= {:?}", b, self.modulus_limbs); + } + assert!(c_cmp, "{:?} >= {:?}", c, self.modulus_limbs); + + let mut write_data = [0u8; WRITE_LIMBS]; + write_data[0] = (b == c) as u8; + + self.adapter + .write(state.memory, instruction, &write_data.into()); + + Ok(()) } } // Returns (cmp_result, diff_idx) pub(super) fn run_unsigned_less_than( - x: &[u32; NUM_LIMBS], - y: &[u32; NUM_LIMBS], + x: &[u8; NUM_LIMBS], + y: &[u8; NUM_LIMBS], ) -> (bool, usize) { for i in (0..NUM_LIMBS).rev() { if x[i] != y[i] { diff --git a/extensions/algebra/circuit/src/modular_chip/mod.rs b/extensions/algebra/circuit/src/modular_chip/mod.rs index 2dd9838206..dc9fbe6342 100644 --- a/extensions/algebra/circuit/src/modular_chip/mod.rs +++ b/extensions/algebra/circuit/src/modular_chip/mod.rs @@ -4,21 +4,56 @@ mod is_eq; pub use is_eq::*; mod muldiv; pub use muldiv::*; -use openvm_circuit::arch::VmChipWrapper; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; use openvm_instructions::riscv::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; -use openvm_rv32_adapters::Rv32IsEqualModAdapterChip; +use openvm_mod_circuit_builder::{FieldExpressionCoreAir, FieldExpressionStep}; +use openvm_rv32_adapters::{ + Rv32VecHeapAdapterStep, Rv32IsEqualModAdapterAir, Rv32IsEqualModeAdapterStep, + Rv32VecHeapAdapterAir, +}; #[cfg(test)] mod tests; +pub(crate) type ModularAir = VmAirWrapper< + Rv32VecHeapAdapterAir<2, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE>, + FieldExpressionCoreAir, +>; + +pub(crate) type ModularStep = + FieldExpressionStep>; + +pub(crate) type ModularChip = + NewVmChipWrapper, ModularStep>; + // Must have TOTAL_LIMBS = NUM_LANES * LANE_SIZE +pub type ModularIsEqualAir< + const NUM_LANES: usize, + const LANE_SIZE: usize, + const TOTAL_LIMBS: usize, +> = VmAirWrapper< + Rv32IsEqualModAdapterAir<2, NUM_LANES, LANE_SIZE, TOTAL_LIMBS>, + ModularIsEqualCoreAir, +>; + +pub type VmModularIsEqualStep< + const NUM_LANES: usize, + const LANE_SIZE: usize, + const TOTAL_LIMBS: usize, +> = ModularIsEqualStep< + Rv32IsEqualModeAdapterStep<2, NUM_LANES, LANE_SIZE, TOTAL_LIMBS>, + TOTAL_LIMBS, + RV32_REGISTER_NUM_LIMBS, + RV32_CELL_BITS, +>; + pub type ModularIsEqualChip< F, const NUM_LANES: usize, const LANE_SIZE: usize, const TOTAL_LIMBS: usize, -> = VmChipWrapper< +> = NewVmChipWrapper< F, - Rv32IsEqualModAdapterChip, - ModularIsEqualCoreChip, + ModularIsEqualAir, + VmModularIsEqualStep, >; diff --git a/extensions/algebra/circuit/src/modular_chip/muldiv.rs b/extensions/algebra/circuit/src/modular_chip/muldiv.rs index 30f063e2b1..1685b114a1 100644 --- a/extensions/algebra/circuit/src/modular_chip/muldiv.rs +++ b/extensions/algebra/circuit/src/modular_chip/muldiv.rs @@ -1,22 +1,26 @@ -use std::{ - cell::RefCell, - rc::Rc, - sync::{Arc, Mutex}, -}; +use std::{cell::RefCell, rc::Rc}; use openvm_algebra_transpiler::Rv32ModularArithmeticOpcode; -use openvm_circuit::{arch::VmChipWrapper, system::memory::OfflineMemory}; -use openvm_circuit_derive::InstructionExecutor; -use openvm_circuit_primitives::var_range::{ - SharedVariableRangeCheckerChip, VariableRangeCheckerBus, +use openvm_circuit::{ + arch::ExecutionBridge, + system::memory::{offline_checker::MemoryBridge, SharedMemoryHelper}, +}; + +use openvm_circuit_derive::{InsExecutorE1, InstructionExecutor}; +use openvm_circuit_primitives::{ + bitwise_op_lookup::SharedBitwiseOperationLookupChip, + var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus}, + Chip, ChipUsageGetter, }; -use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; +use openvm_instructions::riscv::RV32_CELL_BITS; use openvm_mod_circuit_builder::{ - ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreChip, FieldVariable, SymbolicExpr, + ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreAir, FieldVariable, SymbolicExpr, }; -use openvm_rv32_adapters::Rv32VecHeapAdapterChip; +use openvm_rv32_adapters::{Rv32VecHeapAdapterStep, Rv32VecHeapAdapterAir}; use openvm_stark_backend::p3_field::PrimeField32; +use super::{ModularAir, ModularChip, ModularStep}; + pub fn muldiv_expr( config: ExprBuilderConfig, range_bus: VariableRangeCheckerBus, @@ -58,39 +62,58 @@ pub fn muldiv_expr( ) } -#[derive(Chip, ChipUsageGetter, InstructionExecutor)] +#[derive(Chip, ChipUsageGetter, InstructionExecutor, InsExecutorE1)] pub struct ModularMulDivChip( - pub VmChipWrapper< - F, - Rv32VecHeapAdapterChip, - FieldExpressionCoreChip, - >, + pub ModularChip, ); impl ModularMulDivChip { pub fn new( - adapter: Rv32VecHeapAdapterChip, + execution_bridge: ExecutionBridge, + memory_bridge: MemoryBridge, + mem_helper: SharedMemoryHelper, + pointer_max_bits: usize, config: ExprBuilderConfig, offset: usize, + bitwise_lookup_chip: SharedBitwiseOperationLookupChip, range_checker: SharedVariableRangeCheckerChip, - offline_memory: Arc>>, + height: usize, ) -> Self { let (expr, is_mul_flag, is_div_flag) = muldiv_expr(config, range_checker.bus()); - let core = FieldExpressionCoreChip::new( + + let local_opcode_idx = vec![ + Rv32ModularArithmeticOpcode::MUL as usize, + Rv32ModularArithmeticOpcode::DIV as usize, + Rv32ModularArithmeticOpcode::SETUP_MULDIV as usize, + ]; + let opcode_flag_idx = vec![is_mul_flag, is_div_flag]; + let air = ModularAir::new( + Rv32VecHeapAdapterAir::new( + execution_bridge, + memory_bridge, + bitwise_lookup_chip.bus(), + pointer_max_bits, + ), + FieldExpressionCoreAir::new( + expr.clone(), + offset, + local_opcode_idx.clone(), + opcode_flag_idx.clone(), + ), + ); + + let step = ModularStep::new( + Rv32VecHeapAdapterStep::new(pointer_max_bits, bitwise_lookup_chip), expr, offset, - vec![ - Rv32ModularArithmeticOpcode::MUL as usize, - Rv32ModularArithmeticOpcode::DIV as usize, - Rv32ModularArithmeticOpcode::SETUP_MULDIV as usize, - ], - vec![is_mul_flag, is_div_flag], + local_opcode_idx, + opcode_flag_idx, range_checker, "ModularMulDiv", false, ); - Self(VmChipWrapper::new(adapter, core, offline_memory)) + Self(ModularChip::new(air, step, height, mem_helper)) } } diff --git a/extensions/algebra/circuit/src/modular_chip/tests.rs b/extensions/algebra/circuit/src/modular_chip/tests.rs index 86df6161f3..720ffe76ca 100644 --- a/extensions/algebra/circuit/src/modular_chip/tests.rs +++ b/extensions/algebra/circuit/src/modular_chip/tests.rs @@ -3,14 +3,9 @@ use std::{array::from_fn, borrow::BorrowMut}; use num_bigint::BigUint; use num_traits::Zero; use openvm_algebra_transpiler::Rv32ModularArithmeticOpcode; -use openvm_circuit::{ - arch::{ - instructions::LocalOpcode, - testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - AdapterRuntimeContext, InsExecutorE1, Result, VmAdapterInterface, VmChipWrapper, - VmCoreChip, VmExecutionState, - }, - system::memory::online::GuestMemory, +use openvm_circuit::arch::{ + instructions::LocalOpcode, + testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, }; use openvm_circuit_primitives::{ bigint::utils::{big_uint_to_limbs, secp256k1_coord_prime, secp256k1_scalar_prime}, @@ -22,105 +17,61 @@ use openvm_mod_circuit_builder::{ ExprBuilderConfig, }; use openvm_pairing_guest::bls12_381::BLS12_381_MODULUS; -use openvm_rv32_adapters::{ - rv32_write_heap_default, write_ptr_reg, Rv32IsEqualModAdapterChip, Rv32VecHeapAdapterChip, -}; +use openvm_rv32_adapters::{rv32_write_heap_default, write_ptr_reg}; use openvm_rv32im_circuit::adapters::RV32_REGISTER_NUM_LIMBS; -use openvm_stark_backend::p3_field::{FieldAlgebra, PrimeField32}; +use openvm_stark_backend::p3_field::FieldAlgebra; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::Rng; use super::{ - ModularAddSubChip, ModularIsEqualChip, ModularIsEqualCoreAir, ModularIsEqualCoreChip, - ModularIsEqualCoreCols, ModularIsEqualCoreRecord, ModularMulDivChip, + ModularAddSubChip, ModularIsEqualChip, ModularIsEqualCoreAir, ModularIsEqualCoreCols, + ModularMulDivChip, }; +use rand::rngs::StdRng; const NUM_LIMBS: usize = 32; const LIMB_BITS: usize = 8; -const BLOCK_SIZE: usize = 32; +const _BLOCK_SIZE: usize = 32; +const MAX_INS_CAPACITY: usize = 128; type F = BabyBear; -const ADD_LOCAL: usize = Rv32ModularArithmeticOpcode::ADD as usize; -const MUL_LOCAL: usize = Rv32ModularArithmeticOpcode::MUL as usize; +#[cfg(test)] +mod addsubtests { + use super::*; + use test_case::test_case; -#[test] -fn test_coord_addsub() { - let opcode_offset = 0; - let modulus = secp256k1_coord_prime(); - test_addsub(opcode_offset, modulus); -} + const ADD_LOCAL: usize = Rv32ModularArithmeticOpcode::ADD as usize; -#[test] -fn test_scalar_addsub() { - let opcode_offset = 4; - let modulus = secp256k1_scalar_prime(); - test_addsub(opcode_offset, modulus); -} + fn set_and_execute_addsub( + tester: &mut VmChipTestBuilder, + chip: &mut ModularAddSubChip, + modulus: &BigUint, + is_setup: bool, + ) { + let mut rng = create_seeded_rng(); + + let (a, b, op) = if is_setup { + (modulus.clone(), BigUint::zero(), ADD_LOCAL + 2) + } else { + let a_digits: Vec<_> = (0..NUM_LIMBS) + .map(|_| rng.gen_range(0..(1 << LIMB_BITS))) + .collect(); + let mut a = BigUint::new(a_digits.clone()); + let b_digits: Vec<_> = (0..NUM_LIMBS) + .map(|_| rng.gen_range(0..(1 << LIMB_BITS))) + .collect(); + let mut b = BigUint::new(b_digits.clone()); + + let op = rng.gen_range(0..2) + ADD_LOCAL; // 0 for add, 1 for sub + a %= modulus; + b %= modulus; + (a, b, op) + }; -fn test_addsub(opcode_offset: usize, modulus: BigUint) { - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let config = ExprBuilderConfig { - modulus: modulus.clone(), - num_limbs: NUM_LIMBS, - limb_bits: LIMB_BITS, - }; - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - - // doing 1xNUM_LIMBS reads and writes - let adapter = Rv32VecHeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), - ); - let mut chip = ModularAddSubChip::new( - adapter, - config, - Rv32ModularArithmeticOpcode::CLASS_OFFSET + opcode_offset, - tester.range_checker(), - tester.offline_memory_mutex_arc(), - ); - let mut rng = create_seeded_rng(); - let num_tests = 50; - let mut all_ops = vec![ADD_LOCAL + 2]; // setup - let mut all_a = vec![modulus.clone()]; - let mut all_b = vec![BigUint::zero()]; - - // First loop: generate all random test data. - for _ in 0..num_tests { - let a_digits: Vec<_> = (0..NUM_LIMBS) - .map(|_| rng.gen_range(0..(1 << LIMB_BITS))) - .collect(); - let mut a = BigUint::new(a_digits.clone()); - let b_digits: Vec<_> = (0..NUM_LIMBS) - .map(|_| rng.gen_range(0..(1 << LIMB_BITS))) - .collect(); - let mut b = BigUint::new(b_digits.clone()); - - let op = rng.gen_range(0..2) + ADD_LOCAL; // 0 for add, 1 for sub - a %= &modulus; - b %= &modulus; - - all_ops.push(op); - all_a.push(a); - all_b.push(b); - } - // Second loop: actually run the tests. - for i in 0..=num_tests { - let op = all_ops[i]; - let a = all_a[i].clone(); - let b = all_b[i].clone(); - if i > 0 { - // if not setup - assert!(a < modulus); - assert!(b < modulus); - } let expected_answer = match op - ADD_LOCAL { - 0 => (&a + &b) % &modulus, - 1 => (&a + &modulus - &b) % &modulus, - 2 => a.clone() % &modulus, + 0 => (&a + &b) % modulus, + 1 => (&a + modulus - &b) % modulus, + 2 => a.clone() % modulus, _ => panic!(), }; @@ -137,11 +88,11 @@ fn test_addsub(opcode_offset: usize, modulus: BigUint) { let data_as = 2; let address1 = 0u32; let address2 = 128u32; - let address3 = (1 << 28) + 1234; // a large memory address to test heap adapter + let address3 = (1 << 28) + 1228; // a large memory address to test heap adapter - write_ptr_reg(&mut tester, ptr_as, addr_ptr1, address1); - write_ptr_reg(&mut tester, ptr_as, addr_ptr2, address2); - write_ptr_reg(&mut tester, ptr_as, addr_ptr3, address3); + write_ptr_reg(tester, ptr_as, addr_ptr1, address1); + write_ptr_reg(tester, ptr_as, addr_ptr2, address2); + write_ptr_reg(tester, ptr_as, addr_ptr3, address3); let a_limbs: [BabyBear; NUM_LIMBS] = biguint_to_limbs(a.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32); @@ -151,105 +102,91 @@ fn test_addsub(opcode_offset: usize, modulus: BigUint) { tester.write(data_as, address2 as usize, b_limbs); let instruction = Instruction::from_isize( - VmOpcode::from_usize(chip.0.core.air.offset + op), + VmOpcode::from_usize(chip.0.step.offset + op), addr_ptr3 as isize, addr_ptr1 as isize, addr_ptr2 as isize, ptr_as as isize, data_as as isize, ); - tester.execute(&mut chip, &instruction); + tester.execute(chip, &instruction); let expected_limbs = biguint_to_limbs::(expected_answer, LIMB_BITS); - for (i, expected) in expected_limbs.into_iter().enumerate() { - let address = address3 as usize + i; - let read_val = tester.read_cell(data_as, address); - assert_eq!(BabyBear::from_canonical_u32(expected), read_val); - } + let read_vals = tester.read::(data_as, address3 as usize); + assert_eq!(read_vals, expected_limbs.map(F::from_canonical_u32)); } - let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - tester.simple_test().expect("Verification failed"); -} + #[test_case(0, secp256k1_coord_prime(), 50)] + #[test_case(4, secp256k1_scalar_prime(), 50)] + fn test_addsub(opcode_offset: usize, modulus: BigUint, num_ops: usize) { + let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); + let config = ExprBuilderConfig { + modulus: modulus.clone(), + num_limbs: NUM_LIMBS, + limb_bits: LIMB_BITS, + }; + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); -#[test] -fn test_coord_muldiv() { - let opcode_offset = 0; - let modulus = secp256k1_coord_prime(); - test_muldiv(opcode_offset, modulus); -} + // doing 1xNUM_LIMBS reads and writes + let mut chip = ModularAddSubChip::new( + tester.execution_bridge(), + tester.memory_bridge(), + tester.memory_helper(), + tester.address_bits(), + config, + Rv32ModularArithmeticOpcode::CLASS_OFFSET + opcode_offset, + bitwise_chip.clone(), + tester.range_checker(), + MAX_INS_CAPACITY, + ); -#[test] -fn test_scalar_muldiv() { - let opcode_offset = 4; - let modulus = secp256k1_scalar_prime(); - test_muldiv(opcode_offset, modulus); -} + for i in 0..num_ops { + set_and_execute_addsub(&mut tester, &mut chip, &modulus, i == 0); + } -fn test_muldiv(opcode_offset: usize, modulus: BigUint) { - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let config = ExprBuilderConfig { - modulus: modulus.clone(), - num_limbs: NUM_LIMBS, - limb_bits: LIMB_BITS, - }; - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - // doing 1xNUM_LIMBS reads and writes - let adapter = Rv32VecHeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), - ); - let mut chip = ModularMulDivChip::new( - adapter, - config, - Rv32ModularArithmeticOpcode::CLASS_OFFSET + opcode_offset, - tester.range_checker(), - tester.offline_memory_mutex_arc(), - ); - let mut rng = create_seeded_rng(); - let num_tests = 50; - let mut all_ops = vec![MUL_LOCAL + 2]; - let mut all_a = vec![modulus.clone()]; - let mut all_b = vec![BigUint::zero()]; - - // First loop: generate all random test data. - for _ in 0..num_tests { - let a_digits: Vec<_> = (0..NUM_LIMBS) - .map(|_| rng.gen_range(0..(1 << LIMB_BITS))) - .collect(); - let mut a = BigUint::new(a_digits.clone()); - let b_digits: Vec<_> = (0..NUM_LIMBS) - .map(|_| rng.gen_range(0..(1 << LIMB_BITS))) - .collect(); - let mut b = BigUint::new(b_digits.clone()); - - // let op = rng.gen_range(2..4); // 2 for mul, 3 for div - let op = MUL_LOCAL; - a %= &modulus; - b %= &modulus; - - all_ops.push(op); - all_a.push(a); - all_b.push(b); + let tester = tester.build().load(chip).load(bitwise_chip).finalize(); + tester.simple_test().expect("Verification failed"); } - // Second loop: actually run the tests. - for i in 0..=num_tests { - let op = all_ops[i]; - let a = all_a[i].clone(); - let b = all_b[i].clone(); - if i > 0 { - // if not setup - assert!(a < modulus); - assert!(b < modulus); - } +} + +#[cfg(test)] +mod muldivtests { + use super::*; + use test_case::test_case; + + const MUL_LOCAL: usize = Rv32ModularArithmeticOpcode::MUL as usize; + + fn set_and_execute_muldiv( + tester: &mut VmChipTestBuilder, + chip: &mut ModularMulDivChip, + modulus: &BigUint, + is_setup: bool, + ) { + let mut rng = create_seeded_rng(); + + let (a, b, op) = if is_setup { + (modulus.clone(), BigUint::zero(), MUL_LOCAL + 2) + } else { + let a_digits: Vec<_> = (0..NUM_LIMBS) + .map(|_| rng.gen_range(0..(1 << LIMB_BITS))) + .collect(); + let mut a = BigUint::new(a_digits.clone()); + let b_digits: Vec<_> = (0..NUM_LIMBS) + .map(|_| rng.gen_range(0..(1 << LIMB_BITS))) + .collect(); + let mut b = BigUint::new(b_digits.clone()); + + let op = rng.gen_range(0..2) + MUL_LOCAL; // 0 for add, 1 for sub + a %= modulus; + b %= modulus; + (a, b, op) + }; + let expected_answer = match op - MUL_LOCAL { - 0 => (&a * &b) % &modulus, - 1 => (&a * b.modinv(&modulus).unwrap()) % &modulus, - 2 => a.clone() % &modulus, + 0 => (&a * &b) % modulus, + 1 => (&a * b.modinv(modulus).unwrap()) % modulus, + 2 => a.clone() % modulus, _ => panic!(), }; @@ -268,322 +205,369 @@ fn test_muldiv(opcode_offset: usize, modulus: BigUint) { let address2 = 128; let address3 = 256; - write_ptr_reg(&mut tester, ptr_as, addr_ptr1, address1); - write_ptr_reg(&mut tester, ptr_as, addr_ptr2, address2); - write_ptr_reg(&mut tester, ptr_as, addr_ptr3, address3); + write_ptr_reg(tester, ptr_as, addr_ptr1, address1); + write_ptr_reg(tester, ptr_as, addr_ptr2, address2); + write_ptr_reg(tester, ptr_as, addr_ptr3, address3); - let a_limbs: [BabyBear; NUM_LIMBS] = - biguint_to_limbs(a.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32); + let a_limbs: [F; NUM_LIMBS] = + biguint_to_limbs(a.clone(), LIMB_BITS).map(F::from_canonical_u32); tester.write(data_as, address1 as usize, a_limbs); - let b_limbs: [BabyBear; NUM_LIMBS] = - biguint_to_limbs(b.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32); + let b_limbs: [F; NUM_LIMBS] = + biguint_to_limbs(b.clone(), LIMB_BITS).map(F::from_canonical_u32); tester.write(data_as, address2 as usize, b_limbs); let instruction = Instruction::from_isize( - VmOpcode::from_usize(chip.0.core.air.offset + op), + VmOpcode::from_usize(chip.0.step.offset + op), addr_ptr3 as isize, addr_ptr1 as isize, addr_ptr2 as isize, ptr_as as isize, data_as as isize, ); - tester.execute(&mut chip, &instruction); + tester.execute(chip, &instruction); let expected_limbs = biguint_to_limbs::(expected_answer, LIMB_BITS); - for (i, expected) in expected_limbs.into_iter().enumerate() { - let address = address3 as usize + i; - let read_val = tester.read_cell(data_as, address); - assert_eq!(BabyBear::from_canonical_u32(expected), read_val); - } + let read_vals = tester.read::(data_as, address3 as usize); + assert_eq!(read_vals, expected_limbs.map(F::from_canonical_u32)); } - let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - tester.simple_test().expect("Verification failed"); -} - -fn test_is_equal( - opcode_offset: usize, - modulus: BigUint, - num_tests: usize, -) { - let mut rng = create_seeded_rng(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let mut chip = ModularIsEqualChip::::new( - Rv32IsEqualModAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), + #[test_case(0, secp256k1_coord_prime(), 50)] + #[test_case(4, secp256k1_scalar_prime(), 50)] + fn test_muldiv(opcode_offset: usize, modulus: BigUint, num_ops: usize) { + let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); + let config = ExprBuilderConfig { + modulus: modulus.clone(), + num_limbs: NUM_LIMBS, + limb_bits: LIMB_BITS, + }; + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + // doing 1xNUM_LIMBS reads and writes + let mut chip = ModularMulDivChip::new( + tester.execution_bridge(), tester.memory_bridge(), + tester.memory_helper(), tester.address_bits(), + config, + Rv32ModularArithmeticOpcode::CLASS_OFFSET + opcode_offset, bitwise_chip.clone(), - ), - ModularIsEqualCoreChip::new(modulus.clone(), bitwise_chip.clone(), opcode_offset), - tester.offline_memory_mutex_arc(), - ); + tester.range_checker(), + MAX_INS_CAPACITY, + ); - { - let vec = big_uint_to_limbs(&modulus, LIMB_BITS); - let modulus_limbs: [F; TOTAL_LIMBS] = std::array::from_fn(|i| { - if i < vec.len() { - F::from_canonical_usize(vec[i]) - } else { - F::ZERO - } - }); + for i in 0..num_ops { + set_and_execute_muldiv(&mut tester, &mut chip, &modulus, i == 0); + } + let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - let setup_instruction = rv32_write_heap_default::( - &mut tester, - vec![modulus_limbs], - vec![[F::ZERO; TOTAL_LIMBS]], - opcode_offset + Rv32ModularArithmeticOpcode::SETUP_ISEQ as usize, + tester.simple_test().expect("Verification failed"); + } +} + +#[cfg(test)] +mod is_equal_tests { + use crate::modular_chip::{ModularIsEqualAir, ModularIsEqualStep}; + + use super::*; + use openvm_rv32_adapters::{Rv32IsEqualModAdapterAir, Rv32IsEqualModeAdapterStep}; + use openvm_stark_backend::{ + p3_air::BaseAir, + p3_matrix::{ + dense::{DenseMatrix, RowMajorMatrix}, + Matrix, + }, + utils::disable_debug_builder, + verifier::VerificationError, + }; + + fn create_test_chips< + const NUM_LANES: usize, + const LANE_SIZE: usize, + const TOTAL_LIMBS: usize, + >( + tester: &mut VmChipTestBuilder, + modulus: &BigUint, + modulus_limbs: [u8; TOTAL_LIMBS], + offset: usize, + ) -> ( + ModularIsEqualChip, + SharedBitwiseOperationLookupChip, + ) { + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + + let chip = ModularIsEqualChip::::new( + ModularIsEqualAir::new( + Rv32IsEqualModAdapterAir::new( + tester.execution_bridge(), + tester.memory_bridge(), + bitwise_bus, + tester.address_bits(), + ), + ModularIsEqualCoreAir::new(modulus.clone(), bitwise_bus, offset), + ), + ModularIsEqualStep::new( + Rv32IsEqualModeAdapterStep::new(tester.address_bits(), bitwise_chip.clone()), + modulus_limbs, + offset, + bitwise_chip.clone(), + ), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - tester.execute(&mut chip, &setup_instruction); + + (chip, bitwise_chip) } - for _ in 0..num_tests { - let b = generate_field_element::(&modulus, &mut rng); - let c = if rng.gen_bool(0.5) { - b + + fn set_and_execute_is_equal< + const NUM_LANES: usize, + const LANE_SIZE: usize, + const TOTAL_LIMBS: usize, + >( + tester: &mut VmChipTestBuilder, + chip: &mut ModularIsEqualChip, + rng: &mut StdRng, + modulus: &BigUint, + offset: usize, + modulus_limbs: [F; TOTAL_LIMBS], + is_setup: bool, + b: Option<[F; TOTAL_LIMBS]>, + c: Option<[F; TOTAL_LIMBS]>, + ) { + let instruction = if is_setup { + rv32_write_heap_default::( + tester, + vec![modulus_limbs], + vec![[F::ZERO; TOTAL_LIMBS]], + offset + Rv32ModularArithmeticOpcode::SETUP_ISEQ as usize, + ) } else { - generate_field_element::(&modulus, &mut rng) + let b = b.unwrap_or( + generate_field_element::(modulus, rng) + .map(F::from_canonical_u32), + ); + let c = c.unwrap_or(if rng.gen_bool(0.5) { + b + } else { + generate_field_element::(modulus, rng) + .map(F::from_canonical_u32) + }); + + rv32_write_heap_default::( + tester, + vec![b], + vec![c], + offset + Rv32ModularArithmeticOpcode::IS_EQ as usize, + ) }; + tester.execute(chip, &instruction); + } - let instruction = rv32_write_heap_default::( - &mut tester, - vec![b.map(F::from_canonical_u32)], - vec![c.map(F::from_canonical_u32)], - opcode_offset + Rv32ModularArithmeticOpcode::IS_EQ as usize, - ); - tester.execute(&mut chip, &instruction); + ////////////////////////////////////////////////////////////////////////////////////// + // POSITIVE TESTS + // + // Randomly generate computations and execute, ensuring that the generated trace + // passes all constraints. + ////////////////////////////////////////////////////////////////////////////////////// + + #[test] + fn test_modular_is_equal_1x32() { + test_is_equal::<1, 32, 32>(17, secp256k1_coord_prime(), 100); } - // Special case where b == c are close to the prime - let b_vec = big_uint_to_limbs(&modulus, LIMB_BITS); - let mut b = from_fn(|i| if i < b_vec.len() { b_vec[i] as u32 } else { 0 }); - b[0] -= 1; - let instruction = rv32_write_heap_default::( - &mut tester, - vec![b.map(F::from_canonical_u32)], - vec![b.map(F::from_canonical_u32)], - opcode_offset + Rv32ModularArithmeticOpcode::IS_EQ as usize, - ); - tester.execute(&mut chip, &instruction); - - let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - tester.simple_test().expect("Verification failed"); -} + #[test] + fn test_modular_is_equal_3x16() { + test_is_equal::<3, 16, 48>(17, BLS12_381_MODULUS.clone(), 100); + } -#[test] -fn test_modular_is_equal_1x32() { - test_is_equal::<1, 32, 32>(17, secp256k1_coord_prime(), 100); -} + fn test_is_equal( + opcode_offset: usize, + modulus: BigUint, + num_tests: usize, + ) { + let mut rng = create_seeded_rng(); + let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); -#[test] -fn test_modular_is_equal_3x16() { - test_is_equal::<3, 16, 48>(17, BLS12_381_MODULUS.clone(), 100); -} + let vec = big_uint_to_limbs(&modulus, LIMB_BITS); + let modulus_limbs: [u8; TOTAL_LIMBS] = + from_fn(|i| if i < vec.len() { vec[i] as u8 } else { 0 }); -// Wrapper chip for testing a bad setup row -type BadModularIsEqualChip< - F, - const NUM_LANES: usize, - const LANE_SIZE: usize, - const TOTAL_LIMBS: usize, -> = VmChipWrapper< - F, - Rv32IsEqualModAdapterChip, - BadModularIsEqualCoreChip, ->; - -// Wrapper chip for testing a bad setup row -struct BadModularIsEqualCoreChip< - const READ_LIMBS: usize, - const WRITE_LIMBS: usize, - const LIMB_BITS: usize, -> { - chip: ModularIsEqualCoreChip, -} + let (mut chip, bitwise_chip) = create_test_chips::( + &mut tester, + &modulus, + modulus_limbs, + opcode_offset, + ); -impl - BadModularIsEqualCoreChip -{ - pub fn new( - modulus: BigUint, - bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - offset: usize, - ) -> Self { - Self { - chip: ModularIsEqualCoreChip::new(modulus, bitwise_lookup_chip, offset), + let modulus_limbs = modulus_limbs.map(F::from_canonical_u8); + + for i in 0..num_tests { + set_and_execute_is_equal( + &mut tester, + &mut chip, + &mut rng, + &modulus, + opcode_offset, + modulus_limbs, + i == 0, // the first test is a setup test + None, + None, + ); } + + // Special case where b == c are close to the prime + let mut b = modulus_limbs; + b[0] -= F::ONE; + set_and_execute_is_equal( + &mut tester, + &mut chip, + &mut rng, + &modulus, + opcode_offset, + modulus_limbs, + false, + Some(b), + Some(b), + ); + + let tester = tester.build().load(chip).load(bitwise_chip).finalize(); + tester.simple_test().expect("Verification failed"); } -} -impl< - F: PrimeField32, - I: VmAdapterInterface, + ////////////////////////////////////////////////////////////////////////////////////// + // NEGATIVE TESTS + // + // Given a fake trace of a single operation, setup a chip and run the test. We replace + // part of the trace and check that the chip throws the expected error. + ////////////////////////////////////////////////////////////////////////////////////// + + /// Negative tests test for 3 "type" of errors determined by the value of b[0]: + fn run_negative_is_equal_test< + const NUM_LANES: usize, + const LANE_SIZE: usize, const READ_LIMBS: usize, - const WRITE_LIMBS: usize, - const LIMB_BITS: usize, - > VmCoreChip for BadModularIsEqualCoreChip -where - I::Reads: Into<[[F; READ_LIMBS]; 2]>, - I::Writes: From<[[F; WRITE_LIMBS]; 1]>, -{ - type Record = ModularIsEqualCoreRecord; - type Air = ModularIsEqualCoreAir; - - #[allow(clippy::type_complexity)] - fn execute_instruction( - &self, - instruction: &Instruction, - from_pc: u32, - reads: I::Reads, - ) -> Result<(AdapterRuntimeContext, Self::Record)> { - // Override the b_diff_idx to be out of bounds. - // This will cause lt_marker to be all zeros except a 2. - // There was a bug in this case which allowed b to be less than N. - self.chip.execute_instruction(instruction, from_pc, reads) - } + >( + modulus: BigUint, + opcode_offset: usize, + test_case: usize, + expected_error: VerificationError, + ) { + let mut rng = create_seeded_rng(); + let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - fn get_opcode_name(&self, opcode: usize) -> String { - as VmCoreChip>::get_opcode_name(&self.chip, opcode) - } + let vec = big_uint_to_limbs(&modulus, LIMB_BITS); + let modulus_limbs: [u8; READ_LIMBS] = + from_fn(|i| if i < vec.len() { vec[i] as u8 } else { 0 }); - fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { - as VmCoreChip>::generate_trace_row(&self.chip, row_slice, record.clone()); - let row_slice: &mut ModularIsEqualCoreCols<_, READ_LIMBS> = row_slice.borrow_mut(); - // decide which bug to test based on b[0] - if record.b[0] == F::ONE { - // test the constraint that c_lt_mark = 2 when is_setup = 1 - row_slice.c_lt_mark = F::ONE; - row_slice.lt_marker = [F::ZERO; READ_LIMBS]; - row_slice.lt_marker[READ_LIMBS - 1] = F::ONE; - row_slice.c_lt_diff = - F::from_canonical_u32(self.chip.air.modulus_limbs[READ_LIMBS - 1]) - - record.c[READ_LIMBS - 1]; - row_slice.b_lt_diff = - F::from_canonical_u32(self.chip.air.modulus_limbs[READ_LIMBS - 1]) - - record.b[READ_LIMBS - 1]; - } else if record.b[0] == F::from_canonical_u32(2) { - // test the constraint that b[i] = N[i] for all i when prefix_sum is not 1 or - // lt_marker_sum - is_setup - row_slice.c_lt_mark = F::from_canonical_u8(2); - row_slice.lt_marker = [F::ZERO; READ_LIMBS]; - row_slice.lt_marker[READ_LIMBS - 1] = F::from_canonical_u8(2); - row_slice.c_lt_diff = - F::from_canonical_u32(self.chip.air.modulus_limbs[READ_LIMBS - 1]) - - record.c[READ_LIMBS - 1]; - } else if record.b[0] == F::from_canonical_u32(3) { - // test the constraint that sum_i lt_marker[i] = 2 when is_setup = 1 - row_slice.c_lt_mark = F::from_canonical_u8(2); - row_slice.lt_marker = [F::ZERO; READ_LIMBS]; - row_slice.lt_marker[READ_LIMBS - 1] = F::from_canonical_u8(2); - row_slice.lt_marker[0] = F::ONE; - row_slice.b_lt_diff = - F::from_canonical_u32(self.chip.air.modulus_limbs[0]) - record.b[0]; - row_slice.c_lt_diff = - F::from_canonical_u32(self.chip.air.modulus_limbs[READ_LIMBS - 1]) - - record.c[READ_LIMBS - 1]; - } - } + let (mut chip, bitwise_chip) = create_test_chips::( + &mut tester, + &modulus, + modulus_limbs, + opcode_offset, + ); - fn air(&self) -> &Self::Air { - as VmCoreChip>::air( - &self.chip, - ) - } -} + let modulus_limbs = modulus_limbs.map(F::from_canonical_u8); -impl - InsExecutorE1 for BadModularIsEqualCoreChip -where - Mem: GuestMemory, - F: PrimeField32, -{ - fn execute_e1( - &mut self, - _state: &mut VmExecutionState, - _instruction: &Instruction, - ) -> Result<()> { - todo!("Implement execute_e1") - } -} + set_and_execute_is_equal( + &mut tester, + &mut chip, + &mut rng, + &modulus, + opcode_offset, + modulus_limbs, + true, + None, + None, + ); -// Test that passes the wrong modulus in the setup instruction. -// This proof should fail to verify. -fn test_is_equal_setup_bad< - const NUM_LANES: usize, - const LANE_SIZE: usize, - const TOTAL_LIMBS: usize, ->( - opcode_offset: usize, - modulus: BigUint, - b_val: u32, /* used to select which bug to test. currently only 1, 2, and 3 are supported - * (because there are three bugs to test) */ -) { - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let mut chip = BadModularIsEqualChip::::new( - Rv32IsEqualModAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), - ), - BadModularIsEqualCoreChip::new(modulus.clone(), bitwise_chip.clone(), opcode_offset), - tester.offline_memory_mutex_arc(), - ); - - let mut b_limbs = [F::ZERO; TOTAL_LIMBS]; - b_limbs[0] = F::from_canonical_u32(b_val); - let setup_instruction = rv32_write_heap_default::( - &mut tester, - vec![b_limbs], - vec![[F::ZERO; TOTAL_LIMBS]], - opcode_offset + Rv32ModularArithmeticOpcode::SETUP_ISEQ as usize, - ); - tester.execute(&mut chip, &setup_instruction); - - let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - tester.simple_test().expect("Verification failed"); -} + let adapter_width = BaseAir::::width(&chip.air.adapter); + let modify_trace = |trace: &mut DenseMatrix| { + let mut trace_row = trace.row_slice(0).to_vec(); + let cols: &mut ModularIsEqualCoreCols<_, READ_LIMBS> = + trace_row.split_at_mut(adapter_width).1.borrow_mut(); + if test_case == 1 { + // test the constraint that c_lt_mark = 2 when is_setup = 1 + cols.b[0] = F::from_canonical_u32(1); + cols.c_lt_mark = F::ONE; + cols.lt_marker = [F::ZERO; READ_LIMBS]; + cols.lt_marker[READ_LIMBS - 1] = F::ONE; + cols.c_lt_diff = modulus_limbs[READ_LIMBS - 1] - cols.c[READ_LIMBS - 1]; + cols.b_lt_diff = modulus_limbs[READ_LIMBS - 1] - cols.b[READ_LIMBS - 1]; + } else if test_case == 2 { + // test the constraint that b[i] = N[i] for all i when prefix_sum is not 1 or + // lt_marker_sum - is_setup + cols.b[0] = F::from_canonical_u32(2); + cols.c_lt_mark = F::from_canonical_u8(2); + cols.lt_marker = [F::ZERO; READ_LIMBS]; + cols.lt_marker[READ_LIMBS - 1] = F::from_canonical_u8(2); + cols.c_lt_diff = modulus_limbs[READ_LIMBS - 1] - cols.c[READ_LIMBS - 1]; + } else if test_case == 3 { + // test the constraint that sum_i lt_marker[i] = 2 when is_setup = 1 + cols.b[0] = F::from_canonical_u32(3); + cols.c_lt_mark = F::from_canonical_u8(2); + cols.lt_marker = [F::ZERO; READ_LIMBS]; + cols.lt_marker[READ_LIMBS - 1] = F::from_canonical_u8(2); + cols.lt_marker[0] = F::ONE; + cols.b_lt_diff = modulus_limbs[0] - cols.b[0]; + cols.c_lt_diff = modulus_limbs[READ_LIMBS - 1] - cols.c[READ_LIMBS - 1]; + } + *trace = RowMajorMatrix::new(trace_row, trace.width()); + }; -#[should_panic] -#[test] -fn test_modular_is_equal_setup_bad_1_1x32() { - test_is_equal_setup_bad::<1, 32, 32>(17, secp256k1_coord_prime(), 1); -} + disable_debug_builder(); + let tester = tester + .build() + .load_and_prank_trace(chip, modify_trace) + .load(bitwise_chip) + .finalize(); + tester.simple_test_with_expected_error(expected_error); + } -#[should_panic] -#[test] -fn test_modular_is_equal_setup_bad_2_1x32_2() { - test_is_equal_setup_bad::<1, 32, 32>(17, secp256k1_coord_prime(), 2); -} + #[test] + fn negative_test_modular_is_equal_1x32() { + run_negative_is_equal_test::<1, 32, 32>( + secp256k1_coord_prime(), + 17, + 1, + VerificationError::OodEvaluationMismatch, + ); -#[should_panic] -#[test] -fn test_modular_is_equal_setup_bad_3_1x32() { - test_is_equal_setup_bad::<1, 32, 32>(17, secp256k1_coord_prime(), 3); -} + run_negative_is_equal_test::<1, 32, 32>( + secp256k1_coord_prime(), + 17, + 2, + VerificationError::OodEvaluationMismatch, + ); -#[should_panic] -#[test] -fn test_modular_is_equal_setup_bad_1_3x16() { - test_is_equal_setup_bad::<3, 16, 48>(17, BLS12_381_MODULUS.clone(), 1); -} + run_negative_is_equal_test::<1, 32, 32>( + secp256k1_coord_prime(), + 17, + 3, + VerificationError::OodEvaluationMismatch, + ); + } -#[should_panic] -#[test] -fn test_modular_is_equal_setup_bad_2_3x16() { - test_is_equal_setup_bad::<3, 16, 48>(17, BLS12_381_MODULUS.clone(), 2); -} + #[test] + fn negative_test_modular_is_equal_3x16() { + run_negative_is_equal_test::<3, 16, 48>( + BLS12_381_MODULUS.clone(), + 17, + 1, + VerificationError::OodEvaluationMismatch, + ); + + run_negative_is_equal_test::<3, 16, 48>( + BLS12_381_MODULUS.clone(), + 17, + 2, + VerificationError::OodEvaluationMismatch, + ); -#[should_panic] -#[test] -fn test_modular_is_equal_setup_bad_3_3x16() { - test_is_equal_setup_bad::<3, 16, 48>(17, BLS12_381_MODULUS.clone(), 3); + run_negative_is_equal_test::<3, 16, 48>( + BLS12_381_MODULUS.clone(), + 17, + 3, + VerificationError::OodEvaluationMismatch, + ); + } } diff --git a/extensions/algebra/circuit/src/modular_extension.rs b/extensions/algebra/circuit/src/modular_extension.rs index 18a19becfa..bfaed25682 100644 --- a/extensions/algebra/circuit/src/modular_extension.rs +++ b/extensions/algebra/circuit/src/modular_extension.rs @@ -1,28 +1,37 @@ +use std::array; + use derive_more::derive::From; use num_bigint::BigUint; use openvm_algebra_transpiler::Rv32ModularArithmeticOpcode; use openvm_circuit::{ self, - arch::{SystemPort, VmExtension, VmInventory, VmInventoryBuilder, VmInventoryError}, + arch::{ + ExecutionBridge, SystemPort, VmExtension, VmInventory, VmInventoryBuilder, VmInventoryError, + }, system::phantom::PhantomChip, }; -use openvm_circuit_derive::{AnyEnum, InstructionExecutor}; -use openvm_circuit_primitives::bitwise_op_lookup::{ - BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, +use openvm_circuit_derive::{AnyEnum, InsExecutorE1, InstructionExecutor}; +use openvm_circuit_primitives::{ + bigint::utils::big_uint_to_limbs, + bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, }; use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; use openvm_instructions::{LocalOpcode, VmOpcode}; use openvm_mod_circuit_builder::ExprBuilderConfig; -use openvm_rv32_adapters::{Rv32IsEqualModAdapterChip, Rv32VecHeapAdapterChip}; +use openvm_rv32_adapters::{Rv32IsEqualModAdapterAir, Rv32IsEqualModeAdapterStep}; use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr}; use strum::EnumCount; use crate::modular_chip::{ - ModularAddSubChip, ModularIsEqualChip, ModularIsEqualCoreChip, ModularMulDivChip, + ModularAddSubChip, ModularIsEqualAir, ModularIsEqualChip, ModularIsEqualCoreAir, + ModularIsEqualStep, ModularMulDivChip, }; +// TODO: this should be decided after e2 execution +const MAX_INS_CAPACITY: usize = 1 << 22; + #[serde_as] #[derive(Clone, Debug, derive_new::new, Serialize, Deserialize)] pub struct ModularExtension { @@ -30,7 +39,7 @@ pub struct ModularExtension { pub supported_modulus: Vec, } -#[derive(ChipUsageGetter, Chip, InstructionExecutor, AnyEnum, From)] +#[derive(ChipUsageGetter, Chip, InstructionExecutor, AnyEnum, From, InsExecutorE1)] pub enum ModularExtensionExecutor { // 32 limbs prime ModularAddSubRv32_32(ModularAddSubChip), @@ -63,7 +72,11 @@ impl VmExtension for ModularExtension { program_bus, memory_bridge, } = builder.system_port(); + + let execution_bridge = ExecutionBridge::new(execution_bus, program_bus); let range_checker = builder.system_base().range_checker_chip.clone(); + let pointer_max_bits = builder.system_config().memory_config.pointer_max_bits; + let bitwise_lu_chip = if let Some(&chip) = builder .find_chip::>() .first() @@ -75,8 +88,6 @@ impl VmExtension for ModularExtension { inventory.add_periphery_chip(chip.clone()); chip }; - let offline_memory = builder.system_base().offline_memory(); - let address_bits = builder.system_config().memory_config.pointer_max_bits; let addsub_opcodes = (Rv32ModularArithmeticOpcode::ADD as usize) ..=(Rv32ModularArithmeticOpcode::SETUP_ADDSUB as usize); @@ -101,28 +112,20 @@ impl VmExtension for ModularExtension { num_limbs: 48, limb_bits: 8, }; - let adapter_chip_32 = Rv32VecHeapAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - address_bits, - bitwise_lu_chip.clone(), - ); - let adapter_chip_48 = Rv32VecHeapAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - address_bits, - bitwise_lu_chip.clone(), - ); + + let modulus_limbs = big_uint_to_limbs(&modulus, 8); if bytes <= 32 { let addsub_chip = ModularAddSubChip::new( - adapter_chip_32.clone(), + execution_bridge.clone(), + memory_bridge.clone(), + builder.system_base().memory_controller.helper(), + pointer_max_bits, config32.clone(), start_offset, + bitwise_lu_chip.clone(), range_checker.clone(), - offline_memory.clone(), + MAX_INS_CAPACITY, ); inventory.add_executor( ModularExtensionExecutor::ModularAddSubRv32_32(addsub_chip), @@ -131,11 +134,15 @@ impl VmExtension for ModularExtension { .map(|x| VmOpcode::from_usize(x + start_offset)), )?; let muldiv_chip = ModularMulDivChip::new( - adapter_chip_32.clone(), + execution_bridge.clone(), + memory_bridge.clone(), + builder.system_base().memory_controller.helper(), + pointer_max_bits, config32.clone(), start_offset, + bitwise_lu_chip.clone(), range_checker.clone(), - offline_memory.clone(), + MAX_INS_CAPACITY, ); inventory.add_executor( ModularExtensionExecutor::ModularMulDivRv32_32(muldiv_chip), @@ -143,20 +150,36 @@ impl VmExtension for ModularExtension { .clone() .map(|x| VmOpcode::from_usize(x + start_offset)), )?; + + let modulus_limbs = array::from_fn(|i| { + if i < modulus_limbs.len() { + modulus_limbs[i] as u8 + } else { + 0 + } + }); let isequal_chip = ModularIsEqualChip::new( - Rv32IsEqualModAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - address_bits, - bitwise_lu_chip.clone(), + ModularIsEqualAir::new( + Rv32IsEqualModAdapterAir::new( + execution_bridge.clone(), + memory_bridge.clone(), + bitwise_lu_chip.bus(), + pointer_max_bits, + ), + ModularIsEqualCoreAir::new( + modulus.clone(), + bitwise_lu_chip.bus(), + start_offset, + ), ), - ModularIsEqualCoreChip::new( - modulus.clone(), - bitwise_lu_chip.clone(), + ModularIsEqualStep::new( + Rv32IsEqualModeAdapterStep::new(pointer_max_bits, bitwise_lu_chip.clone()), + modulus_limbs, start_offset, + bitwise_lu_chip.clone(), ), - offline_memory.clone(), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor( ModularExtensionExecutor::ModularIsEqualRv32_32(isequal_chip), @@ -166,11 +189,15 @@ impl VmExtension for ModularExtension { )?; } else if bytes <= 48 { let addsub_chip = ModularAddSubChip::new( - adapter_chip_48.clone(), + execution_bridge.clone(), + memory_bridge.clone(), + builder.system_base().memory_controller.helper(), + pointer_max_bits, config48.clone(), start_offset, + bitwise_lu_chip.clone(), range_checker.clone(), - offline_memory.clone(), + MAX_INS_CAPACITY, ); inventory.add_executor( ModularExtensionExecutor::ModularAddSubRv32_48(addsub_chip), @@ -179,11 +206,15 @@ impl VmExtension for ModularExtension { .map(|x| VmOpcode::from_usize(x + start_offset)), )?; let muldiv_chip = ModularMulDivChip::new( - adapter_chip_48.clone(), + execution_bridge.clone(), + memory_bridge.clone(), + builder.system_base().memory_controller.helper(), + pointer_max_bits, config48.clone(), start_offset, + bitwise_lu_chip.clone(), range_checker.clone(), - offline_memory.clone(), + MAX_INS_CAPACITY, ); inventory.add_executor( ModularExtensionExecutor::ModularMulDivRv32_48(muldiv_chip), @@ -191,20 +222,35 @@ impl VmExtension for ModularExtension { .clone() .map(|x| VmOpcode::from_usize(x + start_offset)), )?; + let modulus_limbs = array::from_fn(|i| { + if i < modulus_limbs.len() { + modulus_limbs[i] as u8 + } else { + 0 + } + }); let isequal_chip = ModularIsEqualChip::new( - Rv32IsEqualModAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, - address_bits, - bitwise_lu_chip.clone(), + ModularIsEqualAir::new( + Rv32IsEqualModAdapterAir::new( + execution_bridge.clone(), + memory_bridge.clone(), + bitwise_lu_chip.bus(), + pointer_max_bits, + ), + ModularIsEqualCoreAir::new( + modulus.clone(), + bitwise_lu_chip.bus(), + start_offset, + ), ), - ModularIsEqualCoreChip::new( - modulus.clone(), - bitwise_lu_chip.clone(), + ModularIsEqualStep::new( + Rv32IsEqualModeAdapterStep::new(pointer_max_bits, bitwise_lu_chip.clone()), + modulus_limbs, start_offset, + bitwise_lu_chip.clone(), ), - offline_memory.clone(), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor( ModularExtensionExecutor::ModularIsEqualRv32_48(isequal_chip), diff --git a/extensions/bigint/circuit/src/extension.rs b/extensions/bigint/circuit/src/extension.rs index 12f161c0b8..fac415c965 100644 --- a/extensions/bigint/circuit/src/extension.rs +++ b/extensions/bigint/circuit/src/extension.rs @@ -119,7 +119,7 @@ impl VmExtension for Int256 { inventory.add_periphery_chip(chip.clone()); chip }; - // let offline_memory = builder.system_base().offline_memory(); + let pointer_max_bits = builder.system_config().memory_config.pointer_max_bits; let range_tuple_chip = if let Some(chip) = builder diff --git a/extensions/ecc/circuit/src/weierstrass_chip/add_ne.rs b/extensions/ecc/circuit/src/weierstrass_chip/add_ne.rs index 24bcc52ef3..32ece53961 100644 --- a/extensions/ecc/circuit/src/weierstrass_chip/add_ne.rs +++ b/extensions/ecc/circuit/src/weierstrass_chip/add_ne.rs @@ -1,7 +1,24 @@ use std::{cell::RefCell, rc::Rc}; -use openvm_circuit_primitives::var_range::VariableRangeCheckerBus; -use openvm_mod_circuit_builder::{ExprBuilder, ExprBuilderConfig, FieldExpr}; +use openvm_circuit::{ + arch::ExecutionBridge, + system::memory::{offline_checker::MemoryBridge, SharedMemoryHelper}, +}; +use openvm_circuit_derive::{InsExecutorE1, InstructionExecutor}; +use openvm_circuit_primitives::{ + bitwise_op_lookup::SharedBitwiseOperationLookupChip, + var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus}, + Chip, ChipUsageGetter, +}; +use openvm_ecc_transpiler::Rv32WeierstrassOpcode; +use openvm_instructions::riscv::RV32_CELL_BITS; +use openvm_mod_circuit_builder::{ + ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreAir, +}; +use openvm_rv32_adapters::{Rv32VecHeapAdapterAir, Rv32VecHeapAdapterStep}; +use openvm_stark_backend::p3_field::PrimeField32; + +use super::{WeierstrassAir, WeierstrassChip, WeierstrassStep}; // Assumes that (x1, y1), (x2, y2) both lie on the curve and are not the identity point. // Further assumes that x1, x2 are not equal in the coordinate field. @@ -26,3 +43,58 @@ pub fn ec_add_ne_expr( let builder = builder.borrow().clone(); FieldExpr::new(builder, range_bus, true) } + +/// BLOCK_SIZE: how many cells do we read at a time, must be a power of 2. +/// BLOCKS: how many blocks do we need to represent one input or output +/// For example, for bls12_381, BLOCK_SIZE = 16, each element has 3 blocks and with two elements per +/// input AffinePoint, BLOCKS = 6. For secp256k1, BLOCK_SIZE = 32, BLOCKS = 2. + +#[derive(Chip, ChipUsageGetter, InstructionExecutor, InsExecutorE1)] +pub struct EcAddNeChip( + pub WeierstrassChip, +); + +impl + EcAddNeChip +{ + pub fn new( + execution_bridge: ExecutionBridge, + memory_bridge: MemoryBridge, + mem_helper: SharedMemoryHelper, + pointer_max_bits: usize, + config: ExprBuilderConfig, + offset: usize, + bitwise_lookup_chip: SharedBitwiseOperationLookupChip, + range_checker: SharedVariableRangeCheckerChip, + height: usize, + ) -> Self { + let expr = ec_add_ne_expr(config, range_checker.bus()); + + let local_opcode_idx = vec![ + Rv32WeierstrassOpcode::EC_ADD_NE as usize, + Rv32WeierstrassOpcode::SETUP_EC_ADD_NE as usize, + ]; + + let air = WeierstrassAir::new( + Rv32VecHeapAdapterAir::new( + execution_bridge, + memory_bridge, + bitwise_lookup_chip.bus(), + pointer_max_bits, + ), + FieldExpressionCoreAir::new(expr.clone(), offset, local_opcode_idx.clone(), vec![]), + ); + + let step = WeierstrassStep::new( + Rv32VecHeapAdapterStep::new(pointer_max_bits, bitwise_lookup_chip), + expr, + offset, + local_opcode_idx, + vec![], + range_checker, + "EcAddNe", + false, + ); + Self(WeierstrassChip::new(air, step, height, mem_helper)) + } +} diff --git a/extensions/ecc/circuit/src/weierstrass_chip/double.rs b/extensions/ecc/circuit/src/weierstrass_chip/double.rs index 0ae55f2df7..975925f698 100644 --- a/extensions/ecc/circuit/src/weierstrass_chip/double.rs +++ b/extensions/ecc/circuit/src/weierstrass_chip/double.rs @@ -2,8 +2,25 @@ use std::{cell::RefCell, rc::Rc}; use num_bigint::BigUint; use num_traits::One; -use openvm_circuit_primitives::var_range::VariableRangeCheckerBus; -use openvm_mod_circuit_builder::{ExprBuilder, ExprBuilderConfig, FieldExpr, FieldVariable}; +use openvm_circuit::{ + arch::ExecutionBridge, + system::memory::{offline_checker::MemoryBridge, SharedMemoryHelper}, +}; +use openvm_circuit_derive::{InsExecutorE1, InstructionExecutor}; +use openvm_circuit_primitives::{ + bitwise_op_lookup::SharedBitwiseOperationLookupChip, + var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus}, + Chip, ChipUsageGetter, +}; +use openvm_ecc_transpiler::Rv32WeierstrassOpcode; +use openvm_instructions::riscv::RV32_CELL_BITS; +use openvm_mod_circuit_builder::{ + ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreAir, FieldVariable, +}; +use openvm_rv32_adapters::{Rv32VecHeapAdapterAir, Rv32VecHeapAdapterStep}; +use openvm_stark_backend::p3_field::PrimeField32; + +use super::{WeierstrassAir, WeierstrassChip, WeierstrassStep}; pub fn ec_double_ne_expr( config: ExprBuilderConfig, // The coordinate field. @@ -34,3 +51,58 @@ pub fn ec_double_ne_expr( let builder = builder.borrow().clone(); FieldExpr::new_with_setup_values(builder, range_bus, true, vec![a_biguint]) } + +/// BLOCK_SIZE: how many cells do we read at a time, must be a power of 2. +/// BLOCKS: how many blocks do we need to represent one input or output +/// For example, for bls12_381, BLOCK_SIZE = 16, each element has 3 blocks and with two elements per +/// input AffinePoint, BLOCKS = 6. For secp256k1, BLOCK_SIZE = 32, BLOCKS = 2. +#[derive(Chip, ChipUsageGetter, InstructionExecutor, InsExecutorE1)] +pub struct EcDoubleChip( + pub WeierstrassChip, +); + +impl + EcDoubleChip +{ + pub fn new( + execution_bridge: ExecutionBridge, + memory_bridge: MemoryBridge, + mem_helper: SharedMemoryHelper, + pointer_max_bits: usize, + config: ExprBuilderConfig, + offset: usize, + bitwise_lookup_chip: SharedBitwiseOperationLookupChip, + range_checker: SharedVariableRangeCheckerChip, + a_biguint: BigUint, + height: usize, + ) -> Self { + let expr = ec_double_ne_expr(config, range_checker.bus(), a_biguint); + + let local_opcode_idx = vec![ + Rv32WeierstrassOpcode::EC_DOUBLE as usize, + Rv32WeierstrassOpcode::SETUP_EC_DOUBLE as usize, + ]; + + let air = WeierstrassAir::new( + Rv32VecHeapAdapterAir::new( + execution_bridge, + memory_bridge, + bitwise_lookup_chip.bus(), + pointer_max_bits, + ), + FieldExpressionCoreAir::new(expr.clone(), offset, local_opcode_idx.clone(), vec![]), + ); + + let step = WeierstrassStep::new( + Rv32VecHeapAdapterStep::new(pointer_max_bits, bitwise_lookup_chip), + expr, + offset, + local_opcode_idx, + vec![], + range_checker, + "EcDouble", + false, + ); + Self(WeierstrassChip::new(air, step, height, mem_helper)) + } +} diff --git a/extensions/ecc/circuit/src/weierstrass_chip/mod.rs b/extensions/ecc/circuit/src/weierstrass_chip/mod.rs index 0bcee1facf..e27534e167 100644 --- a/extensions/ecc/circuit/src/weierstrass_chip/mod.rs +++ b/extensions/ecc/circuit/src/weierstrass_chip/mod.rs @@ -1,99 +1,39 @@ mod add_ne; mod double; -use std::sync::Arc; - pub use add_ne::*; pub use double::*; #[cfg(test)] mod tests; -use std::sync::Mutex; - -use num_bigint::BigUint; -use openvm_circuit::{arch::VmChipWrapper, system::memory::OfflineMemory}; -use openvm_circuit_derive::InstructionExecutor; -use openvm_circuit_primitives::var_range::SharedVariableRangeCheckerChip; -use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; -use openvm_ecc_transpiler::Rv32WeierstrassOpcode; -use openvm_mod_circuit_builder::{ExprBuilderConfig, FieldExpressionCoreChip}; -use openvm_rv32_adapters::Rv32VecHeapAdapterChip; -use openvm_stark_backend::p3_field::PrimeField32; - -/// BLOCK_SIZE: how many cells do we read at a time, must be a power of 2. -/// BLOCKS: how many blocks do we need to represent one input or output -/// For example, for bls12_381, BLOCK_SIZE = 16, each element has 3 blocks and with two elements per -/// input AffinePoint, BLOCKS = 6. For secp256k1, BLOCK_SIZE = 32, BLOCKS = 2. -#[derive(Chip, ChipUsageGetter, InstructionExecutor)] -pub struct EcAddNeChip( - pub VmChipWrapper< - F, - Rv32VecHeapAdapterChip, - FieldExpressionCoreChip, - >, -); - -impl - EcAddNeChip -{ - pub fn new( - adapter: Rv32VecHeapAdapterChip, - config: ExprBuilderConfig, - offset: usize, - range_checker: SharedVariableRangeCheckerChip, - offline_memory: Arc>>, - ) -> Self { - let expr = ec_add_ne_expr(config, range_checker.bus()); - let core = FieldExpressionCoreChip::new( - expr, - offset, - vec![ - Rv32WeierstrassOpcode::EC_ADD_NE as usize, - Rv32WeierstrassOpcode::SETUP_EC_ADD_NE as usize, - ], - vec![], - range_checker, - "EcAddNe", - false, - ); - Self(VmChipWrapper::new(adapter, core, offline_memory)) - } -} - -#[derive(Chip, ChipUsageGetter, InstructionExecutor)] -pub struct EcDoubleChip( - pub VmChipWrapper< - F, - Rv32VecHeapAdapterChip, - FieldExpressionCoreChip, - >, -); - -impl - EcDoubleChip -{ - pub fn new( - adapter: Rv32VecHeapAdapterChip, - range_checker: SharedVariableRangeCheckerChip, - config: ExprBuilderConfig, - offset: usize, - a: BigUint, - offline_memory: Arc>>, - ) -> Self { - let expr = ec_double_ne_expr(config, range_checker.bus(), a); - let core = FieldExpressionCoreChip::new( - expr, - offset, - vec![ - Rv32WeierstrassOpcode::EC_DOUBLE as usize, - Rv32WeierstrassOpcode::SETUP_EC_DOUBLE as usize, - ], - vec![], - range_checker, - "EcDouble", - true, - ); - Self(VmChipWrapper::new(adapter, core, offline_memory)) - } -} +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; + +use openvm_mod_circuit_builder::{FieldExpressionCoreAir, FieldExpressionStep}; +use openvm_rv32_adapters::{Rv32VecHeapAdapterAir, Rv32VecHeapAdapterStep}; + +pub(crate) type WeierstrassAir< + const NUM_READS: usize, + const BLOCKS: usize, + const BLOCK_SIZE: usize, +> = VmAirWrapper< + Rv32VecHeapAdapterAir, + FieldExpressionCoreAir, +>; + +pub(crate) type WeierstrassStep< + const NUM_READS: usize, + const BLOCKS: usize, + const BLOCK_SIZE: usize, +> = FieldExpressionStep>; + +pub(crate) type WeierstrassChip< + F, + const NUM_READS: usize, + const BLOCKS: usize, + const BLOCK_SIZE: usize, +> = NewVmChipWrapper< + F, + WeierstrassAir, + WeierstrassStep, +>; diff --git a/extensions/ecc/circuit/src/weierstrass_chip/tests.rs b/extensions/ecc/circuit/src/weierstrass_chip/tests.rs index 213918ec2e..ae7ed125b6 100644 --- a/extensions/ecc/circuit/src/weierstrass_chip/tests.rs +++ b/extensions/ecc/circuit/src/weierstrass_chip/tests.rs @@ -10,7 +10,7 @@ use openvm_circuit_primitives::{ use openvm_ecc_transpiler::Rv32WeierstrassOpcode; use openvm_instructions::{riscv::RV32_CELL_BITS, LocalOpcode}; use openvm_mod_circuit_builder::{test_utils::biguint_to_limbs, ExprBuilderConfig, FieldExpr}; -use openvm_rv32_adapters::{rv32_write_heap_default, Rv32VecHeapAdapterChip}; +use openvm_rv32_adapters::rv32_write_heap_default; use openvm_stark_backend::p3_field::FieldAlgebra; use openvm_stark_sdk::p3_baby_bear::BabyBear; @@ -19,6 +19,7 @@ use super::{EcAddNeChip, EcDoubleChip}; const NUM_LIMBS: usize = 32; const LIMB_BITS: usize = 8; const BLOCK_SIZE: usize = 32; +const MAX_INS_CAPACITY: usize = 128; type F = BabyBear; lazy_static::lazy_static! { @@ -87,21 +88,20 @@ fn test_add_ne() { }; let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let adapter = Rv32VecHeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), + + let mut chip = EcAddNeChip::::new( + tester.execution_bridge(), tester.memory_bridge(), + tester.memory_helper(), tester.address_bits(), - bitwise_chip.clone(), - ); - let mut chip = EcAddNeChip::new( - adapter, config, Rv32WeierstrassOpcode::CLASS_OFFSET, + bitwise_chip.clone(), tester.range_checker(), - tester.offline_memory_mutex_arc(), + MAX_INS_CAPACITY, ); - assert_eq!(chip.0.core.expr().builder.num_variables, 3); // lambda, x3, y3 + + assert_eq!(chip.0.step.expr.builder.num_variables, 3); // lambda, x3, y3 let (p1_x, p1_y) = SampleEcPoints[0].clone(); let (p2_x, p2_y) = SampleEcPoints[1].clone(); @@ -117,21 +117,21 @@ fn test_add_ne() { let r = chip .0 - .core - .expr() + .step + .expr .execute(vec![p1_x, p1_y, p2_x, p2_y], vec![true]); assert_eq!(r.len(), 3); // lambda, x3, y3 assert_eq!(r[1], SampleEcPoints[2].0); assert_eq!(r[2], SampleEcPoints[2].1); - let prime_limbs: [BabyBear; NUM_LIMBS] = prime_limbs(chip.0.core.expr()).try_into().unwrap(); + let prime_limbs: [BabyBear; NUM_LIMBS] = prime_limbs(&chip.0.step.expr).try_into().unwrap(); let mut one_limbs = [BabyBear::ONE; NUM_LIMBS]; one_limbs[0] = BabyBear::ONE; let setup_instruction = rv32_write_heap_default( &mut tester, vec![prime_limbs, one_limbs], // inputs[0] = prime, others doesn't matter vec![one_limbs, one_limbs], - chip.0.core.air.offset + Rv32WeierstrassOpcode::SETUP_EC_ADD_NE as usize, + chip.0.step.offset + Rv32WeierstrassOpcode::SETUP_EC_ADD_NE as usize, ); tester.execute(&mut chip, &setup_instruction); @@ -139,7 +139,7 @@ fn test_add_ne() { &mut tester, vec![p1_x_limbs, p1_y_limbs], vec![p2_x_limbs, p2_y_limbs], - chip.0.core.air.offset + Rv32WeierstrassOpcode::EC_ADD_NE as usize, + chip.0.step.offset + Rv32WeierstrassOpcode::EC_ADD_NE as usize, ); tester.execute(&mut chip, &instruction); @@ -159,12 +159,17 @@ fn test_double() { }; let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let adapter = Rv32VecHeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), + let mut chip = EcDoubleChip::::new( + tester.execution_bridge(), tester.memory_bridge(), + tester.memory_helper(), tester.address_bits(), + config, + Rv32WeierstrassOpcode::CLASS_OFFSET, bitwise_chip.clone(), + tester.range_checker(), + BigUint::zero(), + MAX_INS_CAPACITY, ); let (p1_x, p1_y) = SampleEcPoints[1].clone(); @@ -173,29 +178,21 @@ fn test_double() { let p1_y_limbs = biguint_to_limbs::(p1_y.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32); - let mut chip = EcDoubleChip::new( - adapter, - tester.memory_controller().borrow().range_checker.clone(), - config, - Rv32WeierstrassOpcode::CLASS_OFFSET, - BigUint::zero(), - tester.offline_memory_mutex_arc(), - ); - assert_eq!(chip.0.core.air.expr.builder.num_variables, 3); // lambda, x3, y3 + assert_eq!(chip.0.step.expr.builder.num_variables, 3); // lambda, x3, y3 - let r = chip.0.core.air.expr.execute(vec![p1_x, p1_y], vec![true]); + let r = chip.0.step.expr.execute(vec![p1_x, p1_y], vec![true]); assert_eq!(r.len(), 3); // lambda, x3, y3 assert_eq!(r[1], SampleEcPoints[3].0); assert_eq!(r[2], SampleEcPoints[3].1); - let prime_limbs: [BabyBear; NUM_LIMBS] = prime_limbs(&chip.0.core.air.expr).try_into().unwrap(); + let prime_limbs: [BabyBear; NUM_LIMBS] = prime_limbs(&chip.0.step.expr).try_into().unwrap(); let a_limbs = [BabyBear::ZERO; NUM_LIMBS]; let setup_instruction = rv32_write_heap_default( &mut tester, vec![prime_limbs, a_limbs], /* inputs[0] = prime, inputs[1] = a coeff of weierstrass * equation */ vec![], - chip.0.core.air.offset + Rv32WeierstrassOpcode::SETUP_EC_DOUBLE as usize, + chip.0.step.offset + Rv32WeierstrassOpcode::SETUP_EC_DOUBLE as usize, ); tester.execute(&mut chip, &setup_instruction); @@ -203,7 +200,7 @@ fn test_double() { &mut tester, vec![p1_x_limbs, p1_y_limbs], vec![], - chip.0.core.air.offset + Rv32WeierstrassOpcode::EC_DOUBLE as usize, + chip.0.step.offset + Rv32WeierstrassOpcode::EC_DOUBLE as usize, ); tester.execute(&mut chip, &instruction); @@ -227,12 +224,18 @@ fn test_p256_double() { .unwrap(); let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let adapter = Rv32VecHeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), + + let mut chip = EcDoubleChip::::new( + tester.execution_bridge(), tester.memory_bridge(), + tester.memory_helper(), tester.address_bits(), + config, + Rv32WeierstrassOpcode::CLASS_OFFSET, bitwise_chip.clone(), + tester.range_checker(), + a.clone(), + MAX_INS_CAPACITY, ); // Testing data from: http://point-at-infinity.org/ecc/nisttv @@ -251,17 +254,9 @@ fn test_p256_double() { let p1_y_limbs = biguint_to_limbs::(p1_y.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32); - let mut chip = EcDoubleChip::new( - adapter, - tester.memory_controller().borrow().range_checker.clone(), - config, - Rv32WeierstrassOpcode::CLASS_OFFSET, - a.clone(), - tester.offline_memory_mutex_arc(), - ); - assert_eq!(chip.0.core.air.expr.builder.num_variables, 3); // lambda, x3, y3 + assert_eq!(chip.0.step.expr.builder.num_variables, 3); // lambda, x3, y3 - let r = chip.0.core.air.expr.execute(vec![p1_x, p1_y], vec![true]); + let r = chip.0.step.expr.execute(vec![p1_x, p1_y], vec![true]); assert_eq!(r.len(), 3); // lambda, x3, y3 let expected_double_x = BigUint::from_str_radix( "7CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC47669978", @@ -276,7 +271,7 @@ fn test_p256_double() { assert_eq!(r[1], expected_double_x); assert_eq!(r[2], expected_double_y); - let prime_limbs: [BabyBear; NUM_LIMBS] = prime_limbs(&chip.0.core.air.expr).try_into().unwrap(); + let prime_limbs: [BabyBear; NUM_LIMBS] = prime_limbs(&chip.0.step.expr).try_into().unwrap(); let a_limbs = biguint_to_limbs::(a.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32); let setup_instruction = rv32_write_heap_default( @@ -284,7 +279,7 @@ fn test_p256_double() { vec![prime_limbs, a_limbs], /* inputs[0] = prime, inputs[1] = a coeff of weierstrass * equation */ vec![], - chip.0.core.air.offset + Rv32WeierstrassOpcode::SETUP_EC_DOUBLE as usize, + chip.0.step.offset + Rv32WeierstrassOpcode::SETUP_EC_DOUBLE as usize, ); tester.execute(&mut chip, &setup_instruction); @@ -292,9 +287,11 @@ fn test_p256_double() { &mut tester, vec![p1_x_limbs, p1_y_limbs], vec![], - chip.0.core.air.offset + Rv32WeierstrassOpcode::EC_DOUBLE as usize, + chip.0.step.offset + Rv32WeierstrassOpcode::EC_DOUBLE as usize, ); + tester.execute(&mut chip, &instruction); + // Adding another row to make sure there are dummy rows, and that the dummy row constraints are satisfied tester.execute(&mut chip, &instruction); let tester = tester.build().load(chip).load(bitwise_chip).finalize(); diff --git a/extensions/ecc/circuit/src/weierstrass_extension.rs b/extensions/ecc/circuit/src/weierstrass_extension.rs index 666706cd29..0f0bd25189 100644 --- a/extensions/ecc/circuit/src/weierstrass_extension.rs +++ b/extensions/ecc/circuit/src/weierstrass_extension.rs @@ -4,10 +4,12 @@ use num_traits::{FromPrimitive, Zero}; use once_cell::sync::Lazy; use openvm_algebra_guest::IntMod; use openvm_circuit::{ - arch::{SystemPort, VmExtension, VmInventory, VmInventoryBuilder, VmInventoryError}, + arch::{ + ExecutionBridge, SystemPort, VmExtension, VmInventory, VmInventoryBuilder, VmInventoryError, + }, system::phantom::PhantomChip, }; -use openvm_circuit_derive::{AnyEnum, InstructionExecutor}; +use openvm_circuit_derive::{AnyEnum, InsExecutorE1, InstructionExecutor}; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, }; @@ -19,7 +21,6 @@ use openvm_ecc_guest::{ use openvm_ecc_transpiler::{EccPhantom, Rv32WeierstrassOpcode}; use openvm_instructions::{LocalOpcode, PhantomDiscriminant, VmOpcode}; use openvm_mod_circuit_builder::ExprBuilderConfig; -use openvm_rv32_adapters::Rv32VecHeapAdapterChip; use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr}; @@ -27,6 +28,9 @@ use strum::EnumCount; use super::{EcAddNeChip, EcDoubleChip}; +// TODO: this should be decided after e2 execution +const MAX_INS_CAPACITY: usize = 1 << 22; + #[serde_as] #[derive(Clone, Debug, derive_new::new, Serialize, Deserialize)] pub struct CurveConfig { @@ -63,7 +67,7 @@ pub struct WeierstrassExtension { pub supported_curves: Vec, } -#[derive(Chip, ChipUsageGetter, InstructionExecutor, AnyEnum)] +#[derive(Chip, ChipUsageGetter, InstructionExecutor, AnyEnum, InsExecutorE1)] pub enum WeierstrassExtensionExecutor { // 32 limbs prime EcAddNeRv32_32(EcAddNeChip), @@ -93,6 +97,11 @@ impl VmExtension for WeierstrassExtension { program_bus, memory_bridge, } = builder.system_port(); + + let execution_bridge = ExecutionBridge::new(execution_bus, program_bus); + let range_checker = builder.system_base().range_checker_chip.clone(); + let pointer_max_bits = builder.system_config().memory_config.pointer_max_bits; + let bitwise_lu_chip = if let Some(&chip) = builder .find_chip::>() .first() @@ -104,9 +113,7 @@ impl VmExtension for WeierstrassExtension { inventory.add_periphery_chip(chip.clone()); chip }; - let offline_memory = builder.system_base().offline_memory(); - let range_checker = builder.system_base().range_checker_chip.clone(); - let pointer_bits = builder.system_config().memory_config.pointer_max_bits; + let ec_add_ne_opcodes = (Rv32WeierstrassOpcode::EC_ADD_NE as usize) ..=(Rv32WeierstrassOpcode::SETUP_EC_ADD_NE as usize); let ec_double_opcodes = (Rv32WeierstrassOpcode::EC_DOUBLE as usize) @@ -128,18 +135,17 @@ impl VmExtension for WeierstrassExtension { }; if bytes <= 32 { let add_ne_chip = EcAddNeChip::new( - Rv32VecHeapAdapterChip::::new( - execution_bus, - program_bus, - memory_bridge, - pointer_bits, - bitwise_lu_chip.clone(), - ), + execution_bridge.clone(), + memory_bridge.clone(), + builder.system_base().memory_controller.helper(), + pointer_max_bits, config32.clone(), start_offset, + bitwise_lu_chip.clone(), range_checker.clone(), - offline_memory.clone(), + MAX_INS_CAPACITY, ); + inventory.add_executor( WeierstrassExtensionExecutor::EcAddNeRv32_32(add_ne_chip), ec_add_ne_opcodes @@ -147,18 +153,16 @@ impl VmExtension for WeierstrassExtension { .map(|x| VmOpcode::from_usize(x + start_offset)), )?; let double_chip = EcDoubleChip::new( - Rv32VecHeapAdapterChip::::new( - execution_bus, - program_bus, - memory_bridge, - pointer_bits, - bitwise_lu_chip.clone(), - ), - range_checker.clone(), + execution_bridge.clone(), + memory_bridge.clone(), + builder.system_base().memory_controller.helper(), + pointer_max_bits, config32.clone(), start_offset, + bitwise_lu_chip.clone(), + range_checker.clone(), curve.a.clone(), - offline_memory.clone(), + MAX_INS_CAPACITY, ); inventory.add_executor( WeierstrassExtensionExecutor::EcDoubleRv32_32(double_chip), @@ -168,40 +172,37 @@ impl VmExtension for WeierstrassExtension { )?; } else if bytes <= 48 { let add_ne_chip = EcAddNeChip::new( - Rv32VecHeapAdapterChip::::new( - execution_bus, - program_bus, - memory_bridge, - pointer_bits, - bitwise_lu_chip.clone(), - ), + execution_bridge.clone(), + memory_bridge.clone(), + builder.system_base().memory_controller.helper(), + pointer_max_bits, config48.clone(), start_offset, + bitwise_lu_chip.clone(), range_checker.clone(), - offline_memory.clone(), + MAX_INS_CAPACITY, ); + inventory.add_executor( - WeierstrassExtensionExecutor::EcAddNeRv32_48(add_ne_chip), + WeierstrassExtensionExecutor::EcAddNeRv32_32(add_ne_chip), ec_add_ne_opcodes .clone() .map(|x| VmOpcode::from_usize(x + start_offset)), )?; let double_chip = EcDoubleChip::new( - Rv32VecHeapAdapterChip::::new( - execution_bus, - program_bus, - memory_bridge, - pointer_bits, - bitwise_lu_chip.clone(), - ), - range_checker.clone(), + execution_bridge.clone(), + memory_bridge.clone(), + builder.system_base().memory_controller.helper(), + pointer_max_bits, config48.clone(), start_offset, + bitwise_lu_chip.clone(), + range_checker.clone(), curve.a.clone(), - offline_memory.clone(), + MAX_INS_CAPACITY, ); inventory.add_executor( - WeierstrassExtensionExecutor::EcDoubleRv32_48(double_chip), + WeierstrassExtensionExecutor::EcDoubleRv32_32(double_chip), ec_double_opcodes .clone() .map(|x| VmOpcode::from_usize(x + start_offset)), diff --git a/extensions/pairing/circuit/src/fp12_chip/add.rs b/extensions/pairing/circuit/src/fp12_chip/add.rs deleted file mode 100644 index 643c68ef27..0000000000 --- a/extensions/pairing/circuit/src/fp12_chip/add.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::{cell::RefCell, rc::Rc}; - -use openvm_circuit_primitives::var_range::VariableRangeCheckerBus; -use openvm_mod_circuit_builder::{ExprBuilder, ExprBuilderConfig, FieldExpr}; - -use crate::Fp12; - -pub fn fp12_add_expr(config: ExprBuilderConfig, range_bus: VariableRangeCheckerBus) -> FieldExpr { - config.check_valid(); - let builder = ExprBuilder::new(config, range_bus.range_max_bits); - let builder = Rc::new(RefCell::new(builder)); - - let mut x = Fp12::new(builder.clone()); - let mut y = Fp12::new(builder.clone()); - let mut res = x.add(&mut y); - res.save_output(); - - let builder = builder.borrow().clone(); - FieldExpr::new(builder, range_bus, false) -} diff --git a/extensions/pairing/circuit/src/fp12_chip/mod.rs b/extensions/pairing/circuit/src/fp12_chip/mod.rs deleted file mode 100644 index c6894d0d27..0000000000 --- a/extensions/pairing/circuit/src/fp12_chip/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -mod add; -mod mul; -mod sub; - -pub use add::*; -pub use mul::*; -pub use sub::*; - -#[cfg(test)] -mod tests; diff --git a/extensions/pairing/circuit/src/fp12_chip/mul.rs b/extensions/pairing/circuit/src/fp12_chip/mul.rs deleted file mode 100644 index 0736981de7..0000000000 --- a/extensions/pairing/circuit/src/fp12_chip/mul.rs +++ /dev/null @@ -1,175 +0,0 @@ -use std::{ - cell::RefCell, - rc::Rc, - sync::{Arc, Mutex}, -}; - -use openvm_circuit::{arch::VmChipWrapper, system::memory::OfflineMemory}; -use openvm_circuit_derive::InstructionExecutor; -use openvm_circuit_primitives::var_range::{ - SharedVariableRangeCheckerChip, VariableRangeCheckerBus, -}; -use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; -use openvm_mod_circuit_builder::{ - ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreChip, -}; -use openvm_pairing_transpiler::Fp12Opcode; -use openvm_rv32_adapters::Rv32VecHeapAdapterChip; -use openvm_stark_backend::p3_field::PrimeField32; - -use crate::Fp12; -// Input: Fp12 * 2 -// Output: Fp12 -#[derive(Chip, ChipUsageGetter, InstructionExecutor)] -pub struct Fp12MulChip( - pub VmChipWrapper< - F, - Rv32VecHeapAdapterChip, - FieldExpressionCoreChip, - >, -); - -impl - Fp12MulChip -{ - pub fn new( - adapter: Rv32VecHeapAdapterChip, - config: ExprBuilderConfig, - xi: [isize; 2], - offset: usize, - range_checker: SharedVariableRangeCheckerChip, - offline_memory: Arc>>, - ) -> Self { - let expr = fp12_mul_expr(config, range_checker.bus(), xi); - let core = FieldExpressionCoreChip::new( - expr, - offset, - vec![Fp12Opcode::MUL as usize], - vec![], - range_checker, - "Fp12Mul", - false, - ); - Self(VmChipWrapper::new(adapter, core, offline_memory)) - } -} - -pub fn fp12_mul_expr( - config: ExprBuilderConfig, - range_bus: VariableRangeCheckerBus, - xi: [isize; 2], -) -> FieldExpr { - config.check_valid(); - let builder = ExprBuilder::new(config, range_bus.range_max_bits); - let builder = Rc::new(RefCell::new(builder)); - - let mut x = Fp12::new(builder.clone()); - let mut y = Fp12::new(builder.clone()); - let mut res = x.mul(&mut y, xi); - res.save_output(); - - let builder = builder.borrow().clone(); - FieldExpr::new(builder, range_bus, false) -} - -#[cfg(test)] -mod tests { - use halo2curves_axiom::{bn256::Fq12, ff::Field}; - use itertools::Itertools; - use openvm_circuit::arch::testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}; - use openvm_circuit_primitives::bitwise_op_lookup::{ - BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, - }; - use openvm_ecc_guest::algebra::field::FieldExtension; - use openvm_instructions::{riscv::RV32_CELL_BITS, LocalOpcode}; - use openvm_mod_circuit_builder::{ - test_utils::{biguint_to_limbs, bn254_fq12_to_biguint_vec, bn254_fq2_to_biguint_vec}, - ExprBuilderConfig, - }; - use openvm_pairing_guest::bn254::{BN254_MODULUS, BN254_XI_ISIZE}; - use openvm_rv32_adapters::rv32_write_heap_default_with_increment; - use openvm_stark_backend::p3_field::FieldAlgebra; - use openvm_stark_sdk::p3_baby_bear::BabyBear; - use rand::{rngs::StdRng, SeedableRng}; - - use super::*; - - const LIMB_BITS: usize = 8; - type F = BabyBear; - - #[test] - fn test_fp12_mul_bn254() { - const NUM_LIMBS: usize = 32; - const BLOCK_SIZE: usize = 32; - - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let config = ExprBuilderConfig { - modulus: BN254_MODULUS.clone(), - num_limbs: NUM_LIMBS, - limb_bits: LIMB_BITS, - }; - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let adapter = Rv32VecHeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), - ); - - let mut chip = Fp12MulChip::new( - adapter, - config, - BN254_XI_ISIZE, - Fp12Opcode::CLASS_OFFSET, - tester.range_checker(), - tester.offline_memory_mutex_arc(), - ); - - let mut rng = StdRng::seed_from_u64(64); - let x = Fq12::random(&mut rng); - let y = Fq12::random(&mut rng); - let inputs = [x.to_coeffs(), y.to_coeffs()] - .concat() - .iter() - .flat_map(|&x| bn254_fq2_to_biguint_vec(x)) - .collect::>(); - - let cmp = bn254_fq12_to_biguint_vec(x * y); - let res = chip - .0 - .core - .expr() - .execute_with_output(inputs.clone(), vec![true]); - assert_eq!(res.len(), cmp.len()); - for i in 0..res.len() { - assert_eq!(res[i], cmp[i]); - } - - let x_limbs = inputs[..12] - .iter() - .map(|x| { - biguint_to_limbs::(x.clone(), LIMB_BITS) - .map(BabyBear::from_canonical_u32) - }) - .collect_vec(); - let y_limbs = inputs[12..] - .iter() - .map(|y| { - biguint_to_limbs::(y.clone(), LIMB_BITS) - .map(BabyBear::from_canonical_u32) - }) - .collect_vec(); - let instruction = rv32_write_heap_default_with_increment( - &mut tester, - x_limbs, - y_limbs, - 512, - chip.0.core.air.offset + Fp12Opcode::MUL as usize, - ); - tester.execute(&mut chip, &instruction); - let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - tester.simple_test().expect("Verification failed"); - } -} diff --git a/extensions/pairing/circuit/src/fp12_chip/sub.rs b/extensions/pairing/circuit/src/fp12_chip/sub.rs deleted file mode 100644 index 470e700910..0000000000 --- a/extensions/pairing/circuit/src/fp12_chip/sub.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::{cell::RefCell, rc::Rc}; - -use openvm_circuit_primitives::var_range::VariableRangeCheckerBus; -use openvm_mod_circuit_builder::{ExprBuilder, ExprBuilderConfig, FieldExpr}; - -use crate::Fp12; - -pub fn fp12_sub_expr(config: ExprBuilderConfig, range_bus: VariableRangeCheckerBus) -> FieldExpr { - config.check_valid(); - let builder = ExprBuilder::new(config, range_bus.range_max_bits); - let builder = Rc::new(RefCell::new(builder)); - - let mut x = Fp12::new(builder.clone()); - let mut y = Fp12::new(builder.clone()); - let mut res = x.sub(&mut y); - res.save_output(); - - let builder = builder.borrow().clone(); - FieldExpr::new(builder, range_bus, false) -} diff --git a/extensions/pairing/circuit/src/fp12_chip/tests.rs b/extensions/pairing/circuit/src/fp12_chip/tests.rs deleted file mode 100644 index a9f6b235d5..0000000000 --- a/extensions/pairing/circuit/src/fp12_chip/tests.rs +++ /dev/null @@ -1,271 +0,0 @@ -use num_bigint::BigUint; -use openvm_circuit::arch::{ - testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - VmChipWrapper, -}; -use openvm_circuit_primitives::bitwise_op_lookup::{ - BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, -}; -use openvm_instructions::{riscv::RV32_CELL_BITS, LocalOpcode}; -use openvm_mod_circuit_builder::{ - test_utils::{ - biguint_to_limbs, bls12381_fq12_random, bn254_fq12_random, bn254_fq12_to_biguint_vec, - }, - ExprBuilderConfig, FieldExpr, FieldExpressionCoreChip, -}; -use openvm_pairing_guest::{ - bls12_381::{ - BLS12_381_BLOCK_SIZE, BLS12_381_LIMB_BITS, BLS12_381_MODULUS, BLS12_381_NUM_LIMBS, - BLS12_381_XI_ISIZE, - }, - bn254::{BN254_BLOCK_SIZE, BN254_LIMB_BITS, BN254_MODULUS, BN254_NUM_LIMBS, BN254_XI_ISIZE}, -}; -use openvm_pairing_transpiler::{Bls12381Fp12Opcode, Bn254Fp12Opcode, Fp12Opcode}; -use openvm_rv32_adapters::{rv32_write_heap_default, Rv32VecHeapAdapterChip}; -use openvm_stark_backend::p3_field::FieldAlgebra; -use openvm_stark_sdk::p3_baby_bear::BabyBear; - -use super::{fp12_add_expr, fp12_mul_expr, fp12_sub_expr}; - -type F = BabyBear; - -#[allow(clippy::too_many_arguments)] -fn test_fp12_fn< - const INPUT_SIZE: usize, - const NUM_LIMBS: usize, - const LIMB_BITS: usize, - const BLOCK_SIZE: usize, ->( - mut tester: VmChipTestBuilder, - expr: FieldExpr, - offset: usize, - local_opcode_idx: usize, - name: &str, - x: Vec, - y: Vec, - var_len: usize, -) { - let core = FieldExpressionCoreChip::new( - expr, - offset, - vec![local_opcode_idx], - vec![], - tester.memory_controller().borrow().range_checker.clone(), - name, - false, - ); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - - let adapter = - Rv32VecHeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), - ); - - let x_limbs = x - .iter() - .map(|x| { - biguint_to_limbs::(x.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32) - }) - .collect::>(); - let y_limbs = y - .iter() - .map(|y| { - biguint_to_limbs::(y.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32) - }) - .collect::>(); - let mut chip = VmChipWrapper::new(adapter, core, tester.offline_memory_mutex_arc()); - - let res = chip.core.air.expr.execute([x, y].concat(), vec![]); - assert_eq!(res.len(), var_len); - - let instruction = rv32_write_heap_default( - &mut tester, - x_limbs, - y_limbs, - chip.core.air.offset + local_opcode_idx, - ); - tester.execute(&mut chip, &instruction); - - let run_tester = tester.build().load(chip).load(bitwise_chip).finalize(); - run_tester.simple_test().expect("Verification failed"); -} - -#[test] -fn test_fp12_add_bn254() { - let tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let config = ExprBuilderConfig { - modulus: BN254_MODULUS.clone(), - num_limbs: BN254_NUM_LIMBS, - limb_bits: BN254_LIMB_BITS, - }; - let expr = fp12_add_expr( - config, - tester.memory_controller().borrow().range_checker.bus(), - ); - - let x = bn254_fq12_to_biguint_vec(bn254_fq12_random(1)); - let y = bn254_fq12_to_biguint_vec(bn254_fq12_random(2)); - - test_fp12_fn::<12, BN254_NUM_LIMBS, BN254_LIMB_BITS, BN254_BLOCK_SIZE>( - tester, - expr, - Bn254Fp12Opcode::CLASS_OFFSET, - Fp12Opcode::ADD as usize, - "Bn254Fp12Add", - x, - y, - 12, - ); -} - -#[test] -fn test_fp12_sub_bn254() { - let tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let config = ExprBuilderConfig { - modulus: BN254_MODULUS.clone(), - num_limbs: BN254_NUM_LIMBS, - limb_bits: BN254_LIMB_BITS, - }; - let expr = fp12_sub_expr( - config, - tester.memory_controller().borrow().range_checker.bus(), - ); - - let x = bn254_fq12_to_biguint_vec(bn254_fq12_random(59)); - let y = bn254_fq12_to_biguint_vec(bn254_fq12_random(3)); - - test_fp12_fn::<12, BN254_NUM_LIMBS, BN254_LIMB_BITS, BN254_BLOCK_SIZE>( - tester, - expr, - Bn254Fp12Opcode::CLASS_OFFSET, - Fp12Opcode::SUB as usize, - "Bn254Fp12Sub", - x, - y, - 12, - ); -} - -#[test] -fn test_fp12_mul_bn254() { - let tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let config = ExprBuilderConfig { - modulus: BN254_MODULUS.clone(), - num_limbs: BN254_NUM_LIMBS, - limb_bits: BN254_LIMB_BITS, - }; - let xi = BN254_XI_ISIZE; - let expr = fp12_mul_expr( - config, - tester.memory_controller().borrow().range_checker.bus(), - xi, - ); - - let x = bn254_fq12_to_biguint_vec(bn254_fq12_random(5)); - let y = bn254_fq12_to_biguint_vec(bn254_fq12_random(25)); - - test_fp12_fn::<12, BN254_NUM_LIMBS, BN254_LIMB_BITS, BN254_BLOCK_SIZE>( - tester, - expr, - Bn254Fp12Opcode::CLASS_OFFSET, - Fp12Opcode::MUL as usize, - "Bn254Fp12Mul", - x, - y, - 33, - ); -} - -#[test] -fn test_fp12_add_bls12381() { - let tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let config = ExprBuilderConfig { - modulus: BLS12_381_MODULUS.clone(), - num_limbs: BLS12_381_NUM_LIMBS, - limb_bits: BLS12_381_LIMB_BITS, - }; - let expr = fp12_add_expr( - config, - tester.memory_controller().borrow().range_checker.bus(), - ); - - let x = bls12381_fq12_random(3); - let y = bls12381_fq12_random(99); - - test_fp12_fn::<36, BLS12_381_NUM_LIMBS, BLS12_381_LIMB_BITS, BLS12_381_BLOCK_SIZE>( - tester, - expr, - Bls12381Fp12Opcode::CLASS_OFFSET, - Fp12Opcode::ADD as usize, - "Bls12381Fp12Add", - x, - y, - 12, - ); -} - -#[test] -fn test_fp12_sub_bls12381() { - let tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let config = ExprBuilderConfig { - modulus: BLS12_381_MODULUS.clone(), - num_limbs: BLS12_381_NUM_LIMBS, - limb_bits: BLS12_381_LIMB_BITS, - }; - let expr = fp12_sub_expr( - config, - tester.memory_controller().borrow().range_checker.bus(), - ); - - let x = bls12381_fq12_random(8); - let y = bls12381_fq12_random(9); - - test_fp12_fn::<36, BLS12_381_NUM_LIMBS, BLS12_381_LIMB_BITS, BLS12_381_BLOCK_SIZE>( - tester, - expr, - Bls12381Fp12Opcode::CLASS_OFFSET, - Fp12Opcode::SUB as usize, - "Bls12381Fp12Sub", - x, - y, - 12, - ); -} - -// NOTE[yj]: This test requires RUST_MIN_STACK=8388608 to run without overflowing the stack, so it -// is ignored by the test runner for now -#[test] -#[ignore] -fn test_fp12_mul_bls12381() { - let tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let config = ExprBuilderConfig { - modulus: BLS12_381_MODULUS.clone(), - num_limbs: BLS12_381_NUM_LIMBS, - limb_bits: BLS12_381_LIMB_BITS, - }; - let xi = BLS12_381_XI_ISIZE; - let expr = fp12_mul_expr( - config, - tester.memory_controller().borrow().range_checker.bus(), - xi, - ); - - let x = bls12381_fq12_random(5); - let y = bls12381_fq12_random(25); - - test_fp12_fn::<36, BLS12_381_NUM_LIMBS, BLS12_381_LIMB_BITS, BLS12_381_BLOCK_SIZE>( - tester, - expr, - Bls12381Fp12Opcode::CLASS_OFFSET, - Fp12Opcode::MUL as usize, - "Bls12381Fp12Mul", - x, - y, - 46, - ); -} diff --git a/extensions/pairing/circuit/src/lib.rs b/extensions/pairing/circuit/src/lib.rs index b2b962b7f7..f96d126555 100644 --- a/extensions/pairing/circuit/src/lib.rs +++ b/extensions/pairing/circuit/src/lib.rs @@ -1,11 +1,7 @@ mod config; mod fp12; -mod fp12_chip; -mod pairing_chip; mod pairing_extension; pub use config::*; pub use fp12::*; -pub use fp12_chip::*; -pub use pairing_chip::*; pub use pairing_extension::*; diff --git a/extensions/pairing/circuit/src/pairing_chip/line/d_type/mod.rs b/extensions/pairing/circuit/src/pairing_chip/line/d_type/mod.rs deleted file mode 100644 index 08857995f3..0000000000 --- a/extensions/pairing/circuit/src/pairing_chip/line/d_type/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod mul_013_by_013; -mod mul_by_01234; - -pub use mul_013_by_013::*; -pub use mul_by_01234::*; - -#[cfg(test)] -mod tests; diff --git a/extensions/pairing/circuit/src/pairing_chip/line/d_type/mul_013_by_013.rs b/extensions/pairing/circuit/src/pairing_chip/line/d_type/mul_013_by_013.rs deleted file mode 100644 index 36d1012e9b..0000000000 --- a/extensions/pairing/circuit/src/pairing_chip/line/d_type/mul_013_by_013.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::{ - cell::RefCell, - rc::Rc, - sync::{Arc, Mutex}, -}; - -use openvm_algebra_circuit::Fp2; -use openvm_circuit::{arch::VmChipWrapper, system::memory::OfflineMemory}; -use openvm_circuit_derive::InstructionExecutor; -use openvm_circuit_primitives::var_range::{ - SharedVariableRangeCheckerChip, VariableRangeCheckerBus, -}; -use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; -use openvm_mod_circuit_builder::{ - ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreChip, -}; -use openvm_pairing_transpiler::PairingOpcode; -use openvm_rv32_adapters::Rv32VecHeapAdapterChip; -use openvm_stark_backend::p3_field::PrimeField32; - -// Input: line0.b, line0.c, line1.b, line1.c : 2 x 4 field elements -// Output: 5 Fp2 coefficients -> 10 field elements -#[derive(Chip, ChipUsageGetter, InstructionExecutor)] -pub struct EcLineMul013By013Chip< - F: PrimeField32, - const INPUT_BLOCKS: usize, - const OUTPUT_BLOCKS: usize, - const BLOCK_SIZE: usize, ->( - pub VmChipWrapper< - F, - Rv32VecHeapAdapterChip, - FieldExpressionCoreChip, - >, -); - -impl< - F: PrimeField32, - const INPUT_BLOCKS: usize, - const OUTPUT_BLOCKS: usize, - const BLOCK_SIZE: usize, - > EcLineMul013By013Chip -{ - pub fn new( - adapter: Rv32VecHeapAdapterChip, - range_checker: SharedVariableRangeCheckerChip, - config: ExprBuilderConfig, - xi: [isize; 2], - offset: usize, - offline_memory: Arc>>, - ) -> Self { - assert!( - xi[0].unsigned_abs() < 1 << config.limb_bits, - "expect xi to be small" - ); // not a hard rule, but we expect xi to be small - assert!( - xi[1].unsigned_abs() < 1 << config.limb_bits, - "expect xi to be small" - ); - let expr = mul_013_by_013_expr(config, range_checker.bus(), xi); - let core = FieldExpressionCoreChip::new( - expr, - offset, - vec![PairingOpcode::MUL_013_BY_013 as usize], - vec![], - range_checker, - "Mul013By013", - true, - ); - Self(VmChipWrapper::new(adapter, core, offline_memory)) - } -} - -pub fn mul_013_by_013_expr( - config: ExprBuilderConfig, - range_bus: VariableRangeCheckerBus, - xi: [isize; 2], -) -> FieldExpr { - config.check_valid(); - let builder = ExprBuilder::new(config.clone(), range_bus.range_max_bits); - let builder = Rc::new(RefCell::new(builder)); - - let mut b0 = Fp2::new(builder.clone()); - let mut c0 = Fp2::new(builder.clone()); - let mut b1 = Fp2::new(builder.clone()); - let mut c1 = Fp2::new(builder.clone()); - - // where w⁶ = xi - // l0 * l1 = 1 + (b0 + b1)w + (b0b1)w² + (c0 + c1)w³ + (b0c1 + b1c0)w⁴ + (c0c1)w⁶ - // = (1 + c0c1 * xi) + (b0 + b1)w + (b0b1)w² + (c0 + c1)w³ + (b0c1 + b1c0)w⁴ - let l0 = c0.mul(&mut c1).int_mul(xi).int_add([1, 0]); - let l1 = b0.add(&mut b1); - let l2 = b0.mul(&mut b1); - let l3 = c0.add(&mut c1); - let l4 = b0.mul(&mut c1).add(&mut b1.mul(&mut c0)); - - [l0, l1, l2, l3, l4].map(|mut l| l.save_output()); - - let builder = builder.borrow().clone(); - FieldExpr::new(builder, range_bus, false) -} diff --git a/extensions/pairing/circuit/src/pairing_chip/line/d_type/mul_by_01234.rs b/extensions/pairing/circuit/src/pairing_chip/line/d_type/mul_by_01234.rs deleted file mode 100644 index 996372e994..0000000000 --- a/extensions/pairing/circuit/src/pairing_chip/line/d_type/mul_by_01234.rs +++ /dev/null @@ -1,113 +0,0 @@ -use std::{ - cell::RefCell, - rc::Rc, - sync::{Arc, Mutex}, -}; - -use openvm_algebra_circuit::Fp2; -use openvm_circuit::{arch::VmChipWrapper, system::memory::OfflineMemory}; -use openvm_circuit_derive::InstructionExecutor; -use openvm_circuit_primitives::var_range::{ - SharedVariableRangeCheckerChip, VariableRangeCheckerBus, -}; -use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; -use openvm_mod_circuit_builder::{ - ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreChip, -}; -use openvm_pairing_transpiler::PairingOpcode; -use openvm_rv32_adapters::Rv32VecHeapTwoReadsAdapterChip; -use openvm_stark_backend::p3_field::PrimeField32; - -use crate::Fp12; - -// Input: Fp12 (12 field elements), [Fp2; 5] (5 x 2 field elements) -// Output: Fp12 (12 field elements) -#[derive(Chip, ChipUsageGetter, InstructionExecutor)] -pub struct EcLineMulBy01234Chip< - F: PrimeField32, - const INPUT_BLOCKS1: usize, - const INPUT_BLOCKS2: usize, - const OUTPUT_BLOCKS: usize, - const BLOCK_SIZE: usize, ->( - pub VmChipWrapper< - F, - Rv32VecHeapTwoReadsAdapterChip< - F, - INPUT_BLOCKS1, - INPUT_BLOCKS2, - OUTPUT_BLOCKS, - BLOCK_SIZE, - BLOCK_SIZE, - >, - FieldExpressionCoreChip, - >, -); - -impl< - F: PrimeField32, - const INPUT_BLOCKS1: usize, - const INPUT_BLOCKS2: usize, - const OUTPUT_BLOCKS: usize, - const BLOCK_SIZE: usize, - > EcLineMulBy01234Chip -{ - pub fn new( - adapter: Rv32VecHeapTwoReadsAdapterChip< - F, - INPUT_BLOCKS1, - INPUT_BLOCKS2, - OUTPUT_BLOCKS, - BLOCK_SIZE, - BLOCK_SIZE, - >, - config: ExprBuilderConfig, - xi: [isize; 2], - offset: usize, - range_checker: SharedVariableRangeCheckerChip, - offline_memory: Arc>>, - ) -> Self { - assert!( - xi[0].unsigned_abs() < 1 << config.limb_bits, - "expect xi to be small" - ); // not a hard rule, but we expect xi to be small - assert!( - xi[1].unsigned_abs() < 1 << config.limb_bits, - "expect xi to be small" - ); - let expr = mul_by_01234_expr(config, range_checker.bus(), xi); - let core = FieldExpressionCoreChip::new( - expr, - offset, - vec![PairingOpcode::MUL_BY_01234 as usize], - vec![], - range_checker.clone(), - "MulBy01234", - false, - ); - Self(VmChipWrapper::new(adapter, core, offline_memory)) - } -} - -pub fn mul_by_01234_expr( - config: ExprBuilderConfig, - range_bus: VariableRangeCheckerBus, - xi: [isize; 2], -) -> FieldExpr { - config.check_valid(); - let builder = ExprBuilder::new(config.clone(), range_bus.range_max_bits); - let builder = Rc::new(RefCell::new(builder)); - - let mut f = Fp12::new(builder.clone()); - let mut x0 = Fp2::new(builder.clone()); - let mut x1 = Fp2::new(builder.clone()); - let mut x2 = Fp2::new(builder.clone()); - let mut x3 = Fp2::new(builder.clone()); - let mut x4 = Fp2::new(builder.clone()); - - let mut r = f.mul_by_01234(&mut x0, &mut x1, &mut x2, &mut x3, &mut x4, xi); - r.save_output(); - - let builder = builder.borrow().clone(); - FieldExpr::new(builder, range_bus, false) -} diff --git a/extensions/pairing/circuit/src/pairing_chip/line/d_type/tests.rs b/extensions/pairing/circuit/src/pairing_chip/line/d_type/tests.rs deleted file mode 100644 index 81da3169fa..0000000000 --- a/extensions/pairing/circuit/src/pairing_chip/line/d_type/tests.rs +++ /dev/null @@ -1,287 +0,0 @@ -use halo2curves_axiom::{ - bn256::{Fq, Fq12, Fq2, G1Affine}, - ff::Field, -}; -use openvm_circuit::arch::testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}; -use openvm_circuit_primitives::bitwise_op_lookup::{ - BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, -}; -use openvm_ecc_guest::AffinePoint; -use openvm_instructions::{riscv::RV32_CELL_BITS, LocalOpcode}; -use openvm_mod_circuit_builder::{ - test_utils::{ - biguint_to_limbs, bn254_fq12_to_biguint_vec, bn254_fq2_to_biguint_vec, bn254_fq_to_biguint, - }, - ExprBuilderConfig, -}; -use openvm_pairing_guest::{ - bn254::{BN254_LIMB_BITS, BN254_MODULUS, BN254_NUM_LIMBS, BN254_XI_ISIZE}, - halo2curves_shims::bn254::{tangent_line_013, Bn254}, - pairing::{Evaluatable, LineMulDType, UnevaluatedLine}, -}; -use openvm_pairing_transpiler::PairingOpcode; -use openvm_rv32_adapters::{ - rv32_write_heap_default, rv32_write_heap_default_with_increment, Rv32VecHeapAdapterChip, - Rv32VecHeapTwoReadsAdapterChip, -}; -use openvm_stark_backend::p3_field::FieldAlgebra; -use openvm_stark_sdk::p3_baby_bear::BabyBear; -use rand::{rngs::StdRng, SeedableRng}; - -use super::{super::EvaluateLineChip, *}; - -type F = BabyBear; -const NUM_LIMBS: usize = 32; -const LIMB_BITS: usize = 8; -const BLOCK_SIZE: usize = 32; - -#[test] -fn test_mul_013_by_013() { - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let adapter = Rv32VecHeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), - ); - let mut chip = EcLineMul013By013Chip::new( - adapter, - tester.memory_controller().borrow().range_checker.clone(), - ExprBuilderConfig { - modulus: BN254_MODULUS.clone(), - num_limbs: NUM_LIMBS, - limb_bits: LIMB_BITS, - }, - BN254_XI_ISIZE, - PairingOpcode::CLASS_OFFSET, - tester.offline_memory_mutex_arc(), - ); - - let mut rng0 = StdRng::seed_from_u64(8); - let mut rng1 = StdRng::seed_from_u64(95); - let rnd_pt_0 = G1Affine::random(&mut rng0); - let rnd_pt_1 = G1Affine::random(&mut rng1); - let ec_pt_0 = AffinePoint:: { - x: rnd_pt_0.x, - y: rnd_pt_0.y, - }; - let ec_pt_1 = AffinePoint:: { - x: rnd_pt_1.x, - y: rnd_pt_1.y, - }; - let line0 = tangent_line_013::(ec_pt_0); - let line1 = tangent_line_013::(ec_pt_1); - let input_line0 = [ - bn254_fq2_to_biguint_vec(line0.b), - bn254_fq2_to_biguint_vec(line0.c), - ] - .concat(); - let input_line1 = [ - bn254_fq2_to_biguint_vec(line1.b), - bn254_fq2_to_biguint_vec(line1.c), - ] - .concat(); - - let vars = chip - .0 - .core - .expr() - .execute([input_line0.clone(), input_line1.clone()].concat(), vec![]); - let output_indices = chip.0.core.expr().builder.output_indices.clone(); - let output = output_indices - .iter() - .map(|i| vars[*i].clone()) - .collect::>(); - assert_eq!(output.len(), 10); - - let r_cmp = Bn254::mul_013_by_013(&line0, &line1); - let r_cmp_bigint = r_cmp - .map(|x| [bn254_fq_to_biguint(x.c0), bn254_fq_to_biguint(x.c1)]) - .concat(); - - for i in 0..10 { - assert_eq!(output[i], r_cmp_bigint[i]); - } - - let input_line0_limbs = input_line0 - .iter() - .map(|x| { - biguint_to_limbs::(x.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32) - }) - .collect::>(); - let input_line1_limbs = input_line1 - .iter() - .map(|x| { - biguint_to_limbs::(x.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32) - }) - .collect::>(); - - let instruction = rv32_write_heap_default( - &mut tester, - input_line0_limbs, - input_line1_limbs, - chip.0.core.air.offset + PairingOpcode::MUL_013_BY_013 as usize, - ); - - tester.execute(&mut chip, &instruction); - let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - tester.simple_test().expect("Verification failed"); -} - -#[test] -fn test_mul_by_01234() { - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let adapter = Rv32VecHeapTwoReadsAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), - ); - let mut chip = EcLineMulBy01234Chip::new( - adapter, - ExprBuilderConfig { - modulus: BN254_MODULUS.clone(), - num_limbs: NUM_LIMBS, - limb_bits: LIMB_BITS, - }, - BN254_XI_ISIZE, - PairingOpcode::CLASS_OFFSET, - tester.range_checker(), - tester.offline_memory_mutex_arc(), - ); - - let mut rng = StdRng::seed_from_u64(8); - let f = Fq12::random(&mut rng); - let x0 = Fq2::random(&mut rng); - let x1 = Fq2::random(&mut rng); - let x2 = Fq2::random(&mut rng); - let x3 = Fq2::random(&mut rng); - let x4 = Fq2::random(&mut rng); - - let input_f = bn254_fq12_to_biguint_vec(f); - let input_x = [ - bn254_fq2_to_biguint_vec(x0), - bn254_fq2_to_biguint_vec(x1), - bn254_fq2_to_biguint_vec(x2), - bn254_fq2_to_biguint_vec(x3), - bn254_fq2_to_biguint_vec(x4), - ] - .concat(); - - let vars = chip - .0 - .core - .expr() - .execute([input_f.clone(), input_x.clone()].concat(), vec![]); - let output_indices = chip.0.core.expr().builder.output_indices.clone(); - let output = output_indices - .iter() - .map(|i| vars[*i].clone()) - .collect::>(); - assert_eq!(output.len(), 12); - - let r_cmp = Bn254::mul_by_01234(&f, &[x0, x1, x2, x3, x4]); - let r_cmp_bigint = bn254_fq12_to_biguint_vec(r_cmp); - - for i in 0..12 { - assert_eq!(output[i], r_cmp_bigint[i]); - } - - let input_f_limbs = input_f - .iter() - .map(|x| { - biguint_to_limbs::(x.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32) - }) - .collect::>(); - let input_x_limbs = input_x - .iter() - .map(|x| { - biguint_to_limbs::(x.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32) - }) - .collect::>(); - - let instruction = rv32_write_heap_default_with_increment( - &mut tester, - input_f_limbs, - input_x_limbs, - 512, - chip.0.core.air.offset + PairingOpcode::MUL_BY_01234 as usize, - ); - - tester.execute(&mut chip, &instruction); - let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - tester.simple_test().expect("Verification failed"); -} - -#[test] -fn test_evaluate_line() { - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let config = ExprBuilderConfig { - modulus: BN254_MODULUS.clone(), - limb_bits: BN254_LIMB_BITS, - num_limbs: BN254_NUM_LIMBS, - }; - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let adapter = Rv32VecHeapTwoReadsAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), - ); - let mut chip = EvaluateLineChip::new( - adapter, - config, - PairingOpcode::CLASS_OFFSET, - tester.range_checker(), - tester.offline_memory_mutex_arc(), - ); - - let mut rng = StdRng::seed_from_u64(42); - let uneval_b = Fq2::random(&mut rng); - let uneval_c = Fq2::random(&mut rng); - let x_over_y = Fq::random(&mut rng); - let y_inv = Fq::random(&mut rng); - let mut inputs = vec![]; - inputs.extend(bn254_fq2_to_biguint_vec(uneval_b)); - inputs.extend(bn254_fq2_to_biguint_vec(uneval_c)); - inputs.push(bn254_fq_to_biguint(x_over_y)); - inputs.push(bn254_fq_to_biguint(y_inv)); - let input_limbs = inputs - .iter() - .map(|x| { - biguint_to_limbs::(x.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32) - }) - .collect(); - - let uneval: UnevaluatedLine = UnevaluatedLine { - b: uneval_b, - c: uneval_c, - }; - let evaluated = uneval.evaluate(&(x_over_y, y_inv)); - - let result = chip.0.core.expr().execute_with_output(inputs, vec![]); - assert_eq!(result.len(), 4); - assert_eq!(result[0], bn254_fq_to_biguint(evaluated.b.c0)); - assert_eq!(result[1], bn254_fq_to_biguint(evaluated.b.c1)); - assert_eq!(result[2], bn254_fq_to_biguint(evaluated.c.c0)); - assert_eq!(result[3], bn254_fq_to_biguint(evaluated.c.c1)); - - let instruction = rv32_write_heap_default( - &mut tester, - input_limbs, - vec![], - chip.0.core.air.offset + PairingOpcode::EVALUATE_LINE as usize, - ); - - tester.execute(&mut chip, &instruction); - let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - tester.simple_test().expect("Verification failed"); -} diff --git a/extensions/pairing/circuit/src/pairing_chip/line/evaluate_line.rs b/extensions/pairing/circuit/src/pairing_chip/line/evaluate_line.rs deleted file mode 100644 index dc0a8cdfe1..0000000000 --- a/extensions/pairing/circuit/src/pairing_chip/line/evaluate_line.rs +++ /dev/null @@ -1,102 +0,0 @@ -use std::{ - cell::RefCell, - rc::Rc, - sync::{Arc, Mutex}, -}; - -use openvm_algebra_circuit::Fp2; -use openvm_circuit::{arch::VmChipWrapper, system::memory::OfflineMemory}; -use openvm_circuit_derive::InstructionExecutor; -use openvm_circuit_primitives::var_range::{ - SharedVariableRangeCheckerChip, VariableRangeCheckerBus, -}; -use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; -use openvm_mod_circuit_builder::{ - ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreChip, -}; -use openvm_pairing_transpiler::PairingOpcode; -use openvm_rv32_adapters::Rv32VecHeapTwoReadsAdapterChip; -use openvm_stark_backend::p3_field::PrimeField32; - -// Input: UnevaluatedLine, (Fp, Fp) -// Output: EvaluatedLine -#[derive(Chip, ChipUsageGetter, InstructionExecutor)] -pub struct EvaluateLineChip< - F: PrimeField32, - const INPUT_BLOCKS1: usize, - const INPUT_BLOCKS2: usize, - const OUTPUT_BLOCKS: usize, - const BLOCK_SIZE: usize, ->( - pub VmChipWrapper< - F, - Rv32VecHeapTwoReadsAdapterChip< - F, - INPUT_BLOCKS1, - INPUT_BLOCKS2, - OUTPUT_BLOCKS, - BLOCK_SIZE, - BLOCK_SIZE, - >, - FieldExpressionCoreChip, - >, -); - -impl< - F: PrimeField32, - const INPUT_BLOCKS1: usize, - const INPUT_BLOCKS2: usize, - const OUTPUT_BLOCKS: usize, - const BLOCK_SIZE: usize, - > EvaluateLineChip -{ - pub fn new( - adapter: Rv32VecHeapTwoReadsAdapterChip< - F, - INPUT_BLOCKS1, - INPUT_BLOCKS2, - OUTPUT_BLOCKS, - BLOCK_SIZE, - BLOCK_SIZE, - >, - config: ExprBuilderConfig, - offset: usize, - range_checker: SharedVariableRangeCheckerChip, - offline_memory: Arc>>, - ) -> Self { - let expr = evaluate_line_expr(config, range_checker.bus()); - let core = FieldExpressionCoreChip::new( - expr, - offset, - vec![PairingOpcode::EVALUATE_LINE as usize], - vec![], - range_checker, - "EvaluateLine", - false, - ); - Self(VmChipWrapper::new(adapter, core, offline_memory)) - } -} - -pub fn evaluate_line_expr( - config: ExprBuilderConfig, - range_bus: VariableRangeCheckerBus, -) -> FieldExpr { - config.check_valid(); - let builder = ExprBuilder::new(config, range_bus.range_max_bits); - let builder = Rc::new(RefCell::new(builder)); - - let mut uneval_b = Fp2::new(builder.clone()); - let mut uneval_c = Fp2::new(builder.clone()); - - let mut x_over_y = ExprBuilder::new_input(builder.clone()); - let mut y_inv = ExprBuilder::new_input(builder.clone()); - - let mut b = uneval_b.scalar_mul(&mut x_over_y); - let mut c = uneval_c.scalar_mul(&mut y_inv); - b.save_output(); - c.save_output(); - - let builder = builder.borrow().clone(); - FieldExpr::new(builder, range_bus, false) -} diff --git a/extensions/pairing/circuit/src/pairing_chip/line/m_type/mod.rs b/extensions/pairing/circuit/src/pairing_chip/line/m_type/mod.rs deleted file mode 100644 index b454d260ce..0000000000 --- a/extensions/pairing/circuit/src/pairing_chip/line/m_type/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod mul_023_by_023; -mod mul_by_02345; - -pub use mul_023_by_023::*; -pub use mul_by_02345::*; - -#[cfg(test)] -mod tests; diff --git a/extensions/pairing/circuit/src/pairing_chip/line/m_type/mul_023_by_023.rs b/extensions/pairing/circuit/src/pairing_chip/line/m_type/mul_023_by_023.rs deleted file mode 100644 index 0d760b886e..0000000000 --- a/extensions/pairing/circuit/src/pairing_chip/line/m_type/mul_023_by_023.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::{ - cell::RefCell, - rc::Rc, - sync::{Arc, Mutex}, -}; - -use openvm_algebra_circuit::Fp2; -use openvm_circuit::{arch::VmChipWrapper, system::memory::OfflineMemory}; -use openvm_circuit_derive::InstructionExecutor; -use openvm_circuit_primitives::var_range::{ - SharedVariableRangeCheckerChip, VariableRangeCheckerBus, -}; -use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; -use openvm_mod_circuit_builder::{ - ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreChip, -}; -use openvm_pairing_transpiler::PairingOpcode; -use openvm_rv32_adapters::Rv32VecHeapAdapterChip; -use openvm_stark_backend::p3_field::PrimeField32; - -// Input: line0.b, line0.c, line1.b, line1.c : 2 x 4 field elements -// Output: 5 Fp2 coefficients -> 10 field elements -#[derive(Chip, ChipUsageGetter, InstructionExecutor)] -pub struct EcLineMul023By023Chip< - F: PrimeField32, - const INPUT_BLOCKS: usize, - const OUTPUT_BLOCKS: usize, - const BLOCK_SIZE: usize, ->( - pub VmChipWrapper< - F, - Rv32VecHeapAdapterChip, - FieldExpressionCoreChip, - >, -); - -impl< - F: PrimeField32, - const INPUT_BLOCKS: usize, - const OUTPUT_BLOCKS: usize, - const BLOCK_SIZE: usize, - > EcLineMul023By023Chip -{ - pub fn new( - adapter: Rv32VecHeapAdapterChip, - range_checker: SharedVariableRangeCheckerChip, - config: ExprBuilderConfig, - xi: [isize; 2], - offset: usize, - offline_memory: Arc>>, - ) -> Self { - assert!( - xi[0].unsigned_abs() < 1 << config.limb_bits, - "expect xi to be small" - ); // not a hard rule, but we expect xi to be small - assert!( - xi[1].unsigned_abs() < 1 << config.limb_bits, - "expect xi to be small" - ); - let expr = mul_023_by_023_expr(config, range_checker.bus(), xi); - let core = FieldExpressionCoreChip::new( - expr, - offset, - vec![PairingOpcode::MUL_023_BY_023 as usize], - vec![], - range_checker, - "Mul023By023", - true, - ); - Self(VmChipWrapper::new(adapter, core, offline_memory)) - } -} - -pub fn mul_023_by_023_expr( - config: ExprBuilderConfig, - range_bus: VariableRangeCheckerBus, - xi: [isize; 2], -) -> FieldExpr { - config.check_valid(); - let builder = ExprBuilder::new(config.clone(), range_bus.range_max_bits); - let builder = Rc::new(RefCell::new(builder)); - - let mut b0 = Fp2::new(builder.clone()); // x2 - let mut c0 = Fp2::new(builder.clone()); // x3 - let mut b1 = Fp2::new(builder.clone()); // y2 - let mut c1 = Fp2::new(builder.clone()); // y3 - - // where w⁶ = xi - // l0 * l1 = c0c1 + (c0b1 + c1b0)w² + (c0 + c1)w³ + (b0b1)w⁴ + (b0 +b1)w⁵ + w⁶ - // = (c0c1 + xi) + (c0b1 + c1b0)w² + (c0 + c1)w³ + (b0b1)w⁴ + (b0 + b1)w⁵ - let l0 = c0.mul(&mut c1).int_add(xi); - let l2 = c0.mul(&mut b1).add(&mut c1.mul(&mut b0)); - let l3 = c0.add(&mut c1); - let l4 = b0.mul(&mut b1); - let l5 = b0.add(&mut b1); - - [l0, l2, l3, l4, l5].map(|mut l| l.save_output()); - - let builder = builder.borrow().clone(); - FieldExpr::new(builder, range_bus, false) -} diff --git a/extensions/pairing/circuit/src/pairing_chip/line/m_type/mul_by_02345.rs b/extensions/pairing/circuit/src/pairing_chip/line/m_type/mul_by_02345.rs deleted file mode 100644 index ad0e91e7bd..0000000000 --- a/extensions/pairing/circuit/src/pairing_chip/line/m_type/mul_by_02345.rs +++ /dev/null @@ -1,113 +0,0 @@ -use std::{ - cell::RefCell, - rc::Rc, - sync::{Arc, Mutex}, -}; - -use openvm_algebra_circuit::Fp2; -use openvm_circuit::{arch::VmChipWrapper, system::memory::OfflineMemory}; -use openvm_circuit_derive::InstructionExecutor; -use openvm_circuit_primitives::var_range::{ - SharedVariableRangeCheckerChip, VariableRangeCheckerBus, -}; -use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; -use openvm_mod_circuit_builder::{ - ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreChip, -}; -use openvm_pairing_transpiler::PairingOpcode; -use openvm_rv32_adapters::Rv32VecHeapTwoReadsAdapterChip; -use openvm_stark_backend::p3_field::PrimeField32; - -use crate::Fp12; - -// Input: 2 Fp12: 2 x 12 field elements -// Output: Fp12 -> 12 field elements -#[derive(Chip, ChipUsageGetter, InstructionExecutor)] -pub struct EcLineMulBy02345Chip< - F: PrimeField32, - const INPUT_BLOCKS1: usize, - const INPUT_BLOCKS2: usize, - const OUTPUT_BLOCKS: usize, - const BLOCK_SIZE: usize, ->( - pub VmChipWrapper< - F, - Rv32VecHeapTwoReadsAdapterChip< - F, - INPUT_BLOCKS1, - INPUT_BLOCKS2, - OUTPUT_BLOCKS, - BLOCK_SIZE, - BLOCK_SIZE, - >, - FieldExpressionCoreChip, - >, -); - -impl< - F: PrimeField32, - const INPUT_BLOCKS1: usize, - const INPUT_BLOCKS2: usize, - const OUTPUT_BLOCKS: usize, - const BLOCK_SIZE: usize, - > EcLineMulBy02345Chip -{ - pub fn new( - adapter: Rv32VecHeapTwoReadsAdapterChip< - F, - INPUT_BLOCKS1, - INPUT_BLOCKS2, - OUTPUT_BLOCKS, - BLOCK_SIZE, - BLOCK_SIZE, - >, - range_checker: SharedVariableRangeCheckerChip, - config: ExprBuilderConfig, - xi: [isize; 2], - offset: usize, - offline_memory: Arc>>, - ) -> Self { - assert!( - xi[0].unsigned_abs() < 1 << config.limb_bits, - "expect xi to be small" - ); // not a hard rule, but we expect xi to be small - assert!( - xi[1].unsigned_abs() < 1 << config.limb_bits, - "expect xi to be small" - ); - let expr = mul_by_02345_expr(config, range_checker.bus(), xi); - let core = FieldExpressionCoreChip::new( - expr, - offset, - vec![PairingOpcode::MUL_BY_02345 as usize], - vec![], - range_checker, - "MulBy02345", - false, - ); - Self(VmChipWrapper::new(adapter, core, offline_memory)) - } -} - -pub fn mul_by_02345_expr( - config: ExprBuilderConfig, - range_bus: VariableRangeCheckerBus, - xi: [isize; 2], -) -> FieldExpr { - config.check_valid(); - let builder = ExprBuilder::new(config.clone(), range_bus.range_max_bits); - let builder = Rc::new(RefCell::new(builder)); - - let mut f = Fp12::new(builder.clone()); - let mut x0 = Fp2::new(builder.clone()); - let mut x2 = Fp2::new(builder.clone()); - let mut x3 = Fp2::new(builder.clone()); - let mut x4 = Fp2::new(builder.clone()); - let mut x5 = Fp2::new(builder.clone()); - - let mut r = f.mul_by_02345(&mut x0, &mut x2, &mut x3, &mut x4, &mut x5, xi); - r.save_output(); - - let builder = builder.borrow().clone(); - FieldExpr::new(builder, range_bus, false) -} diff --git a/extensions/pairing/circuit/src/pairing_chip/line/m_type/tests.rs b/extensions/pairing/circuit/src/pairing_chip/line/m_type/tests.rs deleted file mode 100644 index 4331d2278e..0000000000 --- a/extensions/pairing/circuit/src/pairing_chip/line/m_type/tests.rs +++ /dev/null @@ -1,217 +0,0 @@ -use halo2curves_axiom::{ - bls12_381::{Fq, Fq12, Fq2, G1Affine}, - ff::Field, -}; -use openvm_circuit::arch::testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}; -use openvm_circuit_primitives::bitwise_op_lookup::{ - BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, -}; -use openvm_ecc_guest::AffinePoint; -use openvm_instructions::{riscv::RV32_CELL_BITS, LocalOpcode}; -use openvm_mod_circuit_builder::{test_utils::*, ExprBuilderConfig}; -use openvm_pairing_guest::{ - bls12_381::{BLS12_381_LIMB_BITS, BLS12_381_MODULUS, BLS12_381_NUM_LIMBS, BLS12_381_XI_ISIZE}, - halo2curves_shims::bls12_381::{tangent_line_023, Bls12_381}, - pairing::LineMulMType, -}; -use openvm_pairing_transpiler::PairingOpcode; -use openvm_rv32_adapters::{ - rv32_write_heap_default_with_increment, Rv32VecHeapAdapterChip, Rv32VecHeapTwoReadsAdapterChip, -}; -use openvm_stark_backend::p3_field::FieldAlgebra; -use openvm_stark_sdk::p3_baby_bear::BabyBear; -use rand::{rngs::StdRng, SeedableRng}; - -use super::*; - -type F = BabyBear; -const NUM_LIMBS: usize = 48; -const LIMB_BITS: usize = 8; -const BLOCK_SIZE: usize = 16; - -#[test] -fn test_mul_023_by_023() { - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let adapter = Rv32VecHeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), - ); - let mut chip = EcLineMul023By023Chip::new( - adapter, - tester.memory_controller().borrow().range_checker.clone(), - ExprBuilderConfig { - modulus: BLS12_381_MODULUS.clone(), - num_limbs: BLS12_381_NUM_LIMBS, - limb_bits: BLS12_381_LIMB_BITS, - }, - BLS12_381_XI_ISIZE, - PairingOpcode::CLASS_OFFSET, - tester.offline_memory_mutex_arc(), - ); - - let mut rng0 = StdRng::seed_from_u64(15); - let mut rng1 = StdRng::seed_from_u64(95); - let rnd_pt_0 = G1Affine::random(&mut rng0); - let rnd_pt_1 = G1Affine::random(&mut rng1); - let ec_pt_0 = AffinePoint:: { - x: rnd_pt_0.x, - y: rnd_pt_0.y, - }; - let ec_pt_1 = AffinePoint:: { - x: rnd_pt_1.x, - y: rnd_pt_1.y, - }; - let line0 = tangent_line_023::(ec_pt_0); - let line1 = tangent_line_023::(ec_pt_1); - let input_line0 = [ - bls12381_fq2_to_biguint_vec(line0.b), - bls12381_fq2_to_biguint_vec(line0.c), - ] - .concat(); - let input_line1 = [ - bls12381_fq2_to_biguint_vec(line1.b), - bls12381_fq2_to_biguint_vec(line1.c), - ] - .concat(); - - let vars = chip - .0 - .core - .expr() - .execute([input_line0.clone(), input_line1.clone()].concat(), vec![]); - let output_indices = chip.0.core.expr().builder.output_indices.clone(); - let output = output_indices - .iter() - .map(|i| vars[*i].clone()) - .collect::>(); - assert_eq!(output.len(), 10); - - let r_cmp = Bls12_381::mul_023_by_023(&line0, &line1); - let r_cmp_bigint = r_cmp - .map(|x| [bls12381_fq_to_biguint(x.c0), bls12381_fq_to_biguint(x.c1)]) - .concat(); - - for i in 0..10 { - assert_eq!(output[i], r_cmp_bigint[i]); - } - - let input_line0_limbs = input_line0 - .iter() - .map(|x| { - biguint_to_limbs::(x.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32) - }) - .collect::>(); - let input_line1_limbs = input_line1 - .iter() - .map(|x| { - biguint_to_limbs::(x.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32) - }) - .collect::>(); - - let instruction = rv32_write_heap_default_with_increment( - &mut tester, - input_line0_limbs, - input_line1_limbs, - 512, - chip.0.core.air.offset + PairingOpcode::MUL_023_BY_023 as usize, - ); - - tester.execute(&mut chip, &instruction); - let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - tester.simple_test().expect("Verification failed"); -} - -// NOTE[yj]: this test requires `RUST_MIN_STACK=8388608` to run otherwise it will overflow the stack -#[test] -#[ignore] -fn test_mul_by_02345() { - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let adapter = Rv32VecHeapTwoReadsAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), - ); - let mut chip = EcLineMulBy02345Chip::new( - adapter, - tester.memory_controller().borrow().range_checker.clone(), - ExprBuilderConfig { - modulus: BLS12_381_MODULUS.clone(), - num_limbs: BLS12_381_NUM_LIMBS, - limb_bits: BLS12_381_LIMB_BITS, - }, - BLS12_381_XI_ISIZE, - PairingOpcode::CLASS_OFFSET, - tester.offline_memory_mutex_arc(), - ); - - let mut rng = StdRng::seed_from_u64(19); - let f = Fq12::random(&mut rng); - let x0 = Fq2::random(&mut rng); - let x2 = Fq2::random(&mut rng); - let x3 = Fq2::random(&mut rng); - let x4 = Fq2::random(&mut rng); - let x5 = Fq2::random(&mut rng); - - let input_f = bls12381_fq12_to_biguint_vec(f); - let input_x = [ - bls12381_fq2_to_biguint_vec(x0), - bls12381_fq2_to_biguint_vec(x2), - bls12381_fq2_to_biguint_vec(x3), - bls12381_fq2_to_biguint_vec(x4), - bls12381_fq2_to_biguint_vec(x5), - ] - .concat(); - - let vars = chip - .0 - .core - .expr() - .execute([input_f.clone(), input_x.clone()].concat(), vec![]); - let output_indices = chip.0.core.expr().builder.output_indices.clone(); - let output = output_indices - .iter() - .map(|i| vars[*i].clone()) - .collect::>(); - assert_eq!(output.len(), 12); - - let r_cmp = Bls12_381::mul_by_02345(&f, &[x0, x2, x3, x4, x5]); - let r_cmp_bigint = bls12381_fq12_to_biguint_vec(r_cmp); - - for i in 0..12 { - assert_eq!(output[i], r_cmp_bigint[i]); - } - - let input_f_limbs = input_f - .iter() - .map(|x| { - biguint_to_limbs::(x.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32) - }) - .collect::>(); - let input_x_limbs = input_x - .iter() - .map(|x| { - biguint_to_limbs::(x.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32) - }) - .collect::>(); - - let instruction = rv32_write_heap_default_with_increment( - &mut tester, - input_f_limbs, - input_x_limbs, - 1024, - chip.0.core.air.offset + PairingOpcode::MUL_BY_02345 as usize, - ); - - tester.execute(&mut chip, &instruction); - let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - tester.simple_test().expect("Verification failed"); -} diff --git a/extensions/pairing/circuit/src/pairing_chip/line/mod.rs b/extensions/pairing/circuit/src/pairing_chip/line/mod.rs deleted file mode 100644 index acf02c72be..0000000000 --- a/extensions/pairing/circuit/src/pairing_chip/line/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod d_type; -mod evaluate_line; -mod m_type; - -pub use d_type::*; -pub use evaluate_line::*; -pub use m_type::*; diff --git a/extensions/pairing/circuit/src/pairing_chip/miller_double_and_add_step.rs b/extensions/pairing/circuit/src/pairing_chip/miller_double_and_add_step.rs deleted file mode 100644 index 77084428c9..0000000000 --- a/extensions/pairing/circuit/src/pairing_chip/miller_double_and_add_step.rs +++ /dev/null @@ -1,215 +0,0 @@ -use std::{ - cell::RefCell, - rc::Rc, - sync::{Arc, Mutex}, -}; - -use openvm_algebra_circuit::Fp2; -use openvm_circuit::{arch::VmChipWrapper, system::memory::OfflineMemory}; -use openvm_circuit_derive::InstructionExecutor; -use openvm_circuit_primitives::var_range::{ - SharedVariableRangeCheckerChip, VariableRangeCheckerBus, -}; -use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; -use openvm_mod_circuit_builder::{ - ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreChip, -}; -use openvm_pairing_transpiler::PairingOpcode; -use openvm_rv32_adapters::Rv32VecHeapAdapterChip; -use openvm_stark_backend::p3_field::PrimeField32; - -// Input: two AffinePoint: 4 field elements each -// Output: (AffinePoint, UnevaluatedLine, UnevaluatedLine) -> 2*2 + 2*2 + 2*2 = 12 -// field elements -#[derive(Chip, ChipUsageGetter, InstructionExecutor)] -pub struct MillerDoubleAndAddStepChip< - F: PrimeField32, - const INPUT_BLOCKS: usize, - const OUTPUT_BLOCKS: usize, - const BLOCK_SIZE: usize, ->( - pub VmChipWrapper< - F, - Rv32VecHeapAdapterChip, - FieldExpressionCoreChip, - >, -); - -impl< - F: PrimeField32, - const INPUT_BLOCKS: usize, - const OUTPUT_BLOCKS: usize, - const BLOCK_SIZE: usize, - > MillerDoubleAndAddStepChip -{ - pub fn new( - adapter: Rv32VecHeapAdapterChip, - config: ExprBuilderConfig, - offset: usize, - range_checker: SharedVariableRangeCheckerChip, - offline_memory: Arc>>, - ) -> Self { - let expr = miller_double_and_add_step_expr(config, range_checker.bus()); - let core = FieldExpressionCoreChip::new( - expr, - offset, - vec![PairingOpcode::MILLER_DOUBLE_AND_ADD_STEP as usize], - vec![], - range_checker, - "MillerDoubleAndAddStep", - false, - ); - Self(VmChipWrapper::new(adapter, core, offline_memory)) - } -} - -// Ref: openvm_pairing_guest::miller_step -pub fn miller_double_and_add_step_expr( - config: ExprBuilderConfig, - range_bus: VariableRangeCheckerBus, -) -> FieldExpr { - config.check_valid(); - let builder = ExprBuilder::new(config, range_bus.range_max_bits); - let builder = Rc::new(RefCell::new(builder)); - - let mut x_s = Fp2::new(builder.clone()); - let mut y_s = Fp2::new(builder.clone()); - let mut x_q = Fp2::new(builder.clone()); - let mut y_q = Fp2::new(builder.clone()); - - // λ1 = (y_s - y_q) / (x_s - x_q) - let mut lambda1 = y_s.sub(&mut y_q).div(&mut x_s.sub(&mut x_q)); - let mut x_sq = lambda1.square().sub(&mut x_s).sub(&mut x_q); - // λ2 = -λ1 - 2y_s / (x_{s+q} - x_s) - let mut lambda2 = lambda1 - .neg() - .sub(&mut y_s.int_mul([2, 0]).div(&mut x_sq.sub(&mut x_s))); - let mut x_sqs = lambda2.square().sub(&mut x_s).sub(&mut x_sq); - let mut y_sqs = lambda2.mul(&mut (x_s.sub(&mut x_sqs))).sub(&mut y_s); - - x_sqs.save_output(); - y_sqs.save_output(); - - let mut b0 = lambda1.neg(); - let mut c0 = lambda1.mul(&mut x_s).sub(&mut y_s); - b0.save_output(); - c0.save_output(); - - let mut b1 = lambda2.neg(); - let mut c1 = lambda2.mul(&mut x_s).sub(&mut y_s); - b1.save_output(); - c1.save_output(); - - let builder = builder.borrow().clone(); - FieldExpr::new(builder, range_bus, false) -} - -#[cfg(test)] -mod tests { - use halo2curves_axiom::bn256::G2Affine; - use openvm_circuit::arch::testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}; - use openvm_circuit_primitives::bitwise_op_lookup::{ - BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, - }; - use openvm_ecc_guest::AffinePoint; - use openvm_instructions::{riscv::RV32_CELL_BITS, LocalOpcode}; - use openvm_mod_circuit_builder::test_utils::{biguint_to_limbs, bn254_fq_to_biguint}; - use openvm_pairing_guest::{ - bn254::BN254_MODULUS, halo2curves_shims::bn254::Bn254, pairing::MillerStep, - }; - use openvm_pairing_transpiler::PairingOpcode; - use openvm_rv32_adapters::{rv32_write_heap_default, Rv32VecHeapAdapterChip}; - use openvm_stark_backend::p3_field::FieldAlgebra; - use openvm_stark_sdk::p3_baby_bear::BabyBear; - use rand::{rngs::StdRng, SeedableRng}; - - use super::*; - - type F = BabyBear; - const NUM_LIMBS: usize = 32; - const LIMB_BITS: usize = 8; - const BLOCK_SIZE: usize = 32; - - #[test] - #[allow(non_snake_case)] - fn test_miller_double_and_add() { - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let adapter = Rv32VecHeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), - ); - let mut chip = MillerDoubleAndAddStepChip::new( - adapter, - ExprBuilderConfig { - modulus: BN254_MODULUS.clone(), - limb_bits: LIMB_BITS, - num_limbs: NUM_LIMBS, - }, - PairingOpcode::CLASS_OFFSET, - tester.range_checker(), - tester.offline_memory_mutex_arc(), - ); - - let mut rng0 = StdRng::seed_from_u64(2); - let Q = G2Affine::random(&mut rng0); - let Q2 = G2Affine::random(&mut rng0); - let inputs = [ - Q.x.c0, Q.x.c1, Q.y.c0, Q.y.c1, Q2.x.c0, Q2.x.c1, Q2.y.c0, Q2.y.c1, - ] - .map(bn254_fq_to_biguint); - - let Q_ecpoint = AffinePoint { x: Q.x, y: Q.y }; - let Q_ecpoint2 = AffinePoint { x: Q2.x, y: Q2.y }; - let (Q_daa, l_qa, l_sqs) = Bn254::miller_double_and_add_step(&Q_ecpoint, &Q_ecpoint2); - let result = chip - .0 - .core - .expr() - .execute_with_output(inputs.to_vec(), vec![]); - assert_eq!(result.len(), 12); // AffinePoint and 4 Fp2 coefficients - assert_eq!(result[0], bn254_fq_to_biguint(Q_daa.x.c0)); - assert_eq!(result[1], bn254_fq_to_biguint(Q_daa.x.c1)); - assert_eq!(result[2], bn254_fq_to_biguint(Q_daa.y.c0)); - assert_eq!(result[3], bn254_fq_to_biguint(Q_daa.y.c1)); - assert_eq!(result[4], bn254_fq_to_biguint(l_qa.b.c0)); - assert_eq!(result[5], bn254_fq_to_biguint(l_qa.b.c1)); - assert_eq!(result[6], bn254_fq_to_biguint(l_qa.c.c0)); - assert_eq!(result[7], bn254_fq_to_biguint(l_qa.c.c1)); - assert_eq!(result[8], bn254_fq_to_biguint(l_sqs.b.c0)); - assert_eq!(result[9], bn254_fq_to_biguint(l_sqs.b.c1)); - assert_eq!(result[10], bn254_fq_to_biguint(l_sqs.c.c0)); - assert_eq!(result[11], bn254_fq_to_biguint(l_sqs.c.c1)); - - let input1_limbs = inputs[0..4] - .iter() - .map(|x| { - biguint_to_limbs::(x.clone(), LIMB_BITS) - .map(BabyBear::from_canonical_u32) - }) - .collect::>(); - - let input2_limbs = inputs[4..8] - .iter() - .map(|x| { - biguint_to_limbs::(x.clone(), LIMB_BITS) - .map(BabyBear::from_canonical_u32) - }) - .collect::>(); - - let instruction = rv32_write_heap_default( - &mut tester, - input1_limbs, - input2_limbs, - chip.0.core.air.offset + PairingOpcode::MILLER_DOUBLE_AND_ADD_STEP as usize, - ); - - tester.execute(&mut chip, &instruction); - let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - tester.simple_test().expect("Verification failed"); - } -} diff --git a/extensions/pairing/circuit/src/pairing_chip/miller_double_step.rs b/extensions/pairing/circuit/src/pairing_chip/miller_double_step.rs deleted file mode 100644 index 519eb473a5..0000000000 --- a/extensions/pairing/circuit/src/pairing_chip/miller_double_step.rs +++ /dev/null @@ -1,253 +0,0 @@ -use std::{ - cell::RefCell, - rc::Rc, - sync::{Arc, Mutex}, -}; - -use openvm_algebra_circuit::Fp2; -use openvm_circuit::{arch::VmChipWrapper, system::memory::OfflineMemory}; -use openvm_circuit_derive::InstructionExecutor; -use openvm_circuit_primitives::var_range::{ - SharedVariableRangeCheckerChip, VariableRangeCheckerBus, -}; -use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; -use openvm_mod_circuit_builder::{ - ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreChip, -}; -use openvm_pairing_transpiler::PairingOpcode; -use openvm_rv32_adapters::Rv32VecHeapAdapterChip; -use openvm_stark_backend::p3_field::PrimeField32; - -// Input: AffinePoint: 4 field elements -// Output: (AffinePoint, Fp2, Fp2) -> 8 field elements -#[derive(Chip, ChipUsageGetter, InstructionExecutor)] -pub struct MillerDoubleStepChip< - F: PrimeField32, - const INPUT_BLOCKS: usize, - const OUTPUT_BLOCKS: usize, - const BLOCK_SIZE: usize, ->( - VmChipWrapper< - F, - Rv32VecHeapAdapterChip, - FieldExpressionCoreChip, - >, -); - -impl< - F: PrimeField32, - const INPUT_BLOCKS: usize, - const OUTPUT_BLOCKS: usize, - const BLOCK_SIZE: usize, - > MillerDoubleStepChip -{ - pub fn new( - adapter: Rv32VecHeapAdapterChip, - config: ExprBuilderConfig, - offset: usize, - range_checker: SharedVariableRangeCheckerChip, - offline_memory: Arc>>, - ) -> Self { - let expr = miller_double_step_expr(config, range_checker.bus()); - let core = FieldExpressionCoreChip::new( - expr, - offset, - vec![PairingOpcode::MILLER_DOUBLE_STEP as usize], - vec![], - range_checker, - "MillerDoubleStep", - false, - ); - Self(VmChipWrapper::new(adapter, core, offline_memory)) - } -} - -// Ref: https://github.com/openvm-org/openvm/blob/f7d6fa7b8ef247e579740eb652fcdf5a04259c28/lib/ecc-execution/src/common/miller_step.rs#L7 -pub fn miller_double_step_expr( - config: ExprBuilderConfig, - range_bus: VariableRangeCheckerBus, -) -> FieldExpr { - config.check_valid(); - let builder = ExprBuilder::new(config, range_bus.range_max_bits); - let builder = Rc::new(RefCell::new(builder)); - - let mut x_s = Fp2::new(builder.clone()); - let mut y_s = Fp2::new(builder.clone()); - - let mut three_x_square = x_s.square().int_mul([3, 0]); - let mut lambda = three_x_square.div(&mut y_s.int_mul([2, 0])); - let mut x_2s = lambda.square().sub(&mut x_s.int_mul([2, 0])); - let mut y_2s = lambda.mul(&mut (x_s.sub(&mut x_2s))).sub(&mut y_s); - x_2s.save_output(); - y_2s.save_output(); - - let mut b = lambda.neg(); - let mut c = lambda.mul(&mut x_s).sub(&mut y_s); - b.save_output(); - c.save_output(); - - let builder = builder.borrow().clone(); - FieldExpr::new(builder, range_bus, false) -} - -#[cfg(test)] -mod tests { - use openvm_circuit::arch::testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}; - use openvm_circuit_primitives::bitwise_op_lookup::{ - BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, - }; - use openvm_ecc_guest::AffinePoint; - use openvm_instructions::{riscv::RV32_CELL_BITS, LocalOpcode}; - use openvm_mod_circuit_builder::test_utils::{ - biguint_to_limbs, bls12381_fq_to_biguint, bn254_fq_to_biguint, - }; - use openvm_pairing_guest::{ - bls12_381::{BLS12_381_LIMB_BITS, BLS12_381_MODULUS, BLS12_381_NUM_LIMBS}, - bn254::{BN254_LIMB_BITS, BN254_MODULUS, BN254_NUM_LIMBS}, - halo2curves_shims::{bls12_381::Bls12_381, bn254::Bn254}, - pairing::MillerStep, - }; - use openvm_pairing_transpiler::PairingOpcode; - use openvm_rv32_adapters::{rv32_write_heap_default, Rv32VecHeapAdapterChip}; - use openvm_stark_backend::p3_field::FieldAlgebra; - use openvm_stark_sdk::p3_baby_bear::BabyBear; - use rand::{rngs::StdRng, SeedableRng}; - - use super::*; - - type F = BabyBear; - - #[test] - #[allow(non_snake_case)] - fn test_miller_double_bn254() { - use halo2curves_axiom::bn256::G2Affine; - const NUM_LIMBS: usize = 32; - const LIMB_BITS: usize = 8; - const BLOCK_SIZE: usize = 32; - - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let config = ExprBuilderConfig { - modulus: BN254_MODULUS.clone(), - limb_bits: BN254_LIMB_BITS, - num_limbs: BN254_NUM_LIMBS, - }; - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let adapter = Rv32VecHeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), - ); - let mut chip = MillerDoubleStepChip::new( - adapter, - config, - PairingOpcode::CLASS_OFFSET, - tester.range_checker(), - tester.offline_memory_mutex_arc(), - ); - - let mut rng0 = StdRng::seed_from_u64(2); - let Q = G2Affine::random(&mut rng0); - let inputs = [Q.x.c0, Q.x.c1, Q.y.c0, Q.y.c1].map(bn254_fq_to_biguint); - - let Q_ecpoint = AffinePoint { x: Q.x, y: Q.y }; - let (Q_acc_init, l_init) = Bn254::miller_double_step(&Q_ecpoint); - let result = chip - .0 - .core - .expr() - .execute_with_output(inputs.to_vec(), vec![]); - assert_eq!(result.len(), 8); // AffinePoint and two Fp2 coefficients - assert_eq!(result[0], bn254_fq_to_biguint(Q_acc_init.x.c0)); - assert_eq!(result[1], bn254_fq_to_biguint(Q_acc_init.x.c1)); - assert_eq!(result[2], bn254_fq_to_biguint(Q_acc_init.y.c0)); - assert_eq!(result[3], bn254_fq_to_biguint(Q_acc_init.y.c1)); - assert_eq!(result[4], bn254_fq_to_biguint(l_init.b.c0)); - assert_eq!(result[5], bn254_fq_to_biguint(l_init.b.c1)); - assert_eq!(result[6], bn254_fq_to_biguint(l_init.c.c0)); - assert_eq!(result[7], bn254_fq_to_biguint(l_init.c.c1)); - - let input_limbs = inputs - .map(|x| biguint_to_limbs::(x, LIMB_BITS).map(BabyBear::from_canonical_u32)); - - let instruction = rv32_write_heap_default( - &mut tester, - input_limbs.to_vec(), - vec![], - chip.0.core.air.offset + PairingOpcode::MILLER_DOUBLE_STEP as usize, - ); - - tester.execute(&mut chip, &instruction); - let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - tester.simple_test().expect("Verification failed"); - } - - #[test] - #[allow(non_snake_case)] - fn test_miller_double_bls12_381() { - use halo2curves_axiom::bls12_381::G2Affine; - const NUM_LIMBS: usize = 48; - const LIMB_BITS: usize = 8; - const BLOCK_SIZE: usize = 16; - - let mut tester: VmChipTestBuilder = VmChipTestBuilder::default(); - let config = ExprBuilderConfig { - modulus: BLS12_381_MODULUS.clone(), - limb_bits: BLS12_381_LIMB_BITS, - num_limbs: BLS12_381_NUM_LIMBS, - }; - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let adapter = Rv32VecHeapAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), - ); - let mut chip = MillerDoubleStepChip::new( - adapter, - config, - PairingOpcode::CLASS_OFFSET, - tester.range_checker(), - tester.offline_memory_mutex_arc(), - ); - - let mut rng0 = StdRng::seed_from_u64(12); - let Q = G2Affine::random(&mut rng0); - let inputs = [Q.x.c0, Q.x.c1, Q.y.c0, Q.y.c1].map(bls12381_fq_to_biguint); - - let Q_ecpoint = AffinePoint { x: Q.x, y: Q.y }; - let (Q_acc_init, l_init) = Bls12_381::miller_double_step(&Q_ecpoint); - let result = chip - .0 - .core - .expr() - .execute_with_output(inputs.to_vec(), vec![]); - assert_eq!(result.len(), 8); // AffinePoint and two Fp2 coefficients - assert_eq!(result[0], bls12381_fq_to_biguint(Q_acc_init.x.c0)); - assert_eq!(result[1], bls12381_fq_to_biguint(Q_acc_init.x.c1)); - assert_eq!(result[2], bls12381_fq_to_biguint(Q_acc_init.y.c0)); - assert_eq!(result[3], bls12381_fq_to_biguint(Q_acc_init.y.c1)); - assert_eq!(result[4], bls12381_fq_to_biguint(l_init.b.c0)); - assert_eq!(result[5], bls12381_fq_to_biguint(l_init.b.c1)); - assert_eq!(result[6], bls12381_fq_to_biguint(l_init.c.c0)); - assert_eq!(result[7], bls12381_fq_to_biguint(l_init.c.c1)); - - let input_limbs = inputs - .map(|x| biguint_to_limbs::(x, LIMB_BITS).map(BabyBear::from_canonical_u32)); - - let instruction = rv32_write_heap_default( - &mut tester, - input_limbs.to_vec(), - vec![], - chip.0.core.air.offset + PairingOpcode::MILLER_DOUBLE_STEP as usize, - ); - - tester.execute(&mut chip, &instruction); - let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - tester.simple_test().expect("Verification failed"); - } -} diff --git a/extensions/pairing/circuit/src/pairing_chip/mod.rs b/extensions/pairing/circuit/src/pairing_chip/mod.rs deleted file mode 100644 index df00df16ce..0000000000 --- a/extensions/pairing/circuit/src/pairing_chip/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod line; -mod miller_double_step; - -pub use line::*; -pub use miller_double_step::*; - -mod miller_double_and_add_step; -pub use miller_double_and_add_step::*; diff --git a/extensions/pairing/circuit/src/pairing_extension.rs b/extensions/pairing/circuit/src/pairing_extension.rs index baf9f788b3..b39fd9e096 100644 --- a/extensions/pairing/circuit/src/pairing_extension.rs +++ b/extensions/pairing/circuit/src/pairing_extension.rs @@ -5,7 +5,7 @@ use openvm_circuit::{ arch::{VmExtension, VmInventory, VmInventoryBuilder, VmInventoryError}, system::phantom::PhantomChip, }; -use openvm_circuit_derive::{AnyEnum, InstructionExecutor}; +use openvm_circuit_derive::{AnyEnum, InsExecutorE1, InstructionExecutor}; use openvm_circuit_primitives::bitwise_op_lookup::SharedBitwiseOperationLookupChip; use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; use openvm_ecc_circuit::CurveConfig; @@ -19,8 +19,6 @@ use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; use strum::FromRepr; -use super::*; - // All the supported pairing curves. #[derive(Clone, Copy, Debug, FromRepr, Serialize, Deserialize)] #[repr(usize)] @@ -60,14 +58,9 @@ pub struct PairingExtension { pub supported_curves: Vec, } -#[derive(Chip, ChipUsageGetter, InstructionExecutor, AnyEnum)] +#[derive(Chip, ChipUsageGetter, InstructionExecutor, AnyEnum, InsExecutorE1)] pub enum PairingExtensionExecutor { - // bn254 (32 limbs) - MillerDoubleAndAddStepRv32_32(MillerDoubleAndAddStepChip), - EvaluateLineRv32_32(EvaluateLineChip), - // bls12-381 (48 limbs) - MillerDoubleAndAddStepRv32_48(MillerDoubleAndAddStepChip), - EvaluateLineRv32_48(EvaluateLineChip), + Phantom(PhantomChip), } #[derive(ChipUsageGetter, Chip, AnyEnum, From)] diff --git a/extensions/pairing/transpiler/src/lib.rs b/extensions/pairing/transpiler/src/lib.rs index 7777c37c91..e80deaf154 100644 --- a/extensions/pairing/transpiler/src/lib.rs +++ b/extensions/pairing/transpiler/src/lib.rs @@ -1,71 +1,11 @@ use openvm_instructions::{ - instruction::Instruction, riscv::RV32_REGISTER_NUM_LIMBS, LocalOpcode, PhantomDiscriminant, + instruction::Instruction, riscv::RV32_REGISTER_NUM_LIMBS, PhantomDiscriminant, }; -use openvm_instructions_derive::LocalOpcode; use openvm_pairing_guest::{PairingBaseFunct7, OPCODE, PAIRING_FUNCT3}; use openvm_stark_backend::p3_field::PrimeField32; use openvm_transpiler::{TranspilerExtension, TranspilerOutput}; use rrs_lib::instruction_formats::RType; -use strum::{EnumCount, EnumIter, FromRepr}; - -// NOTE: the following opcodes are enabled only in testing and not enabled in the VM Extension -#[derive( - Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, EnumCount, EnumIter, FromRepr, LocalOpcode, -)] -#[opcode_offset = 0x750] -#[repr(usize)] -#[allow(non_camel_case_types)] -pub enum PairingOpcode { - MILLER_DOUBLE_AND_ADD_STEP, - MILLER_DOUBLE_STEP, - EVALUATE_LINE, - MUL_013_BY_013, - MUL_023_BY_023, - MUL_BY_01234, - MUL_BY_02345, -} - -// NOTE: Fp12 opcodes are only enabled in testing and not enabled in the VM Extension -#[derive( - Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, EnumCount, EnumIter, FromRepr, LocalOpcode, -)] -#[opcode_offset = 0x700] -#[repr(usize)] -#[allow(non_camel_case_types)] -pub enum Fp12Opcode { - ADD, - SUB, - MUL, -} -const FP12_OPS: usize = 4; - -pub struct Bn254Fp12Opcode(Fp12Opcode); - -impl LocalOpcode for Bn254Fp12Opcode { - const CLASS_OFFSET: usize = Fp12Opcode::CLASS_OFFSET; - - fn from_usize(value: usize) -> Self { - Self(Fp12Opcode::from_usize(value)) - } - - fn local_usize(&self) -> usize { - self.0.local_usize() - } -} - -pub struct Bls12381Fp12Opcode(Fp12Opcode); - -impl LocalOpcode for Bls12381Fp12Opcode { - const CLASS_OFFSET: usize = Fp12Opcode::CLASS_OFFSET + FP12_OPS; - - fn from_usize(value: usize) -> Self { - Self(Fp12Opcode::from_usize(value - FP12_OPS)) - } - - fn local_usize(&self) -> usize { - self.0.local_usize() + FP12_OPS - } -} +use strum::FromRepr; #[derive(Copy, Clone, Debug, PartialEq, Eq, FromRepr)] #[repr(u16)] diff --git a/extensions/rv32-adapters/src/eq_mod.rs b/extensions/rv32-adapters/src/eq_mod.rs index 3ed486bcf2..d49d6f6912 100644 --- a/extensions/rv32-adapters/src/eq_mod.rs +++ b/extensions/rv32-adapters/src/eq_mod.rs @@ -1,22 +1,18 @@ use std::{ array::from_fn, borrow::{Borrow, BorrowMut}, - marker::PhantomData, }; use itertools::izip; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge, - ExecutionBus, ExecutionState, MinimalInstruction, Result, VmAdapterAir, VmAdapterChip, - VmAdapterInterface, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, + ExecutionBridge, ExecutionState, MinimalInstruction, VmAdapterAir, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, - MemoryAddress, MemoryController, OfflineMemory, RecordId, - }, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, + online::{GuestMemory, TracingMemory}, + MemoryAddress, MemoryAuxColsFactory, }, }; use openvm_circuit_primitives::bitwise_op_lookup::{ @@ -29,16 +25,14 @@ use openvm_instructions::{ riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}, }; use openvm_rv32im_circuit::adapters::{ - read_rv32_register, tmp_convert_to_u8s, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + memory_read, memory_write, new_read_rv32_register, tracing_read, tracing_write, RV32_CELL_BITS, + RV32_REGISTER_NUM_LIMBS, }; use openvm_stark_backend::{ interaction::InteractionBuilder, p3_air::BaseAir, p3_field::{Field, FieldAlgebra, PrimeField32}, }; -use serde::{Deserialize, Serialize}; -use serde_big_array::BigArray; -use serde_with::serde_as; /// This adapter reads from NUM_READS <= 2 pointers and writes to a register. /// * The data is read from the heap (address space 2), and the pointers are read from registers @@ -47,7 +41,7 @@ use serde_with::serde_as; /// starting from the addresses in `rs[0]` (and `rs[1]` if `R = 2`). /// * Writes are to 32-bit register rd. #[repr(C)] -#[derive(AlignedBorrow)] +#[derive(AlignedBorrow, Debug)] pub struct Rv32IsEqualModAdapterCols< T, const NUM_READS: usize, @@ -227,211 +221,208 @@ impl< } } -pub struct Rv32IsEqualModAdapterChip< - F: Field, +pub struct Rv32IsEqualModeAdapterStep< const NUM_READS: usize, const BLOCKS_PER_READ: usize, const BLOCK_SIZE: usize, const TOTAL_READ_SIZE: usize, > { - pub air: Rv32IsEqualModAdapterAir, + pointer_max_bits: usize, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - _marker: PhantomData, } impl< - F: PrimeField32, const NUM_READS: usize, const BLOCKS_PER_READ: usize, const BLOCK_SIZE: usize, const TOTAL_READ_SIZE: usize, - > Rv32IsEqualModAdapterChip + > Rv32IsEqualModeAdapterStep { pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - address_bits: usize, + pointer_max_bits: usize, bitwise_lookup_chip: SharedBitwiseOperationLookupChip, ) -> Self { assert!(NUM_READS <= 2); assert_eq!(TOTAL_READ_SIZE, BLOCKS_PER_READ * BLOCK_SIZE); assert!( - RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - address_bits < RV32_CELL_BITS, - "address_bits={address_bits} needs to be large enough for high limb range check" + RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - pointer_max_bits < RV32_CELL_BITS, + "pointer_max_bits={pointer_max_bits} needs to be large enough for high limb range check" ); Self { - air: Rv32IsEqualModAdapterAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - bus: bitwise_lookup_chip.bus(), - address_bits, - }, + pointer_max_bits, bitwise_lookup_chip, - _marker: PhantomData, } } } -#[repr(C)] -#[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct Rv32IsEqualModReadRecord< - const NUM_READS: usize, - const BLOCKS_PER_READ: usize, - const BLOCK_SIZE: usize, -> { - #[serde(with = "BigArray")] - pub rs: [RecordId; NUM_READS], - #[serde_as(as = "[[_; BLOCKS_PER_READ]; NUM_READS]")] - pub reads: [[RecordId; BLOCKS_PER_READ]; NUM_READS], -} - -#[repr(C)] -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct Rv32IsEqualModWriteRecord { - pub from_state: ExecutionState, - pub rd_id: RecordId, -} - impl< F: PrimeField32, + CTX, const NUM_READS: usize, const BLOCKS_PER_READ: usize, const BLOCK_SIZE: usize, const TOTAL_READ_SIZE: usize, - > VmAdapterChip - for Rv32IsEqualModAdapterChip + > AdapterTraceStep + for Rv32IsEqualModeAdapterStep +where + F: PrimeField32, { - type ReadRecord = Rv32IsEqualModReadRecord; - type WriteRecord = Rv32IsEqualModWriteRecord; - type Air = Rv32IsEqualModAdapterAir; - type Interface = BasicAdapterInterface< - F, - MinimalInstruction, - NUM_READS, - 1, - TOTAL_READ_SIZE, - RV32_REGISTER_NUM_LIMBS, - >; + const WIDTH: usize = + Rv32IsEqualModAdapterCols::::width(); + type ReadData = [[u8; TOTAL_READ_SIZE]; NUM_READS]; + type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + type TraceContext<'a> = (); + + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let cols: &mut Rv32IsEqualModAdapterCols = + adapter_row.borrow_mut(); + cols.from_state.pc = F::from_canonical_u32(pc); + cols.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + } - fn preprocess( - &mut self, - memory: &mut MemoryController, + fn read( + &self, + memory: &mut TracingMemory, instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { + adapter_row: &mut [F], + ) -> Self::ReadData { let Instruction { b, c, d, e, .. } = *instruction; - debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - debug_assert_eq!(e.as_canonical_u32(), RV32_MEMORY_AS); + let e = e.as_canonical_u32(); + let d = d.as_canonical_u32(); + debug_assert_eq!(d, RV32_REGISTER_AS); + debug_assert_eq!(e, RV32_MEMORY_AS); - let mut rs_vals = [0; NUM_READS]; - let rs_records: [_; NUM_READS] = from_fn(|i| { + let cols: &mut Rv32IsEqualModAdapterCols = + adapter_row.borrow_mut(); + + // Read register values + let rs_vals: [_; NUM_READS] = from_fn(|i| { let addr = if i == 0 { b } else { c }; - let (record, val) = read_rv32_register(memory, d, addr); - rs_vals[i] = val; - record + cols.rs_ptr[i] = addr; + let rs_val = tracing_read(memory, d, addr.as_canonical_u32(), &mut cols.rs_read_aux[i]); + cols.rs_val[i] = rs_val.map(F::from_canonical_u8); + u32::from_le_bytes(rs_val) }); - let read_records = rs_vals.map(|address| { - debug_assert!(address < (1 << self.air.address_bits)); - from_fn(|i| { - memory.read::( + // Read memory values + from_fn(|i| { + assert!(rs_vals[i] as usize + TOTAL_READ_SIZE - 1 < (1 << self.pointer_max_bits)); + from_fn::<_, BLOCKS_PER_READ, _>(|j| { + tracing_read::<_, BLOCK_SIZE>( + memory, e, - F::from_canonical_u32(address + (i * BLOCK_SIZE) as u32), + rs_vals[i] + (j * BLOCK_SIZE) as u32, + &mut cols.heap_read_aux[i][j], ) }) - }); - - let read_data = read_records.map(|r| { - let read = r.map(|x| x.1); - let mut read_it = read.iter().flatten(); - from_fn(|_| *(read_it.next().unwrap())).map(F::from_canonical_u8) - }); - let record = Rv32IsEqualModReadRecord { - rs: rs_records, - reads: read_records.map(|r| r.map(|x| x.0)), - }; - - Ok((read_data, record)) + .concat() + .try_into() + .unwrap() + }) } - fn postprocess( - &mut self, - memory: &mut MemoryController, + fn write( + &self, + memory: &mut TracingMemory, instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - _read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { + adapter_row: &mut [F], + data: &Self::WriteData, + ) { let Instruction { a, d, .. } = *instruction; - let (rd_id, _) = memory.write(d, a, &tmp_convert_to_u8s(output.writes[0])); - - debug_assert!( - memory.timestamp() - from_state.timestamp - == (NUM_READS * (BLOCKS_PER_READ + 1) + 1) as u32, - "timestamp delta is {}, expected {}", - memory.timestamp() - from_state.timestamp, - NUM_READS * (BLOCKS_PER_READ + 1) + 1 + let cols: &mut Rv32IsEqualModAdapterCols = + adapter_row.borrow_mut(); + cols.rd_ptr = a; + tracing_write( + memory, + d.as_canonical_u32(), + a.as_canonical_u32(), + data, + &mut cols.writes_aux, ); - - Ok(( - ExecutionState { - pc: from_state.pc + DEFAULT_PC_STEP, - timestamp: memory.timestamp(), - }, - Self::WriteRecord { from_state, rd_id }, - )) } - fn generate_trace_row( + fn fill_trace_row( &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, + mem_helper: &MemoryAuxColsFactory, + _ctx: (), + adapter_row: &mut [F], ) { - let aux_cols_factory = memory.aux_cols_factory(); - let row_slice: &mut Rv32IsEqualModAdapterCols = - row_slice.borrow_mut(); - row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); - - let rs = read_record.rs.map(|r| memory.record_by_id(r)); - for (i, r) in rs.iter().enumerate() { - row_slice.rs_ptr[i] = r.pointer; - row_slice.rs_val[i].copy_from_slice(r.data_slice()); - aux_cols_factory.generate_read_aux(r, &mut row_slice.rs_read_aux[i]); - for (j, x) in read_record.reads[i].iter().enumerate() { - let read = memory.record_by_id(*x); - aux_cols_factory.generate_read_aux(read, &mut row_slice.heap_read_aux[i][j]); - } - } + let cols: &mut Rv32IsEqualModAdapterCols = + adapter_row.borrow_mut(); + let mut timestamp = cols.from_state.timestamp.as_canonical_u32(); + let mut timestamp_pp = || { + timestamp += 1; + timestamp - 1 + }; - let rd = memory.record_by_id(write_record.rd_id); - row_slice.rd_ptr = rd.pointer; - aux_cols_factory.generate_write_aux(rd, &mut row_slice.writes_aux); + cols.rs_read_aux.iter_mut().for_each(|aux| { + mem_helper.fill_from_prev(timestamp_pp(), aux.as_mut()); + }); - // Range checks - let need_range_check: [u32; 2] = from_fn(|i| { - if i < NUM_READS { - rs[i] - .data_at(RV32_REGISTER_NUM_LIMBS - 1) - .as_canonical_u32() - } else { - 0 - } + cols.heap_read_aux.iter_mut().for_each(|reads| { + reads + .iter_mut() + .for_each(|aux| mem_helper.fill_from_prev(timestamp_pp(), aux.as_mut())); }); - let limb_shift_bits = RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.air.address_bits; + + mem_helper.fill_from_prev(timestamp_pp(), cols.writes_aux.as_mut()); + + // Range checks: + debug_assert!(self.pointer_max_bits <= RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS); + let limb_shift_bits = RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.pointer_max_bits; self.bitwise_lookup_chip.request_range( - need_range_check[0] << limb_shift_bits, - need_range_check[1] << limb_shift_bits, + cols.rs_val[0][RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() << limb_shift_bits, + if NUM_READS > 1 { + cols.rs_val[1][RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() << limb_shift_bits + } else { + 0 + }, ); } +} - fn air(&self) -> &Self::Air { - &self.air +impl< + F: PrimeField32, + const NUM_READS: usize, + const BLOCKS_PER_READ: usize, + const BLOCK_SIZE: usize, + const TOTAL_READ_SIZE: usize, + > AdapterExecutorE1 + for Rv32IsEqualModeAdapterStep +{ + type ReadData = [[u8; TOTAL_READ_SIZE]; NUM_READS]; + type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; + + fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData + where + Mem: GuestMemory, + { + let Instruction { b, c, d, e, .. } = *instruction; + + let d = d.as_canonical_u32(); + let e = e.as_canonical_u32(); + debug_assert_eq!(d, RV32_REGISTER_AS); + debug_assert_eq!(e, RV32_MEMORY_AS); + + // Read register values + let rs_vals = from_fn(|i| { + let addr = if i == 0 { b } else { c }; + new_read_rv32_register(memory, d, addr.as_canonical_u32()) + }); + + // Read memory values + rs_vals.map(|address| { + assert!(address as usize + TOTAL_READ_SIZE - 1 < (1 << self.pointer_max_bits)); + memory_read(memory, e, address) + }) + } + + fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) + where + Mem: GuestMemory, + { + let Instruction { a, d, .. } = *instruction; + memory_write(memory, d.as_canonical_u32(), a.as_canonical_u32(), data); } } diff --git a/extensions/rv32-adapters/src/heap.rs b/extensions/rv32-adapters/src/heap.rs index cf7e54ddef..c14fbd64b0 100644 --- a/extensions/rv32-adapters/src/heap.rs +++ b/extensions/rv32-adapters/src/heap.rs @@ -24,7 +24,7 @@ use openvm_stark_backend::{ p3_field::{Field, PrimeField32}, }; -use crate::{RV32VecHeapAdapterStep, Rv32VecHeapAdapterAir, Rv32VecHeapAdapterCols}; +use crate::{Rv32VecHeapAdapterAir, Rv32VecHeapAdapterCols, Rv32VecHeapAdapterStep}; /// This adapter reads from NUM_READS <= 2 pointers and writes to 1 pointer. /// * The data is read from the heap (address space 2), and the pointers are read from registers @@ -96,7 +96,7 @@ pub struct Rv32HeapAdapterStep< const NUM_READS: usize, const READ_SIZE: usize, const WRITE_SIZE: usize, ->(RV32VecHeapAdapterStep); +>(Rv32VecHeapAdapterStep); impl Rv32HeapAdapterStep @@ -110,7 +110,7 @@ impl RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - pointer_max_bits < RV32_CELL_BITS, "pointer_max_bits={pointer_max_bits} needs to be large enough for high limb range check" ); - Rv32HeapAdapterStep(RV32VecHeapAdapterStep::new( + Rv32HeapAdapterStep(Rv32VecHeapAdapterStep::new( pointer_max_bits, bitwise_lookup_chip, )) diff --git a/extensions/rv32-adapters/src/heap_branch.rs b/extensions/rv32-adapters/src/heap_branch.rs index a80c6c214f..262e34616c 100644 --- a/extensions/rv32-adapters/src/heap_branch.rs +++ b/extensions/rv32-adapters/src/heap_branch.rs @@ -1,7 +1,6 @@ use std::{ array::from_fn, borrow::{Borrow, BorrowMut}, - iter::once, }; use itertools::izip; @@ -263,7 +262,7 @@ where let rs_vals: [_; NUM_READS] = from_fn(|i| { let addr = if i == 0 { a } else { b }; cols.rs_ptr[i] = addr; - let rs_val = tracing_read(memory, e, addr.as_canonical_u32(), &mut cols.rs_read_aux[i]); + let rs_val = tracing_read(memory, d, addr.as_canonical_u32(), &mut cols.rs_read_aux[i]); cols.rs_val[i] = rs_val.map(F::from_canonical_u8); u32::from_le_bytes(rs_val) }); @@ -287,25 +286,37 @@ where fn fill_trace_row( &self, - _mem_helper: &MemoryAuxColsFactory, + mem_helper: &MemoryAuxColsFactory, _ctx: (), adapter_row: &mut [F], ) { let cols: &mut Rv32HeapBranchAdapterCols = adapter_row.borrow_mut(); + let mut timestamp = cols.from_state.timestamp.as_canonical_u32(); + let mut timestamp_pp = || { + timestamp += 1; + timestamp - 1 + }; + + cols.rs_read_aux.iter_mut().for_each(|aux| { + mem_helper.fill_from_prev(timestamp_pp(), aux.as_mut()); + }); + + cols.heap_read_aux.iter_mut().for_each(|aux| { + mem_helper.fill_from_prev(timestamp_pp(), aux.as_mut()); + }); + // Range checks: - let need_range_check: Vec = cols - .rs_val - .iter() - .map(|&val| val[RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32()) - .chain(once(0)) // in case NUM_READS is odd - .collect(); debug_assert!(self.pointer_max_bits <= RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS); let limb_shift_bits = RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.pointer_max_bits; - for pair in need_range_check.chunks_exact(2) { - self.bitwise_lookup_chip - .request_range(pair[0] << limb_shift_bits, pair[1] << limb_shift_bits); - } + self.bitwise_lookup_chip.request_range( + cols.rs_val[0][RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() << limb_shift_bits, + if NUM_READS > 1 { + cols.rs_val[1][RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() << limb_shift_bits + } else { + 0 + }, + ); } } diff --git a/extensions/rv32-adapters/src/lib.rs b/extensions/rv32-adapters/src/lib.rs index c194f3e8ca..d84c82f617 100644 --- a/extensions/rv32-adapters/src/lib.rs +++ b/extensions/rv32-adapters/src/lib.rs @@ -1,14 +1,14 @@ -// mod eq_mod; +mod eq_mod; mod heap; mod heap_branch; mod vec_heap; -// mod vec_heap_two_reads; +mod vec_heap_two_reads; -// pub use eq_mod::*; +pub use eq_mod::*; pub use heap::*; pub use heap_branch::*; pub use vec_heap::*; -// pub use vec_heap_two_reads::*; +pub use vec_heap_two_reads::*; #[cfg(any(test, feature = "test-utils"))] mod test_utils; diff --git a/extensions/rv32-adapters/src/vec_heap.rs b/extensions/rv32-adapters/src/vec_heap.rs index 4d430b3fc1..47a8b17553 100644 --- a/extensions/rv32-adapters/src/vec_heap.rs +++ b/extensions/rv32-adapters/src/vec_heap.rs @@ -43,7 +43,7 @@ use openvm_stark_backend::{ /// * Writes take the form of `BLOCKS_PER_WRITE` consecutive writes of size `WRITE_SIZE` to the /// heap, starting from the address in `rd`. #[repr(C)] -#[derive(AlignedBorrow)] +#[derive(AlignedBorrow, Debug)] pub struct Rv32VecHeapAdapterCols< T, const NUM_READS: usize, @@ -262,7 +262,7 @@ impl< } #[derive(derive_new::new)] -pub struct RV32VecHeapAdapterStep< +pub struct Rv32VecHeapAdapterStep< const NUM_READS: usize, const BLOCKS_PER_READ: usize, const BLOCKS_PER_WRITE: usize, @@ -283,7 +283,7 @@ impl< const READ_SIZE: usize, const WRITE_SIZE: usize, > AdapterTraceStep - for RV32VecHeapAdapterStep + for Rv32VecHeapAdapterStep { const WIDTH: usize = Rv32VecHeapAdapterCols::< F, @@ -316,7 +316,7 @@ impl< instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { - let Instruction { b, c, d, e, .. } = *instruction; + let Instruction { a, b, c, d, e, .. } = *instruction; let e = e.as_canonical_u32(); let d = d.as_canonical_u32(); @@ -336,11 +336,15 @@ impl< let rs_vals: [_; NUM_READS] = from_fn(|i| { let addr = if i == 0 { b } else { c }; cols.rs_ptr[i] = addr; - let rs_val = tracing_read(memory, e, addr.as_canonical_u32(), &mut cols.rs_read_aux[i]); + let rs_val = tracing_read(memory, d, addr.as_canonical_u32(), &mut cols.rs_read_aux[i]); cols.rs_val[i] = rs_val.map(F::from_canonical_u8); u32::from_le_bytes(rs_val) }); + cols.rd_ptr = a; + let rd_val = tracing_read(memory, d, a.as_canonical_u32(), &mut cols.rd_read_aux); + cols.rd_val = rd_val.map(F::from_canonical_u8); + // Read memory values from_fn(|i| { assert!( @@ -360,14 +364,12 @@ impl< fn write( &self, - memory: &mut openvm_circuit::system::memory::online::TracingMemory, + memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, ) { - let Instruction { a, d, e, .. } = *instruction; - - let e = e.as_canonical_u32(); + let e = instruction.e.as_canonical_u32(); let cols: &mut Rv32VecHeapAdapterCols< F, NUM_READS, @@ -377,16 +379,7 @@ impl< WRITE_SIZE, > = adapter_row.borrow_mut(); - cols.rd_ptr = a; - let rd_val = tracing_read( - memory, - d.as_canonical_u32(), - a.as_canonical_u32(), - &mut cols.rd_read_aux, - ); - cols.rd_val = rd_val.map(F::from_canonical_u8); - - let rd_val = u32::from_le_bytes(rd_val); + let rd_val = u32::from_le_bytes(cols.rd_val.map(|x| x.as_canonical_u32() as u8)); assert!(rd_val as usize + WRITE_SIZE * BLOCKS_PER_WRITE - 1 < (1 << self.pointer_max_bits)); for i in 0..BLOCKS_PER_WRITE { @@ -402,7 +395,7 @@ impl< fn fill_trace_row( &self, - _mem_helper: &MemoryAuxColsFactory, + mem_helper: &MemoryAuxColsFactory, _ctx: (), adapter_row: &mut [F], ) { @@ -415,18 +408,44 @@ impl< WRITE_SIZE, > = adapter_row.borrow_mut(); + let mut timestamp = cols.from_state.timestamp.as_canonical_u32(); + let mut timestamp_pp = || { + timestamp += 1; + timestamp - 1 + }; + + cols.rs_read_aux + .iter_mut() + .for_each(|aux| mem_helper.fill_from_prev(timestamp_pp(), aux.as_mut())); + mem_helper.fill_from_prev(timestamp_pp(), cols.rd_read_aux.as_mut()); + + cols.reads_aux.iter_mut().for_each(|reads| { + reads + .iter_mut() + .for_each(|aux| mem_helper.fill_from_prev(timestamp_pp(), aux.as_mut())); + }); + + cols.writes_aux.iter_mut().for_each(|write| { + mem_helper.fill_from_prev(timestamp_pp(), write.as_mut()); + }); + // Range checks: - let need_range_check: Vec = cols - .rs_val - .iter() - .chain(std::iter::repeat_n(&cols.rd_val, 2)) - .map(|&val| val[RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32()) - .collect(); debug_assert!(self.pointer_max_bits <= RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS); let limb_shift_bits = RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.pointer_max_bits; - for pair in need_range_check.chunks_exact(2) { - self.bitwise_lookup_chip - .request_range(pair[0] << limb_shift_bits, pair[1] << limb_shift_bits); + if NUM_READS > 1 { + self.bitwise_lookup_chip.request_range( + cols.rs_val[0][RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() << limb_shift_bits, + cols.rs_val[1][RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() << limb_shift_bits, + ); + self.bitwise_lookup_chip.request_range( + cols.rd_val[RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() << limb_shift_bits, + cols.rd_val[RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() << limb_shift_bits, + ); + } else { + self.bitwise_lookup_chip.request_range( + cols.rs_val[0][RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() << limb_shift_bits, + cols.rd_val[RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() << limb_shift_bits, + ); } } } @@ -439,7 +458,7 @@ impl< const READ_SIZE: usize, const WRITE_SIZE: usize, > AdapterExecutorE1 - for RV32VecHeapAdapterStep + for Rv32VecHeapAdapterStep { type ReadData = [[[u8; READ_SIZE]; BLOCKS_PER_READ]; NUM_READS]; type WriteData = [[u8; WRITE_SIZE]; BLOCKS_PER_WRITE]; diff --git a/extensions/rv32-adapters/src/vec_heap_two_reads.rs b/extensions/rv32-adapters/src/vec_heap_two_reads.rs index 2db9ea67ef..6a56ce7c10 100644 --- a/extensions/rv32-adapters/src/vec_heap_two_reads.rs +++ b/extensions/rv32-adapters/src/vec_heap_two_reads.rs @@ -2,21 +2,18 @@ use std::{ array::from_fn, borrow::{Borrow, BorrowMut}, iter::zip, - marker::PhantomData, }; use itertools::izip; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, ExecutionBridge, ExecutionBus, ExecutionState, - Result, VecHeapTwoReadsAdapterInterface, VmAdapterAir, VmAdapterChip, VmAdapterInterface, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ExecutionBridge, ExecutionState, + VecHeapTwoReadsAdapterInterface, VmAdapterAir, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, - MemoryAddress, MemoryController, OfflineMemory, RecordId, - }, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, + online::{GuestMemory, TracingMemory}, + MemoryAddress, }, }; use openvm_circuit_primitives::bitwise_op_lookup::{ @@ -29,16 +26,14 @@ use openvm_instructions::{ riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}, }; use openvm_rv32im_circuit::adapters::{ - abstract_compose, read_rv32_register, tmp_convert_to_u8s, RV32_CELL_BITS, - RV32_REGISTER_NUM_LIMBS, + abstract_compose, memory_read, memory_write, new_read_rv32_register, tracing_read, + tracing_write, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, }; use openvm_stark_backend::{ interaction::InteractionBuilder, p3_air::BaseAir, p3_field::{Field, FieldAlgebra, PrimeField32}, }; -use serde::{Deserialize, Serialize}; -use serde_with::serde_as; /// This adapter reads from 2 pointers and writes to 1 pointer. /// * The data is read from the heap (address space 2), and the pointers are read from registers @@ -48,99 +43,6 @@ use serde_with::serde_as; /// * NOTE that the two reads can read different numbers of blocks. /// * Writes take the form of `BLOCKS_PER_WRITE` consecutive writes of size `WRITE_SIZE` to the /// heap, starting from the address in `rd`. -pub struct Rv32VecHeapTwoReadsAdapterChip< - F: Field, - const BLOCKS_PER_READ1: usize, - const BLOCKS_PER_READ2: usize, - const BLOCKS_PER_WRITE: usize, - const READ_SIZE: usize, - const WRITE_SIZE: usize, -> { - pub air: Rv32VecHeapTwoReadsAdapterAir< - BLOCKS_PER_READ1, - BLOCKS_PER_READ2, - BLOCKS_PER_WRITE, - READ_SIZE, - WRITE_SIZE, - >, - pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - _marker: PhantomData, -} - -impl< - F: PrimeField32, - const BLOCKS_PER_READ1: usize, - const BLOCKS_PER_READ2: usize, - const BLOCKS_PER_WRITE: usize, - const READ_SIZE: usize, - const WRITE_SIZE: usize, - > - Rv32VecHeapTwoReadsAdapterChip< - F, - BLOCKS_PER_READ1, - BLOCKS_PER_READ2, - BLOCKS_PER_WRITE, - READ_SIZE, - WRITE_SIZE, - > -{ - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - address_bits: usize, - bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - ) -> Self { - assert!( - RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - address_bits < RV32_CELL_BITS, - "address_bits={address_bits} needs to be large enough for high limb range check" - ); - Self { - air: Rv32VecHeapTwoReadsAdapterAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - bus: bitwise_lookup_chip.bus(), - address_bits, - }, - bitwise_lookup_chip, - _marker: PhantomData, - } - } -} - -#[repr(C)] -#[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct Rv32VecHeapTwoReadsReadRecord< - F: Field, - const BLOCKS_PER_READ1: usize, - const BLOCKS_PER_READ2: usize, - const READ_SIZE: usize, -> { - /// Read register value from address space e=1 - pub rs1: RecordId, - pub rs2: RecordId, - /// Read register value from address space d=1 - pub rd: RecordId, - - pub rd_val: F, - - #[serde_as(as = "[_; BLOCKS_PER_READ1]")] - pub reads1: [RecordId; BLOCKS_PER_READ1], - #[serde_as(as = "[_; BLOCKS_PER_READ2]")] - pub reads2: [RecordId; BLOCKS_PER_READ2], -} - -#[repr(C)] -#[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Rv32VecHeapTwoReadsWriteRecord { - pub from_state: ExecutionState, - #[serde_as(as = "[_; BLOCKS_PER_WRITE]")] - pub writes: [RecordId; BLOCKS_PER_WRITE], -} - #[repr(C)] #[derive(AlignedBorrow)] pub struct Rv32VecHeapTwoReadsAdapterCols< @@ -373,16 +275,25 @@ impl< } } +pub struct Rv32VecHeapTwoReadsAdapterStep< + const BLOCKS_PER_READ1: usize, + const BLOCKS_PER_READ2: usize, + const BLOCKS_PER_WRITE: usize, + const READ_SIZE: usize, + const WRITE_SIZE: usize, +> { + pointer_max_bits: usize, + pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, +} + impl< - F: PrimeField32, const BLOCKS_PER_READ1: usize, const BLOCKS_PER_READ2: usize, const BLOCKS_PER_WRITE: usize, const READ_SIZE: usize, const WRITE_SIZE: usize, - > VmAdapterChip - for Rv32VecHeapTwoReadsAdapterChip< - F, + > + Rv32VecHeapTwoReadsAdapterStep< BLOCKS_PER_READ1, BLOCKS_PER_READ2, BLOCKS_PER_WRITE, @@ -390,189 +301,266 @@ impl< WRITE_SIZE, > { - type ReadRecord = - Rv32VecHeapTwoReadsReadRecord; - type WriteRecord = Rv32VecHeapTwoReadsWriteRecord; - type Air = Rv32VecHeapTwoReadsAdapterAir< + pub fn new( + pointer_max_bits: usize, + bitwise_lookup_chip: SharedBitwiseOperationLookupChip, + ) -> Self { + assert!( + RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - pointer_max_bits < RV32_CELL_BITS, + "pointer_max_bits={pointer_max_bits} needs to be large enough for high limb range check" + ); + Self { + pointer_max_bits, + bitwise_lookup_chip, + } + } +} + +impl< + F: PrimeField32, + CTX, + const BLOCKS_PER_READ1: usize, + const BLOCKS_PER_READ2: usize, + const BLOCKS_PER_WRITE: usize, + const READ_SIZE: usize, + const WRITE_SIZE: usize, + > AdapterTraceStep + for Rv32VecHeapTwoReadsAdapterStep< BLOCKS_PER_READ1, BLOCKS_PER_READ2, BLOCKS_PER_WRITE, READ_SIZE, WRITE_SIZE, - >; - type Interface = VecHeapTwoReadsAdapterInterface< + > +{ + const WIDTH: usize = Rv32VecHeapTwoReadsAdapterCols::< F, BLOCKS_PER_READ1, BLOCKS_PER_READ2, BLOCKS_PER_WRITE, READ_SIZE, WRITE_SIZE, - >; + >::width(); - fn preprocess( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - ) -> Result<( - >::Reads, - Self::ReadRecord, - )> { - let Instruction { a, b, c, d, e, .. } = *instruction; + type ReadData = ( + [[u8; READ_SIZE]; BLOCKS_PER_READ1], + [[u8; READ_SIZE]; BLOCKS_PER_READ2], + ); + type WriteData = [[u8; WRITE_SIZE]; BLOCKS_PER_WRITE]; + type TraceContext<'a> = (); - debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - debug_assert_eq!(e.as_canonical_u32(), RV32_MEMORY_AS); + fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { + let adapter_cols: &mut Rv32VecHeapTwoReadsAdapterCols< + F, + BLOCKS_PER_READ1, + BLOCKS_PER_READ2, + BLOCKS_PER_WRITE, + READ_SIZE, + WRITE_SIZE, + > = adapter_row.borrow_mut(); + adapter_cols.from_state.pc = F::from_canonical_u32(pc); + adapter_cols.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + } - let (rs1_record, rs1_val) = read_rv32_register(memory, d, b); - let (rs2_record, rs2_val) = read_rv32_register(memory, d, c); - let (rd_record, rd_val) = read_rv32_register(memory, d, a); + fn read( + &self, + memory: &mut TracingMemory, + instruction: &Instruction, + adapter_row: &mut [F], + ) -> Self::ReadData { + let Instruction { a, b, c, d, e, .. } = *instruction; - assert!(rs1_val as usize + READ_SIZE * BLOCKS_PER_READ1 - 1 < (1 << self.air.address_bits)); - let read1_records = from_fn(|i| { - memory.read::(e, F::from_canonical_u32(rs1_val + (i * READ_SIZE) as u32)) - }); - let read1_data = read1_records.map(|r| r.1.map(F::from_canonical_u8)); - assert!(rs2_val as usize + READ_SIZE * BLOCKS_PER_READ2 - 1 < (1 << self.air.address_bits)); - let read2_records = from_fn(|i| { - memory.read::(e, F::from_canonical_u32(rs2_val + (i * READ_SIZE) as u32)) - }); - let read2_data = read2_records.map(|r| r.1.map(F::from_canonical_u8)); - assert!(rd_val as usize + WRITE_SIZE * BLOCKS_PER_WRITE - 1 < (1 << self.air.address_bits)); - - let record = Rv32VecHeapTwoReadsReadRecord { - rs1: rs1_record, - rs2: rs2_record, - rd: rd_record, - rd_val: F::from_canonical_u32(rd_val), - reads1: read1_records.map(|r| r.0), - reads2: read2_records.map(|r| r.0), - }; + let e = e.as_canonical_u32(); + let d = d.as_canonical_u32(); + debug_assert_eq!(d, RV32_REGISTER_AS); + debug_assert_eq!(e, RV32_MEMORY_AS); - Ok(((read1_data, read2_data), record)) + let cols: &mut Rv32VecHeapTwoReadsAdapterCols< + F, + BLOCKS_PER_READ1, + BLOCKS_PER_READ2, + BLOCKS_PER_WRITE, + READ_SIZE, + WRITE_SIZE, + > = adapter_row.borrow_mut(); + + // Read register values + cols.rs1_ptr = b; + let rs1_val = tracing_read(memory, d, b.as_canonical_u32(), &mut cols.rs1_read_aux); + cols.rs1_val = rs1_val.map(F::from_canonical_u8); + let rs1_val = u32::from_le_bytes(rs1_val); + cols.rs2_ptr = c; + let rs2_val = tracing_read(memory, d, c.as_canonical_u32(), &mut cols.rs2_read_aux); + cols.rs2_val = rs2_val.map(F::from_canonical_u8); + let rs2_val = u32::from_le_bytes(rs2_val); + + cols.rd_ptr = a; + let rd_val = tracing_read(memory, d, a.as_canonical_u32(), &mut cols.rd_read_aux); + cols.rd_val = rd_val.map(F::from_canonical_u8); + assert!(rs1_val as usize + READ_SIZE * BLOCKS_PER_READ1 - 1 < (1 << self.pointer_max_bits)); + assert!(rs2_val as usize + READ_SIZE * BLOCKS_PER_READ2 - 1 < (1 << self.pointer_max_bits)); + + ( + from_fn(|i| { + tracing_read( + memory, + e, + rs1_val + (i * READ_SIZE) as u32, + &mut cols.reads1_aux[i], + ) + }), + from_fn(|i| { + tracing_read( + memory, + e, + rs2_val + (i * READ_SIZE) as u32, + &mut cols.reads2_aux[i], + ) + }), + ) } - fn postprocess( - &mut self, - memory: &mut MemoryController, + fn write( + &self, + memory: &mut TracingMemory, instruction: &Instruction, - from_state: ExecutionState, - output: AdapterRuntimeContext, - read_record: &Self::ReadRecord, - ) -> Result<(ExecutionState, Self::WriteRecord)> { - let e = instruction.e; - let mut i = 0; - let writes = output.writes.map(|write| { - let (record_id, _) = memory.write( + adapter_row: &mut [F], + data: &Self::WriteData, + ) { + let e = instruction.e.as_canonical_u32(); + let cols: &mut Rv32VecHeapTwoReadsAdapterCols< + F, + BLOCKS_PER_READ1, + BLOCKS_PER_READ2, + BLOCKS_PER_WRITE, + READ_SIZE, + WRITE_SIZE, + > = adapter_row.borrow_mut(); + + let rd_val = u32::from_le_bytes(cols.rd_val.map(|x| x.as_canonical_u32() as u8)); + assert!(rd_val as usize + WRITE_SIZE * BLOCKS_PER_WRITE - 1 < (1 << self.pointer_max_bits)); + + for i in 0..BLOCKS_PER_WRITE { + tracing_write( + memory, e, - read_record.rd_val + F::from_canonical_u32((i * WRITE_SIZE) as u32), - &tmp_convert_to_u8s(write), + rd_val + (i * WRITE_SIZE) as u32, + &data[i], + &mut cols.writes_aux[i], ); - i += 1; - record_id - }); - - Ok(( - ExecutionState { - pc: from_state.pc + DEFAULT_PC_STEP, - timestamp: memory.timestamp(), - }, - Self::WriteRecord { from_state, writes }, - )) + } } - fn generate_trace_row( + fn fill_trace_row( &self, - row_slice: &mut [F], - read_record: Self::ReadRecord, - write_record: Self::WriteRecord, - memory: &OfflineMemory, + mem_helper: &openvm_circuit::system::memory::MemoryAuxColsFactory, + _ctx: (), + adapter_row: &mut [F], ) { - vec_heap_two_reads_generate_trace_row_impl( - row_slice, - &read_record, - &write_record, - self.bitwise_lookup_chip.clone(), - self.air.address_bits, - memory, - ) - } + let cols: &mut Rv32VecHeapTwoReadsAdapterCols< + F, + BLOCKS_PER_READ1, + BLOCKS_PER_READ2, + BLOCKS_PER_WRITE, + READ_SIZE, + WRITE_SIZE, + > = adapter_row.borrow_mut(); - fn air(&self) -> &Self::Air { - &self.air + let mut timestamp = cols.from_state.timestamp.as_canonical_u32(); + let mut timestamp_pp = || { + timestamp += 1; + timestamp - 1 + }; + + mem_helper.fill_from_prev(timestamp_pp(), cols.rs1_read_aux.as_mut()); + mem_helper.fill_from_prev(timestamp_pp(), cols.rs2_read_aux.as_mut()); + mem_helper.fill_from_prev(timestamp_pp(), cols.rd_read_aux.as_mut()); + cols.reads1_aux.iter_mut().for_each(|aux| { + mem_helper.fill_from_prev(timestamp_pp(), aux.as_mut()); + }); + cols.reads2_aux.iter_mut().for_each(|aux| { + mem_helper.fill_from_prev(timestamp_pp(), aux.as_mut()); + }); + cols.writes_aux.iter_mut().for_each(|aux| { + mem_helper.fill_from_prev(timestamp_pp(), aux.as_mut()); + }); + + debug_assert!(self.pointer_max_bits <= RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS); + + let limb_shift_bits = RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.pointer_max_bits; + self.bitwise_lookup_chip.request_range( + cols.rs1_val[RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() << limb_shift_bits, + cols.rs2_val[RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() << limb_shift_bits, + ); + self.bitwise_lookup_chip.request_range( + cols.rd_val[RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() << limb_shift_bits, + cols.rd_val[RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() << limb_shift_bits, + ); } } -pub(super) fn vec_heap_two_reads_generate_trace_row_impl< - F: PrimeField32, - const BLOCKS_PER_READ1: usize, - const BLOCKS_PER_READ2: usize, - const BLOCKS_PER_WRITE: usize, - const READ_SIZE: usize, - const WRITE_SIZE: usize, ->( - row_slice: &mut [F], - read_record: &Rv32VecHeapTwoReadsReadRecord, - write_record: &Rv32VecHeapTwoReadsWriteRecord, - bitwise_lookup_chip: SharedBitwiseOperationLookupChip, - address_bits: usize, - memory: &OfflineMemory, -) { - let aux_cols_factory = memory.aux_cols_factory(); - let row_slice: &mut Rv32VecHeapTwoReadsAdapterCols< - F, +impl< + F: PrimeField32, + const BLOCKS_PER_READ1: usize, + const BLOCKS_PER_READ2: usize, + const BLOCKS_PER_WRITE: usize, + const READ_SIZE: usize, + const WRITE_SIZE: usize, + > AdapterExecutorE1 + for Rv32VecHeapTwoReadsAdapterStep< BLOCKS_PER_READ1, BLOCKS_PER_READ2, BLOCKS_PER_WRITE, READ_SIZE, WRITE_SIZE, - > = row_slice.borrow_mut(); - row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); - - let rd = memory.record_by_id(read_record.rd); - let rs1 = memory.record_by_id(read_record.rs1); - let rs2 = memory.record_by_id(read_record.rs2); - - row_slice.rd_ptr = rd.pointer; - row_slice.rs1_ptr = rs1.pointer; - row_slice.rs2_ptr = rs2.pointer; - - row_slice.rd_val.copy_from_slice(rd.data_slice()); - row_slice.rs1_val.copy_from_slice(rs1.data_slice()); - row_slice.rs2_val.copy_from_slice(rs2.data_slice()); - - aux_cols_factory.generate_read_aux(rs1, &mut row_slice.rs1_read_aux); - aux_cols_factory.generate_read_aux(rs2, &mut row_slice.rs2_read_aux); - aux_cols_factory.generate_read_aux(rd, &mut row_slice.rd_read_aux); - - for (i, r) in read_record.reads1.iter().enumerate() { - let record = memory.record_by_id(*r); - aux_cols_factory.generate_read_aux(record, &mut row_slice.reads1_aux[i]); - } - - for (i, r) in read_record.reads2.iter().enumerate() { - let record = memory.record_by_id(*r); - aux_cols_factory.generate_read_aux(record, &mut row_slice.reads2_aux[i]); + > +{ + type ReadData = ( + [[u8; READ_SIZE]; BLOCKS_PER_READ1], + [[u8; READ_SIZE]; BLOCKS_PER_READ2], + ); + type WriteData = [[u8; WRITE_SIZE]; BLOCKS_PER_WRITE]; + + fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData + where + Mem: GuestMemory, + { + let Instruction { b, c, d, e, .. } = *instruction; + + let d = d.as_canonical_u32(); + let e = e.as_canonical_u32(); + debug_assert_eq!(d, RV32_REGISTER_AS); + debug_assert_eq!(e, RV32_MEMORY_AS); + + // Read register values + let rs1_val = new_read_rv32_register(memory, d, b.as_canonical_u32()); + let rs2_val = new_read_rv32_register(memory, d, c.as_canonical_u32()); + + assert!(rs1_val as usize + READ_SIZE * BLOCKS_PER_READ1 - 1 < (1 << self.pointer_max_bits)); + assert!(rs2_val as usize + READ_SIZE * BLOCKS_PER_READ2 - 1 < (1 << self.pointer_max_bits)); + // Read memory values + let read_data1 = from_fn(|i| memory_read(memory, e, rs1_val + (i * READ_SIZE) as u32)); + let read_data2 = from_fn(|i| memory_read(memory, e, rs2_val + (i * READ_SIZE) as u32)); + + (read_data1, read_data2) } - for (i, w) in write_record.writes.iter().enumerate() { - let record = memory.record_by_id(*w); - aux_cols_factory.generate_write_aux(record, &mut row_slice.writes_aux[i]); - } - // Range checks: - let need_range_check = [ - &read_record.rs1, - &read_record.rs2, - &read_record.rd, - &read_record.rd, - ] - .map(|record| { - memory - .record_by_id(*record) - .data_at(RV32_REGISTER_NUM_LIMBS - 1) - .as_canonical_u32() - }); - debug_assert!(address_bits <= RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS); - let limb_shift_bits = RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - address_bits; - for pair in need_range_check.chunks_exact(2) { - bitwise_lookup_chip.request_range(pair[0] << limb_shift_bits, pair[1] << limb_shift_bits); + fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) + where + Mem: GuestMemory, + { + let Instruction { a, d, e, .. } = *instruction; + + let rd_val = new_read_rv32_register(memory, d.as_canonical_u32(), a.as_canonical_u32()); + assert!(rd_val as usize + WRITE_SIZE * BLOCKS_PER_WRITE - 1 < (1 << self.pointer_max_bits)); + + for i in 0..BLOCKS_PER_WRITE { + memory_write( + memory, + e.as_canonical_u32(), + rd_val + (i * WRITE_SIZE) as u32, + &data[i], + ); + } } } diff --git a/extensions/rv32im/circuit/src/adapters/loadstore.rs b/extensions/rv32im/circuit/src/adapters/loadstore.rs index 7e3285bb47..aa255662a6 100644 --- a/extensions/rv32im/circuit/src/adapters/loadstore.rs +++ b/extensions/rv32im/circuit/src/adapters/loadstore.rs @@ -32,7 +32,6 @@ use openvm_stark_backend::{ p3_air::{AirBuilder, BaseAir}, p3_field::{Field, FieldAlgebra, PrimeField32}, }; -use serde::{Deserialize, Serialize}; use super::RV32_REGISTER_NUM_LIMBS; use crate::adapters::{ @@ -389,7 +388,18 @@ where // We need to keep values of some cells to keep them unchanged when writing to those cells let prev_data = match local_opcode { - STOREW | STOREH | STOREB => memory_read(memory.data(), e.as_canonical_u32(), ptr_val), + STOREW | STOREH | STOREB => { + if e.as_canonical_u32() == 4 { + unsafe { + memory + .data() + .read::(4, ptr_val) + .map(|x| x.as_canonical_u32() as u8) + } + } else { + memory_read(memory.data(), e.as_canonical_u32(), ptr_val) + } + } LOADW | LOADB | LOADH | LOADBU | LOADHU => { memory_read(memory.data(), d.as_canonical_u32(), a.as_canonical_u32()) } @@ -459,13 +469,27 @@ where let ptr = mem_ptr_limbs[0] + mem_ptr_limbs[1] * (1 << (RV32_CELL_BITS * 2)); let ptr = ptr & 0xfffffffc; - tracing_write_with_base_aux( - memory, - e.as_canonical_u32(), - ptr, - data, - &mut adapter_row.write_base_aux, - ); + // TODO(arayi): This workaround should be temporary + if e.as_canonical_u32() == 4 { + let (t_prev, _) = unsafe { + memory.write::( + e.as_canonical_u32(), + ptr, + &data.map(F::from_canonical_u8), + ) + }; + adapter_row + .write_base_aux + .set_prev(F::from_canonical_u32(t_prev)); + } else { + tracing_write_with_base_aux( + memory, + e.as_canonical_u32(), + ptr, + data, + &mut adapter_row.write_base_aux, + ); + } } LOADW | LOADB | LOADH | LOADBU | LOADHU => { tracing_write_with_base_aux( diff --git a/extensions/rv32im/circuit/src/loadstore/tests.rs b/extensions/rv32im/circuit/src/loadstore/tests.rs index 1e83a3f62f..b5793b4b75 100644 --- a/extensions/rv32im/circuit/src/loadstore/tests.rs +++ b/extensions/rv32im/circuit/src/loadstore/tests.rs @@ -25,8 +25,8 @@ use test_case::test_case; use super::{run_write_data, LoadStoreCoreAir, LoadStoreStep, Rv32LoadStoreChip}; use crate::{ adapters::{ - compose, Rv32LoadStoreAdapterAir, Rv32LoadStoreAdapterStep, RV32_CELL_BITS, - RV32_REGISTER_NUM_LIMBS, + compose, Rv32LoadStoreAdapterAir, Rv32LoadStoreAdapterCols, Rv32LoadStoreAdapterStep, + RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, }, loadstore::LoadStoreCoreCols, test_utils::get_verification_error, @@ -98,9 +98,7 @@ fn set_and_execute( let mem_as = mem_as.unwrap_or(if is_load { *[1, 2].choose(rng).unwrap() } else { - *[2, 3].choose(rng).unwrap() - // TODO(ayush): should this allow 4? - // *[2, 3, 4].choose(rng).unwrap() + *[2, 3, 4].choose(rng).unwrap() }); let ptr_val = imm_ext.wrapping_add(compose(rs1)); @@ -210,6 +208,7 @@ struct LoadStorePrankValues { write_data: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, flags: Option<[u32; 4]>, is_load: Option, + mem_as: Option, } #[allow(clippy::too_many_arguments)] @@ -218,7 +217,6 @@ fn run_negative_loadstore_test( rs1: Option<[u32; RV32_REGISTER_NUM_LIMBS]>, imm: Option, imm_sign: Option, - mem_as: Option, prank_vals: LoadStorePrankValues, interaction_error: bool, ) { @@ -234,14 +232,15 @@ fn run_negative_loadstore_test( rs1, imm, imm_sign, - mem_as, + None, ); let adapter_width = BaseAir::::width(&chip.air.adapter); let modify_trace = |trace: &mut DenseMatrix| { let mut trace_row = trace.row_slice(0).to_vec(); - let (_, core_row) = trace_row.split_at_mut(adapter_width); + let (adapter_row, core_row) = trace_row.split_at_mut(adapter_width); + let adapter_cols: &mut Rv32LoadStoreAdapterCols = adapter_row.borrow_mut(); let core_cols: &mut LoadStoreCoreCols = core_row.borrow_mut(); if let Some(read_data) = prank_vals.read_data { @@ -259,6 +258,9 @@ fn run_negative_loadstore_test( if let Some(is_load) = prank_vals.is_load { core_cols.is_load = F::from_bool(is_load); } + if let Some(mem_as) = prank_vals.mem_as { + adapter_cols.mem_as = F::from_canonical_u32(mem_as); + } *trace = RowMajorMatrix::new(trace_row, trace.width()); }; @@ -278,7 +280,6 @@ fn negative_wrong_opcode_tests() { None, None, None, - None, LoadStorePrankValues { is_load: Some(false), ..Default::default() @@ -291,7 +292,6 @@ fn negative_wrong_opcode_tests() { Some([4, 0, 0, 0]), Some(1), None, - None, LoadStorePrankValues { flags: Some([0, 0, 0, 2]), ..Default::default() @@ -304,7 +304,6 @@ fn negative_wrong_opcode_tests() { Some([11, 169, 76, 28]), Some(37121), None, - None, LoadStorePrankValues { flags: Some([1, 0, 1, 0]), is_load: Some(true), @@ -321,13 +320,13 @@ fn negative_write_data_tests() { Some([13, 11, 156, 23]), Some(43641), None, - None, LoadStorePrankValues { read_data: Some([175, 33, 198, 250]), prev_data: Some([90, 121, 64, 205]), write_data: Some([175, 33, 0, 0]), flags: Some([0, 2, 0, 0]), is_load: Some(true), + mem_as: None, }, true, ); @@ -337,13 +336,13 @@ fn negative_write_data_tests() { Some([45, 123, 87, 24]), Some(28122), Some(0), - None, LoadStorePrankValues { read_data: Some([175, 33, 198, 250]), prev_data: Some([90, 121, 64, 205]), write_data: Some([175, 121, 64, 205]), flags: Some([0, 0, 1, 1]), is_load: None, + mem_as: None, }, false, ); @@ -356,28 +355,34 @@ fn negative_wrong_address_space_tests() { None, None, None, - Some(3), - LoadStorePrankValues::default(), + LoadStorePrankValues { + mem_as: Some(3), + ..Default::default() + }, + false, + ); + + run_negative_loadstore_test( + LOADW, + None, + None, + None, + LoadStorePrankValues { + mem_as: Some(4), + ..Default::default() + }, false, ); - // TODO(ayush): add back - // run_negative_loadstore_test( - // LOADW, - // None, - // None, - // None, - // Some(4), - // LoadStorePrankValues::default(), - // false, - // ); run_negative_loadstore_test( STOREW, None, None, None, - Some(1), - LoadStorePrankValues::default(), + LoadStorePrankValues { + mem_as: Some(1), + ..Default::default() + }, false, ); } From cf8bcdc911f3fb8fd796cd28df9341e325ec088c Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Thu, 15 May 2025 17:22:30 -0400 Subject: [PATCH 24/49] feat(new-execution): measure walltime in codspeed ci job (#1656) - add codspeed walltime measurement job - tweak execution benchmarks to be heavier and more representative --- .github/workflows/benchmarks-execute.yml | 42 +++++++++++------- .../elf/openvm-bubblesort-program.elf | Bin 65684 -> 65692 bytes benchmarks/guest/bubblesort/src/main.rs | 2 +- .../openvm-fibonacci-iterative-program.elf | Bin 64056 -> 64056 bytes .../guest/fibonacci_iterative/src/main.rs | 2 +- .../openvm-fibonacci-recursive-program.elf | Bin 64236 -> 64236 bytes .../guest/fibonacci_recursive/src/main.rs | 2 +- .../elf/openvm-quicksort-program.elf | Bin 66048 -> 66048 bytes benchmarks/guest/quicksort/src/main.rs | 2 +- benchmarks/utils/src/build-elfs.rs | 12 +++++ 10 files changed, 42 insertions(+), 20 deletions(-) diff --git a/.github/workflows/benchmarks-execute.yml b/.github/workflows/benchmarks-execute.yml index 6b043cc558..fdfdeb9047 100644 --- a/.github/workflows/benchmarks-execute.yml +++ b/.github/workflows/benchmarks-execute.yml @@ -1,4 +1,4 @@ -name: "benchmarks-execute" +name: "Execution benchmarks" on: push: @@ -19,6 +19,10 @@ on: - ".github/workflows/benchmarks-execute.yml" workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + env: CARGO_TERM_COLOR: always @@ -27,15 +31,14 @@ jobs: runs-on: - runs-on=${{ github.run_id }} - runner=8cpu-linux-x64 + - extras=s3-cache steps: + - uses: runs-on/action@v1 - uses: actions/checkout@v4 - - - name: Set up Rust - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 with: - profile: minimal - toolchain: stable - override: true + cache-on-failure: true - name: Run execution benchmarks working-directory: benchmarks/execute @@ -113,32 +116,39 @@ jobs: fi codspeed-benchmarks: + name: Run codspeed ${{ matrix.mode }} benchmarks runs-on: - runs-on=${{ github.run_id }} - runner=8cpu-linux-x64 + - extras=s3-cache + + strategy: + matrix: + mode: [instrumentation, walltime] + env: + CODSPEED_RUNNER_MODE: ${{ matrix.mode }} + steps: + - uses: runs-on/action@v1 - uses: actions/checkout@v4 - - - name: Set up Rust - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 with: - profile: minimal - toolchain: stable - override: true + cache-on-failure: true - name: Install cargo-binstall uses: cargo-bins/cargo-binstall@main - - name: Install codspeed run: cargo binstall --no-confirm --force cargo-codspeed - name: Build benchmarks working-directory: benchmarks/execute run: cargo codspeed build - - - name: Run benchmarks with CodSpeed + - name: Run benchmarks uses: CodSpeedHQ/action@v3 with: working-directory: benchmarks/execute run: cargo codspeed run token: ${{ secrets.CODSPEED_TOKEN }} + env: + CODSPEED_RUNNER_MODE: ${{ matrix.mode }} diff --git a/benchmarks/guest/bubblesort/elf/openvm-bubblesort-program.elf b/benchmarks/guest/bubblesort/elf/openvm-bubblesort-program.elf index 0f81a3926fa0eaf4de3baa89647cbe044e5fa4fa..160817026876edf76d4ec5195c6c08c63da84a74 100755 GIT binary patch delta 13673 zcmb803wRVow#TbGGecfLl1YF-Ae~GSLIRnYWF8661TrBiLQo!2d1Nv(0hUK11O!1R z2orHZ4O=`HxITHTvZ5OWU0DT}04i7EE+8r@?Bc?8eT7#>S|cG+_ZJMsjXMsu_T4L7-JXKW(E2>CNM@HGXyM&F$rIeDfrA$m}Bi@ zJ>#mI7^`kmSoPY2UTN5K-!AVWv)}S%v7Y4hCT=cg)`*F1M8%6O2lyXjw{)vI!Xp;8 zv797K;dA1qT_-9}=ZdEnzZ_>7yx!zZW6WwYf2aB}3qNdiAkmy^H8IA`EyHcfbIg|g z7_;R_jGK~}HLh(q&yTMHHO61;UDdI%Ms56rcag%Zma4;6bc(|nZ%QbT&3|m)nowkx zD)^g87v-v>{Q2af_>R|g+vB`5d1N>9IQW;n3sK$dJI#ltWXryzye?&MkubEpkDkc- zm~QiKP}qvR!zQc6%mN!MY#*4nR(0b?Q!4uH=rGM_VQe3~@rduN)lwd_+TBCv2=8z0 zpK`=x?Sh&;Ow79uZJH{(@j7cocHRzCpqEK*WGyDWFKX=s)Ydq{|7iVQ>fo=YX2x&$ z$a@#+cIomdzm!^(7eQ5XqAlk+*=k-c+Y}6EA`R!dpcBIDSMpA9r&oi6yBx}5!`MnlZOENdd>3$A; zq|1`55g}{&P#@k;yeU1qhtFa)SBQMMbj`)CbdPLKP48?e!uRy%vaf(Y zdxOG*eNs{)dD@J$ysSGc;;Z`HOBS&i#VL_oO+RP2o`u5KgdR{C_MaSCUrbR!G{oTH6Cz567fOPFlbIwmao%pvMl( z5@U_*fXpKio{+mUz4iALD^8b{>f}H#y)u6@w@+V7C0A#}!mOI^Ta37Pm zMUo=?k^ZCc_hSFMq*gvX|48}+@iD#!WUI-+o{%IK=oiV*dL7{-2K1b0j-wH;*zSuf zX?Ouzi%gRrlWU1zky{mSHK#G|Nn`DiQ>>lzt1P{3%8qzj_TG51v*quIRlf`Ns^7RW z;$!k!`O^cQiO(~~+j7is5#fP?!M&eEtsQ&gV{+ppTh8n3FDCOB+T29=zJlrKanC}# zY_{;~!t9hPi?vG_}KD9%t6E$zV6>{3G}uw8oJf|vBfG$a`aN|;1>s8cUdJ3 zD(-gpj4fNZs~XR122I1|w`WkHbc(+}XvpB!SZ;B!z|7NF4yss8o(1z*ZdQYrXNlW~ z;H=nD6<0FjG=kbi_HIN_^SZ>Z$QvEcYl_l}l_Ln^JMkp9ls4{vm$0it_7W`QrE7SomH_gi{-2A^|IN|JMFjR#eZYV9-nK= zsmS#XLAVm$+;7YC=i2g}xweAwx%-x)1FI9}vz$La}sMCVp=jM(Q_*-H*Sw4!6s`hxy9kugK=LymG|vQX^dhhmc{GNLLyTkEAi% zKptP-G^k@gt?E{@gj~aNn5!;_-`Mi-8@<5NxLKVn^>>8tEbpHsYE=t8D)_= z+9!|dB6saLiIwxDzH;)$aeetJU$^0hkE=IM?N-{`+luBbBonvAmULbAww2ab)vmmk zKA2bdNnf^XKFqK9ux_5>jw-C1XZhJGS~ss&dGXgf`sr(;_Ab>sCE0hb{n;CCm#}Ij z+_W^VN5qgJqfTja<@=Oxo$!!!xV>=V9vJfXCuMdu&sF22My`W*nOq|KaBG?D>8;&u zIF@s|6Ga2(`rCHm*!@9_{5k|hV6+MP& zQ|KC#G+75PyvDpqlEU04@d!7m@bA-R76GswrIKbRhtI{DPP zEy>MI3R?|(@nOX1r8k zLHh2gu>I)@EVV){XH+X}&M3?WY^ID<*jf5r2|s+;%v;0NixoB&Rz}a~TnbxOf=D1{ zMtLomN-RDU`NUKOf1`Y97Qzf}U*84OJM!VxNQWX80c2xOixA$vobD zR#tMx5=G4&H@+zXUS2V4baD-d@G#T)mhx$JzfNgdc73iw9PclXGPrZDwRh<~3R{Wr zh!l-ZEuZGU-8U2JN$`FNH>XATp52A1*N?G^F?$03CVya7^Ae?TQD}a{oW{FQ=Q96s z?l;m!{?)u0!|Y*&r9f7J?`(X#^y^lzQopcSX`UHUX3YzR7Av!mwCp?B;ES!~n;T1p zrRp`>;h`s_Z{VBqDPB?Lgc_O$lclh!)NMO;n>X6yn!cCPnz78#1W5?{3gpU}IEC%v z>GS)9Cm1xBpa~i{1#{GrK<=qXEDcN#biGuP*$ACgH9U_5iv^5YnxnJOH-f1>!SiKA zh8h%tg)nI59rH(I9WXSz4N@aNN^W`X0=v|UPsHD&Jc7Rm_zMd%!}L(96^Z=>J0nZj zw45~PyMhlOUX-=eT@RR6*<)ZCl36+ne{;akCu{2+>9^@D^pD1{Fk5Me&l@nHrCS*M zH+U=b`4CWpSHNct`tQLeeF90JM?FhXwCboa030;vCxBNO^pj0t#-248+y?{dkXB@d zO@P_~!K=WBpf87j8f@2D==XxDLxNudQwId^2RDO|9uv}jNiN}IB8F)On#?bG( z!KO3?+Y2p?_Uf#)Be9o=akCZI;{|YzfxE{cjdmLZgK@D}_7EMF*f8+7hK`qm>CUee znT-MuHE+ zf@$JvbsjoGo47eThrtvl#GgsB%w7XiT-5{vo6EI0sS^bbR%mfnbo-qZSkNi359oqV zU_hB68W=PZ%-zc}!?Y#Rip1RLJl(zpmx8PB)VqPf21pN9n*~1vW(Ix@deK+hUqgw-sB_|-C4)u;Um4s#vq~^Es1=!w26r0x zMlf|i=x+v#-6Hv0oPc~HfiPGK0~(Rwd%-kf!OOv7V@VEn>n!wpz_ig!gn;z#=`8dg zfGMJa&xIkVF$n$wroE^z_$&B;f&UkL)WDr!+N}!v&%m|wqx=PUrh&f#FE()Q6$qX* z2)+l?7FRg@34DPsyw|Q~%2tRefwLWm3o>OlZe+xFfN5r9X=MTb5=_HV@5e~cH^DRv zkra*74!E0d4`+tO7L;Z<4--vWRIT7X2wu4$ zx(}HEp3o+)2ou{6rpJN|RG=GmTvv@n{Y3B~gTLimO;4vxXq9eB$!VBO=@<{S zW`^gq={>-+!{gBHH~88P9ymzRs>1g!@EQX@4bE7os3#KC)@kq{aIM-CtR5+hk$

J408BNASA*Aq)#(851xE~>dkK8S!245h4J|f|=2aLZ!hu$>=K@oM zf;+(J;3W`HgO_y{`Y*xM0l{B`#rX=^XIV8C`W$N@dhkL9<6uAsF~Z;$usDz*{XIGh zeGB*xuvg;@{nI)N{WIVL{M0JDpN1)B<6xB*2a&chqwypS{k1d&(>y#1H#A#o!88wx z^|bF(t%b=1J>4kRfFnHgK&F(#BM;c|_k{;C!*pb$Ud3qFmkJMHtw^jIOvf@g5YR2@ zMV*EIWpF3-t8~3RO=F=i2GjA4U^keKih>v-`CFfcnI(ZR*aU;xyP~P_8ko+PHo<@@ zC_OY5_BJq`Y(1;%f3367Zv)e*mrHN3Ur&vNKC35om2@OVF{K8!!+=;A>;ltK*=}8L z!_`kL^m$-9PE+r2sJ~5Tp??%iM`^LEEC2tKPG2T>JV?O--M zIt8nUhbZ_%MPjSLkD@;MrPkjIei1CL?(<+e#?y+#E`ZhJJyjru*+mHG98eg10zPVR zkk$vcBIrekH|Q+vH-qW?kQ$^Z?2BwB7J3CtXN`hwU>cBC!q^!?a0U&MoFXtDKmK9h zW#CQMDo890PFkXMi0q#NKRH4;#7zy{2EB;L8F200(Fn)%Wvo=_!*Eap!5Ij|6x;^B zP!V-F1>6e`L`d%k)7h+uNT<%|KZ?XY15*xZMPgro&n=2>{4)A!OnT(M%rYUM%T6mY z%Ldb7vfy0sB?IS!&sR;$WG2__)rZ!gjDY zS|i=`8e8rZ(q-ONG?Bvj#5T_6lz15?ro?f~b5?GTXtWu1loOK>eu zYqL9PIU55p-IL@Sv^#*h{((P*f-*@>W!#e-HzU*d!nVvX9gS;6Vtw#a85QMz_~|fk z7MNaAXhmW<_(?A+1oy{JdTAlJ06!D&iC#bx!9OgEa*KjIJhwD@1wIXfI)lM!a!`do zYLVEx_?bEyf7Bwev-nAGPP8JikMMJoA%Fi2t}_JlV=%pe(2C41DPcH72ZZ2n_?cng zzk>%F_;c_s1OEg3BDg=(W+yXCV;~ByXfO>>D>BOk)5{6L`C$lX#6nOA7H>dkx+m!@ z^tXX2go3AlDa3-OgEI_V52lwULLUNG8`w7!f*ONhHke+chzj$-2Ml~C_^5%K!DslX zU)jSnQQ~yt!%e7mQQ!2H%iTtYvQ>I*&w0te`VWGjqY4#@e7ML<`D7r!UJhfVz zc;WvZhr+M3_Ku*jbzUn1Os7k)WCM|83PxCPZ|d6 z2FLP=o9xb5Yz|E52hHbgm`JiR>%0|AUX#ESj>^e&oxQLrGv%_u_cZu2P2_*wRFq=E z>UvDSoF0NP#Ce;?hL2B*4&^CWzl%1yQ5%?sQ{Ya6{tWmB=+z{LJ|Qh{(zMO{()1Wtln+3kFxf)FpE8I+)&DYei8_6a9_OLjN6@Iw<%@ zFm*sMokvjz1;^xvwH^t91qSrmTW}m$ydtL#xpWqKFIc=fCw;xnLLUOt00qwki`VF6 z&r@}QFxUXT!hPEsC8hm^Z4bwU>0Nm_v?&Nk-azf$tV8D_Fum)=<)P|J!Srr-mChqc zKRBwN0H(K_LhlEcTeWMQ{5OH=?dDOvet00}$5?^G;D$B@ru460Oze3ut*sXG4>d;wjel7v;S&V?Az zxn2_l)Id*Q$K>ZpP0a&6@jL4FWSE0^HoVB?6JM&b?*y@>D= zV7jU+^-23E3<2F`kD&pIfNd0J8M7`zb_X#+=+gC1f@%34qw|(Zq^%(WZ-Ltk%zQY1 zGw@s0*lU(XI}o0Lrv`eyq!1-zPQqY%0#DR?_ySn{=RrD;xKTS}*spis5SY#wR_a`F z6Vi5=!e;7x1WZQ)A_5iYIX%IbBcvE`n7s|bg$nJ7nIiBWxD62z73SZJO%Zl45UUZo z8K~2NsU{6BF}RQ&HYRaEjb{TlVpI#%RhIl+J1wUeKC&YJGNJKzld zqA_%*bH<`j$TfT3tUF8Zy3^qZ@s?c~NhQU_4u>n`@_5`00gvCwckl8imDC4Ii$ku4 z21jYAzO2;AAN|mlZ}JwZoF&>M2qH+bv)0YuyBaui=T^nYpWbpI3A;h*(g?D{9>KWkm${7kV^38ldCHt+uF{hF`i5ec%i|64oTn>hAS=DaZhxRG z;B6>#d;LCSg`U^}kGIs>fLei4S3R=X?f=+*#Bj^tJ{q;qzf7{Z{li%pU&Aa>k zZug1&ByYe|QeReF(%=e)>Ps2|?dMN=3nT|mznW=Q|F;t#cGYI?)q)j=hwzK;nbrKk xtG2jZ^b5C&p?vLCD?fHMNvh@_T+g|z{vV3c^PT_z delta 13801 zcmb803wRVow#TbG(~|@UOfm@x5J)GJ7m#F~B_ zzjIEVsycPLy1H67EVZp(YHLY~KIG7uk1=*)RcfGT_Bh7qqd~wNj4Alao`g@m&aziM zn;2c%z*uRE&PrDu@GFHc|9xqs+PIxBjw*^xuyL)FIh$>?`{v>|-=6K%fvhhpNc29!Msj+V4$MnHg20Cf+P8(y8*m zbEPYc+Z@apU18_>9fm25{JsufbU%FPsS0Xl|GWPAI&<0&w>#m8lfwla`>0I^+ctH~ z)06}JJ;xX7;Uj!+Y+i@*H-zmw{BrEzgr?E(ulnbqy4L=_D;;fRzRSlXB=G#Mk;)NX z7uPROR%!m2YmKToGB(k_R%gpHhuNHVjRn@)*={gzJ{-f3#+CJYuH81Rk+I$I%bVIi zaoS5GRu*;@tZ}z9D{i>W8Ht)*ZOp$Kt=kU8@H%H%TG|#{AjziAVvRP@E2DNt)HYe; ze{%k)l=CC;DIL0=^52KLkq@2cm*Vpjji)3ecD`Xq7rCRW&TA5WeWUm*h%@_?|KQY? zim58?We%v>=4{pwLoukX#-PTuRd)SA9b6tCk)g%A(q3UMeG{8?Ddk5qmdy^XMGHUs zPYGEUXOomQekz9Y6JO9Rt!umH)XIc8J9pj1&Td6&Q#?P{?M1bz2R}c&r`?F2#&>t; zYI|=!vWF*5%M^+Ad*LycxAb^`w2owGGfZvij_cZS$v3Ho{d{%uEy^%{DtWkixTr0= z=f}#Fa*f-&GS`dm+X5r)>h3ibjydW5VfoL`xMv2XkYZ?9AD0XNfL!h5d1o zh~*G|DWkh$Z|j)3UO`f9%^Hrs7qjkD?0j1G5ha?tdnbmps6bs4Tb5Cd4r^={E7i=- zH$_n}mThj2^49*A1$x<4N{on%=ICV^hohZZ0^>yqtgY#N)<*gj_U^8nEm5w#T~TD` zO5b9BXPS&Fq(eley+hmK_nCKCJM-3lzyjUv9U^#p?+qQ&v<@y^>)=G-r}ydCy>&|m z*K@l%L}WxOuJkw9XEyC?ne(spnM*R3Kj6@Am<4r|Ux2Xj68xh=5VuHxF)`0~D) zYP-g__Z^nee8rW1K*go3`p09&?9HQH+2B5BR9DUc)eO@xo|vx$OUm_h9(LT7Cfyyb;1hDE;KJOU+gG`Rf0o-H;ZDix z5p0g4rD55Y!%^O87<{it)&CR%n)y)lvdkmVlwI84m9;frVQr*;JTl6aa|8kXFpBJ4 z=|{}(%nzfMZ7x>~SuJO41RKf>j7XBDP99dF(1%B;$8Wfx?)`i#xkeLw21Tsgh1K7Wg%9J- z2b!+9GVvSz!|!#*C1U*6_>R)7R9T}8a|Ek$!>S-$L*w6--im<_DRW0@F}&&9YiMdw zSvvalOxd&;EYp3Yb4=CXsW6>0_=wuRlMfh@rj)l$9TKT(<;QS!^6BN?n}!`V?yJU~ zc7Jz&Gn%uLOx}GPy9wDlPFjo&yV4}l`F;FEd79eP{^T&MvB!AvFs!kkV2w>fXCjCD z@psVhZP!I@T}EtG)b>+Ya`m?3H}_MplsRv`Kf0?{>9m)!#0Xk#hP-b^y!|}iH12U_ zSey5b9ZGJ(1*iQqv=I!K&rft4acW_>;4xDZuTv+UJ2j-_@Rz3gwO3~8EQ^;-Y2XtpJ1S>+uyTEDeS^+cqE=^osF!DX zY*pXhN%!h38I*{RijNOm-&JRw@Y{w@L$=OFq@j;ISanx)CA8xqJG$bs4$^;Ky(m?t$;t!$G2>Uy(b+=S^+5+?@tu?zwm*U2%UwD?TN{i*v~aW_W?ff_-yioyV+2vGrdS>=^wXsxuoeue&`|x=u7~fzvla}fSvZp?-klm7zfd>QCl}uo z2{*s_?%}Z`K$J34I*vbkw_j=HAKg7D`Z#ojhGptKeobGbqYkC!TlWl){vVJZx(U4J z^p#3F|Ig`uWhXx~J*1rGRrTv*>lWXbw5NH@jJ~GXL$#961r`$O@#CyzA1w`=z4vyb}pjIn6w!nY|jdtgKe=5KPn0J zSWjpw6bx(`vf_BO&RTiu>>iGA1TZ}bCK;r%L4pl6JP!uT zm5N%b7cBL&z|@}P*(zc{4NAd07}WE(W)Di;V`+9Hq*iVvw>)=_TXFEo_`9D!hQE9G zzBwr&dYUzh!hVLGl@;8n-n8grzcQJ%%{(FfM!{0w z8o@$ru_ZqL4Fg(@rNLX^)zD`{Kn;EmK4#JX2)+uvoT*Io?0bv8H+YhmV5&b3+!z=3 zf2S?P*b5ee2Vp=RGKN- zx)4Z%FTvCy$rr$T_>y_<*!`H`?;%!Ig}s9DaDtmS)A*9R+d{yXp` zFfD4tXTduyqyGY2?9`#9qOdQ)11x+2O#aNGvP)p{C;1x@L2E&OTQO^uR>pnj#8TJ5^T1kuvdw3zm>Cm7MzT`tK=@xNTW>_K|fsV z1G>tNDr_M5D@(^q!EtbC7L^SF`z$<+>Rb3`@aq;H38n#<#m2@&BaP&afFivJ2E=Aj z*%I&tFx|dL|AJtt-v*`;O8yPF69yCm0oi{fSn5xJDFTvDr9+Sd!8}N)LWd3}OM@8j zCO+eScZlMW4>6_DmYd9b5+w}rc`!}f1i=FanG;tpI0U9RA^uEJRdx_eaWxVQtd*K^ zGA0IGQ)b3l7xr7ruyj*k9~OcyVL+K78^|3D<^va}glPX`7KIg}^K_$@TnsL~S9Ak| z4UiseGz)$l{Cz{1Uj?5)aExw4{~36aMc)}4$M7aVVX+;NPkXzE8;pm+qdmf$NEP5P z3q7J}mkBmIh-(5&4N4vkK5OAyz|;Y$9|e|ML-MzvBl3v^(%^m=(1;{I0HzU3UJ91G zNOG`Uu+;AW)2^};1f>61u+*OfQ$!`74nZ)&BKQnU8&7HQ7w{en{}sI7!so%XHI?=k z!4GUB_y zG_z5(vVb1})3A*DF%tAoU>b%@=uQRZFdXnl2|fx;y^#9H!88o1f2RPMa>8QZ@L%A`X=|1Ov^gZ4wzV4T;C^LW9xgXI>V^sB)Xr#?dez881EP5h-$ zN=R-tX@)Z~(b&Dih>F5`gBQ;U??c9cN4AJ@;dZ_kOiuvGu%;C_8Ve7xSrpa@yw}2A zz-Q47V_2wB5`#F@24tVn*<@*-4ZaM$ zxbn$MQ)2NPC2SSe0^2(N7;H(3P#U>*vWTQ_E!|PD!eZO23uC2vxNa(#o~_KHvUIS6 zzx1HnsI>=T%Gy{D~x(1e9?2GSrj$~OwVbO zZv)dao8&vdI=m)9Krst}Ph0d4f{QKs?cl2x{f;ikf(nblM=+oVMdR{>%Q={SV%gw) zFg+tkUI?Zq0LhEN^bjdI1f~v}MP(0ysY8;R9Jm@Pche%SXcF~===G?>OIIRvI|XAA$|fT`Q7h5u>&5WEI{a(T+wsNOn^ zs3`0u^magB-5}zf;0d@0&0=G1;MVJ`v0dOg%jjMOpXQ^Q++)cqAJr69V`q_@)QV3G zCbke(s0CwVMY#=3t)OpU8`}z=1U8p68yjb)X;aGBLzcdsLG{bkq!n2R6HvLwrVaN~oqq^Z<*^bI`qF@wFM8o~xTx;rj*fatm(* z$IsJ^V+U&M18@d-g3%MK9&wD3f3w(_4NUfCQCVahDrNAPhuo>PFq47kG|+6e5_Xg& ze*$;nlOIY6+46L=E^OU!W^;$x1KwoeLU7c4^YM~8R|(z&rW(X6!HYEnFR^s) zRq!PX?~TJX)L!*{D^lh>wDo@EH%MD9QY>hu!$Q@8OgX z9oiUIG1@JUhX=4(6gC1(2QcXn&@E|?V5$E-_$>4*gx;NCvebLPbWkI?5KMY!!!O}rGYp8O!3$tI6x%NJE?oV@QlAN?gEQj}hYoHOEcLBmIy9p>qX8TjEcKs+ zAV`LR6nqM<92}mKufcT|{kPyt_k|z-^=>#%C;^KR&ITtf2i3zm8%#hi1=Hn4FxP2(qrZXu8c9`HCg z^a}lC!BSrdrX-Ub0Jnm3As~N`3zqs-V9GJctHE>vKB^EBs_>c+NP~l5N;=8yVB?S+ z05yykM!H2&NE9o&=^4OP&g*_aTz2!SpUfau8f<;qp2NMpy*X!Ss4WR+tIi zW8r(j`z<^de3YMj(jB6SlBXM=4l$cCmR97OKGckx%)e?dWy&SR%vWzY78*>PX>Vh1 zgDLX{pc|CWlSh~nFa0k;K2xTWKhl3V3YkQy*&qa+Mw_WwD!4zGQhkHqzkzAuWe4-e zm=iBMI2%l1NrF9X;$8t$7^M9bFoi+d7gr#Yj#?^wRAHvxBH^INSTptff+vG1vla?| z1}tAkQG;i|^2HXh>o#*D>E=Tm1k*f?7rZqd0_zBe-VWv)SEr;_;tG-bls{v-h|Qv~ z&%rDB$<^MLn;J`fC?*R`>6LM4gHK)?Qs(lLYiB7oKI$nq{w{ecCA7lQ)=?Mi zEc_n$>L}@3VIP2pjukVH-t@$-n%FGZ+k#J9*afZ`7uIKj=UTXTIvdPhun2Nsu;0RN z@KFnUz;8Yv9jdGl%=qMW?gEC*fek?OIS~^{R+``qVDjn!Q#gj+N!QuFbt!S@EWW3} z*VS?SZ|m~nu0s5#xSSq`F~oUKj}E;)UWW%Ncvl1KchN>?gH-lD_^d^L9DEsiBgrvM z5gDevQ5|dtpMpM4)GrV$^*%7wm+a4A{KuzPh4Sx|GKD=4o2#(t3nNO6x4|PU`gg$< z7Cr`^VBrtJlS1Kph?oWNx>ph{Q2~wo6Kft8e!Uw=}Eqn;vVj06l@G*=2Yw&Z> zt7wC|*b}#(Rtt{+ZvsaO{hTbs;7tgO)eIGW4TDQy>JmA415EF&&7!h5!Hc1n6aAfF zss8~?9hCeNm^vVt&ZDSVse! zpyWERe2q@_JYEQ-!CLSoK5FAEC8=%S#wQ{|^sc-V+BgIxGlv=PX7dH-f$3c@E)PRr z45oLxD+CWFeZR1N9GKp2N_`cW-fr52eFK=@ZoV!!l!N)PR^TuwYthja`cGVJIHNMx zSDHWS;5jfQOr>BKwx7mP5h{@WPB6W7j}rR(!KD^|FN5XVbqDPC?K z^(uXc0iEkLKtK&77MW+1GKjbJHxK^vg?x&i-V3w+=*((qUhta2RtFV?j&M27#x6>8hg!MwFT>mkjb_U|P8(9|c=S+%On- zaOh=(p9j-bJycBEsSpHomwgirPy}2K==SvDvEaoHxrYG=DqKCf)(-}m%;6b;TXAFBq2M&Sh zjA6OpvRje119es>_z0Me1Y`ut&~ti%FGWZ(;1GKUf)i!t6Ej8NWAF+@L{^wR3Y#MA zULZCiv>;d4I|H2bA4p=MUw((VR~QjN{`?Bt2LWv^ zl0^gOaU-QWsN|a`n0KQ}p??Ovhd;JCJopTkSA}s$>H_;yg^@e zu&TsU5U46B;rDDA?Ic>hYP_E6;{1}rTK@BvM1E?Eqf1fHTToORD5(v23ahJ& zAomXl7S$F6^Gm!{K5w8TScH9Y`T~|WvTEk6nR!*P^_3JB6a}h^YpVQ#f`ZyWO^Mf6 z;;pGJ^jG=$y<2;Sd^G`Iji;u#s=BzgHsCAz|2W9={JTx%d4Hi_p?}t~ynL2k^KZK@ zAme}dS6Dx9K}ps084dHR=PX*Z@SduMduEr+uJta0*L)=C0$G8td!duVQ{(fNR3YkB zeos|(em;NYg(?ST%7cg(`uw%|#h##_JI=)Oy`Of8FYy$W6cq-%-u&9)LQj#8BF1CS z=%K=3AXrdX6fE`z3#yBK#nw=Ii;GH%Ym2JA9&f(CxX>3Y^c7U+d-AJ`3W|dH)%iCB z=wBKa6k5V5twX=)+vEEu=D%p|ML@6TkaYfw);_-oWnQM4b^odcZ+$Ud|CdI`UL4^l zMu>`v{6#)*wZE_`SjBU;m4yP;wVq&AReo_G;H&j`Jmp9hk?+<1T1-u0&|l;6c#HFW z1zuboRfV+(dx6)VUzO+kw=IqMQkGm#6p>yooQZ(a#sp93=T$-a1s|lZR%@s}e*HYX}D*1tHPQLwGC#{tveBZS!jpW~Q n1@f!c97^Gu7$r-oTvLd@Eo&w#SxJB0%-A(arr: &mut [T]) { let len = arr.len(); diff --git a/benchmarks/guest/fibonacci_iterative/elf/openvm-fibonacci-iterative-program.elf b/benchmarks/guest/fibonacci_iterative/elf/openvm-fibonacci-iterative-program.elf index ac9fbf3e89e5c2ccade3d62e0afdd78b08877c15..888784fb83b566a98c6cdf1f4eaec7ca344b4f10 100755 GIT binary patch delta 764 zcmb7>J8P6d6owZR+3n`g(7^+nRA($Mo1Ab5X9Q<%q?JG(?nSUlFA<-Q&~w? zHg#Z|w5bK_{0X}hmafTy772-P%ICbim$S>Iv&*H^?bVg(%HYxR^26Di)hAy%XB}0} z&XQqu2<_wX%T9}Dmz{|LA|OHge*C)AAW15E2A|Q0Y0oE=t=A;RYjxH~r)Fy~9RIm~ zM@RdkJ_m})VllCr=mU|jK~q6R;9P}!GZ^N@`8_5`A4cyE#{1)XnBR|5IXu`cuANUd zI<(dpDkQ=+i9-81(GFwwwot9cml!;>2NzE}V-pCAmLNreef#`vcvP~VM}%ORgtXw5 z&xJ{v$Ii)n7Ax6cc7bKGrS z{_b(lcG}wXRVM)gd5p1IB5|da>EGI{!+ZqC!xzNit|K nnUiYl>E0x_ucwdOuaouJ<_SIPToR1IVx?Rn)-?O{WA@@VoweAh delta 764 zcmb7?J8M)?6op4ZFpY4AQb|UV zIxtOIYr)z-VVA-(H*r9Vghbfo$71cZ*3t3O(ecvZla=c`*G9LNmtW1FuiV@1G?)rL zOO9kpO0Ip{d(=4*tB$l%wWVNc-}jz%-XUcY1FE@_hW2nqxq2(UiVeF9r|`16t@dqp zx3ihhhf-s3Hbj8-YZkg%ot1)2u7cX)+RdN$yHiKP2@$HY6>6Wqj<>m_Vpxh|F;Pj+ zd@f9)iq|R;ErL^~!ek;?h+?EbgdYF-=u+75IrR30+Lq42FQn1@4^3_nZNKrSj?2x< z-#)IGPP?)HxYHpq5hsMvaVVjc{k0b<4(ka4G^Jrj)~*k2FGQoOLrYv-wiXr0V38WQ o4lNjT5PG^Y$?ePG?e_cN&V22F9&{D4utx));~;P5J3r>DCzImR+W-In diff --git a/benchmarks/guest/fibonacci_iterative/src/main.rs b/benchmarks/guest/fibonacci_iterative/src/main.rs index 09ceb5df41..6c820f4af2 100644 --- a/benchmarks/guest/fibonacci_iterative/src/main.rs +++ b/benchmarks/guest/fibonacci_iterative/src/main.rs @@ -1,7 +1,7 @@ use core::hint::black_box; use openvm as _; -const N: u64 = 100_000; +const N: u64 = 500_000; pub fn main() { let mut a: u64 = 0; diff --git a/benchmarks/guest/fibonacci_recursive/elf/openvm-fibonacci-recursive-program.elf b/benchmarks/guest/fibonacci_recursive/elf/openvm-fibonacci-recursive-program.elf index 7dee9d4286bd1935e369f7c1a69d49f972fedbad..aaacde40d21dfbbe379f8f5770efdda48288d1df 100755 GIT binary patch delta 820 zcmb7?J!@4#5QZaZ(s>FIw2*))1m&X)sxGw&@Afh9tq;~0HFhJfykv=;{F2)wGl&jB4 ztjjFIw2*))1m!S0v%9m~2o?bgiI!p^%zninIrkF3Vs$%fWh)!O zsKHXXVCi4*2Uy#su}JUUa0H8lL>QK3dG=-AdFSA8`QUK*%f`yN*Ymq8uikbXeZWA% zA+XUB>aX`VyF{T~1rsI5kW!_+jZO-d1_l=6A~>#Z_H@U(Aq2`KuN#l;@y5)TtuPrR z#x2E<0+doBV>`6f4%e5~b{Ey(kzq3Qq>~kl48AB=oOh5+)*?BJx0!tpOKWK?&GyBx zyCW{gDQ`c1l6EFfH>b~D+9n38N4g92%7{;WPKVH4y(Rynl5fo?8>P4&d;m_5X$uTQqv!kqn)9%Q_Ujxp1S8O08U}tnTS_MD| zR@n>zdivMxX|OWK(6bY@Q6Gb!NR#>>TJt!^{=pd4smu`-ya`QS&FznG*Z%+z$K@da diff --git a/benchmarks/guest/fibonacci_recursive/src/main.rs b/benchmarks/guest/fibonacci_recursive/src/main.rs index fae64a1b0f..15fcfc0109 100644 --- a/benchmarks/guest/fibonacci_recursive/src/main.rs +++ b/benchmarks/guest/fibonacci_recursive/src/main.rs @@ -1,7 +1,7 @@ use core::hint::black_box; use openvm as _; -const N: u64 = 25; +const N: u64 = 26; pub fn main() { let n = black_box(N); diff --git a/benchmarks/guest/quicksort/elf/openvm-quicksort-program.elf b/benchmarks/guest/quicksort/elf/openvm-quicksort-program.elf index 54af6272d6b5f621aba481da38a3da614af9e081..be7ca2922a0c793fec785ecd0837cde759a7cc0d 100755 GIT binary patch delta 1190 zcmb7DO-qzf6uu)fLTT~BlqFPHGldWg_v75JjVL4}g+HL({WJ(a!bP^4Atz|n=3*{t zVXK+Kpct}cZu$q&qLt7h2>Jn0QSVrikTMGQ-o@p4o^#G~&S^A@jb`!X*?~Gos9Y@F zN~LjBE)~&ghS2B6__8}*8caom-kd}z-GBG7GV%&lE<8n*Y5|pxWGLl#9&Pq@N-L1X>~deA-=BXt2&#hquG@S+8h-7_kag zE0T_GRZd{)h~UBmErKCjbq!5xTO)b-N>U)2au8qw+wC++M!Zvs3o1NjUIwsHTH_eE zdGlzE?JB<~nD9N&^?kMJZf|>vGqQ*HzS|{+#-GvbsZYdT#=jQ!;v}(tH4~a> zP|Vs$0fY?c;rc|K1uiK!`JTzBxiphCyfYV0NJV1Katf>=+5mOJ6Qi-`&KO&R10%QX i&bP07wu{|)JJT<|j%LycW;JCIe6TFK&b1G(FaHM7MN>Bb delta 1190 zcmb7D%}Z556rWe`2ByV@YnD(Uy;lgqLJvqa|8KS7 zy~LnlyfZWm(`@1PyfSl~-qJVajm3;jQxNlW;q!~rG0Qn-+IgcD(*__6z0|Syib;op zD-S7iDjeOc3}#GbQdmUrsbF%8Wmwr9X$WT>71E{Xs7ZOclRl{8v>6PxYSwkJ5!UBLIm=BjH&*Ntm`qS+Il%D)}|EbP@2eqZ-&k=?#KQ?I^^#vaj$3@K#76ys>;%7?cXe*@_AN_+qS diff --git a/benchmarks/guest/quicksort/src/main.rs b/benchmarks/guest/quicksort/src/main.rs index 30218cf40e..a6579306c7 100644 --- a/benchmarks/guest/quicksort/src/main.rs +++ b/benchmarks/guest/quicksort/src/main.rs @@ -1,7 +1,7 @@ use core::hint::black_box; use openvm as _; -const ARRAY_SIZE: usize = 1_000; +const ARRAY_SIZE: usize = 3_500; fn quicksort(arr: &mut [T]) { if arr.len() <= 1 { diff --git a/benchmarks/utils/src/build-elfs.rs b/benchmarks/utils/src/build-elfs.rs index 3bed7cf6fd..3ce24c7c5c 100644 --- a/benchmarks/utils/src/build-elfs.rs +++ b/benchmarks/utils/src/build-elfs.rs @@ -63,6 +63,12 @@ fn main() -> Result<()> { let programs_to_build = if cli.programs.is_empty() { available_programs } else { + for prog in &cli.programs { + if !available_programs.iter().any(|(name, _)| name == prog) { + tracing::warn!("Program '{}' not found in available programs", prog); + } + } + available_programs .into_iter() .filter(|(name, _)| cli.programs.contains(name)) @@ -70,6 +76,12 @@ fn main() -> Result<()> { }; // Filter out skipped programs + for prog in &cli.skip { + if !programs_to_build.iter().any(|(name, _)| name == prog) { + tracing::warn!("Program '{}' not found in programs to skip", prog); + } + } + let programs_to_build = programs_to_build .into_iter() .filter(|(name, _)| !cli.skip.contains(name)) From 00bab47716c38bd8907eaccab2e913500cad51e9 Mon Sep 17 00:00:00 2001 From: Golovanov399 Date: Fri, 16 May 2025 16:00:40 +0300 Subject: [PATCH 25/49] feat: rv32im working execution + tracegen + prove (aka integration tests) (#1659) This resolves INT-3913. As a _side effect_, this removes `GuestMemory` trait -- it is a struct now with underlying `AddressMap` (I didn't make `type GuestMemory = ...` because the vaguely called `read` and `write` methods would be too vaguely called for `AddressMap`). `VmStateMut` is generic over `MEM` though. I didn't fully implement `TraceStep` and `StepExecutorE1` for the phantom chip because the chip is relatively easy and I'm not sure it would be better expressible in terms of `NewVmChipWrapper`. `PhantomSubExecutor` also changed a little (now accepts `u32` instead of `F`, for example, and also `GuestMemory` instead of what it needed before). --- crates/circuits/mod-builder/src/core_chip.rs | 9 +- crates/vm/derive/src/lib.rs | 10 +- crates/vm/src/arch/execution.rs | 16 ++- crates/vm/src/arch/execution_control.rs | 16 +-- crates/vm/src/arch/integration_api.rs | 25 ++-- crates/vm/src/arch/segment.rs | 27 ++--- crates/vm/src/arch/vm.rs | 3 +- crates/vm/src/system/memory/controller/mod.rs | 4 +- crates/vm/src/system/memory/online.rs | 112 +++++++++++++++--- crates/vm/src/system/memory/paged_vec.rs | 68 +++++------ crates/vm/src/system/native_adapter/mod.rs | 15 ++- crates/vm/src/system/phantom/mod.rs | 93 ++++++--------- crates/vm/src/system/public_values/core.rs | 9 +- .../algebra/circuit/src/fp2_chip/addsub.rs | 5 +- .../algebra/circuit/src/fp2_chip/mod.rs | 2 +- .../algebra/circuit/src/fp2_chip/muldiv.rs | 5 +- .../circuit/src/modular_chip/addsub.rs | 2 +- .../algebra/circuit/src/modular_chip/is_eq.rs | 9 +- .../algebra/circuit/src/modular_chip/mod.rs | 4 +- .../circuit/src/modular_chip/muldiv.rs | 3 +- .../algebra/circuit/src/modular_chip/tests.rs | 15 +-- .../ecc/circuit/src/weierstrass_chip/mod.rs | 1 - .../ecc/circuit/src/weierstrass_chip/tests.rs | 3 +- .../src/adapters/alu_native_adapter.rs | 8 +- .../src/adapters/branch_native_adapter.rs | 8 +- .../circuit/src/adapters/convert_adapter.rs | 7 +- .../src/adapters/loadstore_native_adapter.rs | 7 +- .../src/adapters/native_vectorized_adapter.rs | 7 +- extensions/native/circuit/src/castf/core.rs | 7 +- .../circuit/src/field_arithmetic/core.rs | 7 +- .../circuit/src/field_extension/core.rs | 5 +- .../native/circuit/src/loadstore/core.rs | 5 +- extensions/rv32-adapters/src/eq_mod.rs | 15 ++- extensions/rv32-adapters/src/heap.rs | 15 ++- extensions/rv32-adapters/src/heap_branch.rs | 15 ++- extensions/rv32-adapters/src/vec_heap.rs | 15 ++- .../rv32-adapters/src/vec_heap_two_reads.rs | 15 ++- extensions/rv32im/circuit/src/adapters/alu.rs | 10 +- .../rv32im/circuit/src/adapters/branch.rs | 15 ++- .../rv32im/circuit/src/adapters/jalr.rs | 15 ++- .../rv32im/circuit/src/adapters/loadstore.rs | 15 ++- extensions/rv32im/circuit/src/adapters/mod.rs | 16 +-- extensions/rv32im/circuit/src/adapters/mul.rs | 10 +- .../rv32im/circuit/src/adapters/rdwrite.rs | 21 +--- extensions/rv32im/circuit/src/auipc/core.rs | 9 +- .../rv32im/circuit/src/base_alu/core.rs | 9 +- .../rv32im/circuit/src/branch_eq/core.rs | 9 +- .../rv32im/circuit/src/branch_lt/core.rs | 9 +- extensions/rv32im/circuit/src/divrem/core.rs | 9 +- extensions/rv32im/circuit/src/extension.rs | 30 ++--- .../rv32im/circuit/src/hintstore/mod.rs | 9 +- extensions/rv32im/circuit/src/jal_lui/core.rs | 9 +- extensions/rv32im/circuit/src/jalr/core.rs | 9 +- .../rv32im/circuit/src/less_than/core.rs | 9 +- .../circuit/src/load_sign_extend/core.rs | 9 +- .../rv32im/circuit/src/loadstore/core.rs | 9 +- extensions/rv32im/circuit/src/mul/core.rs | 9 +- extensions/rv32im/circuit/src/mulh/core.rs | 9 +- extensions/rv32im/circuit/src/shift/core.rs | 9 +- 59 files changed, 392 insertions(+), 458 deletions(-) diff --git a/crates/circuits/mod-builder/src/core_chip.rs b/crates/circuits/mod-builder/src/core_chip.rs index 4fd037fedb..d91991f52b 100644 --- a/crates/circuits/mod-builder/src/core_chip.rs +++ b/crates/circuits/mod-builder/src/core_chip.rs @@ -306,14 +306,11 @@ where A: 'static + for<'a> AdapterExecutorE1>, WriteData: From>>, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let data: DynArray<_> = self.adapter.read(state.memory, instruction).into(); let writes = run_field_expression(self, &data, instruction).0; diff --git a/crates/vm/derive/src/lib.rs b/crates/vm/derive/src/lib.rs index 3733ce74f6..be61d04d56 100644 --- a/crates/vm/derive/src/lib.rs +++ b/crates/vm/derive/src/lib.rs @@ -142,13 +142,12 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { .push(syn::parse_quote! { #inner_ty: ::openvm_circuit::arch::InsExecutorE1 }); quote! { impl #impl_generics ::openvm_circuit::arch::InsExecutorE1 for #name #ty_generics #where_clause { - fn execute_e1( + fn execute_e1( &mut self, - state: ::openvm_circuit::arch::VmStateMut, + state: ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, Ctx>, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction, ) -> ::openvm_circuit::arch::Result<()> where - Mem: ::openvm_circuit::system::memory::online::GuestMemory, F: ::openvm_stark_backend::p3_field::PrimeField32 { self.0.execute_e1(state, instruction) @@ -190,13 +189,12 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { quote! { impl #impl_generics ::openvm_circuit::arch::InsExecutorE1<#first_ty_generic> for #name #ty_generics { - fn execute_e1( + fn execute_e1( &mut self, - state: ::openvm_circuit::arch::VmStateMut, + state: ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, Ctx>, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction<#first_ty_generic>, ) -> ::openvm_circuit::arch::Result<()> where - Mem: ::openvm_circuit::system::memory::online::GuestMemory, #first_ty_generic: ::openvm_stark_backend::p3_field::PrimeField32 { match self { diff --git a/crates/vm/src/arch/execution.rs b/crates/vm/src/arch/execution.rs index a529c80e2f..d64ee84cf2 100644 --- a/crates/vm/src/arch/execution.rs +++ b/crates/vm/src/arch/execution.rs @@ -113,13 +113,12 @@ pub trait InstructionExecutor { /// New trait for instruction execution pub trait InsExecutorE1 { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, ) -> Result<()> where - Mem: GuestMemory, F: PrimeField32; } @@ -127,13 +126,12 @@ impl InsExecutorE1 for RefCell where C: InsExecutorE1, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, ) -> Result<()> where - Mem: GuestMemory, F: PrimeField32, { self.borrow_mut().execute_e1(state, instruction) @@ -367,11 +365,11 @@ impl From<(u32, Option)> for PcIncOrSet { pub trait PhantomSubExecutor: Send { fn phantom_execute( &mut self, - memory: &MemoryController, + memory: &GuestMemory, streams: &mut Streams, discriminant: PhantomDiscriminant, - a: F, - b: F, + a: u32, + b: u32, c_upper: u16, ) -> eyre::Result<()>; } diff --git a/crates/vm/src/arch/execution_control.rs b/crates/vm/src/arch/execution_control.rs index ecb4d0ffdd..adc8e93aa4 100644 --- a/crates/vm/src/arch/execution_control.rs +++ b/crates/vm/src/arch/execution_control.rs @@ -18,8 +18,6 @@ where F: PrimeField32, VC: VmConfig, { - /// Guest memory type - type Mem: GuestMemory; /// Host context type Ctx; @@ -55,9 +53,9 @@ where /// Execute a single instruction // TODO(ayush): change instruction to Instruction / PInstruction - fn execute_instruction( + fn execute_instruction( &mut self, - vm_state: &mut ExecutionSegmentState, + vm_state: &mut ExecutionSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> @@ -88,7 +86,6 @@ where VC: VmConfig, { type Ctx = TracegenCtx; - type Mem = AddressMap; fn new(chip_complex: &VmChipComplex) -> Self { Self { @@ -153,9 +150,9 @@ where } /// Execute a single instruction - fn execute_instruction( + fn execute_instruction( &mut self, - state: &mut ExecutionSegmentState, + state: &mut ExecutionSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> @@ -197,7 +194,6 @@ where VC::Executor: InsExecutorE1, { type Ctx = (); - type Mem = AddressMap; fn new(_chip_complex: &VmChipComplex) -> Self { Self { final_memory: None } @@ -235,9 +231,9 @@ where } /// Execute a single instruction - fn execute_instruction( + fn execute_instruction( &mut self, - state: &mut ExecutionSegmentState, + state: &mut ExecutionSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> diff --git a/crates/vm/src/arch/integration_api.rs b/crates/vm/src/arch/integration_api.rs index c1754abc8e..d53ed075fe 100644 --- a/crates/vm/src/arch/integration_api.rs +++ b/crates/vm/src/arch/integration_api.rs @@ -387,24 +387,18 @@ where type ReadData; type WriteData; - fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData - where - Mem: GuestMemory; + fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData; - fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) - where - Mem: GuestMemory; + fn write(&self, memory: &mut GuestMemory, instruction: &Instruction, data: &Self::WriteData); } // TODO: Rename core/step to operator pub trait StepExecutorE1 { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory; + ) -> Result<()>; } const DEFAULT_RECORDS_CAPACITY: usize = 1 << 20; @@ -414,14 +408,11 @@ where F: PrimeField32, S: StepExecutorE1, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { self.step.execute_e1(state, instruction) } } diff --git a/crates/vm/src/arch/segment.rs b/crates/vm/src/arch/segment.rs index 034236c833..3603db4253 100644 --- a/crates/vm/src/arch/segment.rs +++ b/crates/vm/src/arch/segment.rs @@ -29,12 +29,11 @@ use crate::{ }, }; -pub struct VmSegmentExecutor +pub struct VmSegmentExecutor where F: PrimeField32, VC: VmConfig, - Mem: GuestMemory, - Ctrl: ExecutionControl, + Ctrl: ExecutionControl, { pub chip_complex: VmChipComplex, /// Execution control for determining segmentation and stopping conditions @@ -49,30 +48,28 @@ where pub metrics: VmMetrics, } -pub type E1VmSegmentExecutor = - VmSegmentExecutor, (), E1ExecutionControl>; +pub type E1VmSegmentExecutor = VmSegmentExecutor; // pub struct TracegenCtx; pub type TracegenCtx = (); -pub type TracegenVmStateMut<'a> = VmStateMut<'a, AddressMap, TracegenCtx>; +pub type TracegenVmStateMut<'a> = VmStateMut<'a, GuestMemory, TracegenCtx>; pub type TracegenVmSegmentExecutor = - VmSegmentExecutor, TracegenCtx, TracegenExecutionControl>; + VmSegmentExecutor; #[derive(derive_new::new)] -pub struct ExecutionSegmentState { - pub memory: Option, +pub struct ExecutionSegmentState { + pub memory: Option, pub pc: u32, pub exit_code: u32, pub is_terminated: bool, } -impl VmSegmentExecutor +impl VmSegmentExecutor where F: PrimeField32, VC: VmConfig, - Mem: GuestMemory, - Ctrl: ExecutionControl, + Ctrl: ExecutionControl, { /// Creates a new execution segment from a program and initial state, using parent VM config pub fn new( @@ -126,8 +123,8 @@ where pub fn execute_from_pc( &mut self, pc: u32, - memory: Option, - ) -> Result, ExecutionError> { + memory: Option, + ) -> Result { let mut prev_backtrace: Option = None; // Call the pre-execution hook @@ -160,7 +157,7 @@ where // TODO(ayush): clean this up, separate to smaller functions fn execute_instruction( &mut self, - state: &mut ExecutionSegmentState, + state: &mut ExecutionSegmentState, prev_backtrace: &mut Option, ) -> Result, ExecutionError> { let pc = state.pc; diff --git a/crates/vm/src/arch/vm.rs b/crates/vm/src/arch/vm.rs index f94cdc9900..b861516622 100644 --- a/crates/vm/src/arch/vm.rs +++ b/crates/vm/src/arch/vm.rs @@ -33,6 +33,7 @@ use crate::{ connector::{VmConnectorPvs, DEFAULT_SUSPEND_EXIT_CODE}, memory::{ merkle::MemoryMerklePvs, + online::GuestMemory, paged_vec::AddressMap, tree::public_values::{UserPublicValuesProof, UserPublicValuesProofError}, MemoryImage, CHUNK, @@ -351,7 +352,7 @@ where } let exec_state = metrics_span("execute_time_ms", || { - segment.execute_from_pc(state.pc, Some(state.memory)) + segment.execute_from_pc(state.pc, Some(GuestMemory::new(state.memory))) })?; if exec_state.is_terminated { diff --git a/crates/vm/src/system/memory/controller/mod.rs b/crates/vm/src/system/memory/controller/mod.rs index aee7f23bf0..28e0254c33 100644 --- a/crates/vm/src/system/memory/controller/mod.rs +++ b/crates/vm/src/system/memory/controller/mod.rs @@ -269,7 +269,7 @@ impl MemoryController { } pub fn memory_image(&self) -> &MemoryImage { - &self.memory.data + &self.memory.data.memory } pub fn set_override_trace_heights(&mut self, overridden_heights: MemoryTraceHeights) { @@ -496,7 +496,7 @@ impl MemoryController { debug_assert!(ptr % min_block_size as u32 == 0); let values = (0..block_size) - .map(|i| self.memory.data.get_f::(addr_space, ptr + i)) + .map(|i| self.memory.data.memory.get_f::(addr_space, ptr + i)) .collect::>(); self.memory.execute_splits::( MemoryAddress::new(addr_space, ptr), diff --git a/crates/vm/src/system/memory/online.rs b/crates/vm/src/system/memory/online.rs index a01e5e4f2c..f176f0d287 100644 --- a/crates/vm/src/system/memory/online.rs +++ b/crates/vm/src/system/memory/online.rs @@ -16,8 +16,12 @@ use crate::{arch::MemoryConfig, system::memory::MemoryImage}; pub const INITIAL_TIMESTAMP: u32 = 0; -/// API for guest memory conforming to OpenVM ISA -pub trait GuestMemory { +#[derive(Debug, Clone, derive_new::new)] +pub struct GuestMemory { + pub memory: AddressMap, +} + +impl GuestMemory { /// Returns `[pointer:BLOCK_SIZE]_{address_space}` /// /// # Safety @@ -25,25 +29,48 @@ pub trait GuestMemory { /// and it must be the exact type used to represent a single memory cell in /// address space `address_space`. For standard usage, /// `T` is either `u8` or `F` where `F` is the base field of the ZK backend. - unsafe fn read( + pub unsafe fn read( &self, - address_space: u32, - pointer: u32, + addr_space: u32, + ptr: u32, ) -> [T; BLOCK_SIZE] where - T: Copy + Debug; + T: Copy + Debug, + { + debug_assert_eq!( + size_of::(), + self.memory.cell_size[(addr_space - self.memory.as_offset) as usize] + ); + let read = self + .memory + .paged_vecs + .get_unchecked((addr_space - self.memory.as_offset) as usize) + .get((ptr as usize) * size_of::()); + read + } /// Writes `values` to `[pointer:BLOCK_SIZE]_{address_space}` /// /// # Safety /// See [`GuestMemory::read`]. - unsafe fn write( + pub unsafe fn write( &mut self, - address_space: u32, - pointer: u32, + addr_space: u32, + ptr: u32, values: &[T; BLOCK_SIZE], ) where - T: Copy + Debug; + T: Copy + Debug, + { + debug_assert_eq!( + size_of::(), + self.memory.cell_size[(addr_space - self.memory.as_offset) as usize], + "addr_space={addr_space}" + ); + self.memory + .paged_vecs + .get_unchecked_mut((addr_space - self.memory.as_offset) as usize) + .set((ptr as usize) * size_of::(), values); + } /// Writes `values` to `[pointer:BLOCK_SIZE]_{address_space}` and returns /// the previous values. @@ -51,7 +78,7 @@ pub trait GuestMemory { /// # Safety /// See [`GuestMemory::read`]. #[inline(always)] - unsafe fn replace( + pub unsafe fn replace( &mut self, address_space: u32, pointer: u32, @@ -66,6 +93,56 @@ pub trait GuestMemory { } } +// /// API for guest memory conforming to OpenVM ISA +// pub trait GuestMemory { +// /// Returns `[pointer:BLOCK_SIZE]_{address_space}` +// /// +// /// # Safety +// /// The type `T` must be stack-allocated `repr(C)` or `repr(transparent)`, +// /// and it must be the exact type used to represent a single memory cell in +// /// address space `address_space`. For standard usage, +// /// `T` is either `u8` or `F` where `F` is the base field of the ZK backend. +// unsafe fn read( +// &self, +// address_space: u32, +// pointer: u32, +// ) -> [T; BLOCK_SIZE] +// where +// T: Copy + Debug; + +// /// Writes `values` to `[pointer:BLOCK_SIZE]_{address_space}` +// /// +// /// # Safety +// /// See [`GuestMemory::read`]. +// unsafe fn write( +// &mut self, +// address_space: u32, +// pointer: u32, +// values: &[T; BLOCK_SIZE], +// ) where +// T: Copy + Debug; + +// /// Writes `values` to `[pointer:BLOCK_SIZE]_{address_space}` and returns +// /// the previous values. +// /// +// /// # Safety +// /// See [`GuestMemory::read`]. +// #[inline(always)] +// unsafe fn replace( +// &mut self, +// address_space: u32, +// pointer: u32, +// values: &[T; BLOCK_SIZE], +// ) -> [T; BLOCK_SIZE] +// where +// T: Copy + Debug, +// { +// let prev = self.read(address_space, pointer); +// self.write(address_space, pointer, values); +// prev +// } +// } + // TO BE DELETED #[derive(Debug, Clone, Serialize, Deserialize)] pub enum MemoryLogEntry { @@ -107,7 +184,7 @@ pub struct TracingMemory { /// The underlying data memory, with memory cells typed by address space: see [AddressMap]. // TODO: make generic in GuestMemory #[getset(get = "pub")] - pub data: AddressMap, + pub data: GuestMemory, /// A map of `addr_space -> (ptr / min_block_size[addr_space] -> (timestamp: u32, block_size: /// u32))` for the timestamp and block size of the latest access. pub(super) meta: Vec>, @@ -146,7 +223,7 @@ impl TracingMemory { }) .collect(); Self { - data: AddressMap::from_mem_config(mem_config), + data: GuestMemory::new(AddressMap::from_mem_config(mem_config)), meta, min_block_size, timestamp: INITIAL_TIMESTAMP + 1, @@ -173,7 +250,7 @@ impl TracingMemory { .div_ceil(PAGE_SIZE * self.min_block_size[i] as usize), ); } - self.data = image; + self.data = GuestMemory::new(image); self } @@ -375,6 +452,7 @@ impl TracingMemory { let values = (0..current_metadata.block_size as usize) .map(|i| { self.data + .memory .get_f(address.address_space, address.pointer + (i as u32)) }) .collect::>(); @@ -385,7 +463,11 @@ impl TracingMemory { if need_to_merge { // Merge let values = (0..BLOCK_SIZE) - .map(|i| self.data.get_f(address_space as u32, (pointer + i) as u32)) + .map(|i| { + self.data + .memory + .get_f(address_space as u32, (pointer + i) as u32) + }) .collect::>(); self.execute_merges::( MemoryAddress::new(address_space as u32, pointer as u32), diff --git a/crates/vm/src/system/memory/paged_vec.rs b/crates/vm/src/system/memory/paged_vec.rs index 35e4e7de03..54c2f44023 100644 --- a/crates/vm/src/system/memory/paged_vec.rs +++ b/crates/vm/src/system/memory/paged_vec.rs @@ -358,40 +358,40 @@ impl AddressMap { } } -impl GuestMemory for AddressMap { - unsafe fn read(&self, addr_space: u32, ptr: u32) -> [T; BLOCK_SIZE] - where - T: Copy + Debug, - { - debug_assert_eq!( - size_of::(), - self.cell_size[(addr_space - self.as_offset) as usize] - ); - let read = self - .paged_vecs - .get_unchecked((addr_space - self.as_offset) as usize) - .get((ptr as usize) * size_of::()); - read - } - - unsafe fn write( - &mut self, - addr_space: u32, - ptr: u32, - values: &[T; BLOCK_SIZE], - ) where - T: Copy + Debug, - { - debug_assert_eq!( - size_of::(), - self.cell_size[(addr_space - self.as_offset) as usize], - "addr_space={addr_space}" - ); - self.paged_vecs - .get_unchecked_mut((addr_space - self.as_offset) as usize) - .set((ptr as usize) * size_of::(), values); - } -} +// impl GuestMemory for AddressMap { +// unsafe fn read(&self, addr_space: u32, ptr: u32) -> [T; +// BLOCK_SIZE] where +// T: Copy + Debug, +// { +// debug_assert_eq!( +// size_of::(), +// self.cell_size[(addr_space - self.as_offset) as usize] +// ); +// let read = self +// .paged_vecs +// .get_unchecked((addr_space - self.as_offset) as usize) +// .get((ptr as usize) * size_of::()); +// read +// } + +// unsafe fn write( +// &mut self, +// addr_space: u32, +// ptr: u32, +// values: &[T; BLOCK_SIZE], +// ) where +// T: Copy + Debug, +// { +// debug_assert_eq!( +// size_of::(), +// self.cell_size[(addr_space - self.as_offset) as usize], +// "addr_space={addr_space}" +// ); +// self.paged_vecs +// .get_unchecked_mut((addr_space - self.as_offset) as usize) +// .set((ptr as usize) * size_of::(), values); +// } +// } #[cfg(test)] mod tests { diff --git a/crates/vm/src/system/native_adapter/mod.rs b/crates/vm/src/system/native_adapter/mod.rs index cc3e31ed2f..9fdde040ce 100644 --- a/crates/vm/src/system/native_adapter/mod.rs +++ b/crates/vm/src/system/native_adapter/mod.rs @@ -247,10 +247,7 @@ where type WriteData = [F; W]; #[inline(always)] - fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData - where - Mem: GuestMemory, - { + fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { assert!(R <= 2); let &Instruction { b, c, e, f, .. } = instruction; @@ -270,10 +267,12 @@ where } #[inline(always)] - fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) - where - Mem: GuestMemory, - { + fn write( + &self, + memory: &mut GuestMemory, + instruction: &Instruction, + data: &Self::WriteData, + ) { assert!(W <= 1); let &Instruction { a, d, .. } = instruction; diff --git a/crates/vm/src/system/phantom/mod.rs b/crates/vm/src/system/phantom/mod.rs index 792739c501..e5f0e91fc0 100644 --- a/crates/vm/src/system/phantom/mod.rs +++ b/crates/vm/src/system/phantom/mod.rs @@ -128,13 +128,12 @@ impl InsExecutorE1 for PhantomChip where F: PrimeField32, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, ) -> Result<(), ExecutionError> where - Mem: GuestMemory, F: PrimeField32, { let &Instruction { @@ -156,21 +155,21 @@ where })?; let mut streams = self.streams.get().unwrap().lock().unwrap(); // TODO(ayush): implement phantom subexecutor for new traits - // sub_executor - // .as_mut() - // .phantom_execute( - // state.memory, - // &mut streams, - // discriminant, - // a, - // b, - // (c_u32 >> 16) as u16, - // ) - // .map_err(|e| ExecutionError::Phantom { - // pc: *state.pc, - // discriminant, - // inner: e, - // })?; + sub_executor + .as_mut() + .phantom_execute( + state.memory, + &mut streams, + discriminant, + a.as_canonical_u32(), + b.as_canonical_u32(), + (c_u32 >> 16) as u16, + ) + .map_err(|e| ExecutionError::Phantom { + pc: *state.pc, + discriminant, + inner: e, + })?; } *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); @@ -186,52 +185,26 @@ impl InstructionExecutor for PhantomChip { instruction: &Instruction, from_state: ExecutionState, ) -> Result, ExecutionError> { - let &Instruction { - opcode, a, b, c, .. - } = instruction; - assert_eq!(opcode, self.air.phantom_opcode); - - let c_u32 = c.as_canonical_u32(); - let discriminant = PhantomDiscriminant(c_u32 as u16); - // If not a system phantom sub-instruction (which is handled in - // ExecutionSegment), look for a phantom sub-executor to handle it. - if SysPhantom::from_repr(discriminant.0).is_none() { - let sub_executor = self - .phantom_executors - .get_mut(&discriminant) - .ok_or_else(|| ExecutionError::PhantomNotFound { - pc: from_state.pc, - discriminant, - })?; - let mut streams = self.streams.get().unwrap().lock().unwrap(); - sub_executor - .as_mut() - .phantom_execute( - memory, - &mut streams, - discriminant, - a, - b, - (c_u32 >> 16) as u16, - ) - .map_err(|e| ExecutionError::Phantom { - pc: from_state.pc, - discriminant, - inner: e, - })?; - } - + let mut pc = from_state.pc; self.rows.push(PhantomCols { - pc: F::from_canonical_u32(from_state.pc), - operands: [a, b, c], - timestamp: F::from_canonical_u32(from_state.timestamp), + pc: F::from_canonical_u32(pc), + operands: [instruction.a, instruction.b, instruction.c], + timestamp: F::from_canonical_u32(memory.memory.timestamp), is_valid: F::ONE, }); + + let state = VmStateMut { + pc: &mut pc, + memory: &mut memory.memory.data, + ctx: &mut (), + }; + self.execute_e1(state, instruction)?; memory.increment_timestamp(); - Ok(ExecutionState::new( - from_state.pc + DEFAULT_PC_STEP, - from_state.timestamp + 1, - )) + + Ok(ExecutionState { + pc, + timestamp: memory.memory.timestamp, + }) } fn get_opcode_name(&self, _: usize) -> String { diff --git a/crates/vm/src/system/public_values/core.rs b/crates/vm/src/system/public_values/core.rs index 0641b41f0d..a573ae8e1e 100644 --- a/crates/vm/src/system/public_values/core.rs +++ b/crates/vm/src/system/public_values/core.rs @@ -209,14 +209,11 @@ where F: PrimeField32, A: 'static + for<'a> AdapterExecutorE1, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let [value, index] = self.adapter.read(state.memory, instruction); let idx: usize = index.as_canonical_u32() as usize; diff --git a/extensions/algebra/circuit/src/fp2_chip/addsub.rs b/extensions/algebra/circuit/src/fp2_chip/addsub.rs index 5d337a902e..5165395b28 100644 --- a/extensions/algebra/circuit/src/fp2_chip/addsub.rs +++ b/extensions/algebra/circuit/src/fp2_chip/addsub.rs @@ -5,14 +5,12 @@ use openvm_circuit::{ arch::ExecutionBridge, system::memory::{offline_checker::MemoryBridge, SharedMemoryHelper}, }; - use openvm_circuit_derive::{InsExecutorE1, InstructionExecutor}; use openvm_circuit_primitives::{ bitwise_op_lookup::SharedBitwiseOperationLookupChip, var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus}, Chip, ChipUsageGetter, }; - use openvm_instructions::riscv::RV32_CELL_BITS; use openvm_mod_circuit_builder::{ ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreAir, @@ -20,9 +18,8 @@ use openvm_mod_circuit_builder::{ use openvm_rv32_adapters::{Rv32VecHeapAdapterAir, Rv32VecHeapAdapterStep}; use openvm_stark_backend::p3_field::PrimeField32; -use crate::Fp2; - use super::{Fp2Air, Fp2Chip, Fp2Step}; +use crate::Fp2; pub fn fp2_addsub_expr( config: ExprBuilderConfig, diff --git a/extensions/algebra/circuit/src/fp2_chip/mod.rs b/extensions/algebra/circuit/src/fp2_chip/mod.rs index a5bba6d9c0..75d15cbdbe 100644 --- a/extensions/algebra/circuit/src/fp2_chip/mod.rs +++ b/extensions/algebra/circuit/src/fp2_chip/mod.rs @@ -5,7 +5,7 @@ mod muldiv; pub use muldiv::*; use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; use openvm_mod_circuit_builder::{FieldExpressionCoreAir, FieldExpressionStep}; -use openvm_rv32_adapters::{Rv32VecHeapAdapterStep, Rv32VecHeapAdapterAir}; +use openvm_rv32_adapters::{Rv32VecHeapAdapterAir, Rv32VecHeapAdapterStep}; pub(crate) type Fp2Air = VmAirWrapper< Rv32VecHeapAdapterAir<2, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE>, diff --git a/extensions/algebra/circuit/src/fp2_chip/muldiv.rs b/extensions/algebra/circuit/src/fp2_chip/muldiv.rs index 0683649ec6..ea4889e74e 100644 --- a/extensions/algebra/circuit/src/fp2_chip/muldiv.rs +++ b/extensions/algebra/circuit/src/fp2_chip/muldiv.rs @@ -5,14 +5,12 @@ use openvm_circuit::{ arch::ExecutionBridge, system::memory::{offline_checker::MemoryBridge, SharedMemoryHelper}, }; - use openvm_circuit_derive::{InsExecutorE1, InstructionExecutor}; use openvm_circuit_primitives::{ bitwise_op_lookup::SharedBitwiseOperationLookupChip, var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus}, Chip, ChipUsageGetter, }; - use openvm_instructions::riscv::RV32_CELL_BITS; use openvm_mod_circuit_builder::{ ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreAir, SymbolicExpr, @@ -20,9 +18,8 @@ use openvm_mod_circuit_builder::{ use openvm_rv32_adapters::{Rv32VecHeapAdapterAir, Rv32VecHeapAdapterStep}; use openvm_stark_backend::p3_field::PrimeField32; -use crate::Fp2; - use super::{Fp2Air, Fp2Chip, Fp2Step}; +use crate::Fp2; pub fn fp2_muldiv_expr( config: ExprBuilderConfig, diff --git a/extensions/algebra/circuit/src/modular_chip/addsub.rs b/extensions/algebra/circuit/src/modular_chip/addsub.rs index fd02bf1b1f..1c3e497b40 100644 --- a/extensions/algebra/circuit/src/modular_chip/addsub.rs +++ b/extensions/algebra/circuit/src/modular_chip/addsub.rs @@ -15,7 +15,7 @@ use openvm_instructions::riscv::RV32_CELL_BITS; use openvm_mod_circuit_builder::{ ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreAir, FieldVariable, }; -use openvm_rv32_adapters::{Rv32VecHeapAdapterStep, Rv32VecHeapAdapterAir}; +use openvm_rv32_adapters::{Rv32VecHeapAdapterAir, Rv32VecHeapAdapterStep}; use openvm_stark_backend::p3_field::PrimeField32; use super::{ModularAir, ModularChip, ModularStep}; diff --git a/extensions/algebra/circuit/src/modular_chip/is_eq.rs b/extensions/algebra/circuit/src/modular_chip/is_eq.rs index 5b1c03430b..621ad69075 100644 --- a/extensions/algebra/circuit/src/modular_chip/is_eq.rs +++ b/extensions/algebra/circuit/src/modular_chip/is_eq.rs @@ -422,14 +422,11 @@ where WriteData: From<[u8; WRITE_LIMBS]>, >, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let Instruction { opcode, .. } = instruction; let local_opcode = diff --git a/extensions/algebra/circuit/src/modular_chip/mod.rs b/extensions/algebra/circuit/src/modular_chip/mod.rs index dc9fbe6342..1ba8876f5e 100644 --- a/extensions/algebra/circuit/src/modular_chip/mod.rs +++ b/extensions/algebra/circuit/src/modular_chip/mod.rs @@ -8,8 +8,8 @@ use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; use openvm_instructions::riscv::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; use openvm_mod_circuit_builder::{FieldExpressionCoreAir, FieldExpressionStep}; use openvm_rv32_adapters::{ - Rv32VecHeapAdapterStep, Rv32IsEqualModAdapterAir, Rv32IsEqualModeAdapterStep, - Rv32VecHeapAdapterAir, + Rv32IsEqualModAdapterAir, Rv32IsEqualModeAdapterStep, Rv32VecHeapAdapterAir, + Rv32VecHeapAdapterStep, }; #[cfg(test)] diff --git a/extensions/algebra/circuit/src/modular_chip/muldiv.rs b/extensions/algebra/circuit/src/modular_chip/muldiv.rs index 1685b114a1..f3defa4507 100644 --- a/extensions/algebra/circuit/src/modular_chip/muldiv.rs +++ b/extensions/algebra/circuit/src/modular_chip/muldiv.rs @@ -5,7 +5,6 @@ use openvm_circuit::{ arch::ExecutionBridge, system::memory::{offline_checker::MemoryBridge, SharedMemoryHelper}, }; - use openvm_circuit_derive::{InsExecutorE1, InstructionExecutor}; use openvm_circuit_primitives::{ bitwise_op_lookup::SharedBitwiseOperationLookupChip, @@ -16,7 +15,7 @@ use openvm_instructions::riscv::RV32_CELL_BITS; use openvm_mod_circuit_builder::{ ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreAir, FieldVariable, SymbolicExpr, }; -use openvm_rv32_adapters::{Rv32VecHeapAdapterStep, Rv32VecHeapAdapterAir}; +use openvm_rv32_adapters::{Rv32VecHeapAdapterAir, Rv32VecHeapAdapterStep}; use openvm_stark_backend::p3_field::PrimeField32; use super::{ModularAir, ModularChip, ModularStep}; diff --git a/extensions/algebra/circuit/src/modular_chip/tests.rs b/extensions/algebra/circuit/src/modular_chip/tests.rs index 720ffe76ca..9a5de34345 100644 --- a/extensions/algebra/circuit/src/modular_chip/tests.rs +++ b/extensions/algebra/circuit/src/modular_chip/tests.rs @@ -21,13 +21,12 @@ use openvm_rv32_adapters::{rv32_write_heap_default, write_ptr_reg}; use openvm_rv32im_circuit::adapters::RV32_REGISTER_NUM_LIMBS; use openvm_stark_backend::p3_field::FieldAlgebra; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; -use rand::Rng; +use rand::{rngs::StdRng, Rng}; use super::{ ModularAddSubChip, ModularIsEqualChip, ModularIsEqualCoreAir, ModularIsEqualCoreCols, ModularMulDivChip, }; -use rand::rngs::StdRng; const NUM_LIMBS: usize = 32; const LIMB_BITS: usize = 8; @@ -37,9 +36,10 @@ type F = BabyBear; #[cfg(test)] mod addsubtests { - use super::*; use test_case::test_case; + use super::*; + const ADD_LOCAL: usize = Rv32ModularArithmeticOpcode::ADD as usize; fn set_and_execute_addsub( @@ -152,9 +152,10 @@ mod addsubtests { #[cfg(test)] mod muldivtests { - use super::*; use test_case::test_case; + use super::*; + const MUL_LOCAL: usize = Rv32ModularArithmeticOpcode::MUL as usize; fn set_and_execute_muldiv( @@ -266,9 +267,6 @@ mod muldivtests { #[cfg(test)] mod is_equal_tests { - use crate::modular_chip::{ModularIsEqualAir, ModularIsEqualStep}; - - use super::*; use openvm_rv32_adapters::{Rv32IsEqualModAdapterAir, Rv32IsEqualModeAdapterStep}; use openvm_stark_backend::{ p3_air::BaseAir, @@ -280,6 +278,9 @@ mod is_equal_tests { verifier::VerificationError, }; + use super::*; + use crate::modular_chip::{ModularIsEqualAir, ModularIsEqualStep}; + fn create_test_chips< const NUM_LANES: usize, const LANE_SIZE: usize, diff --git a/extensions/ecc/circuit/src/weierstrass_chip/mod.rs b/extensions/ecc/circuit/src/weierstrass_chip/mod.rs index e27534e167..2a837b6aac 100644 --- a/extensions/ecc/circuit/src/weierstrass_chip/mod.rs +++ b/extensions/ecc/circuit/src/weierstrass_chip/mod.rs @@ -8,7 +8,6 @@ pub use double::*; mod tests; use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; - use openvm_mod_circuit_builder::{FieldExpressionCoreAir, FieldExpressionStep}; use openvm_rv32_adapters::{Rv32VecHeapAdapterAir, Rv32VecHeapAdapterStep}; diff --git a/extensions/ecc/circuit/src/weierstrass_chip/tests.rs b/extensions/ecc/circuit/src/weierstrass_chip/tests.rs index ae7ed125b6..2809dc23c1 100644 --- a/extensions/ecc/circuit/src/weierstrass_chip/tests.rs +++ b/extensions/ecc/circuit/src/weierstrass_chip/tests.rs @@ -291,7 +291,8 @@ fn test_p256_double() { ); tester.execute(&mut chip, &instruction); - // Adding another row to make sure there are dummy rows, and that the dummy row constraints are satisfied + // Adding another row to make sure there are dummy rows, and that the dummy row constraints are + // satisfied tester.execute(&mut chip, &instruction); let tester = tester.build().load(chip).load(bitwise_chip).finalize(); diff --git a/extensions/native/circuit/src/adapters/alu_native_adapter.rs b/extensions/native/circuit/src/adapters/alu_native_adapter.rs index c476a48c91..9fd2d1632e 100644 --- a/extensions/native/circuit/src/adapters/alu_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/alu_native_adapter.rs @@ -12,6 +12,7 @@ use openvm_circuit::{ system::{ memory::{ offline_checker::{MemoryBridge, MemoryReadOrImmediateAuxCols, MemoryWriteAuxCols}, + online::GuestMemory, MemoryAddress, MemoryController, OfflineMemory, }, native_adapter::{NativeReadRecord, NativeWriteRecord}, @@ -199,15 +200,14 @@ where } } -impl AdapterExecutorE1 for AluNativeAdapterStep +impl AdapterExecutorE1 for AluNativeAdapterStep where - Mem: GuestMemory, F: PrimeField32, { type ReadData = (F, F); type WriteData = F; - fn read(memory: &mut Mem, instruction: &Instruction) -> Self::ReadData { + fn read(memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { let Instruction { b, c, e, f, .. } = instruction; let [read1]: [F; 1] = unsafe { memory.read(e.as_canonical_u32(), b.as_canonical_u32()) }; @@ -216,7 +216,7 @@ where (read1, read2) } - fn write(memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) { + fn write(memory: &mut GuestMemory, instruction: &Instruction, data: &Self::WriteData) { let Instruction { a, .. } = instruction; unsafe { memory.write(AS::Native, a.as_canonical_u32(), &[data]) }; diff --git a/extensions/native/circuit/src/adapters/branch_native_adapter.rs b/extensions/native/circuit/src/adapters/branch_native_adapter.rs index ad36890336..b84867b125 100644 --- a/extensions/native/circuit/src/adapters/branch_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/branch_native_adapter.rs @@ -12,6 +12,7 @@ use openvm_circuit::{ system::{ memory::{ offline_checker::{MemoryBridge, MemoryReadOrImmediateAuxCols}, + online::GuestMemory, MemoryAddress, MemoryController, OfflineMemory, }, native_adapter::NativeReadRecord, @@ -195,15 +196,14 @@ where } } -impl AdapterExecutorE1 for BranchNativeAdapterStep +impl AdapterExecutorE1 for BranchNativeAdapterStep where - Mem: GuestMemory, F: PrimeField32, { type ReadData = (F, F); type WriteData = (); - fn read(memory: &mut Mem, instruction: &Instruction) -> Self::ReadData { + fn read(memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { let Instruction { a, b, d, e, .. } = instruction; let read1 = unsafe { memory.read(d.as_canonical_u32(), a.as_canonical_u32()) }; @@ -212,7 +212,7 @@ where (read1, read2) } - fn write(_memory: &mut Mem, _instruction: &Instruction, _data: &Self::WriteData) {} + fn write(_memory: &mut GuestMemory, _instruction: &Instruction, _data: &Self::WriteData) {} } // impl VmAdapterChip for BranchNativeAdapterChip { diff --git a/extensions/native/circuit/src/adapters/convert_adapter.rs b/extensions/native/circuit/src/adapters/convert_adapter.rs index f0df852d01..a09358fb5a 100644 --- a/extensions/native/circuit/src/adapters/convert_adapter.rs +++ b/extensions/native/circuit/src/adapters/convert_adapter.rs @@ -204,23 +204,22 @@ where } } -impl AdapterExecutorE1 +impl AdapterExecutorE1 for ConvertAdapterStep where - Mem: GuestMemory, F: PrimeField32, { type ReadData = [F; READ_SIZE]; type WriteData = [F; WRITE_SIZE]; - fn read(memory: &mut Mem, instruction: &Instruction) -> Self::ReadData { + fn read(memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { let Instruction { b, e, .. } = instruction; let read = unsafe { memory.read(e.as_canonical_u32(), b.as_canonical_u32()) }; read } - fn write(memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) { + fn write(memory: &mut GuestMemory, instruction: &Instruction, data: &Self::WriteData) { let Instruction { a, d, .. } = instruction; unsafe { diff --git a/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs b/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs index d8e0d7b4c9..5768f2322d 100644 --- a/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs @@ -243,16 +243,15 @@ where } } -impl AdapterExecutorE1 +impl AdapterExecutorE1 for NativeLoadStoreAdapterStep where - Mem: GuestMemory, F: PrimeField32, { type ReadData = (F, [F; NUM_CELLS]); type WriteData = [F; NUM_CELLS]; - fn read(memory: &mut Mem, instruction: &Instruction) -> Self::ReadData { + fn read(memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { let Instruction { opcode, a, @@ -294,7 +293,7 @@ where (read_cell, data_read) } - fn write(memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) { + fn write(memory: &mut GuestMemory, instruction: &Instruction, data: &Self::WriteData) { let Instruction { opcode, a, diff --git a/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs b/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs index fffb338958..21199bed81 100644 --- a/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs +++ b/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs @@ -214,15 +214,14 @@ where } } -impl AdapterExecutorE1 for NativeVectorizedAdapterChip +impl AdapterExecutorE1 for NativeVectorizedAdapterChip where - Mem: GuestMemory, F: PrimeField32, { type ReadData = ([F; N], [F; N]); type WriteData = [F; N]; - fn read(memory: &mut Mem, instruction: &Instruction) -> Self::ReadData { + fn read(memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { let Instruction { b, c, d, e, .. } = instruction; let y_val: [F; N] = unsafe { memory.read(d.as_canonical_u32(), b.as_cas_canonical_u32()) }; @@ -232,7 +231,7 @@ where (y_val, z_val) } - fn write(memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) { + fn write(memory: &mut GuestMemory, instruction: &Instruction, data: &Self::WriteData) { let Instruction { a, d, .. } = instruction; unsafe { memory.write(d.as_canonical_u32(), a.as_cas_canonical_u32(), &data) }; diff --git a/extensions/native/circuit/src/castf/core.rs b/extensions/native/circuit/src/castf/core.rs index b79f517103..099b475869 100644 --- a/extensions/native/circuit/src/castf/core.rs +++ b/extensions/native/circuit/src/castf/core.rs @@ -181,15 +181,14 @@ where } } -impl StepExecutorE1 for CastFStep +impl StepExecutorE1 for CastFStep where - Mem: GuestMemory, F: PrimeField32, - A: 'static + for<'a> AdapterExecutorE1, + A: 'static + for<'a> AdapterExecutorE1, { fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: &mut VmExecutionState, instruction: &Instruction, ) -> Result<()> { let Instruction { diff --git a/extensions/native/circuit/src/field_arithmetic/core.rs b/extensions/native/circuit/src/field_arithmetic/core.rs index d9ab7d0c7f..cd9e6ba1cd 100644 --- a/extensions/native/circuit/src/field_arithmetic/core.rs +++ b/extensions/native/circuit/src/field_arithmetic/core.rs @@ -195,15 +195,14 @@ where } } -impl StepExecutorE1 for FieldArithmeticStep +impl StepExecutorE1 for FieldArithmeticStep where - Mem: GuestMemory, F: PrimeField32, - A: 'static + for<'a> AdapterExecutorE1, + A: 'static + for<'a> AdapterExecutorE1, { fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: &mut VmExecutionState, instruction: &Instruction, ) -> Result<()> { let Instruction { diff --git a/extensions/native/circuit/src/field_extension/core.rs b/extensions/native/circuit/src/field_extension/core.rs index b96b3a6bab..50fd04acbf 100644 --- a/extensions/native/circuit/src/field_extension/core.rs +++ b/extensions/native/circuit/src/field_extension/core.rs @@ -216,9 +216,8 @@ where } } -impl StepExecutorE1 for FieldExtensionStep +impl StepExecutorE1 for FieldExtensionStep where - Mem: GuestMemory, F: PrimeField32, A: 'static + for<'a> AdapterExecutorE1< @@ -230,7 +229,7 @@ where { fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: &mut VmExecutionState, instruction: &Instruction, ) -> Result<()> { let Instruction { diff --git a/extensions/native/circuit/src/loadstore/core.rs b/extensions/native/circuit/src/loadstore/core.rs index 3a9aa498f0..40948f5c60 100644 --- a/extensions/native/circuit/src/loadstore/core.rs +++ b/extensions/native/circuit/src/loadstore/core.rs @@ -206,10 +206,9 @@ where } } -impl StepExecutorE1 +impl StepExecutorE1 for NativeLoadStoreStep where - Mem: GuestMemory, F: PrimeField32, A: 'static + for<'a> AdapterExecutorE1< @@ -221,7 +220,7 @@ where { fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: &mut VmExecutionState, instruction: &Instruction, ) -> Result<()> { let Instruction { diff --git a/extensions/rv32-adapters/src/eq_mod.rs b/extensions/rv32-adapters/src/eq_mod.rs index d49d6f6912..9992f526cb 100644 --- a/extensions/rv32-adapters/src/eq_mod.rs +++ b/extensions/rv32-adapters/src/eq_mod.rs @@ -394,10 +394,7 @@ impl< type ReadData = [[u8; TOTAL_READ_SIZE]; NUM_READS]; type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; - fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData - where - Mem: GuestMemory, - { + fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { let Instruction { b, c, d, e, .. } = *instruction; let d = d.as_canonical_u32(); @@ -418,10 +415,12 @@ impl< }) } - fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) - where - Mem: GuestMemory, - { + fn write( + &self, + memory: &mut GuestMemory, + instruction: &Instruction, + data: &Self::WriteData, + ) { let Instruction { a, d, .. } = *instruction; memory_write(memory, d.as_canonical_u32(), a.as_canonical_u32(), data); } diff --git a/extensions/rv32-adapters/src/heap.rs b/extensions/rv32-adapters/src/heap.rs index c14fbd64b0..acc00eba11 100644 --- a/extensions/rv32-adapters/src/heap.rs +++ b/extensions/rv32-adapters/src/heap.rs @@ -173,19 +173,18 @@ impl(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData - where - Mem: GuestMemory, - { + fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { let read_data = AdapterExecutorE1::::read(&self.0, memory, instruction); read_data.map(|r| r[0]) } #[inline(always)] - fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) - where - Mem: GuestMemory, - { + fn write( + &self, + memory: &mut GuestMemory, + instruction: &Instruction, + data: &Self::WriteData, + ) { AdapterExecutorE1::::write(&self.0, memory, instruction, data); } } diff --git a/extensions/rv32-adapters/src/heap_branch.rs b/extensions/rv32-adapters/src/heap_branch.rs index 262e34616c..1ce1873bb4 100644 --- a/extensions/rv32-adapters/src/heap_branch.rs +++ b/extensions/rv32-adapters/src/heap_branch.rs @@ -193,10 +193,7 @@ impl AdapterExe type ReadData = [[u8; READ_SIZE]; NUM_READS]; type WriteData = (); - fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData - where - Mem: GuestMemory, - { + fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { let Instruction { a, b, d, e, .. } = *instruction; let d = d.as_canonical_u32(); @@ -217,10 +214,12 @@ impl AdapterExe }) } - fn write(&self, _memory: &mut Mem, _instruction: &Instruction, _data: &Self::WriteData) - where - Mem: GuestMemory, - { + fn write( + &self, + _memory: &mut GuestMemory, + _instruction: &Instruction, + _data: &Self::WriteData, + ) { // This function intentionally does nothing } } diff --git a/extensions/rv32-adapters/src/vec_heap.rs b/extensions/rv32-adapters/src/vec_heap.rs index 47a8b17553..85d8b05822 100644 --- a/extensions/rv32-adapters/src/vec_heap.rs +++ b/extensions/rv32-adapters/src/vec_heap.rs @@ -463,10 +463,7 @@ impl< type ReadData = [[[u8; READ_SIZE]; BLOCKS_PER_READ]; NUM_READS]; type WriteData = [[u8; WRITE_SIZE]; BLOCKS_PER_WRITE]; - fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData - where - Mem: GuestMemory, - { + fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { let Instruction { b, c, d, e, .. } = *instruction; let d = d.as_canonical_u32(); @@ -489,10 +486,12 @@ impl< }) } - fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) - where - Mem: GuestMemory, - { + fn write( + &self, + memory: &mut GuestMemory, + instruction: &Instruction, + data: &Self::WriteData, + ) { let Instruction { a, d, e, .. } = *instruction; let rd_val = new_read_rv32_register(memory, d.as_canonical_u32(), a.as_canonical_u32()); assert!(rd_val as usize + WRITE_SIZE * BLOCKS_PER_WRITE - 1 < (1 << self.pointer_max_bits)); diff --git a/extensions/rv32-adapters/src/vec_heap_two_reads.rs b/extensions/rv32-adapters/src/vec_heap_two_reads.rs index 6a56ce7c10..9eeab26654 100644 --- a/extensions/rv32-adapters/src/vec_heap_two_reads.rs +++ b/extensions/rv32-adapters/src/vec_heap_two_reads.rs @@ -521,10 +521,7 @@ impl< ); type WriteData = [[u8; WRITE_SIZE]; BLOCKS_PER_WRITE]; - fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData - where - Mem: GuestMemory, - { + fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { let Instruction { b, c, d, e, .. } = *instruction; let d = d.as_canonical_u32(); @@ -545,10 +542,12 @@ impl< (read_data1, read_data2) } - fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) - where - Mem: GuestMemory, - { + fn write( + &self, + memory: &mut GuestMemory, + instruction: &Instruction, + data: &Self::WriteData, + ) { let Instruction { a, d, e, .. } = *instruction; let rd_val = new_read_rv32_register(memory, d.as_canonical_u32(), a.as_canonical_u32()); diff --git a/extensions/rv32im/circuit/src/adapters/alu.rs b/extensions/rv32im/circuit/src/adapters/alu.rs index 654d101469..a4b930f919 100644 --- a/extensions/rv32im/circuit/src/adapters/alu.rs +++ b/extensions/rv32im/circuit/src/adapters/alu.rs @@ -285,10 +285,7 @@ where type WriteData = [[u8; RV32_REGISTER_NUM_LIMBS]; 1]; #[inline(always)] - fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData - where - Mem: GuestMemory, - { + fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { let Instruction { b, c, d, e, .. } = instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); @@ -315,10 +312,7 @@ where } #[inline(always)] - fn write(&self, memory: &mut Mem, instruction: &Instruction, rd: &Self::WriteData) - where - Mem: GuestMemory, - { + fn write(&self, memory: &mut GuestMemory, instruction: &Instruction, rd: &Self::WriteData) { let Instruction { a, d, .. } = instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); diff --git a/extensions/rv32im/circuit/src/adapters/branch.rs b/extensions/rv32im/circuit/src/adapters/branch.rs index 6fb6ba00c8..c760f54995 100644 --- a/extensions/rv32im/circuit/src/adapters/branch.rs +++ b/extensions/rv32im/circuit/src/adapters/branch.rs @@ -194,10 +194,7 @@ where type WriteData = (); #[inline(always)] - fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData - where - Mem: GuestMemory, - { + fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { let Instruction { a, b, d, e, .. } = instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); @@ -212,9 +209,11 @@ where } #[inline(always)] - fn write(&self, _memory: &mut Mem, _instruction: &Instruction, _data: &Self::WriteData) - where - Mem: GuestMemory, - { + fn write( + &self, + _memory: &mut GuestMemory, + _instruction: &Instruction, + _data: &Self::WriteData, + ) { } } diff --git a/extensions/rv32im/circuit/src/adapters/jalr.rs b/extensions/rv32im/circuit/src/adapters/jalr.rs index a4cdb9ef1d..a2eeb9a11f 100644 --- a/extensions/rv32im/circuit/src/adapters/jalr.rs +++ b/extensions/rv32im/circuit/src/adapters/jalr.rs @@ -242,10 +242,7 @@ where type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; #[inline(always)] - fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData - where - Mem: GuestMemory, - { + fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { let Instruction { b, d, .. } = instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); @@ -257,10 +254,12 @@ where } #[inline(always)] - fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) - where - Mem: GuestMemory, - { + fn write( + &self, + memory: &mut GuestMemory, + instruction: &Instruction, + data: &Self::WriteData, + ) { let Instruction { a, d, f: enabled, .. } = instruction; diff --git a/extensions/rv32im/circuit/src/adapters/loadstore.rs b/extensions/rv32im/circuit/src/adapters/loadstore.rs index aa255662a6..951bf7099d 100644 --- a/extensions/rv32im/circuit/src/adapters/loadstore.rs +++ b/extensions/rv32im/circuit/src/adapters/loadstore.rs @@ -563,10 +563,7 @@ where ); type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; - fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData - where - Mem: GuestMemory, - { + fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { let Instruction { opcode, a, @@ -621,10 +618,12 @@ where ((prev_data, read_data), shift_amount) } - fn write(&self, memory: &mut Mem, instruction: &Instruction, data: &Self::WriteData) - where - Mem: GuestMemory, - { + fn write( + &self, + memory: &mut GuestMemory, + instruction: &Instruction, + data: &Self::WriteData, + ) { // TODO(ayush): remove duplication with read let &Instruction { opcode, diff --git a/extensions/rv32im/circuit/src/adapters/mod.rs b/extensions/rv32im/circuit/src/adapters/mod.rs index f1a29d376b..ba458930df 100644 --- a/extensions/rv32im/circuit/src/adapters/mod.rs +++ b/extensions/rv32im/circuit/src/adapters/mod.rs @@ -53,10 +53,7 @@ pub fn decompose(value: u32) -> [F; RV32_REGISTER_NUM_LIMBS] { } #[inline(always)] -pub fn memory_read(memory: &Mem, address_space: u32, ptr: u32) -> [u8; N] -where - Mem: GuestMemory, -{ +pub fn memory_read(memory: &GuestMemory, address_space: u32, ptr: u32) -> [u8; N] { debug_assert!( address_space == RV32_REGISTER_AS || address_space == RV32_MEMORY_AS @@ -71,14 +68,12 @@ where } #[inline(always)] -pub fn memory_write( - memory: &mut Mem, +pub fn memory_write( + memory: &mut GuestMemory, address_space: u32, ptr: u32, data: &[u8; N], -) where - Mem: GuestMemory, -{ +) { debug_assert!( address_space == RV32_REGISTER_AS || address_space == RV32_MEMORY_AS @@ -223,8 +218,9 @@ pub fn read_rv32_register( (record.0, val) } +// TODO(AG): if "register", why `address_space` is not hardcoded to be 1? #[inline(always)] -pub fn new_read_rv32_register(memory: &Mem, address_space: u32, ptr: u32) -> u32 { +pub fn new_read_rv32_register(memory: &GuestMemory, address_space: u32, ptr: u32) -> u32 { u32::from_le_bytes(memory_read(memory, address_space, ptr)) } diff --git a/extensions/rv32im/circuit/src/adapters/mul.rs b/extensions/rv32im/circuit/src/adapters/mul.rs index 9259f5549b..875b21eda1 100644 --- a/extensions/rv32im/circuit/src/adapters/mul.rs +++ b/extensions/rv32im/circuit/src/adapters/mul.rs @@ -227,10 +227,7 @@ where type WriteData = [[u8; RV32_REGISTER_NUM_LIMBS]; 1]; #[inline(always)] - fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData - where - Mem: GuestMemory, - { + fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { let Instruction { b, c, d, .. } = instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); @@ -244,10 +241,7 @@ where } #[inline(always)] - fn write(&self, memory: &mut Mem, instruction: &Instruction, rd: &Self::WriteData) - where - Mem: GuestMemory, - { + fn write(&self, memory: &mut GuestMemory, instruction: &Instruction, rd: &Self::WriteData) { let Instruction { a, d, .. } = *instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); diff --git a/extensions/rv32im/circuit/src/adapters/rdwrite.rs b/extensions/rv32im/circuit/src/adapters/rdwrite.rs index e8e1c02c25..59ebaddeb6 100644 --- a/extensions/rv32im/circuit/src/adapters/rdwrite.rs +++ b/extensions/rv32im/circuit/src/adapters/rdwrite.rs @@ -265,17 +265,10 @@ where type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; #[inline(always)] - fn read(&self, _memory: &mut Mem, _instruction: &Instruction) -> Self::ReadData - where - Mem: GuestMemory, - { - } + fn read(&self, _memory: &mut GuestMemory, _instruction: &Instruction) -> Self::ReadData {} #[inline(always)] - fn write(&self, memory: &mut Mem, instruction: &Instruction, rd: &Self::WriteData) - where - Mem: GuestMemory, - { + fn write(&self, memory: &mut GuestMemory, instruction: &Instruction, rd: &Self::WriteData) { let Instruction { a, d, .. } = instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); @@ -382,18 +375,12 @@ where type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; #[inline(always)] - fn read(&self, memory: &mut Mem, instruction: &Instruction) -> Self::ReadData - where - Mem: GuestMemory, - { + fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { >::read(&self.inner, memory, instruction) } #[inline(always)] - fn write(&self, memory: &mut Mem, instruction: &Instruction, rd: &Self::WriteData) - where - Mem: GuestMemory, - { + fn write(&self, memory: &mut GuestMemory, instruction: &Instruction, rd: &Self::WriteData) { let Instruction { f: enabled, .. } = instruction; if *enabled != F::ZERO { diff --git a/extensions/rv32im/circuit/src/auipc/core.rs b/extensions/rv32im/circuit/src/auipc/core.rs index f9599b0e87..1e746ccbe8 100644 --- a/extensions/rv32im/circuit/src/auipc/core.rs +++ b/extensions/rv32im/circuit/src/auipc/core.rs @@ -310,14 +310,11 @@ where A: 'static + for<'a> AdapterExecutorE1, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let Instruction { opcode, c: imm, .. } = instruction; let local_opcode = diff --git a/extensions/rv32im/circuit/src/base_alu/core.rs b/extensions/rv32im/circuit/src/base_alu/core.rs index aa25a05309..a653a69da3 100644 --- a/extensions/rv32im/circuit/src/base_alu/core.rs +++ b/extensions/rv32im/circuit/src/base_alu/core.rs @@ -283,14 +283,11 @@ where WriteData: From<[[u8; NUM_LIMBS]; 1]>, >, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let Instruction { opcode, .. } = instruction; let local_opcode = BaseAluOpcode::from_usize(opcode.local_opcode_idx(self.offset)); diff --git a/extensions/rv32im/circuit/src/branch_eq/core.rs b/extensions/rv32im/circuit/src/branch_eq/core.rs index 3f375b31ab..901de4943a 100644 --- a/extensions/rv32im/circuit/src/branch_eq/core.rs +++ b/extensions/rv32im/circuit/src/branch_eq/core.rs @@ -230,14 +230,11 @@ where F: PrimeField32, A: 'static + for<'a> AdapterExecutorE1, WriteData = ()>, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let &Instruction { opcode, c: imm, .. } = instruction; let branch_eq_opcode = BranchEqualOpcode::from_usize(opcode.local_opcode_idx(self.offset)); diff --git a/extensions/rv32im/circuit/src/branch_lt/core.rs b/extensions/rv32im/circuit/src/branch_lt/core.rs index 30d287731f..b08ff35ae3 100644 --- a/extensions/rv32im/circuit/src/branch_lt/core.rs +++ b/extensions/rv32im/circuit/src/branch_lt/core.rs @@ -358,14 +358,11 @@ where F: PrimeField32, A: 'static + for<'a> AdapterExecutorE1, WriteData = ()>, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let &Instruction { opcode, c: imm, .. } = instruction; let blt_opcode = BranchLessThanOpcode::from_usize(opcode.local_opcode_idx(self.offset)); diff --git a/extensions/rv32im/circuit/src/divrem/core.rs b/extensions/rv32im/circuit/src/divrem/core.rs index c8cc31ece2..c3141249ac 100644 --- a/extensions/rv32im/circuit/src/divrem/core.rs +++ b/extensions/rv32im/circuit/src/divrem/core.rs @@ -542,14 +542,11 @@ where WriteData: From<[[u8; NUM_LIMBS]; 1]>, >, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let Instruction { opcode, .. } = *instruction; // Determine opcode and operation type diff --git a/extensions/rv32im/circuit/src/extension.rs b/extensions/rv32im/circuit/src/extension.rs index c2a6a973c9..844840e1b3 100644 --- a/extensions/rv32im/circuit/src/extension.rs +++ b/extensions/rv32im/circuit/src/extension.rs @@ -614,13 +614,13 @@ mod phantom { use eyre::bail; use openvm_circuit::{ arch::{PhantomSubExecutor, Streams}, - system::memory::MemoryController, + system::memory::online::GuestMemory, }; use openvm_instructions::PhantomDiscriminant; use openvm_stark_backend::p3_field::{Field, PrimeField32}; use rand::{rngs::OsRng, Rng}; - use crate::adapters::unsafe_read_rv32_register; + use crate::adapters::{memory_read, new_read_rv32_register}; pub struct Rv32HintInputSubEx; pub struct Rv32HintRandomSubEx { @@ -636,11 +636,11 @@ mod phantom { impl PhantomSubExecutor for Rv32HintInputSubEx { fn phantom_execute( &mut self, - _: &MemoryController, + _: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, - _: F, - _: F, + _: u32, + _: u32, _: u16, ) -> eyre::Result<()> { let mut hint = match streams.input_stream.pop_front() { @@ -667,14 +667,14 @@ mod phantom { impl PhantomSubExecutor for Rv32HintRandomSubEx { fn phantom_execute( &mut self, - memory: &MemoryController, + memory: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, - a: F, - _: F, + a: u32, + _: u32, _: u16, ) -> eyre::Result<()> { - let len = unsafe_read_rv32_register(memory, a) as usize; + let len = new_read_rv32_register(memory, 1, a) as usize; streams.hint_stream.clear(); streams.hint_stream.extend( std::iter::repeat_with(|| F::from_canonical_u8(self.rng.gen::())).take(len * 4), @@ -686,17 +686,17 @@ mod phantom { impl PhantomSubExecutor for Rv32PrintStrSubEx { fn phantom_execute( &mut self, - memory: &MemoryController, + memory: &GuestMemory, _: &mut Streams, _: PhantomDiscriminant, - a: F, - b: F, + a: u32, + b: u32, _: u16, ) -> eyre::Result<()> { - let rd = unsafe_read_rv32_register(memory, a); - let rs1 = unsafe_read_rv32_register(memory, b); + let rd = new_read_rv32_register(memory, 1, a); + let rs1 = new_read_rv32_register(memory, 1, b); let bytes = (0..rs1) - .map(|i| memory.unsafe_read_cell::(F::TWO, F::from_canonical_u32(rd + i))) + .map(|i| memory_read::<1>(memory, 2, rd + i)[0]) .collect::>(); let peeked_str = String::from_utf8(bytes)?; print!("{peeked_str}"); diff --git a/extensions/rv32im/circuit/src/hintstore/mod.rs b/extensions/rv32im/circuit/src/hintstore/mod.rs index b6c456a1f6..41b0501100 100644 --- a/extensions/rv32im/circuit/src/hintstore/mod.rs +++ b/extensions/rv32im/circuit/src/hintstore/mod.rs @@ -462,14 +462,11 @@ impl StepExecutorE1 for Rv32HintStoreStep where F: PrimeField32, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let &Instruction { opcode, a: num_words_ptr, diff --git a/extensions/rv32im/circuit/src/jal_lui/core.rs b/extensions/rv32im/circuit/src/jal_lui/core.rs index c6d11a6f72..e90c0a8432 100644 --- a/extensions/rv32im/circuit/src/jal_lui/core.rs +++ b/extensions/rv32im/circuit/src/jal_lui/core.rs @@ -255,14 +255,11 @@ where A: 'static + for<'a> AdapterExecutorE1, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let Instruction { opcode, c: imm, .. } = instruction; let local_opcode = diff --git a/extensions/rv32im/circuit/src/jalr/core.rs b/extensions/rv32im/circuit/src/jalr/core.rs index 3192307450..b0746c5ae8 100644 --- a/extensions/rv32im/circuit/src/jalr/core.rs +++ b/extensions/rv32im/circuit/src/jalr/core.rs @@ -332,14 +332,11 @@ where WriteData = [u8; RV32_REGISTER_NUM_LIMBS], >, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let Instruction { opcode, c, g, .. } = instruction; let local_opcode = diff --git a/extensions/rv32im/circuit/src/less_than/core.rs b/extensions/rv32im/circuit/src/less_than/core.rs index 0ad602d63d..6baeab4ddd 100644 --- a/extensions/rv32im/circuit/src/less_than/core.rs +++ b/extensions/rv32im/circuit/src/less_than/core.rs @@ -335,14 +335,11 @@ where WriteData: From<[[u8; NUM_LIMBS]; 1]>, >, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let Instruction { opcode, .. } = instruction; let less_than_opcode = LessThanOpcode::from_usize(opcode.local_opcode_idx(self.offset)); diff --git a/extensions/rv32im/circuit/src/load_sign_extend/core.rs b/extensions/rv32im/circuit/src/load_sign_extend/core.rs index dd4051ae9a..d2782005c9 100644 --- a/extensions/rv32im/circuit/src/load_sign_extend/core.rs +++ b/extensions/rv32im/circuit/src/load_sign_extend/core.rs @@ -314,14 +314,11 @@ where WriteData = [u8; NUM_CELLS], >, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let Instruction { opcode, .. } = instruction; let local_opcode = Rv32LoadStoreOpcode::from_usize( diff --git a/extensions/rv32im/circuit/src/loadstore/core.rs b/extensions/rv32im/circuit/src/loadstore/core.rs index 8b8e499f3a..9baa0c65ec 100644 --- a/extensions/rv32im/circuit/src/loadstore/core.rs +++ b/extensions/rv32im/circuit/src/loadstore/core.rs @@ -378,14 +378,11 @@ where WriteData = [u8; NUM_CELLS], >, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let Instruction { opcode, .. } = instruction; // Get the local opcode for this instruction diff --git a/extensions/rv32im/circuit/src/mul/core.rs b/extensions/rv32im/circuit/src/mul/core.rs index efa115aa54..b1df4b8e64 100644 --- a/extensions/rv32im/circuit/src/mul/core.rs +++ b/extensions/rv32im/circuit/src/mul/core.rs @@ -234,14 +234,11 @@ where WriteData: From<[[u8; NUM_LIMBS]; 1]>, >, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let Instruction { opcode, .. } = instruction; // Verify the opcode is MUL diff --git a/extensions/rv32im/circuit/src/mulh/core.rs b/extensions/rv32im/circuit/src/mulh/core.rs index b1ca02ee21..1a202cb306 100644 --- a/extensions/rv32im/circuit/src/mulh/core.rs +++ b/extensions/rv32im/circuit/src/mulh/core.rs @@ -324,14 +324,11 @@ where WriteData: From<[[u8; NUM_LIMBS]; 1]>, >, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let Instruction { opcode, .. } = instruction; let mulh_opcode = MulHOpcode::from_usize(opcode.local_opcode_idx(MulHOpcode::CLASS_OFFSET)); diff --git a/extensions/rv32im/circuit/src/shift/core.rs b/extensions/rv32im/circuit/src/shift/core.rs index ba68e61a05..872917852c 100644 --- a/extensions/rv32im/circuit/src/shift/core.rs +++ b/extensions/rv32im/circuit/src/shift/core.rs @@ -397,14 +397,11 @@ where WriteData: From<[[u8; NUM_LIMBS]; 1]>, >, { - fn execute_e1( + fn execute_e1( &mut self, - state: VmStateMut, + state: VmStateMut, instruction: &Instruction, - ) -> Result<()> - where - Mem: GuestMemory, - { + ) -> Result<()> { let Instruction { opcode, .. } = instruction; let shift_opcode = ShiftOpcode::from_usize(opcode.local_opcode_idx(self.offset)); From 5da1c54778a02cf615d3c823a6a6136b51dc2571 Mon Sep 17 00:00:00 2001 From: Arayi Khalatyan <127004086+arayikhalatyan@users.noreply.github.com> Date: Tue, 20 May 2025 02:50:11 -0400 Subject: [PATCH 26/49] feat: e1, e3 implementation for sha2 + keccak extensions (#1657) Implemented e1, e3 for sha2 and keccak extensions. I feel like the trace generation of sha2 is more readable now. Also, implemented an arbitrary length read function (used in both e1 executions) I think eventually we should reimplement Plonky3's trace generation for Keccak so we don't have to allocate a separate trace matrix for the perm cols. TODOs: - port the tests of keccak to the new framework - spend some time (not much) on optimizing the keccak trace gen Resolves INT-3966 Resolves INT-3921 --- Cargo.lock | 386 +++++----- crates/circuits/sha256-air/src/air.rs | 17 +- crates/circuits/sha256-air/src/tests.rs | 161 ++--- crates/circuits/sha256-air/src/trace.rs | 141 ++-- crates/circuits/sha256-air/src/utils.rs | 12 +- crates/vm/src/system/memory/paged_vec.rs | 34 +- extensions/keccak256/circuit/src/extension.rs | 38 +- extensions/keccak256/circuit/src/lib.rs | 227 +----- extensions/keccak256/circuit/src/tests.rs | 89 ++- extensions/keccak256/circuit/src/trace.rs | 507 ++++++++------ extensions/sha256/circuit/src/extension.rs | 29 +- .../sha256/circuit/src/sha256_chip/air.rs | 28 +- .../sha256/circuit/src/sha256_chip/mod.rs | 161 +---- .../sha256/circuit/src/sha256_chip/tests.rs | 93 +-- .../sha256/circuit/src/sha256_chip/trace.rs | 660 ++++++++++-------- 15 files changed, 1261 insertions(+), 1322 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2eba1c80d..68ea667442 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,14 +51,14 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -323,7 +323,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d162f8524adfdfb0e4bd0505c734c985f3e2474eb022af32eef0d52a4f3935c" dependencies = [ "serde", - "winnow 0.7.9", + "winnow 0.7.10", ] [[package]] @@ -425,15 +425,6 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" -[[package]] -name = "arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" -dependencies = [ - "derive_arbitrary", -] - [[package]] name = "ariadne" version = "0.2.0" @@ -852,9 +843,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b756939cb2f8dc900aa6dcd505e6e2428e9cae7ff7b028c49e3946efa70878" +checksum = "93fcc8f365936c834db5514fc45aee5b1202d677e6b40e48468aaaa8183ca8c7" dependencies = [ "aws-lc-sys", "zeroize", @@ -862,9 +853,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.28.2" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa9b6986f250236c27e5a204062434a773a13243d2ffc2955f37bdba4c5c6a1" +checksum = "61b1d86e7705efe1be1b569bab41d4fa1e14e220b60a160f78de2db687add079" dependencies = [ "bindgen", "cc", @@ -900,9 +891,9 @@ dependencies = [ [[package]] name = "aws-sdk-s3" -version = "1.83.0" +version = "1.85.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51384750334005f40e1a334b0d54eca822a77eacdcf3c50fdf38f583c5eee7a2" +checksum = "d5c82dae9304e7ced2ff6cca43dceb2d6de534c95a506ff0f168a7463c9a885d" dependencies = [ "aws-credential-types", "aws-runtime", @@ -935,9 +926,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.65.0" +version = "1.67.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8efec445fb78df585327094fcef4cad895b154b58711e504db7a93c41aa27151" +checksum = "0d4863da26489d1e6da91d7e12b10c17e86c14f94c53f416bd10e0a9c34057ba" dependencies = [ "aws-credential-types", "aws-runtime", @@ -958,9 +949,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.66.0" +version = "1.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e49cca619c10e7b002dc8e66928ceed66ab7f56c1a3be86c5437bf2d8d89bba" +checksum = "95caa3998d7237789b57b95a8e031f60537adab21fa84c91e35bef9455c652e4" dependencies = [ "aws-credential-types", "aws-runtime", @@ -981,9 +972,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.66.0" +version = "1.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7420479eac0a53f776cc8f0d493841ffe58ad9d9783f3947be7265784471b47a" +checksum = "4939f6f449a37308a78c5a910fd91265479bd2bb11d186f0b8fc114d89ec828d" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1098,14 +1089,14 @@ dependencies = [ [[package]] name = "aws-smithy-http-client" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aff1159006441d02e57204bf57a1b890ba68bedb6904ffd2873c1c4c11c546b" +checksum = "7e44697a9bded898dcd0b1cb997430d949b87f4f8940d91023ae9062bf218250" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", "aws-smithy-types", - "h2 0.4.9", + "h2 0.4.10", "http 0.2.12", "http 1.3.1", "http-body 0.4.6", @@ -1116,7 +1107,7 @@ dependencies = [ "hyper-util", "pin-project-lite", "rustls 0.21.12", - "rustls 0.23.26", + "rustls 0.23.27", "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", @@ -1244,9 +1235,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -1612,11 +1603,10 @@ dependencies = [ [[package]] name = "c-kzg" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7e3c397401eb76228c89561cf22f85f41c95aa799ee9d860de3ea1cbc728fc" +checksum = "7318cfa722931cb5fe0838b98d3ce5621e75f6a6408abc21721d80de9223f2e4" dependencies = [ - "arbitrary", "blst", "cc", "glob", @@ -1692,9 +1682,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.21" +version = "1.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" +checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" dependencies = [ "jobserver", "libc", @@ -1779,9 +1769,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -1789,9 +1779,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", @@ -2002,9 +1992,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" dependencies = [ "crc-catalog", ] @@ -2289,17 +2279,6 @@ dependencies = [ "syn 2.0.101", ] -[[package]] -name = "derive_arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "derive_more" version = "0.99.20" @@ -3082,9 +3061,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", @@ -3192,9 +3171,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" +checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" dependencies = [ "atomic-waker", "bytes", @@ -3418,9 +3397,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hermit-abi" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" +checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" [[package]] name = "hex" @@ -3565,7 +3544,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.9", + "h2 0.4.10", "http 1.3.1", "http-body 1.0.1", "httparse", @@ -3602,7 +3581,7 @@ dependencies = [ "http 1.3.1", "hyper 1.6.0", "hyper-util", - "rustls 0.23.26", + "rustls 0.23.27", "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", @@ -3656,21 +3635,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -3679,31 +3659,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -3711,67 +3671,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -3791,9 +3738,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -3892,7 +3839,7 @@ version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ - "hermit-abi 0.5.0", + "hermit-abi 0.5.1", "libc", "windows-sys 0.59.0", ] @@ -3960,7 +3907,7 @@ version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", "libc", ] @@ -4085,19 +4032,19 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.0", ] [[package]] name = "libm" -version = "0.2.13" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libmimalloc-sys" @@ -4197,9 +4144,9 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" @@ -6421,6 +6368,15 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -6433,7 +6389,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.25", + "zerocopy", ] [[package]] @@ -6658,7 +6614,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", "serde", ] @@ -6702,9 +6658,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ "bitflags 2.9.0", ] @@ -7213,14 +7169,14 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.26" +version = "0.23.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" dependencies = [ "aws-lc-rs", "once_cell", "rustls-pki-types", - "rustls-webpki 0.103.1", + "rustls-webpki 0.103.3", "subtle", "zeroize", ] @@ -7260,9 +7216,12 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] [[package]] name = "rustls-webpki" @@ -7276,9 +7235,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.1" +version = "0.103.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" dependencies = [ "aws-lc-rs", "ring", @@ -8008,12 +7967,12 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.3", "once_cell", "rustix 1.0.7", "windows-sys 0.59.0", @@ -8218,9 +8177,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -8238,9 +8197,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.2" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" dependencies = [ "backtrace", "bytes", @@ -8280,7 +8239,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.26", + "rustls 0.23.27", "tokio", ] @@ -8355,7 +8314,7 @@ dependencies = [ "serde_spanned", "toml_datetime", "toml_write", - "winnow 0.7.9", + "winnow 0.7.10", ] [[package]] @@ -8575,12 +8534,6 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -8599,7 +8552,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", ] [[package]] @@ -8922,13 +8875,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -8941,6 +8910,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -8953,6 +8928,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -8965,12 +8946,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -8983,6 +8976,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -8995,6 +8994,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -9007,6 +9012,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -9019,6 +9030,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.5.40" @@ -9030,9 +9047,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -9056,17 +9073,11 @@ dependencies = [ "bitflags 2.9.0", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "wyz" @@ -9097,9 +9108,9 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -9109,9 +9120,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", @@ -9119,33 +9130,13 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive 0.7.35", -] - [[package]] name = "zerocopy" version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "zerocopy-derive 0.8.25", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", + "zerocopy-derive", ] [[package]] @@ -9200,11 +9191,22 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", @@ -9213,9 +9215,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", diff --git a/crates/circuits/sha256-air/src/air.rs b/crates/circuits/sha256-air/src/air.rs index 96578984d0..b27af6ffa9 100644 --- a/crates/circuits/sha256-air/src/air.rs +++ b/crates/circuits/sha256-air/src/air.rs @@ -15,11 +15,11 @@ use openvm_stark_backend::{ use super::{ big_sig0_field, big_sig1_field, ch_field, compose, maj_field, small_sig0_field, - small_sig1_field, u32_into_limbs, Sha256DigestCols, Sha256RoundCols, SHA256_DIGEST_WIDTH, - SHA256_H, SHA256_HASH_WORDS, SHA256_K, SHA256_ROUNDS_PER_ROW, SHA256_ROUND_WIDTH, - SHA256_WORD_BITS, SHA256_WORD_U16S, SHA256_WORD_U8S, + small_sig1_field, Sha256DigestCols, Sha256RoundCols, SHA256_DIGEST_WIDTH, SHA256_H, + SHA256_HASH_WORDS, SHA256_K, SHA256_ROUNDS_PER_ROW, SHA256_ROUND_WIDTH, SHA256_WORD_BITS, + SHA256_WORD_U16S, SHA256_WORD_U8S, }; -use crate::constraint_word_addition; +use crate::{constraint_word_addition, u32_into_u16s}; /// Expects the message to be padded to a multiple of 512 bits #[derive(Clone, Debug)] @@ -154,7 +154,7 @@ impl Sha256Air { .assert_eq( a_limb, AB::Expr::from_canonical_u32( - u32_into_limbs::<2>(SHA256_H[SHA256_ROUNDS_PER_ROW - i - 1])[j], + u32_into_u16s(SHA256_H[SHA256_ROUNDS_PER_ROW - i - 1])[j], ), ); @@ -166,7 +166,7 @@ impl Sha256Air { .assert_eq( e_limb, AB::Expr::from_canonical_u32( - u32_into_limbs::<2>(SHA256_H[SHA256_ROUNDS_PER_ROW - i + 3])[j], + u32_into_u16s(SHA256_H[SHA256_ROUNDS_PER_ROW - i + 3])[j], ), ); } @@ -561,9 +561,8 @@ impl Sha256Air { .map(|rw_idx| { ( rw_idx, - u32_into_limbs::( - SHA256_K[rw_idx * SHA256_ROUNDS_PER_ROW + i], - )[j] as usize, + u32_into_u16s(SHA256_K[rw_idx * SHA256_ROUNDS_PER_ROW + i])[j] + as usize, ) }) .collect::>(), diff --git a/crates/circuits/sha256-air/src/tests.rs b/crates/circuits/sha256-air/src/tests.rs index 903b7b0695..5822bfe235 100644 --- a/crates/circuits/sha256-air/src/tests.rs +++ b/crates/circuits/sha256-air/src/tests.rs @@ -13,18 +13,19 @@ use openvm_stark_backend::{ interaction::{BusIndex, InteractionBuilder}, p3_air::{Air, BaseAir}, p3_field::{Field, FieldAlgebra, PrimeField32}, - p3_maybe_rayon::prelude::{IndexedParallelIterator, ParallelIterator, ParallelSliceMut}, + p3_matrix::{dense::RowMajorMatrix, Matrix}, prover::types::AirProofInput, rap::{get_air_name, BaseAirWithPublicValues, PartitionedBaseAir}, + utils::disable_debug_builder, + verifier::VerificationError, AirRef, Chip, ChipUsageGetter, }; -use openvm_stark_sdk::utils::create_seeded_rng; +use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::Rng; use crate::{ - compose, small_sig0_field, Sha256Air, Sha256RoundCols, SHA256_BLOCK_U8S, SHA256_DIGEST_WIDTH, - SHA256_HASH_WORDS, SHA256_ROUNDS_PER_ROW, SHA256_ROUND_WIDTH, SHA256_ROWS_PER_BLOCK, - SHA256_WORD_U16S, SHA256_WORD_U8S, + Sha256Air, Sha256DigestCols, Sha256StepHelper, SHA256_BLOCK_U8S, SHA256_DIGEST_WIDTH, + SHA256_HASH_WORDS, SHA256_ROUND_WIDTH, SHA256_ROWS_PER_BLOCK, SHA256_WORD_U8S, }; // A wrapper AIR purely for testing purposes @@ -50,6 +51,7 @@ impl Air for Sha256TestAir { // A wrapper Chip purely for testing purposes pub struct Sha256TestChip { pub air: Sha256TestAir, + pub step: Sha256StepHelper, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip<8>, pub records: Vec<([u8; SHA256_BLOCK_U8S], bool)>, } @@ -64,8 +66,9 @@ where fn generate_air_proof_input(self) -> AirProofInput { let trace = crate::generate_trace::>( - &self.air.sub_air, - self.bitwise_lookup_chip.clone(), + &self.step, + self.bitwise_lookup_chip.as_ref(), + >>::width(&self.air.sub_air), self.records, ); AirProofInput::simple_no_pis(trace) @@ -86,10 +89,10 @@ impl ChipUsageGetter for Sha256TestChip { } const SELF_BUS_IDX: BusIndex = 28; -#[test] -fn rand_sha256_test() { +type F = BabyBear; + +fn create_chip_with_rand_records() -> (Sha256TestChip, SharedBitwiseOperationLookupChip<8>) { let mut rng = create_seeded_rng(); - let tester = VmChipTestBuilder::default(); let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); let len = rng.gen_range(1..100); @@ -105,129 +108,47 @@ fn rand_sha256_test() { air: Sha256TestAir { sub_air: Sha256Air::new(bitwise_bus, SELF_BUS_IDX), }, + step: Sha256StepHelper::new(), bitwise_lookup_chip: bitwise_chip.clone(), records: random_records, }; + (chip, bitwise_chip) +} +#[test] +fn rand_sha256_test() { + let tester = VmChipTestBuilder::default(); + let (chip, bitwise_chip) = create_chip_with_rand_records(); let tester = tester.build().load(chip).load(bitwise_chip).finalize(); tester.simple_test().expect("Verification failed"); } -// A wrapper Chip to test that the final_hash is properly constrained. -// This chip implements a malicious trace gen that violates the final_hash constraints. -pub struct Sha256TestBadFinalHashChip { - pub air: Sha256TestAir, - pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip<8>, - pub records: Vec<([u8; SHA256_BLOCK_U8S], bool)>, -} - -impl Chip for Sha256TestBadFinalHashChip -where - Val: PrimeField32, -{ - fn air(&self) -> AirRef { - Arc::new(self.air.clone()) - } - - fn generate_air_proof_input(self) -> AirProofInput { - let mut trace = crate::generate_trace::>( - &self.air.sub_air, - self.bitwise_lookup_chip.clone(), - self.records.clone(), - ); - - // Set the final_hash in the digest row of the last block of each hash to zero. - // That is, every hash that this chip does will result in a final_hash of zero. - for (i, row) in self.records.iter().enumerate() { - if row.1 { - let last_digest_row_idx = (i + 1) * SHA256_ROWS_PER_BLOCK - 1; - let last_digest_row: &mut crate::Sha256DigestCols> = - trace.row_mut(last_digest_row_idx)[..SHA256_DIGEST_WIDTH].borrow_mut(); - // Set the final_hash to all zeros +#[test] +fn negative_sha256_test_bad_final_hash() { + let tester = VmChipTestBuilder::default(); + let (chip, bitwise_chip) = create_chip_with_rand_records(); + + // Set the final_hash to all zeros + let modify_trace = |trace: &mut RowMajorMatrix| { + trace.row_chunks_exact_mut(1).for_each(|row| { + let mut row_slice = row.row_slice(0).to_vec(); + let cols: &mut Sha256DigestCols = row_slice[..SHA256_DIGEST_WIDTH].borrow_mut(); + if cols.flags.is_last_block.is_one() && cols.flags.is_digest_row.is_one() { for i in 0..SHA256_HASH_WORDS { for j in 0..SHA256_WORD_U8S { - last_digest_row.final_hash[i][j] = Val::::ZERO; + cols.final_hash[i][j] = F::ZERO; } } - - let (last_round_row, last_digest_row) = - trace.row_pair_mut(last_digest_row_idx - 1, last_digest_row_idx); - let last_round_row: &mut crate::Sha256RoundCols> = - last_round_row.borrow_mut(); - let last_digest_row: &mut crate::Sha256RoundCols> = - last_digest_row.borrow_mut(); - // fix the intermed_4 for the digest row - generate_intermed_4(last_round_row, last_digest_row); + row.values.copy_from_slice(&row_slice); } - } - - let non_padded_height = self.records.len() * SHA256_ROWS_PER_BLOCK; - let width = >>::width(&self.air.sub_air); - // recalculate the missing cells (second pass of generate_trace) - trace.values[width..] - .par_chunks_mut(width * SHA256_ROWS_PER_BLOCK) - .take(non_padded_height / SHA256_ROWS_PER_BLOCK) - .for_each(|chunk| { - self.air.sub_air.generate_missing_cells(chunk, width, 0); - }); - - AirProofInput::simple_no_pis(trace) - } -} - -// Copy of private method in Sha256Air used for testing -/// Puts the correct intermed_4 in the `next_row` -fn generate_intermed_4( - local_cols: &Sha256RoundCols, - next_cols: &mut Sha256RoundCols, -) { - let w = [local_cols.message_schedule.w, next_cols.message_schedule.w].concat(); - let w_limbs: Vec<[F; SHA256_WORD_U16S]> = w - .iter() - .map(|x| array::from_fn(|i| compose::(&x[i * 16..(i + 1) * 16], 1))) - .collect(); - for i in 0..SHA256_ROUNDS_PER_ROW { - let sig_w = small_sig0_field::(&w[i + 1]); - let sig_w_limbs: [F; SHA256_WORD_U16S] = - array::from_fn(|j| compose::(&sig_w[j * 16..(j + 1) * 16], 1)); - for (j, sig_w_limb) in sig_w_limbs.iter().enumerate() { - next_cols.schedule_helper.intermed_4[i][j] = w_limbs[i][j] + *sig_w_limb; - } - } -} - -impl ChipUsageGetter for Sha256TestBadFinalHashChip { - fn air_name(&self) -> String { - get_air_name(&self.air) - } - fn current_trace_height(&self) -> usize { - self.records.len() * SHA256_ROWS_PER_BLOCK - } - - fn trace_width(&self) -> usize { - max(SHA256_ROUND_WIDTH, SHA256_DIGEST_WIDTH) - } -} - -#[test] -#[should_panic] -fn test_sha256_final_hash_constraints() { - let mut rng = create_seeded_rng(); - let tester = VmChipTestBuilder::default(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let len = rng.gen_range(1..100); - let random_records: Vec<_> = (0..len) - .map(|_| (array::from_fn(|_| rng.gen::()), true)) - .collect(); - let chip = Sha256TestBadFinalHashChip { - air: Sha256TestAir { - sub_air: Sha256Air::new(bitwise_bus, SELF_BUS_IDX), - }, - bitwise_lookup_chip: bitwise_chip.clone(), - records: random_records, + }); }; - let tester = tester.build().load(chip).load(bitwise_chip).finalize(); - tester.simple_test().expect("Verification failed"); + disable_debug_builder(); + let tester = tester + .build() + .load_and_prank_trace(chip, modify_trace) + .load(bitwise_chip) + .finalize(); + tester.simple_test_with_expected_error(VerificationError::OodEvaluationMismatch); } diff --git a/crates/circuits/sha256-air/src/trace.rs b/crates/circuits/sha256-air/src/trace.rs index eaf9174f50..21483de642 100644 --- a/crates/circuits/sha256-air/src/trace.rs +++ b/crates/circuits/sha256-air/src/trace.rs @@ -1,41 +1,47 @@ use std::{array, borrow::BorrowMut, ops::Range}; use openvm_circuit_primitives::{ - bitwise_op_lookup::SharedBitwiseOperationLookupChip, utils::next_power_of_two_or_zero, + bitwise_op_lookup::BitwiseOperationLookupChip, encoder::Encoder, + utils::next_power_of_two_or_zero, }; use openvm_stark_backend::{ - p3_air::BaseAir, p3_field::PrimeField32, p3_matrix::dense::RowMajorMatrix, - p3_maybe_rayon::prelude::*, + p3_field::PrimeField32, p3_matrix::dense::RowMajorMatrix, p3_maybe_rayon::prelude::*, }; use sha2::{compress256, digest::generic_array::GenericArray}; use super::{ - air::Sha256Air, big_sig0_field, big_sig1_field, ch_field, columns::Sha256RoundCols, compose, - get_flag_pt_array, maj_field, small_sig0_field, small_sig1_field, SHA256_BLOCK_WORDS, - SHA256_DIGEST_WIDTH, SHA256_HASH_WORDS, SHA256_ROUND_WIDTH, + big_sig0_field, big_sig1_field, ch_field, columns::Sha256RoundCols, compose, get_flag_pt_array, + maj_field, small_sig0_field, small_sig1_field, SHA256_BLOCK_WORDS, SHA256_DIGEST_WIDTH, + SHA256_HASH_WORDS, SHA256_ROUND_WIDTH, }; use crate::{ big_sig0, big_sig1, ch, columns::Sha256DigestCols, limbs_into_u32, maj, small_sig0, small_sig1, - u32_into_limbs, SHA256_BLOCK_U8S, SHA256_BUFFER_SIZE, SHA256_H, SHA256_INVALID_CARRY_A, + u32_into_bits_field, u32_into_u16s, SHA256_BLOCK_U8S, SHA256_H, SHA256_INVALID_CARRY_A, SHA256_INVALID_CARRY_E, SHA256_K, SHA256_ROUNDS_PER_ROW, SHA256_ROWS_PER_BLOCK, SHA256_WORD_BITS, SHA256_WORD_U16S, SHA256_WORD_U8S, }; +/// A helper struct for the SHA256 trace generation. +/// Also, separates the inner AIR from the trace generation. +pub struct Sha256StepHelper { + pub row_idx_encoder: Encoder, +} + /// The trace generation of SHA256 should be done in two passes. /// The first pass should do `get_block_trace` for every block and generate the invalid rows through /// `get_default_row` The second pass should go through all the blocks and call /// `generate_missing_cells` -impl Sha256Air { +impl Sha256StepHelper { + pub fn new() -> Self { + Self { + row_idx_encoder: Encoder::new(18, 2, false), + } + } /// This function takes the input_message (padding not handled), the previous hash, - /// and returns the new hash after processing the block input - pub fn get_block_hash( - prev_hash: &[u32; SHA256_HASH_WORDS], - input: [u8; SHA256_BLOCK_U8S], - ) -> [u32; SHA256_HASH_WORDS] { - let mut new_hash = *prev_hash; + /// and updates the prev_hash after processing the block input + pub fn get_block_hash(prev_hash: &mut [u32; SHA256_HASH_WORDS], input: [u8; SHA256_BLOCK_U8S]) { let input_array = [GenericArray::from(input)]; - compress256(&mut new_hash, &input_array); - new_hash + compress256(prev_hash, &input_array); } /// This function takes a 512-bit chunk of the input message (padding not handled), the previous @@ -52,18 +58,16 @@ impl Sha256Air { trace_width: usize, trace_start_col: usize, input: &[u32; SHA256_BLOCK_WORDS], - bitwise_lookup_chip: SharedBitwiseOperationLookupChip<8>, + bitwise_lookup_chip: &BitwiseOperationLookupChip<8>, prev_hash: &[u32; SHA256_HASH_WORDS], is_last_block: bool, global_block_idx: u32, local_block_idx: u32, - buffer_vals: &[[F; SHA256_BUFFER_SIZE]; 4], ) { #[cfg(debug_assertions)] { assert!(trace.len() == trace_width * SHA256_ROWS_PER_BLOCK); assert!(trace_start_col + super::SHA256_WIDTH <= trace_width); - assert!(self.bitwise_lookup_bus == bitwise_lookup_chip.bus()); if local_block_idx == 0 { assert!(*prev_hash == SHA256_H); } @@ -87,14 +91,10 @@ impl Sha256Air { cols.flags.local_block_idx = F::from_canonical_u32(local_block_idx); // W_idx = M_idx - if i < SHA256_ROWS_PER_BLOCK / SHA256_ROUNDS_PER_ROW { + if i < 4 { for j in 0..SHA256_ROUNDS_PER_ROW { - cols.message_schedule.w[j] = u32_into_limbs::( - input[i * SHA256_ROUNDS_PER_ROW + j], - ) - .map(F::from_canonical_u32); - cols.message_schedule.carry_or_buffer[j] = - array::from_fn(|k| buffer_vals[i][j * SHA256_WORD_U16S * 2 + k]); + cols.message_schedule.w[j] = + u32_into_bits_field::(input[i * SHA256_ROUNDS_PER_ROW + j]); } } // W_idx = SIG1(W_{idx-2}) + W_{idx-7} + SIG0(W_{idx-15}) + W_{idx-16} @@ -108,14 +108,10 @@ impl Sha256Air { message_schedule[idx - 16], ]; let w: u32 = nums.iter().fold(0, |acc, &num| acc.wrapping_add(num)); - cols.message_schedule.w[j] = - u32_into_limbs::(w).map(F::from_canonical_u32); + cols.message_schedule.w[j] = u32_into_bits_field::(w); - let nums_limbs = nums - .iter() - .map(|x| u32_into_limbs::(*x)) - .collect::>(); - let w_limbs = u32_into_limbs::(w); + let nums_limbs = nums.map(|x| u32_into_u16s(x)); + let w_limbs = u32_into_u16s(w); // fill in the carrys for k in 0..SHA256_WORD_U16S { @@ -157,25 +153,18 @@ impl Sha256Air { // e = d + t1 let e = work_vars[3].wrapping_add(t1_sum); - cols.work_vars.e[j] = - u32_into_limbs::(e).map(F::from_canonical_u32); - let e_limbs = u32_into_limbs::(e); + cols.work_vars.e[j] = u32_into_bits_field::(e); + let e_limbs = u32_into_u16s(e); // a = t1 + t2 let a = t1_sum.wrapping_add(t2_sum); - cols.work_vars.a[j] = - u32_into_limbs::(a).map(F::from_canonical_u32); - let a_limbs = u32_into_limbs::(a); + cols.work_vars.a[j] = u32_into_bits_field::(a); + let a_limbs = u32_into_u16s(a); // fill in the carrys for k in 0..SHA256_WORD_U16S { - let t1_limb = t1.iter().fold(0, |acc, &num| { - acc + u32_into_limbs::(num)[k] - }); - let t2_limb = t2.iter().fold(0, |acc, &num| { - acc + u32_into_limbs::(num)[k] - }); + let t1_limb = t1.iter().fold(0, |acc, &num| acc + u32_into_u16s(num)[k]); + let t2_limb = t2.iter().fold(0, |acc, &num| acc + u32_into_u16s(num)[k]); - let mut e_limb = - t1_limb + u32_into_limbs::(work_vars[3])[k]; + let mut e_limb = t1_limb + u32_into_u16s(work_vars[3])[k]; let mut a_limb = t1_limb + t2_limb; if k > 0 { a_limb += cols.work_vars.carry_a[j][k - 1].as_canonical_u32(); @@ -203,16 +192,14 @@ impl Sha256Air { if i > 0 { for j in 0..SHA256_ROUNDS_PER_ROW { let idx = i * SHA256_ROUNDS_PER_ROW + j; - let w_4 = u32_into_limbs::(message_schedule[idx - 4]); - let sig_0_w_3 = u32_into_limbs::(small_sig0( - message_schedule[idx - 3], - )); + let w_4 = u32_into_u16s(message_schedule[idx - 4]); + let sig_0_w_3 = u32_into_u16s(small_sig0(message_schedule[idx - 3])); cols.schedule_helper.intermed_4[j] = array::from_fn(|k| F::from_canonical_u32(w_4[k] + sig_0_w_3[k])); if j < SHA256_ROUNDS_PER_ROW - 1 { let w_3 = message_schedule[idx - 3]; cols.schedule_helper.w_3[j] = - u32_into_limbs::(w_3).map(F::from_canonical_u32); + u32_into_u16s(w_3).map(F::from_canonical_u32); } } } @@ -223,8 +210,7 @@ impl Sha256Air { row[get_range(trace_start_col, SHA256_DIGEST_WIDTH)].borrow_mut(); for j in 0..SHA256_ROUNDS_PER_ROW - 1 { let w_3 = message_schedule[i * SHA256_ROUNDS_PER_ROW + j - 3]; - cols.schedule_helper.w_3[j] = - u32_into_limbs::(w_3).map(F::from_canonical_u32); + cols.schedule_helper.w_3[j] = u32_into_u16s(w_3).map(F::from_canonical_u32); } cols.flags.is_round_row = F::ZERO; cols.flags.is_first_4_rows = F::ZERO; @@ -237,29 +223,27 @@ impl Sha256Air { cols.flags.local_block_idx = F::from_canonical_u32(local_block_idx); let final_hash: [u32; SHA256_HASH_WORDS] = array::from_fn(|i| work_vars[i].wrapping_add(prev_hash[i])); - let final_hash_limbs: [[u32; SHA256_WORD_U8S]; SHA256_HASH_WORDS] = - array::from_fn(|i| u32_into_limbs::(final_hash[i])); + let final_hash_limbs: [[u8; SHA256_WORD_U8S]; SHA256_HASH_WORDS] = + array::from_fn(|i| final_hash[i].to_le_bytes()); // need to ensure final hash limbs are bytes, in order for // prev_hash[i] + work_vars[i] == final_hash[i] // to be constrained correctly for word in final_hash_limbs.iter() { for chunk in word.chunks(2) { - bitwise_lookup_chip.request_range(chunk[0], chunk[1]); + bitwise_lookup_chip.request_range(chunk[0] as u32, chunk[1] as u32); } } cols.final_hash = array::from_fn(|i| { - array::from_fn(|j| F::from_canonical_u32(final_hash_limbs[i][j])) + array::from_fn(|j| F::from_canonical_u8(final_hash_limbs[i][j])) }); - cols.prev_hash = prev_hash - .map(|f| u32_into_limbs::(f).map(F::from_canonical_u32)); + cols.prev_hash = prev_hash.map(|f| u32_into_u16s(f).map(F::from_canonical_u32)); let hash = if is_last_block { - SHA256_H.map(u32_into_limbs::) + SHA256_H.map(u32_into_bits_field::) } else { cols.final_hash - .map(|f| limbs_into_u32(f.map(|x| x.as_canonical_u32()))) - .map(u32_into_limbs::) - } - .map(|x| x.map(F::from_canonical_u32)); + .map(|f| u32::from_le_bytes(f.map(|x| x.as_canonical_u32() as u8))) + .map(u32_into_bits_field::) + }; for i in 0..SHA256_ROUNDS_PER_ROW { cols.hash.a[i] = hash[SHA256_ROUNDS_PER_ROW - i - 1]; @@ -338,7 +322,10 @@ impl Sha256Air { /// Fills the `cols` as a padding row /// Note: we still need to correctly fill in the hash values, carries and intermeds - pub fn generate_default_row(self: &Sha256Air, cols: &mut Sha256RoundCols) { + pub fn generate_default_row( + self: &Sha256StepHelper, + cols: &mut Sha256RoundCols, + ) { cols.flags.is_round_row = F::ZERO; cols.flags.is_first_4_rows = F::ZERO; cols.flags.is_digest_row = F::ZERO; @@ -353,9 +340,7 @@ impl Sha256Air { cols.message_schedule.carry_or_buffer = [[F::ZERO; SHA256_WORD_U16S * 2]; SHA256_ROUNDS_PER_ROW]; - let hash = SHA256_H - .map(u32_into_limbs::) - .map(|x| x.map(F::from_canonical_u32)); + let hash = SHA256_H.map(u32_into_bits_field::); for i in 0..SHA256_ROUNDS_PER_ROW { cols.work_vars.a[i] = hash[SHA256_ROUNDS_PER_ROW - i - 1]; @@ -486,15 +471,16 @@ impl Sha256Air { } } +/// Generates a trace for a standalone SHA256 computation (currently only used for testing) /// `records` consists of pairs of `(input_block, is_last_block)`. pub fn generate_trace( - sub_air: &Sha256Air, - bitwise_lookup_chip: SharedBitwiseOperationLookupChip<8>, + step: &Sha256StepHelper, + bitwise_lookup_chip: &BitwiseOperationLookupChip<8>, + width: usize, records: Vec<([u8; SHA256_BLOCK_U8S], bool)>, ) -> RowMajorMatrix { let non_padded_height = records.len() * SHA256_ROWS_PER_BLOCK; let height = next_power_of_two_or_zero(non_padded_height); - let width = >::width(sub_air); let mut values = F::zero_vec(height * width); struct BlockContext { @@ -522,7 +508,7 @@ pub fn generate_trace( prev_hash = SHA256_H; } else { local_block_idx += 1; - prev_hash = Sha256Air::get_block_hash(&prev_hash, input); + Sha256StepHelper::get_block_hash(&mut prev_hash, input); } } // first pass @@ -542,17 +528,16 @@ pub fn generate_trace( input[(i + 1) * SHA256_WORD_U8S - j - 1] as u32 })) }); - sub_air.generate_block_trace( + step.generate_block_trace( block, width, 0, &input_words, - bitwise_lookup_chip.clone(), + bitwise_lookup_chip, &prev_hash, is_last_block, global_block_idx, local_block_idx, - &[[F::ZERO; 16]; 4], ); }); // second pass: padding rows @@ -560,14 +545,14 @@ pub fn generate_trace( .par_chunks_mut(width) .for_each(|row| { let cols: &mut Sha256RoundCols = row.borrow_mut(); - sub_air.generate_default_row(cols); + step.generate_default_row(cols); }); // second pass: non-padding rows values[width..] .par_chunks_mut(width * SHA256_ROWS_PER_BLOCK) .take(non_padded_height / SHA256_ROWS_PER_BLOCK) .for_each(|chunk| { - sub_air.generate_missing_cells(chunk, width, 0); + step.generate_missing_cells(chunk, width, 0); }); RowMajorMatrix::new(values, width) } diff --git a/crates/circuits/sha256-air/src/utils.rs b/crates/circuits/sha256-air/src/utils.rs index abf8b6e7f2..1d56314b13 100644 --- a/crates/circuits/sha256-air/src/utils.rs +++ b/crates/circuits/sha256-air/src/utils.rs @@ -74,10 +74,14 @@ pub const SHA256_H: [u32; 8] = [ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, ]; -/// Convert a u32 into a list of limbs in little endian -pub fn u32_into_limbs(num: u32) -> [u32; NUM_LIMBS] { - let limb_bits = 32 / NUM_LIMBS; - array::from_fn(|i| (num >> (limb_bits * i)) & ((1 << limb_bits) - 1)) +/// Convert a u32 into a list of bits in little endian then convert each bit into a field element +pub fn u32_into_bits_field(num: u32) -> [F; SHA256_WORD_BITS] { + array::from_fn(|i| F::from_bool((num >> i) & 1 == 1)) +} + +/// Convert a u32 into a an array of 2 16-bit limbs in little endian +pub fn u32_into_u16s(num: u32) -> [u32; 2] { + [num & 0xffff, num >> 16] } /// Convert a list of limbs in little endian into a u32 diff --git a/crates/vm/src/system/memory/paged_vec.rs b/crates/vm/src/system/memory/paged_vec.rs index 54c2f44023..808c8b5d53 100644 --- a/crates/vm/src/system/memory/paged_vec.rs +++ b/crates/vm/src/system/memory/paged_vec.rs @@ -1,4 +1,10 @@ -use std::{fmt::Debug, marker::PhantomData, mem::MaybeUninit, ptr}; +use std::{ + alloc::{alloc, Layout}, + fmt::Debug, + marker::PhantomData, + mem::MaybeUninit, + ptr, +}; use itertools::{zip_eq, Itertools}; use openvm_instructions::exe::SparseMemoryImage; @@ -30,7 +36,7 @@ impl PagedVec { // into the memory pointed to by `dst`. If the relevant page is not // initialized, fills that portion with `0u8`. #[inline] - fn read_range_generic(&self, start: usize, len: usize, dst: *mut u8) { + pub fn read_range_generic(&self, start: usize, len: usize, dst: *mut u8) { let start_page = start / PAGE_SIZE; let end_page = (start + len - 1) / PAGE_SIZE; unsafe { @@ -65,7 +71,7 @@ impl PagedVec { // and then writes the new values into the underlying pages, // allocating pages (with defaults) if necessary. #[inline] - fn set_range_generic(&mut self, start: usize, len: usize, new: *const u8, dst: *mut u8) { + pub fn set_range_generic(&mut self, start: usize, len: usize, new: *const u8, dst: *mut u8) { let start_page = start / PAGE_SIZE; let end_page = (start + len - 1) / PAGE_SIZE; unsafe { @@ -324,6 +330,28 @@ impl AddressMap { .get((ptr as usize) * size_of::()) } + /// # Safety + /// - `T` **must** be the correct type for a single memory cell for `addr_space` + /// - Assumes `addr_space` is within the configured memory and not out of bounds + pub fn read_range_generic( + &self, + (addr_space, ptr): Address, + len: usize, + ) -> Vec { + let mut block: Vec = Vec::with_capacity(len); + unsafe { + self.paged_vecs + .get_unchecked((addr_space - self.as_offset) as usize) + .read_range_generic( + (ptr as usize) * size_of::(), + len * size_of::(), + block.as_mut_ptr() as *mut u8, + ); + block.set_len(len); + } + block + } + /// # Safety /// - `T` **must** be the correct type for a single memory cell for `addr_space` /// - Assumes `addr_space` is within the configured memory and not out of bounds diff --git a/extensions/keccak256/circuit/src/extension.rs b/extensions/keccak256/circuit/src/extension.rs index 86ddc5802c..e6101ef6da 100644 --- a/extensions/keccak256/circuit/src/extension.rs +++ b/extensions/keccak256/circuit/src/extension.rs @@ -5,7 +5,7 @@ use openvm_circuit::{ }, system::phantom::PhantomChip, }; -use openvm_circuit_derive::{AnyEnum, InstructionExecutor, VmConfig}; +use openvm_circuit_derive::{AnyEnum, InsExecutorE1, InstructionExecutor, VmConfig}; use openvm_circuit_primitives::bitwise_op_lookup::BitwiseOperationLookupBus; use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; use openvm_instructions::*; @@ -15,10 +15,14 @@ use openvm_rv32im_circuit::{ }; use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; +use std::result::Result; use strum::IntoEnumIterator; use crate::*; +// TODO: this should be decided after e2 execution +const MAX_INS_CAPACITY: usize = 1 << 22; + #[derive(Clone, Debug, VmConfig, derive_new::new, Serialize, Deserialize)] pub struct Keccak256Rv32Config { #[system] @@ -48,7 +52,7 @@ impl Default for Keccak256Rv32Config { #[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)] pub struct Keccak256; -#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)] +#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum, InsExecutorE1)] pub enum Keccak256Executor { Keccak256(KeccakVmChip), } @@ -68,11 +72,8 @@ impl VmExtension for Keccak256 { builder: &mut VmInventoryBuilder, ) -> Result, VmInventoryError> { let mut inventory = VmInventory::new(); - let SystemPort { - execution_bus, - program_bus, - memory_bridge, - } = builder.system_port(); + let pointer_max_bits = builder.system_config().memory_config.pointer_max_bits; + let bitwise_lu_chip = if let Some(&chip) = builder .find_chip::>() .first() @@ -84,16 +85,27 @@ impl VmExtension for Keccak256 { inventory.add_periphery_chip(chip.clone()); chip }; - let address_bits = builder.system_config().memory_config.pointer_max_bits; - let keccak_chip = KeccakVmChip::new( + let SystemPort { execution_bus, program_bus, memory_bridge, - address_bits, - bitwise_lu_chip, - Rv32KeccakOpcode::CLASS_OFFSET, - offline_memory, + } = builder.system_port(); + let keccak_chip = KeccakVmChip::new( + KeccakVmAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + bitwise_lu_chip.bus(), + pointer_max_bits, + Rv32KeccakOpcode::CLASS_OFFSET, + ), + KeccakVmStep::new( + bitwise_lu_chip.clone(), + Rv32KeccakOpcode::CLASS_OFFSET, + pointer_max_bits, + ), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor( keccak_chip, diff --git a/extensions/keccak256/circuit/src/lib.rs b/extensions/keccak256/circuit/src/lib.rs index 0284322c4d..1cfbe0b515 100644 --- a/extensions/keccak256/circuit/src/lib.rs +++ b/extensions/keccak256/circuit/src/lib.rs @@ -1,17 +1,10 @@ //! Stateful keccak256 hasher. Handles full keccak sponge (padding, absorb, keccak-f) on //! variable length inputs read from VM memory. -use std::{ - array::from_fn, - cmp::min, - sync::{Arc, Mutex}, -}; use openvm_circuit_primitives::bitwise_op_lookup::SharedBitwiseOperationLookupChip; use openvm_stark_backend::p3_field::PrimeField32; -use serde::{Deserialize, Serialize}; -use serde_big_array::BigArray; + use tiny_keccak::{Hasher, Keccak}; -use utils::num_keccak_f; pub mod air; pub mod columns; @@ -26,17 +19,16 @@ mod tests; pub use air::KeccakVmAir; use openvm_circuit::{ - arch::{ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InstructionExecutor}, - system::{ - memory::{offline_checker::MemoryBridge, MemoryController, OfflineMemory, RecordId}, - program::ProgramBus, - }, + arch::{ExecutionBridge, NewVmChipWrapper, Result, StepExecutorE1, VmStateMut}, + system::memory::online::GuestMemory, }; use openvm_instructions::{ - instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_REGISTER_NUM_LIMBS, LocalOpcode, + instruction::Instruction, + riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}, + LocalOpcode, }; use openvm_keccak256_transpiler::Rv32KeccakOpcode; -use openvm_rv32im_circuit::adapters::read_rv32_register; +use openvm_rv32im_circuit::adapters::{memory_write, new_read_rv32_register}; // ==== Constants for register/memory adapter ==== /// Register reads to get dst, src, len @@ -69,75 +61,35 @@ pub const KECCAK_DIGEST_BYTES: usize = 32; /// Number of 64-bit digest limbs. pub const KECCAK_DIGEST_U64S: usize = KECCAK_DIGEST_BYTES / 8; -pub struct KeccakVmChip { - pub air: KeccakVmAir, - /// IO and memory data necessary for each opcode call - pub records: Vec>, - pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip<8>, - - offset: usize, +pub type KeccakVmChip = NewVmChipWrapper; - offline_memory: Arc>>, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct KeccakRecord { - pub pc: F, - pub dst_read: RecordId, - pub src_read: RecordId, - pub len_read: RecordId, - pub input_blocks: Vec, - pub digest_writes: [RecordId; KECCAK_DIGEST_WRITES], -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct KeccakInputBlock { - /// Memory reads for non-padding bytes in this block. Length is at most [KECCAK_RATE_BYTES / - /// KECCAK_WORD_SIZE]. - pub reads: Vec, - /// Index in `reads` of the memory read for < KECCAK_WORD_SIZE bytes, if any. - pub partial_read_idx: Option, - /// Bytes with padding. Can be derived from `bytes_read` but we store for convenience. - #[serde(with = "BigArray")] - pub padded_bytes: [u8; KECCAK_RATE_BYTES], - pub remaining_len: usize, - pub src: usize, - pub is_new_start: bool, +//#[derive(derive_new::new)] +pub struct KeccakVmStep { + pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip<8>, + pub offset: usize, + pub pointer_max_bits: usize, } -impl KeccakVmChip { +impl KeccakVmStep { pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - address_bits: usize, bitwise_lookup_chip: SharedBitwiseOperationLookupChip<8>, offset: usize, - offline_memory: Arc>>, + pointer_max_bits: usize, ) -> Self { Self { - air: KeccakVmAir::new( - ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - bitwise_lookup_chip.bus(), - address_bits, - offset, - ), bitwise_lookup_chip, - records: Vec::new(), offset, - offline_memory, + pointer_max_bits, } } } -impl InstructionExecutor for KeccakVmChip { - fn execute( +impl StepExecutorE1 for KeccakVmStep { + fn execute_e1( &mut self, - memory: &mut MemoryController, + state: VmStateMut, instruction: &Instruction, - from_state: ExecutionState, - ) -> Result, ExecutionError> { + ) -> Result<()> { let &Instruction { opcode, a, @@ -147,136 +99,27 @@ impl InstructionExecutor for KeccakVmChip { e, .. } = instruction; - let local_opcode = Rv32KeccakOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - debug_assert_eq!(local_opcode, Rv32KeccakOpcode::KECCAK256); + let d = d.as_canonical_u32(); + let e = e.as_canonical_u32(); + debug_assert_eq!(opcode, Rv32KeccakOpcode::KECCAK256.global_opcode()); + debug_assert_eq!(d, RV32_REGISTER_AS); + debug_assert_eq!(e, RV32_MEMORY_AS); - let mut timestamp_delta = 3; - let (dst_read, dst) = read_rv32_register(memory, d, a); - let (src_read, src) = read_rv32_register(memory, d, b); - let (len_read, len) = read_rv32_register(memory, d, c); - #[cfg(debug_assertions)] - { - assert!(dst < (1 << self.air.ptr_max_bits)); - assert!(src < (1 << self.air.ptr_max_bits)); - assert!(len < (1 << self.air.ptr_max_bits)); - } + let dst = new_read_rv32_register(state.memory, d, a.as_canonical_u32()); + let src = new_read_rv32_register(state.memory, d, b.as_canonical_u32()); + let len = new_read_rv32_register(state.memory, d, c.as_canonical_u32()); - let mut remaining_len = len as usize; - let num_blocks = num_keccak_f(remaining_len); - let mut input_blocks = Vec::with_capacity(num_blocks); let mut hasher = Keccak::v256(); - let mut src = src as usize; - - for block_idx in 0..num_blocks { - if block_idx != 0 { - memory.increment_timestamp_by(KECCAK_REGISTER_READS as u32); - timestamp_delta += KECCAK_REGISTER_READS as u32; - } - let mut reads = Vec::with_capacity(KECCAK_RATE_BYTES); - - let mut partial_read_idx = None; - let mut bytes = [0u8; KECCAK_RATE_BYTES]; - for i in (0..KECCAK_RATE_BYTES).step_by(KECCAK_WORD_SIZE) { - if i < remaining_len { - let read = memory - .read::(e, F::from_canonical_usize(src + i)); - - let chunk = read.1; - let copy_len = min(KECCAK_WORD_SIZE, remaining_len - i); - if copy_len != KECCAK_WORD_SIZE { - partial_read_idx = Some(reads.len()); - } - bytes[i..i + copy_len].copy_from_slice(&chunk[..copy_len]); - reads.push(read.0); - } else { - memory.increment_timestamp(); - } - timestamp_delta += 1; - } - let mut block = KeccakInputBlock { - reads, - partial_read_idx, - padded_bytes: bytes, - remaining_len, - src, - is_new_start: block_idx == 0, - }; - if block_idx != num_blocks - 1 { - src += KECCAK_RATE_BYTES; - remaining_len -= KECCAK_RATE_BYTES; - hasher.update(&block.padded_bytes); - } else { - // handle padding here since it is convenient - debug_assert!(remaining_len < KECCAK_RATE_BYTES); - hasher.update(&block.padded_bytes[..remaining_len]); + let message: Vec = state + .memory + .memory + .read_range_generic((e, src), len as usize); + hasher.update(&message); - if remaining_len == KECCAK_RATE_BYTES - 1 { - block.padded_bytes[remaining_len] = 0b1000_0001; - } else { - block.padded_bytes[remaining_len] = 0x01; - block.padded_bytes[KECCAK_RATE_BYTES - 1] = 0x80; - } - } - input_blocks.push(block); - } let mut output = [0u8; 32]; hasher.finalize(&mut output); - let dst = dst as usize; - let digest_writes: [_; KECCAK_DIGEST_WRITES] = from_fn(|i| { - timestamp_delta += 1; - memory - .write::( - e, - F::from_canonical_usize(dst + i * KECCAK_WORD_SIZE), - &from_fn(|j| output[i * KECCAK_WORD_SIZE + j]), - ) - .0 - }); - tracing::trace!("[runtime] keccak256 output: {:?}", output); - - let record = KeccakRecord { - pc: F::from_canonical_u32(from_state.pc), - dst_read, - src_read, - len_read, - input_blocks, - digest_writes, - }; - - // Add the events to chip state for later trace generation usage - self.records.push(record); - - // NOTE: Check this is consistent with KeccakVmAir::timestamp_change (we don't use it to - // avoid unnecessary conversions here) - let total_timestamp_delta = - len + (KECCAK_REGISTER_READS + KECCAK_ABSORB_READS + KECCAK_DIGEST_WRITES) as u32; - memory.increment_timestamp_by(total_timestamp_delta - timestamp_delta); - - Ok(ExecutionState { - pc: from_state.pc + DEFAULT_PC_STEP, - timestamp: from_state.timestamp + total_timestamp_delta, - }) - } - - fn get_opcode_name(&self, _: usize) -> String { - "KECCAK256".to_string() - } -} - -impl Default for KeccakInputBlock { - fn default() -> Self { - // Padding for empty byte array so padding constraints still hold - let mut padded_bytes = [0u8; KECCAK_RATE_BYTES]; - padded_bytes[0] = 0x01; - *padded_bytes.last_mut().unwrap() = 0x80; - Self { - padded_bytes, - partial_read_idx: None, - remaining_len: 0, - is_new_start: true, - reads: Vec::new(), - src: 0, - } + memory_write(state.memory, e, dst, &output); + Ok(()) } } diff --git a/extensions/keccak256/circuit/src/tests.rs b/extensions/keccak256/circuit/src/tests.rs index 65a34491b8..221fdb144d 100644 --- a/extensions/keccak256/circuit/src/tests.rs +++ b/extensions/keccak256/circuit/src/tests.rs @@ -1,11 +1,17 @@ -use std::borrow::BorrowMut; +use std::{array, borrow::BorrowMut}; use hex::FromHex; -use openvm_circuit::arch::testing::{VmChipTestBuilder, VmChipTester, BITWISE_OP_LOOKUP_BUS}; +use openvm_circuit::arch::testing::{ + memory::gen_pointer, VmChipTestBuilder, VmChipTester, BITWISE_OP_LOOKUP_BUS, +}; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, }; -use openvm_instructions::{instruction::Instruction, LocalOpcode}; +use openvm_instructions::{ + instruction::Instruction, + riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}, + LocalOpcode, +}; use openvm_keccak256_transpiler::Rv32KeccakOpcode; use openvm_stark_backend::{ p3_field::FieldAlgebra, utils::disable_debug_builder, verifier::VerificationError, @@ -18,35 +24,58 @@ use p3_keccak_air::NUM_ROUNDS; use rand::Rng; use tiny_keccak::Hasher; +use crate::{KeccakVmAir, KeccakVmStep}; + use super::{columns::KeccakVmCols, utils::num_keccak_f, KeccakVmChip}; type F = BabyBear; +const MAX_INS_CAPACITY: usize = 4096; + +fn create_test_chips( + tester: &mut VmChipTestBuilder, +) -> (KeccakVmChip, SharedBitwiseOperationLookupChip<8>) { + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let bitwise_chip = SharedBitwiseOperationLookupChip::<8>::new(bitwise_bus); + let chip = KeccakVmChip::new( + KeccakVmAir::new( + tester.execution_bridge(), + tester.memory_bridge(), + bitwise_bus, + tester.address_bits(), + Rv32KeccakOpcode::CLASS_OFFSET, + ), + KeccakVmStep::new( + bitwise_chip.clone(), + Rv32KeccakOpcode::CLASS_OFFSET, + tester.address_bits(), + ), + MAX_INS_CAPACITY, + tester.memory_helper(), + ); + (chip, bitwise_chip) +} + // io is vector of (input, expected_output, prank_output) where prank_output is Some if the trace // will be replaced #[allow(clippy::type_complexity)] fn build_keccak256_test( io: Vec<(Vec, Option<[u8; 32]>, Option<[u8; 32]>)>, ) -> VmChipTester { - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::<8>::new(bitwise_bus); - + let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - let mut chip = KeccakVmChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.address_bits(), - bitwise_chip.clone(), - Rv32KeccakOpcode::CLASS_OFFSET, - tester.offline_memory_mutex_arc(), - ); + let (mut chip, bitwise_chip) = create_test_chips(&mut tester); - let mut dst = 0; - let src = 0; + let max_mem_ptr = 1 << (tester.address_bits() - 3); + let mut dst = rng.gen_range(0..max_mem_ptr) << 2; + let src = rng.gen_range(0..max_mem_ptr) << 2; for (input, expected_output, _) in &io { - let [a, b, c] = [0, 4, 8]; // space apart for register limbs - let [d, e] = [1, 2]; + let [a, b, c] = [ + gen_pointer(&mut rng, 4), + gen_pointer(&mut rng, 4), + gen_pointer(&mut rng, 4), + ]; // space apart for register limbs + let [d, e] = [RV32_REGISTER_AS as usize, RV32_MEMORY_AS as usize]; tester.write(d, a, (dst as u32).to_le_bytes().map(F::from_canonical_u8)); tester.write(d, b, (src as u32).to_le_bytes().map(F::from_canonical_u8)); @@ -55,9 +84,15 @@ fn build_keccak256_test( c, (input.len() as u32).to_le_bytes().map(F::from_canonical_u8), ); - for (i, byte) in input.iter().enumerate() { - tester.write_cell(e, src + i, F::from_canonical_u8(*byte)); - } + + input.chunks(4).enumerate().for_each(|(i, chunk)| { + let chunk: [&u8; 4] = array::from_fn(|i| chunk.get(i).unwrap_or(&0)); + tester.write( + 2, + src as usize + i * 4, + chunk.map(|&x| F::from_canonical_u8(x)), + ); + }); tester.execute( &mut chip, @@ -71,13 +106,15 @@ fn build_keccak256_test( ), ); if let Some(output) = expected_output { - for (i, byte) in output.iter().enumerate() { - assert_eq!(tester.read_cell(e, dst + i), F::from_canonical_u8(*byte)); - } + assert_eq!( + output.map(F::from_canonical_u8), + tester.read::<32>(e, dst as usize) + ); } // shift dst to not deal with timestamps for pranking dst += 32; } + let mut tester = tester.build().load(chip).load(bitwise_chip).finalize(); let keccak_trace = tester.air_proof_inputs[2] @@ -152,7 +189,7 @@ fn test_keccak256_positive_kat_vectors() { let output = Vec::from_hex(output).unwrap(); io.push((input, Some(output.try_into().unwrap()), None)); } - let tester = build_keccak256_test(io); + tester.simple_test().expect("Verification failed"); } diff --git a/extensions/keccak256/circuit/src/trace.rs b/extensions/keccak256/circuit/src/trace.rs index c314c38eac..0bb2d28e23 100644 --- a/extensions/keccak256/circuit/src/trace.rs +++ b/extensions/keccak256/circuit/src/trace.rs @@ -1,150 +1,216 @@ -use std::{array::from_fn, borrow::BorrowMut, sync::Arc}; +use std::{array::from_fn, borrow::BorrowMut, cmp::min}; -use openvm_circuit::system::memory::RecordId; -use openvm_instructions::riscv::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; +use openvm_circuit::{ + arch::{Result, TraceStep, VmStateMut}, + system::memory::{online::TracingMemory, MemoryAuxColsFactory}, +}; +use openvm_instructions::{ + instruction::Instruction, + program::DEFAULT_PC_STEP, + riscv::{RV32_CELL_BITS, RV32_MEMORY_AS, RV32_REGISTER_AS, RV32_REGISTER_NUM_LIMBS}, + LocalOpcode, +}; +use openvm_keccak256_transpiler::Rv32KeccakOpcode; +use openvm_rv32im_circuit::adapters::{tracing_read, tracing_write}; use openvm_stark_backend::{ - config::{StarkGenericConfig, Val}, - p3_air::BaseAir, - p3_field::{FieldAlgebra, PrimeField32}, - p3_matrix::{dense::RowMajorMatrix, Matrix}, - p3_maybe_rayon::prelude::*, - prover::types::AirProofInput, - rap::get_air_name, - AirRef, Chip, ChipUsageGetter, + p3_field::PrimeField32, p3_matrix::dense::RowMajorMatrix, p3_maybe_rayon::prelude::*, }; use p3_keccak_air::{ generate_trace_rows, NUM_KECCAK_COLS as NUM_KECCAK_PERM_COLS, NUM_ROUNDS, U64_LIMBS, }; -use tiny_keccak::keccakf; +use tiny_keccak::{keccakf, Hasher, Keccak}; + +use crate::{columns::NUM_KECCAK_VM_COLS, utils::num_keccak_f, KeccakVmStep, KECCAK_WORD_SIZE}; use super::{ - columns::{KeccakInstructionCols, KeccakVmCols}, - KeccakVmChip, KECCAK_ABSORB_READS, KECCAK_DIGEST_WRITES, KECCAK_RATE_BYTES, KECCAK_RATE_U16S, + columns::KeccakVmCols, KECCAK_ABSORB_READS, KECCAK_DIGEST_WRITES, KECCAK_RATE_BYTES, KECCAK_REGISTER_READS, NUM_ABSORB_ROUNDS, }; -impl Chip for KeccakVmChip> -where - Val: PrimeField32, -{ - fn air(&self) -> AirRef { - Arc::new(self.air) - } +impl TraceStep for KeccakVmStep { + fn execute( + &mut self, + state: VmStateMut, CTX>, + instruction: &Instruction, + trace: &mut [F], + trace_offset: &mut usize, + width: usize, + ) -> Result<()> { + let &Instruction { + opcode, + a, + b, + c, + d, + e, + .. + } = instruction; + let d = d.as_canonical_u32(); + let e = e.as_canonical_u32(); + debug_assert_eq!(opcode, Rv32KeccakOpcode::KECCAK256.global_opcode()); + debug_assert_eq!(d, RV32_REGISTER_AS); + debug_assert_eq!(e, RV32_MEMORY_AS); - fn generate_air_proof_input(self) -> AirProofInput { - let trace_width = self.trace_width(); - let records = self.records; - let total_num_blocks: usize = records.iter().map(|r| r.input_blocks.len()).sum(); - let mut states = Vec::with_capacity(total_num_blocks); - let mut instruction_blocks = Vec::with_capacity(total_num_blocks); - let memory = self.offline_memory.lock().unwrap(); + let trace = &mut trace[*trace_offset..]; + let (dst, mut src, mut remaining_len) = { + let cols: &mut KeccakVmCols = trace[..width].borrow_mut(); + cols.instruction.start_timestamp = F::from_canonical_u32(state.memory.timestamp()); - #[derive(Clone)] - struct StateDiff { - /// hi-byte of pre-state - pre_hi: [u8; KECCAK_RATE_U16S], - /// hi-byte of post-state - post_hi: [u8; KECCAK_RATE_U16S], - /// if first block - register_reads: Option<[RecordId; KECCAK_REGISTER_READS]>, - /// if last block - digest_writes: Option<[RecordId; KECCAK_DIGEST_WRITES]>, - } + let a = a.as_canonical_u32(); + let b = b.as_canonical_u32(); + let c = c.as_canonical_u32(); + let dst = tracing_read(state.memory, d, a, &mut cols.mem_oc.register_aux[0]); + let src = tracing_read(state.memory, d, b, &mut cols.mem_oc.register_aux[1]); + let len = tracing_read(state.memory, d, c, &mut cols.mem_oc.register_aux[2]); + ( + dst, + u32::from_le_bytes(src), + u32::from_le_bytes(len) as usize, + ) + }; + + // Due to the AIR constraints, the final memory timestamp should be the following: + let final_timestamp = state.memory.timestamp() + + (remaining_len + KECCAK_ABSORB_READS + KECCAK_DIGEST_WRITES) as u32; + let num_blocks = num_keccak_f(remaining_len); + let mut hasher = Keccak::v256(); + + trace + .chunks_mut(width * NUM_ROUNDS) + .enumerate() + .take(num_blocks) + .for_each(|(block_idx, chunk)| { + let cols: &mut KeccakVmCols = chunk[..NUM_KECCAK_VM_COLS].borrow_mut(); + if block_idx != 0 { + cols.instruction.start_timestamp = + F::from_canonical_u32(state.memory.timestamp()); - impl Default for StateDiff { - fn default() -> Self { - Self { - pre_hi: [0; KECCAK_RATE_U16S], - post_hi: [0; KECCAK_RATE_U16S], - register_reads: None, - digest_writes: None, + state + .memory + .increment_timestamp_by(KECCAK_REGISTER_READS as u32); } - } + cols.instruction.dst_ptr = a; + cols.instruction.src_ptr = b; + cols.instruction.len_ptr = c; + cols.instruction.dst = dst.map(F::from_canonical_u8); + cols.instruction + .src_limbs + .copy_from_slice(&src.to_le_bytes().map(F::from_canonical_u8)[1..]); + cols.instruction.len_limbs.copy_from_slice( + &(remaining_len as u32) + .to_le_bytes() + .map(F::from_canonical_u8)[1..], + ); + cols.instruction.src = F::from_canonical_u32(src); + cols.instruction.remaining_len = F::from_canonical_usize(remaining_len); + cols.instruction.pc = F::from_canonical_u32(*state.pc); + cols.sponge.is_new_start = F::from_bool(block_idx == 0); + + for i in (0..KECCAK_RATE_BYTES).step_by(KECCAK_WORD_SIZE) { + if i < remaining_len { + let read = tracing_read::<_, KECCAK_WORD_SIZE>( + state.memory, + e, + src + i as u32, + &mut cols.mem_oc.absorb_reads[i / KECCAK_WORD_SIZE], + ); + let copy_len = min(KECCAK_WORD_SIZE, remaining_len - i); + hasher.update(&read[..copy_len]); + cols.sponge.block_bytes[i..i + copy_len] + .copy_from_slice(&read.map(F::from_canonical_u8)[..copy_len]); + if copy_len != KECCAK_WORD_SIZE { + cols.mem_oc + .partial_block + .copy_from_slice(&read.map(F::from_canonical_u8)[1..]); + } + } else { + state.memory.increment_timestamp(); + } + } + if block_idx == num_blocks - 1 { + if remaining_len == KECCAK_RATE_BYTES - 1 { + cols.sponge.block_bytes[remaining_len] = F::from_canonical_u32(0b1000_0001); + } else { + cols.sponge.block_bytes[remaining_len] = F::from_canonical_u32(0x01); + cols.sponge.block_bytes[KECCAK_RATE_BYTES - 1] = + F::from_canonical_u32(0x80); + } + } else { + src += KECCAK_RATE_BYTES as u32; + remaining_len -= KECCAK_RATE_BYTES; + } + }); + + let last_row_offset = (num_blocks * NUM_ROUNDS - 1) * width; + let last_row: &mut KeccakVmCols = + trace[last_row_offset..last_row_offset + NUM_KECCAK_VM_COLS].borrow_mut(); + let mut digest = [0u8; 32]; + hasher.finalize(&mut digest); + for (i, word) in digest.chunks_exact(KECCAK_WORD_SIZE).enumerate() { + tracing_write::<_, KECCAK_WORD_SIZE>( + state.memory, + e, + u32::from_le_bytes(dst) + (i * KECCAK_WORD_SIZE) as u32, + word.try_into().unwrap(), + &mut last_row.mem_oc.digest_writes[i], + ); } - // prepare the states - let mut state: [u64; 25]; - for record in records { - let dst_read = memory.record_by_id(record.dst_read); - let src_read = memory.record_by_id(record.src_read); - let len_read = memory.record_by_id(record.len_read); + state.memory.timestamp = final_timestamp; + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *trace_offset += num_blocks * NUM_ROUNDS * width; + Ok(()) + } - state = [0u64; 25]; - let src_limbs: [_; RV32_REGISTER_NUM_LIMBS - 1] = src_read.data_slice() - [1..RV32_REGISTER_NUM_LIMBS] - .try_into() - .unwrap(); - let len_limbs: [_; RV32_REGISTER_NUM_LIMBS - 1] = len_read.data_slice() - [1..RV32_REGISTER_NUM_LIMBS] - .try_into() - .unwrap(); - let mut instruction = KeccakInstructionCols { - pc: record.pc, - is_enabled: Val::::ONE, - is_enabled_first_round: Val::::ZERO, - start_timestamp: Val::::from_canonical_u32(dst_read.timestamp), - dst_ptr: dst_read.pointer, - src_ptr: src_read.pointer, - len_ptr: len_read.pointer, - dst: dst_read.data_slice().try_into().unwrap(), - src_limbs, - src: Val::::from_canonical_usize(record.input_blocks[0].src), - len_limbs, - remaining_len: Val::::from_canonical_usize( - record.input_blocks[0].remaining_len, - ), - }; - let num_blocks = record.input_blocks.len(); - for (idx, block) in record.input_blocks.into_iter().enumerate() { + fn fill_trace( + &self, + mem_helper: &MemoryAuxColsFactory, + trace: &mut [F], + width: usize, + rows_used: usize, + ) where + Self: Send + Sync, + F: Send + Sync, + { + let num_blocks = rows_used.div_ceil(NUM_ROUNDS); + let mut states = Vec::with_capacity(num_blocks); + let mut state = [0u64; 25]; + trace + .chunks_mut(width * NUM_ROUNDS) + .take(num_blocks) + .for_each(|chunk| { + let cols: &mut KeccakVmCols = chunk[..NUM_KECCAK_VM_COLS].borrow_mut(); + if cols.sponge.is_new_start.is_one() { + // a new instruction is starting + state = [0u64; 25]; + } // absorb - for (bytes, s) in block.padded_bytes.chunks_exact(8).zip(state.iter_mut()) { + for (bytes, s) in cols + .sponge + .block_bytes + .chunks_exact(8) + .zip(state.iter_mut()) + { // u64 <-> bytes conversion is little-endian for (i, &byte) in bytes.iter().enumerate() { + let byte = byte.as_canonical_u32(); let s_byte = (*s >> (i * 8)) as u8; // Update bitwise lookup (i.e. xor) chip state: order matters! - if idx != 0 { - self.bitwise_lookup_chip - .request_xor(byte as u32, s_byte as u32); + if cols.sponge.is_new_start.is_zero() { + self.bitwise_lookup_chip.request_xor(byte, s_byte as u32); } *s ^= (byte as u64) << (i * 8); } } - let pre_hi: [u8; KECCAK_RATE_U16S] = - from_fn(|i| (state[i / U64_LIMBS] >> ((i % U64_LIMBS) * 16 + 8)) as u8); states.push(state); keccakf(&mut state); - let post_hi: [u8; KECCAK_RATE_U16S] = - from_fn(|i| (state[i / U64_LIMBS] >> ((i % U64_LIMBS) * 16 + 8)) as u8); - // Range check the final state - if idx == num_blocks - 1 { - for s in state.into_iter().take(NUM_ABSORB_ROUNDS) { - for s_byte in s.to_le_bytes() { - self.bitwise_lookup_chip.request_xor(0, s_byte as u32); - } - } - } - let register_reads = - (idx == 0).then_some([record.dst_read, record.src_read, record.len_read]); - let digest_writes = (idx == num_blocks - 1).then_some(record.digest_writes); - let diff = StateDiff { - pre_hi, - post_hi, - register_reads, - digest_writes, - }; - instruction_blocks.push((instruction, diff, block)); - instruction.remaining_len -= Val::::from_canonical_usize(KECCAK_RATE_BYTES); - instruction.src += Val::::from_canonical_usize(KECCAK_RATE_BYTES); - instruction.start_timestamp += - Val::::from_canonical_usize(KECCAK_REGISTER_READS + KECCAK_ABSORB_READS); - } - } + }); // We need to transpose state matrices due to a plonky3 issue: https://github.com/Plonky3/Plonky3/issues/672 // Note: the fix for this issue will be a commit after the major Field crate refactor PR https://github.com/Plonky3/Plonky3/pull/640 // which will require a significant refactor to switch to. let p3_states = states - .into_iter() + .par_iter() .map(|state| { // transpose of 5x5 matrix from_fn(|i| { @@ -154,122 +220,147 @@ where }) }) .collect(); - let p3_keccak_trace: RowMajorMatrix> = generate_trace_rows(p3_states, 0); - let num_rows = p3_keccak_trace.height(); - // Every `NUM_ROUNDS` rows corresponds to one input block - let num_blocks = num_rows.div_ceil(NUM_ROUNDS); - // Resize with dummy `is_enabled = 0` - instruction_blocks.resize(num_blocks, Default::default()); - - let aux_cols_factory = memory.aux_cols_factory(); - - // Use unsafe alignment so we can parallelly write to the matrix - let mut trace = - RowMajorMatrix::new(Val::::zero_vec(num_rows * trace_width), trace_width); - let limb_shift_bits = RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.air.ptr_max_bits; + let p3_keccak_trace: RowMajorMatrix = generate_trace_rows(p3_states, 0); trace - .values - .par_chunks_mut(trace_width * NUM_ROUNDS) + .par_chunks_mut(width * NUM_ROUNDS) .zip( p3_keccak_trace .values .par_chunks(NUM_KECCAK_PERM_COLS * NUM_ROUNDS), ) - .zip(instruction_blocks.into_par_iter()) - .for_each(|((rows, p3_keccak_mat), (instruction, diff, block))| { - let height = rows.len() / trace_width; - for (row, p3_keccak_row) in rows - .chunks_exact_mut(trace_width) - .zip(p3_keccak_mat.chunks_exact(NUM_KECCAK_PERM_COLS)) + .enumerate() + .for_each(|(block_idx, (block, p3_keccak_block))| { + // let cols: &mut KeccakVmCols = block[..NUM_KECCAK_VM_COLS].borrow_mut(); + if block_idx >= num_blocks { + // fill in a dummy row + block + .par_chunks_mut(width) + .zip(p3_keccak_block.par_chunks_exact(NUM_KECCAK_PERM_COLS)) + .for_each(|(row, p3_keccak_row)| { + row[..NUM_KECCAK_PERM_COLS].copy_from_slice(p3_keccak_row); + let cols: &mut KeccakVmCols = row.borrow_mut(); + cols.sponge.block_bytes[0] = F::ONE; + cols.sponge.block_bytes[KECCAK_RATE_BYTES - 1] = + F::from_canonical_u32(0x80); + cols.sponge.is_padding_byte[0..KECCAK_RATE_BYTES].fill(F::ONE); + }); + + // The first row of the `dummy` block should have `is_new_start = F::ONE` + let first_dummy_row: &mut KeccakVmCols = block[..width].borrow_mut(); + first_dummy_row.sponge.is_new_start = F::ONE; + return; + } + + // the first row is treated differently + let (first_row, block) = block.split_at_mut(width); + first_row[..NUM_KECCAK_PERM_COLS] + .copy_from_slice(&p3_keccak_block[..NUM_KECCAK_PERM_COLS]); + let first_row: &mut KeccakVmCols = first_row.borrow_mut(); + first_row.instruction.is_enabled = F::ONE; + let remaining_len = first_row.instruction.remaining_len.as_canonical_u32() as usize; + for i in remaining_len..KECCAK_RATE_BYTES { + first_row.sponge.is_padding_byte[i] = F::ONE; + } + + for (row, p3_keccak_row) in block + .chunks_exact_mut(width) + .zip(p3_keccak_block.chunks_exact(NUM_KECCAK_PERM_COLS).skip(1)) { // Safety: `KeccakPermCols` **must** be the first field in `KeccakVmCols` row[..NUM_KECCAK_PERM_COLS].copy_from_slice(p3_keccak_row); - let row_mut: &mut KeccakVmCols> = row.borrow_mut(); - row_mut.instruction = instruction; + let cols: &mut KeccakVmCols = row.borrow_mut(); - row_mut.sponge.block_bytes = - block.padded_bytes.map(Val::::from_canonical_u8); - if let Some(partial_read_idx) = block.partial_read_idx { - let partial_read = memory.record_by_id(block.reads[partial_read_idx]); - row_mut - .mem_oc - .partial_block - .copy_from_slice(&partial_read.data_slice()[1..]); - } - for (i, is_padding) in row_mut.sponge.is_padding_byte.iter_mut().enumerate() { - *is_padding = Val::::from_bool(i >= block.remaining_len); - } + cols.instruction = first_row.instruction; + cols.sponge.block_bytes = first_row.sponge.block_bytes; + cols.sponge.is_padding_byte = first_row.sponge.is_padding_byte; + cols.mem_oc.partial_block = first_row.mem_oc.partial_block; } - let first_row: &mut KeccakVmCols> = rows[..trace_width].borrow_mut(); - first_row.sponge.is_new_start = Val::::from_bool(block.is_new_start); - first_row.sponge.state_hi = diff.pre_hi.map(Val::::from_canonical_u8); + + let (_, last_row) = block.split_at_mut(width * (NUM_ROUNDS - 2)); + let last_row: &mut KeccakVmCols = last_row.borrow_mut(); + first_row.instruction.is_enabled_first_round = first_row.instruction.is_enabled; - // Make memory access aux columns. Any aux column not explicitly defined defaults to - // all 0s - if let Some(register_reads) = diff.register_reads { - let need_range_check = [ - ®ister_reads[0], // dst - ®ister_reads[1], // src - ®ister_reads[2], // len - ®ister_reads[2], - ] - .map(|r| { - memory - .record_by_id(*r) - .data_slice() - .last() - .unwrap() - .as_canonical_u32() - }); - for bytes in need_range_check.chunks(2) { - self.bitwise_lookup_chip.request_range( - bytes[0] << limb_shift_bits, - bytes[1] << limb_shift_bits, - ); - } - for (i, id) in register_reads.into_iter().enumerate() { - aux_cols_factory.generate_read_aux( - memory.record_by_id(id), - &mut first_row.mem_oc.register_aux[i], + first_row.sponge.state_hi = from_fn(|i| { + F::from_canonical_u8( + (states[block_idx][i / U64_LIMBS] >> ((i % U64_LIMBS) * 16 + 8)) as u8, + ) + }); + + let start_timestamp = first_row.instruction.start_timestamp.as_canonical_u32(); + first_row + .mem_oc + .absorb_reads + .par_iter_mut() + .take(remaining_len.div_ceil(KECCAK_WORD_SIZE)) + .enumerate() + .for_each(|(i, read)| { + mem_helper.fill_from_prev( + start_timestamp + KECCAK_REGISTER_READS as u32 + i as u32, + read.as_mut(), ); - } - } - for (i, id) in block.reads.into_iter().enumerate() { - aux_cols_factory.generate_read_aux( - memory.record_by_id(id), - &mut first_row.mem_oc.absorb_reads[i], + }); + + // Check if the first row is a new start (e.g. register reads happened) + if first_row.sponge.is_new_start.is_one() { + let limb_shift_bits = + RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.pointer_max_bits; + + self.bitwise_lookup_chip.request_range( + first_row.instruction.dst[RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() + << limb_shift_bits, + first_row.instruction.src_limbs[RV32_REGISTER_NUM_LIMBS - 2] + .as_canonical_u32() + << limb_shift_bits, + ); + self.bitwise_lookup_chip.request_range( + first_row.instruction.len_limbs[RV32_REGISTER_NUM_LIMBS - 2] + .as_canonical_u32() + << limb_shift_bits, + first_row.instruction.len_limbs[RV32_REGISTER_NUM_LIMBS - 2] + .as_canonical_u32() + << limb_shift_bits, ); + first_row + .mem_oc + .register_aux + .par_iter_mut() + .enumerate() + .for_each(|(i, aux)| { + mem_helper.fill_from_prev(start_timestamp + i as u32, aux.as_mut()); + }); } - let last_row: &mut KeccakVmCols> = - rows[(height - 1) * trace_width..].borrow_mut(); - last_row.sponge.state_hi = diff.post_hi.map(Val::::from_canonical_u8); - last_row.inner.export = instruction.is_enabled - * Val::::from_bool(block.remaining_len < KECCAK_RATE_BYTES); - if let Some(digest_writes) = diff.digest_writes { - for (i, record_id) in digest_writes.into_iter().enumerate() { - let record = memory.record_by_id(record_id); - aux_cols_factory - .generate_write_aux(record, &mut last_row.mem_oc.digest_writes[i]); + let mut state = states[block_idx]; + keccakf(&mut state); + last_row.sponge.state_hi = from_fn(|i| { + F::from_canonical_u8((state[i / U64_LIMBS] >> ((i % U64_LIMBS) * 16 + 8)) as u8) + }); + last_row.inner.export = last_row.instruction.is_enabled + * F::from_bool(remaining_len < KECCAK_RATE_BYTES); + + // Check if this is the last block (e.g. digest write happened) + if remaining_len < KECCAK_RATE_BYTES { + let write_timestamp = + start_timestamp + KECCAK_REGISTER_READS as u32 + KECCAK_ABSORB_READS as u32; + last_row + .mem_oc + .digest_writes + .par_iter_mut() + .enumerate() + .for_each(|(i, write)| { + mem_helper.fill_from_prev(write_timestamp + i as u32, write.as_mut()); + }); + for s in state.into_iter().take(NUM_ABSORB_ROUNDS) { + for s_byte in s.to_le_bytes() { + self.bitwise_lookup_chip.request_xor(0, s_byte as u32); + } } } }); - - AirProofInput::simple_no_pis(trace) - } -} - -impl ChipUsageGetter for KeccakVmChip { - fn air_name(&self) -> String { - get_air_name(&self.air) - } - fn current_trace_height(&self) -> usize { - let num_blocks: usize = self.records.iter().map(|r| r.input_blocks.len()).sum(); - num_blocks * NUM_ROUNDS } - fn trace_width(&self) -> usize { - BaseAir::::width(&self.air) + fn get_opcode_name(&self, _: usize) -> String { + format!("{:?}", Rv32KeccakOpcode::KECCAK256) } } diff --git a/extensions/sha256/circuit/src/extension.rs b/extensions/sha256/circuit/src/extension.rs index 76a6c1ec0c..8a5892aef4 100644 --- a/extensions/sha256/circuit/src/extension.rs +++ b/extensions/sha256/circuit/src/extension.rs @@ -3,7 +3,7 @@ use openvm_circuit::{ arch::{SystemConfig, VmExtension, VmInventory, VmInventoryBuilder, VmInventoryError}, system::phantom::PhantomChip, }; -use openvm_circuit_derive::{AnyEnum, InstructionExecutor, VmConfig}; +use openvm_circuit_derive::{AnyEnum, InsExecutorE1, InstructionExecutor, VmConfig}; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, }; @@ -13,6 +13,7 @@ use openvm_rv32im_circuit::{ Rv32I, Rv32IExecutor, Rv32IPeriphery, Rv32Io, Rv32IoExecutor, Rv32IoPeriphery, Rv32M, Rv32MExecutor, Rv32MPeriphery, }; + use openvm_sha256_transpiler::Rv32Sha256Opcode; use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; @@ -20,6 +21,9 @@ use strum::IntoEnumIterator; use crate::*; +// TODO: this should be decided after e2 execution +const MAX_INS_CAPACITY: usize = 1 << 22; + #[derive(Clone, Debug, VmConfig, derive_new::new, Serialize, Deserialize)] pub struct Sha256Rv32Config { #[system] @@ -49,7 +53,7 @@ impl Default for Sha256Rv32Config { #[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)] pub struct Sha256; -#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)] +#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum, InsExecutorE1)] pub enum Sha256Executor { Sha256(Sha256VmChip), } @@ -69,6 +73,8 @@ impl VmExtension for Sha256 { builder: &mut VmInventoryBuilder, ) -> Result, VmInventoryError> { let mut inventory = VmInventory::new(); + let pointer_max_bits = builder.system_config().memory_config.pointer_max_bits; + let bitwise_lu_chip = if let Some(&chip) = builder .find_chip::>() .first() @@ -82,12 +88,19 @@ impl VmExtension for Sha256 { }; let sha256_chip = Sha256VmChip::new( - builder.system_port(), - builder.system_config().memory_config.pointer_max_bits, - bitwise_lu_chip, - builder.new_bus_idx(), - Rv32Sha256Opcode::CLASS_OFFSET, - builder.system_base().offline_memory(), + Sha256VmAir::new( + builder.system_port(), + bitwise_lu_chip.bus(), + pointer_max_bits, + builder.new_bus_idx(), + ), + Sha256VmStep::new( + bitwise_lu_chip.clone(), + Rv32Sha256Opcode::CLASS_OFFSET, + pointer_max_bits, + ), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor( sha256_chip, diff --git a/extensions/sha256/circuit/src/sha256_chip/air.rs b/extensions/sha256/circuit/src/sha256_chip/air.rs index 286df07697..d8e4c02348 100644 --- a/extensions/sha256/circuit/src/sha256_chip/air.rs +++ b/extensions/sha256/circuit/src/sha256_chip/air.rs @@ -1,7 +1,7 @@ use std::{array, borrow::Borrow, cmp::min}; use openvm_circuit::{ - arch::ExecutionBridge, + arch::{ExecutionBridge, SystemPort}, system::memory::{offline_checker::MemoryBridge, MemoryAddress}, }; use openvm_circuit_primitives::{ @@ -17,7 +17,7 @@ use openvm_sha256_air::{ }; use openvm_sha256_transpiler::Rv32Sha256Opcode; use openvm_stark_backend::{ - interaction::InteractionBuilder, + interaction::{BusIndex, InteractionBuilder}, p3_air::{Air, AirBuilder, BaseAir}, p3_field::{Field, FieldAlgebra}, p3_matrix::Matrix, @@ -31,7 +31,7 @@ use super::{ /// Sha256VmAir does all constraints related to message padding and /// the Sha256Air subair constrains the actual hash -#[derive(Clone, Debug, derive_new::new)] +#[derive(Clone, Debug)] pub struct Sha256VmAir { pub execution_bridge: ExecutionBridge, pub memory_bridge: MemoryBridge, @@ -44,6 +44,28 @@ pub struct Sha256VmAir { pub(super) padding_encoder: Encoder, } +impl Sha256VmAir { + pub fn new( + SystemPort { + execution_bus, + program_bus, + memory_bridge, + }: SystemPort, + bitwise_lookup_bus: BitwiseOperationLookupBus, + ptr_max_bits: usize, + self_bus_idx: BusIndex, + ) -> Self { + Self { + execution_bridge: ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + bitwise_lookup_bus, + ptr_max_bits, + sha256_subair: Sha256Air::new(bitwise_lookup_bus, self_bus_idx), + padding_encoder: Encoder::new(PaddingFlags::COUNT, 2, false), + } + } +} + impl BaseAirWithPublicValues for Sha256VmAir {} impl PartitionedBaseAir for Sha256VmAir {} impl BaseAir for Sha256VmAir { diff --git a/extensions/sha256/circuit/src/sha256_chip/mod.rs b/extensions/sha256/circuit/src/sha256_chip/mod.rs index 48dd693606..f9be3fc137 100644 --- a/extensions/sha256/circuit/src/sha256_chip/mod.rs +++ b/extensions/sha256/circuit/src/sha256_chip/mod.rs @@ -1,28 +1,22 @@ //! Sha256 hasher. Handles full sha256 hashing with padding. //! variable length inputs read from VM memory. -use std::{ - array, - cmp::{max, min}, - sync::{Arc, Mutex}, -}; -use openvm_circuit::arch::{ - ExecutionBridge, ExecutionError, ExecutionState, InstructionExecutor, SystemPort, +use openvm_circuit::{ + arch::{NewVmChipWrapper, Result, StepExecutorE1, VmStateMut}, + system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ bitwise_op_lookup::SharedBitwiseOperationLookupChip, encoder::Encoder, }; use openvm_instructions::{ instruction::Instruction, - program::DEFAULT_PC_STEP, riscv::{RV32_CELL_BITS, RV32_MEMORY_AS, RV32_REGISTER_AS}, LocalOpcode, }; -use openvm_rv32im_circuit::adapters::read_rv32_register; -use openvm_sha256_air::{Sha256Air, SHA256_BLOCK_BITS}; +use openvm_rv32im_circuit::adapters::{memory_write, new_read_rv32_register}; +use openvm_sha256_air::{Sha256StepHelper, SHA256_BLOCK_BITS}; use openvm_sha256_transpiler::Rv32Sha256Opcode; -use openvm_stark_backend::{interaction::BusIndex, p3_field::PrimeField32}; -use serde::{Deserialize, Serialize}; +use openvm_stark_backend::p3_field::PrimeField32; use sha2::{Digest, Sha256}; mod air; @@ -31,7 +25,6 @@ mod trace; pub use air::*; pub use columns::*; -use openvm_circuit::system::memory::{MemoryController, OfflineMemory, RecordId}; #[cfg(test)] mod tests; @@ -47,64 +40,39 @@ const SHA256_WRITE_SIZE: usize = 32; pub const SHA256_BLOCK_CELLS: usize = SHA256_BLOCK_BITS / RV32_CELL_BITS; /// Number of rows we will do a read on for each SHA256 block pub const SHA256_NUM_READ_ROWS: usize = SHA256_BLOCK_CELLS / SHA256_READ_SIZE; -pub struct Sha256VmChip { - pub air: Sha256VmAir, - /// IO and memory data necessary for each opcode call - pub records: Vec>, - pub offline_memory: Arc>>, - pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip<8>, - offset: usize, -} +pub type Sha256VmChip = NewVmChipWrapper; -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct Sha256Record { - pub from_state: ExecutionState, - pub dst_read: RecordId, - pub src_read: RecordId, - pub len_read: RecordId, - pub input_records: Vec<[RecordId; SHA256_NUM_READ_ROWS]>, - pub input_message: Vec<[[u8; SHA256_READ_SIZE]; SHA256_NUM_READ_ROWS]>, - pub digest_write: RecordId, +pub struct Sha256VmStep { + pub inner: Sha256StepHelper, + pub padding_encoder: Encoder, + pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, + pub offset: usize, + pub pointer_max_bits: usize, } -impl Sha256VmChip { +impl Sha256VmStep { pub fn new( - SystemPort { - execution_bus, - program_bus, - memory_bridge, - }: SystemPort, - address_bits: usize, - bitwise_lookup_chip: SharedBitwiseOperationLookupChip<8>, - self_bus_idx: BusIndex, + bitwise_lookup_chip: SharedBitwiseOperationLookupChip, offset: usize, - offline_memory: Arc>>, + pointer_max_bits: usize, ) -> Self { Self { - air: Sha256VmAir::new( - ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - bitwise_lookup_chip.bus(), - address_bits, - Sha256Air::new(bitwise_lookup_chip.bus(), self_bus_idx), - Encoder::new(PaddingFlags::COUNT, 2, false), - ), + inner: Sha256StepHelper::new(), + padding_encoder: Encoder::new(PaddingFlags::COUNT, 2, false), bitwise_lookup_chip, - records: Vec::new(), offset, - offline_memory, + pointer_max_bits, } } } -impl InstructionExecutor for Sha256VmChip { - fn execute( +impl StepExecutorE1 for Sha256VmStep { + fn execute_e1( &mut self, - memory: &mut MemoryController, + state: VmStateMut, instruction: &Instruction, - from_state: ExecutionState, - ) -> Result, ExecutionError> { + ) -> Result<()> { let &Instruction { opcode, a, @@ -114,80 +82,27 @@ impl InstructionExecutor for Sha256VmChip { e, .. } = instruction; + let d = d.as_canonical_u32(); + let e = e.as_canonical_u32(); let local_opcode = opcode.local_opcode_idx(self.offset); debug_assert_eq!(local_opcode, Rv32Sha256Opcode::SHA256.local_usize()); - debug_assert_eq!(d, F::from_canonical_u32(RV32_REGISTER_AS)); - debug_assert_eq!(e, F::from_canonical_u32(RV32_MEMORY_AS)); - - debug_assert_eq!(from_state.timestamp, memory.timestamp()); - - let (dst_read, dst) = read_rv32_register(memory, d, a); - let (src_read, src) = read_rv32_register(memory, d, b); - let (len_read, len) = read_rv32_register(memory, d, c); - - #[cfg(debug_assertions)] - { - assert!(dst < (1 << self.air.ptr_max_bits)); - assert!(src < (1 << self.air.ptr_max_bits)); - assert!(len < (1 << self.air.ptr_max_bits)); - } + debug_assert_eq!(d, RV32_REGISTER_AS); + debug_assert_eq!(e, RV32_MEMORY_AS); + let dst = new_read_rv32_register(state.memory, d, a.as_canonical_u32()); + let src = new_read_rv32_register(state.memory, d, b.as_canonical_u32()); + let len = new_read_rv32_register(state.memory, d, c.as_canonical_u32()); - // need to pad with one 1 bit, 64 bits for the message length and then pad until the length - // is divisible by [SHA256_BLOCK_BITS] - let num_blocks = ((len << 3) as usize + 1 + 64).div_ceil(SHA256_BLOCK_BITS); - - // we will read [num_blocks] * [SHA256_BLOCK_CELLS] cells but only [len] cells will be used - debug_assert!( - src as usize + num_blocks * SHA256_BLOCK_CELLS <= (1 << self.air.ptr_max_bits) - ); + debug_assert!(src + len <= (1 << self.pointer_max_bits)); let mut hasher = Sha256::new(); - let mut input_records = Vec::with_capacity(num_blocks * SHA256_NUM_READ_ROWS); - let mut input_message = Vec::with_capacity(num_blocks * SHA256_NUM_READ_ROWS); - let mut read_ptr = src; - for _ in 0..num_blocks { - let block_reads_records = array::from_fn(|i| { - memory.read::( - e, - F::from_canonical_u32(read_ptr + (i * SHA256_READ_SIZE) as u32), - ) - }); - let block_reads_bytes = array::from_fn(|i| { - // we add to the hasher only the bytes that are part of the message - let num_reads = min( - SHA256_READ_SIZE, - (max(read_ptr, src + len) - read_ptr) as usize, - ); - let row_input = block_reads_records[i].1; - hasher.update(&row_input[..num_reads]); - read_ptr += SHA256_READ_SIZE as u32; - row_input - }); - input_records.push(block_reads_records.map(|x| x.0)); - input_message.push(block_reads_bytes); - } - - let mut digest = [0u8; SHA256_WRITE_SIZE]; - digest.copy_from_slice(hasher.finalize().as_ref()); - let (digest_write, _) = memory.write(e, F::from_canonical_u32(dst), &digest); - self.records.push(Sha256Record { - from_state: from_state.map(F::from_canonical_u32), - dst_read, - src_read, - len_read, - input_records, - input_message, - digest_write, - }); - - Ok(ExecutionState { - pc: from_state.pc + DEFAULT_PC_STEP, - timestamp: memory.timestamp(), - }) - } + let message: Vec = state + .memory + .memory + .read_range_generic((e, src), len as usize); + hasher.update(&message); - fn get_opcode_name(&self, _: usize) -> String { - "SHA256".to_string() + memory_write(state.memory, e, dst, hasher.finalize().as_ref()); + Ok(()) } } diff --git a/extensions/sha256/circuit/src/sha256_chip/tests.rs b/extensions/sha256/circuit/src/sha256_chip/tests.rs index 55bc076e2c..f4d78fe308 100644 --- a/extensions/sha256/circuit/src/sha256_chip/tests.rs +++ b/extensions/sha256/circuit/src/sha256_chip/tests.rs @@ -1,6 +1,7 @@ -use openvm_circuit::arch::{ - testing::{memory::gen_pointer, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - SystemPort, +use std::array; + +use openvm_circuit::arch::testing::{ + memory::gen_pointer, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS, }; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, @@ -12,11 +13,39 @@ use openvm_stark_backend::{interaction::BusIndex, p3_field::FieldAlgebra}; use openvm_stark_sdk::{config::setup_tracing, p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; -use super::Sha256VmChip; +use super::{Sha256VmAir, Sha256VmChip, Sha256VmStep}; use crate::{sha256_solve, Sha256VmDigestCols, Sha256VmRoundCols}; type F = BabyBear; -const BUS_IDX: BusIndex = 28; +const SELF_BUS_IDX: BusIndex = 28; +const MAX_INS_CAPACITY: usize = 4096; + +fn create_test_chips( + tester: &mut VmChipTestBuilder, +) -> ( + Sha256VmChip, + SharedBitwiseOperationLookupChip, +) { + let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); + let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); + let chip = Sha256VmChip::new( + Sha256VmAir::new( + tester.system_port(), + bitwise_bus, + tester.address_bits(), + SELF_BUS_IDX, + ), + Sha256VmStep::new( + bitwise_chip.clone(), + Rv32Sha256Opcode::CLASS_OFFSET, + tester.address_bits(), + ), + MAX_INS_CAPACITY, + tester.memory_helper(), + ); + (chip, bitwise_chip) +} + fn set_and_execute( tester: &mut VmChipTestBuilder, chip: &mut Sha256VmChip, @@ -25,7 +54,7 @@ fn set_and_execute( message: Option<&[u8]>, len: Option, ) { - let len = len.unwrap_or(rng.gen_range(1..100000)); + let len = len.unwrap_or(rng.gen_range(1..3000)); let tmp = get_random_message(rng, len); let message: &[u8] = message.unwrap_or(&tmp); let len = message.len(); @@ -34,12 +63,7 @@ fn set_and_execute( let rs1 = gen_pointer(rng, 4); let rs2 = gen_pointer(rng, 4); - let max_mem_ptr: u32 = 1 - << tester - .memory_controller() - .borrow() - .mem_config() - .pointer_max_bits; + let max_mem_ptr: u32 = 1 << tester.address_bits(); let dst_ptr = rng.gen_range(0..max_mem_ptr); let dst_ptr = dst_ptr ^ (dst_ptr & 3); tester.write(1, rd, dst_ptr.to_le_bytes().map(F::from_canonical_u8)); @@ -48,9 +72,14 @@ fn set_and_execute( tester.write(1, rs1, src_ptr.to_le_bytes().map(F::from_canonical_u8)); tester.write(1, rs2, len.to_le_bytes().map(F::from_canonical_u8)); - for (i, &byte) in message.iter().enumerate() { - tester.write(2, src_ptr as usize + i, [F::from_canonical_u8(byte)]); - } + message.chunks(4).enumerate().for_each(|(i, chunk)| { + let chunk: [&u8; 4] = array::from_fn(|i| chunk.get(i).unwrap_or(&0)); + tester.write( + 2, + src_ptr as usize + i * 4, + chunk.map(|&x| F::from_canonical_u8(x)), + ); + }); tester.execute( chip, @@ -75,23 +104,10 @@ fn rand_sha256_test() { setup_tracing(); let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let mut chip = Sha256VmChip::new( - SystemPort { - execution_bus: tester.execution_bus(), - program_bus: tester.program_bus(), - memory_bridge: tester.memory_bridge(), - }, - tester.address_bits(), - bitwise_chip.clone(), - BUS_IDX, - Rv32Sha256Opcode::CLASS_OFFSET, - tester.offline_memory_mutex_arc(), - ); + let (mut chip, bitwise_chip) = create_test_chips(&mut tester); - let num_tests: usize = 3; - for _ in 0..num_tests { + let num_ops: usize = 10; + for _ in 0..num_ops { set_and_execute(&mut tester, &mut chip, &mut rng, SHA256, None, None); } @@ -108,20 +124,7 @@ fn rand_sha256_test() { fn execute_roundtrip_sanity_test() { let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); - let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let mut chip = Sha256VmChip::new( - SystemPort { - execution_bus: tester.execution_bus(), - program_bus: tester.program_bus(), - memory_bridge: tester.memory_bridge(), - }, - tester.address_bits(), - bitwise_chip.clone(), - BUS_IDX, - Rv32Sha256Opcode::CLASS_OFFSET, - tester.offline_memory_mutex_arc(), - ); + let (mut chip, _) = create_test_chips(&mut tester); println!( "Sha256VmDigestCols::width(): {}", diff --git a/extensions/sha256/circuit/src/sha256_chip/trace.rs b/extensions/sha256/circuit/src/sha256_chip/trace.rs index c02cd00dd8..8c88d62836 100644 --- a/extensions/sha256/circuit/src/sha256_chip/trace.rs +++ b/extensions/sha256/circuit/src/sha256_chip/trace.rs @@ -1,351 +1,415 @@ -use std::{array, borrow::BorrowMut, sync::Arc}; +use std::{ + array, + borrow::{Borrow, BorrowMut}, +}; -use openvm_circuit_primitives::utils::next_power_of_two_or_zero; -use openvm_instructions::riscv::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; -use openvm_rv32im_circuit::adapters::compose; -use openvm_sha256_air::{ - get_flag_pt_array, limbs_into_u32, Sha256Air, SHA256_BLOCK_WORDS, SHA256_BUFFER_SIZE, SHA256_H, - SHA256_HASH_WORDS, SHA256_ROWS_PER_BLOCK, SHA256_WORD_U8S, +use openvm_circuit::{ + arch::{Result, TraceStep, VmStateMut}, + system::memory::{online::TracingMemory, MemoryAuxColsFactory}, }; -use openvm_stark_backend::{ - config::{StarkGenericConfig, Val}, - p3_air::BaseAir, - p3_field::{FieldAlgebra, PrimeField32}, - p3_matrix::dense::RowMajorMatrix, - p3_maybe_rayon::prelude::*, - prover::types::AirProofInput, - rap::get_air_name, - AirRef, Chip, ChipUsageGetter, + +use openvm_instructions::{ + instruction::Instruction, + program::DEFAULT_PC_STEP, + riscv::{RV32_CELL_BITS, RV32_MEMORY_AS, RV32_REGISTER_AS, RV32_REGISTER_NUM_LIMBS}, + LocalOpcode, +}; +use openvm_rv32im_circuit::adapters::{memory_read, tracing_read, tracing_write}; +use openvm_sha256_air::{ + get_flag_pt_array, u32_into_u16s, Sha256StepHelper, SHA256_BLOCK_BITS, SHA256_BLOCK_WORDS, + SHA256_H, SHA256_ROWS_PER_BLOCK, SHA256_WORD_U8S, }; +use openvm_sha256_transpiler::Rv32Sha256Opcode; +use openvm_stark_backend::{p3_field::PrimeField32, p3_maybe_rayon::prelude::*}; use super::{ - Sha256VmChip, Sha256VmDigestCols, Sha256VmRoundCols, SHA256VM_CONTROL_WIDTH, - SHA256VM_DIGEST_WIDTH, SHA256VM_ROUND_WIDTH, + Sha256VmDigestCols, Sha256VmRoundCols, Sha256VmStep, SHA256VM_CONTROL_WIDTH, + SHA256VM_DIGEST_WIDTH, }; use crate::{ sha256_chip::{PaddingFlags, SHA256_READ_SIZE}, - SHA256_BLOCK_CELLS, + SHA256VM_ROUND_WIDTH, SHA256_BLOCK_CELLS, }; -impl Chip for Sha256VmChip> -where - Val: PrimeField32, -{ - fn air(&self) -> AirRef { - Arc::new(self.air.clone()) - } - - fn generate_air_proof_input(self) -> AirProofInput { - let non_padded_height = self.current_trace_height(); - let height = next_power_of_two_or_zero(non_padded_height); - let width = self.trace_width(); - let mut values = Val::::zero_vec(height * width); - if height == 0 { - return AirProofInput::simple_no_pis(RowMajorMatrix::new(values, width)); - } - let records = self.records; - let offline_memory = self.offline_memory.lock().unwrap(); - let memory_aux_cols_factory = offline_memory.aux_cols_factory(); +impl TraceStep for Sha256VmStep { + fn execute( + &mut self, + state: VmStateMut, CTX>, + instruction: &Instruction, + trace: &mut [F], + trace_offset: &mut usize, + width: usize, + ) -> Result<()> { + let Instruction { + opcode, + a, + b, + c, + d, + e, + .. + } = instruction; + let d = d.as_canonical_u32(); + let e = e.as_canonical_u32(); + debug_assert_eq!(*opcode, Rv32Sha256Opcode::SHA256.global_opcode()); + debug_assert_eq!(d, RV32_REGISTER_AS); + debug_assert_eq!(e, RV32_MEMORY_AS); - let mem_ptr_shift: u32 = - 1 << (RV32_REGISTER_NUM_LIMBS * RV32_CELL_BITS - self.air.ptr_max_bits); + let trace = &mut trace[*trace_offset..]; + // Doing an untraced read to get the length to get the correct places to store the aux data + let len = u32::from_le_bytes(memory_read(state.memory.data(), d, c.as_canonical_u32())); + // need to pad with one 1 bit, 64 bits for the message length and then pad until the length + // is divisible by [SHA256_BLOCK_BITS] + let num_blocks = ((len << 3) as usize + 1 + 64).div_ceil(SHA256_BLOCK_BITS); - let mut states = Vec::with_capacity(height.div_ceil(SHA256_ROWS_PER_BLOCK)); - let mut global_block_idx = 0; - for (record_idx, record) in records.iter().enumerate() { - let dst_read = offline_memory.record_by_id(record.dst_read); - let src_read = offline_memory.record_by_id(record.src_read); - let len_read = offline_memory.record_by_id(record.len_read); + let last_row_offset = (num_blocks * SHA256_ROWS_PER_BLOCK - 1) * width; + let (dst, mut src) = { + let last_digest_row: &mut Sha256VmDigestCols = + trace[last_row_offset..last_row_offset + SHA256VM_DIGEST_WIDTH].borrow_mut(); - self.bitwise_lookup_chip.request_range( - dst_read - .data_at(RV32_REGISTER_NUM_LIMBS - 1) - .as_canonical_u32() - * mem_ptr_shift, - src_read - .data_at(RV32_REGISTER_NUM_LIMBS - 1) - .as_canonical_u32() - * mem_ptr_shift, + last_digest_row.from_state.timestamp = F::from_canonical_u32(state.memory.timestamp()); + last_digest_row.from_state.pc = F::from_canonical_u32(*state.pc); + let dst = tracing_read( + state.memory, + d, + a.as_canonical_u32(), + &mut last_digest_row.register_reads_aux[0], ); - let len = compose(len_read.data_slice().try_into().unwrap()); - let mut state = &None; - for (i, input_message) in record.input_message.iter().enumerate() { - let input_message = input_message - .iter() - .flatten() - .copied() - .collect::>() - .try_into() - .unwrap(); - states.push(Some(Self::generate_state( - state, - input_message, - record_idx, + let src = tracing_read( + state.memory, + d, + b.as_canonical_u32(), + &mut last_digest_row.register_reads_aux[1], + ); + let len = tracing_read::<_, RV32_REGISTER_NUM_LIMBS>( + state.memory, + d, + c.as_canonical_u32(), + &mut last_digest_row.register_reads_aux[2], + ); + + last_digest_row.rd_ptr = *a; + last_digest_row.rs1_ptr = *b; + last_digest_row.rs2_ptr = *c; + last_digest_row.dst_ptr = dst.map(F::from_canonical_u8); + last_digest_row.src_ptr = src.map(F::from_canonical_u8); + last_digest_row.len_data = len.map(F::from_canonical_u8); + (u32::from_le_bytes(dst), u32::from_le_bytes(src)) + }; + + // we will read [num_blocks] * [SHA256_BLOCK_CELLS] cells but only [len] cells will be used + debug_assert!( + src as usize + num_blocks * SHA256_BLOCK_CELLS <= (1 << self.pointer_max_bits) + ); + + // // We can deduce the global block index from the trace offset + // // Note: global block index is 1-based + // let global_idx = *trace_offset / (SHA256_ROWS_PER_BLOCK * width) + 1; + let mut prev_hash = SHA256_H; + trace + .chunks_mut(width * SHA256_ROWS_PER_BLOCK) + .enumerate() + .take(num_blocks) + .for_each(|(block_idx, block_slice)| { + let is_last_block = block_idx == num_blocks - 1; + let mut read_data = [[0u8; SHA256_READ_SIZE]; 4]; + block_slice + .chunks_mut(width) + .enumerate() + .take(4) + .for_each(|(row_idx, row)| { + let cols: &mut Sha256VmRoundCols = + row[..SHA256VM_ROUND_WIDTH].borrow_mut(); + read_data[row_idx] = tracing_read::<_, SHA256_READ_SIZE>( + state.memory, + e, + src, + &mut cols.read_aux, + ); + cols.inner + .message_schedule + .carry_or_buffer + .iter_mut() + .zip( + read_data[row_idx] + .map(F::from_canonical_u8) + .chunks_exact(SHA256_WORD_U8S), + ) + .for_each(|(buffer, data)| { + buffer.copy_from_slice(data); + }); + src += SHA256_READ_SIZE as u32; + }); + + let digest_row = &mut block_slice[(SHA256_ROWS_PER_BLOCK - 1) * width..]; + let digest_cols: &mut Sha256VmDigestCols = + digest_row[..SHA256VM_DIGEST_WIDTH].borrow_mut(); + digest_cols.inner.prev_hash = + prev_hash.map(|x| u32_into_u16s(x).map(F::from_canonical_u32)); + digest_cols.inner.flags.local_block_idx = F::from_canonical_usize(block_idx); + digest_cols.inner.flags.is_last_block = F::from_bool(is_last_block); + digest_cols.control.len = F::from_canonical_u32(len); + digest_cols.control.read_ptr = F::from_canonical_u32(src); + digest_cols.control.cur_timestamp = F::from_canonical_u32(state.memory.timestamp()); + let padded_input = get_padded_input( + read_data.concat().try_into().unwrap(), len, - i == record.input_records.len() - 1, - ))); - state = &states[global_block_idx]; - global_block_idx += 1; - } - } - states.extend(std::iter::repeat_n( - None, - (height - non_padded_height).div_ceil(SHA256_ROWS_PER_BLOCK), - )); + block_idx, + is_last_block, + ); + Sha256StepHelper::get_block_hash(&mut prev_hash, padded_input); + }); + + let last_digest_row: &mut Sha256VmDigestCols = + trace[last_row_offset..last_row_offset + SHA256VM_DIGEST_WIDTH].borrow_mut(); + tracing_write( + state.memory, + e, + dst, + &prev_hash + .map(|x| x.to_be_bytes()) + .concat() + .try_into() + .unwrap(), + &mut last_digest_row.writes_aux, + ); + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *trace_offset += num_blocks * SHA256_ROWS_PER_BLOCK * width; + Ok(()) + } + + fn fill_trace( + &self, + mem_helper: &MemoryAuxColsFactory, + trace: &mut [F], + width: usize, + rows_used: usize, + ) where + Self: Send + Sync, + F: Send + Sync, + { + let mem_ptr_shift: u32 = + 1 << (RV32_REGISTER_NUM_LIMBS * RV32_CELL_BITS - self.pointer_max_bits); // During the first pass we will fill out most of the matrix // But there are some cells that can't be generated by the first pass so we will do a second // pass over the matrix - values + trace .par_chunks_mut(width * SHA256_ROWS_PER_BLOCK) - .zip(states.into_par_iter().enumerate()) - .for_each(|(block, (global_block_idx, state))| { - // Fill in a valid block - if let Some(state) = state { - let mut has_padding_occurred = - state.local_block_idx * SHA256_BLOCK_CELLS > state.message_len as usize; - let message_left = if has_padding_occurred { - 0 - } else { - state.message_len as usize - state.local_block_idx * SHA256_BLOCK_CELLS - }; - let is_last_block = state.is_last_block; - let buffer: [[Val; SHA256_BUFFER_SIZE]; 4] = array::from_fn(|j| { - array::from_fn(|k| { - Val::::from_canonical_u8( - state.block_input_message[j * SHA256_BUFFER_SIZE + k], - ) - }) + .enumerate() + .for_each(|(block_idx, block_slice)| { + if block_idx * SHA256_ROWS_PER_BLOCK >= rows_used { + // Fill in the invalid rows + block_slice.par_chunks_mut(width).for_each(|row| { + let cols: &mut Sha256VmRoundCols = + row[..SHA256VM_ROUND_WIDTH].borrow_mut(); + self.inner.generate_default_row(&mut cols.inner); }); + return; + } - let padded_message: [u32; SHA256_BLOCK_WORDS] = array::from_fn(|j| { - limbs_into_u32::(array::from_fn(|k| { - state.block_padded_message[(j + 1) * SHA256_WORD_U8S - k - 1] as u32 - })) - }); + // The read data is kept in the buffer of the first 4 round cols + let read_data: [u8; SHA256_BLOCK_CELLS] = block_slice + .chunks_exact(width) + .take(4) + .map(|row| { + let cols: &Sha256VmRoundCols = row[..SHA256VM_ROUND_WIDTH].borrow(); + cols.inner.message_schedule.carry_or_buffer.as_flattened() + }) + .flatten() + .map(|x| x.as_canonical_u32() as u8) + .collect::>() + .try_into() + .unwrap(); - self.air.sha256_subair.generate_block_trace::>( - block, - width, - SHA256VM_CONTROL_WIDTH, - &padded_message, - self.bitwise_lookup_chip.clone(), - &state.hash, - is_last_block, - global_block_idx as u32 + 1, - state.local_block_idx as u32, - &buffer, - ); + let digest_offset = width * (SHA256_ROWS_PER_BLOCK - 1); + let (local_block_idx, len, is_last_block, prev_hash) = { + let digest_cols: &mut Sha256VmDigestCols = block_slice + [digest_offset..digest_offset + SHA256VM_DIGEST_WIDTH] + .borrow_mut(); + ( + digest_cols.inner.flags.local_block_idx.as_canonical_u32() as usize, + digest_cols.control.len.as_canonical_u32(), + digest_cols.inner.flags.is_last_block.is_one(), + digest_cols + .inner + .prev_hash + .map(|x| x[0].as_canonical_u32() + (x[1].as_canonical_u32() << 16)), + ) + }; + let mut has_padding_occurred = local_block_idx * SHA256_BLOCK_CELLS > len as usize; + let message_left = if has_padding_occurred { + 0 + } else { + len as usize - local_block_idx * SHA256_BLOCK_CELLS + }; - let block_reads = records[state.message_idx].input_records - [state.local_block_idx] - .map(|record_id| offline_memory.record_by_id(record_id)); + let padded_input = get_padded_input(read_data, len, local_block_idx, is_last_block); + let padded_input: [u32; SHA256_BLOCK_WORDS] = array::from_fn(|j| { + u32::from_be_bytes( + padded_input[j * SHA256_WORD_U8S..(j + 1) * SHA256_WORD_U8S] + .try_into() + .unwrap(), + ) + }); - let mut read_ptr = block_reads[0].pointer; - let mut cur_timestamp = Val::::from_canonical_u32(block_reads[0].timestamp); + self.inner.generate_block_trace::( + block_slice, + width, + SHA256VM_CONTROL_WIDTH, + &padded_input, + self.bitwise_lookup_chip.as_ref(), + &prev_hash, + is_last_block, + block_idx as u32 + 1, // global block index is 1-based + local_block_idx as u32, + ); - let read_size = Val::::from_canonical_usize(SHA256_READ_SIZE); - for row in 0..SHA256_ROWS_PER_BLOCK { - let row_slice = &mut block[row * width..(row + 1) * width]; - if row < 16 { - let cols: &mut Sha256VmRoundCols> = - row_slice[..SHA256VM_ROUND_WIDTH].borrow_mut(); - cols.control.len = Val::::from_canonical_u32(state.message_len); - cols.control.read_ptr = read_ptr; - cols.control.cur_timestamp = cur_timestamp; - if row < 4 { - read_ptr += read_size; - cur_timestamp += Val::::ONE; - memory_aux_cols_factory - .generate_read_aux(block_reads[row], &mut cols.read_aux); + let (round_rows, digest_row) = block_slice.split_at_mut(digest_offset); + let digest_cols: &mut Sha256VmDigestCols = + digest_row[..SHA256VM_DIGEST_WIDTH].borrow_mut(); + let len = digest_cols.control.len; + let read_ptr = digest_cols.control.read_ptr; + let timestamp = digest_cols.control.cur_timestamp; - if (row + 1) * SHA256_READ_SIZE <= message_left { - cols.control.pad_flags = get_flag_pt_array( - &self.air.padding_encoder, - PaddingFlags::NotPadding as usize, - ) - .map(Val::::from_canonical_u32); - } else if !has_padding_occurred { - has_padding_occurred = true; - let len = message_left - row * SHA256_READ_SIZE; - cols.control.pad_flags = get_flag_pt_array( - &self.air.padding_encoder, - if row == 3 && is_last_block { - PaddingFlags::FirstPadding0_LastRow - } else { - PaddingFlags::FirstPadding0 - } as usize - + len, - ) - .map(Val::::from_canonical_u32); + // Fill in the first 4 round rows + round_rows + .chunks_mut(width) + .take(4) + .enumerate() + .for_each(|(row, row_slice)| { + let cols: &mut Sha256VmRoundCols = + row_slice[..SHA256VM_ROUND_WIDTH].borrow_mut(); + cols.control.len = len; + cols.control.read_ptr = + read_ptr - F::from_canonical_usize(SHA256_READ_SIZE * (4 - row)); + cols.control.cur_timestamp = timestamp - F::from_canonical_usize(4 - row); + mem_helper.fill_from_prev( + cols.control.cur_timestamp.as_canonical_u32(), + cols.read_aux.as_mut(), + ); + if (row + 1) * SHA256_READ_SIZE <= message_left { + cols.control.pad_flags = get_flag_pt_array( + &self.padding_encoder, + PaddingFlags::NotPadding as usize, + ) + .map(F::from_canonical_u32); + } else if !has_padding_occurred { + has_padding_occurred = true; + let len = message_left - row * SHA256_READ_SIZE; + cols.control.pad_flags = get_flag_pt_array( + &self.padding_encoder, + if row == 3 && is_last_block { + PaddingFlags::FirstPadding0_LastRow } else { - cols.control.pad_flags = get_flag_pt_array( - &self.air.padding_encoder, - if row == 3 && is_last_block { - PaddingFlags::EntirePaddingLastRow - } else { - PaddingFlags::EntirePadding - } as usize, - ) - .map(Val::::from_canonical_u32); - } - } else { - cols.control.pad_flags = get_flag_pt_array( - &self.air.padding_encoder, - PaddingFlags::NotConsidered as usize, - ) - .map(Val::::from_canonical_u32); - } - cols.control.padding_occurred = - Val::::from_bool(has_padding_occurred); + PaddingFlags::FirstPadding0 + } as usize + + len, + ) + .map(F::from_canonical_u32); } else { - if is_last_block { - has_padding_occurred = false; - } - let cols: &mut Sha256VmDigestCols> = - row_slice[..SHA256VM_DIGEST_WIDTH].borrow_mut(); - cols.control.len = Val::::from_canonical_u32(state.message_len); - cols.control.read_ptr = read_ptr; - cols.control.cur_timestamp = cur_timestamp; cols.control.pad_flags = get_flag_pt_array( - &self.air.padding_encoder, - PaddingFlags::NotConsidered as usize, + &self.padding_encoder, + if row == 3 && is_last_block { + PaddingFlags::EntirePaddingLastRow + } else { + PaddingFlags::EntirePadding + } as usize, ) - .map(Val::::from_canonical_u32); - if is_last_block { - let record = &records[state.message_idx]; - let dst_read = offline_memory.record_by_id(record.dst_read); - let src_read = offline_memory.record_by_id(record.src_read); - let len_read = offline_memory.record_by_id(record.len_read); - let digest_write = offline_memory.record_by_id(record.digest_write); - cols.from_state = record.from_state; - cols.rd_ptr = dst_read.pointer; - cols.rs1_ptr = src_read.pointer; - cols.rs2_ptr = len_read.pointer; - cols.dst_ptr.copy_from_slice(dst_read.data_slice()); - cols.src_ptr.copy_from_slice(src_read.data_slice()); - cols.len_data.copy_from_slice(len_read.data_slice()); - memory_aux_cols_factory - .generate_read_aux(dst_read, &mut cols.register_reads_aux[0]); - memory_aux_cols_factory - .generate_read_aux(src_read, &mut cols.register_reads_aux[1]); - memory_aux_cols_factory - .generate_read_aux(len_read, &mut cols.register_reads_aux[2]); - memory_aux_cols_factory - .generate_write_aux(digest_write, &mut cols.writes_aux); - } - cols.control.padding_occurred = - Val::::from_bool(has_padding_occurred); + .map(F::from_canonical_u32); } - } + cols.control.padding_occurred = F::from_bool(has_padding_occurred); + }); + + // Fill in the remaining round rows + + round_rows + .par_chunks_mut(width) + .skip(4) + .for_each(|row_slice| { + let cols: &mut Sha256VmRoundCols = + row_slice[..SHA256VM_ROUND_WIDTH].borrow_mut(); + cols.control.len = len; + cols.control.read_ptr = read_ptr; + cols.control.cur_timestamp = timestamp; + cols.control.pad_flags = get_flag_pt_array( + &self.padding_encoder, + PaddingFlags::NotConsidered as usize, + ) + .map(F::from_canonical_u32); + cols.control.padding_occurred = F::from_bool(has_padding_occurred); + }); + + // Fill in the digest row + if is_last_block { + has_padding_occurred = false; } - // Fill in the invalid rows - else { - block.par_chunks_mut(width).for_each(|row| { - let cols: &mut Sha256VmRoundCols> = row.borrow_mut(); - self.air.sha256_subair.generate_default_row(&mut cols.inner); - }) + digest_cols.control.pad_flags = + get_flag_pt_array(&self.padding_encoder, PaddingFlags::NotConsidered as usize) + .map(F::from_canonical_u32); + if is_last_block { + let mut timestamp = digest_cols.from_state.timestamp.as_canonical_u32(); + digest_cols.register_reads_aux.iter_mut().for_each(|aux| { + mem_helper.fill_from_prev(timestamp, aux.as_mut()); + timestamp += 1; + }); + mem_helper.fill_from_prev( + digest_cols.control.cur_timestamp.as_canonical_u32(), + digest_cols.writes_aux.as_mut(), + ); + self.bitwise_lookup_chip.request_range( + digest_cols.dst_ptr[RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() + * mem_ptr_shift, + digest_cols.src_ptr[RV32_REGISTER_NUM_LIMBS - 1].as_canonical_u32() + * mem_ptr_shift, + ); } + digest_cols.control.padding_occurred = F::from_bool(has_padding_occurred); }); // Do a second pass over the trace to fill in the missing values // Note, we need to skip the very first row - values[width..] + trace[width..] .par_chunks_mut(width * SHA256_ROWS_PER_BLOCK) - .take(non_padded_height / SHA256_ROWS_PER_BLOCK) + .take(rows_used / SHA256_ROWS_PER_BLOCK) .for_each(|chunk| { - self.air - .sha256_subair + self.inner .generate_missing_cells(chunk, width, SHA256VM_CONTROL_WIDTH); }); - - AirProofInput::simple_no_pis(RowMajorMatrix::new(values, width)) - } -} - -impl ChipUsageGetter for Sha256VmChip { - fn air_name(&self) -> String { - get_air_name(&self.air) - } - fn current_trace_height(&self) -> usize { - self.records.iter().fold(0, |acc, record| { - acc + record.input_records.len() * SHA256_ROWS_PER_BLOCK - }) } - fn trace_width(&self) -> usize { - BaseAir::::width(&self.air) + fn get_opcode_name(&self, _: usize) -> String { + format!("{:?}", Rv32Sha256Opcode::SHA256) } } -/// This is the state information that a block will use to generate its trace -#[derive(Debug, Clone)] -struct Sha256State { - hash: [u32; SHA256_HASH_WORDS], - local_block_idx: usize, +fn get_padded_input( + block_input: [u8; SHA256_BLOCK_CELLS], message_len: u32, - block_input_message: [u8; SHA256_BLOCK_CELLS], - block_padded_message: [u8; SHA256_BLOCK_CELLS], - message_idx: usize, + local_block_idx: usize, is_last_block: bool, -} - -impl Sha256VmChip { - fn generate_state( - prev_state: &Option, - block_input_message: [u8; SHA256_BLOCK_CELLS], - message_idx: usize, - message_len: u32, - is_last_block: bool, - ) -> Sha256State { - let local_block_idx = if let Some(prev_state) = prev_state { - prev_state.local_block_idx + 1 - } else { - 0 - }; - let has_padding_occurred = local_block_idx * SHA256_BLOCK_CELLS > message_len as usize; - let message_left = if has_padding_occurred { - 0 - } else { - message_len as usize - local_block_idx * SHA256_BLOCK_CELLS - }; +) -> [u8; SHA256_BLOCK_CELLS] { + let has_padding_occurred = local_block_idx * SHA256_BLOCK_CELLS > message_len as usize; + let message_left = if has_padding_occurred { + 0 + } else { + message_len as usize - local_block_idx * SHA256_BLOCK_CELLS + }; - let padded_message_bytes: [u8; SHA256_BLOCK_CELLS] = array::from_fn(|j| { - if j < message_left { - block_input_message[j] - } else if j == message_left && !has_padding_occurred { - 1 << (RV32_CELL_BITS - 1) - } else if !is_last_block || j < SHA256_BLOCK_CELLS - 4 { - 0u8 - } else { - let shift_amount = (SHA256_BLOCK_CELLS - j - 1) * RV32_CELL_BITS; - ((message_len * RV32_CELL_BITS as u32) - .checked_shr(shift_amount as u32) - .unwrap_or(0) - & ((1 << RV32_CELL_BITS) - 1)) as u8 - } - }); - - if let Some(prev_state) = prev_state { - Sha256State { - hash: Sha256Air::get_block_hash(&prev_state.hash, prev_state.block_padded_message), - local_block_idx, - message_len, - block_input_message, - block_padded_message: padded_message_bytes, - message_idx, - is_last_block, - } + array::from_fn(|j| { + if j < message_left { + block_input[j] + } else if j == message_left && !has_padding_occurred { + 1 << (RV32_CELL_BITS - 1) + } else if !is_last_block || j < SHA256_BLOCK_CELLS - 4 { + 0u8 } else { - Sha256State { - hash: SHA256_H, - local_block_idx: 0, - message_len, - block_input_message, - block_padded_message: padded_message_bytes, - message_idx, - is_last_block, - } + let shift_amount = (SHA256_BLOCK_CELLS - j - 1) * RV32_CELL_BITS; + ((message_len * RV32_CELL_BITS as u32) + .checked_shr(shift_amount as u32) + .unwrap_or(0) + & ((1 << RV32_CELL_BITS) - 1)) as u8 } - } + }) } From 950320bb093361a8850fe326b51c6c89a8a46ea5 Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Tue, 20 May 2025 10:56:19 -0400 Subject: [PATCH 27/49] feat(new-execution): metered execution (#1652) - Added a `execute_metered` function that keeps a track of `trace_heights` during execution and suspends execution if the trace height, number of cells or number of interactions goes above a fixed threshold Resolves INT-3752 --- Cargo.lock | 2 + benchmarks/execute/Cargo.toml | 2 + benchmarks/execute/benches/execute.rs | 14 +- benchmarks/execute/src/main.rs | 40 +- .../guest/factorial_iterative_u256/Cargo.toml | 17 + ...penvm-factorial-iterative-u256-program.elf | Bin 0 -> 64748 bytes .../factorial_iterative_u256/openvm.toml | 4 + .../factorial_iterative_u256/src/main.rs | 16 + .../openvm-fibonacci-iterative-program.elf | Bin 64056 -> 63984 bytes .../guest/fibonacci_iterative/src/main.rs | 12 +- .../openvm-fibonacci-recursive-program.elf | Bin 64236 -> 64148 bytes .../guest/fibonacci_recursive/src/main.rs | 9 +- crates/vm/derive/src/lib.rs | 44 +- crates/vm/src/arch/execution.rs | 33 +- crates/vm/src/arch/execution_control.rs | 239 +---------- crates/vm/src/arch/execution_mode/e1.rs | 79 ++++ .../arch/execution_mode/metered/bounded.rs | 183 ++++++++ .../src/arch/execution_mode/metered/exact.rs | 393 ++++++++++++++++++ .../vm/src/arch/execution_mode/metered/mod.rs | 204 +++++++++ crates/vm/src/arch/execution_mode/mod.rs | 8 + crates/vm/src/arch/execution_mode/tracegen.rs | 117 ++++++ crates/vm/src/arch/extensions.rs | 27 +- crates/vm/src/arch/integration_api.rs | 54 ++- crates/vm/src/arch/mod.rs | 1 + crates/vm/src/arch/segment.rs | 137 +++--- crates/vm/src/arch/segmentation_strategy.rs | 4 +- crates/vm/src/arch/testing/mod.rs | 7 +- crates/vm/src/arch/vm.rs | 268 ++++++++++-- crates/vm/src/system/memory/adapter/mod.rs | 3 +- .../system/memory/controller/dimensions.rs | 9 + crates/vm/src/system/memory/controller/mod.rs | 9 +- crates/vm/src/system/memory/mod.rs | 2 +- crates/vm/src/system/memory/online.rs | 6 +- crates/vm/src/system/memory/paged_vec.rs | 1 - crates/vm/src/system/memory/persistent.rs | 9 +- crates/vm/src/system/memory/volatile/mod.rs | 2 +- crates/vm/src/system/native_adapter/mod.rs | 32 +- crates/vm/src/system/phantom/mod.rs | 24 +- crates/vm/src/system/poseidon2/trace.rs | 3 +- crates/vm/src/system/public_values/core.rs | 21 +- extensions/rv32-adapters/src/eq_mod.rs | 32 +- extensions/rv32-adapters/src/heap.rs | 27 +- extensions/rv32-adapters/src/heap_branch.rs | 90 ++-- extensions/rv32-adapters/src/vec_heap.rs | 45 +- .../rv32-adapters/src/vec_heap_two_reads.rs | 51 ++- extensions/rv32im/circuit/src/adapters/alu.rs | 31 +- .../rv32im/circuit/src/adapters/branch.rs | 28 +- .../rv32im/circuit/src/adapters/jalr.rs | 31 +- .../rv32im/circuit/src/adapters/loadstore.rs | 42 +- extensions/rv32im/circuit/src/adapters/mod.rs | 55 ++- extensions/rv32im/circuit/src/adapters/mul.rs | 31 +- .../rv32im/circuit/src/adapters/rdwrite.rs | 51 ++- extensions/rv32im/circuit/src/auipc/core.rs | 25 +- .../rv32im/circuit/src/base_alu/core.rs | 24 +- .../rv32im/circuit/src/branch_eq/core.rs | 29 +- .../rv32im/circuit/src/branch_eq/tests.rs | 6 +- .../rv32im/circuit/src/branch_lt/core.rs | 22 +- extensions/rv32im/circuit/src/divrem/core.rs | 26 +- .../rv32im/circuit/src/hintstore/mod.rs | 72 +++- extensions/rv32im/circuit/src/jal_lui/core.rs | 25 +- extensions/rv32im/circuit/src/jalr/core.rs | 27 +- .../rv32im/circuit/src/less_than/core.rs | 24 +- .../circuit/src/load_sign_extend/core.rs | 26 +- .../circuit/src/load_sign_extend/tests.rs | 6 +- .../rv32im/circuit/src/loadstore/core.rs | 26 +- extensions/rv32im/circuit/src/mul/core.rs | 24 +- extensions/rv32im/circuit/src/mulh/core.rs | 24 +- extensions/rv32im/circuit/src/shift/core.rs | 24 +- 68 files changed, 2275 insertions(+), 684 deletions(-) create mode 100644 benchmarks/guest/factorial_iterative_u256/Cargo.toml create mode 100755 benchmarks/guest/factorial_iterative_u256/elf/openvm-factorial-iterative-u256-program.elf create mode 100644 benchmarks/guest/factorial_iterative_u256/openvm.toml create mode 100644 benchmarks/guest/factorial_iterative_u256/src/main.rs create mode 100644 crates/vm/src/arch/execution_mode/e1.rs create mode 100644 crates/vm/src/arch/execution_mode/metered/bounded.rs create mode 100644 crates/vm/src/arch/execution_mode/metered/exact.rs create mode 100644 crates/vm/src/arch/execution_mode/metered/mod.rs create mode 100644 crates/vm/src/arch/execution_mode/mod.rs create mode 100644 crates/vm/src/arch/execution_mode/tracegen.rs diff --git a/Cargo.lock b/Cargo.lock index 68ea667442..04361d85f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4694,6 +4694,8 @@ dependencies = [ "derive_more 1.0.0", "eyre", "openvm-benchmarks-utils", + "openvm-bigint-circuit", + "openvm-bigint-transpiler", "openvm-circuit", "openvm-rv32im-circuit", "openvm-rv32im-transpiler", diff --git a/benchmarks/execute/Cargo.toml b/benchmarks/execute/Cargo.toml index db554f5489..bcf726a6bb 100644 --- a/benchmarks/execute/Cargo.toml +++ b/benchmarks/execute/Cargo.toml @@ -16,6 +16,8 @@ openvm-stark-sdk.workspace = true openvm-transpiler.workspace = true openvm-rv32im-circuit.workspace = true openvm-rv32im-transpiler.workspace = true +openvm-bigint-circuit.workspace = true +openvm-bigint-transpiler.workspace = true # openvm-keccak256-circuit.workspace = true # openvm-keccak256-transpiler.workspace = true diff --git a/benchmarks/execute/benches/execute.rs b/benchmarks/execute/benches/execute.rs index bcd60f6811..6cb2a60cf3 100644 --- a/benchmarks/execute/benches/execute.rs +++ b/benchmarks/execute/benches/execute.rs @@ -1,10 +1,8 @@ -use std::path::PathBuf; - -use divan; use eyre::Result; use openvm_benchmarks_utils::{get_elf_path, get_programs_dir, read_elf_file}; +use openvm_bigint_circuit::Int256Rv32Config; +use openvm_bigint_transpiler::Int256TranspilerExtension; use openvm_circuit::arch::{instructions::exe::VmExe, VmExecutor}; -use openvm_rv32im_circuit::Rv32ImConfig; use openvm_rv32im_transpiler::{ Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, }; @@ -16,13 +14,14 @@ static AVAILABLE_PROGRAMS: &[&str] = &[ "fibonacci_iterative", "quicksort", "bubblesort", + "factorial_iterative_u256", + "revm_snailtracer", // "pairing", // "keccak256", // "keccak256_iter", // "sha256", // "sha256_iter", // "revm_transfer", - // "revm_snailtracer", ]; fn main() { @@ -35,12 +34,13 @@ fn run_program(program: &str) -> Result<()> { let elf_path = get_elf_path(&program_dir); let elf = read_elf_file(&elf_path)?; - let vm_config = Rv32ImConfig::default(); + let vm_config = Int256Rv32Config::default(); let transpiler = Transpiler::::default() .with_extension(Rv32ITranspilerExtension) .with_extension(Rv32IoTranspilerExtension) - .with_extension(Rv32MTranspilerExtension); + .with_extension(Rv32MTranspilerExtension) + .with_extension(Int256TranspilerExtension); let exe = VmExe::from_elf(elf, transpiler)?; diff --git a/benchmarks/execute/src/main.rs b/benchmarks/execute/src/main.rs index 9115888898..cd97932f84 100644 --- a/benchmarks/execute/src/main.rs +++ b/benchmarks/execute/src/main.rs @@ -1,12 +1,16 @@ use clap::{Parser, ValueEnum}; use eyre::Result; use openvm_benchmarks_utils::{get_elf_path, get_programs_dir, read_elf_file}; -use openvm_circuit::arch::{instructions::exe::VmExe, VmExecutor}; -use openvm_rv32im_circuit::Rv32ImConfig; +use openvm_bigint_circuit::Int256Rv32Config; +use openvm_bigint_transpiler::Int256TranspilerExtension; +use openvm_circuit::arch::{instructions::exe::VmExe, VirtualMachine, VmExecutor}; use openvm_rv32im_transpiler::{ Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, }; -use openvm_stark_sdk::{bench::run_with_metric_collection, p3_baby_bear::BabyBear}; +use openvm_stark_sdk::{ + bench::run_with_metric_collection, config::baby_bear_poseidon2::default_engine, + p3_baby_bear::BabyBear, +}; use openvm_transpiler::{transpiler::Transpiler, FromElf}; #[derive(Debug, Clone, ValueEnum)] @@ -22,13 +26,14 @@ static AVAILABLE_PROGRAMS: &[&str] = &[ "fibonacci_iterative", "quicksort", "bubblesort", + "factorial_iterative_u256", + "revm_snailtracer", // "pairing", // "keccak256", // "keccak256_iter", // "sha256", // "sha256_iter", // "revm_transfer", - // "revm_snailtracer", ]; #[derive(Parser)] @@ -113,19 +118,40 @@ fn main() -> Result<()> { // let config_path = program_dir.join(DEFAULT_APP_CONFIG_PATH); // let vm_config = read_config_toml_or_default(&config_path)?.app_vm_config; // let transpiler = vm_config.transpiler; - let vm_config = Rv32ImConfig::default(); + let vm_config = Int256Rv32Config::default(); let transpiler = Transpiler::::default() .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) .with_extension(Rv32IoTranspilerExtension) - .with_extension(Rv32MTranspilerExtension); + .with_extension(Int256TranspilerExtension); let exe = VmExe::from_elf(elf, transpiler)?; + let (widths, interactions): (Vec, Vec) = { + let vm = VirtualMachine::new(default_engine(), vm_config.clone()); + let pk = vm.keygen(); + let vk = pk.get_vk(); + vk.inner + .per_air + .iter() + .map(|vk| { + // TODO(ayush): figure out which width to use + // let total_width = vk.params.width.preprocessed.unwrap_or(0) + // + vk.params.width.cached_mains.iter().sum::() + // + vk.params.width.common_main + // + vk.params.width.after_challenge.iter().sum::(); + let total_width = vk.params.width.main_widths().iter().sum::(); + (total_width, vk.symbolic_constraints.interactions.len()) + }) + .unzip() + }; + let executor = VmExecutor::new(vm_config); executor - .execute_e1(exe, vec![]) + .execute_metered(exe.clone(), vec![], widths, interactions) .expect("Failed to execute program"); + tracing::info!("Completed program: {}", program); } tracing::info!("All programs executed successfully"); diff --git a/benchmarks/guest/factorial_iterative_u256/Cargo.toml b/benchmarks/guest/factorial_iterative_u256/Cargo.toml new file mode 100644 index 0000000000..a0abd084d8 --- /dev/null +++ b/benchmarks/guest/factorial_iterative_u256/Cargo.toml @@ -0,0 +1,17 @@ +[workspace] +[package] +name = "openvm-factorial-iterative-u256-program" +version = "0.0.0" +edition = "2021" + +[dependencies] +openvm = { path = "../../../crates/toolchain/openvm", features = ["std"] } +openvm-bigint-guest = { path = "../../../extensions/bigint/guest" } + +[features] +default = [] + +[profile.profiling] +inherits = "release" +debug = 2 +strip = false diff --git a/benchmarks/guest/factorial_iterative_u256/elf/openvm-factorial-iterative-u256-program.elf b/benchmarks/guest/factorial_iterative_u256/elf/openvm-factorial-iterative-u256-program.elf new file mode 100755 index 0000000000000000000000000000000000000000..222122b27b0858b05d302bc436942cd8787f97a0 GIT binary patch literal 64748 zcmeFa37k~bl|O!8y{gv>h%Lny zC;tAQ|0nN5QT2WI-0j?R&pG$p`(A&ee$FgKQJB1J>`#o)-ZOO;z<1z7krH4!D`S(` zFjm1dy3aofz7_Twe$&Tpeu|~hFNKZ57kwz5RnZ+><@C|{=Qmd~Mjr>=H`8Cc9%X(_ z=bs%;#^__se}6M$_ur=T4`sU#A8Hrnzv|77B>g=8;_0}lGS+%$OEGmae$t1^=WQz{ z{q5J^IPf)O>nstyK5>wlku5^Pc1&x_)Ne@SL~p-=NyW*e$SSXYR%5FOwEci zwYIJ-Q>&D@+?&c>Qy(pNP2O4Vn)FgR(lw-ONY{|AxoVMKjr0jS%bkY~pXt(HDmTt{ zj6nV6&I*)Uq1eJV>C9PK`ADu(Va{QwXI|8))+^eNuDhB`H*mm|F#>E%c-N1Yn#)KI5}`n2`G`r2spp}fco=3M{Fdu@aDd)TkfbE51| z+~59k$Ga#;K^qmcQ9&D(lkklpogtkeouLl}z7fx;{Z7UHUPj+JOs!rqEQ9e@&r^nF z&f8h3PF_)&nOs+?PEjf$Kf|qZltd(uP4fm7#hHu8bT-oi+MY!p3Wv_OB%=vx@`QK!IZC}i} zWLG|S=+K$rGDUHgjjigY`lbnnk%x&5mZzjiq0EN3>?B;;w*)ZXsHX!k*d>L`?Z zd^taS9DLM{<+?Rnf79-x)bHHkAj>^IhB?DySns}l%(zb*ky|~QIm;a^vj%*}@0>P{ zx!5?xsU1e2!mKy=2=hmFu-+pMCAWGk=4TmmuF+WkJ>^GpheFI)=@_0XV>YAt@Pr(A zX>=bNnJd$A|IpdFvMOeL^U#FM#4_gUI?Qr=R;6M5ckLR*dCy9gd2m<0cOmXQa0q3rRC3=0-S;%+ zf6J!0wm1~$-Ze_@p4GtRimM0h&ElS><-r4`Ke77*=ju=%ZvAI+kT2(I2c!1)f79_^ z&cPI;>2X`magb44)k&AI%%s&UGhsLCKaBb`ls^V#UIV_Pt+LFw`;O*1yP0tt_*YYg zyqAEU#sq!s?gs5@mg}5FJQ_pkl$PtBh5MH%a%w(|0Ux@`<+wQaqMg}Vwz-SOW1l%5Rc1RdR^!T#YezZ8!**Od zY#0yFm5fI@#-r>s?Wo1rU>rI%#rUS9ob&P?M-}JU8iy_808h)tGPQgnc=R~hcbH`y zVIeo*VQ%#-ws{5d6nyNgWyU>J));2o<*<>w%lwqrSvJw^BaMxq>%Yx$lxQg3N`JEY zGN?~2zn0gRaV%u6m515pm70<%TgqIS$5Gd6q~FapXVAv7?=jaZw09NK9ov~JOZRpo z{S~%3OSJo$3v+lgi4%yiKCdvsnLK zj<;J5HvHjk<@J1S#fp_<<7C5kKBtaXOuO54HraI8dDta|8ArAm#}mobJwg#W{bgQlK^S zxymS?hsw7*_tYq$KU2`Z$x7%g`FS2$FV7!?ZraJ#2cgg4qo5DmKM5~^9nrMMc>f(5 zb9QTN>fMnW#`MziO`ps3lQs-v?6`LC+Er{f`*Qd$|j1AT8(e@*-RtW%4r#0!DzUvO$woHxW*qT{?V+wQ7f!NM@)YV{>= zJIm0nF?JT-2!7e0G1~>X(aaPq33O5oZSXFw3~eAZ5xKR2`t)1nA&DAyUlom^oRrX*@caRoCyB1Tj?Ig z7j0cpkgM%A>wg4ZYScr3oqxgdtP=`3%`Tr z732s0$O^Ra4yN{G(O>l4rr9z*^;`~etDu{*vhLU~UEOy04R)l(V3)TP+L5WQE3^gW z9?R@~RIlRNLW?p#NDr<}cak+00nyfX}*?-!sSem7HUTDQkAKpF8$IM}FYaN$-I-C-2Lz z2J4HM2if0&ALd++oh#;I_uEdiE9b!2(|jC5^HHlq`?-8Lp{MqDX{^19?U47R%((RN zN~Ruts3v*A{7q~Fzrl~&wwA~S_)(CLkI4_{jv_z6V=njLG59LV9IJ9)`=@BzhmdEi zy;flDbqCg7;~g%yS9MLTQ(cpRCvWN4%hf=VE(BXH7;B(Jn>Vsv>IHw$RxL3A1pIg0}^^Yk#=FEb3@AT;YwLLMT zvSO5TbddE9>meJW7#l0j@mD+)Ge%U5bZ)qeVIy-hP-q52G+lExafb=LzOw|_xdMUzxL3< z%+|}mli)D5XBc#Nu+FcAuts`~W* z$H0Rvystvf>^ef@VcNLgn)lv!X63!ESB1WE>?1qNZ6w)F=qlbe(jSu)X?qTk%@t*_ zw+iKx9*uDw@=LVo1ih7m?sZMV9It?GwU1ZCe06-}r1{Eq4?D+T4x3uPf*IvC6<8Ca zKP$k`E%4S4ehjmlzR_k0e_0pW*h9126>-_K5Ar} z%1K^a>}B*D{JI~XE%-oxxgNylVSK*7hq-=;?=A=LKb3)cxXt0ZY25eh5{q`%j}*ES zq^^#}F{wqymjrLaSM&KVbf&u(CR zq3`W#jqKZFjC7}q;r<$jX3oQ3P#aC#szBCBetFxuojGTl9Xhb&exVP#$$Q8r$s*@{ zHGbF9Z`iS-{QaBRhcnD^_-AV$wmW_`3;x58xi2xlr40TME2eiUup z$;|mOCP#eNc7lI$-mo88`3rlGevZc)k(F~D{%#-i0(55$?CdwU*vvM4j{GI(TC#5_ zN7(S^x$dnYy?G~i4WDz>DALtu(0YRSb-Fc0&iD;>+Zu^19bT9TG_+(3fXHef8Y)^5^sHgzkNQ2~ETj$JHF={dB^H351ZV+b3m8f)x!-mci_ zx5<8UTg7dC*OdOV9k6HBib;!nlnz~{L%*EFUlRSUVEyM-9BDcD-J@tXUq_IK4%Nj>}JZ3ia~*us>5)vCNcV;PGWlo%#aHOkEFuRbl>Z8pt>lI~SpV&nAJF{Yo`nzpQ1&V3;Xj|rb?@EBU=VXWb|Rzj}OH~5-P^ntJ8w|-Hq;d|Ecd#3&URl!e&%$W1-m+%kY4|j7t z^8l?Ya+D_gk#VM;nOx#Oxw!xI6_KV*JuyxFdo$m(6VqmVS?VgtT)`gw)z<1%Pn-(tCS&>h*!gsjpU`88Ts$L9O9Upa}a&Q^L? z&4a9d*_2iL$z}E29&Y3Q68Sqpwwtl0p*YJr*dL2*lP>yrvdSJQj9Ececl;2t`awZf z5hozu%e0LLFg_>P3GxY5(z_P_^rSu^=i`ppC-wi* zk;gwm6r0||dMSO4cD8G3&jFYF@dG^OTiqkRYXwHU`=DB@9qiqO`1Pzl=1;%OU>7TS z?2lr_!|}VO0`}oK_^yr-YJCNKsDpXeq#q#`P5$6)%n8I4M8CJF^>qxk3icfS*K1pc z^R?mdZIy^ADm%Gj`IT~>($7_)7_rzK|_6-_i_=rs1mHoXjj@IQ2c?-Iy;LnRV>8{^X{LHoC z&9{wJ&|ey`HVb^j+Ewo<=sT8bjUM zr)0Kb%(ZT;eRp9TcG|+VWH)y0cUA91EdM22_!^GC;F<(H`E~fipjpE2gvxwiaj<9|KQ(29>yzb57r*D zF`ip6p4Gd|*kE52Wn(VW9sqm^E(7HEb=eH814;HMjwkxO=X|Hu<&d=f=U{*MBsSHw zlZEl)da71Ckb?{ui(aO^HpH1Vq1)~P&uhC6(A>=Q>^k6I2t9OenT_v#)vo?yrgrxq zX|0^2Ga1+u?xpnG%5X+$H+0)82Z+v9y<2?OzH%V5wO--(rtW?R>F}u^UjMw43*dtKO1zq1CGF!8M=-r1k?roZ#c)|NWarc2t zPxk@;tlg})?{U`qCepv-Fyoeemy$f2dkrehc@=!6yz2LM{Q>#@;GeY*`Ct!_ufN-I zl;*lgPqM!kalu-|1#7XkszuytB5dYFS9RS)oI9K7(&H0d?i*{6UPHP9@j$LGNSDJV zDMDvDW*ZxEU%MS+u!+kj&VfOe@X?s=tYA9VFMO`DCtZ{7&|QA;-Ep8T^B; zbz%*Om~8d-$>8_8=l7%j8nmM(d;#Xdu=PK1)y8XFle{%px72L^I%w7po9C*&(Z%D^ zwc}kVYXa)5#vGiGgFQum2v5j4l<}Aw6U;GLh<&mN#>g5+rgp`6%x~Q|)-y_lUdhy8 zZ8ouw+Fk9`6lFgv!+xDotyb4nm)Ns&w~avGhZ*TR?V0Mj3F`M@Cu)YD=!4hH5xS~& z1NsI(h4;S;?b!id#f0q;`fI$bA8{PmJ;4Xre`~*o(qHN;=>h5|+CITZJ_KJ9F}b1c z_la>DS7;~MGk$N!n2f%2B6Q|N^m`)KC=+2EycgocoJQ5Se(OARCXJ=B2llFd++KnA@Mjf^-I{^Dn1XL`f)5N{o+b|6{SU;KLL8d!xy2H+7A9L- z8i$SzFT|nGCEmWoO5!YbzW&$vnHKnlPRHv=?>lvT1#=W*jWaO=p!*nc6&Y`?MLf}r z?fA9YI*OMY3+}Ruc3Wc4LY_0H^X(yPVlU%lv1hL5ySD$2#GYT|>$RIs*A}akJp-MqK6|%EF&@;Jjt9&bgL+N_^~)!}KdA&w|j`&{MpPthIOTd5WtV z@rehjp%Y`+x7tE>4(q96EQ;ES`A@QV0{@(tGt@p_rfBbG+DFas82;z}j66e%O>w)k zQY*x&`9AfxL(D%cjdqcri@oZ#K_%CH*xakuj;WK^(s}~&i@oYwf{r2|5#QkZ%jhR> z6Z;m?k{nXG_|{5j*OPB-?nevyyK`CWcZ)oT`(k~-V=#7JSASyuM=IuWVc(ncGWPd; z-+R?6*hg9qBtLRZ2%zoYDdRjHH0}dlnP;ZN86n^k<~i&(#%qwVPM&P6V^{h4SpQFr z^Nv-;ac=lC*-o09Twj`cN}ZL(Is*D~)XP#|^8Pr>vbLf>{V3v-o1kY98y*R}vGU?g zrVXtnIRp>5%;CFa&zj^#t=~iIQ)#!F)LPi~8arfVCe|HTYpbv!@TX1N@KwIQN_*U7 zdvV5$;`T1Y@|`%_6O!N0HGWaB;}=4!!~jd9q~ChTGCP=!s^ zzDDK2S5-xsF?;U4QP^L!+vuDY^_AzD*dEdO}ER$o|H7QfG0{IK$l5sJ}h=o6} zvnq5Pt*0cP@}zInTKFOGK}d$g+E4l!T^`UDnk<3LOT{6P%C<$eh=@;dLckR|ZPPIIUZC&mHmXYdwv;{447jFJ8e>I2uyXrF4zW;@LliiK>~FrFh{){w^a zmWp=MI8qvXc&sC`CW!$k#J|i`qweZ@%b0ap#%#ycIO731xKoZ%hid}(XYnUdp4e;S zOB&zI_wd;&_(o-BX5pOpC3j;S2Ea$G#YL>86KgY5XU9g-9HF_#>33zBy8BKR#yo>w zHtFk^TIkP`V}-RWtqtLWqiuEv1|z z_nMY6lv6MBhS6Wd3YXKI!I=TOoNM8wR~&VgEu2eh`tbWdisJm~+}>ULn1A0c+M`Au z*f;EVlPvZ+u+L7sg*>UI&jubyoB4sujWdZ;SK#?DoJpKC-r=hCI$YItj$B0<)&nT3 zQs{o-5uGiQ=U_-jm^N;}iS-ZH>1S`_=KwJ7e=?qNR3e7>CyGPR8AaEmZSQ8b&V(KY zFV5AfC>8^~13d=aL}wsJj8`|0fUR*T(w1eQw?|T3)EME=GFw;iyp+$0*tXw+aYO8+ zxASqKzhHy7y{7Z0LE>BX2WT&Bg#x{ado^^gZ$D(k7S3|Mu7K=0#P95_)E2JypjTb| zxwsVy>=dvkrVfIrY5E3A{``R@twMay;ZenB^#R9sp(QWWVse0`_SI>1(r} zU6*H|uhn|0FIGwYqZk?XpZ}Dp?Jn?0twEfuXBO?*7I>8duV~#!I+}RSW8~no7#H^6 zIO~Z%!v<#IWAOEFp}*M^`wRPucET1B&upb_jLpo{)ZuI&Y>cq~XPM`1xs5Z+z6pE~ zV*p+j&hvv0CXd&me57+efPcImI_E0*ZjM#X@u-T}h3O;hxDfS%H_+R{pQxaEt-d7r zX=ZFLP4<-h0w>_4=FXYJ7&pR$d-V!^&^_kY4&)pTG7$WwYcY!b6 z6u->(udyGa_#k{ptoxvYiDwuSekO|dVaFS;33N6K^1%4I1U3aW67dGqBhO+K>Z7`h z4Wx6>F4zQmW<;zVW3L@wh9!T&H-g^b=i;GvROlU?eZETNWp-&*6wlaBal^HLHqSd_ zeFt4K#PiNoy{md+#s+&Oop<(E_ORTy0%+$tlvPRl=Y2jQD-Nzq|r`Av-{G zudx2bc}JWx&SJm+kXg6(Mc6zI&v^V2=Y)S5<9luE*5!%6{TnOAS>w01@UzDL${!=f zyanrUM#_8^}zbAQzal-r{ z-jZxHleb;ZhqE;2`<_y3vQNqL!qD*rpI$Y6R(0|U8{%>W-!JjrC@<}oY-<7u&^N?h9J7^Fu$MdH{;tvHsc|?`FUc&hi8O710QHz!u1*E z4D=a%SDWBN_8Ig?`kdr@43Q7nz9JvGX8phA!;bAI@}cYTznl*{PSDHFRT|eFBoj^t zWUQb=*nO@CRtfp)I;AcuiGh&)q_rydIeB@eUk5v>4#lq<>9DIcgZOoC3crr_X34%v z9}DYs4fC~J?Rf_GPg3uxd_9zhef46!OW@yW>ABn|JPiL%+cme~ck()ge^;pYzLWcR zWM52OoQ3RholWZ`F~4@aW7_azIR|_WF4OhTp3e4gnH2ir4U|_{ufev$*BP(U_)(qC z3h3xD_+T82CADSbiyB92%8V~oXna3*>rD7ZkMeoL?HuDgEj(}BGeyy_R#@&QsW^whZVao!ysd!051*xW1=d$} zSQkE*$6hdx^I*P>QF{jq#55Y!~ ze?t1866Z(IcKB3ARa%9wjW|g?mpPFJd8D)vX~Yt~m^Z4HsLng#H^?%&!8h1n@|*bj z7qJNy=j*7RUhN_LMw@$Pfj8g@WIG2tMzJZJ)gPW)yO!R8aSZ*m^;cECi*-dA#eg6$ z8to_KK7%qUq6+PSQoN4Jbj}ZQ9yu$&h_x;J6)R6_o-gubB+5YiS*@Kd>V%$yT-coz zROdR@KQf9~^h{>FjpvCFAIsShyC8hi{X9OH+ZbTI_B$>7##r^tL|;svlPtmafZu{| ztoQYucKCz#%xRu+!hGV-G~xRMduR3YTzVYmP;u^b>I$4gy@TSs3gVyGr@%88c-DsR zPhd_PE3y8YT(9IlJe!3p&tk@^Vd&ctV z6T3bq>ZH6li{*L&ZK5-9kR9l>)A@kv6vdXAvQtsP?+niVs?%_ublOg`$97tSs8bzS zn=&;sCyVo@q8(Vb^ENxrfWJ0;o2cug?P6mf>zaSv@h)ZF}Y&}V$Uat@PqWu~D14bTf4j^>RG!|jw;^lexnFUjIx+{Z51f>ZL9 zRZ710-|qMzQ(MYcn#-2-Bfa)6^z&feX+N0vXIEh^*Eo)EuhTw?_1hfB{J~WUo$ESn znRhwfrTOKWbgSYT{=bNRz#rUz?}h=4Pl=D?!gEEKN6PlHvk<$&T!H^Yd$)$|Fk6oQC*$inh6` z>u{#|Dy(bbcqa~oYGRi4?%#*@f{)}EtSKAiTsjtdt*la=h?rV8^dQaM5oPvV85@Cj zxSW|=gY#!NZcQvwvtg&_5$smGTm z=L)wocAUiz&Llr?@b*InAzx~MSOvb4af81h|6JpZ#f?|V~<#y)$eg?SDI&_ z)L)1*>ZHpt{#bvQzR`akAE!AX-mTI(c=biK26^El(AZi0suO%yf6Jn_rIGPi+8rXGEvz+ z4((j?#3j9}$Evwk#-dL#t#{2>)n7KY&N+6h;=C4g<*ze;**=!J7;Vp#D=KWav;3`m z?;4cfbsgJ$X8$qgYCMaJ^@d?9vmqaIF5R!-{ZiO#gv^#7K|BU!Xd2qngE76I^_Ds6 zoMlvQ8Ei3PDOuNDP!M~A-;3Cd z6aFe+FYtE2*ZKJ6|Mp{)Pr3)^bJ2dBo8^2NLv5%t_Y`82k$#MLgLfcM+h9jb{!t!U ze^7j~8o#kODAwAAbr7AWJ%W7=yhm;Y-XnL1=CqfC4*j(3wHbJBhvup=a+^6usn^K2 zH+laW#dMsAMPU9Z;$GIg*NSxqp6dg@;oI3UcJ#hCllS$X$9=j-dnI-;9`zTYopy`| z`L@_&!F|5gcJw13XzBbDD?{IL?zcfrTFMtxIdef4jO`oK4R6V4eZuva?~Z^gUxsyAV8YNw-UTe|jfzrlfZNM~QZ*VY#^ z4$rrde?sT$dL4K#6ZBWEM?0F^h53dvx%BRv{xOdqqq)-CJwKM)Tg&>_lppQ2T>~E- zap*C9;3wHK#>-L5p#Mz2T;zveKIR&{OAd9w9y<^3%KN*YE~&>~_6l@4)<>wXf1~5r zcI@pqyGK`TcjW2*nSRIjpl@O{W@@kPTGqQpt8=zTnEzNW%l%h|_5P`s^=5vmIKQJR zz3MHvrz-yJX4rVOvVT?id(PE1*y3`QD@RNK?`<>Q7^6V<4$qC8#B$K{{re7okUL`g zLT@?rlv>7AH+-bakL7c(-ol(;ZD#898O(p+=ZgQ9pED}od3iqX{OT<%b-jl*C3iT)a(C6@-E3Gxm0iMemmbOI_AX=2>lX3% zz4@0ti+kyeq0kuT@Lno^b}QfKg<9YjmXA0c z!I*BoZ<^8f5HBO%+}zTkx225Ew)Vy)six(6+_eW|eL+(y-J!QHZ*N%I*4UBKJ6iN~@}jnQ^THHblhhlVlc{C8-qyHqQO5#3 z9dB$(CC}G&>TP>xYimneM=I&Ad=2Ge*~qGz#P?s;-loO`+Uh|W7ow~i&9*YT&g!qe z`iiU1*T00WFYa8dFHS9PXO|}!M(+e&GC-L=7ny!mrO^y zbbObz$KBT1-qG1wjiPW&@+4H@4{QO^pWnlt?XafnaqkN;S6WO{wOE9gA3`S!ZzfKHYXV zwoG5#lH?6vg!-4D{?{=%K6U<)yr3t4sGYb-P5N%tJw$ZqM1|Qkh-=eKL zu$YrHam5T?x~K(OBhi^or`qmo(b+kmfeXzq8~%R+_qs;wY&d?CzFRd>XI~i)om8!} zxSnolTO9A838Z(l#Tz^H#>J5T#ZXK_HrrAioo&rfQSoM>wDhHmQq44)P+G}UThnsN z+THDM*g~1@B;1lHaaZz3>$=DH;H5qwYnp4v{8W&T( z#*W5#Q{zp7bBp84x!O#m^wv}x@flpSSd6JR>MVl%bkVrbr}bR@)8P@H!$Ny?w2}5~ zYrh%oA;Wbm!;+LC^vyYKI$MsmO@?5f zh3}~~#Wo(q?|UZbY%k>6dfzJV*89(bzDtMw!uO}rCuuJ*_wlo_1zkFb{t(Z9g#M6y zI3FLVQhqIK*ZFVxE$>4(jT`g?{=~=j4N2FzOV+V}& zRK2aUSzp@Nu}EKV_58W>8ZNGzaoN19>t@tnl#qD% znbOulBkbtVSsQxajSuPNj<(L!Vt6?hb~ZQ1F@HY)skW&#-rQ*1AZ7<+_o8hid!NNX zxba;nth_#PqAmzYNp|SxSHcmgMy!VH;WT`Y!1o3CCS80HzG)mkm2MR05Y)5i#RAlC zo#UTMJEYv7L?6F}_H=O53#YMcF zbP4)&vCbxeW>KBa-lE?#bT+PDXJTrcasqf4z~^1k*w)fa22WoSZ)@Z(IBaXOvAwkk z8a!T{5g6BJm+0(r)M;(!JAR$5@d@2%y`O}xq>KKfjUAtHP96Uj@p~7dPx$P`=M8** zfxcHEooL^=0DL%KXC8caq3nxWmR*=!-mKR(FaLZz0VkY{ZnBfR?_Bsy$-8f<&V7b$ z_$GPVj&I_TRY0ZO7Va%w1Sg+dYItkyR4`9*>#`zv)-R$Q@*iZmzXA3Gn^VAuceX?N zn_HTvbvDCqPM*9JjMXxoO+&tPyaS6Mj0-oM=3=Pm*a%CC)d_FORMYW5z6Ho1#JB9% zNy}+l3eWlmo&H0kNWGDxgeFz82a$g|@_(vwFPLQdp2bcsOf{!yF$X`Y@kSa-zG%>A z&pcmGGdKmL}&|5N`T^)Eqt|3B&fqirSrzuDFy`Tt)(iT{s#x7QW*s@3oLtNH(^ z^BVAXk=b_X|6AK(_1BCz#J#a&x!wXlB;C}q6nR^3*13=8@p!x*pU3YBc!HjgC+vxM zqF#^J>-Bm4-hemg4SB=fh&PJ0yw~UR`F#Oj&=>NBeGy;O@9}&6KEK}|@CW@Nf7l=K zM+2UKH{c8S1A#y=5DJ6?kw7%)33`LRpg$N027{qsI2Z{=L!OW~qiG>RrhQGFCeN0BXxTj_XH zd+OpA1b&u&sk5V=0zmO5iW6XE!S7O_1^r(5k2)KPZv*xl`fI@ztrR3U|9oiHws`BL z^A_j`8N~Gkmt*0v05+?s6KaIQH*gHwuznK(oR-cGiYtf|(t&)fNw>rjTxb7m*0U4e z6Hwp58-)Fl_@ax(UxcNhu?TDUl5kL^jxy36=j)Z?K3e^vnfJ_FbiVei#W(ps1ycN$ zG^|O~bE!!qY037{rQCEiQ*Kt2&knb7dpHjF#%;*w$&MDT`3cyLPTgoi&&hXfr#Ol? z;P$vf)22*;DEK@fe1aH$@07GJVt72EcrX$TPV?T9TGr~lMUr^KexEn!_6Ea@f;&dB zFn1*6_DnM)Mf#+8FckKAqRF^k2paj%V{;mtZ%84KH)qaF-5Yg#-C=#wqK=N%_VcGt zUkIPOGvPK6yrZJ0QCp`sH7!{@4Yu}cDWk&;3O$^P7*W3w2^%4=H|_H#qlsiR==FGl zA-_NBO9u>ZB#};q&trAxq5Ex(?Zy(Od=5YDRgShLeqW=n)!VSx*Xn7wY4PIDCU2{! zz@@?u@&V*!X9M{dfpVSc__D!6SZiYX!U(Vp_+PiA?>8}}Qu^EYKZf*4a55;r2Ne(& zm%{wOi}3z5i9^8jcl&58735f8Vv&wLY+wc%k~mGcaA~Zu1P|n!ESUP!4g8>`zV*l9 z+kols0*cGd?y%&izYw^w1P|oz2JV1-ic4kRvZPaeeZY@c>U-9bPUXE&f(P7YI$(-f{|uPMcfE;adA9*mJo_9o{U;`t=}!a4FrIR}-vOpLxJ>^e@O4O^ zgd0@f*(zd2mh`^^-hp(|36y>*@bI&AksWs~1J+GFLg`C@H(Jt{12+ao z!TYBxBS+4Kbo;Ed0ii4mM|`j#Ro7hAl~Ur3Ie}Oy!Bo#zq2D zc@mEXruxNYXL!Wda%t?dC3ql@*EJL_YX1cmO#S_Q2_DGTSuoLm)q;sWUV_073nuy% z7EJUw_G7sQ@~bVF=(kuf(SNT559A-VV4~k`!9@Rj2_DG*y9E>d+ZIgpe<;BN`GXcr z^k-<6=7PSe1P@>zvY1Zv9t)=O{UvxHAF^PgpKHNHk0VKzYast+3nuy{7EJWZOYlJc zW(y|zdo7sg?<>Ir`R`gV(LZIuME{c#Jdl6df{FgO7EJWNE5QT#H!YaxZHQ+VFOA_b zEz32K$E&sr7tz;PFqMz(Ov^QppJKs8f2jo%{beP1Ab+_96aCkKWqg6=-(ui7X1sxL zEASQzZU=tQGT-k5t{MgXhY3#ley@o|BNg^*;I73*eXPM5yWfJnz>}Jb>0bf9aX+&&jTXT?>Af|#+URl;YWdKe6KUH@_8}7O(vcRO#DPMnPRiC)xgAmuD^kw z2PS@2nd#@n#CYf?ZUQF$uQl;M1C#v7^4~oUM=b3pd7KAKdrsm~*tNhEyOa2O;AdOR z_JLnGipMqp^Y#Eg0(`Rt{~CA!_{aN$bVnup7mK`kfQxZK_@BTpex_Jo2>AZ9i#Q4_ z_aJG!3=@m?*;pEw>XWz;nD(+HZUUzIC2j$h`*u{`w@fV4{}(XzN8g@@mgz46(>~sC+@SQMCYI@WVB)95wqfYM1>=}A-;DTB zdu93_V3MyF&GwA;3Vu`p(|o%InB?slGyN`Lxfe?5uMj=jM==t@<9veOT_*lAF!6J; ziN6Ib_X~;sJPUtlzSR>yKdXb9E`?nROnY_Wg1-!GZ!7xaOMt(Bo19N}_9`&#_ep<# z3IvU?%NiferJlnJ%PnuZf|2N?GEcE{enD!L!0Rh>YOU@8j z(ti=S{9Ii?q<;zcIt#uMIBCIifvG*>0{?;KekRSA6(*MHtAMFJ60Zg({z$wInD`?u zJG&LQcxmkR5pnVM&jv1D8kHLLE-vYeDlKyjG+LxB;F9Gkhq<;uZd)Ztcf?s!@ zDX^r!2bki#5`P<*;;j;Y2blK7CEf%~?Gu-cZ2_kCNc=rus$b%Vfs2>Meo%r3^5d{k zLHzm+h=~7}0TaJ2H2HrEF!3v9>hDK^iC=QQ{0^A-Be63g_#yF3VCvr_v-~H3secs5 zq57s`{!#z#G1DKhw2#K~XTax7(veD+!u|vJ%E>xMg#Q!xiQ6Q$vpvAuVKHa{w@&F_@piA)V`I#&&d8UJ9`HBSxb9%14pLn zW)%v19{3&${&(Q}(4VtFLH#)b`~%?YOzc7el`k$k!=I1g^a8`40u#N&Q>mcBrLpNH zcpyLj0>*a9_F_C<0;cvYKpyJf?H3{*h4Q4o;IrhX{$36|Zkle^ps=e5yNh@p@Dmn% zE%5CfBBoCLTL~QN)EVU<`~%=o!00x|dx7aJg}7|&*TBmx_*LK+E&O;Lc!MSV4PdHI z=HCx2&p%LoBS#7>(?yhGW~gAI#2R_GrfMSz%u<(U^;Wc{VU}EiHT+U z)4+7zM3(=7iDmjnz#gPaOp3Ont~ef}fN!>>j|E<~MA$Qu=Zk@NM1alyZ3MpGlKvAj zU6%he@Eed%F29gE*JmJ~OHFx2d6xsf71aefsICGYeo>MC4--DWnEnG`9r;l#sBLU7 za0Ix@#Om4T-vvc_4VccXh)ZD=z;w1n;^DxTTJnd1>D-It?-M4L^iKlQnHZ{%#_tQC z6p0>$Kv0737F(jre6t6 z@+a|JV0jjb>c7RrGJORw$)CilfJr{ZWoN5_NxsBoW9xuPz9haCnB+y`+kt63CC&lU zcuIU1FpZzY_X5kaP}II>Of1uP1Ix2al>V-XW%?mtdB%v+^>YQ5=`LUzZ;5MxXwiX9Lrj zK#Au7Z}@s~{w)Q**@7PcUT{-!e*Zi0Yrt~89s#EFg|fU4f%REMd<>Y*CCYRKKMz{u z-wsUYxWr{+W%xsi|~UN8&HyC-qn2FX87w;Ptpc{v-&Pa91WfW9Gco&;m=E`vxEGk@L$>b_Fv*8(U-)vtzj2_a`L!IF z_$BF|1SWn-`j3E#Uy}YiR|xsM$;|&6usnZAfh?CZ2qi7|&}=+yqSXUG}fy>Qek&9famZ`r&CE`X53o{UiH$fdz@e%7E{?6h!<| zSS4`r(%2a#cpzV8!Bl?8g314mmf(T>g%(Wo2@59rR0$r)FS1~wzs-V){*Dqnkl$#* zME@x8W0w9r2E0m+C+6d;!0%YnUk83-wr+M=VQ&Dp(xm1W;^%~s|14md&u2{#Sftz7 zC}1jI;<3QROJir3;DLPOT+zM=h)ABF1g83*F!A46=&3*FTQK$iq7sbrNfu1>|6svH zzn}yU6E{_1P|obS}^7Bv0$Qqs00t>zi+`r{~YiJ3%`D5NvHh( zQGy5ZW%C66v)~WSm;bb+Q+a;`zR%MBcY&X<;6uPwU(%7AF6bZN>#j8C5AYXU0?YJ^ zf!{;A#Pz_-<`(g#z-uh{GT^5y_;TQ1SnyTAZ&>g=;CcTj%d@d-f!A~t@%6yPOJn~~ zf)O7AKZxEABI3un^F?_M6UTw6{wiRSuL>v*s-N@~;RTlRh=2bGeALpuF5s$pI;iPV z7+#ESxgZZEcp(1}>LdD}DWd$jsLzVmTIjjFSTL8D5)65 zjldn6kM9E?v7~Q&o!}3zXq7{OZ;Wv;-#^#l;DAU zg9TH2Z?<6S-#1Dy;&&EI^xw5$qJN+S59GI6Fws8^Jo6geY?8wIfLEctQ*ej+a}f9| zmh?XXCoT9eaFYf98Tbw4CwZa#)ipRjWl5h1{GKKKYGA5QTsAfzSe}oi{9PuN=^0>o zPL|StXkwZEC@|G8@ngVrzEltC=4VmyPWJ zrt-yQV?PC^@+95`Oy_?kehyfk6{hmuGqFs6A6T9Xrt}GLWC_dk8enRl#FK#Kd19iU zYhsyx4KTG|;x7XiFUWrhhWuMFmwyZ9@?U}>{}#;UANXh2>1Gw!pR=S>{;ef=Apf8R zQ~o{+Ci-Vf@Id|r3nu!vESTtjUxEkn2P~NAhfON>p6ExE;DP*E7EJUPSTNClz69fZ zodpy94HiuF%_Vpsf1?Ex{kJWc=)Y5f2lAUNnCM@yV50wd2_DG5WWhxLp#>BDu@XFx zSI#T)gXpJNFwwh9@Ic;c!9@S}7EJVCEx`l%xCImadJ87{+e+|2{tgQ!`X?=z=>M$* z59D`RFwy_sf{Fe>2_DD~STNClc5<=*M1O7x#{R7Z6a8ftO!SwR;DP*A7EJVCw_u{b zr3B-Ar3DlH|F&SF|6vJ+KWxE7|7!~-`d3TvK>l@LI*%ShH%CH)?E|Ls!X6VJ0H*WD zFdIDm2r!*DUSnb#3=zdoW%?*!I{zxu#{<*(SG!5?0jBe>&zkrP$I;IPrt{;nyc{s! z4}!lz?b{7Z=h@56{J#gL^Xo{4&yIJKO=WD81z!M6{#=zwzZ6)W|E@x=TY&lE6JLaH z2PXfH^gGqJ7nuA@I`>UD4@~O?neLw^?2T^HCxPia_iz*c1eom4g(iLnnEbC%CceUr z_3%IGKvchj)%f9`PSN>#m+JdXKxR`H6ll@MGvd@tgSZ zA~2l~k?k3GfjA!`@dLoLpV(^VuR=p<|FFx%{|Ve@;ivr~#&!dfy`=Kb_=501|!ljO5=UV6w+E&GFp=OzX9unf1R#>EO5ImwquY_y@M|{x$-WKRCz4 z3ua(_Zs~sx_$>?W1AfPXbCC;HnDv+ z=r7ROQ)c~m-7SyzuQD-SCC}sii%h%$nBw!Y|Jr3Do*pyhYa=kl!jdoFM2dlSBpCz|jlg28kYPtwWK{m~)z zHbCzHKr@4pG#=wi8=i33@W#CfUJzR9o)gEj$8J346~eQ4-cZO0;F+m((qlvu(L@kW zQ2Bj{R2=-NP8R=#m!$)KuMu5{clUTYLwL9@)SPT@PDW6k2eQAcNQ!J`QxlB~@e-$o zg`KJP4tFGt#|J%03|7?RNrckoAQWI)xriMk0Xg_sULN5(bNK_J?I}h)5Xqb1!mEe;sts8@KEsRW9OJ2IC>8exBXW#{1Qc>bMJ8b6 zNhKp;UjPp}(sPvYl#m*8ECOM_H)%v7K|Dg4^!StER3MQKl_}qTc7d~)< z5_^LmhuRnTIMjac$Ds~{O{o}aKfR%%7!5T~z>-HuEWI9Tai?*EI}lI$lF?8w8Bco* ze_To&7i&{Q@!o`Q*4JIxaOD+qNv=}CurCN{H^S*?FdVPPj57OVgu)ST8Z{b`KqBsq#sfYhl=Q}< zo@CI7#}n?rkSY0C(;OhC^h24bGqq}=oJQ$^WZcjzQIYPf;den zklVp3g7^(af<6vLB4yw-ZIQBg8j{H16-5Un@ea-x@8ARA9ee=1gLBtAICn`~aPGnq zxA!_h$6??p9%bT|_5rQANhoGF8o28T`xSvg@oflEVf6(8RrQ_Gr^K}+~Zn*|30o`m1{oITcmiX-IMx2ux+Th3RAZzF@yF)%)%kpP&%HXd8hgRlvCRSb!Hv#bRv-SBvKyC zv=jzDn1X+s2zit6ON=m_XEzMAIkThUl{k1)jMvM5HW)2<&44#N=ZYD1a~iI!yL|SH zh8b7PzkJ^59cuqIb#tHvym)}$2nFz-iXh}GWmvqh){eG-FWJ`8+R)hC0DBEDdd|FR zu7TWc^KDJ;x%{n7)tAg84?P$Oz~G0{Fy$ySB>XdU)a37=y$n+)gIEWgvYt+#r#jwV zO<&~oyWMc|+-~kG@-~$Ei+DGI#~Tf&!PYc%Sk$WG25k()=@Yib^h3ca@Fo;Zd&5Q~ z=#M1eWTxQm3JEa(FDSXe+a>LsRq#$NX%Ja1X@dIHbh6Y_Qw(abJkq?Lx(uP<(I9U6 z25S@afM`4a-;^S}VAfa?n89!&8A!+d!8n{aIx8@0vbh`^H5x2W88E?fhnPj^_Wxl4!J%hilh?1(Wo>h%kv~mfr z?hGcXJ0tkQA{aAFejrVFsZ7!r@uuPl!xN4Bf+tAR&=|~FMLf0)$6$kET@(4!39PZh zVd#dmk@jPOZbUrsXvi1!CBs3#$6dnS!HA`K9i#pkNxSQ}|V$P#_sk z1>FIkl&Qft>nSG$*(@pIW=j%=FA7Z&4@F=MJg7UA@`7`5KUPR^06cE29EVDZ zW#dpueJ9wqA?K8eVaOTyl3>Wx5faZKQ}czwP^tY=>kTGyT<8ZQ3XohD)WiRT?X;E` z4xe1x6#N0VJCQ=%r$KlHi{j16rc|5TJ&V6QSJ>$*TNyYc0Q+s)r1Wzk)!I9`;M^rX^= z4@5nF5s?u!e@eDL?h7YV-c%5gNuLo7;O$)DP&x@)l1{)CN@9xr^-aI2!hDS_L3*2U zdpN+|9{SCdf5Aow=Y~8U#7lUR>4MPbn6JJ5VSgh0J1tRj4I ztnorgIl?^r`q!x08xR%`Z9`}{l|&nY{zTG?arQ=GtwRyvg#9&p?+rJaFOH{o^2%m- z6G=q%0&o)&AuMfue|1)v-X8`FOh`qd3BA%)Y#SX2Y!cm(Q3t`-;mO zxIHzM?%zd~FwvawW%g+hBHUF1_5{fZdWt>x8u*FTw4;lHPzUiQp#t z7p5ri52YUQdk|Rj1d{2r-y;S8Q)xpe-i}=@TKA<>Nq@p?V4ES~gR@?kM3_fI9-7F| zNqJyls0EkoDhxV={-HM`SO~cfUh2^0F7UC`On|#%A&bOe+raI{-yd(EvpDEaQQ5(;>fBN)G6M97{%K*v0WGTV#`i!6Ml@bEOj^w3xuJL7~-yl zJSYAbYAJpw!GcRY_z3Yjhg`N?#SJ+FUw90eT5cx{IfL9e7;1)qWbu$Q$Qs0ekno&5 zp3fJp9|e>j*h@P19aGt^&*xDHNQn5To27M7BO zlUkJiiIP_or@;%7{1v4PnE`U(Pg=S4rda>*7lXSI9|_`JxA1!c{wQ9@YuQ~iqoVM=B%30(xFwYdnb%c}EI= z?WxJ@6AK{xNtSfuGKfwp5=Ll0jj)o}L%}lXL>Jg>{;)*gPSYCjM+^@qKMkSO!lxmz z#^xf}1H_A?<2WE1j<{iR3@N^On83|=;m@D!%-X__oW z?KC7(O`L`V)fJqtK*%2tCH*0v;YoS@;bZ{ICBy*nPHay!n2aXfUQR8zZgzjrJbpfl zhD>c`*5GAx$sTgql4Aw#o?hC~6d_*)2KoJ=WH4+5qEUYmN3YU2KauvNJf67M@Og~{ z!tDiLZ%|T;5gWABLgaGLQUe7Ka&Xd8Ni(@Pcs8zL$$TOmN#SgdFBmjW^++R# zLx)46m)2mY^u;C*t^AMMemR+j+8#NsL#3C}IaGS-cn+0bTD&3BTf}Wh{45-=8EW|- zQywf*JGAzEOnE3A$)S^+tU(-0Wpvn#yOjJFx9;4FYY-tC@wa3^P2QR~XCpx}#T^t0 z@n;n1&nZgCLBma{wif*56u1^%dC=5)dNV-hX#?}|UnJeuvbdoWe+Ym=s@VBMFw=uS zEfI(Z5z90qNOYt&T0BzAy%PFUp2*`(L4O*$;n+BA$u%V@8 z@o8up+84ESHYH8gm}TM5N{VjL@!AH2Vew}J`FUIVgOWHdTHIa0zJ1Y!D%mJUyd>;R zc*EEmz-g!u8Y+&TGIKl{8WuLSByi{$f8~z;GHEq0)Mo~kO4jjQb%`SaGsT}uqP4tV z91IN`aBiZhgfESSm*0!PsMWtKgwUbDQV6354Fjie!YM5HK_-q@h0QY&7F(=*q{gs zW*4?9PQqsEb_HbC!uB|wX^F-W!Hp*HM@z6J(SSecTC4{f>yf{7U!BXI+kM=|2slFZ z(+6El8Q4q3A8PTVa$h{{t9R31yKsl`*I2yx+bB5R7Q(KOKghR$JS7J8(Tti=gq{v8)!oTpD&ogAB*y1;FD>b`ZYy5 zia!PA35M}p1Qu(dV8l;tC@CatG^OIrovl=3Fdgzl(gu8p6l|arU?V`g^U0_$9*^K? z6|W|azhgx!Vr-8m-NB*P=8dAkSI(c;Fr)6udGoKXZJf^Z7d3mh+e({#aFOXGep9Q(Sm52`v1- z&7IAT97z$!EpY&G0l|qAK5(`wGwUN$;$p8Yb|l*i-Zcm=y_Hp2_F#KvG#_iv9(W7x zNW26`9%WyF19yIrnN`(2Q`6n!S+6Lmr@N=Cva<3kBLDG^2wx420TX0L+`yk;;g1tw zXA@3Kz+#7$Nw{tue!*Y7S`(>WRe}6rL!}3g1Q*rrBZqO}g6&OfTLU;~2ZR$PT~T(_ zK<2}<*Q;0L2%{B1jY`upB&(rs88x|W@o!=>8#<+Z1Kx)BMdwjv zNQwqKEj0E)Zu-T_rOw+`LY6>_B}l7_E+h)(qW=;ch_dt6_gZ^N*_JwL>m?<0VCB7T zydmS8N~-?$yWm-dOF+R|L=rhh{nA}FR1&SZLj|7W1O8AdkDtwIZCqfxL0A-qG&uEvL z;S=A}wXBrNabG`RBup*&iSOBwR1j_>0s&s};IsZ&$F^M%VC~T*T-2jWY0u{T@+7Pd zg{6Z9fLw#RW%&mL)lj#0bSW+hXdkAgW4nQk&V0eVuTZ8vVYM)Yz>V)*n(V^0nI0adpk5nH_k-EVp(!SU}@f*g>K0U&N2OGs; zfR}O}JjgoK^yA*D7CncfE*sUFC5wm1Vs|$yH*a6Iv{&8-mtTJV#mlFUN4ha5G&;GoZp5i*wRjTb` z%1>lO8Wp#l%B=y{P%H(dcojGEz=UgP{lzc@hBZ|49}G7ujRL zxiCauGr;=*fsjSbdfOy$VtV)*80PHwsDiW}KB$tcq2Yr_^0;8f@hj~|zEjo-4BHK> z^1zsh(srlsn94gwg(nB&x^b)kxC)c46n4j+Mbv4U+@XkZ=e$_NxJ*_tcWD(fo9a4# zNq`j~TpvEH$Y>guL0ZEXC5lPeSLi0<-5t9*%Jhvw%2WvG$n_$?XjIi&*V+nqa+}uC zNiCRwnk$^rT|mv*;kRK;ue2I)tgEys$q!q%NnMeS7t(4RF&jd@8E~$xhPLf})1d_I z-N|`Bx7fUUc5>d)Ef$fbM4YO7jn>)|41iT`DDfQX)r{!uUxbxbz>xsEF9hp;O`PHi zFChw)NvU$zL}OQh2uXTiGWik_U>+4~ve9h_RK^8*Gb5@CD2NVC2l5}l(xTq(lB%8N zW+n?;QQ`@ZAmX)9&?Ky9t`jgo1B{6jNxFaft89ij6T}-4W0~8&YdoISWH#=Zrdbg% zQUCDA$BE5+-Dm6i9$FP}HSmG!P%9LzHa7rHkZ<2=?CcQz0GMMw;?I9y zqcJK}L~?*{SZYQzruppYlZVe=N=yvvL!f>JFC_qBgQrq#J5}%>YXE+LsM$+1%~Tqo zyPbu$l(5-oN{QJk>n-K8hX>E4y&5*ucpn!8>JaYCgw-roX|up_;Hp|6l=mc>US;J- zx)5E}qr>U87T>H{wyZ67x8Q_tSFgCAFH4v#4eBtdDj4R2~Z8lXX8s48|mj&LgkM44CDto7>Oukom7 z{&E>0x(4FZ7(fUj+KfX|!-yjhJt^yDJ~>^(1G!dkmSuU+;&2eo-4DNwqDmG{_bia_$xbVz*Pl4bP8O1$taMoKC8ttmX~-Jmqu|DFCq- z(sGw0ZTOPd9xWbcUDNwQyj7@mHlnWk`!1k0H`GSrN_Bl-XdL(OyG2!+Z#~-Vky0Qp zu{KFo>?C@Rwjr3V*XMRz6n7ReE{aPf@X+5E8$a$`nU^>jI}|@HY+IDTSyP`@-UWv= z_A}^0pu@#b`DlU7>(+EpUapg=uPj`LVjjARwG)VCUO3JFn-loWn$~{$@MxM_($)4X zA>-p8UqX5J60(iz4t2+JVBf5dvY+EFbsX`iz*yQU&+y>Jd--_|4PZOSpSiXWQe)w?ld7rzo>oYN-wk zmIUjt*0>uyz;_x?qIM8r*&Dr|W`c}28vj9EHSijMA-e5bq)%Qs%&ww_$V(J4Tr6>J z6nK;EdNCTmx3(NIbf%ga$oiB6gAOMN3)2UB7siMrlA8!xtf6ER$^Qm)I)Y$QwJb4` z%OBVg%#lLDs7EZ7>oz-1s8YP165D3{6oVU{Q70riArYb9*TibXK#}Sc!Ip_>MqG%K zwmUq(TpfQCPKU%E0J*@N0Q~?XAv*fF-#&;_Z9uNr6!u-!!b%%S z5Uy^41_%Ud2*d;=qzAU|v;}q+M%)D}Djwjys$299{(Ar3cYh&f`7imBkNf%kXS~aKggU-={*73OHynhkLc|6AR{s*A)s&4=Q literal 0 HcmV?d00001 diff --git a/benchmarks/guest/factorial_iterative_u256/openvm.toml b/benchmarks/guest/factorial_iterative_u256/openvm.toml new file mode 100644 index 0000000000..b226887890 --- /dev/null +++ b/benchmarks/guest/factorial_iterative_u256/openvm.toml @@ -0,0 +1,4 @@ +[app_vm_config.rv32i] +[app_vm_config.rv32m] +[app_vm_config.io] +[app_vm_config.bigint] diff --git a/benchmarks/guest/factorial_iterative_u256/src/main.rs b/benchmarks/guest/factorial_iterative_u256/src/main.rs new file mode 100644 index 0000000000..359de19ae7 --- /dev/null +++ b/benchmarks/guest/factorial_iterative_u256/src/main.rs @@ -0,0 +1,16 @@ +use core::hint::black_box; +use openvm as _; +use openvm_bigint_guest::U256; + +// This will overflow but that is fine +const N: u32 = 65_000; + +pub fn main() { + let mut acc = U256::from_u32(1); + let mut i = U256::from_u32(N); + while i > black_box(U256::ZERO) { + acc *= i.clone(); + i -= U256::from_u32(1); + } + black_box(acc); +} diff --git a/benchmarks/guest/fibonacci_iterative/elf/openvm-fibonacci-iterative-program.elf b/benchmarks/guest/fibonacci_iterative/elf/openvm-fibonacci-iterative-program.elf index 888784fb83b566a98c6cdf1f4eaec7ca344b4f10..a7b1753491e8679dfde13c0db0ec37da854c13fc 100755 GIT binary patch delta 12340 zcmZ{q3s_ahwZ~^~&OQPn0!I)~VZ-4Z2_g`Dphy%IkR(>|Szl2M#79DXBx*204@M5f zN)$6hsgK4~B_=hADMoD`7ONOENHI}kZ~d+|#>Cs0)~}7N>9xuIuRSxK?fvfE-*-6Y zH*3wBHEY(MnLV4M-v-_Kdr(7c$ju%m8_XCRwk4_Tp<5-4(Z@gmi(pK{*R2`&EHbT6 zlZ!F>IBfE|3dZW{O;)$%c#gK_o$ns)HYKO62Xh(5dv5Qm_&&~pJjYpCsG)NwW88>f zgMNAe<+p!u)f$Y`$LPkc#Teha&~A5+lesiaTUgDQ3;ME*UkM(d8=G6gLNW|}^s78C z{7b#Ifv5D$2sK{HS!F`Ewu#T_IVRjlawos4=d47L;r|6c&@);0H}I~Wxfx2aXb(54 z^=_Ddhe-rm(yTnTb=9(nAaA4!iPnSPHb*#Cr6IJ8u$D6x}5nz8;8V5 zZhMV4MR=n=YIk)*)fflMX+)!rf=_r?#Avs-xxFmbp)Y0C4(W_ly*sK4M)kpw|I&>& zcve(G=*8D^o<;3$<68KPs0^7m7mo2@O_}?P1MW1ma zMA~V+BFiqi{Hm<_KV=6`@~bh)G5!vhF&7*1 z#=fMt-Qwmrlb?@`iZZ?t-d4XaL-Z}b6T6l)D&w-FjGMAAZ{L%C5I0gk^*z5E_lRC} zn-|9y>8JkP(h&b2T6WuM?u=pXw=T4ojd$utUN%|KDgSB8zOo3(&4s6VZQ*Gb>Alg` z&Ug0h*E7tp20VxKJ&?Ee-KxLb-cs6cmKN^}-j-$M%&S2jO&h?(6PL~nQ_|Rz9;=jOJNM7fRcbmJzy3`EaIKz*oCPx*WadnGe?&|Aj%4(dt%UN`W-%R!Dg=cuL2a3GT z)9#exy4!nB&zX+IcRG{Yso=Cf>F)I7y3DE=Uj4vZY1hx}2|A8=!`M&vohgfVG75mp zxPAr`i5|Frai`5lyYEh`PRkBI^@clH89ddSx2Db23uC!CAWfeZ$Hx!I&8_X=&Ma28 z=pv?xA{OLzvZXBFu!87yo;`vft#2+mJ80oW1h<=>vkk%ZcI#a49oNZQ2Sg7tKSD4+ z>!fmb>PH>cciLwi7!b|+ZGO}NdB<1P2D1jL&HphEPR|JSe|%&W-JMP^&ri?QF`8BB z-h@I_G&)+oY-44coDOQ-YOw#fv+J9kXL)OSOmbm=OjE3P5GBz1p!JwfgWxx*2EA6) z0ksZ(C;f5V-=EJJIN2L|+MPVf<4zgv$r*v5b^myi+dJRmPJPhhPMhR8@;qGFxG}rs z{J>^SKb6^1m5DTT=C#B=xWLezMZ9|G1iiMHcMbhrOdIZd3_Np>?gK{uxGun|z1!t+Md>c&)l z64W-0e>>us?*B1Al=tfx|GBc0MySi_KSv$o{%Gb-f47TQJ+vgFZ4*uYE<-~CV#@Sb zQ|4)`Lux^oXN?_v!&F+qV@GtKQI!ba*q3N|6sNE z#YjwHOga8em&3#+U;PL^$T(PV6u<;Uju$z6X9FBpqu zuAVO%i%E6|-#r$S>@WHF0(iKGFDn?T8+-Zw0$X8AWg!N%t+~Yile2~_5r^;wdnsBf5_)dJC=O?80JhSy47~fok1H2lWsUolo`L}dDD|K{E1i+ z9kjQ=css<>=ud!44m7N}z3({OsVKJ^_8;f<)3Zm4D(>VhQr*}{>Xr$w%>=4hCemOc zU72V*(QOqWYN%=iSKZHn~$$v@x(Lcg87Rpo+t+^c_shDY@gP|XU@sdOul7~ zPb=Y(kL`%ORAI7>(4)F>_>}Mu9~&^R<|iiW3+jyzT{=FyQP$`(Sr~ph@M-dxY*`|l z=FxL!hjc+Y9i=6F_1w*o`AbYz4cdWEHa_`0Zr%v}>JmPE-pJm)mYQrB3h5$4^r`7< z^37$d`SANfN=-Tx!gBue}mcskZe_p?|jBlSGA5yy9w6?0H{KNTQ>)$NrZ#=$T zTgMB_2E>IuW3mvm8I5lWpBvwBgL(LdG9Lyoqgkat{suT0({xQPB8JZ$d~U#7N_g*NbxF@4Y5?u zEy&Rtc*w%{L&rReB?o2gt9a+apJ|PJ=As-*)j3o27mX-e`*u?n@=r_+lHkp z3hJ-pI~m_c65%W z*hB@oG?tFBr-!3hi*z>KV!n0B$fT<_wY4a=^Ez-Hzr7?=o5MXzGxZz6eEQN1{b~rW zU7FydM>26~>yEge{aGeYv$-7}6V)hKw4+Y<_$%laV z+vLN7e2fi7k0__fKp7OsA#v$!0U5OMVsJ4y9}1-3C|T)m1Cv9Fw}Z(6#k;_FzC)K8;Bp&xg2|q^baoC*_7tCYpbPaa)1Dp`7%%0p_uaM&&Wd>I7%4etcS$F&5&FTrh%)Fd;&~yf-$D)I&+Q`akUZ*{4ki} zWQ_xO|0oe>Q|f;`in*=8Zjc2T`S&-l++yymOz_dpLR=b)31QYks5lOM@F&s_^tJ%J z0&KMlUJ5R^@pf=A^272Q@{{0pTfY1i99U#E_O}ou)EB)24%~*qq1XW50h=(;A0AP- zlS2gy2X*EFQ-g|A!6h~x044{NJOivYUSw}(C=!Yal)@Y+P>&SP15=L`KMq!VG&1m_ zWF>C^(|#=s1tfo8vXUPIQ$!UX_o3jZt>7e>wscD2BXH`H0G|P8+4wA&HhoIJ3*2hs zPr+R_z6ic*W6vK@5Vka6;4+vtipt;>@OWPMT&9&NJ5fv-ydXnl$^onl#IJy9WP@pH z0e6C_Th@w$4DFdIx}h?<1Wesf8N3BdUMTq|VCsgFyRwie<82C4vzV1RUDBaOF!fVa zxB#Y1Iw|GR4~op7O$Xgl)4^)%OuP+Dajcf|OJIsqn&h}_tPs8U%I6b(YBNV8+^svK zZ84PS(pV_?Cfv08jqx7>Zmd^#goEt@)7Dxo)qj?(>VE_N!e;Llcq;5!-GaUNFvNlO z;>+Y5{#+Q`0xJV+z?W?m_Je8v-UJ1j^PN2eEBz2~-@c|m$iu)DHtq?ov~g6ADZ(RB zLub)YP!A83@%fUKd@Pt8Qd|V42q}IT+{mw2Wkyn5)eQN>aKx2+)+9#a0Y&O+Y&mor z__Q^dRw;UBMuUwmH)jBF|9fd8b zMSDQXW7kkFhRXPEH2QQH62Fs8F6zOOoQQkK_*5?vbG1b53*O^b-PfOpp` zcCbe9jeBgcZQx6`?rjH`@aT1!lSvCR38oB#r4GWJg;oa3&P)TBEDz+yRxq^!-=Obc zUxW98#hm6~O-OD$+-_Z$FzKevx!<9&a+p!ca1&hdfO1x2e+8?DVM>-bCT<#xdZ^r^VA7IWroZ`i#k_25|-WF68jZ;%(q>Y|d>5mqIQso$ZRmZFJPu zo83@YVN=)xrUq4oX7D=jY7|g|=Oionc`!Mk_%pCNtRQ_oO0bd}QDuQs3{uE~0-b6o zg+X9-x124 z`4rq>lV1dvt`KuA<(h^G>HJWz?BNt}olU-0%9Z_f;0~2#n0kDlqkyfHX$6Bl;&6B5 zm_i>tiU6A<0zyrGle~rYy#hbTt(zJ;MP@v2!8?It>iu!=!ur|Z3?+y z2Xa7M8hZ#_4Y>;GA~2nFs)(GGtn|CUltYR?1sCJ4szd=f{BOx5NB(2J=qGMFQ9)-x zU^<#r>;#t}sT7BRV_#HkurM$srIPmqQxYnU0;_{qa^NA!kfVJRBLh)D_LfRk@~6R+V~SUT>5zHC5R_1b-LgO_>;Y5KDQ*TgfHP4*`sXAo`FU{b zD*olhjBJ|E#-p58rJ`|S39u$W@Pm(vRI+jztJ(^1*E77RKEX$)%i_}5U0BP{5AYA* z5*zD@%sTW}`UaQ|{uKv<^F{}_J9x$Fz%4WsTyEp}iO9s_%D_!p4~2HHy6297)5iu3 zz6Ty$5a18N^d>@F8fyh#vSsiIaJwy>r@(ZeFD{*(PW0iXwiR?hp~l9)2RGXI6L16^ zR0b}9O|UwIVGt23c@Tb52NgTo zrqQ5R4~i*xyKP(qK4{~@hf#3URxln+Zz9B{vx)eb`dok~<7bwQr-AeNzD=1v8Yp$f zkvv8;LkUPn2<2dkn@YfDFlEXuO(g8IV{xQ_ffM=;7F8fJFAw!8pLFrdb@WCS}s0#fiiqv~f8kh%G?~=&it6<8k)lz;9OqsDtvZq80BvuW}frVfi z$S}#TgVh@&viEf_6xe%MHwnz2Xh=+|!yTe_Dkn@V61F?$6!;+D*YJB^JEewoY3!LK zJZGuiGCU1{**H^(F%j(sci4CjxTZKDZwAjm{YccHEbfvl4Cw4rFxgjpF^TcNZ%K+G zABLfjhBS8ywZXkFt<`q&eJ?H5R`6(^iNA$B(Ra|+*gMHkwDBQu%A?A-#*Tn1CIk)u zzLzW-$M)9ESTLozbzyr8uAda(Zs46Z4t29Jtjku=0}3~6909&#V;4B@d1X*%G2ms~ z)0mmP>}eCbK{3F)rV1@X@>MWdjQ~?T>ZXxZUfY-u89_Ipb!luAjHU2*8#5zQP}-ph z@Qvsa;P$D1R1~0S8e+{m*@w?z^8y^LCpgx6#>R$t2 zwaLE(U$)63J(wA&L2>Dq z9^{Ec5-W-!dEs~Y~VQ@Ga3_$_O|0G$- zzwizc9;$+`prBqhQ-!Ghf|WcPoX6j7TB={2-s1iFrXU}^h0cdK3JvVT$uqq}&64~k znBK`^=q>pXFujAVlYE@ySV@)qQ!u@RRPw7}dI{-}`hNw}OUQP~->0!D_f?2O_FAVi zxZTL1A~3!04we<31k?WrLyS4l!CnGyu<>#5%^tjVSAvfw5?V9JOme!I$!1{Si5~{j zw5sH5z%(;a?vwo2V9F~~@-;BMyAG2)a-hgFT183!DKO0)y(D)IL|&x4XaXT%2HTsB zoAUv&ic*EygAs_-K%lk`7f0|a@Eu?}AumCF(htr>BBP%aNb2`QFrAnyhqi&~#9VRG z2&|fP2DeNa*gQg{JTp3pM&BfpG4o7@yvZ{EbF0d}neTh`eNFeT z&Z$#Xr%o-muHO3JkQ28@3J9<;~ArmfeaSKn1-*ysrW23txvs+ zG5R=c@<2Ibfm)LVwl@`M%}2g_Ak^zl(^$d5Q06j>7Ux&<`9W6Z za5{MyW3Etq=-bH{r%z}Ut6-iG|CjzH*yTRsS1zX~WOM&`$;F5HxTv(4``TQgC>rlz1v^o_6! z;Ls~rl|wemD&7gj1*7Xycxk_%1)|);m1M zW%7+NFa13GJ!Ge4_?l_pM5jDSqEppvu4;pELv}3Q>R6h;%5_m6-h3q_)py06e3ZG( z1LXgI3V*izH#fqioyIFN?~g8@%B%X5?9GR~K0Z0#Xm=UIq&mBK$3!50fNosnVcmYM z8#j1l@m-;o2YomArEWX);%|6)LT-%jYth`QcceqU<8LIaBWX@|NN>r`ynaWzqI;2E z62gymzh7_pZbxE?UZVR)CH_&HS$viws@%W0aJFoWQ$PN^$wFF;v)qVcWl@qFn%j7F zGe#MDuQj*2pJ{+&;BcS8XoOvuMf6>OvTkOaRlh@0QLd`z3wm6eKjE%Nw8ZMjx*RKT z_J^cu+J@9&n&@D4}7liH$( zpXcM!;=M+D*#aM?ZZW2?LDSQq4q>F*$tfESHn-=^{|zfka_STzA*t2pHl((ka~W}r z4~S!a-v#C;d5tsCozig5op$UT>A5o-0@iodG3y)C<$Oq*^Sr<10?R9DW_j~3va&>H zLejq(fZ$8Ug(@WB-+uSKlI;OT>FbRX_tWI^O zgERiDyEB_~8Gebp@b1?#=6&>XND~GC#x@^3i{@El>oVqjq#`pM2n$?(pQ5ip* ze}x&jT}obeCo6*`J$YruOue}$zm$=oU$~1q`VP{a?cCEhaej6CaX4s0LrZ(!V{k%J zs9sP9$EN+qxeaNj&sl3-0j+i4GC#?;hK9LQPQ$UkKSz4*%+mquJL~s>4F`)g>vziO z0OWxiRR*&Lt=S0h#=cn*M&S4=n!(i>yaNp|+IfCv+JI&h^acE%H^aEoL5;B*(C2jf zhKB0)ypq$*-+YYuuS5Qlp^Gjz_vPC%6P+yqs08>MnR9d_lgIX(koIhwJ9&JnJ7rjE z!M$*Arw2F|Vz~I8RCmVs)ZpP!9ztoHZ=Ir9y@`hH$GV65(Z zkk<_OAifyu0zCih!9ZE8GfaBA)IX~rL(`3kytZ%-W)#0wh;g69&4JHS=HY>3iM}7$ zkQ~?MGO92^J*+P9(@!_58Yh%F4Bc%$uLf;0KQ#ztC-d8bs&(HKzV_ZMz4#G+_}=$* zV>&M%{ARpyvFwx)fmQutNLmTTISvcmZ!YruAxpA~Ic*j$LqkAf(?~^E4PTo(?al@= z*P1y959BZ9fgx$hs>m>GPf#niL}9@qzL`9s=ziUKkFpaoy92Ib1 z@d^{Ar4`fVL#Be<1)V1t$lf-<*yNz@2s{pzs++>bM@kP z_}tO~iDH}M*h$HC6>t&C*4_ehB0aJ(?p){ zB)>f|Im<|KoBJIuqluwOjsmMmh8sig6;kA#OEDjp``Lb!%+G{ z_5^R79;Vgv52wEvZ7w$1W|ZoJPj`Ik`N|o6wN}1=hO7Hc$S%7~7KwZZK37vscA^&= z$J=I13r|>LvPo8nduBctT?48Dy@*dPJ~h03=Dm7)1@H1`QQY(j959hdi44)FmCt_k zsno-eRD!l(H|>G%sKj7(tvyUO9ekT>v%FKrEH&90Xk_S&MdQ219n>6?0xc6KUF6yG z?=<+|@>8IHImu+@$mi?lEY4Y2k^RJqxeF_@mOo)G6D@j6XAEayHV7X&C{sEF8T~%Q zSI+)I@4uYSnX^m#kpD2JZ}-wCOcoB&41AL>AICQ=u*G~>nHOW&RQBAAgcZU^7m*)J zpPXP9uI8F-Tb{{I<)ixkCTpedSd*o6GodLGRlTv;WFLD>wh=m~hMH{TF#N`61%Gw! z!`=?mFcw;N%cEfV8fv=T-TYa8BbX9S=+=O&h&BoD@Hi~!t%KlHe0%W8feng48oW`+ zUr=bW+JSH+@`qPc4xYPa3HQ$H9o@Rpv?A#`zR3eO_>p<{#W3td6h|sIautu5KR}zp zhs_@vz74|umg=VY1==OvH2}Z1&RGWeZoV zF)LQhTRy+MVl{O4@x_b&q1Ewuk3ZJ^%2OtbLEhK+PR2J|Bz*QYzy5gMkgIYI4_#}r zZV*nwH^oeZX_n8MzXCqR9FsaxvSg*yndF~X{*I;{;?YZbB;O1pRZ1GmL{!iNT!KY9 zn`AK`yQHXhdbmkCNE%y*Y&)+92l%T?vb6*JlO@@DPB`yUk)@|c@S=(&FFiJjq_Hc| zwDYIn!8Z9<;99hn;zGlScf+95^3@8~k#oOfW$!_7&#qYBq2J_W|GS=vhnZ0hi!ZoO!h?5Sv#2QDgM}jN#L*ONc}k&27&_t#R+t2Cwl%yr zxW&dP;F~t?1HNVB4Dec1D3Z?lfvE!`Iap2@;z;evq5PSAl8Z$|au!lb>LWX}ZpwMZ&LEfPqJY$xqfefDa87em1547eny? zO^)3pGqODRVXI)7#r*x%NnSdRh@`Q2tevz)D((*MSS;It-i`#n3s&P_0X}TwUEpnq z53AkKKLw7m$^QlpZoeA)dpH6rGa*>vH&FQA#vNemjD;4FyOSdXiw5c}6-*T>P6yZ9 zxG$I*pyXL#bv`3|QzH;il%W)+LxFmv_)#$RSn(WiE0~sAGO$Iml5YdkAum$O-;u23 z?}N#sikrO1U^wZij8kAb2P!@dUTWjtf-7y@2Bwpu(hq>cDuR3-oM7XN;B*_O{s9@K zwv5YQI$bJ*SHL^@4{NfmNZE~Sis1QKB2o@xXCU4SrjZS!r3HKzOx?2fP(*0gY|#xB z(c{3>4Hd!L!PE*RKL@66D7h;Kk+RdKP@2Q6$O*^>?F3UlRe=j&ilkFg9(Rw33_6q0 zl$r!q=WXITusU%QUjmb#GNgX@TyrKePn@MY2^K{8w-$BcdKrpL}DcTKu7qcat(hHkw&ZvJPl4k z11vwFz#%Xl`|F`V6*@Z$R{G)KxjjvRkVk@#+PEvY(Z(^I9~LbVC3F@C1@+89Gr}WT z$wz>xL5fSjO? zFg;nR^4q!~3c{8J4ZIBndQ7vXCq}Wft6)`NGMLWkiXQ>fDO~XkFg@BSo&}}`ilnnL zFf~Z={H{2nge~K*d9y4>T!w($^;@`y95GnB?*}kB!lMg$4xT^A5j~~;Y%q0P@eVL` zRPjkLwL4wv7Zi$i@09jmDTMc~@)=dV##g49P@<%eYXeUkUKY6I4r4G^YeC}oXRgLm3^6Zp1`Yr(C{OzS>?DmxDT1Uv<`5q}CM z`yx5mbuj6Rq_dk*Q2K;#c_zE}x2hR%#khM#wX>i{anf}#PPqJ&XL@;8^fg6Un2(Ca zF)1_1E^utWAjg9nmy0JnYRoY3(kDzt8pP$`kzlmR;yQ4D=!=A51s}5Uo@h)Xb|(%T zY#$WfwJE#|rV5pT2JnaAHOQa}+a)Xc$6#uJ;@^YSH3sSHF@lxch$#!+Z;(O`6zIM~ zDdd6Gy$8u>N>=jO;Kk6lLJsm;$x8klcqyN;F?$YmQw63`Ec{^k35(i&;4sMV1S*)u zVIR!UXk7r)I8@5Of3!rn32T70mj=MNy7_8jl2*$*`?B$Oh_9EIu1u_njEd7-;tEAH zOk;P0>G~sus(}1IB`f)Ea4F<9QXUm6Sjk;rx+YN^52h=bx#%O=TNR6;r3|I81`1&- zg0b;3nC@}vp+E(+IKfKa0j3+IH>CV2$x6NsOt(w9vqQ)G-f|J9o$IC=50z z3cLW#gCmCo*_F>rW*zsKGx-COP6C>&0DYA%aFE zorQqu(pIq(TyNuW@GJz8NCt}pQ&5V84uL5M6~}vTk*wsOf@#bZUk6)P!_GfJ`7Aj zrg$WH3b-FK$Xi zTg_kEoRv%K*%;*0u2gcL*aEBt5PZ)AB9yFH#;&#koUnrbxH-v7m(L<;?0ZB7qq3MzA1yiW+mYn;b7X3S z($`r$_yqriXXl<+F5jGB0QZy%Edy3*K(B(yY806KQ8SUO@}iwd(Nk>3hr(DnU%WFr zx*WN!novFm<#PV&&WF5(rK)0$ZHDe9Gz2#y*03A{m)hiSfe%A&1v!T8n$(9nlFn{` z>mbLIlT|(@MX)7?JPu6d6(^)H{?zl^z58r+Y=q8H==6mWg~sdP6E^vq;6@w&27JcG zC&005#ec1aW7O3+4vXWYc@Uz9smME!1QidaSd3#N2Uh(B`f(c zuzJ5t@;^&f@@r{%qJ=8s1{BoGW-1WVN3fE|fj9ES^%Z*hWdEP)w}yD>Ewl&X7Jjhn#vU3k%+Brh!_G;U-jIa91; zQ!wzvqrtSSD*4l3TA64`Ao-hMiYrs{XJC4F9Vxk}pNKQsMM?j0Fs&W2lF#--T$FDy zf!M5=!4BkNa^5X=Q7SMkAC5>5I_iai;tF0lz7&hNI8=3VPdCil?`|dd<{dOT(*~ZylGv=Q;%ve{oCMQzaHJ+&{2iEX0#YPz7$u(c z-<1sr8!evnH%j(_=|O*iP%A}+aCuKzLCz#Z zHC(KC3z&9xJX_s*WV5^?wYe@?9Ot#(}jaJHVSd+=lfZ3;6%w s7u{S3c=UIDv;ZH7zs-C${@&u7tiMOT>yt2gKmM)tWK?6vjk_W?V<57-tHY=)XFk1_Vi_3erC`X)_gj6Mb=EQ~P?U#ZjadB|j`+Yd$u zH&-#%+-Tyn{k?qsjis;Mn9wcNWgOMqNe7wB*u(;oH?iHw_2=4VjiZ>Sk_}d8o@6df z)A-Tsm|T~GF>aWwa{e1Scl2Vepls&yesEi4D!Rote|J0A;bmX`kaXK+gt1)1%fBg& zX{kr-Bqy0WG<(3FF(|CM6co` zcj7_4U^PUB@d7o`opPSvj_Ad^T-~&tJSJ>#rpnUR#En{gcM&z6O)q%HWOXSe?JlRm zDxYz(V_;t2*2!zbh9|tx+P>I;x=-!ukO+mN}9KY#zb_gC|-1`zTH)CUO1=RkGU8WN%LM)ab-0qr+tk zm*(usog(H$=j+Djd{6XCx^bEBE$-XR@}T!JUmUYTFaD0-jLC`cUK7o&{jGG!bzao_ zG14~ohV~oTnOE;<9kE085}l8V9iz8h=i6dS^b*Z?G4^AvqWBDVMlttGo$Zz5ocgg{ zCJSgY&Ts@(Wtilq)^=XsieZKwe!CxUvVzm)H5d)C3lU7;1<30TG^}B#-vP&|HEaA&^RL3Zw>Ch?sj81v;qK#KE% zudS2imb9|mg&(rYSZ8nmUw!x9?uo_)w`p9!M7+S`QU~=p&~(B5!kZTYl7p~z{DFPx zF#aKi)EmQ>r&j1N>qyPiy=QjxON?kcKwgS8jIdE@#^rp4Tr$Z#~0j_A9|O zbtj$FF}3xAX_zg}k|cL3IPDAFopDl^;n$m=?)OUCg0sH}z+!|DaM!(f7Ok^})@3X> ziv>#e&uYxS18F~*f2Y%OLY=R;6IFpFeRyDch2GkSFHTR>JLCAy^ufBbgLkCIF0AjM z^`fq+ts{34d=TGFFW3#graW=JF6GpD3N~I4#eDTYFdylk?iT3II0e6c)IoOcq*LNM z<)esn;Q7a2?5K=%28wFE_xr9Dq-nZQ#^VOf z!-V3sgD~t<`O!f?BVpm-2}F&9n-Y7qyNp`Q&my+FKkWzw9_hRnUH$LDk_b$sc zcF<vyB~fJ=jxmE-v6ZEpKZ|!3KRk4d z?(F2>4aHD(a_{%It@<*CCF0N z6>T)|#*vA-v5g-ciG}&+{K`ly%uQIB(YPjld{n;f{UyIP$|m$R7h{l%U&2al`Wi+L z)@T82DP12F9#1@iAUD+M%afT^(V0(P~@kTfDg}M=ySxe^-_tD>giiJ;j~$8YVjYWmy$B^HEbv{(tpJ zerW2ui3{GtTFYdP_uQFuP%&v0)P&6Y9^X7IF|#$%Z60*EjFSvaauiriYP}0ha-d?( zo${K)oeH^CvC+yun3gkCWN{~Mm*&M!letwyimeD_vx-PV5gDq8;#0m+4;1OVc6w4! zQcsf=;vg2tU!J~1tL42PoE_AKBNv8?h4D=fj?}Vw>w`;-Hy<{c$?sacoWD3DLGOB) zx6Q~2+PK7Iy}Nw9d{>4_j)or(hC_Pj7r0xKWYb{{*CQplEz3>G)iP?6k{dA^7dUr#;zZhZ4|y zKC5CzP~b9?O@p+7pRCv#UIVHHJ&#WgJ~e#q>>*kV-#2?$%(!Zk4TOvml|!H7afq7m z$_4!1`L5KxFsOlW3%1}s_`ccOU+M8aCM%6KSr=q7L-2x?bJxyZvq1K>aJk9WAy*pO z9FFf*Zh!89Nyur!laSfvce?T)@-Wz+jyG93f~e;l{kbY03QCdC%)_t>jNvru826h{=M`fbsYy@6N!tj_+Cge5D6NJ$e4tS%IsB zpFV^<5+9cBZ)|&x$u{Pi>}a0J=HFwoJkEpIGoWE`v@44`igy_^BT4AR6&Q@J1|ud1ks% zUSkV%y`hFbIP1BHuyy|t={4M0k=FkN#QzoQkX?i;RjA*I0}A!x!1XfJQOg?CyQCYl zrlCEDJ<=-YE}vWZ(5iK2^_m4M7gklTMV@W^*2DkQp5WgvSrmJ2gUKQw`vTvI_`W9R z{}&thnyQ?8J0xapG_Cnqf^UkeVAHHxuy7UpjtMSp7?uedk;T%cgnw1#*5>J%x^6lmKYi2&vgn?JHHQZ zZsZl!nRw9JQk|v6@pteyl7C&D;Gw5Zku;WyV6byKIMt>v0B=N(D88urIbd2W+riX) zE?JfL3^=f-7*5iklC1Ra>&(N7Z2kET2DAbzgMWg{p-+W?Don(tmTl7~gPWlz7n6Px zxXh-%A8g7YApK+DPi*>40UpMxYz9YQKn)T}XRm^(0g8VEt_2rCKm~p(S?RmL)F8!Q zf~f(DFN3qeg%D8w@Fe(>1j-;1Obt>T4c^FKUXc|Z7i6;Q5bL_e0x~ed{Oc7HJuSEa z5X0eMZ{tTT^5(&c3cmz?9gN|(I27x5NSFztq_J@DP8&yp_t-cZOy!BBvsf^dr?{`d z0$59<@7$yts*N{*Pum)P2wWZkucD4Od;oi^ z8s^YIc-h8-0ue{0*i4ndd>HI;sg`PN5%>*T%O3{Q4la_;mVz(ZcsaPs#*cujP@%G4 z1Evm$0kKFr`z3fgI1~cXUy!WyonY#r;)`IM8(0qr$UXoI46)KX z!Q=tOK}isFK(HDT%8(~n85Dp^`M$MT9`Z{z#1u!l_lix0B8GS!m15AFh#sU28FyUuY+UFH9x8>L;rC@E*Pb+xaV!o^<-a|(l zku)|Nt*5P4@jURAr8uQQK)u}pJ_5Gt1s@0RwegqWTEvI4?-7iQHvK@be;e1>kYEH< zOpGb&Llr||aK*;M!PeX1kUd?;Dz{|Kf7V+aJKcXSu5^xeSZQN_U?2(H-#J-~FPR0d(- zIW~5I7uz@rOs7s|-y3}0#&O`lYQH`K9Ao3;LQ&bE0{c_cn7$>kxzIuGlCqe zmdIBIkz@JOkH$yPLy|}udmGj*ar|9alNg<|!tG%knIZo7qp###vRRjgqHTTsxyOR( zIY}g)O#mNX&OduBa{y&K4mky{mM^kp$Y7ocKCXO<*w_y)bf!2Yec(#y~)h`xUs*rawaYZTb(v^gyQa|1lJyaNTAQ&{I71S#uOc z?vt!C901daUhyx$bY5571g0lK#lHem14Yu=5im7K@vA*?@VUO6SJY-&j`#!$a-R#2 zDAohAbYBUW98o0KigjRegf$0X{}PxwuJ})2>Zsz7fuh~1viw>wwR@*5|GR-WAGIg* z&`t4^=ckyWdFcN~Xz!j>KYt9q*r?dSJ_Vaw6dSAy9JWHS&b|aU@JBagO(rX>FIYGX zwjviRA8N^yz1a_LKrU;-Avit>Vr*5&a2RtZWYU1vre~+LSQa?5FC|Sa)RK zi&9iD+yg#f(+>ozXKo6Xd6H2HlFk-@_wuEWXK5zi_jrQml4=3goObBK(oD;eLhT&5 z*2WjWT{i9npIBj9w+K`h{Wt?IM+wB~U@BiE2g?MLy+}G65RUOUvw{z;%S^ni+5t~I zJ4Dnw2Zj_U<%KxsuHaAC#e3$Yn<6(Vn2zG+WcYa|nDzJbZ1B;Q;#rWI^E0x4#AIYa z{02A=j9ysW1x|*&NElY|9vfc{$2_`b>(5_caKvVC6-*VX0=@yi1zraMRTvu~Sn2zM zsR4>ju(}i>`>~RhenLd0|F(n-*1>>oPn5w%u)0Md{Vyde{Y&6ku(v`E`cBD8e-S)~ z_wZ)TrEaRgoKP(MVEGAwyA6CD`a6LNrg1ocVraPHOYj5ZP$L8XFqqtgMZnrodyj^n z`95!gR>R-%X5sJG-UJWbvse=u^;WxJ3Kj{E-C(*)NrHg(rvH_!^k0FCpl^`+>5+n! zekPdiTolg+)BVnTbdk#YT_i@91j^t&7+i;=RA^iU)1}ZJ7*K{Wy#y=!@nE`+Ix6)o zl9m1xn69ewWQCQ6~l z`uD+f|E0>;v3?LMz2SkN#wG{^pBUyJk~naSO`ibXv&w%HFa{i00G2)63vRaQPf5M1 z@O`jjiwZ>t^Q8C2V!c{M6?`xaoITLr!s*~L+^OwB3FM*Q5Zm-`fQyhHd8n_&+ zhOm1qIylH*UMQIEKM#_LPg8B`f>igXz+bDx?OE?ju;~ z$AIaAQ1N&$bx0%+j))M{phB7j)PoJ+oi=U)U%it-W4{6)g`OHj_8)++7n-6xNIn8* zukm|CN5R*@*1Uy%fAB@*w>;*7pd5l4n?W6TAYwoyjn#vXL$6%=8kjCrl}Dob3Rd>L z!4yM^;-=W8$pnc`hwiW$Xwz%-^8|1FqeO!2#5y2_q# z4ar-3c4Vfa!gLNIL6-pQUK9;&}YD-Y!5u!*icxr5^{T4l16A zpA9yi;vpS+ERxQq;-`8uK?T-IR{Ag3P0l_t! zU>BI)Xo#e_yU9Huwo}gv>ufveH+`{EFu$F`oX+ zc2A?Njx(@1gfh})8P|zz`hS6s*!bVz*KMpN!rVDK=J>`0u`SH(|csa=fPR9kA{F6bW^g@-v+C9(WFniTd>mizdKj7PzkbOpk7Q< zh8dETz5=|8_kFfni}Y=Ic58r#UPTu{8-e=&jtgjdm6|R22Qa;wMWk4IiYTfy|!J5Xluf$85z zLybky!Ttn3Y2)AwhJV5H^}YlTEh98)RLT-aX&o!auoLe9)3U1cZD3lLXgwhPzrYk# zrsRzNh!S)=MDi10iZa?q$^LaPtsIe(qcaf|Gq;#PhC&VYbq*HFeqtY`42SaIhg83x zJ|D!ai+JVtUW3Iw`6T2g`^n%B&`t6qb$bt(?#)$$y1++l{O}O$n{*4eTo!O~h`4pD zmHZr<{uK&TeryN-8BFtnD(D<4)(++O*kNMrP&^B4@A0i7+|y$%Q6A0q3>On`6l$Oz zRDo%yYnKf;N_q@|a@iH)$++B*`T--DwSbP3oH`Oe;8Ep)8Q?%0Zvltc*fV3axNA@i z_ySCic_An#977UPDjtMGWCKTl=_(*eayytF^-stKOukP%>Ti~O7EDk2izSzh5l`mQ z10Hr30(z1zl0i3ftauQvkr@_&FO~RRb{kBm?r~Dz4M!O|b*mmPn<6&T&9c0GV0+Ns z07pK6Ahz_Q@h<_&#BN<0AckNJn0EI#S;0E&WVDAV{t!(2!5pd2cmM_Z#veSXh24{% zGjDEwW%h!FIl1`@78dy8e=%j6mc&>5m|$4{cY*Kv(QWL+I<}L)^P@Y+`Y#Msn94l- zhKt|)F~mui+Rarw^hS!&48`|+@{cY)?nW5?j{~o`k)n4k<@GmG@UKl?zHxWV^n;Am LqIQ)qBv$%AcH<`j delta 12501 zcmZvj3w%_?^~dMl-Mb_hAjyV2AS9P;5<&<`2q6g%VR{5*b7zLV_Wz&HXS4e~ z=ggUzGiT=BI~TTo<+$;cV{>fhwOvh?%NWbO{Zi8WUNw^$qmKaz3ujEjSIrE39y3|Z zOM7BE$1Y_ow%)|&r4RD-F3aA&Goe#IPm;#m#%|4H?!mXlYEwosM~cR_qxN5FnC?k> zn@Jttp4gjrq&R(iox$8rduPge5Cx?$rJlNFoXboe>;FJ6PZ@OJx#|wj^B{v=7>%vnb&zyC4 zLe*#|%ioHoont=XC&EX1GWWNX#X9v$R^yaivZ~*Q>Vi?-jJTs40UjTj(7F9k{%X|j zH1-4^6PcOnKZ_`bd6M7O-Nv&zW=GGTO5^&$7~kg&k?(c=f#@VeX13_B@e%4WF-~uS z7^kb%U1u>~7ab6<&3u7*{<>hmj3?S>?^dZ$)^EI=B%k{WM;dvdeD2 zlGXf44)FK9IXWrY-{v+(N^^Goo(pG8p6hhPP+%@dVZpjx4X$j^fe}B6*)bTa(cps z3y#7Ky4#t}ey(Y(EWsZyI_z&NEB9l87ozh9t7Kk7$c=UiK<&Q5_JNCzz}}@(Iyh2X z7iv?AF7TGr?w+*$?Vj|b?Iib@`^9&XU@VUdj?}sfJfzQ?_hlOGo(!WMOY8z~>@&FM z0n|HqwB3;ss(F%s%dR+$e~Q^L5_nYFY#nAR(=v7A9N(Lk6j^l6-6@=T)?aHaTj$c< zuA+1NVp^(Rcy3GIqEuI_C;6m~MXTpeGqJ8+>7F!j`k!@A#z|dfQX;SDdvMCNb3b>S z#Cm|QP5WBQdbq3^a~s#rVUN&*Yt{&04PhUkozKfWY1k9eHRj3Cm~dxXb`|M4T~7x* zNvfsOCO?oqTQBU*!}_J`QzahT5Htx!1W##SIGm4la)g^*V!Ay0v)80|Lzo>oS zqwNTCCp~{V$va)BO&xoIvW(|Pvq0U~EI|4fJB4`C&LHR)+R4sio)O+dZlCQaot`QatICQ;w}cpI&%jXrQtG zeoa4J5Gc<=2D=Ia&H;-I-8GR{U;J?vM}{!3-2jLvSC{}K(1`@1tw@{vot{QhN`P21^;fomdC7dvbUMr0VR z$QE@KDYupo;sp7V+&MfoNmUsMFEA^#R9%8qj50oc_&B}p5^oreX}UxgN2|S-;Yqr& zl;0Y@h<5CB)r7HvmyejOyC(1tN1W{BCwEOXe6zO~{pS9{X9I`5E`)F|7W7`;I4Wm& z(QfW{vF*70|D~p}M}8fuxJc%dvYI$4`>C6@ZOhIZn{T=H*YoVrNxFXm}Ia zB%S8PQ{PLvcBYawGSTs-GoDOZ!zQgaO~{N7dD=8wZ_;pi#0|+whRJk_%cQ>#pF1sD z_kYBzrsbpx3s2HZQfq8QcSMa;Ta6afXlf2zpXSwh#tbvGrklwcaR0S{Po1$uyU0(@ zm=jvC*rXdTR?hoAGFn^Dmp-yY@4AFvdSr`Uu!Jw0nUj8giOIUd;tPCt#kV=Xdga0= z%tiAat5~=Itn?uj{G*xA=xG&vTxp(mh(BBE(>k~|Yez)+Qj@&^OR61*PX~WnX{2hA46FjdhJ#Oa{Cc`}{+lOxoUI5?d1v|p4%Y2x?+hv<( z&0Q(N&<=SNKBd{g?xhVh*+qQ92AQl8pD6l9cq?K|Sc*co+|7lUijgL3hCa+|vW@f` zpA-Drf`@(HCrvg1R(8*ub4->ozzWn48%H0+XE|j3B}HsLHBg3#ym#V+(snM&aT>snZ|d6oIT&{I3g(B8!2PD@t6l zijP{9r|ss|i$-+02<0Hl76Il@FN)=#E$XS8tNEQp?}WCl#>HOVCU(l1Q__%T_ zY0IsoeQZZ$dG&uJZ52N_E4_czQ~yuaLY9lLsjLmoGP1V+TxhMVtuomvw78@bv(}Tn z#=Sh^^^0S)>wMqhe`%Nbt4kj3(Pyp6B2jBFzLW4>EZ6JcwLGCBCn8PakD-o%dKaHp z;f_<%=~CMWE#=WJUSHwS$^&m#Y}Yg|e_&b9ku@O*I#fDxuoAgIEQ-b+2WN){`AO2_ z$t)HI8fyHMnD1LQB5AqJ>I|fI{sm zwR1o4I-5Qpd>$jBT%qRYf@wQz22=OBWaaN=@Pck)x=4RUveJK~GaqZPjpr5&YEVHL z{0n>t`ZNfr!6e*MY_#c9z_BvKq@M&nWYa$kuCnQ$0T+Y^{XgsQF?PykZ~z9>AyIVp z7CE%>ufP|%qeyp!yNXh$RV>K@^xeq&NnAo=;to z6;TyxvTq>Pb&WYPFvEP?iiy7XdNmzR_6}?>qHZp%$oW-p7_J$Zev7+ezb&_oJ_3Bx z#!=u~HjV+4KT&kn15ExD_cEA+#n%TGJwDvwQ~L}x^&}S9O}Xz7SAmNoRYi@h0h_jt z?*$)*-iw}6o^(hS4QcEO_!~4#`xFhVYY3uT5#+%k$fMmh!2%fEat9lH6x_wu^Tpt< zHeL$Ow()XsfsLO4pR(~PFbzNyC#w!Y9;wSP^>8l?h(*!aYhZQxCVi`9rEde%2o+xh zhhRWmARv1OHVNT@=tWMz;K-pvbFXZa{D*$`764ZL4mASh#q ztHCsJ({}>_SY)boFJTO^--5>?4 zy+3T=9gBH%Rf3Q1LPXKn9Q2+pn~LXyy}0|Z`hn5bgPmZNAjiSCmInC>_#&dC?7MWv zb;qVJ00%G08XMXf3AHjd*x+y&jEM{KNbn&z?1LUrxR*#49n{$}Fg2)n1-Qe;E5XzO zrC$wJH)-VWXPuEyBv1zXU_c{M{5qIMtoWB;bw5WA+9fOfAHj5Q7X|_8o%ab=`c7bq zsN&8(2#RfjE?~O-QwHJS<2H7K8*Lm7rW-_M9|xXP8RU5ITpK5Xm)kfc34)z=0hsP8 zmBTdfr+n&DSyra(gqSk;txSjFpX1HNEje8>6FyZ22*CxjR|$=r(lZn zG|3%cb)!xC-Z>&p=~6#G2bYw++`lH#r*8ddh9@C9xQB)j6^%U%Uc219cjxd7IJsV} z2q*g-O!wjRJV0CUKqLyWC>qNHue9-C@KrS|)F=x>9O%Y*B81?rr=an4WS((b)uW_zGV4Oy&Tpc3gVX@xWJ}w2OqZS z|JW6&Fv(`%=!O)A!A`V5Q~j!B)!=S0-LWhFIhgLp74HXI&vTYQW4{1X2Sw4@0Wft) z@mt++<1=XmchzKCf%p^(TKjH#+(00P$?z3{DG**==&QjLh2= z=y#g5uK`oPw@UxF3YY^O=SOQ2CO4*-iY~tj#eN|sHb|0$Wf)|wxgTQxf`U0?eex_A6Uosj&(b+=qE#CdPENv|>d@j*9 zO!WW{5zWw5q??u{#o-U&Pi@=^9@a0YZv#g?Zdzvm)E50n0Z)eq;(lQAFN%|8g2`SK zoehYt*1ap>n>PMD0_&*QHl8nF;DiHFban$w z4J!T;909I|fEw%(DOl-yfvE$EO|UxPAo~X;EB%DXvf#-F8C1i7PC}HyTCh41A^mHT zmHrLz5!hQP2Ys7lrN0P1&X26inn%M_i8-NA#KDRau6Ucllc2wss9>6h-Ec$Gl~5$+ zp-LwH>tG5KHUaC38aEbk=7s)5?L1%T&%)nr{zM<0rC19Y?N++Q*^1~F9=pMGE|N?w zK>y#8mHrxdC-m#3enymFr7s23`HA8=U^=T=fFY8<_oFbgBv1w)z+lqKU~1d|(*aKd z45-4m?t+#5Ltr{uIwbX{BrE+HFdaMP$_9(01uOl7(YU>&GbxHGHSh-*5G#XLFr8H$ zkot#W1S|a{Fr8Q#duflF1AAvJvc;t*0)u(ApBoWTuut(rU>cApKHMTg(1HeO70?LQfN$7%Ke%|9s({Nb zIAoRR5ZRvxm)sxJUjVO%UPVNQ<4LQ75$+GpR(c;COoyNa0yPD-;H@Kq4(q_-aG*l^ zHkb}aRYanD2}b`>G!_S@91=xi@!*Tt2&yct&Wa?H9{I1c2OywjCyLI-gXuI^@kDTk zji-QLM-qu*u&H25N>MN%FeRblnP7ExOA)J+3_aS1U;_k{grewdBbc&O6ersRrX&*u z&(~l|GR51#lrxGOz%-?bcYtY174HJm6pHrIfjtnYlU?dzvt(7_4`6k)OZq#KmHs<$ z793&~S~--KAXw@9foaYaXM&5tVKjbnI714QK`FQd1_P!3$C8!)c`zlJ;(G9IaDNEM z-*L%G|7$SinBw=qbSyn#5F}LL3n@?rH^7v1ioXP#;4BEpzDJ^9rSAovw3>hYLgoP4 z&&ESe7o| zKLNba#*@L#HZB1>p9-!ZANW>vkl#o|B3`TtF2Ob!Ot%>@v~^;1ut5iYo*ENm7k<*a z3sE!{il61S1P%jFw?(rXm=69$(OIPF!x}~hl%P9)M%g$PoNVKs;N3P(03QPPVPbk7 zkPJk@k~T5lHv5U-4jWJLK|muGMQ2m-Q@weh`L2e=Lh(lY z%(n3+u-C?gTOcU53ATahjfE&WYrxOrHr@elwDB%*3qSH=mX9V%opUT1C7Pl9qho_N z!4x-@e_w(rQ*LS^UmHi`SOF6!?44}p7?F9yP@nSouehnAiC6wRBY!DV$sg$#j7KI> zYA%<8SFjQ`g1wSI1XHT-l)UC4G4ZN{?}BOKRR_NZQ&?hQPXib^L4-ls*Mcbw%D!m= zGO5K@VbMg9dQV9QuY=V)Cvx~#FlAP?)Mrc*nXyXpGO&8rMD~ZkG?8Ia|4%T@(=^Gs z-6#KWgl8@0t2QSj#TEtcRKj}WA^{dfW8J{svHa-fkA2g}2E`{Jz6XA15tDq4<`Sj=qxdbamSY45!8nQvcRO)K8!Z_ zpe;T<;{kqj%QD@$hNtpO`kKZQd|q2~ci^tn#@~StKd9Vm4ClV=?gTmg=+IcnqIulm zf*U3V?H>ZiPYLoQaE6T^_OQ`xu1zov230nm0bXz8QgHJcWw`LAtwugn! zON5yLEzNr0slshIm_l+Eyq<5{ni#R$=6pSz9p+72vmy>d+Kl0nZBL}eVQy@j=&LDF z4QuQ?Y|o-2U8x03#P{HxHocCgj$6>HiSG|4dr@?j4ZaMHl=WvyR{GhpzT$bwjGx=~ zlJB~$jo-uOE`0QpKE454JD*iaW8Z>9XeX5XZ*aJcwG_n?4I%Vbed7 zg57}{6h&vVV4z;CQV`ZlR{9sf>V+%mk4jeh{{mA76rTVWFAnniVCsO8hPHXZ}60e6!6O7QDoYr{gVpMaaeev!2hj8?HCyu6V0tx6nM8WZ zEqWEZUUFwJJzpt(JeXcaDt%uty^M59`vNe%jQmjY*nVuv4=T)rfpt8Cwy42PV0!Bv zA}a*I^bePz#wO@wpMc|R+&KgD$+x|l=%a0fW{tcoft2>K>6munEnwPKmHsrC_9fa6 zNdGOEvdWa4(H~iXVTVcnF_^N9uA^js7)(1yl;oI9WX0in6Nn|o4E8@cSekvsb(AXX z%|#q=ShyZ~;>&}@LA;84_aWk(e3H~p24}z@#gT^H0H$+u)u9eBotrCOJQUYWI)z&< z9b6nLPTguG{{&sX9}ZO%j|b z?3+23S!WHZ1Ahk7V_p~>Mqo<9im|y21IrGM0@G1IvgBqkJ?fv39hm%pc+_7f`C~9W z>6c3`87H31WdwZeV+iO;+AEW;^g;0;TqP?!3icu*DrDb->8^Xc)OW(I4Bd6B5igq} zE~e|GzgNNbq`d=P{v#x@r5~%Yh!SzNE_R5Jjsw%xJzh3YjVl>l!xXoJ!!hS`r9R_F za2WV%_em{$QEq-&_WYc|`MJ3R@(0W>2%P@elxbQGkGq{{SpTVj-+$X<+`v9{gD<@8 zanTZuUCLj)ofJx^*|AHJef*`{Zr*%5T!-RMw^Q-I4!G-1s$Q^+XWmKGrt_(H`e<+S Ir|-o4FHa)vQvd(} diff --git a/benchmarks/guest/fibonacci_recursive/src/main.rs b/benchmarks/guest/fibonacci_recursive/src/main.rs index 15fcfc0109..9020bc91ef 100644 --- a/benchmarks/guest/fibonacci_recursive/src/main.rs +++ b/benchmarks/guest/fibonacci_recursive/src/main.rs @@ -1,14 +1,15 @@ use core::hint::black_box; -use openvm as _; +use openvm::io::reveal_u32; -const N: u64 = 26; +const N: u32 = 27; pub fn main() { let n = black_box(N); - black_box(fibonacci(n)); + let result = fibonacci(n); + reveal_u32(result, 0); } -fn fibonacci(n: u64) -> u64 { +fn fibonacci(n: u32) -> u32 { if n == 0 { 0 } else if n == 1 { diff --git a/crates/vm/derive/src/lib.rs b/crates/vm/derive/src/lib.rs index be61d04d56..08a0952ecd 100644 --- a/crates/vm/derive/src/lib.rs +++ b/crates/vm/derive/src/lib.rs @@ -144,14 +144,27 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { impl #impl_generics ::openvm_circuit::arch::InsExecutorE1 for #name #ty_generics #where_clause { fn execute_e1( &mut self, - state: ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, Ctx>, + state: &mut ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, Ctx>, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction, ) -> ::openvm_circuit::arch::Result<()> where - F: ::openvm_stark_backend::p3_field::PrimeField32 + F: ::openvm_stark_backend::p3_field::PrimeField32, + Ctx: ::openvm_circuit::arch::execution_mode::E1E2ExecutionCtx, { self.0.execute_e1(state, instruction) } + + fn execute_metered( + &mut self, + state: &mut ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, ::openvm_circuit::arch::execution_mode::metered::MeteredCtx>, + instruction: &::openvm_circuit::arch::instructions::instruction::Instruction, + chip_index: usize, + ) -> ::openvm_circuit::arch::Result<()> + where + F: ::openvm_stark_backend::p3_field::PrimeField32, + { + self.0.execute_metered(state, instruction, chip_index) + } } } .into() @@ -180,25 +193,46 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { .expect("First generic must be type for Field"); // Use full path ::openvm_circuit... so it can be used either within or outside the vm // crate. Assume F is already generic of the field. - let execute_arms = variants.iter().map(|(variant_name, field)| { + let execute_e1_arms = variants.iter().map(|(variant_name, field)| { let field_ty = &field.ty; quote! { #name::#variant_name(x) => <#field_ty as ::openvm_circuit::arch::InsExecutorE1<#first_ty_generic>>::execute_e1(x, state, instruction) } }).collect::>(); + let execute_metered_arms = variants.iter().map(|(variant_name, field)| { + let field_ty = &field.ty; + quote! { + #name::#variant_name(x) => <#field_ty as ::openvm_circuit::arch::InsExecutorE1<#first_ty_generic>>::execute_metered(x, state, instruction, chip_index) + } + }).collect::>(); quote! { impl #impl_generics ::openvm_circuit::arch::InsExecutorE1<#first_ty_generic> for #name #ty_generics { fn execute_e1( &mut self, - state: ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, Ctx>, + state: &mut ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, Ctx>, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction<#first_ty_generic>, ) -> ::openvm_circuit::arch::Result<()> + where + #first_ty_generic: ::openvm_stark_backend::p3_field::PrimeField32, + Ctx: ::openvm_circuit::arch::execution_mode::E1E2ExecutionCtx, + { + match self { + #(#execute_e1_arms,)* + } + } + + fn execute_metered( + &mut self, + state: &mut ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, ::openvm_circuit::arch::execution_mode::metered::MeteredCtx>, + instruction: &::openvm_circuit::arch::instructions::instruction::Instruction<#first_ty_generic>, + chip_index: usize, + ) -> ::openvm_circuit::arch::Result<()> where #first_ty_generic: ::openvm_stark_backend::p3_field::PrimeField32 { match self { - #(#execute_arms,)* + #(#execute_metered_arms,)* } } } diff --git a/crates/vm/src/arch/execution.rs b/crates/vm/src/arch/execution.rs index d64ee84cf2..6ac124aa42 100644 --- a/crates/vm/src/arch/execution.rs +++ b/crates/vm/src/arch/execution.rs @@ -11,7 +11,10 @@ use openvm_stark_backend::{ use serde::{Deserialize, Serialize}; use thiserror::Error; -use super::Streams; +use super::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, + Streams, +}; use crate::system::{ memory::{ online::{GuestMemory, TracingMemory}, @@ -115,8 +118,18 @@ pub trait InstructionExecutor { pub trait InsExecutorE1 { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Result<()> + where + F: PrimeField32, + Ctx: E1E2ExecutionCtx; + + fn execute_metered( + &mut self, + state: &mut VmStateMut, instruction: &Instruction, + chip_index: usize, ) -> Result<()> where F: PrimeField32; @@ -128,14 +141,28 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where F: PrimeField32, + Ctx: E1E2ExecutionCtx, { self.borrow_mut().execute_e1(state, instruction) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> + where + F: PrimeField32, + { + self.borrow_mut() + .execute_metered(state, instruction, chip_index) + } } impl> InstructionExecutor for RefCell { diff --git a/crates/vm/src/arch/execution_control.rs b/crates/vm/src/arch/execution_control.rs index adc8e93aa4..1be6a166fe 100644 --- a/crates/vm/src/arch/execution_control.rs +++ b/crates/vm/src/arch/execution_control.rs @@ -1,16 +1,7 @@ use openvm_instructions::instruction::Instruction; use openvm_stark_backend::p3_field::PrimeField32; -use super::{ - ExecutionError, ExecutionSegmentState, TracegenCtx, VmChipComplex, VmConfig, VmStateMut, -}; -use crate::{ - arch::{ExecutionState, InsExecutorE1, InstructionExecutor}, - system::memory::{online::GuestMemory, AddressMap, MemoryImage, PAGE_SIZE}, -}; - -/// Check segment every 100 instructions. -const SEGMENT_CHECK_INTERVAL: usize = 100; +use super::{ExecutionError, VmChipComplex, VmConfig, VmSegmentState}; /// Trait for execution control, determining segmentation and stopping conditions pub trait ExecutionControl @@ -21,241 +12,53 @@ where /// Host context type Ctx; - fn new(chip_complex: &VmChipComplex) -> Self; - - /// Determines if execution should stop - // TODO(ayush): rename to should_suspend - fn should_stop(&mut self, chip_complex: &VmChipComplex) - -> bool; - - /// Called before segment execution begins - fn on_segment_start( + /// Determines if execution should suspend + fn should_suspend( &mut self, - pc: u32, - chip_complex: &mut VmChipComplex, - ); + state: &mut VmSegmentState, + chip_complex: &VmChipComplex, + ) -> bool; - // TODO(ayush): maybe combine with on_terminate - /// Called after segment execution completes - fn on_segment_end( + /// Called before execution begins + fn on_start( &mut self, - pc: u32, + state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ); - /// Called after program termination - fn on_terminate( + /// Called after suspend or terminate + fn on_suspend_or_terminate( &mut self, - pc: u32, + state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, - exit_code: u32, + exit_code: Option, ); - /// Execute a single instruction - // TODO(ayush): change instruction to Instruction / PInstruction - fn execute_instruction( + fn on_suspend( &mut self, - vm_state: &mut ExecutionSegmentState, - instruction: &Instruction, - chip_complex: &mut VmChipComplex, - ) -> Result<(), ExecutionError> - where - F: PrimeField32; -} - -/// Implementation of the ExecutionControl trait using the old segmentation strategy -pub struct TracegenExecutionControl { - pub final_memory: Option, - pub since_last_segment_check: usize, - air_names: Vec, -} - -impl TracegenExecutionControl { - pub fn new(air_names: Vec) -> Self { - Self { - final_memory: None, - since_last_segment_check: 0, - air_names, - } - } -} - -impl ExecutionControl for TracegenExecutionControl -where - F: PrimeField32, - VC: VmConfig, -{ - type Ctx = TracegenCtx; - - fn new(chip_complex: &VmChipComplex) -> Self { - Self { - final_memory: None, - since_last_segment_check: 0, - air_names: chip_complex.air_names(), - } - } - - fn should_stop( - &mut self, - chip_complex: &VmChipComplex, - ) -> bool { - // Avoid checking segment too often. - if self.since_last_segment_check != SEGMENT_CHECK_INTERVAL { - self.since_last_segment_check += 1; - return false; - } - self.since_last_segment_check = 0; - chip_complex.config().segmentation_strategy.should_segment( - &self.air_names, - &chip_complex.dynamic_trace_heights().collect::>(), - &chip_complex.current_trace_cells(), - ) - } - - fn on_segment_start( - &mut self, - pc: u32, - chip_complex: &mut VmChipComplex, - ) { - let timestamp = chip_complex.memory_controller().timestamp(); - chip_complex - .connector_chip_mut() - .begin(ExecutionState::new(pc, timestamp)); - } - - fn on_segment_end( - &mut self, - pc: u32, + state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ) { - let timestamp = chip_complex.memory_controller().timestamp(); - // End the current segment with connector chip - chip_complex - .connector_chip_mut() - .end(ExecutionState::new(pc, timestamp), None); - self.final_memory = Some(chip_complex.base.memory_controller.memory_image().clone()); + self.on_suspend_or_terminate(state, chip_complex, None); } fn on_terminate( &mut self, - pc: u32, + state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, exit_code: u32, ) { - let timestamp = chip_complex.memory_controller().timestamp(); - chip_complex - .connector_chip_mut() - .end(ExecutionState::new(pc, timestamp), Some(exit_code)); - self.final_memory = Some(chip_complex.base.memory_controller.memory_image().clone()); - } - - /// Execute a single instruction - fn execute_instruction( - &mut self, - state: &mut ExecutionSegmentState, - instruction: &Instruction, - chip_complex: &mut VmChipComplex, - ) -> Result<(), ExecutionError> - where - F: PrimeField32, - { - let timestamp = chip_complex.memory_controller().timestamp(); - - let &Instruction { opcode, .. } = instruction; - - if let Some(executor) = chip_complex.inventory.get_mut_executor(&opcode) { - let memory_controller = &mut chip_complex.base.memory_controller; - let new_state = executor.execute( - memory_controller, - instruction, - ExecutionState::new(state.pc, timestamp), - )?; - state.pc = new_state.pc; - } else { - return Err(ExecutionError::DisabledOperation { - pc: state.pc, - opcode, - }); - }; - - Ok(()) - } -} - -/// Implementation of the ExecutionControl trait using the old segmentation strategy -pub struct E1ExecutionControl { - pub final_memory: Option, -} - -impl ExecutionControl for E1ExecutionControl -where - F: PrimeField32, - VC: VmConfig, - VC::Executor: InsExecutorE1, -{ - type Ctx = (); - - fn new(_chip_complex: &VmChipComplex) -> Self { - Self { final_memory: None } - } - - fn should_stop( - &mut self, - _chip_complex: &VmChipComplex, - ) -> bool { - false - } - - fn on_segment_start( - &mut self, - _pc: u32, - _chip_complex: &mut VmChipComplex, - ) { - } - - fn on_segment_end( - &mut self, - _pc: u32, - chip_complex: &mut VmChipComplex, - ) { - self.final_memory = Some(chip_complex.base.memory_controller.memory_image().clone()); - } - - fn on_terminate( - &mut self, - _pc: u32, - chip_complex: &mut VmChipComplex, - _exit_code: u32, - ) { - self.final_memory = Some(chip_complex.base.memory_controller.memory_image().clone()); + self.on_suspend_or_terminate(state, chip_complex, Some(exit_code)); } /// Execute a single instruction + // TODO(ayush): change instruction to Instruction / PInstruction fn execute_instruction( &mut self, - state: &mut ExecutionSegmentState, + state: &mut VmSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> where - F: PrimeField32, - { - let &Instruction { opcode, .. } = instruction; - - if let Some(executor) = chip_complex.inventory.get_mut_executor(&opcode) { - let vm_state = VmStateMut { - pc: &mut state.pc, - memory: state.memory.as_mut().unwrap(), - ctx: &mut (), - }; - executor.execute_e1(vm_state, instruction)?; - } else { - return Err(ExecutionError::DisabledOperation { - pc: state.pc, - opcode, - }); - }; - - Ok(()) - } + F: PrimeField32; } diff --git a/crates/vm/src/arch/execution_mode/e1.rs b/crates/vm/src/arch/execution_mode/e1.rs new file mode 100644 index 0000000000..0721e2158d --- /dev/null +++ b/crates/vm/src/arch/execution_mode/e1.rs @@ -0,0 +1,79 @@ +use openvm_instructions::instruction::Instruction; +use openvm_stark_backend::p3_field::PrimeField32; + +use crate::arch::{ + execution_control::ExecutionControl, execution_mode::E1E2ExecutionCtx, ExecutionError, + InsExecutorE1, VmChipComplex, VmConfig, VmSegmentState, VmStateMut, +}; + +pub type E1Ctx = (); + +impl E1E2ExecutionCtx for E1Ctx { + fn on_memory_operation(&mut self, _address_space: u32, _ptr: u32, _size: usize) {} +} + +/// Implementation of the ExecutionControl trait using the old segmentation strategy +#[derive(Default)] +pub struct E1ExecutionControl; + +impl ExecutionControl for E1ExecutionControl +where + F: PrimeField32, + VC: VmConfig, + VC::Executor: InsExecutorE1, +{ + type Ctx = E1Ctx; + + fn should_suspend( + &mut self, + _state: &mut VmSegmentState, + _chip_complex: &VmChipComplex, + ) -> bool { + false + } + + fn on_start( + &mut self, + _state: &mut VmSegmentState, + _chip_complex: &mut VmChipComplex, + ) { + } + + fn on_suspend_or_terminate( + &mut self, + _state: &mut VmSegmentState, + _chip_complex: &mut VmChipComplex, + _exit_code: Option, + ) { + } + + /// Execute a single instruction + fn execute_instruction( + &mut self, + state: &mut VmSegmentState, + instruction: &Instruction, + chip_complex: &mut VmChipComplex, + ) -> Result<(), ExecutionError> + where + F: PrimeField32, + { + let &Instruction { opcode, .. } = instruction; + + if let Some(executor) = chip_complex.inventory.get_mut_executor(&opcode) { + let mut vm_state = VmStateMut { + pc: &mut state.pc, + memory: state.memory.as_mut().unwrap(), + ctx: &mut state.ctx, + }; + executor.execute_e1(&mut vm_state, instruction)?; + } else { + return Err(ExecutionError::DisabledOperation { + pc: state.pc, + opcode, + }); + }; + state.clk += 1; + + Ok(()) + } +} diff --git a/crates/vm/src/arch/execution_mode/metered/bounded.rs b/crates/vm/src/arch/execution_mode/metered/bounded.rs new file mode 100644 index 0000000000..52b8609b03 --- /dev/null +++ b/crates/vm/src/arch/execution_mode/metered/bounded.rs @@ -0,0 +1,183 @@ +use openvm_instructions::riscv::RV32_IMM_AS; + +use crate::{ + arch::{execution_mode::E1E2ExecutionCtx, PUBLIC_VALUES_AIR_ID}, + system::memory::{dimensions::MemoryDimensions, CHUNK, CHUNK_BITS}, +}; + +// TODO(ayush): can segmentation also be triggered by timestamp overflow? should that be tracked? +#[derive(Debug)] +pub struct MeteredCtxBounded { + pub trace_heights: Vec, + + continuations_enabled: bool, + num_access_adapters: usize, + // TODO(ayush): take alignment into account for access adapters + #[allow(dead_code)] + as_byte_alignment_bits: Vec, + pub memory_dimensions: MemoryDimensions, + + // Indices of leaf nodes in the memory merkle tree + pub leaf_indices: Vec, +} + +impl MeteredCtxBounded { + pub fn new( + num_traces: usize, + continuations_enabled: bool, + num_access_adapters: usize, + as_byte_alignment_bits: Vec, + memory_dimensions: MemoryDimensions, + ) -> Self { + Self { + trace_heights: vec![0; num_traces], + continuations_enabled, + num_access_adapters, + as_byte_alignment_bits, + memory_dimensions, + leaf_indices: Vec::new(), + } + } +} + +impl MeteredCtxBounded { + fn update_boundary_merkle_heights(&mut self, address_space: u32, ptr: u32, size: usize) { + let boundary_idx = if self.continuations_enabled { + PUBLIC_VALUES_AIR_ID + } else { + PUBLIC_VALUES_AIR_ID + 1 + }; + let poseidon2_idx = self.trace_heights.len() - 2; + + let num_blocks = (size + CHUNK - 1) >> CHUNK_BITS; + for i in 0..num_blocks { + let addr = ptr.wrapping_add((i * CHUNK) as u32); + let block_id = addr >> CHUNK_BITS; + let leaf_id = self + .memory_dimensions + .label_to_index((address_space, block_id)); + + if let Err(insert_idx) = self.leaf_indices.binary_search(&leaf_id) { + self.leaf_indices.insert(insert_idx, leaf_id); + + self.trace_heights[boundary_idx] += 1; + self.trace_heights[poseidon2_idx] += 2; + + if self.continuations_enabled { + let pred_id = insert_idx.checked_sub(1).map(|idx| self.leaf_indices[idx]); + let succ_id = (insert_idx < self.leaf_indices.len() - 1) + .then(|| self.leaf_indices[insert_idx + 1]); + let height_change = calculate_merkle_node_updates( + pred_id, + succ_id, + leaf_id, + self.memory_dimensions.overall_height(), + ); + self.trace_heights[boundary_idx + 1] += height_change * 2; + self.trace_heights[poseidon2_idx] += height_change * 2; + } + } + } + } + + fn update_adapter_heights_batch(&mut self, size: usize, num: usize) { + let adapter_offset = if self.continuations_enabled { + PUBLIC_VALUES_AIR_ID + 2 + } else { + PUBLIC_VALUES_AIR_ID + 1 + }; + + apply_adapter_updates_batch(size, num, &mut self.trace_heights[adapter_offset..]); + } + + fn update_adapter_heights(&mut self, size: usize) { + self.update_adapter_heights_batch(size, 1); + } + + pub fn finalize_access_adapter_heights(&mut self) { + self.update_adapter_heights_batch(CHUNK, self.leaf_indices.len()); + } + + pub fn trace_heights_if_finalized(&mut self) -> Vec { + let num_leaves = self.leaf_indices.len(); + let mut access_adapter_updates = vec![0; self.num_access_adapters]; + apply_adapter_updates_batch(CHUNK, num_leaves, &mut access_adapter_updates); + + let adapter_offset = if self.continuations_enabled { + PUBLIC_VALUES_AIR_ID + 2 + } else { + PUBLIC_VALUES_AIR_ID + 1 + }; + self.trace_heights + .iter() + .enumerate() + .map(|(i, &height)| { + if i >= adapter_offset && i < adapter_offset + access_adapter_updates.len() { + height + access_adapter_updates[i - adapter_offset] + } else { + height + } + }) + .collect() + } +} + +impl E1E2ExecutionCtx for MeteredCtxBounded { + fn on_memory_operation(&mut self, address_space: u32, ptr: u32, size: usize) { + debug_assert!( + address_space != RV32_IMM_AS, + "address space must not be immediate" + ); + debug_assert!(size.is_power_of_two(), "size must be a power of 2"); + + // Handle access adapter updates + self.update_adapter_heights(size); + + // Handle merkle tree updates + // TODO(ayush): use a looser upper bound + // see if this can be approximated by total number of reads/writes for AS != register + self.update_boundary_merkle_heights(address_space, ptr, size); + } +} + +fn apply_adapter_updates_batch(size: usize, num: usize, trace_heights: &mut [usize]) { + let size_bits = size.ilog2() as usize; + for adapter_bits in (3..=size_bits).rev() { + trace_heights[adapter_bits - 1] += num << (size_bits - adapter_bits + 1); + } +} + +fn calculate_merkle_node_updates( + pred_id: Option, + succ_id: Option, + leaf_id: u64, + height: usize, +) -> usize { + // First node requires height many updates + if pred_id.is_none() && succ_id.is_none() { + return height; + } + + // Calculate the difference in divergence + let mut diff = 0; + + // Add new divergences between pred and leaf_index + if let Some(p) = pred_id { + let new_divergence = (p ^ leaf_id).ilog2() as usize; + diff += new_divergence; + } + + // Add new divergences between leaf_index and succ + if let Some(s) = succ_id { + let new_divergence = (leaf_id ^ s).ilog2() as usize; + diff += new_divergence; + } + + // Remove old divergence between pred and succ if both existed + if let (Some(p), Some(s)) = (pred_id, succ_id) { + let old_divergence = (p ^ s).ilog2() as usize; + diff -= old_divergence; + } + + diff +} diff --git a/crates/vm/src/arch/execution_mode/metered/exact.rs b/crates/vm/src/arch/execution_mode/metered/exact.rs new file mode 100644 index 0000000000..53dc49bea0 --- /dev/null +++ b/crates/vm/src/arch/execution_mode/metered/exact.rs @@ -0,0 +1,393 @@ +use std::collections::BTreeMap; + +use openvm_instructions::riscv::RV32_IMM_AS; + +use crate::{ + arch::{execution_mode::E1E2ExecutionCtx, PUBLIC_VALUES_AIR_ID}, + system::memory::{dimensions::MemoryDimensions, CHUNK, CHUNK_BITS}, +}; + +// TODO(ayush): can segmentation also be triggered by timestamp overflow? should that be tracked? +#[derive(Debug)] +pub struct MeteredCtxExact { + pub trace_heights: Vec, + + continuations_enabled: bool, + num_access_adapters: usize, + as_byte_alignment_bits: Vec, + pub memory_dimensions: MemoryDimensions, + + // Map from (addr_space, addr) -> (size, offset) + pub last_memory_access: BTreeMap<(u8, u32), (u8, u8)>, + // Indices of leaf nodes in the memory merkle tree + pub leaf_indices: Vec, +} + +impl MeteredCtxExact { + pub fn new( + num_traces: usize, + continuations_enabled: bool, + num_access_adapters: usize, + as_byte_alignment_bits: Vec, + memory_dimensions: MemoryDimensions, + ) -> Self { + Self { + trace_heights: vec![0; num_traces], + continuations_enabled, + num_access_adapters, + as_byte_alignment_bits, + memory_dimensions, + last_memory_access: BTreeMap::new(), + leaf_indices: Vec::new(), + } + } +} + +impl MeteredCtxExact { + fn update_boundary_merkle_heights(&mut self, address_space: u32, ptr: u32, size: usize) { + let boundary_idx = if self.continuations_enabled { + PUBLIC_VALUES_AIR_ID + } else { + PUBLIC_VALUES_AIR_ID + 1 + }; + let poseidon2_idx = self.trace_heights.len() - 2; + + let num_blocks = (size + CHUNK - 1) >> CHUNK_BITS; + for i in 0..num_blocks { + let addr = ptr.wrapping_add((i * CHUNK) as u32); + let block_id = addr >> CHUNK_BITS; + let leaf_id = self + .memory_dimensions + .label_to_index((address_space, block_id)); + + if let Err(insert_idx) = self.leaf_indices.binary_search(&leaf_id) { + self.leaf_indices.insert(insert_idx, leaf_id); + + self.trace_heights[boundary_idx] += 1; + // NOTE: this is an upper bound since poseidon chip removes duplicates + self.trace_heights[poseidon2_idx] += 2; + + if self.continuations_enabled { + let pred_id = insert_idx.checked_sub(1).map(|idx| self.leaf_indices[idx]); + let succ_id = (insert_idx < self.leaf_indices.len() - 1) + .then(|| self.leaf_indices[insert_idx + 1]); + let height_change = calculate_merkle_node_updates( + pred_id, + succ_id, + leaf_id, + self.memory_dimensions.overall_height(), + ); + self.trace_heights[boundary_idx + 1] += height_change * 2; + self.trace_heights[poseidon2_idx] += height_change * 2; + } + } + } + } + + #[allow(clippy::type_complexity)] + fn calculate_splits_and_merges( + &self, + address_space: u32, + ptr: u32, + size: usize, + ) -> (Vec<(u32, usize)>, Vec<(u32, usize)>) { + // Skip adapters if this is a repeated access to the same location with same size + let last_access = self.last_memory_access.get(&(address_space as u8, ptr)); + if matches!(last_access, Some(&(last_access_size, 0)) if size == last_access_size as usize) + { + return (vec![], vec![]); + } + + // Go to the start of block + let mut ptr_start = ptr; + if let Some(&(_, last_access_offset)) = last_access { + ptr_start = ptr.wrapping_sub(last_access_offset as u32); + } + + let align_bits = self.as_byte_alignment_bits[address_space as usize]; + let align = 1 << align_bits; + + // Split intersecting blocks to align bytes + let mut curr_block = ptr_start >> align_bits; + let end_block = curr_block + (size as u32 >> align_bits); + let mut splits = vec![]; + while curr_block < end_block { + let curr_block_size = if let Some(&(last_access_size, _)) = self + .last_memory_access + .get(&(address_space as u8, curr_block.wrapping_mul(align as u32))) + { + last_access_size as usize + } else { + // Initial memory access only happens at CHUNK boundary + let chunk_ratio = 1 << (CHUNK_BITS - align_bits); + let chunk_offset = curr_block & (chunk_ratio - 1); + curr_block -= chunk_offset; + CHUNK + }; + + if curr_block_size > align { + let curr_ptr = curr_block.wrapping_mul(align as u32); + splits.push((curr_ptr, curr_block_size)); + } + + curr_block += (curr_block_size >> align_bits) as u32; + } + // Merge added blocks from align to size bytes + let merges = vec![(ptr, size)]; + + (splits, merges) + } + + #[allow(clippy::type_complexity)] + fn apply_adapter_updates( + &mut self, + addr_space: u32, + ptr: u32, + size: usize, + trace_heights: &mut Option<&mut [usize]>, + memory_updates: &mut Option)>>, + ) { + let adapter_offset = if self.continuations_enabled { + PUBLIC_VALUES_AIR_ID + 2 + } else { + PUBLIC_VALUES_AIR_ID + 1 + }; + + let (splits, merges) = self.calculate_splits_and_merges(addr_space, ptr, size); + for (curr_ptr, curr_size) in splits { + if let Some(trace_heights) = trace_heights { + apply_single_adapter_heights_update(trace_heights, curr_size); + } else { + apply_single_adapter_heights_update( + &mut self.trace_heights[adapter_offset..], + curr_size, + ); + } + let updates = add_memory_access_split_with_return( + &mut self.last_memory_access, + (addr_space, curr_ptr), + curr_size, + self.as_byte_alignment_bits[addr_space as usize], + ); + if let Some(memory_updates) = memory_updates { + memory_updates.extend(&updates); + } + } + for (curr_ptr, curr_size) in merges { + if let Some(trace_heights) = trace_heights { + apply_single_adapter_heights_update(trace_heights, curr_size); + } else { + apply_single_adapter_heights_update( + &mut self.trace_heights[adapter_offset..], + curr_size, + ); + } + let updates = add_memory_access_merge_with_return( + &mut self.last_memory_access, + (addr_space, curr_ptr), + curr_size, + self.as_byte_alignment_bits[addr_space as usize], + ); + if let Some(memory_updates) = memory_updates { + memory_updates.extend(updates); + } + } + } + + fn update_adapter_heights(&mut self, addr_space: u32, ptr: u32, size: usize) { + self.apply_adapter_updates(addr_space, ptr, size, &mut None, &mut None); + } + + pub fn finalize_access_adapter_heights(&mut self) { + let indices_to_process: Vec<_> = self + .leaf_indices + .iter() + .map(|&idx| { + let (addr_space, block_id) = self.memory_dimensions.index_to_label(idx); + (addr_space, block_id) + }) + .collect(); + for (addr_space, block_id) in indices_to_process { + self.update_adapter_heights(addr_space, block_id * CHUNK as u32, CHUNK); + } + } + + pub fn trace_heights_if_finalized(&mut self) -> Vec { + let indices_to_process: Vec<_> = self + .leaf_indices + .iter() + .map(|&idx| { + let (addr_space, block_id) = self.memory_dimensions.index_to_label(idx); + (addr_space, block_id) + }) + .collect(); + + let mut access_adapter_updates = vec![0; self.num_access_adapters]; + let mut memory_updates = Some(vec![]); + for (addr_space, block_id) in indices_to_process { + let ptr = block_id * CHUNK as u32; + self.apply_adapter_updates( + addr_space, + ptr, + CHUNK, + &mut Some(&mut access_adapter_updates), + &mut memory_updates, + ); + } + + // Restore original memory state + for (key, old_value) in memory_updates.unwrap().into_iter().rev() { + match old_value { + Some(value) => { + self.last_memory_access.insert(key, value); + } + None => { + self.last_memory_access.remove(&key); + } + } + } + + let adapter_offset = if self.continuations_enabled { + PUBLIC_VALUES_AIR_ID + 2 + } else { + PUBLIC_VALUES_AIR_ID + 1 + }; + self.trace_heights + .iter() + .enumerate() + .map(|(i, &height)| { + if i >= adapter_offset && i < adapter_offset + access_adapter_updates.len() { + height + access_adapter_updates[i - adapter_offset] + } else { + height + } + }) + .collect() + } +} + +impl E1E2ExecutionCtx for MeteredCtxExact { + fn on_memory_operation(&mut self, address_space: u32, ptr: u32, size: usize) { + debug_assert!( + address_space != RV32_IMM_AS, + "address space must not be immediate" + ); + debug_assert!(size.is_power_of_two(), "size must be a power of 2"); + + // Handle access adapter updates + self.update_adapter_heights(address_space, ptr, size); + + // Handle merkle tree updates + // TODO(ayush): see if this can be approximated by total number of reads/writes for AS != register + self.update_boundary_merkle_heights(address_space, ptr, size); + } +} + +fn apply_single_adapter_heights_update(trace_heights: &mut [usize], size: usize) { + let size_bits = size.ilog2() as usize; + for adapter_bits in (3..=size_bits).rev() { + trace_heights[adapter_bits - 1] += 1 << (size_bits - adapter_bits); + } +} + +#[allow(clippy::type_complexity)] +fn add_memory_access( + memory_access_map: &mut BTreeMap<(u8, u32), (u8, u8)>, + (address_space, ptr): (u32, u32), + size: usize, + align_bits: usize, + is_split: bool, +) -> Vec<((u8, u32), Option<(u8, u8)>)> { + let align = 1 << align_bits; + debug_assert_eq!( + size & (align - 1), + 0, + "Size must be a multiple of alignment" + ); + + let num_chunks = size >> align_bits; + let mut old_values = Vec::with_capacity(num_chunks); + + for i in 0..num_chunks { + let curr_ptr = ptr.wrapping_add(i as u32 * align as u32); + let key = (address_space as u8, curr_ptr); + + let value = if is_split { + (align as u8, 0) + } else { + (size as u8, (i * align) as u8) + }; + + let old_value = memory_access_map.insert(key, value); + old_values.push((key, old_value)); + } + + old_values +} + +#[allow(clippy::type_complexity)] +fn add_memory_access_split_with_return( + memory_access_map: &mut BTreeMap<(u8, u32), (u8, u8)>, + (address_space, ptr): (u32, u32), + size: usize, + align_bits: usize, +) -> Vec<((u8, u32), Option<(u8, u8)>)> { + add_memory_access( + memory_access_map, + (address_space, ptr), + size, + align_bits, + true, + ) +} + +#[allow(clippy::type_complexity)] +fn add_memory_access_merge_with_return( + memory_access_map: &mut BTreeMap<(u8, u32), (u8, u8)>, + (address_space, ptr): (u32, u32), + size: usize, + align_bits: usize, +) -> Vec<((u8, u32), Option<(u8, u8)>)> { + add_memory_access( + memory_access_map, + (address_space, ptr), + size, + align_bits, + false, + ) +} + +fn calculate_merkle_node_updates( + pred_id: Option, + succ_id: Option, + leaf_id: u64, + height: usize, +) -> usize { + // First node requires height many updates + if pred_id.is_none() && succ_id.is_none() { + return height; + } + + // Calculate the difference in divergence + let mut diff = 0; + + // Add new divergences between pred and leaf_index + if let Some(p) = pred_id { + let new_divergence = (p ^ leaf_id).ilog2() as usize; + diff += new_divergence; + } + + // Add new divergences between leaf_index and succ + if let Some(s) = succ_id { + let new_divergence = (leaf_id ^ s).ilog2() as usize; + diff += new_divergence; + } + + // Remove old divergence between pred and succ if both existed + if let (Some(p), Some(s)) = (pred_id, succ_id) { + let old_divergence = (p ^ s).ilog2() as usize; + diff -= old_divergence; + } + + diff +} diff --git a/crates/vm/src/arch/execution_mode/metered/mod.rs b/crates/vm/src/arch/execution_mode/metered/mod.rs new file mode 100644 index 0000000000..67698e8b5a --- /dev/null +++ b/crates/vm/src/arch/execution_mode/metered/mod.rs @@ -0,0 +1,204 @@ +pub mod bounded; +pub mod exact; + +// pub use exact::MeteredCtxExact as MeteredCtx; +pub use bounded::MeteredCtxBounded as MeteredCtx; + +use openvm_instructions::instruction::Instruction; +use openvm_stark_backend::{p3_field::PrimeField32, ChipUsageGetter}; + +use crate::arch::{ + execution_control::ExecutionControl, ChipId, ExecutionError, InsExecutorE1, VmChipComplex, + VmConfig, VmSegmentState, VmStateMut, CONNECTOR_AIR_ID, DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT, + DEFAULT_MAX_SEGMENT_LEN, PROGRAM_AIR_ID, PUBLIC_VALUES_AIR_ID, +}; + +/// Check segment every 100 instructions. +const SEGMENT_CHECK_INTERVAL: usize = 100; + +// TODO(ayush): fix these values +const MAX_TRACE_HEIGHT: usize = DEFAULT_MAX_SEGMENT_LEN; +const MAX_TRACE_CELLS: usize = DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT; +const MAX_INTERACTIONS: usize = DEFAULT_MAX_SEGMENT_LEN * 100; + +pub struct MeteredExecutionControl<'a> { + pub widths: &'a [usize], + pub interactions: &'a [usize], + pub since_last_segment_check: usize, +} + +impl<'a> MeteredExecutionControl<'a> { + pub fn new(widths: &'a [usize], interactions: &'a [usize]) -> Self { + Self { + widths, + interactions, + since_last_segment_check: 0, + } + } + + /// Calculate the total cells used based on trace heights and widths + // TODO(ayush): account for preprocessed and permutation columns + fn calculate_total_cells(&self, trace_heights: &[usize]) -> usize { + trace_heights + .iter() + .zip(self.widths) + .map(|(&height, &width)| height.next_power_of_two() * width) + .sum() + } + + /// Calculate the total interactions based on trace heights and interaction counts + fn calculate_total_interactions(&self, trace_heights: &[usize]) -> usize { + trace_heights + .iter() + .zip(self.interactions) + // We add 1 for the zero messages from the padding rows + .map(|(&height, &interactions)| (height + 1) * interactions) + .sum() + } +} + +impl ExecutionControl for MeteredExecutionControl<'_> +where + F: PrimeField32, + VC: VmConfig, + VC::Executor: InsExecutorE1, +{ + type Ctx = MeteredCtx; + + fn should_suspend( + &mut self, + state: &mut VmSegmentState, + _chip_complex: &VmChipComplex, + ) -> bool { + // Avoid checking segment too often. + if self.since_last_segment_check != SEGMENT_CHECK_INTERVAL { + self.since_last_segment_check += 1; + return false; + } + self.since_last_segment_check = 0; + + let trace_heights = state.ctx.trace_heights_if_finalized(); + + let max_height = trace_heights.iter().map(|&h| h.next_power_of_two()).max(); + if let Some(height) = max_height { + if height > MAX_TRACE_HEIGHT { + tracing::info!( + "Suspending execution: trace height ({}) exceeds maximum ({})", + height, + MAX_TRACE_HEIGHT + ); + return true; + } + } + + let total_cells = self.calculate_total_cells(&trace_heights); + if total_cells > MAX_TRACE_CELLS { + tracing::info!( + "Suspending execution: total cells ({}) exceeds maximum ({})", + total_cells, + MAX_TRACE_CELLS + ); + return true; + } + + let total_interactions = self.calculate_total_interactions(&trace_heights); + if total_interactions > MAX_INTERACTIONS { + tracing::info!( + "Suspending execution: total interactions ({}) exceeds maximum ({})", + total_interactions, + MAX_INTERACTIONS + ); + return true; + } + + false + } + + fn on_start( + &mut self, + state: &mut VmSegmentState, + chip_complex: &mut VmChipComplex, + ) { + // Program | Connector | Public Values | Memory ... | Executors (except Public Values) | Range Checker + state.ctx.trace_heights[PROGRAM_AIR_ID] = chip_complex.program_chip().true_program_length; + state.ctx.trace_heights[CONNECTOR_AIR_ID] = 2; + + let mut offset = if chip_complex.config().has_public_values_chip() { + PUBLIC_VALUES_AIR_ID + 1 + } else { + PUBLIC_VALUES_AIR_ID + }; + offset += chip_complex.memory_controller().num_airs(); + + // Periphery chips with constant heights + for (i, chip_id) in chip_complex + .inventory + .insertion_order + .iter() + .rev() + .enumerate() + { + if let &ChipId::Periphery(id) = chip_id { + if let Some(constant_height) = + chip_complex.inventory.periphery[id].constant_trace_height() + { + state.ctx.trace_heights[offset + i] = constant_height; + } + } + } + + // Range checker chip + if let (Some(range_checker_height), Some(last_height)) = ( + chip_complex.range_checker_chip().constant_trace_height(), + state.ctx.trace_heights.last_mut(), + ) { + *last_height = range_checker_height; + } + } + + fn on_suspend_or_terminate( + &mut self, + state: &mut VmSegmentState, + _chip_complex: &mut VmChipComplex, + _exit_code: Option, + ) { + state.ctx.finalize_access_adapter_heights(); + } + + /// Execute a single instruction + fn execute_instruction( + &mut self, + state: &mut VmSegmentState, + instruction: &Instruction, + chip_complex: &mut VmChipComplex, + ) -> Result<(), ExecutionError> + where + F: PrimeField32, + { + let &Instruction { opcode, .. } = instruction; + + let mut offset = if chip_complex.config().has_public_values_chip() { + PUBLIC_VALUES_AIR_ID + 1 + } else { + PUBLIC_VALUES_AIR_ID + }; + offset += chip_complex.memory_controller().num_airs(); + + if let Some((executor, i)) = chip_complex.inventory.get_mut_executor_with_index(&opcode) { + let mut vm_state = VmStateMut { + pc: &mut state.pc, + memory: state.memory.as_mut().unwrap(), + ctx: &mut state.ctx, + }; + executor.execute_metered(&mut vm_state, instruction, offset + i)?; + } else { + return Err(ExecutionError::DisabledOperation { + pc: state.pc, + opcode, + }); + }; + state.clk += 1; + + Ok(()) + } +} diff --git a/crates/vm/src/arch/execution_mode/mod.rs b/crates/vm/src/arch/execution_mode/mod.rs new file mode 100644 index 0000000000..a77832cb05 --- /dev/null +++ b/crates/vm/src/arch/execution_mode/mod.rs @@ -0,0 +1,8 @@ +pub mod e1; +pub mod metered; +pub mod tracegen; + +// TODO(ayush): better name +pub trait E1E2ExecutionCtx { + fn on_memory_operation(&mut self, address_space: u32, ptr: u32, size: usize); +} diff --git a/crates/vm/src/arch/execution_mode/tracegen.rs b/crates/vm/src/arch/execution_mode/tracegen.rs new file mode 100644 index 0000000000..146cb58652 --- /dev/null +++ b/crates/vm/src/arch/execution_mode/tracegen.rs @@ -0,0 +1,117 @@ +use openvm_instructions::instruction::Instruction; +use openvm_stark_backend::p3_field::PrimeField32; + +use crate::{ + arch::{ + execution_control::ExecutionControl, ExecutionError, ExecutionState, InstructionExecutor, + VmChipComplex, VmConfig, VmSegmentState, + }, + system::memory::MemoryImage, +}; + +/// Check segment every 100 instructions. +const SEGMENT_CHECK_INTERVAL: usize = 100; + +pub type TracegenCtx = (); + +/// Implementation of the ExecutionControl trait using the old segmentation strategy +pub struct TracegenExecutionControl { + pub since_last_segment_check: usize, + air_names: Vec, + pub final_memory: Option, +} + +impl TracegenExecutionControl { + pub fn new(air_names: Vec) -> Self { + Self { + since_last_segment_check: 0, + air_names, + final_memory: None, + } + } +} + +impl ExecutionControl for TracegenExecutionControl +where + F: PrimeField32, + VC: VmConfig, +{ + type Ctx = TracegenCtx; + + fn should_suspend( + &mut self, + _state: &mut VmSegmentState, + chip_complex: &VmChipComplex, + ) -> bool { + // Avoid checking segment too often. + if self.since_last_segment_check != SEGMENT_CHECK_INTERVAL { + self.since_last_segment_check += 1; + return false; + } + self.since_last_segment_check = 0; + chip_complex.config().segmentation_strategy.should_segment( + &self.air_names, + &chip_complex.dynamic_trace_heights().collect::>(), + &chip_complex.current_trace_cells(), + ) + } + + fn on_start( + &mut self, + state: &mut VmSegmentState, + chip_complex: &mut VmChipComplex, + ) { + let timestamp = chip_complex.memory_controller().timestamp(); + chip_complex + .connector_chip_mut() + .begin(ExecutionState::new(state.pc, timestamp)); + } + + fn on_suspend_or_terminate( + &mut self, + state: &mut VmSegmentState, + chip_complex: &mut VmChipComplex, + exit_code: Option, + ) { + // TODO(ayush): this should ideally not be here + self.final_memory = Some(chip_complex.base.memory_controller.memory_image().clone()); + + let timestamp = chip_complex.memory_controller().timestamp(); + chip_complex + .connector_chip_mut() + .end(ExecutionState::new(state.pc, timestamp), exit_code); + } + + /// Execute a single instruction + fn execute_instruction( + &mut self, + state: &mut VmSegmentState, + instruction: &Instruction, + chip_complex: &mut VmChipComplex, + ) -> Result<(), ExecutionError> + where + F: PrimeField32, + { + let timestamp = chip_complex.memory_controller().timestamp(); + + let &Instruction { opcode, .. } = instruction; + + if let Some(executor) = chip_complex.inventory.get_mut_executor(&opcode) { + let memory_controller = &mut chip_complex.base.memory_controller; + let new_state = executor.execute( + memory_controller, + instruction, + ExecutionState::new(state.pc, timestamp), + )?; + state.pc = new_state.pc; + } else { + return Err(ExecutionError::DisabledOperation { + pc: state.pc, + opcode, + }); + }; + state.clk += 1; + + Ok(()) + } +} diff --git a/crates/vm/src/arch/extensions.rs b/crates/vm/src/arch/extensions.rs index b20e4f6630..f3a09d3151 100644 --- a/crates/vm/src/arch/extensions.rs +++ b/crates/vm/src/arch/extensions.rs @@ -211,7 +211,7 @@ pub struct VmInventory { pub periphery: Vec

, /// Order of insertion. The reverse of this will be the order the chips are destroyed /// to generate trace. - insertion_order: Vec, + pub insertion_order: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -337,6 +337,25 @@ impl VmInventory { self.executors.get_mut(*id) } + pub fn get_mut_executor_with_index(&mut self, opcode: &VmOpcode) -> Option<(&mut E, usize)> { + let id = *self.instruction_lookup.get(opcode)?; + + self.executors.get_mut(id).map(|executor| { + // TODO(ayush): cache this somewhere + let insertion_id = self + .insertion_order + .iter() + .rev() + .position(|chip_id| match chip_id { + ChipId::Executor(exec_id) => *exec_id == id, + _ => false, + }) + .unwrap(); + + (executor, insertion_id) + }) + } + pub fn executors(&self) -> &[E] { &self.executors } @@ -788,11 +807,11 @@ impl VmChipComplex { .as_any_kind_mut() .downcast_mut() .expect("Poseidon2 chip required for persistent memory"); - self.base.memory_controller.finalize(Some(hasher)) + self.base.memory_controller.finalize(Some(hasher)); } else { self.base .memory_controller - .finalize(None::<&mut Poseidon2PeripheryChip>) + .finalize(None::<&mut Poseidon2PeripheryChip>); }; } @@ -821,7 +840,7 @@ impl VmChipComplex { } // we always need to special case it because we need to fix the air id. - fn public_values_chip_idx(&self) -> Option { + pub(crate) fn public_values_chip_idx(&self) -> Option { self.config .has_public_values_chip() .then_some(Self::PV_EXECUTOR_IDX) diff --git a/crates/vm/src/arch/integration_api.rs b/crates/vm/src/arch/integration_api.rs index d53ed075fe..3f6808e3c8 100644 --- a/crates/vm/src/arch/integration_api.rs +++ b/crates/vm/src/arch/integration_api.rs @@ -15,7 +15,10 @@ use openvm_stark_backend::{ }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use super::{ExecutionState, InsExecutorE1, InstructionExecutor, Result, VmStateMut}; +use super::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, + ExecutionState, InsExecutorE1, InstructionExecutor, Result, VmStateMut, +}; use crate::system::memory::{ online::{GuestMemory, TracingMemory}, MemoryAuxColsFactory, MemoryController, SharedMemoryHelper, @@ -200,7 +203,7 @@ pub trait TraceStep { /// being used, and all other rows in the trace will be filled with zeroes. /// /// The provided `row_slice` will have length equal to the width of the AIR. - fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + fn fill_trace_row(&self, _mem_helper: &MemoryAuxColsFactory, _row_slice: &mut [F]) { unreachable!("fill_trace_row is not implemented") } @@ -208,7 +211,7 @@ pub trait TraceStep { /// By default the trace is padded with empty (all 0) rows to make the height a power of 2. /// /// The provided `row_slice` will have length equal to the width of the AIR. - fn fill_dummy_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + fn fill_dummy_trace_row(&self, _mem_helper: &MemoryAuxColsFactory, _row_slice: &mut [F]) { // By default, the row is filled with zeroes } /// Returns a list of public values to publish. @@ -387,22 +390,41 @@ where type ReadData; type WriteData; - fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData; + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx; - fn write(&self, memory: &mut GuestMemory, instruction: &Instruction, data: &Self::WriteData); + fn write( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + data: &Self::WriteData, + ) where + Ctx: E1E2ExecutionCtx; } // TODO: Rename core/step to operator pub trait StepExecutorE1 { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx; + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, ) -> Result<()>; } -const DEFAULT_RECORDS_CAPACITY: usize = 1 << 20; - impl InsExecutorE1 for NewVmChipWrapper where F: PrimeField32, @@ -410,11 +432,23 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { self.step.execute_e1(state, instruction) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + self.step.execute_metered(state, instruction, chip_index) + } } #[derive(Clone, Copy, derive_new::new)] diff --git a/crates/vm/src/arch/mod.rs b/crates/vm/src/arch/mod.rs index 7ecb289cc9..fdb2b7e49a 100644 --- a/crates/vm/src/arch/mod.rs +++ b/crates/vm/src/arch/mod.rs @@ -4,6 +4,7 @@ mod config; mod execution; /// Module for controlling VM execution flow, including segmentation and instruction execution pub mod execution_control; +pub mod execution_mode; /// Traits and builders to compose collections of chips into a virtual machine. mod extensions; /// Traits and wrappers to facilitate VM chip integration diff --git a/crates/vm/src/arch/segment.rs b/crates/vm/src/arch/segment.rs index 3603db4253..4f325cef4e 100644 --- a/crates/vm/src/arch/segment.rs +++ b/crates/vm/src/arch/segment.rs @@ -1,3 +1,13 @@ +use super::{ + execution_control::ExecutionControl, ExecutionError, GenerationError, SystemConfig, + VmChipComplex, VmComplexTraceHeights, VmConfig, +}; +#[cfg(feature = "bench-metrics")] +use crate::metrics::VmMetrics; +use crate::{ + arch::{instructions::*, InstructionExecutor}, + system::memory::online::GuestMemory, +}; use backtrace::Backtrace; use openvm_instructions::{ exe::FnBounds, @@ -12,32 +22,36 @@ use openvm_stark_backend::{ utils::metrics_span, Chip, }; -use program::Program; -use super::{ - execution_control::{E1ExecutionControl, ExecutionControl, TracegenExecutionControl}, - ExecutionError, GenerationError, Streams, SystemConfig, VmChipComplex, VmComplexTraceHeights, - VmConfig, VmStateMut, -}; -#[cfg(feature = "bench-metrics")] -use crate::metrics::VmMetrics; -use crate::{ - arch::{instructions::*, InstructionExecutor}, - system::{ - connector::DEFAULT_SUSPEND_EXIT_CODE, - memory::{online::GuestMemory, paged_vec::PAGE_SIZE, AddressMap, MemoryImage}, - }, -}; +pub struct VmSegmentState { + pub clk: u64, + pub pc: u32, + pub memory: Option, + pub exit_code: Option, + pub ctx: Ctx, +} + +impl VmSegmentState { + pub fn new(clk: u64, pc: u32, memory: Option, ctx: Ctx) -> Self { + Self { + clk, + pc, + memory, + ctx, + exit_code: None, + } + } +} -pub struct VmSegmentExecutor +pub struct VmSegmentExecutor where F: PrimeField32, VC: VmConfig, - Ctrl: ExecutionControl, + Ctrl: ExecutionControl, { pub chip_complex: VmChipComplex, /// Execution control for determining segmentation and stopping conditions - pub control: Ctrl, + pub ctrl: Ctrl, pub trace_height_constraints: Vec, @@ -48,56 +62,24 @@ where pub metrics: VmMetrics, } -pub type E1VmSegmentExecutor = VmSegmentExecutor; - -// pub struct TracegenCtx; -pub type TracegenCtx = (); -pub type TracegenVmStateMut<'a> = VmStateMut<'a, GuestMemory, TracegenCtx>; - -pub type TracegenVmSegmentExecutor = - VmSegmentExecutor; - -#[derive(derive_new::new)] -pub struct ExecutionSegmentState { - pub memory: Option, - pub pc: u32, - pub exit_code: u32, - pub is_terminated: bool, -} - -impl VmSegmentExecutor +impl VmSegmentExecutor where F: PrimeField32, VC: VmConfig, - Ctrl: ExecutionControl, + Ctrl: ExecutionControl, { /// Creates a new execution segment from a program and initial state, using parent VM config pub fn new( - config: &VC, - program: Program, - init_streams: Streams, - initial_memory: Option, + chip_complex: VmChipComplex, trace_height_constraints: Vec, #[allow(unused_variables)] fn_bounds: FnBounds, + ctrl: Ctrl, ) -> Self { - let mut chip_complex = config.create_chip_complex().unwrap(); - chip_complex.set_streams(init_streams); - let program = if !config.system().profiling { - program.strip_debug_infos() - } else { - program - }; - chip_complex.set_program(program); - - if let Some(initial_memory) = initial_memory { - chip_complex.set_initial_memory(initial_memory); - } let air_names = chip_complex.air_names(); - let control = Ctrl::new(&chip_complex); Self { chip_complex, - control, + ctrl, air_names, trace_height_constraints, #[cfg(feature = "bench-metrics")] @@ -120,46 +102,40 @@ where } /// Stopping is triggered by should_stop() or if VM is terminated - pub fn execute_from_pc( + pub fn execute_from_state( &mut self, - pc: u32, - memory: Option, - ) -> Result { + state: &mut VmSegmentState, + ) -> Result<(), ExecutionError> { let mut prev_backtrace: Option = None; // Call the pre-execution hook - self.control.on_segment_start(pc, &mut self.chip_complex); + self.ctrl.on_start(state, &mut self.chip_complex); - let mut state = ExecutionSegmentState::new(memory, pc, 0, false); loop { // Fetch, decode and execute single instruction - let terminated_exit_code = self.execute_instruction(&mut state, &mut prev_backtrace)?; + self.execute_instruction(state, &mut prev_backtrace)?; - if let Some(exit_code) = terminated_exit_code { - state.is_terminated = true; - state.exit_code = exit_code; - self.control - .on_terminate(state.pc, &mut self.chip_complex, exit_code); + if let Some(exit_code) = state.exit_code { + self.ctrl + .on_terminate(state, &mut self.chip_complex, exit_code); break; } - if self.should_stop() { - state.exit_code = DEFAULT_SUSPEND_EXIT_CODE; - self.control - .on_segment_end(state.pc, &mut self.chip_complex); + if self.should_suspend(state) { + self.ctrl.on_suspend(state, &mut self.chip_complex); break; } } - Ok(state) + Ok(()) } /// Executes a single instruction and updates VM state // TODO(ayush): clean this up, separate to smaller functions fn execute_instruction( &mut self, - state: &mut ExecutionSegmentState, + state: &mut VmSegmentState, prev_backtrace: &mut Option, - ) -> Result, ExecutionError> { + ) -> Result<(), ExecutionError> { let pc = state.pc; let timestamp = self.chip_complex.memory_controller().timestamp(); @@ -172,7 +148,8 @@ where // Handle termination instruction if opcode == SystemOpcode::TERMINATE.global_opcode() { - return Ok(Some(c.as_canonical_u32())); + state.exit_code = Some(c.as_canonical_u32()); + return Ok(()); } // Extract debug info components @@ -218,7 +195,7 @@ where // Execute the instruction using the control implementation // TODO(AG): maybe avoid cloning the instruction? - self.control + self.ctrl .execute_instruction(state, &instruction.clone(), &mut self.chip_complex)?; // Update metrics if enabled @@ -227,20 +204,20 @@ where self.update_instruction_metrics(pc, opcode, dsl_instr); } - Ok(None) + Ok(()) } /// Returns bool of whether to switch to next segment or not. - fn should_stop(&mut self) -> bool { + fn should_suspend(&mut self, state: &mut VmSegmentState) -> bool { if !self.system_config().continuation_enabled { return false; } // Check with the execution control policy - self.control.should_stop(&self.chip_complex) + self.ctrl.should_suspend(state, &self.chip_complex) } - // TODO(ayush): not sure what to do of these + // TODO(ayush): this is not relevant for e1/e2 execution /// Generate ProofInput to prove the segment. Should be called after ::execute pub fn generate_proof_input( #[allow(unused_mut)] mut self, diff --git a/crates/vm/src/arch/segmentation_strategy.rs b/crates/vm/src/arch/segmentation_strategy.rs index a17ef97fde..000bc0f8e0 100644 --- a/crates/vm/src/arch/segmentation_strategy.rs +++ b/crates/vm/src/arch/segmentation_strategy.rs @@ -1,13 +1,13 @@ use std::sync::Arc; -const DEFAULT_MAX_SEGMENT_LEN: usize = (1 << 22) - 100; +pub const DEFAULT_MAX_SEGMENT_LEN: usize = (1 << 22) - 100; // a heuristic number for the maximum number of cells per chip in a segment // a few reasons for this number: // 1. `VmAirWrapper` is // the chip with the most cells in a segment from the reth-benchmark. // 2. `VmAirWrapper`: // its trace width is 36 and its after challenge trace width is 80. -const DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT: usize = DEFAULT_MAX_SEGMENT_LEN * 120; +pub const DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT: usize = DEFAULT_MAX_SEGMENT_LEN * 120; pub trait SegmentationStrategy: std::fmt::Debug + Send + Sync + std::panic::UnwindSafe + std::panic::RefUnwindSafe diff --git a/crates/vm/src/arch/testing/mod.rs b/crates/vm/src/arch/testing/mod.rs index bdd5691bce..fd0c73dc1a 100644 --- a/crates/vm/src/arch/testing/mod.rs +++ b/crates/vm/src/arch/testing/mod.rs @@ -1,9 +1,4 @@ -use std::{ - borrow::Borrow, - iter::zip, - rc::Rc, - sync::{Arc, Mutex}, -}; +use std::{borrow::Borrow, iter::zip}; use openvm_circuit_primitives::var_range::{ SharedVariableRangeCheckerChip, VariableRangeCheckerBus, diff --git a/crates/vm/src/arch/vm.rs b/crates/vm/src/arch/vm.rs index b861516622..ae5fd27d76 100644 --- a/crates/vm/src/arch/vm.rs +++ b/crates/vm/src/arch/vm.rs @@ -1,13 +1,14 @@ use std::{borrow::Borrow, collections::VecDeque, marker::PhantomData, mem, sync::Arc}; use openvm_circuit::system::program::trace::compute_exe_commit; -use openvm_instructions::exe::VmExe; +use openvm_instructions::{exe::VmExe, program::Program}; use openvm_stark_backend::{ config::{Com, Domain, StarkGenericConfig, Val}, engine::StarkEngine, keygen::types::{LinearConstraint, MultiStarkProvingKey, MultiStarkVerifyingKey}, p3_commit::PolynomialSpace, p3_field::{FieldAlgebra, PrimeField32}, + p3_util::log2_strict_usize, proof::Proof, prover::types::{CommittedTraceData, ProofInput}, utils::metrics_span, @@ -19,15 +20,20 @@ use thiserror::Error; use tracing::info_span; use super::{ - ExecutionError, InsExecutorE1, VmComplexTraceHeights, VmConfig, CONNECTOR_AIR_ID, - MERKLE_AIR_ID, PROGRAM_AIR_ID, PROGRAM_CACHED_TRACE_INDEX, + ExecutionError, InsExecutorE1, VmChipComplex, VmComplexTraceHeights, VmConfig, + VmInventoryError, CONNECTOR_AIR_ID, MERKLE_AIR_ID, PROGRAM_AIR_ID, PROGRAM_CACHED_TRACE_INDEX, }; #[cfg(feature = "bench-metrics")] use crate::metrics::VmMetrics; use crate::{ arch::{ - hasher::poseidon2::vm_poseidon2_hasher, segment::TracegenVmSegmentExecutor, - E1VmSegmentExecutor, + execution_mode::{ + e1::E1ExecutionControl, + metered::{MeteredCtx, MeteredExecutionControl}, + tracegen::TracegenExecutionControl, + }, + hasher::poseidon2::vm_poseidon2_hasher, + VmSegmentExecutor, VmSegmentState, }, system::{ connector::{VmConnectorPvs, DEFAULT_SUSPEND_EXIT_CODE}, @@ -101,17 +107,22 @@ pub struct VmExecutorResult { pub final_memory: Option, } -pub struct VmExecutorNextSegmentState { +pub struct VmState +where + F: PrimeField32, +{ + pub clk: u64, + pub pc: u32, pub memory: MemoryImage, pub input: Streams, - pub pc: u32, #[cfg(feature = "bench-metrics")] pub metrics: VmMetrics, } -impl VmExecutorNextSegmentState { - pub fn new(memory: MemoryImage, input: impl Into>, pc: u32) -> Self { +impl VmState { + pub fn new(clk: u64, pc: u32, memory: MemoryImage, input: impl Into>) -> Self { Self { + clk, memory, input: input.into(), pc, @@ -121,13 +132,14 @@ impl VmExecutorNextSegmentState { } } +type TracegenVmSegmentExecutor = VmSegmentExecutor; pub struct VmExecutorOneSegmentResult where F: PrimeField32, VC: VmConfig, { pub segment: TracegenVmSegmentExecutor, - pub next_state: Option>, + pub next_state: Option>, } impl VmExecutor @@ -176,7 +188,6 @@ where ) -> Result, E> { let mem_config = self.config.system().memory_config; let exe = exe.into(); - let mut segment_results = vec![]; let memory = AddressMap::from_sparse( mem_config.as_offset, 1 << mem_config.as_height, @@ -185,16 +196,16 @@ where ); let pc = exe.pc_start; - let mut state = VmExecutorNextSegmentState::new(memory, input, pc); + let mut state = VmState::new(0, pc, memory, input); #[cfg(feature = "bench-metrics")] { state.metrics.fn_bounds = exe.fn_bounds.clone(); } - let mut segment_idx = 0; - + let mut segment_results = vec![]; loop { + let segment_idx = segment_results.len(); let _span = info_span!("execute_segment", segment = segment_idx).entered(); let one_segment_result = self .execute_until_segment(exe.clone(), state) @@ -204,7 +215,6 @@ where break; } state = one_segment_result.next_state.unwrap(); - segment_idx += 1; } tracing::debug!("Number of continuation segments: {}", segment_results.len()); #[cfg(feature = "bench-metrics")] @@ -228,18 +238,25 @@ where pub fn execute_until_segment( &self, exe: impl Into>, - from_state: VmExecutorNextSegmentState, + from_state: VmState, ) -> Result, ExecutionError> { let exe = exe.into(); - let mut segment = TracegenVmSegmentExecutor::new( + let chip_complex = create_and_initialize_chip_complex( &self.config, exe.program.clone(), from_state.input, Some(from_state.memory), + ) + .unwrap(); + let ctrl = TracegenExecutionControl::new(chip_complex.air_names()); + let mut segment = TracegenVmSegmentExecutor::new( + chip_complex, self.trace_height_constraints.clone(), exe.fn_bounds.clone(), + ctrl, ); + #[cfg(feature = "bench-metrics")] { segment.metrics = from_state.metrics; @@ -247,11 +264,13 @@ where if let Some(overridden_heights) = self.overridden_heights.as_ref() { segment.set_override_trace_heights(overridden_heights.clone()); } - let state = metrics_span("execute_time_ms", || { - segment.execute_from_pc(from_state.pc, None) + + let mut exec_state = VmSegmentState::new(from_state.clk, from_state.pc, None, ()); + metrics_span("execute_time_ms", || { + segment.execute_from_state(&mut exec_state) })?; - if state.is_terminated { + if exec_state.exit_code.is_some() { return Ok(VmExecutorOneSegmentResult { segment, next_state: None, @@ -263,22 +282,21 @@ where "multiple segments require to enable continuations" ); assert_eq!( - state.pc, + exec_state.pc, segment.chip_complex.connector_chip().boundary_states[1] .unwrap() .pc ); - let final_memory = mem::take(&mut segment.control.final_memory) - .expect("final memory should be set in continuations segment"); let streams = segment.chip_complex.take_streams(); #[cfg(feature = "bench-metrics")] let metrics = segment.metrics.partial_take(); Ok(VmExecutorOneSegmentResult { segment, - next_state: Some(VmExecutorNextSegmentState { - memory: final_memory, + next_state: Some(VmState { + clk: exec_state.clk, + pc: exec_state.pc, + memory: exec_state.memory.unwrap().memory, input: streams, - pc: state.pc, #[cfg(feature = "bench-metrics")] metrics, }), @@ -301,7 +319,7 @@ where |err| err, )?; let last = last.expect("at least one segment must be executed"); - let final_memory = last.control.final_memory; + let final_memory = last.ctrl.final_memory; let end_state = last.chip_complex.connector_chip().boundary_states[1].expect("end state must be set"); if end_state.is_terminate != 1 { @@ -331,34 +349,44 @@ where ); let pc = exe.pc_start; - let mut state = VmExecutorNextSegmentState::new(memory, input, pc); + let mut state = VmState::new(0, pc, memory, input); let mut segment_idx = 0; loop { let _span = info_span!("execute_segment", segment = segment_idx).entered(); - let mut segment = E1VmSegmentExecutor::new( + let chip_complex = create_and_initialize_chip_complex( &self.config, - // TODO(ayush): avoid clones exe.program.clone(), state.input, None, + ) + .unwrap(); + let mut segment = VmSegmentExecutor::::new( + chip_complex, self.trace_height_constraints.clone(), exe.fn_bounds.clone(), + E1ExecutionControl, ); #[cfg(feature = "bench-metrics")] { segment.metrics = state.metrics; } - let exec_state = metrics_span("execute_time_ms", || { - segment.execute_from_pc(state.pc, Some(GuestMemory::new(state.memory))) + let mut exec_state = VmSegmentState::new( + state.clk, + state.pc, + Some(GuestMemory::new(state.memory)), + (), + ); + metrics_span("execute_time_ms", || { + segment.execute_from_state(&mut exec_state) })?; - if exec_state.is_terminated { + if let Some(exit_code) = exec_state.exit_code { // Check exit code for the final segment - if exec_state.exit_code != ExitCode::Success as u32 { - return Err(ExecutionError::FailedWithExitCode(exec_state.exit_code)); + if exit_code != ExitCode::Success as u32 { + return Err(ExecutionError::FailedWithExitCode(exit_code)); } tracing::debug!("Execution completed in {} segments", segment_idx + 1); #[cfg(feature = "bench-metrics")] @@ -371,17 +399,134 @@ where "multiple segments require to enable continuations" ); - let final_memory = mem::take(&mut segment.control.final_memory) - .expect("final memory should be set in continuations segment"); let streams = segment.chip_complex.take_streams(); #[cfg(feature = "bench-metrics")] let metrics = segment.metrics.partial_take(); - state = VmExecutorNextSegmentState { - memory: final_memory, + state = VmState { + clk: exec_state.clk, + pc: exec_state.pc, + memory: exec_state.memory.unwrap().memory, input: streams, + #[cfg(feature = "bench-metrics")] + metrics, + }; + + segment_idx += 1; + } + } + + pub fn execute_metered( + &self, + exe: impl Into>, + input: impl Into>, + widths: Vec, + interactions: Vec, + ) -> Result<(), ExecutionError> + where + VC::Executor: InsExecutorE1, + { + let mem_config = self.config.system().memory_config; + let exe = exe.into(); + let memory = AddressMap::from_sparse( + mem_config.as_offset, + 1 << mem_config.as_height, + 1 << mem_config.pointer_max_bits, + exe.init_memory.clone(), + ); + + let pc = exe.pc_start; + let mut state = VmState::new(0, pc, memory, input); + let mut segment_idx = 0; + + loop { + let _span = info_span!("execute_segment", segment = segment_idx).entered(); + + let chip_complex = create_and_initialize_chip_complex( + &self.config, + exe.program.clone(), + state.input, + None, + ) + .unwrap(); + let ctrl = MeteredExecutionControl::new(&widths, &interactions); + let mut segment = VmSegmentExecutor::::new( + chip_complex, + self.trace_height_constraints.clone(), + exe.fn_bounds.clone(), + ctrl, + ); + + #[cfg(feature = "bench-metrics")] + { + segment.metrics = state.metrics; + } + + let continuations_enabled = segment + .chip_complex + .memory_controller() + .continuation_enabled(); + let num_access_adapters = segment + .chip_complex + .memory_controller() + .access_adapters + .num_access_adapters(); + let ctx = MeteredCtx::new( + widths.len(), + continuations_enabled, + num_access_adapters, + segment + .chip_complex + .memory_controller() + .memory + .min_block_size + .iter() + .map(|&x| log2_strict_usize(x as usize)) + .collect(), + segment + .chip_complex + .memory_controller() + .mem_config() + .memory_dimensions(), + ); + + let mut exec_state = VmSegmentState::new( + state.clk, + state.pc, + Some(GuestMemory::new(state.memory)), + ctx, + ); + metrics_span("execute_time_ms", || { + segment.execute_from_state(&mut exec_state) + })?; + + if let Some(exit_code) = exec_state.exit_code { + // Check exit code for the final segment + if exit_code != ExitCode::Success as u32 { + return Err(ExecutionError::FailedWithExitCode(exit_code)); + } + tracing::debug!("Execution completed in {} segments", segment_idx + 1); + #[cfg(feature = "bench-metrics")] + metrics::counter!("num_segments").absolute((segment_idx + 1) as u64); + return Ok(()); + } + + assert!( + self.continuation_enabled(), + "multiple segments require to enable continuations" + ); + + let streams = segment.chip_complex.take_streams(); + + #[cfg(feature = "bench-metrics")] + let metrics = segment.metrics.partial_take(); + + state = VmState { + clk: exec_state.clk, pc: exec_state.pc, + memory: exec_state.memory.unwrap().memory, + input: streams, #[cfg(feature = "bench-metrics")] metrics, }; @@ -438,7 +583,7 @@ where |seg_idx, mut seg| { // Note: this will only be Some on the last segment; otherwise it is // already moved into next segment state - final_memory = mem::take(&mut seg.control.final_memory); + final_memory = mem::take(&mut seg.ctrl.final_memory); tracing::info_span!("trace_gen", segment = seg_idx) .in_scope(|| seg.generate_proof_input(committed_program.clone())) }, @@ -555,19 +700,28 @@ where exe: VmExe, input: impl Into>, ) -> Result, ExecutionError> { - let mut segment = TracegenVmSegmentExecutor::new( + let chip_complex = create_and_initialize_chip_complex( &self.config, exe.program.clone(), input.into(), None, + ) + .unwrap(); + let ctrl = TracegenExecutionControl::new(chip_complex.air_names()); + let mut segment = TracegenVmSegmentExecutor::new( + chip_complex, self.trace_height_constraints.clone(), exe.fn_bounds.clone(), + ctrl, ); + if let Some(overridden_heights) = self.overridden_heights.as_ref() { segment.set_override_trace_heights(overridden_heights.clone()); } + + let mut exec_state = VmSegmentState::new(0, exe.pc_start, None, ()); metrics_span("execute_time_ms", || { - segment.execute_from_pc(exe.pc_start, None) + segment.execute_from_state(&mut exec_state) })?; Ok(segment) } @@ -948,3 +1102,33 @@ where } } } + +/// Create and initialize a chip complex with program, streams, and optional memory +pub fn create_and_initialize_chip_complex( + config: &VC, + program: Program, + init_streams: Streams, + initial_memory: Option, +) -> Result, VmInventoryError> +where + F: PrimeField32, + VC: VmConfig, +{ + let mut chip_complex = config.create_chip_complex()?; + chip_complex.set_streams(init_streams); + + // Strip debug info if profiling is disabled + let program = if !config.system().profiling { + program.strip_debug_infos() + } else { + program + }; + + chip_complex.set_program(program); + + if let Some(initial_memory) = initial_memory { + chip_complex.set_initial_memory(initial_memory); + } + + Ok(chip_complex) +} diff --git a/crates/vm/src/system/memory/adapter/mod.rs b/crates/vm/src/system/memory/adapter/mod.rs index 821657e1d2..ea76117066 100644 --- a/crates/vm/src/system/memory/adapter/mod.rs +++ b/crates/vm/src/system/memory/adapter/mod.rs @@ -82,7 +82,8 @@ impl AccessAdapterInventory { where F: PrimeField32, { - self.chips[index].set_trace(RowMajorMatrix::new(trace, width)); + let trace = RowMajorMatrix::new(trace, width); + self.chips[index].set_trace(trace); } #[cfg(test)] diff --git a/crates/vm/src/system/memory/controller/dimensions.rs b/crates/vm/src/system/memory/controller/dimensions.rs index 1082d3adf0..87976d2fa0 100644 --- a/crates/vm/src/system/memory/controller/dimensions.rs +++ b/crates/vm/src/system/memory/controller/dimensions.rs @@ -30,6 +30,15 @@ impl MemoryDimensions { debug_assert!(block_id < (1 << self.address_height)); (((addr_space - self.as_offset) as u64) << self.address_height) + block_id as u64 } + + /// Convert an index in the memory merkle tree to an address label (address space, block id). + /// + /// This function performs the inverse operation of `label_to_index`. + pub fn index_to_label(&self, index: u64) -> (u32, u32) { + let block_id = (index & ((1 << self.address_height) - 1)) as u32; + let addr_space = (index >> self.address_height) as u32 + self.as_offset; + (addr_space, block_id) + } } impl MemoryConfig { diff --git a/crates/vm/src/system/memory/controller/mod.rs b/crates/vm/src/system/memory/controller/mod.rs index 28e0254c33..66d8cdde7c 100644 --- a/crates/vm/src/system/memory/controller/mod.rs +++ b/crates/vm/src/system/memory/controller/mod.rs @@ -44,6 +44,8 @@ pub mod dimensions; pub mod interface; pub const CHUNK: usize = 8; +pub const CHUNK_BITS: usize = CHUNK.ilog2() as usize; + /// The offset of the Merkle AIR in AIRs of MemoryController. pub const MERKLE_AIR_OFFSET: usize = 1; /// The offset of the boundary AIR in AIRs of MemoryController. @@ -572,11 +574,8 @@ impl MemoryController { for i in 0..self.access_adapters.num_access_adapters() { let width = self.memory.adapter_inventory_trace_cursor.width(i); - self.access_adapters.set_trace( - i, - self.memory.adapter_inventory_trace_cursor.extract_trace(i), - width, - ); + let trace = self.memory.adapter_inventory_trace_cursor.extract_trace(i); + self.access_adapters.set_trace(i, trace, width); } final_memory diff --git a/crates/vm/src/system/memory/mod.rs b/crates/vm/src/system/memory/mod.rs index 2e14cc9c86..0020e0708d 100644 --- a/crates/vm/src/system/memory/mod.rs +++ b/crates/vm/src/system/memory/mod.rs @@ -1,6 +1,6 @@ use openvm_circuit_primitives_derive::AlignedBorrow; -mod adapter; +pub mod adapter; mod controller; pub mod merkle; pub mod offline_checker; diff --git a/crates/vm/src/system/memory/online.rs b/crates/vm/src/system/memory/online.rs index f176f0d287..38d78c5f2c 100644 --- a/crates/vm/src/system/memory/online.rs +++ b/crates/vm/src/system/memory/online.rs @@ -190,9 +190,9 @@ pub struct TracingMemory { pub(super) meta: Vec>, /// For each `addr_space`, the minimum block size allowed for memory accesses. In other words, /// all memory accesses in `addr_space` must be aligned to this block size. - pub(super) min_block_size: Vec, - pub(super) access_adapter_inventory: AccessAdapterInventory, - pub(super) adapter_inventory_trace_cursor: AdapterInventoryTraceCursor, + pub min_block_size: Vec, + pub access_adapter_inventory: AccessAdapterInventory, + pub adapter_inventory_trace_cursor: AdapterInventoryTraceCursor, } impl TracingMemory { diff --git a/crates/vm/src/system/memory/paged_vec.rs b/crates/vm/src/system/memory/paged_vec.rs index 808c8b5d53..192a655780 100644 --- a/crates/vm/src/system/memory/paged_vec.rs +++ b/crates/vm/src/system/memory/paged_vec.rs @@ -11,7 +11,6 @@ use openvm_instructions::exe::SparseMemoryImage; use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; -use super::online::GuestMemory; use crate::arch::MemoryConfig; /// (address_space, pointer) diff --git a/crates/vm/src/system/memory/persistent.rs b/crates/vm/src/system/memory/persistent.rs index 3391e7f9e0..bdd1fa4d04 100644 --- a/crates/vm/src/system/memory/persistent.rs +++ b/crates/vm/src/system/memory/persistent.rs @@ -123,18 +123,18 @@ impl Air for PersistentBoundaryA pub struct PersistentBoundaryChip { pub air: PersistentBoundaryAir, - touched_labels: TouchedLabels, + pub touched_labels: TouchedLabels, overridden_height: Option, } #[derive(Debug)] -enum TouchedLabels { +pub enum TouchedLabels { Running(FxHashSet<(u32, u32)>), Final(Vec>), } #[derive(Debug)] -struct FinalTouchedLabel { +pub struct FinalTouchedLabel { address_space: u32, label: u32, init_values: [F; CHUNK], @@ -159,7 +159,8 @@ impl TouchedLabels { _ => panic!("Cannot touch after finalization"), } } - fn len(&self) -> usize { + + pub fn len(&self) -> usize { match self { TouchedLabels::Running(touched_labels) => touched_labels.len(), TouchedLabels::Final(touched_labels) => touched_labels.len(), diff --git a/crates/vm/src/system/memory/volatile/mod.rs b/crates/vm/src/system/memory/volatile/mod.rs index e01162c789..e345acddda 100644 --- a/crates/vm/src/system/memory/volatile/mod.rs +++ b/crates/vm/src/system/memory/volatile/mod.rs @@ -183,7 +183,7 @@ pub struct VolatileBoundaryChip { pub air: VolatileBoundaryAir, range_checker: SharedVariableRangeCheckerChip, overridden_height: Option, - final_memory: Option>, + pub final_memory: Option>, addr_space_max_bits: usize, pointer_max_bits: usize, } diff --git a/crates/vm/src/system/native_adapter/mod.rs b/crates/vm/src/system/native_adapter/mod.rs index 9fdde040ce..179a1fef29 100644 --- a/crates/vm/src/system/native_adapter/mod.rs +++ b/crates/vm/src/system/native_adapter/mod.rs @@ -28,7 +28,7 @@ use serde_big_array::BigArray; use super::memory::{online::TracingMemory, MemoryAuxColsFactory}; use crate::{ - arch::{AdapterExecutorE1, AdapterTraceStep}, + arch::{AdapterExecutorE1, AdapterTraceStep, VmStateMut}, system::memory::{online::GuestMemory, RecordId}, }; @@ -247,29 +247,39 @@ where type WriteData = [F; W]; #[inline(always)] - fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData { assert!(R <= 2); let &Instruction { b, c, e, f, .. } = instruction; let mut reads = [F::ZERO; R]; if R >= 1 { - let [value] = - unsafe { memory.read::(e.as_canonical_u32(), b.as_canonical_u32()) }; + let [value] = unsafe { + state + .memory + .read::(e.as_canonical_u32(), b.as_canonical_u32()) + }; reads[0] = value; } if R >= 2 { - let [value] = - unsafe { memory.read::(f.as_canonical_u32(), c.as_canonical_u32()) }; + let [value] = unsafe { + state + .memory + .read::(f.as_canonical_u32(), c.as_canonical_u32()) + }; reads[1] = value; } reads } #[inline(always)] - fn write( + fn write( &self, - memory: &mut GuestMemory, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, ) { @@ -277,7 +287,11 @@ where let &Instruction { a, d, .. } = instruction; if W >= 1 { - unsafe { memory.write(d.as_canonical_u32(), a.as_canonical_u32(), data) }; + unsafe { + state + .memory + .write(d.as_canonical_u32(), a.as_canonical_u32(), data) + }; } } } diff --git a/crates/vm/src/system/phantom/mod.rs b/crates/vm/src/system/phantom/mod.rs index e5f0e91fc0..4e151c525b 100644 --- a/crates/vm/src/system/phantom/mod.rs +++ b/crates/vm/src/system/phantom/mod.rs @@ -26,6 +26,7 @@ use serde_big_array::BigArray; use super::memory::{online::GuestMemory, MemoryController}; use crate::{ arch::{ + execution_mode::{e1::E1Ctx, metered::MeteredCtx, E1E2ExecutionCtx}, ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InsExecutorE1, InstructionExecutor, PcIncOrSet, PhantomSubExecutor, Streams, VmStateMut, }, @@ -130,11 +131,12 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<(), ExecutionError> where F: PrimeField32, + Ctx: E1E2ExecutionCtx, { let &Instruction { opcode, a, b, c, .. @@ -176,6 +178,20 @@ where Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + _chip_index: usize, + ) -> Result<(), ExecutionError> + where + F: PrimeField32, + { + self.execute_e1(state, instruction)?; + + Ok(()) + } } impl InstructionExecutor for PhantomChip { @@ -193,12 +209,12 @@ impl InstructionExecutor for PhantomChip { is_valid: F::ONE, }); - let state = VmStateMut { + let mut state = VmStateMut { pc: &mut pc, memory: &mut memory.memory.data, - ctx: &mut (), + ctx: &mut E1Ctx::default(), }; - self.execute_e1(state, instruction)?; + self.execute_e1(&mut state, instruction)?; memory.increment_timestamp(); Ok(ExecutionState { diff --git a/crates/vm/src/system/poseidon2/trace.rs b/crates/vm/src/system/poseidon2/trace.rs index 2b6f3e6b0b..979585c830 100644 --- a/crates/vm/src/system/poseidon2/trace.rs +++ b/crates/vm/src/system/poseidon2/trace.rs @@ -8,7 +8,6 @@ use openvm_stark_backend::{ p3_matrix::dense::RowMajorMatrix, p3_maybe_rayon::prelude::*, prover::types::AirProofInput, - rap::get_air_name, AirRef, Chip, ChipUsageGetter, }; @@ -63,7 +62,7 @@ impl ChipUsageGetter for Poseidon2PeripheryBaseChip { fn air_name(&self) -> String { - get_air_name(&self.air) + format!("Poseidon2PeripheryAir", SBOX_REGISTERS) } fn current_trace_height(&self) -> usize { diff --git a/crates/vm/src/system/public_values/core.rs b/crates/vm/src/system/public_values/core.rs index a573ae8e1e..7b798606e9 100644 --- a/crates/vm/src/system/public_values/core.rs +++ b/crates/vm/src/system/public_values/core.rs @@ -17,6 +17,7 @@ use serde::{Deserialize, Serialize}; use crate::{ arch::{ + execution_mode::{e1::E1Ctx, metered::MeteredCtx, E1E2ExecutionCtx}, AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, AdapterTraceStep, BasicAdapterInterface, MinimalInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmCoreChip, VmStateMut, @@ -211,10 +212,13 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { - let [value, index] = self.adapter.read(state.memory, instruction); + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { + let [value, index] = self.adapter.read(state, instruction); let idx: usize = index.as_canonical_u32() as usize; { @@ -233,6 +237,17 @@ where Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + _chip_index: usize, + ) -> Result<()> { + self.execute_e1(state, instruction)?; + + Ok(()) + } } // /// ATTENTION: If a specific public value is not provided, a default 0 will be used when diff --git a/extensions/rv32-adapters/src/eq_mod.rs b/extensions/rv32-adapters/src/eq_mod.rs index 9992f526cb..e8e2c88f47 100644 --- a/extensions/rv32-adapters/src/eq_mod.rs +++ b/extensions/rv32-adapters/src/eq_mod.rs @@ -6,8 +6,9 @@ use std::{ use itertools::izip; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, - ExecutionBridge, ExecutionState, MinimalInstruction, VmAdapterAir, + execution_mode::E1E2ExecutionCtx, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, + BasicAdapterInterface, ExecutionBridge, ExecutionState, MinimalInstruction, VmAdapterAir, + VmStateMut, }, system::memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, @@ -25,8 +26,8 @@ use openvm_instructions::{ riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}, }; use openvm_rv32im_circuit::adapters::{ - memory_read, memory_write, new_read_rv32_register, tracing_read, tracing_write, RV32_CELL_BITS, - RV32_REGISTER_NUM_LIMBS, + memory_read_from_state, memory_write_from_state, new_read_rv32_register_from_state, + tracing_read, tracing_write, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, }; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -394,7 +395,14 @@ impl< type ReadData = [[u8; TOTAL_READ_SIZE]; NUM_READS]; type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; - fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { let Instruction { b, c, d, e, .. } = *instruction; let d = d.as_canonical_u32(); @@ -405,23 +413,25 @@ impl< // Read register values let rs_vals = from_fn(|i| { let addr = if i == 0 { b } else { c }; - new_read_rv32_register(memory, d, addr.as_canonical_u32()) + new_read_rv32_register_from_state(state, d, addr.as_canonical_u32()) }); // Read memory values rs_vals.map(|address| { assert!(address as usize + TOTAL_READ_SIZE - 1 < (1 << self.pointer_max_bits)); - memory_read(memory, e, address) + memory_read_from_state(state, e, address) }) } - fn write( + fn write( &self, - memory: &mut GuestMemory, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, - ) { + ) where + Ctx: E1E2ExecutionCtx, + { let Instruction { a, d, .. } = *instruction; - memory_write(memory, d.as_canonical_u32(), a.as_canonical_u32(), data); + memory_write_from_state(state, d.as_canonical_u32(), a.as_canonical_u32(), data); } } diff --git a/extensions/rv32-adapters/src/heap.rs b/extensions/rv32-adapters/src/heap.rs index acc00eba11..d596092e39 100644 --- a/extensions/rv32-adapters/src/heap.rs +++ b/extensions/rv32-adapters/src/heap.rs @@ -2,8 +2,8 @@ use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, - ExecutionBridge, MinimalInstruction, VmAdapterAir, + execution_mode::E1E2ExecutionCtx, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, + BasicAdapterInterface, ExecutionBridge, MinimalInstruction, VmAdapterAir, VmStateMut, }, system::memory::{ offline_checker::MemoryBridge, @@ -172,19 +172,26 @@ impl) -> Self::ReadData { - let read_data = AdapterExecutorE1::::read(&self.0, memory, instruction); + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { + let read_data = AdapterExecutorE1::::read(&self.0, state, instruction); read_data.map(|r| r[0]) } - #[inline(always)] - fn write( + fn write( &self, - memory: &mut GuestMemory, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, - ) { - AdapterExecutorE1::::write(&self.0, memory, instruction, data); + ) where + Ctx: E1E2ExecutionCtx, + { + AdapterExecutorE1::::write(&self.0, state, instruction, data); } } diff --git a/extensions/rv32-adapters/src/heap_branch.rs b/extensions/rv32-adapters/src/heap_branch.rs index 1ce1873bb4..8997b78f2f 100644 --- a/extensions/rv32-adapters/src/heap_branch.rs +++ b/extensions/rv32-adapters/src/heap_branch.rs @@ -6,8 +6,9 @@ use std::{ use itertools::izip; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, - ExecutionBridge, ExecutionState, ImmInstruction, VmAdapterAir, + execution_mode::E1E2ExecutionCtx, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, + BasicAdapterInterface, ExecutionBridge, ExecutionState, ImmInstruction, VmAdapterAir, + VmStateMut, }, system::memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols}, @@ -25,7 +26,8 @@ use openvm_instructions::{ riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}, }; use openvm_rv32im_circuit::adapters::{ - memory_read, new_read_rv32_register, tracing_read, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + memory_read_from_state, new_read_rv32_register_from_state, tracing_read, RV32_CELL_BITS, + RV32_REGISTER_NUM_LIMBS, }; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -187,42 +189,6 @@ impl } } } -impl AdapterExecutorE1 - for Rv32HeapBranchAdapterStep -{ - type ReadData = [[u8; READ_SIZE]; NUM_READS]; - type WriteData = (); - - fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { - let Instruction { a, b, d, e, .. } = *instruction; - - let d = d.as_canonical_u32(); - let e = e.as_canonical_u32(); - debug_assert_eq!(d, RV32_REGISTER_AS); - debug_assert_eq!(e, RV32_MEMORY_AS); - - // Read register values - let rs_vals = from_fn(|i| { - let addr = if i == 0 { a } else { b }; - new_read_rv32_register(memory, d, addr.as_canonical_u32()) - }); - - // Read memory values - rs_vals.map(|address| { - assert!(address as usize + READ_SIZE - 1 < (1 << self.pointer_max_bits)); - memory_read(memory, e, address) - }) - } - - fn write( - &self, - _memory: &mut GuestMemory, - _instruction: &Instruction, - _data: &Self::WriteData, - ) { - // This function intentionally does nothing - } -} impl AdapterTraceStep for Rv32HeapBranchAdapterStep @@ -319,3 +285,49 @@ where ); } } + +impl AdapterExecutorE1 + for Rv32HeapBranchAdapterStep +{ + type ReadData = [[u8; READ_SIZE]; NUM_READS]; + type WriteData = (); + + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { + let Instruction { a, b, d, e, .. } = *instruction; + + let d = d.as_canonical_u32(); + let e = e.as_canonical_u32(); + debug_assert_eq!(d, RV32_REGISTER_AS); + debug_assert_eq!(e, RV32_MEMORY_AS); + + // Read register values + let rs_vals = from_fn(|i| { + let addr = if i == 0 { a } else { b }; + new_read_rv32_register_from_state(state, d, addr.as_canonical_u32()) + }); + + // Read memory values + rs_vals.map(|address| { + assert!(address as usize + READ_SIZE - 1 < (1 << self.pointer_max_bits)); + memory_read_from_state(state, e, address) + }) + } + + fn write( + &self, + _state: &mut VmStateMut, + _instruction: &Instruction, + _data: &Self::WriteData, + ) where + Ctx: E1E2ExecutionCtx, + { + // This function intentionally does nothing + } +} diff --git a/extensions/rv32-adapters/src/vec_heap.rs b/extensions/rv32-adapters/src/vec_heap.rs index 85d8b05822..924cff2f3c 100644 --- a/extensions/rv32-adapters/src/vec_heap.rs +++ b/extensions/rv32-adapters/src/vec_heap.rs @@ -7,8 +7,8 @@ use std::{ use itertools::izip; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ExecutionBridge, ExecutionState, - VecHeapAdapterInterface, VmAdapterAir, + execution_mode::E1E2ExecutionCtx, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, + ExecutionBridge, ExecutionState, VecHeapAdapterInterface, VmAdapterAir, VmStateMut, }, system::memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, @@ -26,8 +26,9 @@ use openvm_instructions::{ riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}, }; use openvm_rv32im_circuit::adapters::{ - abstract_compose, memory_read, memory_write, new_read_rv32_register, tracing_read, - tracing_write, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + abstract_compose, memory_read_from_state, memory_write_from_state, + new_read_rv32_register_from_state, tracing_read, tracing_write, RV32_CELL_BITS, + RV32_REGISTER_NUM_LIMBS, }; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -382,12 +383,12 @@ impl< let rd_val = u32::from_le_bytes(cols.rd_val.map(|x| x.as_canonical_u32() as u8)); assert!(rd_val as usize + WRITE_SIZE * BLOCKS_PER_WRITE - 1 < (1 << self.pointer_max_bits)); - for i in 0..BLOCKS_PER_WRITE { + for (i, block) in data.iter().enumerate() { tracing_write( memory, e, rd_val + (i * WRITE_SIZE) as u32, - &data[i], + block, &mut cols.writes_aux[i], ); } @@ -463,7 +464,14 @@ impl< type ReadData = [[[u8; READ_SIZE]; BLOCKS_PER_READ]; NUM_READS]; type WriteData = [[u8; WRITE_SIZE]; BLOCKS_PER_WRITE]; - fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { let Instruction { b, c, d, e, .. } = *instruction; let d = d.as_canonical_u32(); @@ -474,7 +482,7 @@ impl< // Read register values let rs_vals = from_fn(|i| { let addr = if i == 0 { b } else { c }; - new_read_rv32_register(memory, d, addr.as_canonical_u32()) + new_read_rv32_register_from_state(state, d, addr.as_canonical_u32()) }); // Read memory values @@ -482,26 +490,29 @@ impl< assert!( address as usize + READ_SIZE * BLOCKS_PER_READ - 1 < (1 << self.pointer_max_bits) ); - from_fn(|i| memory_read(memory, e, address + (i * READ_SIZE) as u32)) + from_fn(|i| memory_read_from_state(state, e, address + (i * READ_SIZE) as u32)) }) } - fn write( + fn write( &self, - memory: &mut GuestMemory, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, - ) { + ) where + Ctx: E1E2ExecutionCtx, + { let Instruction { a, d, e, .. } = *instruction; - let rd_val = new_read_rv32_register(memory, d.as_canonical_u32(), a.as_canonical_u32()); + let rd_val = + new_read_rv32_register_from_state(state, d.as_canonical_u32(), a.as_canonical_u32()); assert!(rd_val as usize + WRITE_SIZE * BLOCKS_PER_WRITE - 1 < (1 << self.pointer_max_bits)); - for i in 0..BLOCKS_PER_WRITE { - memory_write( - memory, + for (i, block) in data.iter().enumerate() { + memory_write_from_state( + state, e.as_canonical_u32(), rd_val + (i * WRITE_SIZE) as u32, - &data[i], + block, ); } } diff --git a/extensions/rv32-adapters/src/vec_heap_two_reads.rs b/extensions/rv32-adapters/src/vec_heap_two_reads.rs index 9eeab26654..2929ece00b 100644 --- a/extensions/rv32-adapters/src/vec_heap_two_reads.rs +++ b/extensions/rv32-adapters/src/vec_heap_two_reads.rs @@ -7,8 +7,8 @@ use std::{ use itertools::izip; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ExecutionBridge, ExecutionState, - VecHeapTwoReadsAdapterInterface, VmAdapterAir, + execution_mode::E1E2ExecutionCtx, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, + ExecutionBridge, ExecutionState, VecHeapTwoReadsAdapterInterface, VmAdapterAir, VmStateMut, }, system::memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, @@ -26,8 +26,9 @@ use openvm_instructions::{ riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}, }; use openvm_rv32im_circuit::adapters::{ - abstract_compose, memory_read, memory_write, new_read_rv32_register, tracing_read, - tracing_write, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, + abstract_compose, memory_read_from_state, memory_write_from_state, + new_read_rv32_register_from_state, tracing_read, tracing_write, RV32_CELL_BITS, + RV32_REGISTER_NUM_LIMBS, }; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -440,12 +441,12 @@ impl< let rd_val = u32::from_le_bytes(cols.rd_val.map(|x| x.as_canonical_u32() as u8)); assert!(rd_val as usize + WRITE_SIZE * BLOCKS_PER_WRITE - 1 < (1 << self.pointer_max_bits)); - for i in 0..BLOCKS_PER_WRITE { + for (i, block) in data.iter().enumerate() { tracing_write( memory, e, rd_val + (i * WRITE_SIZE) as u32, - &data[i], + block, &mut cols.writes_aux[i], ); } @@ -521,7 +522,14 @@ impl< ); type WriteData = [[u8; WRITE_SIZE]; BLOCKS_PER_WRITE]; - fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { let Instruction { b, c, d, e, .. } = *instruction; let d = d.as_canonical_u32(); @@ -530,35 +538,40 @@ impl< debug_assert_eq!(e, RV32_MEMORY_AS); // Read register values - let rs1_val = new_read_rv32_register(memory, d, b.as_canonical_u32()); - let rs2_val = new_read_rv32_register(memory, d, c.as_canonical_u32()); + let rs1_val = new_read_rv32_register_from_state(state, d, b.as_canonical_u32()); + let rs2_val = new_read_rv32_register_from_state(state, d, c.as_canonical_u32()); assert!(rs1_val as usize + READ_SIZE * BLOCKS_PER_READ1 - 1 < (1 << self.pointer_max_bits)); assert!(rs2_val as usize + READ_SIZE * BLOCKS_PER_READ2 - 1 < (1 << self.pointer_max_bits)); // Read memory values - let read_data1 = from_fn(|i| memory_read(memory, e, rs1_val + (i * READ_SIZE) as u32)); - let read_data2 = from_fn(|i| memory_read(memory, e, rs2_val + (i * READ_SIZE) as u32)); + let read_data1 = + from_fn(|i| memory_read_from_state(state, e, rs1_val + (i * READ_SIZE) as u32)); + let read_data2 = + from_fn(|i| memory_read_from_state(state, e, rs2_val + (i * READ_SIZE) as u32)); (read_data1, read_data2) } - fn write( + fn write( &self, - memory: &mut GuestMemory, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, - ) { + ) where + Ctx: E1E2ExecutionCtx, + { let Instruction { a, d, e, .. } = *instruction; - let rd_val = new_read_rv32_register(memory, d.as_canonical_u32(), a.as_canonical_u32()); + let rd_val = + new_read_rv32_register_from_state(state, d.as_canonical_u32(), a.as_canonical_u32()); assert!(rd_val as usize + WRITE_SIZE * BLOCKS_PER_WRITE - 1 < (1 << self.pointer_max_bits)); - for i in 0..BLOCKS_PER_WRITE { - memory_write( - memory, + for (i, block) in data.iter().enumerate() { + memory_write_from_state( + state, e.as_canonical_u32(), rd_val + (i * WRITE_SIZE) as u32, - &data[i], + block, ); } } diff --git a/extensions/rv32im/circuit/src/adapters/alu.rs b/extensions/rv32im/circuit/src/adapters/alu.rs index a4b930f919..242a11f90b 100644 --- a/extensions/rv32im/circuit/src/adapters/alu.rs +++ b/extensions/rv32im/circuit/src/adapters/alu.rs @@ -2,8 +2,9 @@ use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, - ExecutionBridge, ExecutionState, MinimalInstruction, VmAdapterAir, + execution_mode::E1E2ExecutionCtx, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, + BasicAdapterInterface, ExecutionBridge, ExecutionState, MinimalInstruction, VmAdapterAir, + VmStateMut, }, system::memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, @@ -30,7 +31,7 @@ use openvm_stark_backend::{ use super::{ tracing_read, tracing_read_imm, tracing_write, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, }; -use crate::adapters::{memory_read, memory_write}; +use crate::adapters::{memory_read_from_state, memory_write_from_state}; #[repr(C)] #[derive(AlignedBorrow)] @@ -285,7 +286,14 @@ where type WriteData = [[u8; RV32_REGISTER_NUM_LIMBS]; 1]; #[inline(always)] - fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { let Instruction { b, c, d, e, .. } = instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); @@ -294,11 +302,11 @@ where ); let rs1: [u8; RV32_REGISTER_NUM_LIMBS] = - memory_read(memory, RV32_REGISTER_AS, b.as_canonical_u32()); + memory_read_from_state(state, RV32_REGISTER_AS, b.as_canonical_u32()); let rs2 = if e.as_canonical_u32() == RV32_REGISTER_AS { let rs2: [u8; RV32_REGISTER_NUM_LIMBS] = - memory_read(memory, RV32_REGISTER_AS, c.as_canonical_u32()); + memory_read_from_state(state, RV32_REGISTER_AS, c.as_canonical_u32()); rs2 } else { let imm = c.as_canonical_u32(); @@ -312,11 +320,18 @@ where } #[inline(always)] - fn write(&self, memory: &mut GuestMemory, instruction: &Instruction, rd: &Self::WriteData) { + fn write( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + rd: &Self::WriteData, + ) where + Ctx: E1E2ExecutionCtx, + { let Instruction { a, d, .. } = instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - memory_write(memory, d.as_canonical_u32(), a.as_canonical_u32(), &rd[0]); + memory_write_from_state(state, d.as_canonical_u32(), a.as_canonical_u32(), &rd[0]); } } diff --git a/extensions/rv32im/circuit/src/adapters/branch.rs b/extensions/rv32im/circuit/src/adapters/branch.rs index c760f54995..7af331d7c5 100644 --- a/extensions/rv32im/circuit/src/adapters/branch.rs +++ b/extensions/rv32im/circuit/src/adapters/branch.rs @@ -2,8 +2,9 @@ use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, - ExecutionBridge, ExecutionState, ImmInstruction, VmAdapterAir, + execution_mode::E1E2ExecutionCtx, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, + BasicAdapterInterface, ExecutionBridge, ExecutionState, ImmInstruction, VmAdapterAir, + VmStateMut, }, system::memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols}, @@ -22,7 +23,7 @@ use openvm_stark_backend::{ }; use super::RV32_REGISTER_NUM_LIMBS; -use crate::adapters::{memory_read, tracing_read}; +use crate::adapters::{memory_read_from_state, tracing_read}; #[repr(C)] #[derive(AlignedBorrow)] @@ -194,26 +195,35 @@ where type WriteData = (); #[inline(always)] - fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { let Instruction { a, b, d, e, .. } = instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); debug_assert_eq!(e.as_canonical_u32(), RV32_REGISTER_AS); let rs1: [u8; RV32_REGISTER_NUM_LIMBS] = - memory_read(memory, RV32_REGISTER_AS, a.as_canonical_u32()); + memory_read_from_state(state, RV32_REGISTER_AS, a.as_canonical_u32()); let rs2: [u8; RV32_REGISTER_NUM_LIMBS] = - memory_read(memory, RV32_REGISTER_AS, b.as_canonical_u32()); + memory_read_from_state(state, RV32_REGISTER_AS, b.as_canonical_u32()); [rs1, rs2] } #[inline(always)] - fn write( + fn write( &self, - _memory: &mut GuestMemory, + _state: &mut VmStateMut, _instruction: &Instruction, _data: &Self::WriteData, - ) { + ) where + Ctx: E1E2ExecutionCtx, + { } } diff --git a/extensions/rv32im/circuit/src/adapters/jalr.rs b/extensions/rv32im/circuit/src/adapters/jalr.rs index a2eeb9a11f..29bc4552df 100644 --- a/extensions/rv32im/circuit/src/adapters/jalr.rs +++ b/extensions/rv32im/circuit/src/adapters/jalr.rs @@ -2,8 +2,9 @@ use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, - ExecutionBridge, ExecutionState, SignedImmInstruction, VmAdapterAir, + execution_mode::E1E2ExecutionCtx, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, + BasicAdapterInterface, ExecutionBridge, ExecutionState, SignedImmInstruction, VmAdapterAir, + VmStateMut, }, system::memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, @@ -21,10 +22,11 @@ use openvm_stark_backend::{ p3_air::{AirBuilder, BaseAir}, p3_field::{Field, FieldAlgebra, PrimeField32}, }; -use serde::{Deserialize, Serialize}; use super::RV32_REGISTER_NUM_LIMBS; -use crate::adapters::{memory_read, memory_write, tracing_read, tracing_write}; +use crate::adapters::{ + memory_read_from_state, memory_write_from_state, tracing_read, tracing_write, +}; #[repr(C)] #[derive(Debug, Clone, AlignedBorrow)] @@ -242,24 +244,33 @@ where type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; #[inline(always)] - fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { let Instruction { b, d, .. } = instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); let rs1: [u8; RV32_REGISTER_NUM_LIMBS] = - memory_read(memory, RV32_REGISTER_AS, b.as_canonical_u32()); + memory_read_from_state(state, RV32_REGISTER_AS, b.as_canonical_u32()); rs1 } #[inline(always)] - fn write( + fn write( &self, - memory: &mut GuestMemory, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, - ) { + ) where + Ctx: E1E2ExecutionCtx, + { let Instruction { a, d, f: enabled, .. } = instruction; @@ -267,7 +278,7 @@ where debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); if *enabled != F::ZERO { - memory_write(memory, RV32_REGISTER_AS, a.as_canonical_u32(), data); + memory_write_from_state(state, RV32_REGISTER_AS, a.as_canonical_u32(), data); } } } diff --git a/extensions/rv32im/circuit/src/adapters/loadstore.rs b/extensions/rv32im/circuit/src/adapters/loadstore.rs index 951bf7099d..9ca3135af2 100644 --- a/extensions/rv32im/circuit/src/adapters/loadstore.rs +++ b/extensions/rv32im/circuit/src/adapters/loadstore.rs @@ -6,8 +6,8 @@ use std::{ use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ExecutionBridge, ExecutionState, - VmAdapterAir, VmAdapterInterface, + execution_mode::E1E2ExecutionCtx, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, + ExecutionBridge, ExecutionState, VmAdapterAir, VmAdapterInterface, VmStateMut, }, system::memory::{ offline_checker::{MemoryBaseAuxCols, MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, @@ -35,7 +35,8 @@ use openvm_stark_backend::{ use super::RV32_REGISTER_NUM_LIMBS; use crate::adapters::{ - memory_read, memory_write, tracing_read, tracing_write_with_base_aux, RV32_CELL_BITS, + memory_read, memory_read_from_state, memory_write_from_state, tracing_read, + tracing_write_with_base_aux, RV32_CELL_BITS, }; /// LoadStore Adapter handles all memory and register operations, so it must be aware @@ -563,7 +564,14 @@ where ); type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; - fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { let Instruction { opcode, a, @@ -583,7 +591,7 @@ where ); let rs1_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = - memory_read(memory, d.as_canonical_u32(), b.as_canonical_u32()); + memory_read_from_state(state, d.as_canonical_u32(), b.as_canonical_u32()); let rs1_val = u32::from_le_bytes(rs1_bytes); let imm = c.as_canonical_u32(); @@ -602,28 +610,32 @@ where let read_data: [u8; RV32_REGISTER_NUM_LIMBS] = match local_opcode { LOADW | LOADB | LOADH | LOADBU | LOADHU => { - memory_read(memory, e.as_canonical_u32(), ptr_val) + memory_read_from_state(state, e.as_canonical_u32(), ptr_val) + } + STOREW | STOREH | STOREB => { + memory_read_from_state(state, RV32_REGISTER_AS, a.as_canonical_u32()) } - STOREW | STOREH | STOREB => memory_read(memory, RV32_REGISTER_AS, a.as_canonical_u32()), }; // For stores, we need the previous memory content to preserve unchanged bytes let prev_data: [u8; RV32_REGISTER_NUM_LIMBS] = match local_opcode { - STOREW | STOREH | STOREB => memory_read(memory, e.as_canonical_u32(), ptr_val), + STOREW | STOREH | STOREB => memory_read(state.memory, e.as_canonical_u32(), ptr_val), LOADW | LOADB | LOADH | LOADBU | LOADHU => { - memory_read(memory, RV32_REGISTER_AS, a.as_canonical_u32()) + memory_read(state.memory, RV32_REGISTER_AS, a.as_canonical_u32()) } }; ((prev_data, read_data), shift_amount) } - fn write( + fn write( &self, - memory: &mut GuestMemory, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, - ) { + ) where + Ctx: E1E2ExecutionCtx, + { // TODO(ayush): remove duplication with read let &Instruction { opcode, @@ -645,7 +657,7 @@ where ); let rs1_bytes: [u8; RV32_REGISTER_NUM_LIMBS] = - memory_read(memory, RV32_REGISTER_AS, b.as_canonical_u32()); + memory_read(state.memory, RV32_REGISTER_AS, b.as_canonical_u32()); let rs1_val = u32::from_le_bytes(rs1_bytes); let imm = c.as_canonical_u32(); @@ -669,10 +681,10 @@ where match local_opcode { STOREW | STOREH | STOREB => { let ptr = mem_ptr_limbs[0] + mem_ptr_limbs[1] * (1 << (RV32_CELL_BITS * 2)); - memory_write(memory, e.as_canonical_u32(), ptr & 0xfffffffc, data); + memory_write_from_state(state, e.as_canonical_u32(), ptr & 0xfffffffc, data); } LOADW | LOADB | LOADH | LOADBU | LOADHU => { - memory_write(memory, RV32_REGISTER_AS, a.as_canonical_u32(), data); + memory_write_from_state(state, RV32_REGISTER_AS, a.as_canonical_u32(), data); } } } diff --git a/extensions/rv32im/circuit/src/adapters/mod.rs b/extensions/rv32im/circuit/src/adapters/mod.rs index ba458930df..484c646a52 100644 --- a/extensions/rv32im/circuit/src/adapters/mod.rs +++ b/extensions/rv32im/circuit/src/adapters/mod.rs @@ -1,10 +1,13 @@ use std::ops::Mul; -use openvm_circuit::system::memory::{ - offline_checker::{MemoryBaseAuxCols, MemoryReadAuxCols, MemoryWriteAuxCols}, - online::{GuestMemory, TracingMemory}, - tree::public_values::PUBLIC_VALUES_AS, - MemoryController, RecordId, +use openvm_circuit::{ + arch::{execution_mode::E1E2ExecutionCtx, VmStateMut}, + system::memory::{ + offline_checker::{MemoryBaseAuxCols, MemoryReadAuxCols, MemoryWriteAuxCols}, + online::{GuestMemory, TracingMemory}, + tree::public_values::PUBLIC_VALUES_AS, + MemoryController, RecordId, + }, }; use openvm_instructions::riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}; use openvm_stark_backend::p3_field::{FieldAlgebra, PrimeField32}; @@ -87,6 +90,34 @@ pub fn memory_write( unsafe { memory.write::(address_space, ptr, data) } } +#[inline(always)] +pub fn memory_read_from_state( + state: &mut VmStateMut, + address_space: u32, + ptr: u32, +) -> [u8; N] +where + Ctx: E1E2ExecutionCtx, +{ + state.ctx.on_memory_operation(address_space, ptr, N); + + memory_read(state.memory, address_space, ptr) +} + +#[inline(always)] +pub fn memory_write_from_state( + state: &mut VmStateMut, + address_space: u32, + ptr: u32, + data: &[u8; N], +) where + Ctx: E1E2ExecutionCtx, +{ + state.ctx.on_memory_operation(address_space, ptr, N); + + memory_write(state.memory, address_space, ptr, data) +} + /// Atomic read operation which increments the timestamp by 1. /// Returns `(t_prev, [ptr:4]_{address_space})` where `t_prev` is the timestamp of the last memory /// access. @@ -218,12 +249,24 @@ pub fn read_rv32_register( (record.0, val) } -// TODO(AG): if "register", why `address_space` is not hardcoded to be 1? #[inline(always)] pub fn new_read_rv32_register(memory: &GuestMemory, address_space: u32, ptr: u32) -> u32 { u32::from_le_bytes(memory_read(memory, address_space, ptr)) } +// TODO(AG): if "register", why `address_space` is not hardcoded to be 1? +#[inline(always)] +pub fn new_read_rv32_register_from_state( + state: &mut VmStateMut, + address_space: u32, + ptr: u32, +) -> u32 +where + Ctx: E1E2ExecutionCtx, +{ + u32::from_le_bytes(memory_read_from_state(state, address_space, ptr)) +} + /// Peeks at the value of a register without updating the memory state or incrementing the /// timestamp. pub fn unsafe_read_rv32_register(memory: &MemoryController, pointer: F) -> u32 { diff --git a/extensions/rv32im/circuit/src/adapters/mul.rs b/extensions/rv32im/circuit/src/adapters/mul.rs index 875b21eda1..de5460e402 100644 --- a/extensions/rv32im/circuit/src/adapters/mul.rs +++ b/extensions/rv32im/circuit/src/adapters/mul.rs @@ -2,8 +2,9 @@ use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, - ExecutionBridge, ExecutionState, MinimalInstruction, VmAdapterAir, + execution_mode::E1E2ExecutionCtx, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, + BasicAdapterInterface, ExecutionBridge, ExecutionState, MinimalInstruction, VmAdapterAir, + VmStateMut, }, system::memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, @@ -22,7 +23,7 @@ use openvm_stark_backend::{ }; use super::{tracing_write, RV32_REGISTER_NUM_LIMBS}; -use crate::adapters::{memory_read, memory_write, tracing_read}; +use crate::adapters::{memory_read_from_state, memory_write_from_state, tracing_read}; #[repr(C)] #[derive(AlignedBorrow)] @@ -227,25 +228,39 @@ where type WriteData = [[u8; RV32_REGISTER_NUM_LIMBS]; 1]; #[inline(always)] - fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { let Instruction { b, c, d, .. } = instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); let rs1: [u8; RV32_REGISTER_NUM_LIMBS] = - memory_read(memory, RV32_REGISTER_AS, b.as_canonical_u32()); + memory_read_from_state(state, RV32_REGISTER_AS, b.as_canonical_u32()); let rs2: [u8; RV32_REGISTER_NUM_LIMBS] = - memory_read(memory, RV32_REGISTER_AS, c.as_canonical_u32()); + memory_read_from_state(state, RV32_REGISTER_AS, c.as_canonical_u32()); [rs1, rs2] } #[inline(always)] - fn write(&self, memory: &mut GuestMemory, instruction: &Instruction, rd: &Self::WriteData) { + fn write( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + rd: &Self::WriteData, + ) where + Ctx: E1E2ExecutionCtx, + { let Instruction { a, d, .. } = *instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - memory_write(memory, RV32_REGISTER_AS, a.as_canonical_u32(), &rd[0]); + memory_write_from_state(state, RV32_REGISTER_AS, a.as_canonical_u32(), &rd[0]); } } diff --git a/extensions/rv32im/circuit/src/adapters/rdwrite.rs b/extensions/rv32im/circuit/src/adapters/rdwrite.rs index 59ebaddeb6..d577d32a0b 100644 --- a/extensions/rv32im/circuit/src/adapters/rdwrite.rs +++ b/extensions/rv32im/circuit/src/adapters/rdwrite.rs @@ -2,8 +2,9 @@ use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, - ExecutionBridge, ExecutionState, ImmInstruction, VmAdapterAir, + execution_mode::E1E2ExecutionCtx, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, + BasicAdapterInterface, ExecutionBridge, ExecutionState, ImmInstruction, VmAdapterAir, + VmStateMut, }, system::memory::{ offline_checker::{MemoryBridge, MemoryWriteAuxCols}, @@ -21,10 +22,9 @@ use openvm_stark_backend::{ p3_air::{AirBuilder, BaseAir}, p3_field::{Field, FieldAlgebra, PrimeField32}, }; -use serde::{Deserialize, Serialize}; use super::RV32_REGISTER_NUM_LIMBS; -use crate::adapters::{memory_write, tracing_write}; +use crate::adapters::{memory_write_from_state, tracing_write}; #[repr(C)] #[derive(Debug, Clone, AlignedBorrow)] @@ -265,15 +265,30 @@ where type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; #[inline(always)] - fn read(&self, _memory: &mut GuestMemory, _instruction: &Instruction) -> Self::ReadData {} + fn read( + &self, + _state: &mut VmStateMut, + _instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { + } #[inline(always)] - fn write(&self, memory: &mut GuestMemory, instruction: &Instruction, rd: &Self::WriteData) { + fn write( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + rd: &Self::WriteData, + ) where + Ctx: E1E2ExecutionCtx, + { let Instruction { a, d, .. } = instruction; debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - memory_write(memory, RV32_REGISTER_AS, a.as_canonical_u32(), rd); + memory_write_from_state(state, RV32_REGISTER_AS, a.as_canonical_u32(), rd); } } @@ -375,18 +390,32 @@ where type WriteData = [u8; RV32_REGISTER_NUM_LIMBS]; #[inline(always)] - fn read(&self, memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { - >::read(&self.inner, memory, instruction) + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { + >::read(&self.inner, state, instruction) } #[inline(always)] - fn write(&self, memory: &mut GuestMemory, instruction: &Instruction, rd: &Self::WriteData) { + fn write( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + rd: &Self::WriteData, + ) where + Ctx: E1E2ExecutionCtx, + { let Instruction { f: enabled, .. } = instruction; if *enabled != F::ZERO { >::write( &self.inner, - memory, + state, instruction, rd, ) diff --git a/extensions/rv32im/circuit/src/auipc/core.rs b/extensions/rv32im/circuit/src/auipc/core.rs index 1e746ccbe8..e8cd98ac4a 100644 --- a/extensions/rv32im/circuit/src/auipc/core.rs +++ b/extensions/rv32im/circuit/src/auipc/core.rs @@ -5,6 +5,7 @@ use std::{ use openvm_circuit::{ arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ImmInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, @@ -29,7 +30,6 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, rap::BaseAirWithPublicValues, }; -use serde::{Deserialize, Serialize}; use crate::adapters::{Rv32RdWriteAdapterCols, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; @@ -239,7 +239,7 @@ where let local_opcode = Rv32AuipcOpcode::from_usize(opcode.local_opcode_idx(Rv32AuipcOpcode::CLASS_OFFSET)); - let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -312,9 +312,12 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let Instruction { opcode, c: imm, .. } = instruction; let local_opcode = @@ -323,12 +326,24 @@ where let imm = imm.as_canonical_u32(); let rd = run_auipc(local_opcode, *state.pc, imm); - self.adapter.write(state.memory, instruction, &rd); + self.adapter.write(state, instruction, &rd); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + state.ctx.trace_heights[chip_index] += 1; + self.execute_e1(state, instruction)?; + + Ok(()) + } } // returns rd_data diff --git a/extensions/rv32im/circuit/src/base_alu/core.rs b/extensions/rv32im/circuit/src/base_alu/core.rs index a653a69da3..7e87739569 100644 --- a/extensions/rv32im/circuit/src/base_alu/core.rs +++ b/extensions/rv32im/circuit/src/base_alu/core.rs @@ -6,6 +6,7 @@ use std::{ use openvm_circuit::{ arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, @@ -285,21 +286,36 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let Instruction { opcode, .. } = instruction; let local_opcode = BaseAluOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let [rs1, rs2] = self.adapter.read(state.memory, instruction).into(); + let [rs1, rs2] = self.adapter.read(state, instruction).into(); let rd = run_alu::(local_opcode, &rs1, &rs2); - self.adapter.write(state.memory, instruction, &[rd].into()); + self.adapter.write(state, instruction, &[rd].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + state.ctx.trace_heights[chip_index] += 1; + self.execute_e1(state, instruction)?; + + Ok(()) + } } #[inline(always)] diff --git a/extensions/rv32im/circuit/src/branch_eq/core.rs b/extensions/rv32im/circuit/src/branch_eq/core.rs index 901de4943a..b95bb0f79b 100644 --- a/extensions/rv32im/circuit/src/branch_eq/core.rs +++ b/extensions/rv32im/circuit/src/branch_eq/core.rs @@ -5,6 +5,7 @@ use std::{ use openvm_circuit::{ arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ImmInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, @@ -13,12 +14,9 @@ use openvm_circuit::{ MemoryAuxColsFactory, }, }; -use openvm_circuit_primitives::{ - bitwise_op_lookup::{BitwiseOperationLookupChip, SharedBitwiseOperationLookupChip}, - utils::not, -}; +use openvm_circuit_primitives::utils::not; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, riscv::RV32_CELL_BITS, LocalOpcode}; +use openvm_instructions::{instruction::Instruction, LocalOpcode}; use openvm_rv32im_transpiler::BranchEqualOpcode; use openvm_stark_backend::{ interaction::InteractionBuilder, @@ -232,14 +230,17 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let &Instruction { opcode, c: imm, .. } = instruction; let branch_eq_opcode = BranchEqualOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let [rs1, rs2] = self.adapter.read(state.memory, instruction).into(); + let [rs1, rs2] = self.adapter.read(state, instruction).into(); // TODO(ayush): probably don't need the other values let (cmp_result, _, _) = run_eq::(branch_eq_opcode, &rs1, &rs2); @@ -254,6 +255,18 @@ where Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + state.ctx.trace_heights[chip_index] += 1; + self.execute_e1(state, instruction)?; + + Ok(()) + } } // Returns (cmp_result, diff_idx, x[diff_idx] - y[diff_idx]) diff --git a/extensions/rv32im/circuit/src/branch_eq/tests.rs b/extensions/rv32im/circuit/src/branch_eq/tests.rs index 33d4566315..e5da1f901f 100644 --- a/extensions/rv32im/circuit/src/branch_eq/tests.rs +++ b/extensions/rv32im/circuit/src/branch_eq/tests.rs @@ -40,7 +40,7 @@ const MAX_INS_CAPACITY: usize = 128; const ABS_MAX_IMM: i32 = 1 << (RV_B_TYPE_IMM_BITS - 1); fn create_test_chip(tester: &mut VmChipTestBuilder) -> Rv32BranchEqualChip { - let chip = Rv32BranchEqualChip::::new( + Rv32BranchEqualChip::::new( VmAirWrapper::new( Rv32BranchAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), BranchEqualCoreAir::new(BranchEqualOpcode::CLASS_OFFSET, DEFAULT_PC_STEP), @@ -52,9 +52,7 @@ fn create_test_chip(tester: &mut VmChipTestBuilder) -> Rv32BranchEqualChip ), MAX_INS_CAPACITY, tester.memory_helper(), - ); - - chip + ) } #[allow(clippy::too_many_arguments)] diff --git a/extensions/rv32im/circuit/src/branch_lt/core.rs b/extensions/rv32im/circuit/src/branch_lt/core.rs index b08ff35ae3..6240c064f5 100644 --- a/extensions/rv32im/circuit/src/branch_lt/core.rs +++ b/extensions/rv32im/circuit/src/branch_lt/core.rs @@ -5,6 +5,7 @@ use std::{ use openvm_circuit::{ arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ImmInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, @@ -360,14 +361,17 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let &Instruction { opcode, c: imm, .. } = instruction; let blt_opcode = BranchLessThanOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let [rs1, rs2] = self.adapter.read(state.memory, instruction).into(); + let [rs1, rs2] = self.adapter.read(state, instruction).into(); // TODO(ayush): probably don't need the other values let (cmp_result, _, _, _) = run_cmp::(blt_opcode, &rs1, &rs2); @@ -380,6 +384,18 @@ where Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + state.ctx.trace_heights[chip_index] += 1; + self.execute_e1(state, instruction)?; + + Ok(()) + } } // Returns (cmp_result, diff_idx, x_sign, y_sign) diff --git a/extensions/rv32im/circuit/src/divrem/core.rs b/extensions/rv32im/circuit/src/divrem/core.rs index c3141249ac..01585ab2af 100644 --- a/extensions/rv32im/circuit/src/divrem/core.rs +++ b/extensions/rv32im/circuit/src/divrem/core.rs @@ -7,6 +7,7 @@ use num_bigint::BigUint; use num_integer::Integer; use openvm_circuit::{ arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, @@ -463,7 +464,7 @@ where ); } - let c_sum_f = F::from_canonical_u32(c.iter().fold(0, |acc, c| acc + c)); + let c_sum_f = F::from_canonical_u32(c.iter().sum()); let c_sum_inv_f = c_sum_f.try_inverse().unwrap_or(F::ZERO); let r_sum_f = r @@ -544,15 +545,18 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let Instruction { opcode, .. } = *instruction; // Determine opcode and operation type let divrem_opcode = DivRemOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let [rs1, rs2] = self.adapter.read(state.memory, instruction).into(); + let [rs1, rs2] = self.adapter.read(state, instruction).into(); let rs1 = rs1.map(u32::from); let rs2 = rs2.map(u32::from); @@ -569,12 +573,24 @@ where r.map(|x| x as u8) }; - self.adapter.write(state.memory, instruction, &[rd].into()); + self.adapter.write(state, instruction, &[rd].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + state.ctx.trace_heights[chip_index] += 1; + self.execute_e1(state, instruction)?; + + Ok(()) + } } // Returns (quotient, remainder, x_sign, y_sign, q_sign, case) where case = 0 for normal, 1 diff --git a/extensions/rv32im/circuit/src/hintstore/mod.rs b/extensions/rv32im/circuit/src/hintstore/mod.rs index 41b0501100..e770abc4fe 100644 --- a/extensions/rv32im/circuit/src/hintstore/mod.rs +++ b/extensions/rv32im/circuit/src/hintstore/mod.rs @@ -5,22 +5,19 @@ use std::{ use openvm_circuit::{ arch::{ - ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InsExecutorE1, - InstructionExecutor, NewVmChipWrapper, Result, StepExecutorE1, Streams, TraceStep, - VmStateMut, + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, + ExecutionBridge, ExecutionError, ExecutionState, NewVmChipWrapper, Result, StepExecutorE1, + Streams, TraceStep, VmStateMut, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, - online::{GuestMemory, TracingMemory}, - MemoryAddress, MemoryAuxColsFactory, MemoryController, RecordId, - }, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, + online::{GuestMemory, TracingMemory}, + MemoryAddress, MemoryAuxColsFactory, RecordId, }, }; use openvm_circuit_primitives::{ bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip}, - utils::{next_power_of_two_or_zero, not}, + utils::not, }; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{ @@ -34,21 +31,15 @@ use openvm_rv32im_transpiler::{ Rv32HintStoreOpcode::{HINT_BUFFER, HINT_STOREW}, }; use openvm_stark_backend::{ - config::{StarkGenericConfig, Val}, interaction::InteractionBuilder, p3_air::{Air, AirBuilder, BaseAir}, p3_field::{Field, FieldAlgebra, PrimeField32}, - p3_matrix::{dense::RowMajorMatrix, Matrix}, - prover::types::AirProofInput, - rap::{AnyRap, BaseAirWithPublicValues, PartitionedBaseAir}, - Chip, ChipUsageGetter, + p3_matrix::Matrix, + rap::{BaseAirWithPublicValues, PartitionedBaseAir}, }; -use rand::distributions::weighted; use serde::{Deserialize, Serialize}; -use crate::adapters::{ - decompose, memory_read, memory_write, tmp_convert_to_u8s, tracing_read, tracing_write, -}; +use crate::adapters::{decompose, memory_read, memory_write, tracing_read, tracing_write}; #[cfg(test)] mod tests; @@ -345,7 +336,7 @@ where let local_opcode = Rv32HintStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let mut row: &mut Rv32HintStoreCols = + let row: &mut Rv32HintStoreCols = trace[*trace_offset..*trace_offset + width].borrow_mut(); row.from_state.pc = F::from_canonical_u32(*state.pc); @@ -464,9 +455,12 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let &Instruction { opcode, a: num_words_ptr, @@ -523,6 +517,38 @@ where Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + // TODO(ayush): remove duplication + let &Instruction { + opcode, + a: num_words_ptr, + .. + } = instruction; + + let local_opcode = Rv32HintStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + + let num_words = if local_opcode == HINT_STOREW { + 1 + } else { + let num_words_limbs = memory_read( + state.memory, + RV32_REGISTER_AS, + num_words_ptr.as_canonical_u32(), + ); + u32::from_le_bytes(num_words_limbs) + }; + + state.ctx.trace_heights[chip_index] += num_words as usize; + self.execute_e1(state, instruction)?; + + Ok(()) + } } pub type Rv32HintStoreChip = NewVmChipWrapper>; diff --git a/extensions/rv32im/circuit/src/jal_lui/core.rs b/extensions/rv32im/circuit/src/jal_lui/core.rs index e90c0a8432..e210ad4eb3 100644 --- a/extensions/rv32im/circuit/src/jal_lui/core.rs +++ b/extensions/rv32im/circuit/src/jal_lui/core.rs @@ -2,6 +2,7 @@ use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ImmInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, @@ -26,7 +27,6 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, rap::BaseAirWithPublicValues, }; -use serde::{Deserialize, Serialize}; use crate::adapters::{RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, RV_J_TYPE_IMM_BITS}; @@ -194,7 +194,7 @@ where let local_opcode = Rv32JalLuiOpcode::from_usize(opcode.local_opcode_idx(Rv32JalLuiOpcode::CLASS_OFFSET)); - let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -257,9 +257,12 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let Instruction { opcode, c: imm, .. } = instruction; let local_opcode = @@ -280,12 +283,24 @@ where }; let (to_pc, rd) = run_jal_lui(local_opcode, *state.pc, signed_imm); - self.adapter.write(state.memory, instruction, &rd); + self.adapter.write(state, instruction, &rd); *state.pc = to_pc; Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + state.ctx.trace_heights[chip_index] += 1; + self.execute_e1(state, instruction)?; + + Ok(()) + } } // returns (to_pc, rd_data) diff --git a/extensions/rv32im/circuit/src/jalr/core.rs b/extensions/rv32im/circuit/src/jalr/core.rs index b0746c5ae8..2f016d826c 100644 --- a/extensions/rv32im/circuit/src/jalr/core.rs +++ b/extensions/rv32im/circuit/src/jalr/core.rs @@ -5,6 +5,7 @@ use std::{ use openvm_circuit::{ arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, Result, SignedImmInstruction, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, @@ -30,7 +31,6 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, rap::BaseAirWithPublicValues, }; -use serde::{Deserialize, Serialize}; use crate::adapters::{compose, Rv32JalrAdapterCols, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS}; @@ -229,7 +229,7 @@ where let local_opcode = Rv32JalrOpcode::from_usize(opcode.local_opcode_idx(Rv32JalrOpcode::CLASS_OFFSET)); - let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -334,15 +334,18 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let Instruction { opcode, c, g, .. } = instruction; let local_opcode = Rv32JalrOpcode::from_usize(opcode.local_opcode_idx(Rv32JalrOpcode::CLASS_OFFSET)); - let rs1 = self.adapter.read(state.memory, instruction); + let rs1 = self.adapter.read(state, instruction); let rs1 = u32::from_le_bytes(rs1); let imm = c.as_canonical_u32(); @@ -353,12 +356,24 @@ where let (to_pc, rd) = run_jalr(local_opcode, *state.pc, imm_extended, rs1); let rd = rd.map(|x| x as u8); - self.adapter.write(state.memory, instruction, &rd); + self.adapter.write(state, instruction, &rd); *state.pc = to_pc; Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + state.ctx.trace_heights[chip_index] += 1; + self.execute_e1(state, instruction)?; + + Ok(()) + } } // returns (to_pc, rd_data) diff --git a/extensions/rv32im/circuit/src/less_than/core.rs b/extensions/rv32im/circuit/src/less_than/core.rs index 6baeab4ddd..1ddd266ab6 100644 --- a/extensions/rv32im/circuit/src/less_than/core.rs +++ b/extensions/rv32im/circuit/src/less_than/core.rs @@ -5,6 +5,7 @@ use std::{ use openvm_circuit::{ arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, @@ -337,14 +338,17 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let Instruction { opcode, .. } = instruction; let less_than_opcode = LessThanOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let [rs1, rs2] = self.adapter.read(state.memory, instruction).into(); + let [rs1, rs2] = self.adapter.read(state, instruction).into(); // Run the comparison let (cmp_result, _, _, _) = @@ -352,12 +356,24 @@ where let mut rd = [0u8; NUM_LIMBS]; rd[0] = cmp_result as u8; - self.adapter.write(state.memory, instruction, &[rd].into()); + self.adapter.write(state, instruction, &[rd].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + state.ctx.trace_heights[chip_index] += 1; + self.execute_e1(state, instruction)?; + + Ok(()) + } } // Returns (cmp_result, diff_idx, x_sign, y_sign) diff --git a/extensions/rv32im/circuit/src/load_sign_extend/core.rs b/extensions/rv32im/circuit/src/load_sign_extend/core.rs index d2782005c9..992229b893 100644 --- a/extensions/rv32im/circuit/src/load_sign_extend/core.rs +++ b/extensions/rv32im/circuit/src/load_sign_extend/core.rs @@ -5,6 +5,7 @@ use std::{ use openvm_circuit::{ arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, @@ -235,7 +236,7 @@ where opcode.local_opcode_idx(Rv32LoadStoreOpcode::CLASS_OFFSET), ); - let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -316,16 +317,19 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let Instruction { opcode, .. } = instruction; let local_opcode = Rv32LoadStoreOpcode::from_usize( opcode.local_opcode_idx(Rv32LoadStoreOpcode::CLASS_OFFSET), ); - let ((_, read_data), shift_amount) = self.adapter.read(state.memory, instruction); + let ((_, read_data), shift_amount) = self.adapter.read(state, instruction); let read_data = read_data.map(F::from_canonical_u8); // TODO(ayush): clean this up for e1 @@ -337,12 +341,24 @@ where ); let write_data = write_data.map(|x| x.as_canonical_u32() as u8); - self.adapter.write(state.memory, instruction, &write_data); + self.adapter.write(state, instruction, &write_data); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + state.ctx.trace_heights[chip_index] += 1; + self.execute_e1(state, instruction)?; + + Ok(()) + } } // TODO(ayush): remove _prev_data diff --git a/extensions/rv32im/circuit/src/load_sign_extend/tests.rs b/extensions/rv32im/circuit/src/load_sign_extend/tests.rs index 32474eb494..b3f4805a00 100644 --- a/extensions/rv32im/circuit/src/load_sign_extend/tests.rs +++ b/extensions/rv32im/circuit/src/load_sign_extend/tests.rs @@ -41,7 +41,7 @@ fn into_limbs(num: u32) -> [u32; fn create_test_chip(tester: &mut VmChipTestBuilder) -> Rv32LoadSignExtendChip { let range_checker_chip = tester.memory_controller().range_checker.clone(); - let chip = Rv32LoadSignExtendChip::::new( + Rv32LoadSignExtendChip::::new( VmAirWrapper::new( Rv32LoadStoreAdapterAir::new( tester.memory_bridge(), @@ -57,9 +57,7 @@ fn create_test_chip(tester: &mut VmChipTestBuilder) -> Rv32LoadSignExtendChip ), MAX_INS_CAPACITY, tester.memory_helper(), - ); - - chip + ) } #[allow(clippy::too_many_arguments)] diff --git a/extensions/rv32im/circuit/src/loadstore/core.rs b/extensions/rv32im/circuit/src/loadstore/core.rs index 9baa0c65ec..dc9be8aac5 100644 --- a/extensions/rv32im/circuit/src/loadstore/core.rs +++ b/extensions/rv32im/circuit/src/loadstore/core.rs @@ -2,6 +2,7 @@ use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, @@ -305,7 +306,7 @@ where let local_opcode = Rv32LoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let mut row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; A::start(*state.pc, state.memory, adapter_row); @@ -380,15 +381,18 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let Instruction { opcode, .. } = instruction; // Get the local opcode for this instruction let local_opcode = Rv32LoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let ((prev_data, read_data), shift_amount) = self.adapter.read(state.memory, instruction); + let ((prev_data, read_data), shift_amount) = self.adapter.read(state, instruction); let prev_data = prev_data.map(F::from_canonical_u8); let read_data = read_data.map(F::from_canonical_u8); @@ -396,12 +400,24 @@ where let write_data = run_write_data(local_opcode, read_data, prev_data, shift_amount); let write_data = write_data.map(|x| x.as_canonical_u32() as u8); - self.adapter.write(state.memory, instruction, &write_data); + self.adapter.write(state, instruction, &write_data); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + state.ctx.trace_heights[chip_index] += 1; + self.execute_e1(state, instruction)?; + + Ok(()) + } } #[inline(always)] diff --git a/extensions/rv32im/circuit/src/mul/core.rs b/extensions/rv32im/circuit/src/mul/core.rs index b1df4b8e64..32ce87af1b 100644 --- a/extensions/rv32im/circuit/src/mul/core.rs +++ b/extensions/rv32im/circuit/src/mul/core.rs @@ -5,6 +5,7 @@ use std::{ use openvm_circuit::{ arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, @@ -236,9 +237,12 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let Instruction { opcode, .. } = instruction; // Verify the opcode is MUL @@ -248,16 +252,28 @@ where MulOpcode::MUL ); - let [rs1, rs2] = self.adapter.read(state.memory, instruction).into(); + let [rs1, rs2] = self.adapter.read(state, instruction).into(); let (rd, _) = run_mul::(&rs1, &rs2); - self.adapter.write(state.memory, instruction, &[rd].into()); + self.adapter.write(state, instruction, &[rd].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + state.ctx.trace_heights[chip_index] += 1; + self.execute_e1(state, instruction)?; + + Ok(()) + } } // returns mul, carry diff --git a/extensions/rv32im/circuit/src/mulh/core.rs b/extensions/rv32im/circuit/src/mulh/core.rs index 1a202cb306..361a912b02 100644 --- a/extensions/rv32im/circuit/src/mulh/core.rs +++ b/extensions/rv32im/circuit/src/mulh/core.rs @@ -5,6 +5,7 @@ use std::{ use openvm_circuit::{ arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, @@ -326,26 +327,41 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let Instruction { opcode, .. } = instruction; let mulh_opcode = MulHOpcode::from_usize(opcode.local_opcode_idx(MulHOpcode::CLASS_OFFSET)); - let [rs1, rs2] = self.adapter.read(state.memory, instruction).into(); + let [rs1, rs2] = self.adapter.read(state, instruction).into(); let rs1 = rs1.map(u32::from); let rs2 = rs2.map(u32::from); let (rd, _, _, _, _) = run_mulh::(mulh_opcode, &rs1, &rs2); let rd = rd.map(|x| x as u8); - self.adapter.write(state.memory, instruction, &[rd].into()); + self.adapter.write(state, instruction, &[rd].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + state.ctx.trace_heights[chip_index] += 1; + self.execute_e1(state, instruction)?; + + Ok(()) + } } // returns mulh[[s]u], mul, carry, x_ext, y_ext diff --git a/extensions/rv32im/circuit/src/shift/core.rs b/extensions/rv32im/circuit/src/shift/core.rs index 872917852c..e8f903bd92 100644 --- a/extensions/rv32im/circuit/src/shift/core.rs +++ b/extensions/rv32im/circuit/src/shift/core.rs @@ -5,6 +5,7 @@ use std::{ use openvm_circuit::{ arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, @@ -399,23 +400,38 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let Instruction { opcode, .. } = instruction; let shift_opcode = ShiftOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let [rs1, rs2] = self.adapter.read(state.memory, instruction).into(); + let [rs1, rs2] = self.adapter.read(state, instruction).into(); let (rd, _, _) = run_shift::(shift_opcode, &rs1, &rs2); - self.adapter.write(state.memory, instruction, &[rd].into()); + self.adapter.write(state, instruction, &[rd].into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + state.ctx.trace_heights[chip_index] += 1; + self.execute_e1(state, instruction)?; + + Ok(()) + } } // Returns (result, limb_shift, bit_shift) From e8afdadf534d970b086f89037b7d95bb4a6f5c5d Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Wed, 21 May 2025 11:53:46 -0400 Subject: [PATCH 28/49] fix: handle keccak256 input/output not 4-byte aligned (#1671) We removed support for memory read/writes that aren't 4-byte aligned (except risc-v loadh, loadb), so now the keccak guest binding must handle the input misalignment cases --- crates/toolchain/platform/src/lib.rs | 2 +- .../src/arch/execution_mode/metered/exact.rs | 3 +- .../vm/src/arch/execution_mode/metered/mod.rs | 4 +- crates/vm/src/arch/segment.rs | 21 +-- extensions/keccak256/circuit/src/extension.rs | 3 +- extensions/keccak256/circuit/src/lib.rs | 17 ++- extensions/keccak256/circuit/src/tests.rs | 3 +- extensions/keccak256/circuit/src/trace.rs | 3 +- extensions/keccak256/guest/src/lib.rs | 121 ++++++++++++++++-- extensions/sha256/circuit/src/extension.rs | 1 - .../sha256/circuit/src/sha256_chip/trace.rs | 1 - 11 files changed, 147 insertions(+), 32 deletions(-) diff --git a/crates/toolchain/platform/src/lib.rs b/crates/toolchain/platform/src/lib.rs index 901666d530..016211936b 100644 --- a/crates/toolchain/platform/src/lib.rs +++ b/crates/toolchain/platform/src/lib.rs @@ -4,7 +4,7 @@ #![deny(rustdoc::broken_intra_doc_links)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#[cfg(all(feature = "rust-runtime", target_os = "zkvm"))] +#[cfg(target_os = "zkvm")] pub use openvm_custom_insn::{custom_insn_i, custom_insn_r}; #[cfg(all(feature = "export-getrandom", target_os = "zkvm"))] mod getrandom; diff --git a/crates/vm/src/arch/execution_mode/metered/exact.rs b/crates/vm/src/arch/execution_mode/metered/exact.rs index 53dc49bea0..de91e95535 100644 --- a/crates/vm/src/arch/execution_mode/metered/exact.rs +++ b/crates/vm/src/arch/execution_mode/metered/exact.rs @@ -278,7 +278,8 @@ impl E1E2ExecutionCtx for MeteredCtxExact { self.update_adapter_heights(address_space, ptr, size); // Handle merkle tree updates - // TODO(ayush): see if this can be approximated by total number of reads/writes for AS != register + // TODO(ayush): see if this can be approximated by total number of reads/writes for AS != + // register self.update_boundary_merkle_heights(address_space, ptr, size); } } diff --git a/crates/vm/src/arch/execution_mode/metered/mod.rs b/crates/vm/src/arch/execution_mode/metered/mod.rs index 67698e8b5a..ea66f85f81 100644 --- a/crates/vm/src/arch/execution_mode/metered/mod.rs +++ b/crates/vm/src/arch/execution_mode/metered/mod.rs @@ -3,7 +3,6 @@ pub mod exact; // pub use exact::MeteredCtxExact as MeteredCtx; pub use bounded::MeteredCtxBounded as MeteredCtx; - use openvm_instructions::instruction::Instruction; use openvm_stark_backend::{p3_field::PrimeField32, ChipUsageGetter}; @@ -119,7 +118,8 @@ where state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ) { - // Program | Connector | Public Values | Memory ... | Executors (except Public Values) | Range Checker + // Program | Connector | Public Values | Memory ... | Executors (except Public Values) | + // Range Checker state.ctx.trace_heights[PROGRAM_AIR_ID] = chip_complex.program_chip().true_program_length; state.ctx.trace_heights[CONNECTOR_AIR_ID] = 2; diff --git a/crates/vm/src/arch/segment.rs b/crates/vm/src/arch/segment.rs index 4f325cef4e..142ed0a6b1 100644 --- a/crates/vm/src/arch/segment.rs +++ b/crates/vm/src/arch/segment.rs @@ -1,13 +1,3 @@ -use super::{ - execution_control::ExecutionControl, ExecutionError, GenerationError, SystemConfig, - VmChipComplex, VmComplexTraceHeights, VmConfig, -}; -#[cfg(feature = "bench-metrics")] -use crate::metrics::VmMetrics; -use crate::{ - arch::{instructions::*, InstructionExecutor}, - system::memory::online::GuestMemory, -}; use backtrace::Backtrace; use openvm_instructions::{ exe::FnBounds, @@ -23,6 +13,17 @@ use openvm_stark_backend::{ Chip, }; +use super::{ + execution_control::ExecutionControl, ExecutionError, GenerationError, SystemConfig, + VmChipComplex, VmComplexTraceHeights, VmConfig, +}; +#[cfg(feature = "bench-metrics")] +use crate::metrics::VmMetrics; +use crate::{ + arch::{instructions::*, InstructionExecutor}, + system::memory::online::GuestMemory, +}; + pub struct VmSegmentState { pub clk: u64, pub pc: u32, diff --git a/extensions/keccak256/circuit/src/extension.rs b/extensions/keccak256/circuit/src/extension.rs index e6101ef6da..616de6bfe5 100644 --- a/extensions/keccak256/circuit/src/extension.rs +++ b/extensions/keccak256/circuit/src/extension.rs @@ -1,3 +1,5 @@ +use std::result::Result; + use derive_more::derive::From; use openvm_circuit::{ arch::{ @@ -15,7 +17,6 @@ use openvm_rv32im_circuit::{ }; use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; -use std::result::Result; use strum::IntoEnumIterator; use crate::*; diff --git a/extensions/keccak256/circuit/src/lib.rs b/extensions/keccak256/circuit/src/lib.rs index 1cfbe0b515..33e5bbfd5b 100644 --- a/extensions/keccak256/circuit/src/lib.rs +++ b/extensions/keccak256/circuit/src/lib.rs @@ -3,7 +3,6 @@ use openvm_circuit_primitives::bitwise_op_lookup::SharedBitwiseOperationLookupChip; use openvm_stark_backend::p3_field::PrimeField32; - use tiny_keccak::{Hasher, Keccak}; pub mod air; @@ -19,7 +18,10 @@ mod tests; pub use air::KeccakVmAir; use openvm_circuit::{ - arch::{ExecutionBridge, NewVmChipWrapper, Result, StepExecutorE1, VmStateMut}, + arch::{ + execution_mode::metered::MeteredCtx, ExecutionBridge, NewVmChipWrapper, Result, + StepExecutorE1, VmStateMut, + }, system::memory::online::GuestMemory, }; use openvm_instructions::{ @@ -87,7 +89,7 @@ impl KeccakVmStep { impl StepExecutorE1 for KeccakVmStep { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> { let &Instruction { @@ -122,4 +124,13 @@ impl StepExecutorE1 for KeccakVmStep { memory_write(state.memory, e, dst, &output); Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + todo!() + } } diff --git a/extensions/keccak256/circuit/src/tests.rs b/extensions/keccak256/circuit/src/tests.rs index 221fdb144d..0732729024 100644 --- a/extensions/keccak256/circuit/src/tests.rs +++ b/extensions/keccak256/circuit/src/tests.rs @@ -24,9 +24,8 @@ use p3_keccak_air::NUM_ROUNDS; use rand::Rng; use tiny_keccak::Hasher; -use crate::{KeccakVmAir, KeccakVmStep}; - use super::{columns::KeccakVmCols, utils::num_keccak_f, KeccakVmChip}; +use crate::{KeccakVmAir, KeccakVmStep}; type F = BabyBear; const MAX_INS_CAPACITY: usize = 4096; diff --git a/extensions/keccak256/circuit/src/trace.rs b/extensions/keccak256/circuit/src/trace.rs index 0bb2d28e23..bf28db55bd 100644 --- a/extensions/keccak256/circuit/src/trace.rs +++ b/extensions/keccak256/circuit/src/trace.rs @@ -20,12 +20,11 @@ use p3_keccak_air::{ }; use tiny_keccak::{keccakf, Hasher, Keccak}; -use crate::{columns::NUM_KECCAK_VM_COLS, utils::num_keccak_f, KeccakVmStep, KECCAK_WORD_SIZE}; - use super::{ columns::KeccakVmCols, KECCAK_ABSORB_READS, KECCAK_DIGEST_WRITES, KECCAK_RATE_BYTES, KECCAK_REGISTER_READS, NUM_ABSORB_ROUNDS, }; +use crate::{columns::NUM_KECCAK_VM_COLS, utils::num_keccak_f, KeccakVmStep, KECCAK_WORD_SIZE}; impl TraceStep for KeccakVmStep { fn execute( diff --git a/extensions/keccak256/guest/src/lib.rs b/extensions/keccak256/guest/src/lib.rs index 459c4c910d..0e5fbfffe9 100644 --- a/extensions/keccak256/guest/src/lib.rs +++ b/extensions/keccak256/guest/src/lib.rs @@ -1,7 +1,9 @@ #![no_std] #[cfg(target_os = "zkvm")] -use core::mem::MaybeUninit; +extern crate alloc; +#[cfg(target_os = "zkvm")] +use {core::mem::MaybeUninit, zkvm::*}; /// This is custom-0 defined in RISC-V spec document pub const OPCODE: u8 = 0x0b; @@ -25,7 +27,7 @@ pub fn keccak256(input: &[u8]) -> [u8; 32] { } } -/// Native hook for keccak256 for use with `alloy-primitives` "native-keccak" feature. +/// keccak256 intrinsic binding /// /// # Safety /// @@ -33,14 +35,10 @@ pub fn keccak256(input: &[u8]) -> [u8; 32] { /// 32-byte hash. /// - `bytes` must point to an input buffer at least `len` long. /// - `output` must point to a buffer that is at least 32-bytes long. -/// -/// [`keccak256`]: https://en.wikipedia.org/wiki/SHA-3 -/// [`sha3`]: https://docs.rs/sha3/latest/sha3/ -/// [`tiny_keccak`]: https://docs.rs/tiny-keccak/latest/tiny_keccak/ +/// - `bytes` and `output` must be 4-byte aligned. #[cfg(target_os = "zkvm")] #[inline(always)] -#[no_mangle] -extern "C" fn native_keccak256(bytes: *const u8, len: usize, output: *mut u8) { +unsafe fn __native_keccak256(bytes: *const u8, len: usize, output: *mut u8) { openvm_platform::custom_insn_r!( opcode = OPCODE, funct3 = KECCAK256_FUNCT3, @@ -51,6 +49,46 @@ extern "C" fn native_keccak256(bytes: *const u8, len: usize, output: *mut u8) { ); } +/// Native hook for keccak256 for use with `alloy-primitives` "native-keccak" feature. +/// +/// # Safety +/// +/// The VM accepts the preimage by pointer and length, and writes the +/// 32-byte hash. +/// - `bytes` must point to an input buffer at least `len` long. +/// - `output` must point to a buffer that is at least 32-bytes long. +/// +/// [`keccak256`]: https://en.wikipedia.org/wiki/SHA-3 +/// [`sha3`]: https://docs.rs/sha3/latest/sha3/ +/// [`tiny_keccak`]: https://docs.rs/tiny-keccak/latest/tiny_keccak/ +#[cfg(target_os = "zkvm")] +#[inline(always)] +#[no_mangle] +extern "C" fn native_keccak256(bytes: *const u8, len: usize, output: *mut u8) { + // SAFETY: assuming safety assumptions of the inputs, we handle all cases where `bytes` or + // `output` are not aligned to 4 bytes. + unsafe { + if bytes as usize % MIN_ALIGN != 0 { + let aligned_buff = AlignedBuf::new(bytes, len); + if output as usize % MIN_ALIGN != 0 { + let aligned_out = AlignedBuf::uninit(32); + __native_keccak256(aligned_buff.ptr, len, aligned_out.ptr); + core::ptr::copy_nonoverlapping(aligned_out.ptr as *const u8, output, 32); + } else { + __native_keccak256(aligned_buff.ptr, len, output); + } + } else { + if output as usize % MIN_ALIGN != 0 { + let aligned_out = AlignedBuf::uninit(32); + __native_keccak256(bytes, len, aligned_out.ptr); + core::ptr::copy_nonoverlapping(aligned_out.ptr as *const u8, output, 32); + } else { + __native_keccak256(bytes, len, output); + } + }; + } +} + /// Sets `output` to the keccak256 hash of `input`. pub fn set_keccak256(input: &[u8], output: &mut [u8; 32]) { #[cfg(not(target_os = "zkvm"))] @@ -63,3 +101,70 @@ pub fn set_keccak256(input: &[u8], output: &mut [u8; 32]) { #[cfg(target_os = "zkvm")] native_keccak256(input.as_ptr(), input.len(), output.as_mut_ptr() as *mut u8); } + +#[cfg(target_os = "zkvm")] +mod zkvm { + use alloc::alloc::{alloc, dealloc, handle_alloc_error, Layout}; + use core::ptr::NonNull; + + use super::*; + + pub const MIN_ALIGN: usize = 4; + + /// Bytes aligned to 4 bytes. + pub struct AlignedBuf { + pub ptr: *mut u8, + pub layout: Layout, + } + + impl AlignedBuf { + /// Allocate a new buffer whose start address is aligned to 4 bytes. + pub fn uninit(len: usize) -> Self { + let layout = Layout::from_size_align(len, MIN_ALIGN).unwrap(); + if layout.size() == 0 { + return Self { + ptr: NonNull::::dangling().as_ptr() as *mut u8, + layout, + }; + } + // SAFETY: `len` is nonzero + let ptr = unsafe { alloc(layout) }; + if ptr.is_null() { + handle_alloc_error(layout); + } + AlignedBuf { ptr, layout } + } + + /// Allocate a new buffer whose start address is aligned to 4 bytes + /// and copy the given data into it. + /// + /// # Safety + /// - `bytes` must not be null + /// - `len` should not be zero + /// + /// See [alloc]. In particular `data` should not be empty. + pub unsafe fn new(bytes: *const u8, len: usize) -> Self { + let buf = Self::uninit(len); + // SAFETY: + // - src and dst are not null + // - src and dst are allocated for size + // - no alignment requirements on u8 + // - non-overlapping since ptr is newly allocated + unsafe { + core::ptr::copy_nonoverlapping(bytes, buf.ptr, len); + } + + buf + } + } + + impl Drop for AlignedBuf { + fn drop(&mut self) { + if self.layout.size() != 0 { + unsafe { + dealloc(self.ptr, self.layout); + } + } + } + } +} diff --git a/extensions/sha256/circuit/src/extension.rs b/extensions/sha256/circuit/src/extension.rs index 8a5892aef4..deba8b2d10 100644 --- a/extensions/sha256/circuit/src/extension.rs +++ b/extensions/sha256/circuit/src/extension.rs @@ -13,7 +13,6 @@ use openvm_rv32im_circuit::{ Rv32I, Rv32IExecutor, Rv32IPeriphery, Rv32Io, Rv32IoExecutor, Rv32IoPeriphery, Rv32M, Rv32MExecutor, Rv32MPeriphery, }; - use openvm_sha256_transpiler::Rv32Sha256Opcode; use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; diff --git a/extensions/sha256/circuit/src/sha256_chip/trace.rs b/extensions/sha256/circuit/src/sha256_chip/trace.rs index 8c88d62836..24d4799d3d 100644 --- a/extensions/sha256/circuit/src/sha256_chip/trace.rs +++ b/extensions/sha256/circuit/src/sha256_chip/trace.rs @@ -7,7 +7,6 @@ use openvm_circuit::{ arch::{Result, TraceStep, VmStateMut}, system::memory::{online::TracingMemory, MemoryAuxColsFactory}, }; - use openvm_instructions::{ instruction::Instruction, program::DEFAULT_PC_STEP, From 15a9ae877fba4fb09360ff6441f4c46a499fe4fc Mon Sep 17 00:00:00 2001 From: Arayi Khalatyan <127004086+arayikhalatyan@users.noreply.github.com> Date: Wed, 21 May 2025 14:55:42 -0400 Subject: [PATCH 29/49] fix: misaligned calls to sha256 (#1674) Fixes misaligned calls to sha256. Moved Aligned_buf to openvm platform so both `sha2` and `keccak` can access it. --- crates/toolchain/platform/src/alloc.rs | 62 +++++++++ crates/toolchain/platform/src/lib.rs | 3 + extensions/keccak256/circuit/src/lib.rs | 6 +- extensions/keccak256/guest/src/lib.rs | 120 ++++-------------- .../sha256/circuit/src/sha256_chip/mod.rs | 15 ++- extensions/sha256/guest/src/lib.rs | 50 +++++++- 6 files changed, 156 insertions(+), 100 deletions(-) create mode 100644 crates/toolchain/platform/src/alloc.rs diff --git a/crates/toolchain/platform/src/alloc.rs b/crates/toolchain/platform/src/alloc.rs new file mode 100644 index 0000000000..0af25a3671 --- /dev/null +++ b/crates/toolchain/platform/src/alloc.rs @@ -0,0 +1,62 @@ +extern crate alloc; + +use alloc::alloc::{alloc, dealloc, handle_alloc_error, Layout}; +use core::ptr::NonNull; + +/// Bytes allocated according to the given Layout +pub struct AlignedBuf { + pub ptr: *mut u8, + pub layout: Layout, +} + +impl AlignedBuf { + /// Allocate a new buffer whose start address is aligned to `align` bytes. + /// *NOTE* if `len` is zero then a creates new `NonNull` that is dangling and 16-byte aligned. + pub fn uninit(len: usize, align: usize) -> Self { + let layout = Layout::from_size_align(len, align).unwrap(); + if layout.size() == 0 { + return Self { + ptr: NonNull::::dangling().as_ptr() as *mut u8, + layout, + }; + } + // SAFETY: `len` is nonzero + let ptr = unsafe { alloc(layout) }; + if ptr.is_null() { + handle_alloc_error(layout); + } + AlignedBuf { ptr, layout } + } + + /// Allocate a new buffer whose start address is aligned to `align` bytes + /// and copy the given data into it. + /// + /// # Safety + /// - `bytes` must not be null + /// - `len` should not be zero + /// + /// See [alloc]. In particular `data` should not be empty. + pub unsafe fn new(bytes: *const u8, len: usize, align: usize) -> Self { + let buf = Self::uninit(len, align); + // SAFETY: + // - src and dst are not null + // - src and dst are allocated for size + // - no alignment requirements on u8 + // - non-overlapping since ptr is newly allocated + unsafe { + core::ptr::copy_nonoverlapping(bytes, buf.ptr, len); + } + + buf + } +} + +impl Drop for AlignedBuf { + fn drop(&mut self) { + if self.layout.size() != 0 { + unsafe { + dealloc(self.ptr, self.layout); + } + } + } +} diff --git a/crates/toolchain/platform/src/lib.rs b/crates/toolchain/platform/src/lib.rs index 016211936b..b6a9cfc88e 100644 --- a/crates/toolchain/platform/src/lib.rs +++ b/crates/toolchain/platform/src/lib.rs @@ -12,6 +12,9 @@ mod getrandom; pub mod heap; #[cfg(all(feature = "export-libm", target_os = "zkvm"))] mod libm_extern; +#[cfg(target_os = "zkvm")] +pub mod alloc; + pub mod memory; pub mod print; #[cfg(feature = "rust-runtime")] diff --git a/extensions/keccak256/circuit/src/lib.rs b/extensions/keccak256/circuit/src/lib.rs index 33e5bbfd5b..6ca057597f 100644 --- a/extensions/keccak256/circuit/src/lib.rs +++ b/extensions/keccak256/circuit/src/lib.rs @@ -127,9 +127,9 @@ impl StepExecutorE1 for KeccakVmStep { fn execute_metered( &mut self, - state: &mut VmStateMut, - instruction: &Instruction, - chip_index: usize, + _state: &mut VmStateMut, + _instruction: &Instruction, + _chip_index: usize, ) -> Result<()> { todo!() } diff --git a/extensions/keccak256/guest/src/lib.rs b/extensions/keccak256/guest/src/lib.rs index 0e5fbfffe9..86ba45e577 100644 --- a/extensions/keccak256/guest/src/lib.rs +++ b/extensions/keccak256/guest/src/lib.rs @@ -3,7 +3,7 @@ #[cfg(target_os = "zkvm")] extern crate alloc; #[cfg(target_os = "zkvm")] -use {core::mem::MaybeUninit, zkvm::*}; +use {core::mem::MaybeUninit, openvm_platform::alloc::AlignedBuf}; /// This is custom-0 defined in RISC-V spec document pub const OPCODE: u8 = 0x0b; @@ -27,28 +27,6 @@ pub fn keccak256(input: &[u8]) -> [u8; 32] { } } -/// keccak256 intrinsic binding -/// -/// # Safety -/// -/// The VM accepts the preimage by pointer and length, and writes the -/// 32-byte hash. -/// - `bytes` must point to an input buffer at least `len` long. -/// - `output` must point to a buffer that is at least 32-bytes long. -/// - `bytes` and `output` must be 4-byte aligned. -#[cfg(target_os = "zkvm")] -#[inline(always)] -unsafe fn __native_keccak256(bytes: *const u8, len: usize, output: *mut u8) { - openvm_platform::custom_insn_r!( - opcode = OPCODE, - funct3 = KECCAK256_FUNCT3, - funct7 = KECCAK256_FUNCT7, - rd = In output, - rs1 = In bytes, - rs2 = In len - ); -} - /// Native hook for keccak256 for use with `alloy-primitives` "native-keccak" feature. /// /// # Safety @@ -67,11 +45,12 @@ unsafe fn __native_keccak256(bytes: *const u8, len: usize, output: *mut u8) { extern "C" fn native_keccak256(bytes: *const u8, len: usize, output: *mut u8) { // SAFETY: assuming safety assumptions of the inputs, we handle all cases where `bytes` or // `output` are not aligned to 4 bytes. + const MIN_ALIGN: usize = 4; unsafe { if bytes as usize % MIN_ALIGN != 0 { - let aligned_buff = AlignedBuf::new(bytes, len); + let aligned_buff = AlignedBuf::new(bytes, len, MIN_ALIGN); if output as usize % MIN_ALIGN != 0 { - let aligned_out = AlignedBuf::uninit(32); + let aligned_out = AlignedBuf::uninit(32, MIN_ALIGN); __native_keccak256(aligned_buff.ptr, len, aligned_out.ptr); core::ptr::copy_nonoverlapping(aligned_out.ptr as *const u8, output, 32); } else { @@ -79,7 +58,7 @@ extern "C" fn native_keccak256(bytes: *const u8, len: usize, output: *mut u8) { } } else { if output as usize % MIN_ALIGN != 0 { - let aligned_out = AlignedBuf::uninit(32); + let aligned_out = AlignedBuf::uninit(32, MIN_ALIGN); __native_keccak256(bytes, len, aligned_out.ptr); core::ptr::copy_nonoverlapping(aligned_out.ptr as *const u8, output, 32); } else { @@ -89,6 +68,28 @@ extern "C" fn native_keccak256(bytes: *const u8, len: usize, output: *mut u8) { } } +/// keccak256 intrinsic binding +/// +/// # Safety +/// +/// The VM accepts the preimage by pointer and length, and writes the +/// 32-byte hash. +/// - `bytes` must point to an input buffer at least `len` long. +/// - `output` must point to a buffer that is at least 32-bytes long. +/// - `bytes` and `output` must be 4-byte aligned. +#[cfg(target_os = "zkvm")] +#[inline(always)] +fn __native_keccak256(bytes: *const u8, len: usize, output: *mut u8) { + openvm_platform::custom_insn_r!( + opcode = OPCODE, + funct3 = KECCAK256_FUNCT3, + funct7 = KECCAK256_FUNCT7, + rd = In output, + rs1 = In bytes, + rs2 = In len + ); +} + /// Sets `output` to the keccak256 hash of `input`. pub fn set_keccak256(input: &[u8], output: &mut [u8; 32]) { #[cfg(not(target_os = "zkvm"))] @@ -101,70 +102,3 @@ pub fn set_keccak256(input: &[u8], output: &mut [u8; 32]) { #[cfg(target_os = "zkvm")] native_keccak256(input.as_ptr(), input.len(), output.as_mut_ptr() as *mut u8); } - -#[cfg(target_os = "zkvm")] -mod zkvm { - use alloc::alloc::{alloc, dealloc, handle_alloc_error, Layout}; - use core::ptr::NonNull; - - use super::*; - - pub const MIN_ALIGN: usize = 4; - - /// Bytes aligned to 4 bytes. - pub struct AlignedBuf { - pub ptr: *mut u8, - pub layout: Layout, - } - - impl AlignedBuf { - /// Allocate a new buffer whose start address is aligned to 4 bytes. - pub fn uninit(len: usize) -> Self { - let layout = Layout::from_size_align(len, MIN_ALIGN).unwrap(); - if layout.size() == 0 { - return Self { - ptr: NonNull::::dangling().as_ptr() as *mut u8, - layout, - }; - } - // SAFETY: `len` is nonzero - let ptr = unsafe { alloc(layout) }; - if ptr.is_null() { - handle_alloc_error(layout); - } - AlignedBuf { ptr, layout } - } - - /// Allocate a new buffer whose start address is aligned to 4 bytes - /// and copy the given data into it. - /// - /// # Safety - /// - `bytes` must not be null - /// - `len` should not be zero - /// - /// See [alloc]. In particular `data` should not be empty. - pub unsafe fn new(bytes: *const u8, len: usize) -> Self { - let buf = Self::uninit(len); - // SAFETY: - // - src and dst are not null - // - src and dst are allocated for size - // - no alignment requirements on u8 - // - non-overlapping since ptr is newly allocated - unsafe { - core::ptr::copy_nonoverlapping(bytes, buf.ptr, len); - } - - buf - } - } - - impl Drop for AlignedBuf { - fn drop(&mut self) { - if self.layout.size() != 0 { - unsafe { - dealloc(self.ptr, self.layout); - } - } - } - } -} diff --git a/extensions/sha256/circuit/src/sha256_chip/mod.rs b/extensions/sha256/circuit/src/sha256_chip/mod.rs index f9be3fc137..cad58e807c 100644 --- a/extensions/sha256/circuit/src/sha256_chip/mod.rs +++ b/extensions/sha256/circuit/src/sha256_chip/mod.rs @@ -2,7 +2,9 @@ //! variable length inputs read from VM memory. use openvm_circuit::{ - arch::{NewVmChipWrapper, Result, StepExecutorE1, VmStateMut}, + arch::{ + execution_mode::metered::MeteredCtx, NewVmChipWrapper, Result, StepExecutorE1, VmStateMut, + }, system::memory::online::GuestMemory, }; use openvm_circuit_primitives::{ @@ -70,7 +72,7 @@ impl Sha256VmStep { impl StepExecutorE1 for Sha256VmStep { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> { let &Instruction { @@ -104,6 +106,15 @@ impl StepExecutorE1 for Sha256VmStep { memory_write(state.memory, e, dst, hasher.finalize().as_ref()); Ok(()) } + + fn execute_metered( + &mut self, + _state: &mut VmStateMut, + _instruction: &Instruction, + _chip_index: usize, + ) -> Result<()> { + todo!() + } } pub fn sha256_solve(input_message: &[u8]) -> [u8; SHA256_WRITE_SIZE] { diff --git a/extensions/sha256/guest/src/lib.rs b/extensions/sha256/guest/src/lib.rs index cb34bcd5aa..de7bc6fcb9 100644 --- a/extensions/sha256/guest/src/lib.rs +++ b/extensions/sha256/guest/src/lib.rs @@ -1,5 +1,8 @@ #![no_std] +#[cfg(target_os = "zkvm")] +use openvm_platform::alloc::AlignedBuf; + /// This is custom-0 defined in RISC-V spec document pub const OPCODE: u8 = 0x0b; pub const SHA256_FUNCT3: u8 = 0b100; @@ -13,7 +16,8 @@ pub fn sha256(input: &[u8]) -> [u8; 32] { output } -/// zkvm native implementation of sha256 +/// Native hook for sha256 +/// /// # Safety /// /// The VM accepts the preimage by pointer and length, and writes the @@ -21,11 +25,53 @@ pub fn sha256(input: &[u8]) -> [u8; 32] { /// - `bytes` must point to an input buffer at least `len` long. /// - `output` must point to a buffer that is at least 32-bytes long. /// -/// [`sha2-256`]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf +/// [`sha2`]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf #[cfg(target_os = "zkvm")] #[inline(always)] #[no_mangle] extern "C" fn zkvm_sha256_impl(bytes: *const u8, len: usize, output: *mut u8) { + // SAFETY: assuming safety assumptions of the inputs, we handle all cases where `bytes` or + // `output` are not aligned to 4 bytes. + // The minimum alignment required for the input and output buffers + const MIN_ALIGN: usize = 4; + // The preferred alignment for the input buffer, since the input is read in chunks of 16 bytes + const INPUT_ALIGN: usize = 16; + // The preferred alignment for the output buffer, since the output is written in chunks of 32 bytes + const OUTPUT_ALIGN: usize = 32; + unsafe { + if bytes as usize % MIN_ALIGN != 0 { + let aligned_buff = AlignedBuf::new(bytes, len, INPUT_ALIGN); + if output as usize % MIN_ALIGN != 0 { + let aligned_out = AlignedBuf::uninit(32, OUTPUT_ALIGN); + __native_sha256(aligned_buff.ptr, len, aligned_out.ptr); + core::ptr::copy_nonoverlapping(aligned_out.ptr as *const u8, output, 32); + } else { + __native_sha256(aligned_buff.ptr, len, output); + } + } else { + if output as usize % MIN_ALIGN != 0 { + let aligned_out = AlignedBuf::uninit(32, OUTPUT_ALIGN); + __native_sha256(bytes, len, aligned_out.ptr); + core::ptr::copy_nonoverlapping(aligned_out.ptr as *const u8, output, 32); + } else { + __native_sha256(bytes, len, output); + } + }; + } +} + +/// sha256 intrinsic binding +/// +/// # Safety +/// +/// The VM accepts the preimage by pointer and length, and writes the +/// 32-byte hash. +/// - `bytes` must point to an input buffer at least `len` long. +/// - `output` must point to a buffer that is at least 32-bytes long. +/// - `bytes` and `output` must be 4-byte aligned. +#[cfg(target_os = "zkvm")] +#[inline(always)] +fn __native_sha256(bytes: *const u8, len: usize, output: *mut u8) { openvm_platform::custom_insn_r!(opcode = OPCODE, funct3 = SHA256_FUNCT3, funct7 = SHA256_FUNCT7, rd = In output, rs1 = In bytes, rs2 = In len); } From 87e9e3f29b43ddb0cd02bf4d39c2081f50211b59 Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Wed, 21 May 2025 17:58:05 -0400 Subject: [PATCH 30/49] fix: remove serialized_modulus and align modulus constant (#1678) --- crates/circuits/mod-builder/src/core_chip.rs | 22 ++++++++--- crates/toolchain/platform/src/lib.rs | 4 +- .../algebra/circuit/src/modular_chip/is_eq.rs | 22 ++++++++--- extensions/algebra/moduli-macros/src/lib.rs | 39 +++---------------- .../ecc/circuit/src/weierstrass_extension.rs | 17 ++++---- extensions/sha256/guest/src/lib.rs | 3 +- 6 files changed, 52 insertions(+), 55 deletions(-) diff --git a/crates/circuits/mod-builder/src/core_chip.rs b/crates/circuits/mod-builder/src/core_chip.rs index d91991f52b..c85be82a5e 100644 --- a/crates/circuits/mod-builder/src/core_chip.rs +++ b/crates/circuits/mod-builder/src/core_chip.rs @@ -3,6 +3,7 @@ use num_bigint::BigUint; use num_traits::Zero; use openvm_circuit::{ arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, DynAdapterInterface, DynArray, MinimalInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, @@ -308,17 +309,28 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { - let data: DynArray<_> = self.adapter.read(state.memory, instruction).into(); + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { + let data: DynArray<_> = self.adapter.read(state, instruction).into(); let writes = run_field_expression(self, &data, instruction).0; - self.adapter - .write(state.memory, instruction, &writes.into()); + self.adapter.write(state, instruction, &writes.into()); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + todo!() + } } fn run_field_expression( diff --git a/crates/toolchain/platform/src/lib.rs b/crates/toolchain/platform/src/lib.rs index b6a9cfc88e..4d02cc3138 100644 --- a/crates/toolchain/platform/src/lib.rs +++ b/crates/toolchain/platform/src/lib.rs @@ -6,14 +6,14 @@ #[cfg(target_os = "zkvm")] pub use openvm_custom_insn::{custom_insn_i, custom_insn_r}; +#[cfg(target_os = "zkvm")] +pub mod alloc; #[cfg(all(feature = "export-getrandom", target_os = "zkvm"))] mod getrandom; #[cfg(all(feature = "rust-runtime", target_os = "zkvm"))] pub mod heap; #[cfg(all(feature = "export-libm", target_os = "zkvm"))] mod libm_extern; -#[cfg(target_os = "zkvm")] -pub mod alloc; pub mod memory; pub mod print; diff --git a/extensions/algebra/circuit/src/modular_chip/is_eq.rs b/extensions/algebra/circuit/src/modular_chip/is_eq.rs index 621ad69075..e774df93c0 100644 --- a/extensions/algebra/circuit/src/modular_chip/is_eq.rs +++ b/extensions/algebra/circuit/src/modular_chip/is_eq.rs @@ -7,6 +7,7 @@ use num_bigint::BigUint; use openvm_algebra_transpiler::Rv32ModularArithmeticOpcode; use openvm_circuit::{ arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, @@ -424,9 +425,12 @@ where { fn execute_e1( &mut self, - state: VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let Instruction { opcode, .. } = instruction; let local_opcode = @@ -436,7 +440,7 @@ where Rv32ModularArithmeticOpcode::IS_EQ | Rv32ModularArithmeticOpcode::SETUP_ISEQ ); - let [b, c] = self.adapter.read(state.memory, instruction).into(); + let [b, c] = self.adapter.read(state, instruction).into(); let (b_cmp, _) = run_unsigned_less_than::(&b, &self.modulus_limbs); let (c_cmp, _) = run_unsigned_less_than::(&c, &self.modulus_limbs); let is_setup = instruction.opcode.local_opcode_idx(self.offset) @@ -450,11 +454,19 @@ where let mut write_data = [0u8; WRITE_LIMBS]; write_data[0] = (b == c) as u8; - self.adapter - .write(state.memory, instruction, &write_data.into()); + self.adapter.write(state, instruction, &write_data.into()); Ok(()) } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + todo!() + } } // Returns (cmp_result, diff_idx) diff --git a/extensions/algebra/moduli-macros/src/lib.rs b/extensions/algebra/moduli-macros/src/lib.rs index bfd0f1362c..18931d05fb 100644 --- a/extensions/algebra/moduli-macros/src/lib.rs +++ b/extensions/algebra/moduli-macros/src/lib.rs @@ -734,7 +734,6 @@ pub fn moduli_init(input: TokenStream) -> TokenStream { let mut externs = Vec::new(); let mut setups = Vec::new(); - let mut openvm_section = Vec::new(); let mut setup_all_moduli = Vec::new(); // List of all modular limbs in one (that is, with a compile-time known size) array. @@ -780,28 +779,8 @@ pub fn moduli_init(input: TokenStream) -> TokenStream { .collect::>() .join(""); - let serialized_modulus = - core::iter::once(1) // 1 for "modulus" - .chain(core::iter::once(mod_idx as u8)) // mod_idx is u8 for now (can make it u32), because we don't know the order of - // variables in the elf - .chain((modulus_bytes.len() as u32).to_le_bytes().iter().copied()) - .chain(modulus_bytes.iter().copied()) - .collect::>(); - let serialized_name = syn::Ident::new( - &format!("OPENVM_SERIALIZED_MODULUS_{}", mod_idx), - span.into(), - ); - let serialized_len = serialized_modulus.len(); let setup_function = syn::Ident::new(&format!("setup_{}", mod_idx), span.into()); - openvm_section.push(quote::quote_spanned! { span.into() => - #[cfg(target_os = "zkvm")] - #[link_section = ".openvm"] - #[no_mangle] - #[used] - static #serialized_name: [u8; #serialized_len] = [#(#serialized_modulus),*]; - }); - for op_type in ["add", "sub", "mul", "div"] { let func_name = syn::Ident::new( &format!("{}_extern_func_{}", op_type, modulus_hex), @@ -855,19 +834,12 @@ pub fn moduli_init(input: TokenStream) -> TokenStream { pub fn #setup_function() { #[cfg(target_os = "zkvm")] { - let mut ptr = 0; - assert_eq!(#serialized_name[ptr], 1); - ptr += 1; - assert_eq!(#serialized_name[ptr], #mod_idx as u8); - ptr += 1; - assert_eq!(#serialized_name[ptr..ptr+4].iter().rev().fold(0, |acc, &x| acc * 256 + x as usize), #limbs); - ptr += 4; - let remaining = &#serialized_name[ptr..]; - // To avoid importing #struct_name, we create a placeholder struct with the same size and alignment. #[repr(C, align(#block_size))] struct AlignedPlaceholder([u8; #limbs]); + const MODULUS_BYTES: AlignedPlaceholder = AlignedPlaceholder([#(#modulus_bytes),*]); + // We are going to use the numeric representation of the `rs2` register to distinguish the chip to setup. // The transpiler will transform this instruction, based on whether `rs2` is `x0`, `x1` or `x2`, into a `SETUP_ADDSUB`, `SETUP_MULDIV` or `SETUP_ISEQ` instruction. let mut uninit: core::mem::MaybeUninit = core::mem::MaybeUninit::uninit(); @@ -878,7 +850,7 @@ pub fn moduli_init(input: TokenStream) -> TokenStream { + #mod_idx * (::openvm_algebra_guest::ModArithBaseFunct7::MODULAR_ARITHMETIC_MAX_KINDS as usize), rd = In uninit.as_mut_ptr(), - rs1 = In remaining.as_ptr(), + rs1 = In MODULUS_BYTES.0.as_ptr(), rs2 = Const "x0" // will be parsed as 0 and therefore transpiled to SETUP_ADDMOD ); openvm::platform::custom_insn_r!( @@ -888,7 +860,7 @@ pub fn moduli_init(input: TokenStream) -> TokenStream { + #mod_idx * (::openvm_algebra_guest::ModArithBaseFunct7::MODULAR_ARITHMETIC_MAX_KINDS as usize), rd = In uninit.as_mut_ptr(), - rs1 = In remaining.as_ptr(), + rs1 = In MODULUS_BYTES.0.as_ptr(), rs2 = Const "x1" // will be parsed as 1 and therefore transpiled to SETUP_MULDIV ); unsafe { @@ -901,7 +873,7 @@ pub fn moduli_init(input: TokenStream) -> TokenStream { + #mod_idx * (::openvm_algebra_guest::ModArithBaseFunct7::MODULAR_ARITHMETIC_MAX_KINDS as usize), rd = InOut tmp, - rs1 = In remaining.as_ptr(), + rs1 = In MODULUS_BYTES.0.as_ptr(), rs2 = Const "x2" // will be parsed as 2 and therefore transpiled to SETUP_ISEQ ); // rd = inout(reg) is necessary because this instruction will write to `rd` register @@ -914,7 +886,6 @@ pub fn moduli_init(input: TokenStream) -> TokenStream { let total_limbs_cnt = two_modular_limbs_flattened_list.len(); let cnt_limbs_list_len = limb_list_borders.len(); TokenStream::from(quote::quote_spanned! { span.into() => - #(#openvm_section)* #[cfg(target_os = "zkvm")] mod openvm_intrinsics_ffi { #(#externs)* diff --git a/extensions/ecc/circuit/src/weierstrass_extension.rs b/extensions/ecc/circuit/src/weierstrass_extension.rs index 0f0bd25189..1db3e5a9c6 100644 --- a/extensions/ecc/circuit/src/weierstrass_extension.rs +++ b/extensions/ecc/circuit/src/weierstrass_extension.rs @@ -237,7 +237,7 @@ pub(crate) mod phantom { use num_traits::{FromPrimitive, One}; use openvm_circuit::{ arch::{PhantomSubExecutor, Streams}, - system::memory::MemoryController, + system::memory::{online::GuestMemory, MemoryController}, }; use openvm_ecc_guest::weierstrass::DecompressionHint; use openvm_instructions::{riscv::RV32_MEMORY_AS, PhantomDiscriminant}; @@ -261,14 +261,14 @@ pub(crate) mod phantom { impl PhantomSubExecutor for DecompressHintSubEx { fn phantom_execute( &mut self, - memory: &MemoryController, + memory: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, - a: F, - b: F, + a: u32, + b: u32, c_upper: u16, ) -> eyre::Result<()> { - let c_idx = c_upper as usize; + /*let c_idx = c_upper as usize; if c_idx >= self.supported_curves.len() { bail!( "Curve index {c_idx} out of range: {} supported curves", @@ -312,6 +312,7 @@ pub(crate) mod phantom { ) .collect(); streams.hint_stream = hint_bytes; + */ Ok(()) } } @@ -443,11 +444,11 @@ pub(crate) mod phantom { impl PhantomSubExecutor for NonQrHintSubEx { fn phantom_execute( &mut self, - _: &MemoryController, + _: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, - _: F, - _: F, + _: u32, + _: u32, c_upper: u16, ) -> eyre::Result<()> { let c_idx = c_upper as usize; diff --git a/extensions/sha256/guest/src/lib.rs b/extensions/sha256/guest/src/lib.rs index de7bc6fcb9..5dea93b35a 100644 --- a/extensions/sha256/guest/src/lib.rs +++ b/extensions/sha256/guest/src/lib.rs @@ -36,7 +36,8 @@ extern "C" fn zkvm_sha256_impl(bytes: *const u8, len: usize, output: *mut u8) { const MIN_ALIGN: usize = 4; // The preferred alignment for the input buffer, since the input is read in chunks of 16 bytes const INPUT_ALIGN: usize = 16; - // The preferred alignment for the output buffer, since the output is written in chunks of 32 bytes + // The preferred alignment for the output buffer, since the output is written in chunks of 32 + // bytes const OUTPUT_ALIGN: usize = 32; unsafe { if bytes as usize % MIN_ALIGN != 0 { From cc1002c37b9d7278f08a3703bc29bbeb49af3ced Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Thu, 22 May 2025 00:58:52 -0400 Subject: [PATCH 31/49] feat(new-execution): port native chips (#1665) Resolves INT-3753 --- Cargo.lock | 6 + benchmarks/execute/Cargo.toml | 7 +- benchmarks/execute/benches/execute.rs | 70 +- benchmarks/execute/src/main.rs | 86 ++- .../elf/openvm-keccak256-program.elf | Bin 64932 -> 66640 bytes benchmarks/guest/keccak256/src/main.rs | 2 +- .../elf/openvm-keccak256-iter-program.elf | Bin 64904 -> 64904 bytes benchmarks/guest/keccak256_iter/src/main.rs | 2 +- .../sha256/elf/openvm-sha256-program.elf | Bin 64708 -> 64708 bytes benchmarks/guest/sha256/src/main.rs | 2 +- .../elf/openvm-sha256-iter-program.elf | Bin 64424 -> 64424 bytes benchmarks/guest/sha256_iter/src/main.rs | 2 +- crates/circuits/mod-builder/src/core_chip.rs | 5 +- crates/vm/derive/src/lib.rs | 11 +- crates/vm/src/arch/execution_mode/e1.rs | 2 +- .../arch/execution_mode/metered/bounded.rs | 54 +- .../src/arch/execution_mode/metered/exact.rs | 93 ++- .../vm/src/arch/execution_mode/metered/mod.rs | 17 +- crates/vm/src/arch/execution_mode/mod.rs | 2 +- crates/vm/src/arch/extensions.rs | 4 +- crates/vm/src/arch/integration_api.rs | 5 +- crates/vm/src/arch/vm.rs | 9 +- .../system/memory/offline_checker/bridge.rs | 2 +- .../system/memory/offline_checker/columns.rs | 15 +- crates/vm/src/system/memory/paged_vec.rs | 7 +- crates/vm/src/system/native_adapter/mod.rs | 11 +- crates/vm/src/system/phantom/mod.rs | 5 +- crates/vm/src/system/public_values/core.rs | 8 +- crates/vm/src/system/public_values/mod.rs | 6 +- .../algebra/circuit/src/modular_chip/is_eq.rs | 5 +- extensions/keccak256/circuit/src/lib.rs | 96 ++- extensions/native/circuit/Cargo.toml | 1 + .../src/adapters/alu_native_adapter.rs | 209 +++--- .../src/adapters/branch_native_adapter.rs | 192 +++--- .../circuit/src/adapters/convert_adapter.rs | 203 +++--- .../src/adapters/loadstore_native_adapter.rs | 382 +++++------ extensions/native/circuit/src/adapters/mod.rs | 140 +++- .../src/adapters/native_vectorized_adapter.rs | 221 +++---- .../native/circuit/src/branch_eq/core.rs | 168 +++++ .../native/circuit/src/branch_eq/mod.rs | 15 +- extensions/native/circuit/src/castf/core.rs | 225 +++---- extensions/native/circuit/src/castf/mod.rs | 6 +- extensions/native/circuit/src/castf/tests.rs | 82 +-- extensions/native/circuit/src/extension.rs | 322 +++++---- .../circuit/src/field_arithmetic/core.rs | 236 +++---- .../circuit/src/field_arithmetic/mod.rs | 7 +- .../circuit/src/field_arithmetic/tests.rs | 69 +- .../circuit/src/field_extension/core.rs | 224 +++---- .../native/circuit/src/field_extension/mod.rs | 9 +- .../circuit/src/field_extension/tests.rs | 37 +- extensions/native/circuit/src/fri/mod.rs | 611 +++++++++--------- extensions/native/circuit/src/fri/tests.rs | 59 +- extensions/native/circuit/src/jal/mod.rs | 350 +++++----- extensions/native/circuit/src/jal/tests.rs | 62 +- extensions/native/circuit/src/lib.rs | 6 +- .../native/circuit/src/loadstore/core.rs | 226 +++---- .../native/circuit/src/loadstore/mod.rs | 11 +- .../native/circuit/src/loadstore/tests.rs | 46 +- .../native/circuit/src/poseidon2/trace.rs | 2 +- extensions/rv32im/circuit/src/adapters/alu.rs | 8 +- extensions/rv32im/circuit/src/adapters/mod.rs | 47 +- extensions/rv32im/circuit/src/auipc/core.rs | 10 +- extensions/rv32im/circuit/src/auipc/mod.rs | 4 +- .../rv32im/circuit/src/base_alu/core.rs | 2 +- .../rv32im/circuit/src/branch_eq/core.rs | 2 +- .../rv32im/circuit/src/branch_eq/mod.rs | 6 +- .../rv32im/circuit/src/branch_lt/core.rs | 2 +- .../rv32im/circuit/src/branch_lt/mod.rs | 4 +- extensions/rv32im/circuit/src/divrem/core.rs | 2 +- .../rv32im/circuit/src/hintstore/mod.rs | 32 +- extensions/rv32im/circuit/src/jal_lui/core.rs | 10 +- extensions/rv32im/circuit/src/jal_lui/mod.rs | 4 +- extensions/rv32im/circuit/src/jalr/core.rs | 10 +- extensions/rv32im/circuit/src/jalr/mod.rs | 4 +- .../rv32im/circuit/src/less_than/core.rs | 2 +- .../circuit/src/load_sign_extend/core.rs | 2 +- .../rv32im/circuit/src/loadstore/core.rs | 2 +- .../rv32im/circuit/src/loadstore/tests.rs | 8 +- extensions/rv32im/circuit/src/mul/core.rs | 2 +- extensions/rv32im/circuit/src/mulh/core.rs | 2 +- extensions/rv32im/circuit/src/shift/core.rs | 2 +- .../sha256/circuit/src/sha256_chip/mod.rs | 89 ++- 82 files changed, 2528 insertions(+), 2383 deletions(-) create mode 100644 extensions/native/circuit/src/branch_eq/core.rs diff --git a/Cargo.lock b/Cargo.lock index 04361d85f4..e6472bfbc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4697,10 +4697,15 @@ dependencies = [ "openvm-bigint-circuit", "openvm-bigint-transpiler", "openvm-circuit", + "openvm-keccak256-circuit", + "openvm-keccak256-transpiler", "openvm-rv32im-circuit", "openvm-rv32im-transpiler", + "openvm-sha256-circuit", + "openvm-sha256-transpiler", "openvm-stark-sdk", "openvm-transpiler", + "serde", "tracing", "tracing-subscriber 0.3.19", ] @@ -5154,6 +5159,7 @@ dependencies = [ "openvm-native-compiler", "openvm-poseidon2-air", "openvm-rv32im-circuit", + "openvm-rv32im-transpiler", "openvm-stark-backend", "openvm-stark-sdk", "rand 0.8.5", diff --git a/benchmarks/execute/Cargo.toml b/benchmarks/execute/Cargo.toml index bcf726a6bb..bdcc539092 100644 --- a/benchmarks/execute/Cargo.toml +++ b/benchmarks/execute/Cargo.toml @@ -18,13 +18,16 @@ openvm-rv32im-circuit.workspace = true openvm-rv32im-transpiler.workspace = true openvm-bigint-circuit.workspace = true openvm-bigint-transpiler.workspace = true -# openvm-keccak256-circuit.workspace = true -# openvm-keccak256-transpiler.workspace = true +openvm-keccak256-circuit.workspace = true +openvm-keccak256-transpiler.workspace = true +openvm-sha256-circuit.workspace = true +openvm-sha256-transpiler.workspace = true clap = { version = "4.5.9", features = ["derive", "env"] } eyre.workspace = true tracing.workspace = true derive_more = { workspace = true, features = ["from"] } +serde = { workspace = true, features = ["derive"] } tracing-subscriber = { version = "0.3.17", features = ["std", "env-filter"] } diff --git a/benchmarks/execute/benches/execute.rs b/benchmarks/execute/benches/execute.rs index 6cb2a60cf3..06b99fe430 100644 --- a/benchmarks/execute/benches/execute.rs +++ b/benchmarks/execute/benches/execute.rs @@ -1,13 +1,30 @@ use eyre::Result; use openvm_benchmarks_utils::{get_elf_path, get_programs_dir, read_elf_file}; -use openvm_bigint_circuit::Int256Rv32Config; +use openvm_bigint_circuit::Int256; use openvm_bigint_transpiler::Int256TranspilerExtension; -use openvm_circuit::arch::{instructions::exe::VmExe, VmExecutor}; +use openvm_circuit::{ + arch::{instructions::exe::VmExe, SystemConfig, VmExecutor}, + derive::VmConfig, +}; +use openvm_keccak256_circuit::Keccak256; +use openvm_keccak256_transpiler::Keccak256TranspilerExtension; +use openvm_rv32im_circuit::{Rv32I, Rv32Io, Rv32M}; use openvm_rv32im_transpiler::{ Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, }; +use openvm_sha256_circuit::Sha256; +use openvm_sha256_transpiler::Sha256TranspilerExtension; use openvm_stark_sdk::p3_baby_bear::BabyBear; use openvm_transpiler::{transpiler::Transpiler, FromElf}; +use serde::{Deserialize, Serialize}; + +use openvm_bigint_circuit::{Int256Executor, Int256Periphery}; +use openvm_keccak256_circuit::{Keccak256Executor, Keccak256Periphery}; +use openvm_rv32im_circuit::{ + Rv32IExecutor, Rv32IPeriphery, Rv32IoExecutor, Rv32IoPeriphery, Rv32MExecutor, Rv32MPeriphery, +}; +use openvm_sha256_circuit::{Sha256Executor, Sha256Periphery}; +use openvm_stark_sdk::openvm_stark_backend::{self, p3_field::PrimeField32}; static AVAILABLE_PROGRAMS: &[&str] = &[ "fibonacci_recursive", @@ -16,14 +33,47 @@ static AVAILABLE_PROGRAMS: &[&str] = &[ "bubblesort", "factorial_iterative_u256", "revm_snailtracer", - // "pairing", - // "keccak256", - // "keccak256_iter", - // "sha256", - // "sha256_iter", + "keccak256", + "keccak256_iter", + "sha256", + "sha256_iter", // "revm_transfer", + // "pairing", ]; +// TODO(ayush): remove from here +#[derive(Clone, Debug, VmConfig, Serialize, Deserialize)] +pub struct ExecuteConfig { + #[system] + pub system: SystemConfig, + #[extension] + pub rv32i: Rv32I, + #[extension] + pub rv32m: Rv32M, + #[extension] + pub io: Rv32Io, + #[extension] + pub bigint: Int256, + #[extension] + pub keccak: Keccak256, + #[extension] + pub sha256: Sha256, +} + +impl Default for ExecuteConfig { + fn default() -> Self { + Self { + system: SystemConfig::default().with_continuations(), + rv32i: Rv32I::default(), + rv32m: Rv32M::default(), + io: Rv32Io::default(), + bigint: Int256::default(), + keccak: Keccak256::default(), + sha256: Sha256::default(), + } + } +} + fn main() { divan::main(); } @@ -34,13 +84,15 @@ fn run_program(program: &str) -> Result<()> { let elf_path = get_elf_path(&program_dir); let elf = read_elf_file(&elf_path)?; - let vm_config = Int256Rv32Config::default(); + let vm_config = ExecuteConfig::default(); let transpiler = Transpiler::::default() .with_extension(Rv32ITranspilerExtension) .with_extension(Rv32IoTranspilerExtension) .with_extension(Rv32MTranspilerExtension) - .with_extension(Int256TranspilerExtension); + .with_extension(Int256TranspilerExtension) + .with_extension(Keccak256TranspilerExtension) + .with_extension(Sha256TranspilerExtension); let exe = VmExe::from_elf(elf, transpiler)?; diff --git a/benchmarks/execute/src/main.rs b/benchmarks/execute/src/main.rs index cd97932f84..37426b0ab5 100644 --- a/benchmarks/execute/src/main.rs +++ b/benchmarks/execute/src/main.rs @@ -1,39 +1,49 @@ use clap::{Parser, ValueEnum}; use eyre::Result; use openvm_benchmarks_utils::{get_elf_path, get_programs_dir, read_elf_file}; -use openvm_bigint_circuit::Int256Rv32Config; +use openvm_bigint_circuit::{Int256, Int256Executor, Int256Periphery, Int256Rv32Config}; use openvm_bigint_transpiler::Int256TranspilerExtension; -use openvm_circuit::arch::{instructions::exe::VmExe, VirtualMachine, VmExecutor}; +use openvm_circuit::{ + arch::{instructions::exe::VmExe, SystemConfig, VirtualMachine, VmExecutor}, + derive::VmConfig, +}; +use openvm_keccak256_circuit::{ + Keccak256, Keccak256Executor, Keccak256Periphery, Keccak256Rv32Config, +}; +use openvm_keccak256_transpiler::Keccak256TranspilerExtension; +use openvm_rv32im_circuit::{ + Rv32I, Rv32IExecutor, Rv32IPeriphery, Rv32Io, Rv32IoExecutor, Rv32IoPeriphery, Rv32M, + Rv32MExecutor, Rv32MPeriphery, +}; use openvm_rv32im_transpiler::{ Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, }; +use openvm_sha256_circuit::{Sha256, Sha256Executor, Sha256Periphery}; +use openvm_sha256_transpiler::Sha256TranspilerExtension; use openvm_stark_sdk::{ - bench::run_with_metric_collection, config::baby_bear_poseidon2::default_engine, + bench::run_with_metric_collection, + config::baby_bear_poseidon2::default_engine, + openvm_stark_backend::{self, p3_field::PrimeField32}, p3_baby_bear::BabyBear, }; use openvm_transpiler::{transpiler::Transpiler, FromElf}; - -#[derive(Debug, Clone, ValueEnum)] -enum BuildProfile { - Debug, - Release, -} +use serde::{Deserialize, Serialize}; // const DEFAULT_APP_CONFIG_PATH: &str = "./openvm.toml"; static AVAILABLE_PROGRAMS: &[&str] = &[ - "fibonacci_recursive", - "fibonacci_iterative", - "quicksort", - "bubblesort", - "factorial_iterative_u256", - "revm_snailtracer", - // "pairing", - // "keccak256", + // "fibonacci_recursive", + // "fibonacci_iterative", + // "quicksort", + // "bubblesort", + // "factorial_iterative_u256", + // "revm_snailtracer", + "keccak256", // "keccak256_iter", // "sha256", // "sha256_iter", // "revm_transfer", + // "pairing", ]; #[derive(Parser)] @@ -60,6 +70,39 @@ struct Cli { verbose: bool, } +#[derive(Clone, Debug, VmConfig, Serialize, Deserialize)] +pub struct ExecuteConfig { + #[system] + pub system: SystemConfig, + #[extension] + pub rv32i: Rv32I, + #[extension] + pub rv32m: Rv32M, + #[extension] + pub io: Rv32Io, + #[extension] + pub bigint: Int256, + #[extension] + pub keccak: Keccak256, + #[extension] + pub sha256: Sha256, +} + +impl Default for ExecuteConfig { + // TODO(ayush): this should be auto-derived as vmconfig should have a with_continuations method + fn default() -> Self { + Self { + system: SystemConfig::default().with_continuations(), + rv32i: Rv32I::default(), + rv32m: Rv32M::default(), + io: Rv32Io::default(), + bigint: Int256::default(), + keccak: Keccak256::default(), + sha256: Sha256::default(), + } + } +} + fn main() -> Result<()> { let cli = Cli::parse(); @@ -118,13 +161,15 @@ fn main() -> Result<()> { // let config_path = program_dir.join(DEFAULT_APP_CONFIG_PATH); // let vm_config = read_config_toml_or_default(&config_path)?.app_vm_config; // let transpiler = vm_config.transpiler; - let vm_config = Int256Rv32Config::default(); + let vm_config = ExecuteConfig::default(); let transpiler = Transpiler::::default() .with_extension(Rv32ITranspilerExtension) .with_extension(Rv32MTranspilerExtension) .with_extension(Rv32IoTranspilerExtension) - .with_extension(Int256TranspilerExtension); + .with_extension(Int256TranspilerExtension) + .with_extension(Keccak256TranspilerExtension) + .with_extension(Sha256TranspilerExtension); let exe = VmExe::from_elf(elf, transpiler)?; @@ -149,7 +194,8 @@ fn main() -> Result<()> { let executor = VmExecutor::new(vm_config); executor - .execute_metered(exe.clone(), vec![], widths, interactions) + .execute_e1(exe.clone(), vec![]) + // .execute_metered(exe.clone(), vec![], widths, interactions) .expect("Failed to execute program"); tracing::info!("Completed program: {}", program); diff --git a/benchmarks/guest/keccak256/elf/openvm-keccak256-program.elf b/benchmarks/guest/keccak256/elf/openvm-keccak256-program.elf index 7425897f996e62a6472bdf52bec88d3273cb600d..3686d7042487ecaede815a38c9bf72e8015f7c19 100755 GIT binary patch delta 19637 zcma)^3w%_?^~dMl>~2B=7_uRR5b!3O7Xd=DyU8XPV38!CMMXqJMagEfAu5m%-Uw_E zlA7;j8Ngd=?qzXJI;H z^sy=W%r?d{w;A}{`*MXoXz4R&8xt$;&)H{lFrVGQxIL9Q2c)tG;qK%1_WePdJ(mw) z%$0jDbLBnCTm>5QEi;(YcJNR9zNE=oK7TXGt@q?~Ck?n&({}Jp$+>BVPB`IaAKX3Y zCpZ2J`TpeGi#z`4v~OU}M8+O8Oz(9YjRVZ7Y1)Ez#)df=TflyEWZw#d@yAw37mf*iX$Fw26*);!VC;S8l+B2N?0Vreub2^|yq&{wEh0ZU%ESO{;=(IDP@>xFH z4?z}Al@9xrmcxy~!S>XY+S}m>nW&N-Hq)`?vJ5qA(HP&A}|2qlK|#v%q9~ zQom2L5$!JpnLRnayvtA;pm~68u zL9Mpic#C&<%CH18yz93dyd6Apx^Fo;*xvD;)fJuZ`c{Gyr+v)rdy)4U8I2E|hzyls z^>R<2#VFrz_Q1LBq?8o8Lx+?d>8vTfqH8 zeCa_~?xVU@f!eRE5%)U3JAEkMF(eV0yM~OOd^#G%&|I8?R$7L=!kyz>%w7BSz6T7p zvEWdS(}8||z`^!`d1u`io|-o0!l!x-8g%>6Qr=P9hi*yOQl25WBg$LT3epbeI1`aI z!^V8~p|-Y~F}y2nimPO|5zMsd2&hfe)6AZN>=q;YK<7D4<4Nfk4v1R8OHARV=_Ptc z4zEuiHb`ycU_NuG!FQ(rI8K@hY2oNhnlK`ohSeTNuCo)9!Y|`ycFFa>O*)g7@vn@CNzY1B2ISS2=Vt*0s$maVqT{(G9`xIf_{z<#o5SB6@x7rlIKsglSTho3ZQ6gu@5#yJGqOi$ zS9dpO|53ZKHwW;h#Id`uYNz6Tyt#rqdSIqw|iA&-v`UpOqh{4&YcKEGPaEB6B@A`xk3lCl)@J zxhT#P-O#{G^G9gG?iu+%(zMU{$%1M4e`(>$A;+EeWNWQ4Yt(+4R!V-X@Dcs?Lf%@G z5$?zjHg~X%`L!$|-;Tw=Z?{@xFQ5o*+*MoPY5D~Vj&kTU%o6e)g&Xth3Z3?$j8_b0 z-5qbSZqjddWVwoV7r2U_DAzbhx4G%`?%fC%zo?b z$m8#gD@pFi+qZ&Ng3fh3vt)wak0-JkfzAJA^zV9LgHs#f0 zu7W_mtEepBReWXszAY$xOUfPHd&WPi4eO-H*L@#5gW;rQ!=OpxpVQq~)}bYu%1+*W z+8wY9c$m+vyu|wL*ulFhU(V>wH~VGluDoEnBWYg;ymxf`th>D`UrRK>-o^J!Xuw3| zsTYm5L>-TD|3w}B>Z2E3(l5`h-kp1Yj?>-PxPP2~P%T|O z&eJDu5ZRyL+b0Tn?UVfI#OF!wwA-KNcTW0wMn`e*pgq~?=qOGos6#D>;*|7@Vm^Cv z%eZ5-lR51g1~+!Jd^1yus{p?(2m8!fX#Vc_4L?1(Fh}H=LMJWVS8L{|i{kwpErOXU0TgX18QTd0u?q##LYt7EiFQeUuFzVO7&3q2l?r}Wl_mpFet{ob;0VM3U>j}Gh zwFK88F-q=FaXQ{(3s?djThG%j?6gO8^T-mS?&{cRcAeR&y@UA7IUa2=-!#WJ+}!aw zHda^OGgzNwC)K9&H|I?IkJ_Q!d(EYKPa$u=W@%0awhv~7x;DdA@+gWkXeHMySw{v> zpX(YIa2b!+epz zHct=t;3|mDh`4%TL-CP37{4#TXGUOk%kpKlZEY>MBoc|Ppje8*f`Nq#LyboLtkBZs z3ztopuzc~#wm@sq=z61Nu@P8o)FWHHu_CZ=d1(CAOG9l-#|Ks~U)nrAu&jsTKRza zDf-bRJY)V3(`L6BYzw?opoZfE@r$<<7qg`X%LWd^N5>}{{OmA;4a9F7KKn-->>d}Y zTgWr&uS=c|^&E(idrke;wEe(#;E(WeQ6sl^lZa`ofrf zUJ-2bz>ul(Sl#30eXx5r*I+e;d{l6aK63^CVK6KCIQClVoZ~C_i@{Iy&`SPz;}7+( zSMs`0@yJc940Ficg>Q=BkMK?5dKljtLfy>23_10lRXn+A>y6V_TTyu%2I=^m^2F-8 zzrtY6K7(zqG}z24gYBg6OoR1h8L+ex)neWy2HR6(uz9fAJ{5gbi{JRnn8v?qniVcu zW3bC%rn)xZ`US5UwmA7i2rZ=~e5ea%3^nlo7#B@OH722o_|C*92|j2XlC1%`zuI7p z6A?1VFK$^?-MD%&KfE9(tq*k=4x6F3TCo^*D?huSF0B+uqeqIXZ{@R^EA-8`@;jO* z4eEim9NJ;{*7#k`nf%k{5%#3D1{-gV*mXBeOa3Lu2OIxd{=1v*&hEs53w zqh^T>H2%oK4EOhKxD*&c(M^Oy`^pykxQa>U4oEP*b%! z@lA_ZvSBO?H7%Q9;J37?5w=~zM%t2*xtPKBP3Uy~=;8|pe6cwirM|_vZ}HPw+KEM0 z_i1SLWLO_v#5-F@&7#{!nJ^;p0}vBiNoPL*%Y~HmzY#3;hfFsy``?03EaG3a-l%78 z<8zjj=qESu&Lz2Fin)#=?$z-FE3TC^b|3f@3Z#{r3V0Emt>_Pcldxcp5&FM_&nWE} zn!wmH=xJD*?E;fOE9vZFFzmzqf?5d3p){BdmMbzjXcH{;%fVED<@quWsw~0Ab23Q3@d5u*uU`` z;PkL!@Gb;B7)LaVsl`e7L3_?oFm=&If~CVr;4h$;UDzU6>f6AZ5Ft5+wu5OOllmWn z!`op{1PK-R3Iy4T!E0cf7}%sg1NJNWbKpirUx1zAh@u}2-mB2f=;_s6gBZFj?w1gQ-H2w}7bvlDC4FfvX{){Lc%P`WL`dA;~Y<(f<^3DR>zM zR|}#(?*+$FB$_-PG#D!qTCVFFTMMQ!a|UAKBZ@tZk&hK7f1kv0B>HJs5m1J72fARJ z90NAi06vHcdQkwi=uYq%Fby=~=ZKY%KM!tCH=w1Yu@}Ib6n+Uz{;Z_4@XO>t5%hwo z0#>rIBj8wauvgAbR0*bV-+U*OB5#i z8F3tmUZF7AFI1T9TVhP>ZzS3l6JYg=MDJFZGHi?ENc4V%$^PdGll?E^I1=5jFxkJ= z&*u8?V8{Cf6t+n89fc{w8HK5Ub8#GrexoqiXW^V64{bCJM}phMA&%GpA1AiXCyj;16(RA-C7(z_ukZ{tNg{g$E{M9T;Yqxhekz;IA?5 zt@5#uq(N|2SqFxKvr&MRbe0LuQ+Nb;ufn6i)Br2lSPocT`p`l(L$LIB1^BI2BW#|{ z$zY8o5Ds*<4ooeS8QQ^LfCoW9_D=|w`lrAY0m;vR)4|IjAo~-7rT$&;ol9_pl!CuP z5K9jBVH`)IHc&_081ydg{6r4T9iYN^gP1o6c@Yi94 z99?V*egXl7G*z%R)#?&Ma1EHc;C8{^2df3V@^5@nX#fpM8_sfcp=c$IC4$3rB_jpN z5ELyn%*zf6SvmN0TWnC(gWpql3-};HY_l81wjDx3|b3P}BhV0jfy{w@_P^_LCA_@@?0!AuxX zizQzLmerSNJe^uEMW?Hz^!`4FbCAmky7C4=VfyIF=mj?KsBXP-YFP?J%NQ z@DiBD)E-<95T60l7)Zh>CoXhb5ik#>x*lfD5KxQdw0I0mEta$36EM|S>c@Dj7EAp? zFjZLUp9Iq&J0>dhDVQQ6?MpCUY0Moo^Tw$GDq-fu)J$R`m%B> zq{Twt2A20tNdLb|glO7*36>Z1G)m5Zai0Xak-`w1gP?JF?3`YJB`$B9Xc6|qtzf#@ zk_~&h4tPhf=>Ys^ux*rKA^0!gqq4=wkun&Uw$Qgy7eIfDV9J2;j|f088x=4^1d|_t z>5k4rFrW|zg^WZxbrrUZCDVzYN3Ry{K4zReJMD0gNg+Lm3!Bip1 zJ}^Z{@&qtN$VxUg5gbbn=8xk@bXB$01zs4@Abb%_U64)fC&5%D1NGT_Fz$N^pBifb z?^QUY*wcBSIgTSyccqn|h64Ft1E&1sk@yyAkMhkE&-bMOOb+je<4E)_g{gv%DNOcz z;y4n0T4Az(RbjIKonR^-DH45MF`x{ef{!UJJ`LWSV~{bD#{LPWJA78s*;n8*3V#ib zB?s$^<4E)%A_D(mwjWj$k|9-A0b@0b7lO&5oVQPbLiQDq3G9xYZUzkaHXPu0!;Tdx1&Uw zMYTgLmKB%|?h7jpz6Sx_uaX9rgXxB-r*xv)D z2Hq$9+p01DsD%#+gL0R##XKOCi*BJKF%J3VB6Do&u9YbhWv6d7kj4KNj8B^!HNWUv_9tFV`R0vt;Y_HGC_`hK6I>c&8~BvMiQv;{&;aD1 z2D!mIRvC}zjw8_@OtT^)r`cb@6frps^QL2(ZIfMUW6QzR zWjo-M2KCRu)McwM{!BsjC00mrThE-wPlBmS+63RORDj0BE^wpAfR>WRx)eQauTR8r zBziF_NcNjyO%-@b>Z$$Y@D&K?$&k5p!r`!?7L$7_APY=SdGbKCncN}RRDbW z0IX$LEcG@pJq?na2&N}b;YM1Ekzs)lNQ0Zfm8(TaVR0Xr9#@%5HS9kZEbYGp)048j z5Rkux!z`Bi7BD?U^NR9LTTJbzgaZWx^oT79L=IMB$s@KB77{Q$dfOxPUkaA`Ghli= zH&EzXhFdK4ZD4vdC;dNevazSlbKo#NE|h}jA)v>Kl3xIKt%*7K7)*~GWr&+GT8X9o zEns>aNd;0DekNGzKL^vJOUYl-`I}lK1!rKe9|hvejDTw-uGJL29DH1{zY=^Jda4jb zY%TZ$zhSw9Bn)o7H5QS_!If)c5q@hV#$Tplz(%10VA%y@z zelT^ZT)ZEb@ny{@(DO)&5r9KFz zF=i&(|C{hzK4hm1_X@Twu=Ijy&`Ev}Kh4MQ5Rm=Lf~CF}Oru=#5&6>;;1Q0(VY>F) zRcEbyGhkp|2V81x7Uoik-D>dV)}S*-Jubbj1j{!j=u+%=ibLAWJ^|B1cq?h_Q~a#2 ziScRtY_%BWqYeLrfL@)jlE%KmPkMbq^4IuDuTn_v!_QOeW7DuO2d7+xXMuax#isE( zuwT)4foDu3e^fpy@B{?2r;~sZ_Gd7?&tWBvJp*o5X2G*yzY^koV0s6_N;>-`_zQ)9 z4enF82RslJw8}>x{sw~0BrucC4uQ?rC&1Ll5y4Xb1(;eW`Cnjq4?^-;Ftu3nd9Zx* zg8UWbT5PtTR_iekPzYs)5-^2Wav7N3(U9x~)B72cec&R6CxA;8o(QHFHl)2Dyd!Lg z@=<}Q5OgU9b>RI9UjmLLJamfVNc4X$w)#}wXpGia6*8A{4ALpJR)pms-Uyyoivmt- z)~KJ2S$g)G_5$8LYOJ|EigjXZ$IoxD`L|AS};XS+Mfl}AYCK! zSI@ErsZa2FaF|BnY9Z)>K)y^v70|D;`aE0cYrxd!=5`BxCz$$lj?f82?gnK*#J~$8jXuqc9clk-}vEw>XYOPvy$- zhY~46noE>zemU6CIF3X!6(;*h3R3}7;y4nmQJCxl;FC&&8p9BLAo~=vU@O@8UaZCU zf=?-YA9&}CnEpZVG2|bD3e&tkAlR%w_#l}4OFj&aC0zf%5+{g6d#|=?SP3f{)x*%a zRDu11UsLRu+fFDQHtd|Kgez)5o~ zdyIcvEaqcg3jwhW{A{~)sIx@y$(1oq2FH?v4T@vDf^m&yZ-WsdlY+fOvG^{YKMQaKPr~`r~4W0rvj^# z4CbH#r_PNPc&(zR0_Mdro);)g`PYIgmHc7w^N7G;w1B4RW zOJR!0)o~n&&Q+M~mxFgG5m^QPpdI`F7*vx&@>2-*D+as4JqkYtKBDj*aLtC;8SoF_ zqH7I!qolDvf@g1t>FtGPfujNw1|Vd6n+5wfx!_V_$?R^TM4rO+zB240qGM)TP*bsFf~B(0B{#r z+Is~{y$?(clssWH;{PTDGGrkb5L-!S&0uP=Tpg=v=D8^@98 zePKmF8J+<@q=fugMNba)#c?G1ONA-_9~370KgMw+`X`0SK73XYP=@ny9Eq~xSbHhM zNQKGXh~r4qr7+oFrZCyhjAOk2caF{B7Zj!f z9#)v_cf@fd`cs9;{xyZk{+PlT|B>h$ihwelSC}%evHi`*54>QgFxih&nCwgAI1=?J zO!ha#nASfBtB(oH0)h%thPxCd`+MRz66Fe${jU`!`<^()803jc|-T}s?G_~InY=?kOsj`M2fa#b#7i%-sI0dn$-OzwNaW$CUW*;b+{#2QE z$7;a`!E|Pn6(}sj+K&cNO2tw1d^tTy#s1$40XcluXFXh&UGNz^(Q7sEW9suK;=Z3@ zu$h7b;IVK>T|^=80@GVCvO=f9^cIZdHIr~PJuX(>N$}BdiNV^1!^bBxHc<)r3Giev zZBA6chf}N*lI)`5ixx}^@HiWP@`E5S_=--g@RX9pceuA{$-M$|vD)9ED7=QExBToqG zzXyjC4fcYlz=vRZK(R@118PPOD4GSI1k(cr*}{g)&_X|^sTeb-!Ff}y+wIiF=fHG7 zLE7`n!`6L1+2SAJmI>YGvzaxvF&$UKbg8vTI4lFJvtTQjw(C@(--T;Ix|E|oUZDd2 z45n+pY*FDs*IL&Fl5ZXg0i9~=g+ULv=vw~3PuHb3g(|(3fy$7_*H9H`YHZ@Jos&{M zm7ywMmD}rU@RqtuO9On#&dXEXfre19ssV=XKxr`4#DBlDI9yfk^E6bH1w0j19(Sp? zw3g+qVkI*Ii(3|#_}rzT%F1AQpuEyo8uXQVJmpOlO$}uYZeLjya+P@gYfJZka2x(s zzojg%@xOLmM#k!YRps8$5?`}#aj>~;;gV2Ab4$gNHSXn2Zn!MPC|adh^MCCcKEdOw zEN^T;W8FS?$Xi)CF1TQMNhzCu-Q^Wk^YdoR&I`7*g-S~BN2V>ol9GkyAEK5ZMyp%6 zcCI8`=0SWKy`^O#UsZ#nFEF9nB{adqmVp;MUGnx4Zw2MkSZVWYV$XiiS-W03| zl<{f11F4>d#!3XWsj0Lw)KFDf#(UmzrI)&cWfkQWflyUNC{R{jNfF}*-f{AlKMx9r z8Y_KnZ&{VQ+*{!dggk0Ey*^J_W3aNayuw}4Sn6r?g}e<-zJ>t8+E9j}ju9CP=XV<8 z70v%|9sZBL-H25G-1tuC9*oQX-rDOEAuYMi>f!J7Use(81krEbm4`3rnx0xwRaWJ# z^fWXyxxL;BUx>fdH6`o`mHXV~fnZh8*Mt=zP>XRX2SbIgvaAWYf|cHeKv`A5>kd{l zmIbOx8_R=%Km%Ts{ckPJa3d)6U)+@K4nKCsDE|IoSNG^6|I)Qp{NYzd>*b|<)TSLl>^DuY*`5Du>el0o3A^Gynhq delta 18061 zcma)^4R}<=)yL=F>~2B;U9w3C5b%;sLJT2nvdM0Od?XM8A}UHiR9+;XAfMqoAh59z zM5TZo6ht126%a8+=%OISf=KvK-Bw%eC)y*&k)DSjILQruWPh##_v#Y1))V#=4@TQ`p-(JC?u3h=XyRJK8%0x>hon zymf-7CVZ;B#&0C_PkP5y46Bs4KH!|6NkCeUeNsuptTQw+v@K7d|h%9 z-|C7*<(;luhcL6{wH@(o8MX*_j*}60?0)|<23wI?e%a-)v+8FYY(JQ+=-wA;meu1G zEtcop-qU3Uu_+dv)kJ4M}0|^%^d&Az~>)k1Qbgv#-T5xKwkF-g{uW&~)bN}jt zE7hYN`u?X47IWNwg${~pr{Enem-(s|BnIquw_I}nYzG30S!%MIY3stlNXg+xT{p8| zk-uXyP;aa8jGlBkSk(JLty|7U52Ye|Atpo9R%DcFR=#dJIDy8h$ME_(@5 zXa~}nvDxg1&YPMJ^NP%u3b)e)@Fp9xoE%9JmHTmXejUuU%E7w2%_C}}n1=q`)2D~# z36AdboTeS-7qdp-ujkf9UADXI@oU! z7L#E|M@sB8I^;`;BD7+AOUPUI8msQ*&?(JgGFDw(k#YR0%bvuzFNp=4K43x8uXc2I zr|-Dx&V2nU*|~Few3*+zubbaE+`fy+bbJ>){sHrrw=i$rhpf80BR+=P`)=u&ZNKWy zvcswUJKo&4*u4{ucD;TzCZmJqPCv!Iu-UJQBq$a!Im+X5#_KSjoRh0JUE%9<(h`ne zam6~Bd*zo`s#iL6m*e;qek>WtH>h7Gk zbSsL-2k;5~c4ycA`NbGy67s_R@`xnsS&~k=*a@FH_Law*- zX?L1*S3Zz$%6&*1$bZP~s}17m{Rild5TD$?d)=zge#B_Sj^iQk6vUxdtnSCE8I$?g z)fJg1u2S}Ke=-ZMy3T^6UmF|ePCtRzp1VqR?wk{C=6CM7wiVllYv%9t6K&A9ecfos z+6><<_BQ@m|J-PgM1|1|v#s+J=O8>ye!cpbJsw-k^_a|ZWJ?m(-q)`4iqhHr z_Rvn|vTI0g>}VNgrElGt_}ygwUTGeM=I^HUysflPZ&9O^wrSlz+_cIxYnV+oE$2l; zN9m61eC1F~`E}m(m$2E|p`mVVHUDhr6l@{c(tpz$UNP)`3Mx)*yjP5VM2iWwjvgPQv)#e0F-vTENnLPTMPDuH4(#TvYYeTN z!#>!L0Aq(g&o7Kyr@b4TKYojr7_-B!$82xWV%*1>rGNT7A3dR)Yn&PKN;CYZ=d!na z#2Y4fwg2VK6a3xGU7KTbbf@pd8Y2Te{Ez&`gc1Kz`#GO@|0umAoA11TUhglk1u!dI z!@qFnzJMkST8%YJ*7OA*_<*}#soQwLMqxw-+5Bdwno7_q8yeT#nR{*SEXd6c+P~s! z9!R^36~SDCLtU7gE^x*ts@s1})lHX~ic7dLU1q~&j&wQvBERuKzu>bEdUanGPDAND z3#X4P2_MNf;rGq>$YP$hb}?8M&Tg+c`Q}N}^bOOwH`sWHQF~YQ+Jx1>M&NV!-pl8kDt7-;!ojAu}MUYY%*z9}Y`(lqso7;Ng#6Q(wr+biXJ^!}0^Z3zo4Yn9|+2~{!d{54Z z*r#X0HVq7!Dv$I%T7Cm|`wWAXW$?S|mgvSj{!U%@_?`0%^O|DkJbtC_8@+Ho|7gl) z{nUKEaB6nX3M?x$v;T!}ir{p7Q@Cd0TSKU)@{EUF`uYWYz{6`NrC=p#gz`gt67ji| z7wPMP0)tI0G}soO!N&LvwvE1%4R#~NfTdL^MJA3gSo2VWO@z&sQq0jX{KjYWaGp8s z-oOuN;x3q}t_`?8ly8PDO1=r9r4oma4WH321Ail&H3;1ph%Vwg1)n(hpgbg71M-pq z2CKnVwh!{5vl|B1ES|-`pVm9!^diH|q%ZMJvG{rs&z@eMz#6R4N^z$K{F3qGkVz1L)zcW*k8{W5&y(uY`?H^F6Muq@r3^IV*bQTpMB~Q zHLEu*F|*phcB*DI#K`L0(-5b^LsxvLQ1>oEcC0iq6G|2!vvX6HTA95G%`AlZ^jPaS zR;xr3jh~sFtZ!Y)znXnr-?)tbUwvKoPnQ`i0Tn~|PQ$lth84!pGCpZee(42qbPri> znB5wWZ_226!zq;A_r+m0e_z zh0XJt;;=8cLYabbU}qP@st@}K;2F?oK|&3#0H-SYRp7JG(*a2Oz2Fi>zYkmx5cxp{ zpF(g_G58$ZsOURmXw)IA=*$VG4oG%^H-JkZAb*1eOMNMrIwZLa94J5oQZNF7GVnkM z$iZB}QojI99g^GtrjSd11Uya<&G~Y0q=dp>;?EolQfSYWcwhJ#OnDbHwtv+ZdA2PNclVbGXh zKutws3&1NCZV(PFz0MvH4ka%KQwOYKV~>I(#lcoaaVY#)KoQUgwkb>_ekqDW;av)o z{RxH1{+%dBL=-0bZxkl`z~v}GDBPwnRp{)9^q(p?qc{|HDNObrg~{F<#i6iIVb}-4 zV-*2a7$3!<@cjx?0}B-<`^8Zl3NKZd?4MPb?6*X5AQb+kBA^O~6s8J?qc{{ksxaAq zsxaAq9>t;XmkN`;j_s(u1W>`Q3SbbYFjdGS56;!wD+!el>EVY0s?ibLVM6b?`Y z+8`sv!DdBqC_G1DvR|t(HSkmvhr$~aCi}e#ll{I3)A}C@?~e#@)P}Lj*}lx05q?1#0VU;| zV7i{NipGwB>3m=E+u$E?&1H5F5xoLF0+u<{EuOJG3YUR51Q0@5VHO05iovts$i;-l zwt#KjBMtr%d{*IKfz1m>^oZv8v|y`)I{R<%24%{A4-RZm1Rp>^9gr2ygXP60IgG(o zF|pJ;z%(Ms9l$hV$(_LRs+8*c1xx*aj>vzyOzi{-8PvjnSQ<?6QBP9< zeLB`qVykGZ4>%niFe3zud%<+yV-pO>eve>j9|RBVWmp)1!RrvLkpl1=;7tl20#k>q zqO-$bdDDfCnr{S4{bew9NOBvPA|&}cFhz(8^54d;Ll7wr_G1)>!o3DqQ$VNDl!P^4 zngTPV;2VlPO9dz1n70*iw7aYE2LA^?ZN=W$;Fx0lRyff_V?mIDJ? z;&jEH8Z1$m{0)oZP`F%Svaf1qY9AG9+Xcj-@KlAV!b*k7{;?hW0!=uVndG8Z;Ls zBxU@~IL;M)XKj}?w0CXbrQ;H-cmcnSi9q}|{sm`0c-8t@fcBg88YW`n!HbYpar(05Zh zNK;w}9+PE2OGRTv;FZ~iNyN8-0~gmyfsGA@z_U)Wot1(OB_d_uO$v_yM~Z`uisDfC zbFbyk10zkhvp!frs6%vbi6T}}fcz(iRFAUqX&6w4FhH=4eFMG(wl*g`kHNY?9cmEv zcPb6ih#v-b>T5ttMPoC-&9F}~37ZjkjiP_niTsmyi)kr+8wSKy(b;j~K=LVYq&V2= zD8|JqMob-Ogb|JKzZE@o@LTXTIY6wu{Q_Mu$4ZYp;2AlFXhUOO@M(p8#ONTtFg~4C z;>Rc834+&vsR66l*g9cvvCe)W>?H@Dh9FWLY*Q5DQtURyuFD={O70$Lb#O8&(FkwC zdHgYSK%PH5r_`qr?+1_WXNV>=c91x?odfJm2rfkg>OE{mmuj$rfY1O%k_36}a|Fhxl6 zKyaiu*pMj3t=HkuA|iB@1?Vt40s)203@Hvny&Nkc4CG-q7EE)t6%}aC*Mn)w8pLtj zeS{TK8}xJ-KMAHOnJajx(gDheN#L4116p(c(^#cqK-+6g6o$iq!1OF69Ym*O!xAkP`>)2zA;^J&*#Ha<3O4J5-vrY`6Uj%w z^nj*@mSX6QB#Wiq4UStFNzyyP^iaoKs$u_%V6p#eY&Qh-Kxq#=ki(2*i>1RXFg^dO3+jOu&3S}gTmFg>7Z7W&NS zB^#&~EcJEZ8pZx$@Xpf6lq>`9QS^9e|7 zdf8xKaH7a!y4Sk~JaUoML9*WuP90!a=4jy{xMWad3S8a61KRh0jU_`c1`g1!*`ZSK zRIpR<3@|-Bvx>%MgXy7~SaNr0Gs7F+x5P}PX4a*-SMc|T# zNJOf^af>4%UJJg6`Z8i~fLFtwa)kU{0H>Em{Cy2R3B8PHLQmxX78uBoEb57@RVpk2 zQxaK4W6Qwn7l{2I4ZI}S((7y&m<~J1zXsDIT*-UDn-mWAME*~1-yH1hbr>Kk+bg^Q zrX;kA&JKa)16vy5*Mg<~8!#oIW(n$UhOmiyv z2AJklau`fgC|U1?^8+xF-JF7cf~DRAmXC930D%XEKpIQ}e+>t;Igr8Qf~9^9nC4va zIu0gn(TEPXPCWfGTvm#bT*X1yhnqz6C5F zMU(v~!BT%Gn37KN7_j+}8Ul@B|9?aXq``79Wx3=>!I6SzI8ltvr_5UUM#G3UCvBv) zSC}g$_|hF#(wSM0-LXFmDJdIauLU4@OmRrN*^A&~r3TbgG`0ggd8lC$@ypd=5lem@KjjMw|{suqkO$N!A@sr+S5c|K*+VJ}a#o#+IqhXmvXV>wQ-fOUm&VIzt zdWCO*8x#(MS1YXdwrW~`@ytmu#UCXUZYZ(lRNiR(WT@34b1g@b-ZIPzvCQI1FeOzR zZP&>1d>jQ?m}AS{#@+){k`9D-(oe)WA?1klzZ6FWk~(1Rb};x5hXrMEy{ORfPAiMi zFVn#QFdY?J1Yagb4#*Dnzst%I*}*wrieL)9=%h3Vrie-VzknN-B2#1q|J_!|WQFr! z%F-pmLH99?$@f{v;Y2Vc;bNhG9xPuuA^m4ynsaP=W(U&lvF5y!;2LmB06iKn1Un#5 zM>y=>XpU3nkk*O5;GHdB@LPbyYi=r6M|4S>Z3MbJH9X7Wq2IO!8 zxK$a!gWyXFPXd?SX^39pS`xfwj94cysXGN*2DtPBXWb+1vH$Icpn0qm;7SO5Na20p zHih?t<0>Nd2f--{zX@KsOzL%Z1bkqAgx?0Y7kd7GToqu%N#m>vHds+aCVVOkWNj3}%2z4Mu@F~R~Mer-I2YRdE(JQ!2F!CQ~C`tvIqd!M+DE!4ZtHNo`YVhZ1 zkQ_+9s@R)3lP=mazmPLgi~&YCfGam74Ft~dhNz{nLh$1l@lBA>VL2I`qv)%_&CtgQ z{aRsfIndct;Em8H2>l_!Qh!*~Z{PoqLJ%nq))K{`@JWTK!(S>)5xW@0xX-6B*~eu> zypw%L$=3QC3g09Ju%iln!41lYbHV#CqQ1~mMD7Q#SM(FXn-qQsyj9^UaN=^Y*`fZ= z!R$UmuD`ga13|iC@LMo7Xce9P4lLh`qz?Q^u+(1y%l9Qo-!;=>sqYS^4oL0^#(S04 z_>or3fPgw64cy>Jaj?uN4ux-3m?BmVo{SM$MPs*v8x|@mjqUh{aun&5x=qv|J{v`JYUkFT(2=XD2 z@03yl|0P&fm<*Qhnv(uW!BW2#OdXW`6j;7@O7@2YOZ{PRfJPt%MP+4$w98d)L>o|hr$I4ll?sk zlYK=Lhr;(|1=?FP54-tM1`f6;ibLTg3R45y6s87ViekL3r!d){RhaD01)>C@@J9+$ z1>3EW4pN2KC=P|=6(;)vg~{F@#i8&3g~>ipqX?+Nlqkl7Mun-u28GFfV-$zNn-wPe zV+xb~i73YY|BfP{3fB~-3g1U@DEwE2$v!3{L{id zKgT++r=^+v)q%~2PN+b<8T>t0DrjyVK*es}lrC>UTbczn_1=D6^2>u*Q?~Zp8JSZ0tfPNsYKjdH<1ayvu=L#mj z2cEIc08t0*>_xBj}}X=Fk*zS3 zph$L=m;(;9k2qLro$<&Je+8!fx=hSr+E6e;E+g~+F+yG-I`kr#&Jjlo-aHJELj>iB z-vg&890sSZ!~WlHFv5C5AsZY!5_5ONfShwFx5JQ^dGvM@6!+hFJ{0A^y&rAC`7%JUn zlOtYyHyTt%_}RVImCj1x?`B+#s!2Knd@t?CPGPVDmtu4&H8RHPz#%YQdZmgUe>)x_ zMTjMjyw5txoGkRugV}w&*Un{5e~qW0IzKPZmzP&pQ(NQXuk0M`EUfcYSNV!O)is{l zx~hB+|8eJC&dTDN;%blAS5=f(TUVQ(&+mIVJ5b~Cc#5j4y}rWY>gpO_&2X09z;Z`d z&YC?dx31Dx>&Y)Ht|{`=6#6Tx3krO7o`Rz4%G%oMx|%$HuJ=E-%>Nf|1OL>oho#s2 z$FB3pSpBbP!IW7Gi)S^=TsW(~u%>b`Mo{nbO|2=0OAoTBL9yn6UETaul|^;cRmEO^ zt*^2uFF&te^^^s<=z}zzU0*wE;mq9pngVZeCE{7>&#$cVczAMiWtX}-f4;BQTUg+) z^AzRR`iJxS&(oa6`MzRbVYS!msVgeX_Z9Gi&FO){+UnZ8LSJo>w>Gb;sGvvZsPZ7xk$L&I#(9PR#5(XVediba6Z3y*?M1-b zQ{>-vrNA$uo%?{5RR6X{o-Z;#A`$;x9q!vb(piLv`Fws~fw#(ESXo=i*X}M2R9DsI z*H%_iiWk)7=jRW{yb7mP{yNN3VXeO=Ki^yADaiBI`sykR>o5y>UcaX@x8OgvG{a5l zA~Qy&WALt5pXkN+o-u+yz4yJ&=e^_N4XwR-#rsqE9tb=A-e&JSvv%gZ+W8D$)%A;c z%=xEy%DFV>(Em3!l-T0xd z^@=zDk1h}54O7>~zk?mymK$u_tc__gTAIWBzq!oXIEsGLTSu+4HqMRFGIe}!rtg;7 z8;{~UyKOesWo&=(SX{1SrK0uPi4T@*hJ~uVY<+QKf=$az`SC@@uF^^*3YKTH{6GDm BM#}&I diff --git a/benchmarks/guest/keccak256/src/main.rs b/benchmarks/guest/keccak256/src/main.rs index ee7ec8b09a..1d88ab2432 100644 --- a/benchmarks/guest/keccak256/src/main.rs +++ b/benchmarks/guest/keccak256/src/main.rs @@ -3,7 +3,7 @@ use openvm as _; use openvm_keccak256_guest::keccak256; -const INPUT_LENGTH_BYTES: usize = 100 * 1024; // 100 KB +const INPUT_LENGTH_BYTES: usize = 384 * 1024; pub fn main() { let mut input = Vec::with_capacity(INPUT_LENGTH_BYTES); diff --git a/benchmarks/guest/keccak256_iter/elf/openvm-keccak256-iter-program.elf b/benchmarks/guest/keccak256_iter/elf/openvm-keccak256-iter-program.elf index 0cf372eec35c5580260f823355f0701e6e9257b7..390c94590718b0f0e352d2ef60811983a2af6832 100755 GIT binary patch delta 14025 zcmb8#3v^Z0nFsKFFUb{#hyf8$P!9?cdBmIhjzkCu8UZmNAX=7s^Aa^YM8Kfbiz8}O zC}?zsj!~%(L@O-{Dq5Ujq$+kYf{GTET3V56eQ_$j3VP=MKYMS^cUN+K#+P~%O3&WNgsaB#%bq>x>2 zig1x~z1i??(ZSU}@IrXy3db~Aq!x+#N)=*iaj3xc3UEWgR_LVuRm9ohdU&tsaD#{| z6;n@#?0U}%v;J?T3n=&i1)qopKNJaU@MB>%@GZQz#11f`4#7c6OnJcm(jeY78g@7M z8xt?E>TxKTFw0RzSR|YibtYWW-BBGd_ryYD5JyBUf_EX_4`Tc=V?PY9gG-wapJVk~ z_yF>4{2jOq@iMGG(xa*vn@9Bs5<0^xkx+_a#*^>>5zoN;MLZ8bF5(Sv6((fsuYfrq zeMHrrFbBZ?aYWVKD5yn2HN+0L7&FTFR+t0QM^wE8a{$(_zzQ8!;UCsNU~J=G!yJ(H zUtzAe?jIGtL%~!N<~jGz#bLYN{vmpge1o!RG@`zSxd%#c&gf1&cjn+8IZ2r94Gh`! z%8dQ_v%+~OxC@sF%Om*vUi_yPBTV^@s+ZxVFgF@!=)$|jk{^H@j&=~`fdRrZh5rh( zJ$+!pFxvC`M;UfRu^xa3fcAE6=wc7 z!puJi+x{^W*ZW>1utHz#!{CUi6GC>q{=&>3A+KL`{-?t1V1LN2_k}R?OOFWF zlley%LItkZOPCds!mN-9+4U-gnLibd_jF7z5j7od5dIOoS-5a93Ra2h{ua3G7)Mp( zLg&4GtFdk|qF#dM&JJ#^F1%8>UuUKI%n9OW!!vsY0~!b46x;_2>Rc4u92BTpc&+gH z@Ot5Ca1}b#M^s%1a{~G(QZwKQh#!Lj-l%Rd*72yi4bCIp&5swN>M2uT6E?w|q4lre zCGe3bV1xUNZTuscD`5Qzyc%AB0_OMbqHW{H!CWE#C?Mf@6a+_1IU&2=eYn?i3FAoO zy?)Rc`W*AtOwWROj;9+x5Az&18>i`iQD2@O0q|U=F~>FM{{Lx1)f2;3i`mZ-qG_bN`E~+fl$7+k~IM%V54@ zv4LleZG1Dl7V#1j|C6ze{~6|rT7L>}5#A5)g3bLes=h!${HmbBS8z`FTewE}5WG#; zgLer>yJE?Oi{YO0g7!PZ{pLCL{u@m;! z<|f-=2J{-t6)83C^{CLB?$;(h8J=FiCRu0_+EBoAGR=4=%q3r9d`v0sk&FqTl| zjC+a|;$;_y7x!}ze|trRok(as!BMM_!G})*_gvamNW;#F zfphR&;orc?1vkSjDBxAqd?fZC3v9w1Byd31^I)!!^#YhHq>m!i0tZJ- zT^F+J&8XC;z}~7KfO!i16~(5!z91^_oc#yfDrRsme3$TQk(^Fe~g8X8xZ-cD+9fGe6cXSRdwh3E3@pC8B^8%HWM+ z#zWv{hvVlFQRQ&S)s8Rjk_xz0I0XksOl3oMy|q{ow!aBU(I`Ibe~AKiFqFF#=cG2H z8?@K$oiICGgbKWIoGu#VfTzK$`a6j7h^PzU`6oHP=o#?(lLKD@?;mK)&%bqS03F!N za2Z^3N>E`18xZkl;W8253~v+heef8>{W~MtUviYT9#KTy2=6iVvHv6LW)yTD6m)ni z++G%V8O#CdBdS)w9FX;$Fb80LHyj)>wJK!S+ldwBitXm}k4t_;RxeqVIR&S~T(W9& zFP{Z-$?RowAIuf9-VSp`ta(f149hSIwm%={1n)BKe+YAetN8q5!aZVyJg3jVsZ$*@ ziHLd*o;f7&^YGdg)GN%VRJ{WG2l~emQysRzx9d4c-C!J! zM2l4;%mJm$irfjaLAy!cfH@#c5cx%_40oKzVf}C_Qg6Z>P>Uars_mvfzkkm7V-)Nc zGyDv0MhAUZ0hd`H^fY&<1M!HczVJ-M{SFZyYpml@RRgoW^>{cqVrrtcoAF zWvbQl)YEprI3>$r4)9Xc;RGjC;Tw7I-AQLwGd2WPzjbGVITA7V{T6iZwXFRd5lEiTmCP z7rqoTdI<$H(SSa%OW`eIMLOWcBEB7Fhc^Fp_*U4zQ=-G~jBQ-?&;zjU)C1RFPmIWp zxEu+zO{jp6hr6MGGnip)Vf^oC9(zoiG<*Y zss9ccU%igeD`GFR(PQs zZ=Gmxva#PjJQe-~@d0cAe$m**x51@J(*fdDJ+*CoB)kf7>(TIPxB*Lu_6q6&Q(zMw zhPUCA*j>6EE=u!Wtp_md7;T#$huaZfZ{m*_+xTPfgbePSxp!{dD71F$~^S37Z)f;FTi&n2Fo=*TFlnNx6wJ|031xSfx6* zm|cqoD`7R<(fLTLf(MKUPQl%@h_8Z6TY_iBR=5ndr|>hlrYdO9g^SMc&HH~u9Y8^= zF7W*|oNEahIO#Y%8xSu+luP)aF^D6g*1(4lXNQar?yK#G;bCwICSVUpwaSn*5;2C(^NC)D9_XSuj3RdI;#$o@yfc#Q)v=;UI zuzxl%90fHZp$YDcJ)n<>x*Tpr+%D-|@DjxBihOHq^AEw?L)IQ#yD<3VbL#Qh{`|RU zLs7uXP9ITq8oWz50Z+%Kvhg(Rth6pxIhdQ$#)re)gw`YAP4Gk%aKe{soBmPIi~?># zTVW2&U8;{FH4o+{(??V-fVs)6TVU=P>+4{iQtRtso>J=@;3X}1{@a43C|HJsV^P2n z{?gdSpM)Pq+&*l6XKdqp;71VWWyShkPtdmUZZOZeb$5760R?swRk1)MjD$PjBx$z|rco+(pe~+<^-v@J#S^o?!)S@7b5?1Iy0c{hu!`yV%ufwb0(@?8!>rLnHdORJUfVnA` zAfHdmE}}s`%?842#jY=d$DHXPn@2y6$KSYP-7fs09t`vU&U8Cv7jKus6M zlh~iJjpOX9J&50q0=8FgY~lsggaR(1DNvW=zg%MLX8d=V@Emxp@H}{v@B(;?FfXWG z!W?V-$N0-X@csYw_F(QuA+g(7tx-r2ZItI#X2iudr2$UR~^_9~Bj ziRT(G=jDVBs*N|pyeis_=bWqe2>bKD|9=7n+$45{U%*_#K1kqYmY%4W%+BZd~O2k#T>9JG(CtPJm;*pm_Uxhc>&5%m{%<2m@tKkyxq{jcws`X*%8 zJ1EQ!`uFz}&0jG!AY|7YWR3n6E1YW!{3M8P67RHgHwQt~Xzp_3sd7{#_yC3lG}%&k7q+uvjeFGocDG^<2oVw-qbI{B3B5 zSJCq#&i3AcH;M`T4&EWW2QHLNbWoiKK1`xu?j&=gfCrzfZR5k>lF5!H;&FK8l)y=N zwQvUBDO?GEB3yMc=6^^Oj6}ki>urNkH5zW=4c$NRB@-MRF;x>XmUN2FFG3Pmu*&GCoF0X_qtgBf@8hZ|LYgi9|7;vc}xh}+A) zP&zW>nGsVVg)wB9^$=` zzybZ<*yjHc?u8ER_l!s4?Upw8Uwj@%0Y|70Tm^7WR2X4w%A?^{I7(W|82;wcSxA|r$sk*?-5WlY{%nH+l*};V&<43K+%)eQf z`L~Abddqy9{#oJaP(n;S8?x(d7H0lFVRrCQ$gcN^F!OtzYUbwu6H~|1cK&<=>L(Id z;VfZxa8AgsH$j;B3x%1#C}h`LEX@2haL_+0JSq}6pvQ$-;Vofy@OH?qw_BL`vBBXP zcL~|`N(PJmSs@cjh^fkuU9U=*`Axzc(B&cHS2l&2|8K&~|LI^cf6lmFB(TCu!mRL0 z$gbA`w|8|^H742vx1QHv{vvYR_-*FHc!c=z_hJ4bbF1;E1r+d?$vc~Z?^$7ZP@xo$ zE&iIVt#CZdU$ZST`9op;n(a2@;XlYf5B7hCgclGF@J7Z(e}zX-z+aC?9Pjc)YCC*~ z@b|F&W%5#F6%R!sKZ)X?d&7Lk;~Nw^xCG`)=5#z9=$m1FZm{v^V7@c*X^MD3eU1V? zd>qq&GYrpy?)tMH8@LYU!>G!{*TQ@y>uKDzT&bbvi^x*E>HQ(0e^5Es#8;)TLPLTT ze4|qT(uzJsc>Z@eUHQLMQfn%Vg&)ESaEZAh%V7SJNr{Q?g!xM*))x=QHylG9|Km3Y z+yV0kk4sGcPcZVQ#ENc#pMm-G>|Q_zuZ+-dBz6yU8>wGNtS7;8#%{HK!?8;`V3dA5 zkHJ~t04|03QLe)b;339w3havQrf~|eFsi@`14rwZ#0jQCzcXl)E%cp)#z`}OUwW>Fn@k9AcE_k1K5HB{(xY!8Nk?cFoQE3wbb|p zm_HwwXA<&UXM>wHJx;OBVc~?>SqQ#YnpydxBd;xcg#yW zn*^uLeejl}>QYUKOs27^K9y+5Rwmk79~x1b&!p-b8*-_JWHw%z%%s}hdT3&4zOu0? zl}|M`CK_|~@%pB=)WfGWRAv(Sd^VrUB~ulthE&s7HK2um$|vM!&zW5wPgmwE(sl8M zRDHZD*ObXLHD>GU5_S1>x}qYPZ7Pqe0S)F)e%`h7F2A}ZJu9BAXkK)6#iBWN>54@a zGp}uX`Qf42WHy&>s7u5f6WK&#Dwi8pf7t@oMsI!tbLKYAUO1~fnMlOrsm4?$lWwZd zl$*X)YKHuHP%(;D%vi2_vF%eqP`-N&g2^_GmRKyuI<$|0}7aQHj%2R zOr%qpRK79kPshw9l}%PO)aP>POd`_|Pc~#5Q*}+*x_o^yU00EcC(4sz;{PzYBKIC z_4#~VdFr3eOt-Cgxz~nEAFVGd&DLjvk{F4b@4sQFexy2g}9c+p8 PoPPnX4|Cw=;UE781h&0d delta 14026 zcmb8$36xaTnFsJ&MNura(kx1=ppVsNYpScNtEvU0WwSxV2H7-vq*objpiw|{FxBH= zXax~%;$Q|MiKrMEf`W?c;79}wOge616eA`?)VS~r8W+sV|Nrj2yzhCElgv9F;CH|8 zuJ693s@s-t+O>SsZrd+9br_&j_gMRd+xK;fU6s!$9336y;IF8PsJt=%Qt-|A(-HBv zjTvS95dN${{PK3Xey@l&ql}*ouRqE0MfZijyv9*Qel(g_lTCrnh^mX>;E1bfA-moT z;h1v01@J-9!J_Z@dU(wpj%hNcZWQ&E%E#4Bp#s-yhU@aSLOb>EBF+vUfe(ugcZj%B zarH#VuJ@EM>mQKLqu@Ohd?XtDKqRoikA&I4*YM#2JHUwg77kM4JP+z;>B2hq(@a8n@4pE5<0BIRNXIVTBGy;xE?!+}OsyggGGV zZ(y#t?jIHYf`S<)%yS;$xj1pSo;yVEkuOmejYia$F!w+K^5~ez=N{=N%=QL`?0SQY z{rR&(6$;kkGGRHIQ$NN(sxZQ|@2J`fFNe9&IK$82gJQ`)hwHjKi1NSy;km-!z-&(+ zm@tg?{QgmfZPZ>5Kqtgh3=WRC>JYN)br$9X&JpH}2Z!u>!-SbX$+ztv6)qA9iYp?_ z3iE{7K~u=Ccbzcv*9bGeHDuSjo3{P4!uC)?Ts

pdyV{5OQz!T*KqdIyD>|CKQF zkHfZq48`?4k-!SQa4!jtxH>yz*Xt|H{L#YfU`)uaH(r?eHF4QLD%6Vv4rs10E8HT? z4q8HXy_Leu-z?1hheLL~t#L7bR`_KoA+G*6WY_z(F!MhVW(RJ_u6InB`CU5%>&g5c z`A~uD^%Q1>v@k1VLw3D$g_%DSPV{h0FA;SqTqk@v{ETq^N))UU*L@3Ie5#`=aG~?w zzS~&07*TuRruo6G^)q;laPN*v6)gzjmGIotf&pC!-xAyh@@fhSeh?I>sqkju>F_q; zS@1}7sE??+4CVy%5mQ&dlMz1^1-wzU80&act%R!)KgEydqv~-}U=yB%IYaAb;AZ$_ z6tKY~#y0*T%oVWy7~TjkMgjASI%(VZnJ`z#Kk`U83kAUuSA9Zuy?b%5=MpB6#Cv`5 zD1DB3Yo_PJJjb(*pM!ah8;v99=@rGeR3wVWJIqtSTX~)ZD^S2C^&gk;d!zL!aZH6l z75Wt1YTO9R4u1X}_ZJi3rmTS1i9IkDPWN=snt%OA)C3glY;u%8LafL_co}S;-|OK< z;XQB_meA(Aa2ss%dv#Xo(CK~$=mRgI@cU;3?hEfcllHIwh#G)`Vl;4$pN^MMV-QDF zO@rB?KB8&{JX?4+%mLW=_uxbDN)&Jp{J_}8Z-qG_bN`E~RVd($ZNeS!DwwZWY~U$l z8-EtwjCg^Gzi({ge}uWB)}O$;gkAUmZ0>(ibqocGg+YZs!#Uxv;Y#6e;l0A@Bs3`8 z4t`%a4)?e=XulKO>srU&f1|1Z1=B@B7dSZLs$0mom6qsDwH`^l3O2#qR9o>3@#Y1SD*;_-1Ha0iG_I2x0l6g6mZ6cCgE2wM{MJT2|Z&Qp9*upHhve(P1a@x z^a{)sDKzbMFV&lFw~1c@&(33$EHMddQNVLD)A)6mOWtgJT2e3RVB=XZmQdx4PZcY~ z%dQk&*2_VZM?|IJZNgbNe+&)yOM*>SgM#&Um>J<6@MU-(D%kh@p4e@)J|gN2_@MxF=(nnN{fg9naDBx8!?_}&h7TAO)Byd31*TGyN>m@K(NFOnE0~{Q2wJc=U zyP{m50(+~z59TTGR}`BvpBEK)&i)f_5i|HPyjJ)pBA@RAn?lC>Lz%9Rh4gp*l`!i& z#t({o_LqM_6tKY;Lw3EFgjwMa!pwg^WY_znF!MW{608sNJBRG%y^}=&D-42nh#3!s z8y$|HM??*W3$Al~ahD|F7U42DIN~Z7vg0 z-L}H)@CH=ijbnspkOQ6tuj}I=$|IsKgBSI6e9>3H+xi8b10Nk=%+J5IYychD%kXx% zU|>*T4I2>gr{H1{e-_>=;z!_di2HX&wEu5iwDpK0YB_w!)W`mhs1+#acuvsaZSeZy zz_-I3pgy8%4a@;qx56BN_1$oA#MM0^yWZBV!#bv(hmW3cd$k)rBvxb(JbRJNkE)kp|3LpZ;%dJw@a=kiQo6we9ErA9 z4KN3kHY?H!vq8H_UxPUyOc41oH3)Z{EwFw##nc-x2ej0WM^&5Y&+nfz{s;v}#SA}% z8__`#E8sFa9X&M-bs!!Q)eD}BxZfe-6O45{swTp$Z#@|fj<~v5ThE`L_0}cz0Gg4- zQ!-6d-~i{tJM0YcaM}hR5(C-}R}FJa9})F9yiNE?cohcJ6&ak+VR*x$yyL$%v!Dwi z>_8te)eU9^eMHr%@CM-`wshi%t20A(y>26vdfW~er(`wE0nR}kPVl0U_y#182K70@ z=K#?FXM8T4C~-^|5jBb)9=HN-6CMjUFLo4OhW#1NXZ{jLu?8o24;+Irao@Y){O4ju zdr&YJ4d?^A6y7CPWIw!2#M@wYX!Bo%Z-xCkB|7|zv5iN%=>b^By5aikff3me4@UxR z6OwRm_!Jay23Hu{_#Ai$;T z)$2IDBK9&HJ6^Atzod9J+yd{#ik-OZV4kv#Xq4x?`viT;mYU0WLmmZO(ijr>a5?1y zrFcrNHU5Pd0QbbZaAnd#lt)Az7IEH3j)d%b_hG=yZ$UN(@S%vay?neoMuY_a)`cdMvyVuEP?dy}Y{56xf6Z;Jr8{ zc9*unu`=GP^#Dpv)wcPi@Os3znfPX78-Ey{oWY$_cc_M*rmf?6{trh%b5@@-&TzZ2 zZQyaZ1@SGWgVLVbHlBvlImiDf#`+H%+xS+v0&(sM#@{pc<9z;qfPzXS=p&*&f>(_R zP6_Y9Yel>rJokEsU!S9PRF%L7%3(9ZW_XQ=KWyT*{jKn8*reRVn14)lIbEraOU4UT_NTrbYZ7xNvFktk?|~!}b(@3RjK{+WQQSjq=U=e?)zbf)-ui z`%5^tG-#mT8F)4zUVtc+S zDBxwMkElw(2ZU4bY-}nU&%n+a>-MS~=BBjqkuW!*^=Nn}JOu@u@YUL;e-z9^0XLzo z&;)ar>LaGEgSpA{5mifIZZhi|VD1^~WiU^vbspv^wZ0i{UW(_xEw}{*tB`Oy3OK@T z#x}kkz8`V>u=%aAjlT;&h&V4R)-O0q+s3=VJm=Qk;9YqX*iAHw1tOsW-Up{pgAHC| zY#W#lbCX#wgp1%|C}942V;jF0<{q>DDV(oDK^aO|VLu9Jo6rVx(^dY4Q#K=? zPs`4tK|aj}!kfje9|VsZ>mZv)L=Aq4 zaHH^Qxawbn>-ZzZf+yW* z^P}nuc)9Rb@G9Zs@Ot6Ahk_!EP#;kh#XpPTb5Ou@Jj~d}6EJ6J%}4ue;S9_f>m#bl z@y|o>N))iYI%5;htE*7JB{T)tV?JMbpyUCjm=zZ91bH*u5kYFH%yV8~d<5n>ooPI1YIue( ze#a{vB$|WwiLDOW$5mF0_fPD}h&pNtbWTM53EnXgfA|N!BeMVW9amq4?0UzA*+Jhv zexms!t_Fncdd1f0U$MdzTi{oTtEnNo-gIH+-zd!Y{+mL^FR=(Se*?TvOlTv#2&XLX zpHr;C%P5FVa?FS$YCqg2+y=K?7{p(N=b(YEsKM*{TVvfoRH?qY1M7BhaKu$yoAc*^ zd-o-JggJ=2dMV5S>@@Bq8sLlu3UdVph3tAmg_%E5nE8`^+y0Sou}ENrMtGx`@d6QN z1J{P^dW(fw|1M$X|1e~H;X&K}S>Z7hEW?uO122`K3UT#x$gZ~=E5!W0sKBe}IT2@j zZ^1jn1bz#*3BL>Hi>KHQ@nI4LO;gQ{0%vtdKW#lAyhg(X(}Mg`c+K>{X?UY>7Jg0m zT=*m5QT;IgouT5=I@K>_6u>xl3tt_ z75q&j>~EToF+*X0&%noKI{xrDz@6|!%=i?4xKZ_cxNuewe-Ca%++OziuKjfd-9l9L zfM+40n`vOQv5k*`*?{$UI5^^}GGy1gP?!UrD~t)|)it34e9tG$3M+-VBCA7oy*q`O zzZJe*%=i&_5e7U2qvVb1Ablc^&%f`WVA+X+i25D8T=)<0YiNL*h7A-Bz|V+8ygM!8 z=fUhyA5nEad@pQQw9(j)^XLByQ1A#6aP|2e{K(igumR=(tT)2D8Ux=4a{xB}02~}~ z^-##Jw?#P5C3^`4<7PXiPDJg4>x6#=Zxnt7{sJ9FQNzFY!^eb+2jUkD;SMH#3GCoC zi&q)*zYmXt9sl`{f`?GR2J{hCTVQrz{d1TNT0aVJ6f5u;>>=J03GCpIvCaQId>T5m z-!qO!M*ZWqO`R5e-b20s+!eCusW{BU{6J~{3!tCI(kny8dVdk$8X8vsP5n*=lVaTrcu`u&{4l;A||B9s=(w z{Og68e`Cn5cat#lAB2PcQQ>DIfdl$4VOIE!FgtiNWY>FJnE4$BhiBY5WY;@+u;`x^ zvY~{yIyYq38zs#AtAshAt3$@GYzi~~KZKe8qrqbSobd*czzTbWS>ff7U2i|U{v=0L zV4~e{>v;v{FCr(5-()_FM~ELk4D%P6TZ})+qkz9m-q93ztS};|&=rp@{+g|=a2Cv8 zvyGYjVK9HqcCYcs@8nm({?Cx`0>S||hSem(1CCIM6F#er~Ywr(wP`@@a~AUL8dNA3lz0pwBQo3%csh zdTd}B%!koP6MqQiD_IZYf)b^Mu3>EssJ;4KQvaZ`31on;`Gd!1lYa+B{+L+N z=iw({K0UkU(ZS2-={FL)2fB>bFC^B}U^(N@D)bwUUD5&P>&NproE6Tv8sXZpnpJN2k-{W?}oWT zU2#exmHONB0yDrY%%30h=l^cP0X&Za{(#^aGk^&bF@sT#T5kLT%%2a;HJ&(0`QPW; z6?g&W*Y6c((@nh)dvuKcwwvu=4)f!@%|8b7Teiu(|1jbB6#X@uJ?FL4(7`kez%+Ol zEOxE>6z1c&(8S}@@m`NlQkCs=d!u1~^y*~>Jb$KsO}G9f%y-N=9Zf=?R`Y7BNb@E^>z!})D=bfD$}?rPsZ^pNSC-7ATlYWEKVMc~TVGn1sn4bAOKWoJ zoIf2imsBoOo@>a|q>`ybHdmIeFH4u!B$A1mOlhV*QIjZ1iHZNi&jhFKpIn{RiJL16b6BxVCYwp8YO-b3_0_FE*?j(0 zwKWaN`s(ULuC_MakW41W8wtKjUrO#m#-+D?4@b^W9B0#W@JcQ1r&7SZ>GOPSswxH`dtN&h@M3l}1#G zGFfX%v;xigfpm394l|XaTn^qrb9iv=lEMhqSjpaI0pm^_Y+nCZ?dRke2|OCIBw3%e zmRLiI&KZj~DV;b|C>$he6QH%Xwe~cJnJ}0MbbqQg(gb`ety_ctS7PQ-EE>1?>IX7rJZH!K@%v}4g|*+FTiqN5<- zg)rLob9j|Z6bTj0O`7KSrz_2$4_8NT-Vd*@*aA)iTLl4>^G*Nz<zZSY2tqvY_ Pp_GFzarE%pi(7vI(_IuF delta 975 zcma))ziZP$7{(I|QYSNX(WzijMEr7h@4fHcEd_^)gW%vMBHWMn0%@A+P>DLke?d+M z=UAds#ejc7M?oANbai#Kn^z3BLqozH-12#!_jsP?@Mz`mXyv0{J^#AhGH=?`?&|IK zx%wVl@NYYcKx_0dsgPr$T-L{%9f=x9Q4(Zy%sQ6A_})dCkP{)-qeV#N%lO(T8Inn8 zb`hL4DQ&ejhOOqVAM6gAh!PX(Xjyq@p%OVu&Po9qhvXVqnpAUS`)BHk)<#-dS{}ov zFZO$VxhLYF_dGq@@lp2N_OQ>}viy8^-GJ~C60nxZM5o-%*xhUD!r@F~_c0Hi?ldZC zaGrDUM)@E_+1&S?oQ)y|)Fun7*p!3)D{D@XBa8qdTU4Zv9V{KKtB4Z)yiUp`N6bz{hiy%#n{U5l!=YP2)aU~)|g*`!)v zE3^cr>@n9ABpBfvy)=pC_oqwc&xgy?SMMiRJ5eVFp*UJ04U#A?zpqUne*M+%WB^77 PQaB8Cv7Fxj_TeV~0$tiTV-fM#~dt0bh<4X)4 zx=%lDUo$p=W2Ys!qQHKlyu1GM=61`Xvo0IVQYco5nvf%q zu`T{%{z5SD8R+eExhGWp60xT7uWz64{sur6=+XcH delta 818 zcmb7CyK0p|5Iz`{ls{-C7K%X-^=EfxXD^MCB48mtfZgow?D2YHierpTsyx9~X%oq% zQx4eMq*5%b1S=oGCarto0WA^|VajjjyUpI=+}`2b`-Az5gTbZq7k*AY&+l(`kiB*k zwRVw|4^TI@?sXX`d4j|oF(-=k+t#Db8Vp{c5l*@2NL6p&$&w%&WM>LH36hou3vZ^= zKjhK!sE1S#G-VM1V(~17k}ZLwa@L9M)nFmd#;gq~kNNEyKY9B6MIJuGl@&W)MX8^* zZ#h(~F{2orT?!EHrr~(H-Pn!}EWeV`>zDmhO<*aJ9W)YEufOB26bDw2j4R56xccGy zjmrUSu#^y0X;!V7Dz8_5Tw5neqREO!(a{@!WNGFHy$!zDh=9s@GD1d21u9x?MMc%) zKju#bYdQwKe&TH~$G}fSqy8VXakCuz$A6kTUA+BU-5JxVi#rcG-x9OdwQq{9y#&|I zokdBpfG{hck|_YN)T*5wjjpt&NL&U$uZ%{qg{ia_G&;q-IV*B6@${doH~e-7jv diff --git a/benchmarks/guest/sha256_iter/src/main.rs b/benchmarks/guest/sha256_iter/src/main.rs index 7d0d23dd7f..fe2acc265b 100644 --- a/benchmarks/guest/sha256_iter/src/main.rs +++ b/benchmarks/guest/sha256_iter/src/main.rs @@ -3,7 +3,7 @@ use openvm as _; use openvm_sha256_guest::sha256; -const ITERATIONS: usize = 20_000; +const ITERATIONS: usize = 150_000; pub fn main() { // Initialize with hash of an empty vector diff --git a/crates/circuits/mod-builder/src/core_chip.rs b/crates/circuits/mod-builder/src/core_chip.rs index c85be82a5e..4e9bbf3b44 100644 --- a/crates/circuits/mod-builder/src/core_chip.rs +++ b/crates/circuits/mod-builder/src/core_chip.rs @@ -329,7 +329,10 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - todo!() + self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; + + Ok(()) } } diff --git a/crates/vm/derive/src/lib.rs b/crates/vm/derive/src/lib.rs index 08a0952ecd..a7913893df 100644 --- a/crates/vm/derive/src/lib.rs +++ b/crates/vm/derive/src/lib.rs @@ -148,7 +148,6 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { instruction: &::openvm_circuit::arch::instructions::instruction::Instruction, ) -> ::openvm_circuit::arch::Result<()> where - F: ::openvm_stark_backend::p3_field::PrimeField32, Ctx: ::openvm_circuit::arch::execution_mode::E1E2ExecutionCtx, { self.0.execute_e1(state, instruction) @@ -160,9 +159,7 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { instruction: &::openvm_circuit::arch::instructions::instruction::Instruction, chip_index: usize, ) -> ::openvm_circuit::arch::Result<()> - where - F: ::openvm_stark_backend::p3_field::PrimeField32, - { + where { self.0.execute_metered(state, instruction, chip_index) } } @@ -214,7 +211,6 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { instruction: &::openvm_circuit::arch::instructions::instruction::Instruction<#first_ty_generic>, ) -> ::openvm_circuit::arch::Result<()> where - #first_ty_generic: ::openvm_stark_backend::p3_field::PrimeField32, Ctx: ::openvm_circuit::arch::execution_mode::E1E2ExecutionCtx, { match self { @@ -227,10 +223,7 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { state: &mut ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, ::openvm_circuit::arch::execution_mode::metered::MeteredCtx>, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction<#first_ty_generic>, chip_index: usize, - ) -> ::openvm_circuit::arch::Result<()> - where - #first_ty_generic: ::openvm_stark_backend::p3_field::PrimeField32 - { + ) -> ::openvm_circuit::arch::Result<()> { match self { #(#execute_metered_arms,)* } diff --git a/crates/vm/src/arch/execution_mode/e1.rs b/crates/vm/src/arch/execution_mode/e1.rs index 0721e2158d..15592c81b2 100644 --- a/crates/vm/src/arch/execution_mode/e1.rs +++ b/crates/vm/src/arch/execution_mode/e1.rs @@ -9,7 +9,7 @@ use crate::arch::{ pub type E1Ctx = (); impl E1E2ExecutionCtx for E1Ctx { - fn on_memory_operation(&mut self, _address_space: u32, _ptr: u32, _size: usize) {} + fn on_memory_operation(&mut self, _address_space: u32, _ptr: u32, _size: u32) {} } /// Implementation of the ExecutionControl trait using the old segmentation strategy diff --git a/crates/vm/src/arch/execution_mode/metered/bounded.rs b/crates/vm/src/arch/execution_mode/metered/bounded.rs index 52b8609b03..0a933a5493 100644 --- a/crates/vm/src/arch/execution_mode/metered/bounded.rs +++ b/crates/vm/src/arch/execution_mode/metered/bounded.rs @@ -8,13 +8,13 @@ use crate::{ // TODO(ayush): can segmentation also be triggered by timestamp overflow? should that be tracked? #[derive(Debug)] pub struct MeteredCtxBounded { - pub trace_heights: Vec, + pub trace_heights: Vec, continuations_enabled: bool, - num_access_adapters: usize, + num_access_adapters: u8, // TODO(ayush): take alignment into account for access adapters #[allow(dead_code)] - as_byte_alignment_bits: Vec, + as_byte_alignment_bits: Vec, pub memory_dimensions: MemoryDimensions, // Indices of leaf nodes in the memory merkle tree @@ -25,8 +25,8 @@ impl MeteredCtxBounded { pub fn new( num_traces: usize, continuations_enabled: bool, - num_access_adapters: usize, - as_byte_alignment_bits: Vec, + num_access_adapters: u8, + as_byte_alignment_bits: Vec, memory_dimensions: MemoryDimensions, ) -> Self { Self { @@ -41,7 +41,7 @@ impl MeteredCtxBounded { } impl MeteredCtxBounded { - fn update_boundary_merkle_heights(&mut self, address_space: u32, ptr: u32, size: usize) { + fn update_boundary_merkle_heights(&mut self, address_space: u32, ptr: u32, size: u32) { let boundary_idx = if self.continuations_enabled { PUBLIC_VALUES_AIR_ID } else { @@ -49,9 +49,9 @@ impl MeteredCtxBounded { }; let poseidon2_idx = self.trace_heights.len() - 2; - let num_blocks = (size + CHUNK - 1) >> CHUNK_BITS; + let num_blocks = (size + CHUNK as u32 - 1) >> CHUNK_BITS; for i in 0..num_blocks { - let addr = ptr.wrapping_add((i * CHUNK) as u32); + let addr = ptr.wrapping_add(i * CHUNK as u32); let block_id = addr >> CHUNK_BITS; let leaf_id = self .memory_dimensions @@ -68,10 +68,10 @@ impl MeteredCtxBounded { let succ_id = (insert_idx < self.leaf_indices.len() - 1) .then(|| self.leaf_indices[insert_idx + 1]); let height_change = calculate_merkle_node_updates( + leaf_id, pred_id, succ_id, - leaf_id, - self.memory_dimensions.overall_height(), + self.memory_dimensions.overall_height() as u32, ); self.trace_heights[boundary_idx + 1] += height_change * 2; self.trace_heights[poseidon2_idx] += height_change * 2; @@ -80,7 +80,7 @@ impl MeteredCtxBounded { } } - fn update_adapter_heights_batch(&mut self, size: usize, num: usize) { + fn update_adapter_heights_batch(&mut self, size: u32, num: u32) { let adapter_offset = if self.continuations_enabled { PUBLIC_VALUES_AIR_ID + 2 } else { @@ -90,18 +90,18 @@ impl MeteredCtxBounded { apply_adapter_updates_batch(size, num, &mut self.trace_heights[adapter_offset..]); } - fn update_adapter_heights(&mut self, size: usize) { + fn update_adapter_heights(&mut self, size: u32) { self.update_adapter_heights_batch(size, 1); } pub fn finalize_access_adapter_heights(&mut self) { - self.update_adapter_heights_batch(CHUNK, self.leaf_indices.len()); + self.update_adapter_heights_batch(CHUNK as u32, self.leaf_indices.len() as u32); } - pub fn trace_heights_if_finalized(&mut self) -> Vec { - let num_leaves = self.leaf_indices.len(); - let mut access_adapter_updates = vec![0; self.num_access_adapters]; - apply_adapter_updates_batch(CHUNK, num_leaves, &mut access_adapter_updates); + pub fn trace_heights_if_finalized(&mut self) -> Vec { + let num_leaves = self.leaf_indices.len() as u32; + let mut access_adapter_updates = vec![0; self.num_access_adapters as usize]; + apply_adapter_updates_batch(CHUNK as u32, num_leaves, &mut access_adapter_updates); let adapter_offset = if self.continuations_enabled { PUBLIC_VALUES_AIR_ID + 2 @@ -123,7 +123,7 @@ impl MeteredCtxBounded { } impl E1E2ExecutionCtx for MeteredCtxBounded { - fn on_memory_operation(&mut self, address_space: u32, ptr: u32, size: usize) { + fn on_memory_operation(&mut self, address_space: u32, ptr: u32, size: u32) { debug_assert!( address_space != RV32_IMM_AS, "address space must not be immediate" @@ -140,19 +140,19 @@ impl E1E2ExecutionCtx for MeteredCtxBounded { } } -fn apply_adapter_updates_batch(size: usize, num: usize, trace_heights: &mut [usize]) { - let size_bits = size.ilog2() as usize; +fn apply_adapter_updates_batch(size: u32, num: u32, trace_heights: &mut [u32]) { + let size_bits = size.ilog2(); for adapter_bits in (3..=size_bits).rev() { - trace_heights[adapter_bits - 1] += num << (size_bits - adapter_bits + 1); + trace_heights[adapter_bits as usize - 1] += num << (size_bits - adapter_bits + 1); } } fn calculate_merkle_node_updates( + leaf_id: u64, pred_id: Option, succ_id: Option, - leaf_id: u64, - height: usize, -) -> usize { + height: u32, +) -> u32 { // First node requires height many updates if pred_id.is_none() && succ_id.is_none() { return height; @@ -163,19 +163,19 @@ fn calculate_merkle_node_updates( // Add new divergences between pred and leaf_index if let Some(p) = pred_id { - let new_divergence = (p ^ leaf_id).ilog2() as usize; + let new_divergence = (p ^ leaf_id).ilog2(); diff += new_divergence; } // Add new divergences between leaf_index and succ if let Some(s) = succ_id { - let new_divergence = (leaf_id ^ s).ilog2() as usize; + let new_divergence = (leaf_id ^ s).ilog2(); diff += new_divergence; } // Remove old divergence between pred and succ if both existed if let (Some(p), Some(s)) = (pred_id, succ_id) { - let old_divergence = (p ^ s).ilog2() as usize; + let old_divergence = (p ^ s).ilog2(); diff -= old_divergence; } diff --git a/crates/vm/src/arch/execution_mode/metered/exact.rs b/crates/vm/src/arch/execution_mode/metered/exact.rs index de91e95535..c88e7cad17 100644 --- a/crates/vm/src/arch/execution_mode/metered/exact.rs +++ b/crates/vm/src/arch/execution_mode/metered/exact.rs @@ -10,11 +10,11 @@ use crate::{ // TODO(ayush): can segmentation also be triggered by timestamp overflow? should that be tracked? #[derive(Debug)] pub struct MeteredCtxExact { - pub trace_heights: Vec, + pub trace_heights: Vec, continuations_enabled: bool, - num_access_adapters: usize, - as_byte_alignment_bits: Vec, + num_access_adapters: u8, + as_byte_alignment_bits: Vec, pub memory_dimensions: MemoryDimensions, // Map from (addr_space, addr) -> (size, offset) @@ -27,8 +27,8 @@ impl MeteredCtxExact { pub fn new( num_traces: usize, continuations_enabled: bool, - num_access_adapters: usize, - as_byte_alignment_bits: Vec, + num_access_adapters: u8, + as_byte_alignment_bits: Vec, memory_dimensions: MemoryDimensions, ) -> Self { Self { @@ -44,7 +44,7 @@ impl MeteredCtxExact { } impl MeteredCtxExact { - fn update_boundary_merkle_heights(&mut self, address_space: u32, ptr: u32, size: usize) { + fn update_boundary_merkle_heights(&mut self, address_space: u32, ptr: u32, size: u32) { let boundary_idx = if self.continuations_enabled { PUBLIC_VALUES_AIR_ID } else { @@ -52,9 +52,9 @@ impl MeteredCtxExact { }; let poseidon2_idx = self.trace_heights.len() - 2; - let num_blocks = (size + CHUNK - 1) >> CHUNK_BITS; + let num_blocks = (size + CHUNK as u32 - 1) >> CHUNK_BITS; for i in 0..num_blocks { - let addr = ptr.wrapping_add((i * CHUNK) as u32); + let addr = ptr.wrapping_add(i * CHUNK as u32); let block_id = addr >> CHUNK_BITS; let leaf_id = self .memory_dimensions @@ -72,10 +72,10 @@ impl MeteredCtxExact { let succ_id = (insert_idx < self.leaf_indices.len() - 1) .then(|| self.leaf_indices[insert_idx + 1]); let height_change = calculate_merkle_node_updates( + leaf_id, pred_id, succ_id, - leaf_id, - self.memory_dimensions.overall_height(), + self.memory_dimensions.overall_height() as u32, ); self.trace_heights[boundary_idx + 1] += height_change * 2; self.trace_heights[poseidon2_idx] += height_change * 2; @@ -89,12 +89,11 @@ impl MeteredCtxExact { &self, address_space: u32, ptr: u32, - size: usize, - ) -> (Vec<(u32, usize)>, Vec<(u32, usize)>) { + size: u32, + ) -> (Vec<(u32, u32)>, Vec<(u32, u32)>) { // Skip adapters if this is a repeated access to the same location with same size let last_access = self.last_memory_access.get(&(address_space as u8, ptr)); - if matches!(last_access, Some(&(last_access_size, 0)) if size == last_access_size as usize) - { + if matches!(last_access, Some(&(last_access_size, 0)) if size == last_access_size as u32) { return (vec![], vec![]); } @@ -104,33 +103,33 @@ impl MeteredCtxExact { ptr_start = ptr.wrapping_sub(last_access_offset as u32); } - let align_bits = self.as_byte_alignment_bits[address_space as usize]; + let align_bits = self.as_byte_alignment_bits[address_space as usize] as usize; let align = 1 << align_bits; // Split intersecting blocks to align bytes let mut curr_block = ptr_start >> align_bits; - let end_block = curr_block + (size as u32 >> align_bits); + let end_block = curr_block + (size >> align_bits); let mut splits = vec![]; while curr_block < end_block { let curr_block_size = if let Some(&(last_access_size, _)) = self .last_memory_access .get(&(address_space as u8, curr_block.wrapping_mul(align as u32))) { - last_access_size as usize + last_access_size as u32 } else { // Initial memory access only happens at CHUNK boundary let chunk_ratio = 1 << (CHUNK_BITS - align_bits); let chunk_offset = curr_block & (chunk_ratio - 1); curr_block -= chunk_offset; - CHUNK + CHUNK as u32 }; - if curr_block_size > align { + if curr_block_size > align as u32 { let curr_ptr = curr_block.wrapping_mul(align as u32); splits.push((curr_ptr, curr_block_size)); } - curr_block += (curr_block_size >> align_bits) as u32; + curr_block += curr_block_size >> align_bits; } // Merge added blocks from align to size bytes let merges = vec![(ptr, size)]; @@ -143,8 +142,8 @@ impl MeteredCtxExact { &mut self, addr_space: u32, ptr: u32, - size: usize, - trace_heights: &mut Option<&mut [usize]>, + size: u32, + trace_heights: &mut Option<&mut [u32]>, memory_updates: &mut Option)>>, ) { let adapter_offset = if self.continuations_enabled { @@ -194,7 +193,7 @@ impl MeteredCtxExact { } } - fn update_adapter_heights(&mut self, addr_space: u32, ptr: u32, size: usize) { + fn update_adapter_heights(&mut self, addr_space: u32, ptr: u32, size: u32) { self.apply_adapter_updates(addr_space, ptr, size, &mut None, &mut None); } @@ -208,11 +207,11 @@ impl MeteredCtxExact { }) .collect(); for (addr_space, block_id) in indices_to_process { - self.update_adapter_heights(addr_space, block_id * CHUNK as u32, CHUNK); + self.update_adapter_heights(addr_space, block_id * CHUNK as u32, CHUNK as u32); } } - pub fn trace_heights_if_finalized(&mut self) -> Vec { + pub fn trace_heights_if_finalized(&mut self) -> Vec { let indices_to_process: Vec<_> = self .leaf_indices .iter() @@ -222,14 +221,14 @@ impl MeteredCtxExact { }) .collect(); - let mut access_adapter_updates = vec![0; self.num_access_adapters]; + let mut access_adapter_updates = vec![0; self.num_access_adapters as usize]; let mut memory_updates = Some(vec![]); for (addr_space, block_id) in indices_to_process { let ptr = block_id * CHUNK as u32; self.apply_adapter_updates( addr_space, ptr, - CHUNK, + CHUNK as u32, &mut Some(&mut access_adapter_updates), &mut memory_updates, ); @@ -267,7 +266,7 @@ impl MeteredCtxExact { } impl E1E2ExecutionCtx for MeteredCtxExact { - fn on_memory_operation(&mut self, address_space: u32, ptr: u32, size: usize) { + fn on_memory_operation(&mut self, address_space: u32, ptr: u32, size: u32) { debug_assert!( address_space != RV32_IMM_AS, "address space must not be immediate" @@ -284,10 +283,10 @@ impl E1E2ExecutionCtx for MeteredCtxExact { } } -fn apply_single_adapter_heights_update(trace_heights: &mut [usize], size: usize) { - let size_bits = size.ilog2() as usize; +fn apply_single_adapter_heights_update(trace_heights: &mut [u32], size: u32) { + let size_bits = size.ilog2() as u32; for adapter_bits in (3..=size_bits).rev() { - trace_heights[adapter_bits - 1] += 1 << (size_bits - adapter_bits); + trace_heights[adapter_bits as usize - 1] += 1 << (size_bits - adapter_bits); } } @@ -295,28 +294,28 @@ fn apply_single_adapter_heights_update(trace_heights: &mut [usize], size: usize) fn add_memory_access( memory_access_map: &mut BTreeMap<(u8, u32), (u8, u8)>, (address_space, ptr): (u32, u32), - size: usize, - align_bits: usize, + size: u32, + align_bits: u8, is_split: bool, ) -> Vec<((u8, u32), Option<(u8, u8)>)> { let align = 1 << align_bits; debug_assert_eq!( - size & (align - 1), + size & (align as u32 - 1), 0, "Size must be a multiple of alignment" ); let num_chunks = size >> align_bits; - let mut old_values = Vec::with_capacity(num_chunks); + let mut old_values = Vec::with_capacity(num_chunks as usize); for i in 0..num_chunks { - let curr_ptr = ptr.wrapping_add(i as u32 * align as u32); + let curr_ptr = ptr.wrapping_add(i * align as u32); let key = (address_space as u8, curr_ptr); let value = if is_split { (align as u8, 0) } else { - (size as u8, (i * align) as u8) + (size as u8, (i * align as u32) as u8) }; let old_value = memory_access_map.insert(key, value); @@ -330,8 +329,8 @@ fn add_memory_access( fn add_memory_access_split_with_return( memory_access_map: &mut BTreeMap<(u8, u32), (u8, u8)>, (address_space, ptr): (u32, u32), - size: usize, - align_bits: usize, + size: u32, + align_bits: u8, ) -> Vec<((u8, u32), Option<(u8, u8)>)> { add_memory_access( memory_access_map, @@ -346,8 +345,8 @@ fn add_memory_access_split_with_return( fn add_memory_access_merge_with_return( memory_access_map: &mut BTreeMap<(u8, u32), (u8, u8)>, (address_space, ptr): (u32, u32), - size: usize, - align_bits: usize, + size: u32, + align_bits: u8, ) -> Vec<((u8, u32), Option<(u8, u8)>)> { add_memory_access( memory_access_map, @@ -359,11 +358,11 @@ fn add_memory_access_merge_with_return( } fn calculate_merkle_node_updates( + leaf_id: u64, pred_id: Option, succ_id: Option, - leaf_id: u64, - height: usize, -) -> usize { + height: u32, +) -> u32 { // First node requires height many updates if pred_id.is_none() && succ_id.is_none() { return height; @@ -374,19 +373,19 @@ fn calculate_merkle_node_updates( // Add new divergences between pred and leaf_index if let Some(p) = pred_id { - let new_divergence = (p ^ leaf_id).ilog2() as usize; + let new_divergence = (p ^ leaf_id).ilog2(); diff += new_divergence; } // Add new divergences between leaf_index and succ if let Some(s) = succ_id { - let new_divergence = (leaf_id ^ s).ilog2() as usize; + let new_divergence = (leaf_id ^ s).ilog2(); diff += new_divergence; } // Remove old divergence between pred and succ if both existed if let (Some(p), Some(s)) = (pred_id, succ_id) { - let old_divergence = (p ^ s).ilog2() as usize; + let old_divergence = (p ^ s).ilog2(); diff -= old_divergence; } diff --git a/crates/vm/src/arch/execution_mode/metered/mod.rs b/crates/vm/src/arch/execution_mode/metered/mod.rs index ea66f85f81..ac1f8f46bf 100644 --- a/crates/vm/src/arch/execution_mode/metered/mod.rs +++ b/crates/vm/src/arch/execution_mode/metered/mod.rs @@ -16,7 +16,7 @@ use crate::arch::{ const SEGMENT_CHECK_INTERVAL: usize = 100; // TODO(ayush): fix these values -const MAX_TRACE_HEIGHT: usize = DEFAULT_MAX_SEGMENT_LEN; +const MAX_TRACE_HEIGHT: u32 = DEFAULT_MAX_SEGMENT_LEN as u32; const MAX_TRACE_CELLS: usize = DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT; const MAX_INTERACTIONS: usize = DEFAULT_MAX_SEGMENT_LEN * 100; @@ -37,21 +37,21 @@ impl<'a> MeteredExecutionControl<'a> { /// Calculate the total cells used based on trace heights and widths // TODO(ayush): account for preprocessed and permutation columns - fn calculate_total_cells(&self, trace_heights: &[usize]) -> usize { + fn calculate_total_cells(&self, trace_heights: &[u32]) -> usize { trace_heights .iter() .zip(self.widths) - .map(|(&height, &width)| height.next_power_of_two() * width) + .map(|(&height, &width)| height.next_power_of_two() as usize * width) .sum() } /// Calculate the total interactions based on trace heights and interaction counts - fn calculate_total_interactions(&self, trace_heights: &[usize]) -> usize { + fn calculate_total_interactions(&self, trace_heights: &[u32]) -> usize { trace_heights .iter() .zip(self.interactions) // We add 1 for the zero messages from the padding rows - .map(|(&height, &interactions)| (height + 1) * interactions) + .map(|(&height, &interactions)| (height + 1) as usize * interactions) .sum() } } @@ -120,7 +120,8 @@ where ) { // Program | Connector | Public Values | Memory ... | Executors (except Public Values) | // Range Checker - state.ctx.trace_heights[PROGRAM_AIR_ID] = chip_complex.program_chip().true_program_length; + state.ctx.trace_heights[PROGRAM_AIR_ID] = + chip_complex.program_chip().true_program_length as u32; state.ctx.trace_heights[CONNECTOR_AIR_ID] = 2; let mut offset = if chip_complex.config().has_public_values_chip() { @@ -142,7 +143,7 @@ where if let Some(constant_height) = chip_complex.inventory.periphery[id].constant_trace_height() { - state.ctx.trace_heights[offset + i] = constant_height; + state.ctx.trace_heights[offset + i] = constant_height as u32; } } } @@ -152,7 +153,7 @@ where chip_complex.range_checker_chip().constant_trace_height(), state.ctx.trace_heights.last_mut(), ) { - *last_height = range_checker_height; + *last_height = range_checker_height as u32; } } diff --git a/crates/vm/src/arch/execution_mode/mod.rs b/crates/vm/src/arch/execution_mode/mod.rs index a77832cb05..0f37eb3638 100644 --- a/crates/vm/src/arch/execution_mode/mod.rs +++ b/crates/vm/src/arch/execution_mode/mod.rs @@ -4,5 +4,5 @@ pub mod tracegen; // TODO(ayush): better name pub trait E1E2ExecutionCtx { - fn on_memory_operation(&mut self, address_space: u32, ptr: u32, size: usize); + fn on_memory_operation(&mut self, address_space: u32, ptr: u32, size: u32); } diff --git a/crates/vm/src/arch/extensions.rs b/crates/vm/src/arch/extensions.rs index f3a09d3151..7f62f2046f 100644 --- a/crates/vm/src/arch/extensions.rs +++ b/crates/vm/src/arch/extensions.rs @@ -53,7 +53,7 @@ use crate::{ poseidon2::Poseidon2PeripheryChip, program::{ProgramBus, ProgramChip}, public_values::{ - core::{PublicValuesCoreAir, PublicValuesStep}, + core::{PublicValuesCoreAir, PublicValuesCoreStep}, PublicValuesChip, }, }, @@ -604,7 +604,7 @@ impl SystemComplex { config.max_constraint_degree as u32 - 1, ), ), - PublicValuesStep::new(NativeAdapterStep::new(), config.num_public_values), + PublicValuesCoreStep::new(NativeAdapterStep::new(), config.num_public_values), MAX_INS_CAPACITY, memory_controller.helper(), ); diff --git a/crates/vm/src/arch/integration_api.rs b/crates/vm/src/arch/integration_api.rs index 3f6808e3c8..4294458b22 100644 --- a/crates/vm/src/arch/integration_api.rs +++ b/crates/vm/src/arch/integration_api.rs @@ -446,7 +446,10 @@ where state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, - ) -> Result<()> { + ) -> Result<()> + where + F: PrimeField32, + { self.step.execute_metered(state, instruction, chip_index) } } diff --git a/crates/vm/src/arch/vm.rs b/crates/vm/src/arch/vm.rs index ae5fd27d76..0a6392179a 100644 --- a/crates/vm/src/arch/vm.rs +++ b/crates/vm/src/arch/vm.rs @@ -290,12 +290,15 @@ where let streams = segment.chip_complex.take_streams(); #[cfg(feature = "bench-metrics")] let metrics = segment.metrics.partial_take(); + + // TODO(ayush): this can probably be avoided + let memory = segment.ctrl.final_memory.as_ref().unwrap().clone(); Ok(VmExecutorOneSegmentResult { segment, next_state: Some(VmState { clk: exec_state.clk, pc: exec_state.pc, - memory: exec_state.memory.unwrap().memory, + memory, input: streams, #[cfg(feature = "bench-metrics")] metrics, @@ -475,14 +478,14 @@ where let ctx = MeteredCtx::new( widths.len(), continuations_enabled, - num_access_adapters, + num_access_adapters as u8, segment .chip_complex .memory_controller() .memory .min_block_size .iter() - .map(|&x| log2_strict_usize(x as usize)) + .map(|&x| log2_strict_usize(x as usize) as u8) .collect(), segment .chip_complex diff --git a/crates/vm/src/system/memory/offline_checker/bridge.rs b/crates/vm/src/system/memory/offline_checker/bridge.rs index 2c7e180cfb..3174309454 100644 --- a/crates/vm/src/system/memory/offline_checker/bridge.rs +++ b/crates/vm/src/system/memory/offline_checker/bridge.rs @@ -21,7 +21,7 @@ use crate::system::memory::{ /// be decomposed into) for the `AssertLtSubAir` in the `MemoryOfflineChecker`. /// Warning: This requires that (clk_max_bits + decomp - 1) / decomp = AUX_LEN /// in MemoryOfflineChecker (or whenever AssertLtSubAir is used) -pub(crate) const AUX_LEN: usize = 2; +pub const AUX_LEN: usize = 2; /// The [MemoryBridge] is used within AIR evaluation functions to constrain logical memory /// operations (read/write). It adds all necessary constraints and interactions. diff --git a/crates/vm/src/system/memory/offline_checker/columns.rs b/crates/vm/src/system/memory/offline_checker/columns.rs index 677e04fd31..be5037d3ec 100644 --- a/crates/vm/src/system/memory/offline_checker/columns.rs +++ b/crates/vm/src/system/memory/offline_checker/columns.rs @@ -18,7 +18,7 @@ pub struct MemoryBaseAuxCols { /// The previous timestamps in which the cells were accessed. pub(in crate::system::memory) prev_timestamp: T, /// The auxiliary columns to perform the less than check. - pub(in crate::system::memory) timestamp_lt_aux: LessThanAuxCols, + pub timestamp_lt_aux: LessThanAuxCols, } impl MemoryBaseAuxCols { @@ -30,7 +30,7 @@ impl MemoryBaseAuxCols { #[repr(C)] #[derive(Clone, Copy, Debug, AlignedBorrow)] pub struct MemoryWriteAuxCols { - pub(in crate::system::memory) base: MemoryBaseAuxCols, + pub base: MemoryBaseAuxCols, pub(in crate::system::memory) prev_data: [T; N], } @@ -79,10 +79,7 @@ pub struct MemoryReadAuxCols { } impl MemoryReadAuxCols { - pub(in crate::system::memory) fn new( - prev_timestamp: u32, - timestamp_lt_aux: LessThanAuxCols, - ) -> Self { + pub fn new(prev_timestamp: u32, timestamp_lt_aux: LessThanAuxCols) -> Self { Self { base: MemoryBaseAuxCols { prev_timestamp: F::from_canonical_u32(prev_timestamp), @@ -104,9 +101,9 @@ impl MemoryReadAuxCols { #[repr(C)] #[derive(Clone, Debug, AlignedBorrow)] pub struct MemoryReadOrImmediateAuxCols { - pub(crate) base: MemoryBaseAuxCols, - pub(crate) is_immediate: T, - pub(crate) is_zero_aux: T, + pub base: MemoryBaseAuxCols, + pub is_immediate: T, + pub is_zero_aux: T, } impl AsRef> for MemoryWriteAuxCols { diff --git a/crates/vm/src/system/memory/paged_vec.rs b/crates/vm/src/system/memory/paged_vec.rs index 192a655780..ead78f81ee 100644 --- a/crates/vm/src/system/memory/paged_vec.rs +++ b/crates/vm/src/system/memory/paged_vec.rs @@ -47,7 +47,12 @@ impl PagedVec { std::slice::from_raw_parts_mut(dst, len).fill(0u8); } } else { - debug_assert_eq!(start_page + 1, end_page); + debug_assert_eq!( + start_page + 1, + end_page, + "Range spans more than two pages: {:?}", + (start_page, end_page, start, len) + ); let offset = start % PAGE_SIZE; let first_part = PAGE_SIZE - offset; if let Some(page) = self.pages[start_page].as_ref() { diff --git a/crates/vm/src/system/native_adapter/mod.rs b/crates/vm/src/system/native_adapter/mod.rs index 179a1fef29..18f759a5ec 100644 --- a/crates/vm/src/system/native_adapter/mod.rs +++ b/crates/vm/src/system/native_adapter/mod.rs @@ -28,7 +28,7 @@ use serde_big_array::BigArray; use super::memory::{online::TracingMemory, MemoryAuxColsFactory}; use crate::{ - arch::{AdapterExecutorE1, AdapterTraceStep, VmStateMut}, + arch::{execution_mode::E1E2ExecutionCtx, AdapterExecutorE1, AdapterTraceStep, VmStateMut}, system::memory::{online::GuestMemory, RecordId}, }; @@ -251,7 +251,10 @@ where &self, state: &mut VmStateMut, instruction: &Instruction, - ) -> Self::ReadData { + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { assert!(R <= 2); let &Instruction { b, c, e, f, .. } = instruction; @@ -282,7 +285,9 @@ where state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, - ) { + ) where + Ctx: E1E2ExecutionCtx, + { assert!(W <= 1); let &Instruction { a, d, .. } = instruction; diff --git a/crates/vm/src/system/phantom/mod.rs b/crates/vm/src/system/phantom/mod.rs index 4e151c525b..cdf6f0dfa0 100644 --- a/crates/vm/src/system/phantom/mod.rs +++ b/crates/vm/src/system/phantom/mod.rs @@ -184,10 +184,7 @@ where state: &mut VmStateMut, instruction: &Instruction, _chip_index: usize, - ) -> Result<(), ExecutionError> - where - F: PrimeField32, - { + ) -> Result<(), ExecutionError> { self.execute_e1(state, instruction)?; Ok(()) diff --git a/crates/vm/src/system/public_values/core.rs b/crates/vm/src/system/public_values/core.rs index 7b798606e9..770475ccee 100644 --- a/crates/vm/src/system/public_values/core.rs +++ b/crates/vm/src/system/public_values/core.rs @@ -118,13 +118,13 @@ pub struct PublicValuesRecord { /// ATTENTION: If a specific public value is not provided, a default 0 will be used when generating /// the proof but in the perspective of constraints, it could be any value. -pub struct PublicValuesStep { +pub struct PublicValuesCoreStep { adapter: A, // Mutex is to make the struct Sync. But it actually won't be accessed by multiple threads. pub(crate) custom_pvs: Mutex>>, } -impl PublicValuesStep +impl PublicValuesCoreStep where F: PrimeField32, { @@ -142,7 +142,7 @@ where } } -impl TraceStep for PublicValuesStep +impl TraceStep for PublicValuesCoreStep where F: PrimeField32, A: 'static @@ -205,7 +205,7 @@ where } } -impl StepExecutorE1 for PublicValuesStep +impl StepExecutorE1 for PublicValuesCoreStep where F: PrimeField32, A: 'static + for<'a> AdapterExecutorE1, diff --git a/crates/vm/src/system/public_values/mod.rs b/crates/vm/src/system/public_values/mod.rs index 8b849ab819..5b1b28cc01 100644 --- a/crates/vm/src/system/public_values/mod.rs +++ b/crates/vm/src/system/public_values/mod.rs @@ -1,4 +1,4 @@ -use core::PublicValuesStep; +use core::PublicValuesCoreStep; use crate::{ arch::{NewVmChipWrapper, VmAirWrapper}, @@ -16,5 +16,5 @@ pub mod core; mod tests; pub type PublicValuesAir = VmAirWrapper, PublicValuesCoreAir>; -pub type PublicValuesStepWithAdapter = PublicValuesStep, F>; -pub type PublicValuesChip = NewVmChipWrapper>; +pub type PublicValuesStep = PublicValuesCoreStep, F>; +pub type PublicValuesChip = NewVmChipWrapper>; diff --git a/extensions/algebra/circuit/src/modular_chip/is_eq.rs b/extensions/algebra/circuit/src/modular_chip/is_eq.rs index e774df93c0..e9ef70d3f1 100644 --- a/extensions/algebra/circuit/src/modular_chip/is_eq.rs +++ b/extensions/algebra/circuit/src/modular_chip/is_eq.rs @@ -465,7 +465,10 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - todo!() + self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; + + Ok(()) } } diff --git a/extensions/keccak256/circuit/src/lib.rs b/extensions/keccak256/circuit/src/lib.rs index 6ca057597f..b1fefcf20f 100644 --- a/extensions/keccak256/circuit/src/lib.rs +++ b/extensions/keccak256/circuit/src/lib.rs @@ -3,6 +3,8 @@ use openvm_circuit_primitives::bitwise_op_lookup::SharedBitwiseOperationLookupChip; use openvm_stark_backend::p3_field::PrimeField32; + +use p3_keccak_air::NUM_ROUNDS; use tiny_keccak::{Hasher, Keccak}; pub mod air; @@ -19,18 +21,22 @@ mod tests; pub use air::KeccakVmAir; use openvm_circuit::{ arch::{ - execution_mode::metered::MeteredCtx, ExecutionBridge, NewVmChipWrapper, Result, - StepExecutorE1, VmStateMut, + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, + ExecutionBridge, NewVmChipWrapper, Result, StepExecutorE1, VmStateMut, }, system::memory::online::GuestMemory, }; use openvm_instructions::{ instruction::Instruction, + program::DEFAULT_PC_STEP, riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}, LocalOpcode, }; use openvm_keccak256_transpiler::Rv32KeccakOpcode; -use openvm_rv32im_circuit::adapters::{memory_write, new_read_rv32_register}; +use openvm_rv32im_circuit::adapters::{ + memory_read_from_state, memory_write_from_state, new_read_rv32_register_from_state, +}; +use utils::num_keccak_f; // ==== Constants for register/memory adapter ==== /// Register reads to get dst, src, len @@ -91,7 +97,10 @@ impl StepExecutorE1 for KeccakVmStep { &mut self, state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let &Instruction { opcode, a, @@ -103,34 +112,89 @@ impl StepExecutorE1 for KeccakVmStep { } = instruction; let d = d.as_canonical_u32(); let e = e.as_canonical_u32(); + debug_assert_eq!(opcode, Rv32KeccakOpcode::KECCAK256.global_opcode()); debug_assert_eq!(d, RV32_REGISTER_AS); debug_assert_eq!(e, RV32_MEMORY_AS); - let dst = new_read_rv32_register(state.memory, d, a.as_canonical_u32()); - let src = new_read_rv32_register(state.memory, d, b.as_canonical_u32()); - let len = new_read_rv32_register(state.memory, d, c.as_canonical_u32()); + let dst = new_read_rv32_register_from_state(state, d, a.as_canonical_u32()); + let src = new_read_rv32_register_from_state(state, d, b.as_canonical_u32()); + let len = new_read_rv32_register_from_state(state, d, c.as_canonical_u32()); let mut hasher = Keccak::v256(); - let message: Vec = state - .memory - .memory - .read_range_generic((e, src), len as usize); + // TODO(ayush): read in a single call + let mut message = Vec::with_capacity(len as usize); + for offset in (0..len as usize).step_by(KECCAK_WORD_SIZE) { + let read = memory_read_from_state::<_, KECCAK_WORD_SIZE>(state, e, src + offset as u32); + let copy_len = std::cmp::min(KECCAK_WORD_SIZE, (len as usize) - offset); + message.extend_from_slice(&read[..copy_len]); + } hasher.update(&message); let mut output = [0u8; 32]; hasher.finalize(&mut output); - memory_write(state.memory, e, dst, &output); + memory_write_from_state(state, e, dst, &output); + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + Ok(()) } fn execute_metered( &mut self, - _state: &mut VmStateMut, - _instruction: &Instruction, - _chip_index: usize, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, ) -> Result<()> { - todo!() + let &Instruction { + opcode, + a, + b, + c, + d, + e, + .. + } = instruction; + let d = d.as_canonical_u32(); + let e = e.as_canonical_u32(); + + debug_assert_eq!(opcode, Rv32KeccakOpcode::KECCAK256.global_opcode()); + debug_assert_eq!(d, RV32_REGISTER_AS); + debug_assert_eq!(e, RV32_MEMORY_AS); + + let dst = new_read_rv32_register_from_state(state, d, a.as_canonical_u32()); + let src = new_read_rv32_register_from_state(state, d, b.as_canonical_u32()); + let len = new_read_rv32_register_from_state(state, d, c.as_canonical_u32()); + + let num_blocks = num_keccak_f(len as usize); + + let mut message = Vec::with_capacity(len as usize); + for offset in (0..len as usize).step_by(KECCAK_WORD_SIZE) { + let read = memory_read_from_state::<_, KECCAK_WORD_SIZE>(state, e, src + offset as u32); + let copy_len = std::cmp::min(KECCAK_WORD_SIZE, (len as usize) - offset); + message.extend_from_slice(&read[..copy_len]); + } + + let mut hasher = Keccak::v256(); + hasher.update(&message); + + let mut output = [0u8; 32]; + hasher.finalize(&mut output); + + for (i, word) in output.chunks_exact(KECCAK_WORD_SIZE).enumerate() { + memory_write_from_state::<_, KECCAK_WORD_SIZE>( + state, + e, + dst + (i * KECCAK_WORD_SIZE) as u32, + word.try_into().unwrap(), + ); + } + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + state.ctx.trace_heights[chip_index] += (num_blocks * NUM_ROUNDS) as u32; + + Ok(()) } } diff --git a/extensions/native/circuit/Cargo.toml b/extensions/native/circuit/Cargo.toml index 5d5913b4be..a81be9b41b 100644 --- a/extensions/native/circuit/Cargo.toml +++ b/extensions/native/circuit/Cargo.toml @@ -17,6 +17,7 @@ openvm-circuit = { workspace = true } openvm-circuit-derive = { workspace = true } openvm-instructions = { workspace = true } openvm-rv32im-circuit = { workspace = true } +openvm-rv32im-transpiler = { workspace = true } openvm-native-compiler = { workspace = true } diff --git a/extensions/native/circuit/src/adapters/alu_native_adapter.rs b/extensions/native/circuit/src/adapters/alu_native_adapter.rs index 9fd2d1632e..74ac396091 100644 --- a/extensions/native/circuit/src/adapters/alu_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/alu_native_adapter.rs @@ -1,22 +1,18 @@ use std::{ borrow::{Borrow, BorrowMut}, - marker::PhantomData, + mem::size_of, }; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, AdapterTraceStep, - BasicAdapterInterface, ExecutionBridge, ExecutionBus, ExecutionState, MinimalInstruction, - Result, VmAdapterAir, VmAdapterChip, VmAdapterInterface, + execution_mode::E1E2ExecutionCtx, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, + BasicAdapterInterface, ExecutionBridge, ExecutionState, MinimalInstruction, VmAdapterAir, + VmStateMut, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadOrImmediateAuxCols, MemoryWriteAuxCols}, - online::GuestMemory, - MemoryAddress, MemoryController, OfflineMemory, - }, - native_adapter::{NativeReadRecord, NativeWriteRecord}, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryReadOrImmediateAuxCols, MemoryWriteAuxCols}, + online::{GuestMemory, TracingMemory}, + MemoryAddress, MemoryAuxColsFactory, }, }; use openvm_circuit_primitives_derive::AlignedBorrow; @@ -28,7 +24,12 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, }; -use super::{tracing_read_or_imm, tracing_write, tracing_write_reg}; +use crate::adapters::{ + memory_read_or_imm_native_from_state, memory_write_native_from_state, + tracing_read_or_imm_native, +}; + +use super::tracing_write_native; #[repr(C)] #[derive(AlignedBorrow)] @@ -135,7 +136,7 @@ where const WIDTH: usize = size_of::>(); type ReadData = [F; 2]; type WriteData = [F; 1]; - type TraceContext<'a> = &'a BitwiseOperationLookupChip; + type TraceContext<'a> = (); #[inline(always)] fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { @@ -147,56 +148,88 @@ where #[inline(always)] fn read( + &self, memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { - let Instruction { b, c, e, f, .. } = instruction; + let &Instruction { b, c, e, f, .. } = instruction; let adapter_row: &mut AluNativeAdapterCols = adapter_row.borrow_mut(); - let read1 = tracing_read_or_imm( + adapter_row.b_pointer = b; + let rs1 = tracing_read_or_imm_native( memory, e.as_canonical_u32(), - b.as_canonical_u32(), + b, &mut adapter_row.e_as, - (&mut adapter_row.b_pointer, &mut adapter_row.reads_aux[0]), + &mut adapter_row.reads_aux[0], ); - let read2 = tracing_read_or_imm( + adapter_row.c_pointer = c; + let rs2 = tracing_read_or_imm_native( memory, f.as_canonical_u32(), - c.as_canonical_u32(), + c, &mut adapter_row.f_as, - (&mut adapter_row.c_pointer, &mut adapter_row.reads_aux[1]), + &mut adapter_row.reads_aux[1], ); - [read1, read2] + [rs1, rs2] } #[inline(always)] fn write( + &self, memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, ) { - let Instruction { a, .. } = instruction; + let &Instruction { a, .. } = instruction; let adapter_row: &mut AluNativeAdapterCols = adapter_row.borrow_mut(); - tracing_write( + adapter_row.a_pointer = a; + tracing_write_native( memory, a.as_canonical_u32(), data, - (&mut adapter_row.a_pointer, &mut adapter_row.write_aux), + &mut adapter_row.write_aux, ); } #[inline(always)] fn fill_trace_row( + &self, mem_helper: &MemoryAuxColsFactory, - bitwise_lookup_chip: &BitwiseOperationLookupChip, + _ctx: (), adapter_row: &mut [F], ) { - todo!("Implement fill_trace_row") + let adapter_row: &mut AluNativeAdapterCols = adapter_row.borrow_mut(); + + let mut timestamp = adapter_row.from_state.timestamp.as_canonical_u32(); + + mem_helper.fill_from_prev(timestamp, &mut adapter_row.reads_aux[0].base); + timestamp += 1; + + mem_helper.fill_from_prev(timestamp, &mut adapter_row.reads_aux[1].base); + timestamp += 1; + + mem_helper.fill_from_prev(timestamp, adapter_row.write_aux.as_mut()); + + if adapter_row.e_as.is_zero() { + adapter_row.reads_aux[0].is_immediate = F::ONE; + adapter_row.reads_aux[0].is_zero_aux = F::ZERO; + } else { + adapter_row.reads_aux[0].is_immediate = F::ZERO; + adapter_row.reads_aux[0].is_zero_aux = adapter_row.e_as.inverse(); + } + + if adapter_row.f_as.is_zero() { + adapter_row.reads_aux[1].is_immediate = F::ONE; + adapter_row.reads_aux[1].is_zero_aux = F::ZERO; + } else { + adapter_row.reads_aux[1].is_immediate = F::ZERO; + adapter_row.reads_aux[1].is_zero_aux = adapter_row.f_as.inverse(); + } } } @@ -204,107 +237,37 @@ impl AdapterExecutorE1 for AluNativeAdapterStep where F: PrimeField32, { - type ReadData = (F, F); - type WriteData = F; + type ReadData = [F; 2]; + type WriteData = [F; 1]; - fn read(memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { - let Instruction { b, c, e, f, .. } = instruction; + #[inline(always)] + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { + let &Instruction { b, c, e, f, .. } = instruction; - let [read1]: [F; 1] = unsafe { memory.read(e.as_canonical_u32(), b.as_canonical_u32()) }; - let [read2]: [F; 1] = unsafe { memory.read(f.as_canonical_u32(), c.as_canonical_u32()) }; + let rs1 = memory_read_or_imm_native_from_state(state, e.as_canonical_u32(), b); + let rs2 = memory_read_or_imm_native_from_state(state, f.as_canonical_u32(), c); - (read1, read2) + [rs1, rs2] } - fn write(memory: &mut GuestMemory, instruction: &Instruction, data: &Self::WriteData) { - let Instruction { a, .. } = instruction; + #[inline(always)] + fn write( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + data: &Self::WriteData, + ) where + Ctx: E1E2ExecutionCtx, + { + let &Instruction { a, .. } = instruction; - unsafe { memory.write(AS::Native, a.as_canonical_u32(), &[data]) }; + memory_write_native_from_state(state, a.as_canonical_u32(), data); } } - -// impl VmAdapterChip for AluNativeAdapterChip { -// type ReadRecord = NativeReadRecord; -// type WriteRecord = NativeWriteRecord; -// type Air = AluNativeAdapterAir; -// type Interface = BasicAdapterInterface, 2, 1, 1, 1>; - -// fn preprocess( -// &mut self, -// memory: &mut MemoryController, -// instruction: &Instruction, -// ) -> Result<( -// >::Reads, -// Self::ReadRecord, -// )> { -// let Instruction { b, c, e, f, .. } = *instruction; - -// let reads = vec![memory.read::(e, b), memory.read::(f, c)]; -// let i_reads: [_; 2] = std::array::from_fn(|i| reads[i].1); - -// Ok(( -// i_reads, -// Self::ReadRecord { -// reads: reads.try_into().unwrap(), -// }, -// )) -// } - -// fn postprocess( -// &mut self, -// memory: &mut MemoryController, -// _instruction: &Instruction, -// from_state: ExecutionState, -// output: AdapterRuntimeContext, -// _read_record: &Self::ReadRecord, -// ) -> Result<(ExecutionState, Self::WriteRecord)> { -// let Instruction { a, .. } = *_instruction; -// let writes = vec![memory.write( -// F::from_canonical_u32(AS::Native as u32), -// a, -// &output.writes[0], -// )]; - -// Ok(( -// ExecutionState { -// pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), -// timestamp: memory.timestamp(), -// }, -// Self::WriteRecord { -// from_state, -// writes: writes.try_into().unwrap(), -// }, -// )) -// } - -// fn generate_trace_row( -// &self, -// row_slice: &mut [F], -// read_record: Self::ReadRecord, -// write_record: Self::WriteRecord, -// memory: &OfflineMemory, -// ) { -// let row_slice: &mut AluNativeAdapterCols<_> = row_slice.borrow_mut(); -// let aux_cols_factory = memory.aux_cols_factory(); - -// row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); - -// row_slice.a_pointer = memory.record_by_id(write_record.writes[0].0).pointer; -// row_slice.b_pointer = memory.record_by_id(read_record.reads[0].0).pointer; -// row_slice.c_pointer = memory.record_by_id(read_record.reads[1].0).pointer; -// row_slice.e_as = memory.record_by_id(read_record.reads[0].0).address_space; -// row_slice.f_as = memory.record_by_id(read_record.reads[1].0).address_space; - -// for (i, x) in read_record.reads.iter().enumerate() { -// let read = memory.record_by_id(x.0); -// aux_cols_factory.generate_read_or_immediate_aux(read, &mut row_slice.reads_aux[i]); -// } - -// let write = memory.record_by_id(write_record.writes[0].0); -// aux_cols_factory.generate_write_aux(write, &mut row_slice.write_aux); -// } - -// fn air(&self) -> &Self::Air { -// &self.air -// } -// } diff --git a/extensions/native/circuit/src/adapters/branch_native_adapter.rs b/extensions/native/circuit/src/adapters/branch_native_adapter.rs index b84867b125..4c553d399e 100644 --- a/extensions/native/circuit/src/adapters/branch_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/branch_native_adapter.rs @@ -1,22 +1,17 @@ -use std::{ - borrow::{Borrow, BorrowMut}, - marker::PhantomData, -}; +use std::borrow::{Borrow, BorrowMut}; +use std::mem::size_of; +use openvm_circuit::arch::execution_mode::E1E2ExecutionCtx; +use openvm_circuit::arch::VmStateMut; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, AdapterTraceStep, - BasicAdapterInterface, ExecutionBridge, ExecutionBus, ExecutionState, ImmInstruction, - Result, VmAdapterAir, VmAdapterChip, VmAdapterInterface, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, + ExecutionBridge, ExecutionState, ImmInstruction, VmAdapterAir, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadOrImmediateAuxCols}, - online::GuestMemory, - MemoryAddress, MemoryController, OfflineMemory, - }, - native_adapter::NativeReadRecord, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryReadOrImmediateAuxCols}, + online::{GuestMemory, TracingMemory}, + MemoryAddress, MemoryAuxColsFactory, }, }; use openvm_circuit_primitives_derive::AlignedBorrow; @@ -28,6 +23,8 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, }; +use crate::adapters::{memory_read_or_imm_native_from_state, tracing_read_or_imm_native}; + #[repr(C)] #[derive(AlignedBorrow)] pub struct BranchNativeAdapterReadCols { @@ -134,8 +131,7 @@ where const WIDTH: usize = size_of::>(); type ReadData = [F; 2]; type WriteData = (); - // TODO(ayush): what's this? - type TraceContext<'a> = &'a BitwiseOperationLookupChip; + type TraceContext<'a> = (); #[inline(always)] fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { @@ -147,52 +143,76 @@ where #[inline(always)] fn read( + &self, memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { - let Instruction { b, c, e, f, .. } = instruction; + let &Instruction { a, b, d, e, .. } = instruction; let adapter_row: &mut BranchNativeAdapterCols = adapter_row.borrow_mut(); - let read1 = tracing_read_or_imm( + adapter_row.reads_aux[0].address.pointer = a; + let rs1 = tracing_read_or_imm_native( memory, d.as_canonical_u32(), - a.as_canonical_u32(), + a, &mut adapter_row.reads_aux[0].address.address_space, - ( - &mut adapter_row.reads_aux[0].address.pointer, - &mut adapter_row.reads_aux[0].read_aux, - ), + &mut adapter_row.reads_aux[0].read_aux, ); - let read2 = tracing_read_or_imm( + adapter_row.reads_aux[1].address.pointer = b; + let rs2 = tracing_read_or_imm_native( memory, e.as_canonical_u32(), - b.as_canonical_u32(), + b, &mut adapter_row.reads_aux[1].address.address_space, - ( - &mut adapter_row.reads_aux[1].address.pointer, - &mut adapter_row.reads_aux[1].read_aux, - ), + &mut adapter_row.reads_aux[1].read_aux, ); - [read1, read2] + [rs1, rs2] } #[inline(always)] fn write( - memory: &mut TracingMemory, - instruction: &Instruction, - adapter_row: &mut [F], - data: &Self::WriteData, + &self, + _memory: &mut TracingMemory, + _instruction: &Instruction, + _adapter_row: &mut [F], + _data: &Self::WriteData, ) { } #[inline(always)] fn fill_trace_row( + &self, mem_helper: &MemoryAuxColsFactory, - bitwise_lookup_chip: &BitwiseOperationLookupChip, + _trace_ctx: Self::TraceContext<'_>, adapter_row: &mut [F], ) { - todo!("Implement fill_trace_row") + let adapter_row: &mut BranchNativeAdapterCols = adapter_row.borrow_mut(); + + let mut timestamp = adapter_row.from_state.timestamp.as_canonical_u32(); + + mem_helper.fill_from_prev(timestamp, &mut adapter_row.reads_aux[0].read_aux.base); + timestamp += 1; + + mem_helper.fill_from_prev(timestamp, &mut adapter_row.reads_aux[1].read_aux.base); + + let read_aux0 = &mut adapter_row.reads_aux[0]; + if read_aux0.address.address_space.is_zero() { + read_aux0.read_aux.is_immediate = F::ONE; + read_aux0.read_aux.is_zero_aux = F::ZERO; + } else { + read_aux0.read_aux.is_immediate = F::ZERO; + read_aux0.read_aux.is_zero_aux = read_aux0.address.address_space.inverse(); + } + + let read_aux1 = &mut adapter_row.reads_aux[1]; + if read_aux1.address.address_space.is_zero() { + read_aux1.read_aux.is_immediate = F::ONE; + read_aux1.read_aux.is_zero_aux = F::ZERO; + } else { + read_aux1.read_aux.is_immediate = F::ZERO; + read_aux1.read_aux.is_zero_aux = read_aux1.address.address_space.inverse(); + } } } @@ -200,86 +220,32 @@ impl AdapterExecutorE1 for BranchNativeAdapterStep where F: PrimeField32, { - type ReadData = (F, F); + type ReadData = [F; 2]; type WriteData = (); - fn read(memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { - let Instruction { a, b, d, e, .. } = instruction; + #[inline(always)] + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { + let &Instruction { a, b, d, e, .. } = instruction; - let read1 = unsafe { memory.read(d.as_canonical_u32(), a.as_canonical_u32()) }; - let read2 = unsafe { memory.read(e.as_canonical_u32(), b.as_canonical_u32()) }; + let rs1 = memory_read_or_imm_native_from_state(state, d.as_canonical_u32(), a); + let rs2 = memory_read_or_imm_native_from_state(state, e.as_canonical_u32(), b); - (read1, read2) + [rs1, rs2] } - fn write(_memory: &mut GuestMemory, _instruction: &Instruction, _data: &Self::WriteData) {} + #[inline(always)] + fn write( + &self, + _state: &mut VmStateMut, + _instruction: &Instruction, + _data: &Self::WriteData, + ) { + } } - -// impl VmAdapterChip for BranchNativeAdapterChip { -// type ReadRecord = NativeReadRecord; -// type WriteRecord = ExecutionState; -// type Air = BranchNativeAdapterAir; -// type Interface = BasicAdapterInterface, 2, 0, 1, 1>; - -// fn preprocess( -// &mut self, -// memory: &mut MemoryController, -// instruction: &Instruction, -// ) -> Result<( -// >::Reads, -// Self::ReadRecord, -// )> { -// let Instruction { a, b, d, e, .. } = *instruction; - -// let reads = vec![memory.read::(d, a), memory.read::(e, b)]; -// let i_reads: [_; 2] = std::array::from_fn(|i| reads[i].1); - -// Ok(( -// i_reads, -// Self::ReadRecord { -// reads: reads.try_into().unwrap(), -// }, -// )) -// } - -// fn postprocess( -// &mut self, -// memory: &mut MemoryController, -// _instruction: &Instruction, -// from_state: ExecutionState, -// output: AdapterRuntimeContext, -// _read_record: &Self::ReadRecord, -// ) -> Result<(ExecutionState, Self::WriteRecord)> { -// Ok(( -// ExecutionState { -// pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), -// timestamp: memory.timestamp(), -// }, -// from_state, -// )) -// } - -// fn generate_trace_row( -// &self, -// row_slice: &mut [F], -// read_record: Self::ReadRecord, -// write_record: Self::WriteRecord, -// memory: &OfflineMemory, -// ) { -// let row_slice: &mut BranchNativeAdapterCols<_> = row_slice.borrow_mut(); -// let aux_cols_factory = memory.aux_cols_factory(); - -// row_slice.from_state = write_record.map(F::from_canonical_u32); -// for (i, x) in read_record.reads.iter().enumerate() { -// let read = memory.record_by_id(x.0); - -// row_slice.reads_aux[i].address = MemoryAddress::new(read.address_space, -// read.pointer); aux_cols_factory -// .generate_read_or_immediate_aux(read, &mut row_slice.reads_aux[i].read_aux); -// } -// } - -// fn air(&self) -> &Self::Air { -// &self.air -// } -// } diff --git a/extensions/native/circuit/src/adapters/convert_adapter.rs b/extensions/native/circuit/src/adapters/convert_adapter.rs index a09358fb5a..74550df1b8 100644 --- a/extensions/native/circuit/src/adapters/convert_adapter.rs +++ b/extensions/native/circuit/src/adapters/convert_adapter.rs @@ -1,47 +1,33 @@ use std::{ borrow::{Borrow, BorrowMut}, - marker::PhantomData, + mem::size_of, }; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, AdapterTraceStep, - BasicAdapterInterface, ExecutionBridge, ExecutionBus, ExecutionState, MinimalInstruction, - Result, VmAdapterAir, VmAdapterChip, VmAdapterInterface, + execution_mode::E1E2ExecutionCtx, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, + BasicAdapterInterface, ExecutionBridge, ExecutionState, MinimalInstruction, VmAdapterAir, + VmStateMut, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, - online::GuestMemory, - MemoryAddress, MemoryController, OfflineMemory, RecordId, - }, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, + online::{GuestMemory, TracingMemory}, + MemoryAddress, MemoryAuxColsFactory, }, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP}; +use openvm_instructions::{ + instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_MEMORY_AS, +}; use openvm_native_compiler::conversion::AS; +use openvm_rv32im_circuit::adapters::{memory_write, memory_write_from_state, tracing_write}; use openvm_stark_backend::{ interaction::InteractionBuilder, p3_air::BaseAir, p3_field::{Field, FieldAlgebra, PrimeField32}, }; -use serde::{Deserialize, Serialize}; -use serde_big_array::BigArray; - -#[repr(C)] -#[derive(Debug, Serialize, Deserialize)] -pub struct VectorReadRecord { - #[serde(with = "BigArray")] - pub reads: [RecordId; NUM_READS], -} -#[repr(C)] -#[derive(Debug, Serialize, Deserialize)] -pub struct VectorWriteRecord { - pub from_state: ExecutionState, - pub writes: [RecordId; 1], -} +use crate::adapters::{memory_read_native, memory_read_native_from_state, tracing_read_native}; #[repr(C)] #[derive(AlignedBorrow)] @@ -141,13 +127,13 @@ where { const WIDTH: usize = size_of::>(); type ReadData = [F; READ_SIZE]; - type WriteData = [F; WRITE_SIZE]; - // TODO(ayush): what's this? - type TraceContext<'a> = &'a BitwiseOperationLookupChip; + type WriteData = [u8; WRITE_SIZE]; + type TraceContext<'a> = (); #[inline(always)] fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { - let adapter_row: &mut ConvertAdapterCols = adapter_row.borrow_mut(); + let adapter_row: &mut ConvertAdapterCols = + adapter_row.borrow_mut(); adapter_row.from_state.pc = F::from_canonical_u32(pc); adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); @@ -155,52 +141,68 @@ where #[inline(always)] fn read( + &self, memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { - let Instruction { b, c, e, f, .. } = instruction; - let adapter_row: &mut ConvertAdapterCols = adapter_row.borrow_mut(); + let &Instruction { b, e, .. } = instruction; + + debug_assert_eq!(e.as_canonical_u32(), AS::Native as u32); - // TODO(ayush): create similar `tracing_read_reg_or_imm` for F - let read = tracing_read_reg_or_imm( + let adapter_row: &mut ConvertAdapterCols = + adapter_row.borrow_mut(); + + adapter_row.b_pointer = b; + let read = tracing_read_native( memory, - e.as_canonical_u32(), b.as_canonical_u32(), - // TODO(ayush): why no address space pointer? Should this be hardcoded? - &mut adapter_row.e_as, - (&mut adapter_row.b_pointer, &mut adapter_row.reads_aux[0]), + adapter_row.reads_aux[0].as_mut(), ); read } #[inline(always)] fn write( + &self, memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, ) { - let Instruction { a, d, .. } = instruction; - let adapter_row: &mut ConvertAdapterCols = adapter_row.borrow_mut(); + let &Instruction { a, d, .. } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), RV32_MEMORY_AS); - // TODO(ayush): create similar `tracing_read_reg_or_imm` for F - tracing_write_reg( + let adapter_row: &mut ConvertAdapterCols = + adapter_row.borrow_mut(); + + adapter_row.a_pointer = a; + tracing_write( memory, - d.as_canonical_u32(), + RV32_MEMORY_AS, a.as_canonical_u32(), data, - (&mut adapter_row.a_pointer, &mut adapter_row.write_aux[0]), + &mut adapter_row.writes_aux[0], ); } #[inline(always)] fn fill_trace_row( + &self, mem_helper: &MemoryAuxColsFactory, - bitwise_lookup_chip: &BitwiseOperationLookupChip, + _ctx: Self::TraceContext<'_>, adapter_row: &mut [F], ) { - todo!("Implement fill_trace_row") + let adapter_row: &mut ConvertAdapterCols = + adapter_row.borrow_mut(); + + let mut timestamp = adapter_row.from_state.timestamp.as_canonical_u32(); + + mem_helper.fill_from_prev(timestamp, adapter_row.reads_aux[0].as_mut()); + timestamp += 1; + + mem_helper.fill_from_prev(timestamp, adapter_row.writes_aux[0].as_mut()); } } @@ -210,94 +212,37 @@ where F: PrimeField32, { type ReadData = [F; READ_SIZE]; - type WriteData = [F; WRITE_SIZE]; + type WriteData = [u8; WRITE_SIZE]; - fn read(memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { + #[inline(always)] + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { let Instruction { b, e, .. } = instruction; - let read = unsafe { memory.read(e.as_canonical_u32(), b.as_canonical_u32()) }; - read + debug_assert_eq!(e.as_canonical_u32(), AS::Native as u32); + + memory_read_native_from_state(state, b.as_canonical_u32()) } - fn write(memory: &mut GuestMemory, instruction: &Instruction, data: &Self::WriteData) { + #[inline(always)] + fn write( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + data: &Self::WriteData, + ) where + Ctx: E1E2ExecutionCtx, + { let Instruction { a, d, .. } = instruction; - unsafe { - memory.write::(d.as_canonical_u32(), a.as_cas_canonical_u32(), &data) - }; + debug_assert_eq!(d.as_canonical_u32(), RV32_MEMORY_AS); + + memory_write_from_state(state, RV32_MEMORY_AS, a.as_canonical_u32(), data); } } - -// impl VmAdapterChip -// for ConvertAdapterChip -// { -// type ReadRecord = VectorReadRecord<1, READ_SIZE>; -// type WriteRecord = VectorWriteRecord; -// type Air = ConvertAdapterAir; -// type Interface = BasicAdapterInterface, 1, 1, READ_SIZE, -// WRITE_SIZE>; - -// fn preprocess( -// &mut self, -// memory: &mut MemoryController, -// instruction: &Instruction, -// ) -> Result<( -// >::Reads, -// Self::ReadRecord, -// )> { -// let Instruction { b, e, .. } = *instruction; - -// let y_val = memory.read::(e, b); - -// Ok(([y_val.1], Self::ReadRecord { reads: [y_val.0] })) -// } - -// fn postprocess( -// &mut self, -// memory: &mut MemoryController, -// instruction: &Instruction, -// from_state: ExecutionState, -// output: AdapterRuntimeContext, -// _read_record: &Self::ReadRecord, -// ) -> Result<(ExecutionState, Self::WriteRecord)> { -// let Instruction { a, d, .. } = *instruction; -// let (write_id, _) = memory.write::(d, a, &output.writes[0]); - -// Ok(( -// ExecutionState { -// pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), -// timestamp: memory.timestamp(), -// }, -// Self::WriteRecord { -// from_state, -// writes: [write_id], -// }, -// )) -// } - -// fn generate_trace_row( -// &self, -// row_slice: &mut [F], -// read_record: Self::ReadRecord, -// write_record: Self::WriteRecord, -// memory: &OfflineMemory, -// ) { -// let aux_cols_factory = memory.aux_cols_factory(); -// let row_slice: &mut ConvertAdapterCols<_, READ_SIZE, WRITE_SIZE> = -// row_slice.borrow_mut(); - -// let read = memory.record_by_id(read_record.reads[0]); -// let write = memory.record_by_id(write_record.writes[0]); - -// row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); -// row_slice.a_pointer = write.pointer; -// row_slice.b_pointer = read.pointer; - -// aux_cols_factory.generate_read_aux(read, &mut row_slice.reads_aux[0]); -// aux_cols_factory.generate_write_aux(write, &mut row_slice.writes_aux[0]); -// } - -// fn air(&self) -> &Self::Air { -// &self.air -// } -// } diff --git a/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs b/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs index 5768f2322d..d6eaf0a3bf 100644 --- a/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs @@ -5,20 +5,17 @@ use std::{ use openvm_circuit::{ arch::{ - instructions::LocalOpcode, AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, - ExecutionBridge, ExecutionBus, ExecutionState, Result, VmAdapterAir, VmAdapterChip, - VmAdapterInterface, + execution_mode::E1E2ExecutionCtx, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, + ExecutionBridge, ExecutionState, VmAdapterAir, VmAdapterInterface, VmStateMut, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, - MemoryAddress, MemoryController, OfflineMemory, RecordId, - }, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, + online::{GuestMemory, TracingMemory}, + MemoryAddress, MemoryAuxColsFactory, }, }; use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP}; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; use openvm_native_compiler::{ conversion::AS, NativeLoadStoreOpcode::{self, *}, @@ -28,7 +25,11 @@ use openvm_stark_backend::{ p3_air::BaseAir, p3_field::{Field, FieldAlgebra, PrimeField32}, }; -use serde::{Deserialize, Serialize}; + +use crate::adapters::{ + memory_read_native, memory_read_native_from_state, memory_write_native_from_state, + tracing_read_native, tracing_write_native, +}; pub struct NativeLoadStoreInstruction { pub is_valid: T, @@ -49,30 +50,6 @@ impl VmAdapterInterface type ProcessedInstruction = NativeLoadStoreInstruction; } -#[repr(C)] -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct NativeLoadStoreReadRecord { - pub pointer_read: RecordId, - pub data_read: Option, - pub write_as: F, - pub write_ptr: F, - - pub a: F, - pub b: F, - pub c: F, - pub d: F, - pub e: F, -} - -#[repr(C)] -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct NativeLoadStoreWriteRecord { - pub from_state: ExecutionState, - pub write_id: RecordId, -} - #[repr(C)] #[derive(Clone, Debug, AlignedBorrow)] pub struct NativeLoadStoreAdapterCols { @@ -200,15 +177,14 @@ impl AdapterTraceStep where F: PrimeField32, { - const WIDTH: usize = size_of::>(); + const WIDTH: usize = std::mem::size_of::>(); type ReadData = (F, [F; NUM_CELLS]); type WriteData = [F; NUM_CELLS]; - // TODO(ayush): what's this? - type TraceContext<'a> = &'a BitwiseOperationLookupChip; + type TraceContext<'a> = F; #[inline(always)] fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { - let adapter_row: &mut NativeLoadStoreAdapterCols = adapter_row.borrow_mut(); + let adapter_row: &mut NativeLoadStoreAdapterCols = adapter_row.borrow_mut(); adapter_row.from_state.pc = F::from_canonical_u32(pc); adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); @@ -216,43 +192,151 @@ where #[inline(always)] fn read( + &self, memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { - todo!("Implement read method"); + let &Instruction { + opcode, + a, + b, + c, + d, + e, + .. + } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), AS::Native as u32); + + let local_opcode = NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + + let adapter_row: &mut NativeLoadStoreAdapterCols = adapter_row.borrow_mut(); + adapter_row.a = a; + adapter_row.b = b; + adapter_row.c = c; + + // Read the pointer value from memory + let [read_cell] = tracing_read_native::( + memory, + c.as_canonical_u32(), + adapter_row.pointer_read_aux_cols.as_mut(), + ); + + let (data_read_as, _) = match local_opcode { + LOADW => (e.as_canonical_u32(), d.as_canonical_u32()), + STOREW | HINT_STOREW => (d.as_canonical_u32(), e.as_canonical_u32()), + }; + + debug_assert_eq!(data_read_as, AS::Native as u32); + + let (data_read_ptr, _) = match local_opcode { + LOADW => ((read_cell + b).as_canonical_u32(), a.as_canonical_u32()), + STOREW | HINT_STOREW => (a.as_canonical_u32(), (read_cell + b).as_canonical_u32()), + }; + + // Read data based on opcode + let data_read: [F; NUM_CELLS] = match local_opcode { + HINT_STOREW => [F::ZERO; NUM_CELLS], + LOADW | STOREW => tracing_read_native::( + memory, + data_read_ptr, + adapter_row.data_read_aux_cols.as_mut(), + ), + }; + + (read_cell, data_read) } #[inline(always)] fn write( + &self, memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, ) { - todo!("Implement write method"); + let &Instruction { + opcode, + a, + b, + c, + d, + e, + .. + } = instruction; + + // TODO(ayush): remove duplication + debug_assert_eq!(d.as_canonical_u32(), AS::Native as u32); + + let local_opcode = NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + + let adapter_row: &mut NativeLoadStoreAdapterCols = adapter_row.borrow_mut(); + + let [read_cell] = memory_read_native::(memory.data(), c.as_canonical_u32()); + + let (_, data_write_as) = match local_opcode { + LOADW => (e.as_canonical_u32(), d.as_canonical_u32()), + STOREW | HINT_STOREW => (d.as_canonical_u32(), e.as_canonical_u32()), + }; + + debug_assert_eq!(data_write_as, AS::Native as u32); + + let data_write_ptr = match local_opcode { + LOADW => a.as_canonical_u32(), + STOREW | HINT_STOREW => (read_cell + b).as_canonical_u32(), + }; + + adapter_row.data_write_pointer = F::from_canonical_u32(data_write_ptr); + + // Write data to memory + tracing_write_native( + memory, + data_write_ptr, + data, + &mut adapter_row.data_write_aux_cols, + ); } #[inline(always)] fn fill_trace_row( + &self, mem_helper: &MemoryAuxColsFactory, - bitwise_lookup_chip: &BitwiseOperationLookupChip, + is_hint_storew: Self::TraceContext<'_>, adapter_row: &mut [F], ) { - todo!("Implement fill_trace_row") + let adapter_row: &mut NativeLoadStoreAdapterCols = adapter_row.borrow_mut(); + let mut timestamp = adapter_row.from_state.timestamp.as_canonical_u32(); + + // Fill auxiliary columns for memory operations + mem_helper.fill_from_prev(timestamp, adapter_row.pointer_read_aux_cols.as_mut()); + timestamp += 1; + + if is_hint_storew.is_zero() { + mem_helper.fill_from_prev(timestamp, adapter_row.data_read_aux_cols.as_mut()); + timestamp += 1; + } + + mem_helper.fill_from_prev(timestamp, adapter_row.data_write_aux_cols.as_mut()); } } -impl AdapterExecutorE1 - for NativeLoadStoreAdapterStep +impl AdapterExecutorE1 for NativeLoadStoreAdapterStep where F: PrimeField32, { type ReadData = (F, [F; NUM_CELLS]); type WriteData = [F; NUM_CELLS]; - fn read(memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { - let Instruction { + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { + let &Instruction { opcode, a, b, @@ -261,40 +345,42 @@ where e, .. } = instruction; - // TODO(ayush): how to handle self.offset? + + debug_assert_eq!(d.as_canonical_u32(), AS::Native as u32); + let local_opcode = NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let [read_cell]: [F; 1] = - unsafe { memory.read(d.as_canonical_u32(), c.as_canonical_u32()) }; + let [read_cell]: [F; 1] = memory_read_native_from_state(state, c.as_canonical_u32()); - let (data_read_as, data_write_as) = { - match local_opcode { - LOADW => (e, d), - STOREW | HINT_STOREW => (d, e), - } + let data_read_as = match local_opcode { + LOADW => e.as_canonical_u32(), + STOREW | HINT_STOREW => d.as_canonical_u32(), }; - let (data_read_ptr, data_write_ptr) = { - match local_opcode { - LOADW => (read_cell + b, a), - STOREW | HINT_STOREW => (a, read_cell + b), - } + + debug_assert_eq!(data_read_as, AS::Native as u32); + + let data_read_ptr = match local_opcode { + LOADW => (read_cell + b).as_canonical_u32(), + STOREW | HINT_STOREW => a.as_canonical_u32(), }; let data_read: [F; NUM_CELLS] = match local_opcode { HINT_STOREW => [F::ZERO; NUM_CELLS], - LOADW | STOREW => unsafe { - memory.read( - data_read_as.as_canonical_u32(), - data_read_ptr.as_canonical_u32(), - ) - }, + LOADW | STOREW => memory_read_native_from_state(state, data_read_ptr), }; (read_cell, data_read) } - fn write(memory: &mut GuestMemory, instruction: &Instruction, data: &Self::WriteData) { - let Instruction { + fn write( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + data: &Self::WriteData, + ) where + Ctx: E1E2ExecutionCtx, + { + let &Instruction { opcode, a, b, @@ -303,157 +389,25 @@ where e, .. } = instruction; - // TODO(ayush): how to handle self.offset? + + debug_assert_eq!(d.as_canonical_u32(), AS::Native as u32); + let local_opcode = NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let [read_cell]: [F; 1] = - unsafe { memory.read(d.as_canonical_u32(), c.as_canonical_u32()) }; + let [read_cell]: [F; 1] = memory_read_native(state.memory, c.as_canonical_u32()); - let (data_read_as, data_write_as) = { - match local_opcode { - LOADW => (e, d), - STOREW | HINT_STOREW => (d, e), - } - }; - let (data_read_ptr, data_write_ptr) = { - match local_opcode { - LOADW => (read_cell + b, a), - STOREW | HINT_STOREW => (a, read_cell + b), - } + let data_write_as = match local_opcode { + LOADW => d.as_canonical_u32(), + STOREW | HINT_STOREW => e.as_canonical_u32(), }; - unsafe { - memory.write( - data_write_as.as_canonical_u32(), - data_write_ptr.as_cas_canonical_u32(), - &data, - ) + debug_assert_eq!(data_write_as, AS::Native as u32); + + let data_write_ptr = match local_opcode { + LOADW => a.as_canonical_u32(), + STOREW | HINT_STOREW => (read_cell + b).as_canonical_u32(), }; + + memory_write_native_from_state(state, data_write_ptr, data); } } - -// impl VmAdapterChip -// for NativeLoadStoreAdapterChip -// { -// type ReadRecord = NativeLoadStoreReadRecord; -// type WriteRecord = NativeLoadStoreWriteRecord; -// type Air = NativeLoadStoreAdapterAir; -// type Interface = NativeLoadStoreAdapterInterface; - -// fn preprocess( -// &mut self, -// memory: &mut MemoryController, -// instruction: &Instruction, -// ) -> Result<( -// >::Reads, -// Self::ReadRecord, -// )> { -// let Instruction { -// opcode, -// a, -// b, -// c, -// d, -// e, -// .. -// } = *instruction; -// let local_opcode = -// NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - -// let read_as = d; -// let read_ptr = c; -// let read_cell = memory.read_cell(read_as, read_ptr); - -// let (data_read_as, data_write_as) = { -// match local_opcode { -// LOADW => (e, d), -// STOREW | HINT_STOREW => (d, e), -// } -// }; -// let (data_read_ptr, data_write_ptr) = { -// match local_opcode { -// LOADW => (read_cell.1 + b, a), -// STOREW | HINT_STOREW => (a, read_cell.1 + b), -// } -// }; - -// let data_read = match local_opcode { -// HINT_STOREW => None, -// LOADW | STOREW => Some(memory.read::(data_read_as, data_read_ptr)), -// }; -// let record = NativeLoadStoreReadRecord { -// pointer_read: read_cell.0, -// data_read: data_read.map(|x| x.0), -// write_as: data_write_as, -// write_ptr: data_write_ptr, -// a, -// b, -// c, -// d, -// e, -// }; - -// Ok(( -// (read_cell.1, data_read.map_or([F::ZERO; NUM_CELLS], |x| x.1)), -// record, -// )) -// } - -// fn postprocess( -// &mut self, -// memory: &mut MemoryController, -// _instruction: &Instruction, -// from_state: ExecutionState, -// output: AdapterRuntimeContext, -// read_record: &Self::ReadRecord, -// ) -> Result<(ExecutionState, Self::WriteRecord)> { -// let (write_id, _) = memory.write::( -// read_record.write_as, -// read_record.write_ptr, -// &output.writes, -// ); -// Ok(( -// ExecutionState { -// pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), -// timestamp: memory.timestamp(), -// }, -// Self::WriteRecord { -// from_state: from_state.map(F::from_canonical_u32), -// write_id, -// }, -// )) -// } - -// fn generate_trace_row( -// &self, -// row_slice: &mut [F], -// read_record: Self::ReadRecord, -// write_record: Self::WriteRecord, -// memory: &OfflineMemory, -// ) { -// let aux_cols_factory = memory.aux_cols_factory(); -// let cols: &mut NativeLoadStoreAdapterCols<_, NUM_CELLS> = row_slice.borrow_mut(); -// cols.from_state = write_record.from_state; -// cols.a = read_record.a; -// cols.b = read_record.b; -// cols.c = read_record.c; - -// let data_read = read_record.data_read.map(|read| memory.record_by_id(read)); -// if let Some(data_read) = data_read { -// aux_cols_factory.generate_read_aux(data_read, &mut cols.data_read_aux_cols); -// } - -// let write = memory.record_by_id(write_record.write_id); -// cols.data_write_pointer = write.pointer; - -// aux_cols_factory.generate_read_aux( -// memory.record_by_id(read_record.pointer_read), -// &mut cols.pointer_read_aux_cols, -// ); -// aux_cols_factory.generate_write_aux(write, &mut cols.data_write_aux_cols); -// } - -// fn air(&self) -> &Self::Air { -// &self.air -// } -// } diff --git a/extensions/native/circuit/src/adapters/mod.rs b/extensions/native/circuit/src/adapters/mod.rs index 18525af962..6041f5861c 100644 --- a/extensions/native/circuit/src/adapters/mod.rs +++ b/extensions/native/circuit/src/adapters/mod.rs @@ -1,6 +1,9 @@ -use openvm_circuit::system::memory::{ - offline_checker::{MemoryReadAuxCols, MemoryWriteAuxCols}, - online::TracingMemory, +use openvm_circuit::{ + arch::{execution_mode::E1E2ExecutionCtx, VmStateMut}, + system::memory::{ + offline_checker::{MemoryBaseAuxCols, MemoryReadOrImmediateAuxCols, MemoryWriteAuxCols}, + online::{GuestMemory, TracingMemory}, + }, }; use openvm_native_compiler::conversion::AS; use openvm_stark_backend::p3_field::PrimeField32; @@ -14,11 +17,96 @@ pub mod loadstore_native_adapter; // 2 reads, 1 write, read size = write size = N, no imm support, read/write to address space d pub mod native_vectorized_adapter; +#[inline(always)] +pub fn memory_read_native(memory: &GuestMemory, ptr: u32) -> [F; N] +where + F: PrimeField32, +{ + // SAFETY: + // - address space `AS::Native` will always have cell type `F` and minimum alignment of `1` + unsafe { memory.read::(AS::Native as u32, ptr) } +} + +#[inline(always)] +pub fn memory_read_or_imm_native(memory: &GuestMemory, addr_space: u32, ptr_or_imm: F) -> F +where + F: PrimeField32, +{ + debug_assert!(addr_space == AS::Immediate as u32 || addr_space == AS::Native as u32); + + if addr_space == AS::Native as u32 { + let [result]: [F; 1] = memory_read_native(memory, ptr_or_imm.as_canonical_u32()); + result + } else { + ptr_or_imm + } +} +#[inline(always)] +pub fn memory_write_native(memory: &mut GuestMemory, ptr: u32, data: &[F; N]) +where + F: PrimeField32, +{ + // SAFETY: + // - address space `AS::Native` will always have cell type `F` and minimum alignment of `1` + unsafe { memory.write::(AS::Native as u32, ptr, data) } +} + +#[inline(always)] +pub fn memory_read_native_from_state( + state: &mut VmStateMut, + ptr: u32, +) -> [F; N] +where + F: PrimeField32, + Ctx: E1E2ExecutionCtx, +{ + state + .ctx + .on_memory_operation(AS::Native as u32, ptr, N as u32); + + memory_read_native(state.memory, ptr) +} + +#[inline(always)] +pub fn memory_read_or_imm_native_from_state( + state: &mut VmStateMut, + addr_space: u32, + ptr_or_imm: F, +) -> F +where + F: PrimeField32, + Ctx: E1E2ExecutionCtx, +{ + debug_assert!(addr_space == AS::Immediate as u32 || addr_space == AS::Native as u32); + + if addr_space == AS::Native as u32 { + let [result]: [F; 1] = memory_read_native_from_state(state, ptr_or_imm.as_canonical_u32()); + result + } else { + ptr_or_imm + } +} + +#[inline(always)] +pub fn memory_write_native_from_state( + state: &mut VmStateMut, + ptr: u32, + data: &[F; N], +) where + F: PrimeField32, + Ctx: E1E2ExecutionCtx, +{ + state + .ctx + .on_memory_operation(AS::Native as u32, ptr, N as u32); + + memory_write_native(state.memory, ptr, data) +} /// Atomic read operation which increments the timestamp by 1. /// Returns `(t_prev, [ptr:BLOCK_SIZE]_4)` where `t_prev` is the timestamp of the last memory /// access. #[inline(always)] -pub fn timed_read( +fn timed_read( memory: &mut TracingMemory, ptr: u32, ) -> (u32, [F; BLOCK_SIZE]) @@ -27,11 +115,11 @@ where { // SAFETY: // - address space `Native` will always have cell type `F` and minimum alignment of `1` - unsafe { memory.read::(AS::Native, ptr) } + unsafe { memory.read::(AS::Native as u32, ptr) } } #[inline(always)] -pub fn timed_write( +fn timed_write( memory: &mut TracingMemory, ptr: u32, vals: &[F; BLOCK_SIZE], @@ -41,22 +129,21 @@ where { // SAFETY: // - address space `Native` will always have cell type `F` and minimum alignment of `1` - unsafe { memory.write::(AS::Native, ptr, vals) } + unsafe { memory.write::(AS::Native as u32, ptr, vals) } } /// Reads register value at `ptr` from memory and records the memory access in mutable buffer. /// Trace generation relevant to this memory access can be done fully from the recorded buffer. #[inline(always)] -pub fn tracing_read( +pub fn tracing_read_native( memory: &mut TracingMemory, ptr: u32, - (ptr_mut, aux_cols): (&mut F, &mut MemoryReadAuxCols), + aux_cols: &mut MemoryBaseAuxCols, ) -> [F; BLOCK_SIZE] where F: PrimeField32, { let (t_prev, data) = timed_read(memory, ptr); - *ptr_mut = F::from_canonical_u32(ptr); aux_cols.set_prev(F::from_canonical_u32(t_prev)); data } @@ -64,50 +151,41 @@ where /// Writes `ptr, vals` into memory and records the memory access in mutable buffer. /// Trace generation relevant to this memory access can be done fully from the recorded buffer. #[inline(always)] -pub fn tracing_write( +pub fn tracing_write_native( memory: &mut TracingMemory, ptr: u32, vals: &[F; BLOCK_SIZE], - (ptr_mut, aux_cols): (&mut F, &mut MemoryWriteAuxCols), + aux_cols: &mut MemoryWriteAuxCols, ) where F: PrimeField32, { let (t_prev, data_prev) = timed_write(memory, ptr, vals); - *ptr_mut = F::from_canonical_u32(ptr); aux_cols.set_prev(F::from_canonical_u32(t_prev), data_prev); } /// Reads value at `_ptr` from memory and records the memory access in mutable buffer. /// Trace generation relevant to this memory access can be done fully from the recorded buffer. -/// -/// Assumes that `addr_space` is [Immediate] or [Native]. #[inline(always)] -pub fn tracing_read_or_imm( +pub fn tracing_read_or_imm_native( memory: &mut TracingMemory, addr_space: u32, - ptr_or_imm: u32, + ptr_or_imm: F, addr_space_mut: &mut F, - (ptr_or_imm_mut, aux_cols): (&mut F, &mut MemoryReadAuxCols), + aux_cols: &mut MemoryReadOrImmediateAuxCols, ) -> F where F: PrimeField32, { - debug_assert!(addr_space == AS::Immediate || addr_space == AS::Native); + debug_assert!(addr_space == AS::Immediate as u32 || addr_space == AS::Native as u32); - if addr_space == AS::Immediate { - // TODO(ayush): check this + if addr_space == AS::Immediate as u32 { *addr_space_mut = F::ZERO; - let imm = ptr_or_imm; - *ptr_or_imm_mut = F::from_canonical_u32(imm); - debug_assert_eq!(imm >> 24, 0); // highest byte should be zero to prevent overflow memory.increment_timestamp(); - let mut imm_le = imm.to_le_bytes(); - // Important: we set the highest byte equal to the second highest byte, using the assumption - // that imm is at most 24 bits - imm_le[3] = imm_le[2]; - imm_le + ptr_or_imm } else { - *addr_space_mut = F::from_canonical_u32(AS::Native); - tracing_read(memory, ptr_or_imm, (ptr_or_imm_mut, aux_cols)) + *addr_space_mut = F::from_canonical_u32(AS::Native as u32); + let data: [F; 1] = + tracing_read_native(memory, ptr_or_imm.as_canonical_u32(), &mut aux_cols.base); + data[0] } } diff --git a/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs b/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs index 21199bed81..bf57a3b4e6 100644 --- a/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs +++ b/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs @@ -1,20 +1,18 @@ use std::{ borrow::{Borrow, BorrowMut}, - marker::PhantomData, + mem::size_of, }; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, AdapterTraceStep, - BasicAdapterInterface, ExecutionBridge, ExecutionBus, ExecutionState, MinimalInstruction, - Result, VmAdapterAir, VmAdapterChip, VmAdapterInterface, + execution_mode::E1E2ExecutionCtx, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, + BasicAdapterInterface, ExecutionBridge, ExecutionState, MinimalInstruction, VmAdapterAir, + VmStateMut, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, - MemoryAddress, MemoryController, OfflineMemory, RecordId, - }, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, + online::{GuestMemory, TracingMemory}, + MemoryAddress, MemoryAuxColsFactory, }, }; use openvm_circuit_primitives_derive::AlignedBorrow; @@ -25,21 +23,11 @@ use openvm_stark_backend::{ p3_air::BaseAir, p3_field::{Field, FieldAlgebra, PrimeField32}, }; -use serde::{Deserialize, Serialize}; -#[repr(C)] -#[derive(Debug, Serialize, Deserialize)] -pub struct NativeVectorizedReadRecord { - pub b: RecordId, - pub c: RecordId, -} - -#[repr(C)] -#[derive(Debug, Serialize, Deserialize)] -pub struct NativeVectorizedWriteRecord { - pub from_state: ExecutionState, - pub a: RecordId, -} +use super::{ + memory_read_native_from_state, memory_write_native_from_state, tracing_read_native, + tracing_write_native, +}; #[repr(C)] #[derive(AlignedBorrow)] @@ -134,51 +122,50 @@ impl VmAdapterAir for NativeVectoriz } #[derive(derive_new::new)] -pub struct NativeVectorizedAdapterChip; +pub struct NativeVectorizedAdapterStep; -impl AdapterTraceStep for NativeVectorizedAdapterChip +impl AdapterTraceStep for NativeVectorizedAdapterStep where F: PrimeField32, { - const WIDTH: usize = size_of::>(); + const WIDTH: usize = size_of::>(); type ReadData = [[F; N]; 2]; type WriteData = [F; N]; - // TODO(ayush): what's this? - type TraceContext<'a> = &'a BitwiseOperationLookupChip; + type TraceContext<'a> = (); #[inline(always)] fn start(pc: u32, memory: &TracingMemory, adapter_row: &mut [F]) { - let adapter_row: &mut NativeVectorizedAdapterCols = adapter_row.borrow_mut(); + let adapter_row: &mut NativeVectorizedAdapterCols = adapter_row.borrow_mut(); adapter_row.from_state.pc = F::from_canonical_u32(pc); - adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp); + adapter_row.from_state.timestamp = F::from_canonical_u32(memory.timestamp()); } #[inline(always)] fn read( + &self, memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { - let Instruction { b, c, e, f, .. } = instruction; - let adapter_row: &mut NativeVectorizedAdapterCols = adapter_row.borrow_mut(); + let &Instruction { b, c, d, e, .. } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), AS::Native as u32); + debug_assert_eq!(e.as_canonical_u32(), AS::Native as u32); - // TODO(ayush): create similar `tracing_read_reg_or_imm` for F - let y_val = tracing_read_reg_or_imm( + let adapter_row: &mut NativeVectorizedAdapterCols = adapter_row.borrow_mut(); + + adapter_row.b_pointer = b; + let y_val = tracing_read_native( memory, - d.as_canonical_u32(), b.as_canonical_u32(), - // TODO(ayush): why no address space pointer? Should this be hardcoded? - &mut adapter_row.d_as, - (&mut adapter_row.b_pointer, &mut adapter_row.reads_aux[0]), + adapter_row.reads_aux[0].as_mut(), ); - let z_val = tracing_read_reg_or_imm( + adapter_row.c_pointer = c; + let z_val = tracing_read_native( memory, - e.as_canonical_u32(), c.as_canonical_u32(), - // TODO(ayush): why no address space pointer? Should this be hardcoded? - &mut adapter_row.e_as, - (&mut adapter_row.c_pointer, &mut adapter_row.reads_aux[1]), + adapter_row.reads_aux[1].as_mut(), ); [y_val, z_val] @@ -186,132 +173,88 @@ where #[inline(always)] fn write( + &self, memory: &mut TracingMemory, instruction: &Instruction, adapter_row: &mut [F], data: &Self::WriteData, ) { - let Instruction { a, d, .. } = instruction; - let adapter_row: &mut NativeVectorizedAdapterCols = adapter_row.borrow_mut(); + let &Instruction { a, d, .. } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), AS::Native as u32); - // TODO(ayush): create similar `tracing_read_reg_or_imm` for F - tracing_write_reg( + let adapter_row: &mut NativeVectorizedAdapterCols = adapter_row.borrow_mut(); + + adapter_row.a_pointer = a; + tracing_write_native( memory, - d.as_canonical_u32(), a.as_canonical_u32(), data, - (&mut adapter_row.a_pointer, &mut adapter_row.writes_aux[0]), + &mut adapter_row.writes_aux[0], ); } #[inline(always)] fn fill_trace_row( + &self, mem_helper: &MemoryAuxColsFactory, - bitwise_lookup_chip: &BitwiseOperationLookupChip, + _ctx: Self::TraceContext<'_>, adapter_row: &mut [F], ) { - todo!("Implement fill_trace_row") + let adapter_row: &mut NativeVectorizedAdapterCols = adapter_row.borrow_mut(); + + let mut timestamp = adapter_row.from_state.timestamp.as_canonical_u32(); + + mem_helper.fill_from_prev(timestamp, adapter_row.reads_aux[0].as_mut()); + timestamp += 1; + + mem_helper.fill_from_prev(timestamp, adapter_row.reads_aux[1].as_mut()); + timestamp += 1; + + mem_helper.fill_from_prev(timestamp, adapter_row.writes_aux[0].as_mut()); } } -impl AdapterExecutorE1 for NativeVectorizedAdapterChip +impl AdapterExecutorE1 for NativeVectorizedAdapterStep where F: PrimeField32, { - type ReadData = ([F; N], [F; N]); + type ReadData = [[F; N]; 2]; type WriteData = [F; N]; - fn read(memory: &mut GuestMemory, instruction: &Instruction) -> Self::ReadData { + #[inline(always)] + fn read( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Self::ReadData + where + Ctx: E1E2ExecutionCtx, + { let Instruction { b, c, d, e, .. } = instruction; - let y_val: [F; N] = unsafe { memory.read(d.as_canonical_u32(), b.as_cas_canonical_u32()) }; - let z_val: [F; N] = - unsafe { memory.read(e.as_cas_canonical_u32(), c.as_cas_canonical_u32()) }; + debug_assert_eq!(d.as_canonical_u32(), AS::Native as u32); + debug_assert_eq!(e.as_canonical_u32(), AS::Native as u32); + + let y_val: [F; N] = memory_read_native_from_state(state, b.as_canonical_u32()); + let z_val: [F; N] = memory_read_native_from_state(state, c.as_canonical_u32()); - (y_val, z_val) + [y_val, z_val] } - fn write(memory: &mut GuestMemory, instruction: &Instruction, data: &Self::WriteData) { + #[inline(always)] + fn write( + &self, + state: &mut VmStateMut, + instruction: &Instruction, + data: &Self::WriteData, + ) where + Ctx: E1E2ExecutionCtx, + { let Instruction { a, d, .. } = instruction; - unsafe { memory.write(d.as_canonical_u32(), a.as_cas_canonical_u32(), &data) }; + debug_assert_eq!(d.as_canonical_u32(), AS::Native as u32); + + memory_write_native_from_state(state, a.as_canonical_u32(), data); } } - -// impl VmAdapterChip for NativeVectorizedAdapterChip { -// type ReadRecord = NativeVectorizedReadRecord; -// type WriteRecord = NativeVectorizedWriteRecord; -// type Air = NativeVectorizedAdapterAir; -// type Interface = BasicAdapterInterface, 2, 1, N, N>; - -// fn preprocess( -// &mut self, -// memory: &mut MemoryController, -// instruction: &Instruction, -// ) -> Result<( -// >::Reads, -// Self::ReadRecord, -// )> { -// let Instruction { b, c, d, e, .. } = *instruction; - -// let y_val = memory.read::(d, b); -// let z_val = memory.read::(e, c); - -// Ok(( -// [y_val.1, z_val.1], -// Self::ReadRecord { -// b: y_val.0, -// c: z_val.0, -// }, -// )) -// } - -// fn postprocess( -// &mut self, -// memory: &mut MemoryController, -// instruction: &Instruction, -// from_state: ExecutionState, -// output: AdapterRuntimeContext, -// _read_record: &Self::ReadRecord, -// ) -> Result<(ExecutionState, Self::WriteRecord)> { -// let Instruction { a, d, .. } = *instruction; -// let (a_val, _) = memory.write(d, a, &output.writes[0]); - -// Ok(( -// ExecutionState { -// pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), -// timestamp: memory.timestamp(), -// }, -// Self::WriteRecord { -// from_state, -// a: a_val, -// }, -// )) -// } - -// fn generate_trace_row( -// &self, -// row_slice: &mut [F], -// read_record: Self::ReadRecord, -// write_record: Self::WriteRecord, -// memory: &OfflineMemory, -// ) { -// let aux_cols_factory = memory.aux_cols_factory(); -// let row_slice: &mut NativeVectorizedAdapterCols<_, N> = row_slice.borrow_mut(); - -// let b_record = memory.record_by_id(read_record.b); -// let c_record = memory.record_by_id(read_record.c); -// let a_record = memory.record_by_id(write_record.a); -// row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); -// row_slice.a_pointer = a_record.pointer; -// row_slice.b_pointer = b_record.pointer; -// row_slice.c_pointer = c_record.pointer; -// aux_cols_factory.generate_read_aux(b_record, &mut row_slice.reads_aux[0]); -// aux_cols_factory.generate_read_aux(c_record, &mut row_slice.reads_aux[1]); -// aux_cols_factory.generate_write_aux(a_record, &mut row_slice.writes_aux[0]); -// } - -// fn air(&self) -> &Self::Air { -// &self.air -// } -// } diff --git a/extensions/native/circuit/src/branch_eq/core.rs b/extensions/native/circuit/src/branch_eq/core.rs new file mode 100644 index 0000000000..d1b9359815 --- /dev/null +++ b/extensions/native/circuit/src/branch_eq/core.rs @@ -0,0 +1,168 @@ +use std::{array, borrow::BorrowMut}; + +use openvm_circuit::{ + arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, + AdapterExecutorE1, AdapterTraceStep, Result, StepExecutorE1, TraceStep, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, + }, +}; +use openvm_instructions::{instruction::Instruction, LocalOpcode}; +use openvm_native_compiler::NativeBranchEqualOpcode; +use openvm_rv32im_circuit::BranchEqualCoreCols; +use openvm_rv32im_transpiler::BranchEqualOpcode; +use openvm_stark_backend::p3_field::PrimeField32; + +pub struct NativeBranchEqualStep { + adapter: A, + pub offset: usize, + pub pc_step: u32, +} + +impl NativeBranchEqualStep { + pub fn new(adapter: A, offset: usize, pc_step: u32) -> Self { + Self { + adapter, + offset, + pc_step, + } + } +} + +impl TraceStep for NativeBranchEqualStep +where + F: PrimeField32, + A: 'static + + for<'a> AdapterTraceStep< + F, + CTX, + ReadData: Into<[F; 2]>, + WriteData = (), + TraceContext<'a> = (), + >, +{ + fn get_opcode_name(&self, opcode: usize) -> String { + format!( + "{:?}", + NativeBranchEqualOpcode::from_usize(opcode - self.offset) + ) + } + + fn execute( + &mut self, + state: VmStateMut, CTX>, + instruction: &Instruction, + trace: &mut [F], + trace_offset: &mut usize, + width: usize, + ) -> Result<()> { + let &Instruction { opcode, c: imm, .. } = instruction; + + let branch_eq_opcode = + NativeBranchEqualOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + A::start(*state.pc, state.memory, adapter_row); + + let [rs1, rs2] = self + .adapter + .read(state.memory, instruction, adapter_row) + .into(); + + let (cmp_result, diff_idx, diff_inv_val) = run_eq(branch_eq_opcode, rs1, rs2); + + let core_row: &mut BranchEqualCoreCols<_, 1> = core_row.borrow_mut(); + core_row.a = [rs1]; + core_row.b = [rs2]; + core_row.cmp_result = F::from_bool(cmp_result); + core_row.imm = imm; + core_row.opcode_beq_flag = F::from_bool(branch_eq_opcode.0 == BranchEqualOpcode::BEQ); + core_row.opcode_bne_flag = F::from_bool(branch_eq_opcode.0 == BranchEqualOpcode::BNE); + core_row.diff_inv_marker = + array::from_fn(|i| if i == diff_idx { diff_inv_val } else { F::ZERO }); + + if cmp_result { + *state.pc = (F::from_canonical_u32(*state.pc) + imm).as_canonical_u32(); + } else { + *state.pc = state.pc.wrapping_add(self.pc_step); + } + + *trace_offset += width; + + Ok(()) + } + + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, _core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + self.adapter.fill_trace_row(mem_helper, (), adapter_row); + } +} + +impl StepExecutorE1 for NativeBranchEqualStep +where + F: PrimeField32, + A: 'static + for<'a> AdapterExecutorE1, WriteData = ()>, +{ + fn execute_e1( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { + let &Instruction { opcode, c: imm, .. } = instruction; + + let branch_eq_opcode = + NativeBranchEqualOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + + let [rs1, rs2] = self.adapter.read(state, instruction).into(); + + // TODO(ayush): probably don't need the other values + let (cmp_result, _, _) = run_eq::(branch_eq_opcode, rs1, rs2); + + if cmp_result { + // TODO(ayush): verify this is fine + // state.pc = state.pc.wrapping_add(imm.as_canonical_u32()); + *state.pc = (F::from_canonical_u32(*state.pc) + imm).as_canonical_u32(); + } else { + *state.pc = state.pc.wrapping_add(self.pc_step); + } + + Ok(()) + } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; + + Ok(()) + } +} + +// Returns (cmp_result, diff_idx, x[diff_idx] - y[diff_idx]) +#[inline(always)] +pub(super) fn run_eq(local_opcode: NativeBranchEqualOpcode, x: F, y: F) -> (bool, usize, F) +where + F: PrimeField32, +{ + if x != y { + return ( + local_opcode.0 == BranchEqualOpcode::BNE, + 0, + (x - y).inverse(), + ); + } + (local_opcode.0 == BranchEqualOpcode::BEQ, 0, F::ZERO) +} diff --git a/extensions/native/circuit/src/branch_eq/mod.rs b/extensions/native/circuit/src/branch_eq/mod.rs index 265f45b549..272f83289e 100644 --- a/extensions/native/circuit/src/branch_eq/mod.rs +++ b/extensions/native/circuit/src/branch_eq/mod.rs @@ -1,9 +1,12 @@ -use openvm_circuit::arch::{VmAirWrapper, VmChipWrapper}; -use openvm_rv32im_circuit::{BranchEqualCoreAir, BranchEqualCoreChip}; +pub mod core; -use super::adapters::branch_native_adapter::{BranchNativeAdapterAir, BranchNativeAdapterChip}; +use core::NativeBranchEqualStep; + +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; +use openvm_rv32im_circuit::BranchEqualCoreAir; + +use crate::adapters::branch_native_adapter::{BranchNativeAdapterAir, BranchNativeAdapterStep}; pub type NativeBranchEqAir = VmAirWrapper>; -pub type NativeBranchEqualStep = BranchEqualStep; -pub type NativeBranchEqualChip = - NewVmChipWrapper; +pub type NativeBranchEqStep = NativeBranchEqualStep; +pub type NativeBranchEqChip = NewVmChipWrapper; diff --git a/extensions/native/circuit/src/castf/core.rs b/extensions/native/circuit/src/castf/core.rs index 099b475869..0c096b402a 100644 --- a/extensions/native/circuit/src/castf/core.rs +++ b/extensions/native/circuit/src/castf/core.rs @@ -2,11 +2,14 @@ use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, AdapterTraceStep, - InsExecutorE1, MinimalInstruction, Result, StepExecutorE1, VmAdapterInterface, VmCoreAir, - VmCoreChip, VmExecutionState, + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, + StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::online::GuestMemory, }; use openvm_circuit_primitives::var_range::{ SharedVariableRangeCheckerChip, VariableRangeCheckerBus, @@ -21,7 +24,6 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, rap::BaseAirWithPublicValues, }; -use serde::{Deserialize, Serialize}; // LIMB_BITS is the size of the limbs in bits. pub(crate) const LIMB_BITS: usize = 8; @@ -36,7 +38,7 @@ pub struct CastFCoreCols { pub is_valid: T, } -#[derive(Copy, Clone, Debug)] +#[derive(derive_new::new, Copy, Clone, Debug)] pub struct CastFCoreAir { pub bus: VariableRangeCheckerBus, /* to communicate with the range checker that checks that * all limbs are < 2^LIMB_BITS */ @@ -108,59 +110,22 @@ where } } -#[repr(C)] -#[derive(Debug, Serialize, Deserialize)] -pub struct CastFRecord { - pub in_val: F, - pub out_val: [u32; RV32_REGISTER_NUM_LIMBS], -} - -pub struct CastFStep { - offset: usize, +#[derive(derive_new::new)] +pub struct CastFCoreStep { + adapter: A, pub range_checker_chip: SharedVariableRangeCheckerChip, - phantom: PhantomData, -} - -impl CastFStep { - pub fn new(range_checker_chip: SharedVariableRangeCheckerChip, offset: usize) -> Self { - Self { - offset, - range_checker_chip, - phantom: PhantomData, - } - } - - #[inline] - pub fn execute_trace_core( - &self, - instruction: &Instruction, - [x, y]: [[u8; NUM_LIMBS]; 2], - core_row: &mut [F], - ) -> [u8; NUM_LIMBS] - where - F: PrimeField32, - { - todo!("Implement execute_trace_core") - } - - pub fn fill_trace_row_core(&self, core_row: &mut [F]) - where - F: PrimeField32, - { - todo!("Implement fill_trace_row_core") - } } -impl TraceStep for CastFStep +impl TraceStep for CastFCoreStep where F: PrimeField32, A: 'static + for<'a> AdapterTraceStep< F, CTX, - ReadData = [[u8; NUM_LIMBS]; 2], - WriteData = [u8; NUM_LIMBS], - TraceContext<'a> = &'a BitwiseOperationLookupChip, + ReadData = [F; 1], + WriteData = [u8; RV32_REGISTER_NUM_LIMBS], + TraceContext<'a> = (), >, { fn get_opcode_name(&self, _opcode: usize) -> String { @@ -171,119 +136,113 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { - todo!("Implement execute") + let Instruction { opcode, .. } = instruction; + + assert_eq!( + opcode.local_opcode_idx(CastfOpcode::CLASS_OFFSET), + CastfOpcode::CASTF as usize + ); + + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + A::start(*state.pc, state.memory, adapter_row); + + let [y] = self.adapter.read(state.memory, instruction, adapter_row); + + let x = CastF::solve(y.as_canonical_u32()); + + let core_row: &mut CastFCoreCols = core_row.borrow_mut(); + core_row.in_val = y; + core_row.out_val = x.map(F::from_canonical_u8); + core_row.is_valid = F::ONE; + + self.adapter + .write(state.memory, instruction, adapter_row, &x); + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + *trace_offset += width; + + Ok(()) } fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { - todo!("Implement fill_trace_row") + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + self.adapter.fill_trace_row(mem_helper, (), adapter_row); + + let core_row: &mut CastFCoreCols = core_row.borrow_mut(); + + if core_row.is_valid == F::ONE { + for (i, limb) in core_row.out_val.iter().enumerate() { + if i == 3 { + self.range_checker_chip + .add_count(limb.as_canonical_u32(), FINAL_LIMB_BITS); + } else { + self.range_checker_chip + .add_count(limb.as_canonical_u32(), LIMB_BITS); + } + } + } } } -impl StepExecutorE1 for CastFStep +impl StepExecutorE1 for CastFCoreStep where F: PrimeField32, - A: 'static + for<'a> AdapterExecutorE1, + A: 'static + + for<'a> AdapterExecutorE1, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, a, b, d, e, .. - } = instruction; + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { + let Instruction { opcode, .. } = instruction; assert_eq!( opcode.local_opcode_idx(CastfOpcode::CLASS_OFFSET), CastfOpcode::CASTF as usize ); - // TODO(ayush): check if can be read directly as [u8; 4] or u32? - let [y] = A::read(&mut state.memory, instruction); + let [y] = self.adapter.read(state, instruction); let x = CastF::solve(y.as_canonical_u32()); - let x = x.map(F::from_canonical_u32); - A::write(&mut state.memory, instruction, x); + self.adapter.write(state, instruction, &x); - state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } -} -// impl VmCoreChip for CastFCoreChip -// where -// F: PrimeField32, -// I: VmAdapterInterface, -// I::Reads: Into<[[F; 1]; 1]>, -// I::Writes: From<[[F; RV32_REGISTER_NUM_LIMBS]; 1]>, -// { -// type Record = CastFRecord; -// type Air = CastFCoreAir; - -// #[allow(clippy::type_complexity)] -// fn execute_instruction( -// &self, -// instruction: &Instruction, -// _from_pc: u32, -// reads: I::Reads, -// ) -> Result<(AdapterRuntimeContext, Self::Record)> { -// let Instruction { opcode, .. } = instruction; - -// assert_eq!( -// opcode.local_opcode_idx(CastfOpcode::CLASS_OFFSET), -// CastfOpcode::CASTF as usize -// ); - -// let y = reads.into()[0][0]; -// let x = CastF::solve(y.as_canonical_u32()); - -// let output = AdapterRuntimeContext { -// to_pc: None, -// writes: [x.map(F::from_canonical_u32)].into(), -// }; - -// let record = CastFRecord { -// in_val: y, -// out_val: x, -// }; - -// Ok((output, record)) -// } - -// fn get_opcode_name(&self, _opcode: usize) -> String { -// format!("{:?}", CastfOpcode::CASTF) -// } - -// fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { -// for (i, limb) in record.out_val.iter().enumerate() { -// if i == 3 { -// self.range_checker_chip.add_count(*limb, FINAL_LIMB_BITS); -// } else { -// self.range_checker_chip.add_count(*limb, LIMB_BITS); -// } -// } - -// let cols: &mut CastFCoreCols = row_slice.borrow_mut(); -// cols.in_val = record.in_val; -// cols.out_val = record.out_val.map(F::from_canonical_u32); -// cols.is_valid = F::ONE; -// } - -// fn air(&self) -> &Self::Air { -// &self.air -// } -// } + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; + + Ok(()) + } +} pub struct CastF; impl CastF { - pub(super) fn solve(y: u32) -> [u32; RV32_REGISTER_NUM_LIMBS] { - let mut x = [0; 4]; + pub(super) fn solve(y: u32) -> [u8; RV32_REGISTER_NUM_LIMBS] { + let mut x = [0u8; RV32_REGISTER_NUM_LIMBS]; for (i, limb) in x.iter_mut().enumerate() { - *limb = (y >> (8 * i)) & 0xFF; + *limb = ((y >> (8 * i)) & 0xFF) as u8; } x } diff --git a/extensions/native/circuit/src/castf/mod.rs b/extensions/native/circuit/src/castf/mod.rs index 7a701eb5bf..b7ac2fc266 100644 --- a/extensions/native/circuit/src/castf/mod.rs +++ b/extensions/native/circuit/src/castf/mod.rs @@ -1,4 +1,4 @@ -use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper, VmChipWrapper}; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; use super::adapters::convert_adapter::{ConvertAdapterAir, ConvertAdapterStep}; @@ -9,5 +9,5 @@ mod core; pub use core::*; pub type CastFAir = VmAirWrapper, CastFCoreAir>; -pub type CastFStepWithAdapter = CastFStep>; -pub type CastFChip = NewVmChipWrapper; +pub type CastFStep = CastFCoreStep>; +pub type CastFChip = NewVmChipWrapper; diff --git a/extensions/native/circuit/src/castf/tests.rs b/extensions/native/circuit/src/castf/tests.rs index 9758e6b956..5b816d5945 100644 --- a/extensions/native/circuit/src/castf/tests.rs +++ b/extensions/native/circuit/src/castf/tests.rs @@ -1,6 +1,9 @@ -use std::borrow::BorrowMut; +use std::borrow::{Borrow, BorrowMut}; -use openvm_circuit::arch::testing::{memory::gen_pointer, VmChipTestBuilder}; +use openvm_circuit::arch::{ + testing::{memory::gen_pointer, VmChipTestBuilder}, + VmAirWrapper, +}; use openvm_instructions::{instruction::Instruction, LocalOpcode}; use openvm_native_compiler::CastfOpcode; use openvm_stark_backend::{ @@ -13,11 +16,28 @@ use openvm_stark_sdk::{ use rand::{rngs::StdRng, Rng}; use super::{ - super::adapters::convert_adapter::{ConvertAdapterChip, ConvertAdapterCols}, - CastF, CastFChip, CastFCoreChip, CastFCoreCols, FINAL_LIMB_BITS, LIMB_BITS, + super::adapters::convert_adapter::{ConvertAdapterAir, ConvertAdapterCols, ConvertAdapterStep}, + CastF, CastFChip, CastFCoreAir, CastFCoreCols, CastFStep, FINAL_LIMB_BITS, LIMB_BITS, }; + +const MAX_INS_CAPACITY: usize = 128; type F = BabyBear; +fn create_test_chip(tester: &VmChipTestBuilder) -> CastFChip { + CastFChip::::new( + VmAirWrapper::new( + ConvertAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), + CastFCoreAir::new(tester.range_checker().bus()), + ), + CastFStep::new( + ConvertAdapterStep::<1, 4>::new(), + tester.range_checker().clone(), + ), + MAX_INS_CAPACITY, + tester.memory_helper(), + ) +} + fn generate_uint_number(rng: &mut StdRng) -> u32 { rng.gen_range(0..(1 << 30) - 1) } @@ -37,7 +57,7 @@ fn prepare_castf_rand_write_execute( let operand1_f = F::from_canonical_u32(y); - tester.write_cell(as_y, address_y, operand1_f); + tester.memory.write(as_y, address_y, [operand1_f]); let x = CastF::solve(operand1); tester.execute( @@ -48,7 +68,7 @@ fn prepare_castf_rand_write_execute( ), ); assert_eq!( - x.map(F::from_canonical_u32), + x.map(F::from_canonical_u8), tester.read::<4>(as_x, address_x) ); } @@ -57,15 +77,7 @@ fn prepare_castf_rand_write_execute( fn castf_rand_test() { let mut rng = create_seeded_rng(); let mut tester = VmChipTestBuilder::default(); - let mut chip = CastFChip::::new( - ConvertAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - ), - CastFCoreChip::new(tester.range_checker()), - tester.offline_memory_mutex_arc(), - ); + let mut chip = create_test_chip(&tester); let num_tests: usize = 3; for _ in 0..num_tests { @@ -81,15 +93,7 @@ fn castf_rand_test() { fn negative_castf_overflow_test() { let mut tester = VmChipTestBuilder::default(); let range_checker_chip = tester.range_checker(); - let mut chip = CastFChip::::new( - ConvertAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - ), - CastFCoreChip::new(range_checker_chip.clone()), - tester.offline_memory_mutex_arc(), - ); + let mut chip = create_test_chip(&tester); let mut rng = create_seeded_rng(); let y = generate_uint_number(&mut rng); @@ -125,15 +129,7 @@ fn negative_castf_overflow_test() { fn negative_castf_memread_test() { let mut tester = VmChipTestBuilder::default(); let range_checker_chip = tester.memory_controller().borrow().range_checker.clone(); - let mut chip = CastFChip::::new( - ConvertAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - ), - CastFCoreChip::new(range_checker_chip.clone()), - tester.offline_memory_mutex_arc(), - ); + let mut chip = create_test_chip(&tester); let mut rng = create_seeded_rng(); let y = generate_uint_number(&mut rng); @@ -169,15 +165,7 @@ fn negative_castf_memread_test() { fn negative_castf_memwrite_test() { let mut tester = VmChipTestBuilder::default(); let range_checker_chip = tester.memory_controller().borrow().range_checker.clone(); - let mut chip = CastFChip::::new( - ConvertAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - ), - CastFCoreChip::new(range_checker_chip.clone()), - tester.offline_memory_mutex_arc(), - ); + let mut chip = create_test_chip(&tester); let mut rng = create_seeded_rng(); let y = generate_uint_number(&mut rng); @@ -213,15 +201,7 @@ fn negative_castf_memwrite_test() { fn negative_castf_as_test() { let mut tester = VmChipTestBuilder::default(); let range_checker_chip = tester.memory_controller().borrow().range_checker.clone(); - let mut chip = CastFChip::::new( - ConvertAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - ), - CastFCoreChip::new(range_checker_chip.clone()), - tester.offline_memory_mutex_arc(), - ); + let mut chip = create_test_chip(&tester); let mut rng = create_seeded_rng(); let y = generate_uint_number(&mut rng); diff --git a/extensions/native/circuit/src/extension.rs b/extensions/native/circuit/src/extension.rs index d5ddb7f236..a6a103a18a 100644 --- a/extensions/native/circuit/src/extension.rs +++ b/extensions/native/circuit/src/extension.rs @@ -1,17 +1,19 @@ -use air::VerifyBatchBus; -use alu_native_adapter::AluNativeAdapterChip; -use branch_native_adapter::BranchNativeAdapterChip; +use alu_native_adapter::{AluNativeAdapterAir, AluNativeAdapterStep}; +use branch_native_adapter::{BranchNativeAdapterAir, BranchNativeAdapterStep}; +use convert_adapter::{ConvertAdapterAir, ConvertAdapterStep}; use derive_more::derive::From; -use loadstore_native_adapter::NativeLoadStoreAdapterChip; -use native_vectorized_adapter::NativeVectorizedAdapterChip; +use fri::{FriReducedOpeningAir, FriReducedOpeningChip, FriReducedOpeningStep}; +use jal::{JalRangeCheckAir, JalRangeCheckChip, JalRangeCheckStep}; +use loadstore_native_adapter::{NativeLoadStoreAdapterAir, NativeLoadStoreAdapterStep}; +use native_vectorized_adapter::{NativeVectorizedAdapterAir, NativeVectorizedAdapterStep}; use openvm_circuit::{ arch::{ - ExecutionBridge, MemoryConfig, SystemConfig, SystemPort, VmExtension, VmInventory, - VmInventoryBuilder, VmInventoryError, + ExecutionBridge, MemoryConfig, SystemConfig, SystemPort, VmAirWrapper, VmExtension, + VmInventory, VmInventoryBuilder, VmInventoryError, }, system::phantom::PhantomChip, }; -use openvm_circuit_derive::{AnyEnum, InstructionExecutor, VmConfig}; +use openvm_circuit_derive::{AnyEnum, InsExecutorE1, InstructionExecutor, VmConfig}; use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; use openvm_instructions::{program::DEFAULT_PC_STEP, LocalOpcode, PhantomDiscriminant}; use openvm_native_compiler::{ @@ -21,19 +23,17 @@ use openvm_native_compiler::{ }; use openvm_poseidon2_air::Poseidon2Config; use openvm_rv32im_circuit::{ - BranchEqualCoreChip, Rv32I, Rv32IExecutor, Rv32IPeriphery, Rv32Io, Rv32IoExecutor, + BranchEqualCoreAir, Rv32I, Rv32IExecutor, Rv32IPeriphery, Rv32Io, Rv32IoExecutor, Rv32IoPeriphery, Rv32M, Rv32MExecutor, Rv32MPeriphery, }; use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; -use crate::{ - adapters::{convert_adapter::ConvertAdapterChip, *}, - chip::NativePoseidon2Chip, - phantom::*, - *, -}; +use crate::{adapters::*, phantom::*, *}; + +// TODO(ayush): this should be decided after e2 execution +const MAX_INS_CAPACITY: usize = 1 << 22; #[derive(Clone, Debug, Serialize, Deserialize, VmConfig, derive_new::new)] pub struct NativeConfig { @@ -63,7 +63,7 @@ impl NativeConfig { #[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)] pub struct Native; -#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)] +#[derive(ChipUsageGetter, Chip, InstructionExecutor, InsExecutorE1, From, AnyEnum)] pub enum NativeExecutor { LoadStore(NativeLoadStoreChip), BlockLoadStore(NativeLoadStoreChip), @@ -72,7 +72,7 @@ pub enum NativeExecutor { FieldArithmetic(FieldArithmeticChip), FieldExtension(FieldExtensionChip), FriReducedOpening(FriReducedOpeningChip), - VerifyBatch(NativePoseidon2Chip), + // VerifyBatch(NativePoseidon2Chip), } #[derive(From, ChipUsageGetter, Chip, AnyEnum)] @@ -94,58 +94,83 @@ impl VmExtension for Native { program_bus, memory_bridge, } = builder.system_port(); - let offline_memory = builder.system_base().offline_memory(); + + let range_checker = &builder.system_base().range_checker_chip; let mut load_store_chip = NativeLoadStoreChip::::new( - NativeLoadStoreAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, + VmAirWrapper::new( + NativeLoadStoreAdapterAir::new( + memory_bridge, + ExecutionBridge::new(execution_bus, program_bus), + ), + NativeLoadStoreCoreAir::new(NativeLoadStoreOpcode::CLASS_OFFSET), + ), + NativeLoadStoreCoreStep::new( + NativeLoadStoreAdapterStep::new(NativeLoadStoreOpcode::CLASS_OFFSET), NativeLoadStoreOpcode::CLASS_OFFSET, ), - NativeLoadStoreCoreChip::new(NativeLoadStoreOpcode::CLASS_OFFSET), - offline_memory.clone(), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); - load_store_chip.core.set_streams(builder.streams().clone()); - + load_store_chip.step.set_streams(builder.streams().clone()); inventory.add_executor( load_store_chip, NativeLoadStoreOpcode::iter().map(|x| x.global_opcode()), )?; let mut block_load_store_chip = NativeLoadStoreChip::::new( - NativeLoadStoreAdapterChip::new( - execution_bus, - program_bus, - memory_bridge, + VmAirWrapper::new( + NativeLoadStoreAdapterAir::new( + memory_bridge, + ExecutionBridge::new(execution_bus, program_bus), + ), + NativeLoadStoreCoreAir::new(NativeLoadStore4Opcode::CLASS_OFFSET), + ), + NativeLoadStoreCoreStep::new( + NativeLoadStoreAdapterStep::new(NativeLoadStore4Opcode::CLASS_OFFSET), NativeLoadStore4Opcode::CLASS_OFFSET, ), - NativeLoadStoreCoreChip::new(NativeLoadStore4Opcode::CLASS_OFFSET), - offline_memory.clone(), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); block_load_store_chip - .core + .step .set_streams(builder.streams().clone()); - inventory.add_executor( block_load_store_chip, NativeLoadStore4Opcode::iter().map(|x| x.global_opcode()), )?; let branch_equal_chip = NativeBranchEqChip::new( - BranchNativeAdapterChip::<_>::new(execution_bus, program_bus, memory_bridge), - BranchEqualCoreChip::new(NativeBranchEqualOpcode::CLASS_OFFSET, DEFAULT_PC_STEP), - offline_memory.clone(), + NativeBranchEqAir::new( + BranchNativeAdapterAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + ), + BranchEqualCoreAir::new(NativeBranchEqualOpcode::CLASS_OFFSET, DEFAULT_PC_STEP), + ), + NativeBranchEqStep::new( + BranchNativeAdapterStep::new(), + NativeBranchEqualOpcode::CLASS_OFFSET, + DEFAULT_PC_STEP, + ), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor( branch_equal_chip, NativeBranchEqualOpcode::iter().map(|x| x.global_opcode()), )?; - let jal_chip = JalRangeCheckChip::new( - ExecutionBridge::new(execution_bus, program_bus), - offline_memory.clone(), - builder.system_base().range_checker_chip.clone(), + let jal_chip = JalRangeCheckChip::::new( + JalRangeCheckAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + range_checker.bus(), + ), + JalRangeCheckStep::new(range_checker.clone()), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor( jal_chip, @@ -155,53 +180,69 @@ impl VmExtension for Native { ], )?; - let field_arithmetic_chip = FieldArithmeticChip::new( - AluNativeAdapterChip::::new(execution_bus, program_bus, memory_bridge), - FieldArithmeticCoreChip::new(), - offline_memory.clone(), + let field_arithmetic_chip = FieldArithmeticChip::::new( + VmAirWrapper::new( + AluNativeAdapterAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + ), + FieldArithmeticCoreAir::new(), + ), + FieldArithmeticStep::new(AluNativeAdapterStep::new()), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor( field_arithmetic_chip, FieldArithmeticOpcode::iter().map(|x| x.global_opcode()), )?; - let field_extension_chip = FieldExtensionChip::new( - NativeVectorizedAdapterChip::new(execution_bus, program_bus, memory_bridge), - FieldExtensionCoreChip::new(), - offline_memory.clone(), + let field_extension_chip = FieldExtensionChip::::new( + VmAirWrapper::new( + NativeVectorizedAdapterAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + ), + FieldExtensionCoreAir::new(), + ), + FieldExtensionStep::new(NativeVectorizedAdapterStep::new()), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor( field_extension_chip, FieldExtensionOpcode::iter().map(|x| x.global_opcode()), )?; - let fri_reduced_opening_chip = FriReducedOpeningChip::new( - execution_bus, - program_bus, - memory_bridge, - offline_memory.clone(), - builder.streams().clone(), + let fri_reduced_opening_chip = FriReducedOpeningChip::::new( + FriReducedOpeningAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + ), + FriReducedOpeningStep::new(builder.streams().clone()), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); + inventory.add_executor( fri_reduced_opening_chip, FriOpcode::iter().map(|x| x.global_opcode()), )?; - let poseidon2_chip = NativePoseidon2Chip::new( - builder.system_port(), - offline_memory.clone(), - Poseidon2Config::default(), - VerifyBatchBus::new(builder.new_bus_idx()), - builder.streams().clone(), - ); - inventory.add_executor( - poseidon2_chip, - [ - VerifyBatchOpcode::VERIFY_BATCH.global_opcode(), - Poseidon2Opcode::PERM_POS2.global_opcode(), - Poseidon2Opcode::COMP_POS2.global_opcode(), - ], - )?; + // let poseidon2_chip = NativePoseidon2Chip::new( + // builder.system_port(), + // Poseidon2Config::default(), + // VerifyBatchBus::new(builder.new_bus_idx()), + // builder.streams().clone(), + // ); + // inventory.add_executor( + // poseidon2_chip, + // [ + // VerifyBatchOpcode::VERIFY_BATCH.global_opcode(), + // Poseidon2Opcode::PERM_POS2.global_opcode(), + // Poseidon2Opcode::COMP_POS2.global_opcode(), + // ], + // )?; builder.add_phantom_sub_executor( NativeHintInputSubEx, @@ -213,15 +254,15 @@ impl VmExtension for Native { PhantomDiscriminant(NativePhantom::HintFelt as u16), )?; - builder.add_phantom_sub_executor( - NativeHintBitsSubEx, - PhantomDiscriminant(NativePhantom::HintBits as u16), - )?; + // builder.add_phantom_sub_executor( + // NativeHintBitsSubEx, + // PhantomDiscriminant(NativePhantom::HintBits as u16), + // )?; - builder.add_phantom_sub_executor( - NativePrintSubEx, - PhantomDiscriminant(NativePhantom::Print as u16), - )?; + // builder.add_phantom_sub_executor( + // NativePrintSubEx, + // PhantomDiscriminant(NativePhantom::Print as u16), + // )?; builder.add_phantom_sub_executor( NativeHintLoadSubEx, @@ -236,7 +277,7 @@ pub(crate) mod phantom { use eyre::bail; use openvm_circuit::{ arch::{PhantomSubExecutor, Streams}, - system::memory::MemoryController, + system::memory::online::GuestMemory, }; use openvm_instructions::PhantomDiscriminant; use openvm_stark_backend::p3_field::{Field, PrimeField32}; @@ -250,11 +291,11 @@ pub(crate) mod phantom { impl PhantomSubExecutor for NativeHintInputSubEx { fn phantom_execute( &mut self, - _: &MemoryController, + _: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, - _: F, - _: F, + _: u32, + _: u32, _: u16, ) -> eyre::Result<()> { let hint = match streams.input_stream.pop_front() { @@ -275,11 +316,11 @@ pub(crate) mod phantom { impl PhantomSubExecutor for NativeHintSliceSubEx { fn phantom_execute( &mut self, - _: &MemoryController, + _: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, - _: F, - _: F, + _: u32, + _: u32, _: u16, ) -> eyre::Result<()> { let hint = match streams.input_stream.pop_front() { @@ -295,57 +336,56 @@ pub(crate) mod phantom { } } - impl PhantomSubExecutor for NativePrintSubEx { - fn phantom_execute( - &mut self, - memory: &MemoryController, - _: &mut Streams, - _: PhantomDiscriminant, - a: F, - _: F, - c_upper: u16, - ) -> eyre::Result<()> { - let addr_space = F::from_canonical_u16(c_upper); - let value = memory.unsafe_read_cell::(addr_space, a); - println!("{}", value); - Ok(()) - } - } - - impl PhantomSubExecutor for NativeHintBitsSubEx { - fn phantom_execute( - &mut self, - memory: &MemoryController, - streams: &mut Streams, - _: PhantomDiscriminant, - a: F, - b: F, - c_upper: u16, - ) -> eyre::Result<()> { - let addr_space = F::from_canonical_u16(c_upper); - let val = memory.unsafe_read_cell::(addr_space, a); - let mut val = val.as_canonical_u32(); - - let len = b.as_canonical_u32(); - assert!(streams.hint_stream.is_empty()); - for _ in 0..len { - streams - .hint_stream - .push_back(F::from_canonical_u32(val & 1)); - val >>= 1; - } - Ok(()) - } - } + // impl PhantomSubExecutor for NativePrintSubEx { + // fn phantom_execute( + // &mut self, + // memory: &GuestMemory, + // _: &mut Streams, + // _: PhantomDiscriminant, + // a: u32, + // _: u32, + // c_upper: u16, + // ) -> eyre::Result<()> { + // let addr_space = F::from_canonical_u16(c_upper); + // let value = memory.unsafe_read_cell::(addr_space, a); + // println!("{}", value); + // Ok(()) + // } + // } + + // impl PhantomSubExecutor for NativeHintBitsSubEx { + // fn phantom_execute( + // &mut self, + // memory: &GuestMemory, + // streams: &mut Streams, + // _: PhantomDiscriminant, + // a: u32, + // len: u32, + // c_upper: u16, + // ) -> eyre::Result<()> { + // let addr_space = F::from_canonical_u16(c_upper); + // let val = memory.unsafe_read_cell::(addr_space, a); + // let mut val = val.as_canonical_u32(); + + // assert!(streams.hint_stream.is_empty()); + // for _ in 0..len { + // streams + // .hint_stream + // .push_back(F::from_canonical_u32(val & 1)); + // val >>= 1; + // } + // Ok(()) + // } + // } impl PhantomSubExecutor for NativeHintLoadSubEx { fn phantom_execute( &mut self, - _: &MemoryController, + _: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, - _: F, - _: F, + _: u32, + _: u32, _: u16, ) -> eyre::Result<()> { let payload = match streams.input_stream.pop_front() { @@ -367,7 +407,7 @@ pub(crate) mod phantom { #[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)] pub struct CastFExtension; -#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)] +#[derive(ChipUsageGetter, Chip, InstructionExecutor, InsExecutorE1, From, AnyEnum)] pub enum CastFExtensionExecutor { CastF(CastFChip), } @@ -391,13 +431,19 @@ impl VmExtension for CastFExtension { program_bus, memory_bridge, } = builder.system_port(); - let offline_memory = builder.system_base().offline_memory(); - let range_checker = builder.system_base().range_checker_chip.clone(); - - let castf_chip = CastFChip::new( - ConvertAdapterChip::new(execution_bus, program_bus, memory_bridge), - CastFCoreChip::new(range_checker.clone()), - offline_memory.clone(), + let range_checker = &builder.system_base().range_checker_chip; + + let castf_chip = CastFChip::::new( + VmAirWrapper::new( + ConvertAdapterAir::new( + ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + ), + CastFCoreAir::new(range_checker.bus()), + ), + CastFStep::new(ConvertAdapterStep::<1, 4>::new(), range_checker.clone()), + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), ); inventory.add_executor(castf_chip, [CastfOpcode::CASTF.global_opcode()])?; diff --git a/extensions/native/circuit/src/field_arithmetic/core.rs b/extensions/native/circuit/src/field_arithmetic/core.rs index cd9e6ba1cd..2ae4811450 100644 --- a/extensions/native/circuit/src/field_arithmetic/core.rs +++ b/extensions/native/circuit/src/field_arithmetic/core.rs @@ -1,23 +1,20 @@ -use std::{ - borrow::{Borrow, BorrowMut}, - marker::PhantomData, -}; +use std::borrow::{Borrow, BorrowMut}; use itertools::izip; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, InsExecutorE1, - MinimalInstruction, Result, StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, - VmCoreChip, VmExecutionState, + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, + StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::online::GuestMemory, }; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; -use openvm_native_compiler::{ - conversion::AS, - FieldArithmeticOpcode::{self, *}, -}; +use openvm_native_compiler::FieldArithmeticOpcode::{self, *}; use openvm_stark_backend::{ interaction::InteractionBuilder, p3_air::BaseAir, @@ -41,7 +38,7 @@ pub struct FieldArithmeticCoreCols { pub divisor_inv: T, } -#[derive(Copy, Clone, Debug)] +#[derive(derive_new::new, Copy, Clone, Debug)] pub struct FieldArithmeticCoreAir {} impl BaseAir for FieldArithmeticCoreAir { @@ -124,54 +121,21 @@ pub struct FieldArithmeticRecord { pub c: F, } -pub struct FieldArithmeticStep { - phantom: PhantomData, -} - -impl Default for FieldArithmeticStep { - fn default() -> Self { - Self::new() - } -} - -impl FieldArithmeticStep { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } - - #[inline] - pub fn execute_trace_core( - &self, - instruction: &Instruction, - [x, y]: [[u8; NUM_LIMBS]; 2], - core_row: &mut [F], - ) -> [u8; NUM_LIMBS] - where - F: PrimeField32, - { - todo!("Implement execute_trace_core") - } - - pub fn fill_trace_row_core(&self, core_row: &mut [F]) - where - F: PrimeField32, - { - todo!("Implement fill_trace_row_core") - } +#[derive(derive_new::new)] +pub struct FieldArithmeticCoreStep { + adapter: A, } -impl TraceStep for FieldArithmeticStep +impl TraceStep for FieldArithmeticCoreStep where F: PrimeField32, A: 'static + for<'a> AdapterTraceStep< F, CTX, - ReadData = [[u8; NUM_LIMBS]; 2], - WriteData = [u8; NUM_LIMBS], - TraceContext<'a> = &'a BitwiseOperationLookupChip, + ReadData = [F; 2], + WriteData = [F; 1], + TraceContext<'a> = (), >, { fn get_opcode_name(&self, opcode: usize) -> String { @@ -185,116 +149,100 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { - todo!("Implement execute") + let &Instruction { opcode, .. } = instruction; + let local_opcode = FieldArithmeticOpcode::from_usize( + opcode.local_opcode_idx(FieldArithmeticOpcode::CLASS_OFFSET), + ); + + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + A::start(*state.pc, state.memory, adapter_row); + + let [b_val, c_val] = self.adapter.read(state.memory, instruction, adapter_row); + + let a_val = FieldArithmetic::run_field_arithmetic(local_opcode, b_val, c_val).unwrap(); + + let core_row: &mut FieldArithmeticCoreCols<_> = core_row.borrow_mut(); + core_row.a = a_val; + core_row.b = b_val; + core_row.c = c_val; + + core_row.is_add = F::from_bool(local_opcode == FieldArithmeticOpcode::ADD); + core_row.is_sub = F::from_bool(local_opcode == FieldArithmeticOpcode::SUB); + core_row.is_mul = F::from_bool(local_opcode == FieldArithmeticOpcode::MUL); + core_row.is_div = F::from_bool(local_opcode == FieldArithmeticOpcode::DIV); + + self.adapter + .write(state.memory, instruction, adapter_row, &[a_val]); + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + *trace_offset += width; + + Ok(()) } fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { - todo!("Implement fill_trace_row") + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + self.adapter.fill_trace_row(mem_helper, (), adapter_row); + + let core_row: &mut FieldArithmeticCoreCols<_> = core_row.borrow_mut(); + + core_row.divisor_inv = if core_row.is_div.is_zero() { + F::ZERO + } else { + core_row.c.inverse() + }; } } -impl StepExecutorE1 for FieldArithmeticStep +impl StepExecutorE1 for FieldArithmeticCoreStep where F: PrimeField32, - A: 'static + for<'a> AdapterExecutorE1, + A: 'static + for<'a> AdapterExecutorE1, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, - a, - b, - c, - e, - f, - .. - } = instruction; - - let (b_val, c_val) = A::read(&mut state.memory, instruction); + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { + let Instruction { opcode, .. } = instruction; + + let local_opcode = FieldArithmeticOpcode::from_usize( + opcode.local_opcode_idx(FieldArithmeticOpcode::CLASS_OFFSET), + ); + + let [b_val, c_val] = self.adapter.read(state, instruction); let a_val = FieldArithmetic::run_field_arithmetic(local_opcode, b_val, c_val).unwrap(); - A::write(&mut state.memory, instruction, &a_val); + self.adapter.write(state, instruction, &[a_val]); - state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } -} -// impl> VmCoreChip for FieldArithmeticCoreChip -// where -// I::Reads: Into<[[F; 1]; 2]>, -// I::Writes: From<[[F; 1]; 1]>, -// { -// type Record = FieldArithmeticRecord; -// type Air = FieldArithmeticCoreAir; - -// #[allow(clippy::type_complexity)] -// fn execute_instruction( -// &self, -// instruction: &Instruction, -// _from_pc: u32, -// reads: I::Reads, -// ) -> Result<(AdapterRuntimeContext, Self::Record)> { -// let Instruction { opcode, .. } = instruction; -// let local_opcode = FieldArithmeticOpcode::from_usize( -// opcode.local_opcode_idx(FieldArithmeticOpcode::CLASS_OFFSET), -// ); - -// let data: [[F; 1]; 2] = reads.into(); -// let b = data[0][0]; -// let c = data[1][0]; -// let a = FieldArithmetic::run_field_arithmetic(local_opcode, b, c).unwrap(); - -// let output: AdapterRuntimeContext = AdapterRuntimeContext { -// to_pc: None, -// writes: [[a]].into(), -// }; - -// let record = Self::Record { -// opcode: local_opcode, -// a, -// b, -// c, -// }; - -// Ok((output, record)) -// } - -// fn get_opcode_name(&self, opcode: usize) -> String { -// format!( -// "{:?}", -// FieldArithmeticOpcode::from_usize(opcode - FieldArithmeticOpcode::CLASS_OFFSET) -// ) -// } - -// fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { -// let FieldArithmeticRecord { opcode, a, b, c } = record; -// let row_slice: &mut FieldArithmeticCoreCols<_> = row_slice.borrow_mut(); -// row_slice.a = a; -// row_slice.b = b; -// row_slice.c = c; - -// row_slice.is_add = F::from_bool(opcode == FieldArithmeticOpcode::ADD); -// row_slice.is_sub = F::from_bool(opcode == FieldArithmeticOpcode::SUB); -// row_slice.is_mul = F::from_bool(opcode == FieldArithmeticOpcode::MUL); -// row_slice.is_div = F::from_bool(opcode == FieldArithmeticOpcode::DIV); -// row_slice.divisor_inv = if opcode == FieldArithmeticOpcode::DIV { -// c.inverse() -// } else { -// F::ZERO -// }; -// } - -// fn air(&self) -> &Self::Air { -// &self.air -// } -// } + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; + + Ok(()) + } +} pub struct FieldArithmetic; impl FieldArithmetic { diff --git a/extensions/native/circuit/src/field_arithmetic/mod.rs b/extensions/native/circuit/src/field_arithmetic/mod.rs index 2c9a8f061e..1cf9a27925 100644 --- a/extensions/native/circuit/src/field_arithmetic/mod.rs +++ b/extensions/native/circuit/src/field_arithmetic/mod.rs @@ -1,4 +1,4 @@ -use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper, VmChipWrapper}; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; use crate::adapters::alu_native_adapter::{AluNativeAdapterAir, AluNativeAdapterStep}; @@ -9,6 +9,5 @@ mod core; pub use core::*; pub type FieldArithmeticAir = VmAirWrapper; -pub type FieldArithmeticStepWithAdapter = FieldArithmeticStep; -pub type FieldArithmeticChip = - NewVmChipWrapper; +pub type FieldArithmeticStep = FieldArithmeticCoreStep; +pub type FieldArithmeticChip = NewVmChipWrapper; diff --git a/extensions/native/circuit/src/field_arithmetic/tests.rs b/extensions/native/circuit/src/field_arithmetic/tests.rs index 8e69f8c44b..25f7a62895 100644 --- a/extensions/native/circuit/src/field_arithmetic/tests.rs +++ b/extensions/native/circuit/src/field_arithmetic/tests.rs @@ -1,6 +1,9 @@ use std::borrow::BorrowMut; -use openvm_circuit::arch::testing::{memory::gen_pointer, VmChipTestBuilder}; +use openvm_circuit::arch::{ + testing::{memory::gen_pointer, VmChipTestBuilder}, + VmAirWrapper, +}; use openvm_instructions::{instruction::Instruction, LocalOpcode}; use openvm_native_compiler::FieldArithmeticOpcode; use openvm_stark_backend::{ @@ -17,9 +20,27 @@ use rand::Rng; use strum::EnumCount; use super::{ - core::FieldArithmeticCoreChip, FieldArithmetic, FieldArithmeticChip, FieldArithmeticCoreCols, + FieldArithmetic, FieldArithmeticChip, FieldArithmeticCoreAir, FieldArithmeticCoreCols, + FieldArithmeticStep, +}; +use crate::adapters::alu_native_adapter::{ + AluNativeAdapterAir, AluNativeAdapterCols, AluNativeAdapterStep, }; -use crate::adapters::alu_native_adapter::{AluNativeAdapterChip, AluNativeAdapterCols}; + +const MAX_INS_CAPACITY: usize = 128; +type F = BabyBear; + +fn create_test_chip(tester: &VmChipTestBuilder) -> FieldArithmeticChip { + FieldArithmeticChip::::new( + VmAirWrapper::new( + AluNativeAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), + FieldArithmeticCoreAir::new(), + ), + FieldArithmeticStep::new(AluNativeAdapterStep::new()), + MAX_INS_CAPACITY, + tester.memory_helper(), + ) +} #[test] fn new_field_arithmetic_air_test() { @@ -28,15 +49,7 @@ fn new_field_arithmetic_air_test() { let xy_address_space_range = || 0usize..=1; let mut tester = VmChipTestBuilder::default(); - let mut chip = FieldArithmeticChip::new( - AluNativeAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - ), - FieldArithmeticCoreChip::new(), - tester.offline_memory_mutex_arc(), - ); + let mut chip = create_test_chip(&tester); let mut rng = create_seeded_rng(); @@ -74,10 +87,10 @@ fn new_field_arithmetic_air_test() { ); if as1 != 0 { - tester.write_cell(as1, address1, operand1); + tester.write(as1, address1, [operand1]); } if as2 != 0 { - tester.write_cell(as2, address2, operand2); + tester.write(as2, address2, [operand2]); } tester.execute( &mut chip, @@ -86,7 +99,7 @@ fn new_field_arithmetic_air_test() { [result_address, address1, address2, result_as, as1, as2], ), ); - assert_eq!(result, tester.read_cell(result_as, result_address)); + assert_eq!(result, tester.read::<1>(result_as, result_address)[0]); } let mut tester = tester.build().load(chip).finalize(); @@ -122,17 +135,9 @@ fn new_field_arithmetic_air_test() { #[test] fn new_field_arithmetic_air_zero_div_zero() { let mut tester = VmChipTestBuilder::default(); - let mut chip = FieldArithmeticChip::new( - AluNativeAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - ), - FieldArithmeticCoreChip::new(), - tester.offline_memory_mutex_arc(), - ); - tester.write_cell(4, 6, BabyBear::from_canonical_u32(111)); - tester.write_cell(4, 7, BabyBear::from_canonical_u32(222)); + let mut chip = create_test_chip(&tester); + tester.write(4, 6, [BabyBear::from_canonical_u32(111)]); + tester.write(4, 7, [BabyBear::from_canonical_u32(222)]); tester.execute( &mut chip, @@ -166,16 +171,8 @@ fn new_field_arithmetic_air_zero_div_zero() { #[test] fn new_field_arithmetic_air_test_panic() { let mut tester = VmChipTestBuilder::default(); - let mut chip = FieldArithmeticChip::new( - AluNativeAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - ), - FieldArithmeticCoreChip::new(), - tester.offline_memory_mutex_arc(), - ); - tester.write_cell(4, 0, BabyBear::ZERO); + let mut chip = create_test_chip(&tester); + tester.write(4, 0, [BabyBear::ZERO]); // should panic tester.execute( &mut chip, diff --git a/extensions/native/circuit/src/field_extension/core.rs b/extensions/native/circuit/src/field_extension/core.rs index 50fd04acbf..2b77bdc7cc 100644 --- a/extensions/native/circuit/src/field_extension/core.rs +++ b/extensions/native/circuit/src/field_extension/core.rs @@ -1,17 +1,20 @@ use std::{ array, borrow::{Borrow, BorrowMut}, - marker::PhantomData, ops::{Add, Mul, Sub}, }; use itertools::izip; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, InsExecutorE1, - MinimalInstruction, Result, VmAdapterInterface, VmCoreAir, VmCoreChip, VmExecutionState, + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, MinimalInstruction, Result, + StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::online::GuestMemory, }; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; @@ -42,7 +45,7 @@ pub struct FieldExtensionCoreCols { pub divisor_inv: [T; EXT_DEG], } -#[derive(Copy, Clone, Debug)] +#[derive(derive_new::new, Copy, Clone, Debug)] pub struct FieldExtensionCoreAir {} impl BaseAir for FieldExtensionCoreAir { @@ -145,54 +148,21 @@ pub struct FieldExtensionRecord { pub z: [F; EXT_DEG], } -pub struct FieldExtensionStep { - phantom: PhantomData, -} - -impl Default for FieldExtensionStep { - fn default() -> Self { - Self::new() - } -} - -impl FieldExtensionChip { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } - - #[inline] - pub fn execute_trace_core( - &self, - instruction: &Instruction, - [x, y]: [[u8; NUM_LIMBS]; 2], - core_row: &mut [F], - ) -> [u8; NUM_LIMBS] - where - F: PrimeField32, - { - todo!("Implement execute_trace_core") - } - - pub fn fill_trace_row_core(&self, core_row: &mut [F]) - where - F: PrimeField32, - { - todo!("Implement fill_trace_row_core") - } +#[derive(derive_new::new)] +pub struct FieldExtensionCoreStep { + adapter: A, } -impl TraceStep for FieldExtensionStep +impl TraceStep for FieldExtensionCoreStep where F: PrimeField32, A: 'static + for<'a> AdapterTraceStep< F, CTX, - ReadData = [[u8; NUM_LIMBS]; 2], - WriteData = [u8; NUM_LIMBS], - TraceContext<'a> = &'a BitwiseOperationLookupChip, + ReadData = [[F; EXT_DEG]; 2], + WriteData = [F; EXT_DEG], + TraceContext<'a> = (), >, { fn get_opcode_name(&self, opcode: usize) -> String { @@ -206,45 +176,78 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { - todo!("Implement execute") + let &Instruction { opcode, .. } = instruction; + + let local_opcode = FieldExtensionOpcode::from_usize( + opcode.local_opcode_idx(FieldExtensionOpcode::CLASS_OFFSET), + ); + + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + A::start(*state.pc, state.memory, adapter_row); + + let [y, z] = self.adapter.read(state.memory, instruction, adapter_row); + + let x = FieldExtension::solve(local_opcode, y, z).unwrap(); + + let core_row: &mut FieldExtensionCoreCols<_> = core_row.borrow_mut(); + core_row.x = x; + core_row.y = y; + core_row.z = z; + core_row.is_add = F::from_bool(local_opcode == FieldExtensionOpcode::FE4ADD); + core_row.is_sub = F::from_bool(local_opcode == FieldExtensionOpcode::FE4SUB); + core_row.is_mul = F::from_bool(local_opcode == FieldExtensionOpcode::BBE4MUL); + core_row.is_div = F::from_bool(local_opcode == FieldExtensionOpcode::BBE4DIV); + + self.adapter + .write(state.memory, instruction, adapter_row, &x); + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + *trace_offset += width; + + Ok(()) } fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { - todo!("Implement fill_trace_row") + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + self.adapter.fill_trace_row(mem_helper, (), adapter_row); + + let core_row: &mut FieldExtensionCoreCols<_> = core_row.borrow_mut(); + + core_row.divisor_inv = if core_row.is_div.is_one() { + FieldExtension::invert(core_row.z) + } else { + [F::ZERO; EXT_DEG] + }; } } -impl StepExecutorE1 for FieldExtensionStep +impl StepExecutorE1 for FieldExtensionCoreStep where F: PrimeField32, A: 'static - + for<'a> AdapterExecutorE1< - Mem, - F, - ReadData = ([F; EXT_DEG], [F; EXT_DEG]), - WriteData = [F; EXT_DEG], - >, + + for<'a> AdapterExecutorE1, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, - a, - b, - c, - d, - e, - .. - } = instruction; + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { + let Instruction { opcode, .. } = instruction; let local_opcode_idx = opcode.local_opcode_idx(FieldExtensionOpcode::CLASS_OFFSET); - let (y_val, z_val) = A::read(&mut state.memory, instruction); + let [y_val, z_val] = self.adapter.read(state, instruction); let x_val = FieldExtension::solve( FieldExtensionOpcode::from_usize(local_opcode_idx), @@ -253,82 +256,25 @@ where ) .unwrap(); - A::write(&mut state.memory, instruction, &x_val); + self.adapter.write(state, instruction, &x_val); - state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } -} -// impl> VmCoreChip for FieldExtensionCoreChip -// where -// I::Reads: Into<[[F; EXT_DEG]; 2]>, -// I::Writes: From<[[F; EXT_DEG]; 1]>, -// { -// type Record = FieldExtensionRecord; -// type Air = FieldExtensionCoreAir; - -// #[allow(clippy::type_complexity)] -// fn execute_instruction( -// &self, -// instruction: &Instruction, -// _from_pc: u32, -// reads: I::Reads, -// ) -> Result<(AdapterRuntimeContext, Self::Record)> { -// let Instruction { opcode, .. } = instruction; -// let local_opcode_idx = opcode.local_opcode_idx(FieldExtensionOpcode::CLASS_OFFSET); - -// let data: [[F; EXT_DEG]; 2] = reads.into(); -// let y: [F; EXT_DEG] = data[0]; -// let z: [F; EXT_DEG] = data[1]; - -// let x = FieldExtension::solve(FieldExtensionOpcode::from_usize(local_opcode_idx), y, z) -// .unwrap(); - -// let output = AdapterRuntimeContext { -// to_pc: None, -// writes: [x].into(), -// }; - -// let record = Self::Record { -// opcode: FieldExtensionOpcode::from_usize(local_opcode_idx), -// x, -// y, -// z, -// }; - -// Ok((output, record)) -// } - -// fn get_opcode_name(&self, opcode: usize) -> String { -// format!( -// "{:?}", -// FieldExtensionOpcode::from_usize(opcode - FieldExtensionOpcode::CLASS_OFFSET) -// ) -// } - -// fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { -// let FieldExtensionRecord { opcode, x, y, z } = record; -// let cols: &mut FieldExtensionCoreCols<_> = row_slice.borrow_mut(); -// cols.x = x; -// cols.y = y; -// cols.z = z; -// cols.is_add = F::from_bool(opcode == FieldExtensionOpcode::FE4ADD); -// cols.is_sub = F::from_bool(opcode == FieldExtensionOpcode::FE4SUB); -// cols.is_mul = F::from_bool(opcode == FieldExtensionOpcode::BBE4MUL); -// cols.is_div = F::from_bool(opcode == FieldExtensionOpcode::BBE4DIV); -// cols.divisor_inv = if opcode == FieldExtensionOpcode::BBE4DIV { -// FieldExtension::invert(z) -// } else { -// [F::ZERO; EXT_DEG] -// }; -// } - -// fn air(&self) -> &Self::Air { -// &self.air -// } -// } + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; + + Ok(()) + } +} pub struct FieldExtension; impl FieldExtension { diff --git a/extensions/native/circuit/src/field_extension/mod.rs b/extensions/native/circuit/src/field_extension/mod.rs index daa29a8e0b..c6bcf39f49 100644 --- a/extensions/native/circuit/src/field_extension/mod.rs +++ b/extensions/native/circuit/src/field_extension/mod.rs @@ -1,7 +1,7 @@ -use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper, VmChipWrapper}; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; use super::adapters::native_vectorized_adapter::{ - NativeVectorizedAdapterAir, NativeVectorizedAdapterChip, + NativeVectorizedAdapterAir, NativeVectorizedAdapterStep, }; #[cfg(test)] @@ -12,6 +12,5 @@ pub use core::*; pub type FieldExtensionAir = VmAirWrapper, FieldExtensionCoreAir>; -pub type FieldExtensionStepWithAdapter = FieldExtensionStep>; -pub type FieldExtensionChip = - NewVmChipWrapper; +pub type FieldExtensionStep = FieldExtensionCoreStep>; +pub type FieldExtensionChip = NewVmChipWrapper; diff --git a/extensions/native/circuit/src/field_extension/tests.rs b/extensions/native/circuit/src/field_extension/tests.rs index 66d6c94004..ef197d8aa3 100644 --- a/extensions/native/circuit/src/field_extension/tests.rs +++ b/extensions/native/circuit/src/field_extension/tests.rs @@ -3,7 +3,10 @@ use std::{ ops::{Add, Div, Mul, Sub}, }; -use openvm_circuit::arch::testing::{memory::gen_pointer, VmChipTestBuilder}; +use openvm_circuit::arch::{ + testing::{memory::gen_pointer, VmChipTestBuilder}, + VmAirWrapper, +}; use openvm_instructions::{instruction::Instruction, LocalOpcode}; use openvm_native_compiler::FieldExtensionOpcode; use openvm_stark_backend::{ @@ -16,25 +19,33 @@ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::Rng; use strum::EnumCount; -use super::{ - super::adapters::native_vectorized_adapter::NativeVectorizedAdapterChip, FieldExtension, - FieldExtensionChip, FieldExtensionCoreChip, +use crate::adapters::native_vectorized_adapter::{ + NativeVectorizedAdapterAir, NativeVectorizedAdapterStep, }; +use super::{FieldExtension, FieldExtensionChip, FieldExtensionCoreAir, FieldExtensionCoreStep}; + +const MAX_INS_CAPACITY: usize = 128; +type F = BabyBear; + +fn create_test_chip(tester: &VmChipTestBuilder) -> FieldExtensionChip { + FieldExtensionChip::::new( + VmAirWrapper::new( + NativeVectorizedAdapterAir::new(tester.execution_bridge(), tester.memory_bridge()), + FieldExtensionCoreAir::new(), + ), + FieldExtensionCoreStep::new(NativeVectorizedAdapterStep::new()), + MAX_INS_CAPACITY, + tester.memory_helper(), + ) +} + #[test] fn new_field_extension_air_test() { type F = BabyBear; let mut tester = VmChipTestBuilder::default(); - let mut chip = FieldExtensionChip::new( - NativeVectorizedAdapterChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - ), - FieldExtensionCoreChip::new(), - tester.offline_memory_mutex_arc(), - ); + let mut chip = create_test_chip(&tester); let trace_width = chip.trace_width(); let mut rng = create_seeded_rng(); diff --git a/extensions/native/circuit/src/fri/mod.rs b/extensions/native/circuit/src/fri/mod.rs index e54d5d506f..2886589827 100644 --- a/extensions/native/circuit/src/fri/mod.rs +++ b/extensions/native/circuit/src/fri/mod.rs @@ -5,38 +5,36 @@ use std::{ sync::{Arc, Mutex}, }; -use itertools::{zip_eq, Itertools}; +use itertools::zip_eq; use openvm_circuit::{ arch::{ - ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InstructionExecutor, Streams, + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, + ExecutionBridge, ExecutionState, NewVmChipWrapper, Result, StepExecutorE1, Streams, + TraceStep, VmStateMut, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, - MemoryAddress, MemoryAuxColsFactory, MemoryController, OfflineMemory, RecordId, - }, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols, AUX_LEN}, + online::{GuestMemory, TracingMemory}, + MemoryAddress, MemoryAuxColsFactory, }, }; -use openvm_circuit_primitives::utils::next_power_of_two_or_zero; +use openvm_circuit_primitives::is_less_than::LessThanAuxCols; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; use openvm_native_compiler::{conversion::AS, FriOpcode::FRI_REDUCED_OPENING}; use openvm_stark_backend::{ - config::{StarkGenericConfig, Val}, interaction::InteractionBuilder, p3_air::{Air, AirBuilder, BaseAir}, p3_field::{Field, FieldAlgebra, PrimeField32}, - p3_matrix::{dense::RowMajorMatrix, Matrix}, - p3_maybe_rayon::prelude::*, - prover::types::AirProofInput, + p3_matrix::Matrix, rap::{BaseAirWithPublicValues, PartitionedBaseAir}, - AirRef, Chip, ChipUsageGetter, }; -use serde::{Deserialize, Serialize}; use static_assertions::const_assert_eq; use crate::{ + adapters::{ + memory_read_native, memory_write_native, tracing_read_native, tracing_write_native, + }, field_extension::{FieldExtension, EXT_DEG}, utils::const_max, }; @@ -219,8 +217,8 @@ const INSTRUCTION_READS: usize = 5; /// it starts with a Workload row (T1) and ends with either a Disabled or Instruction2 row (T7). /// The other transition constraints then ensure the proper state transitions from Workload to /// Instruction2. -#[derive(Copy, Clone, Debug)] -struct FriReducedOpeningAir { +#[derive(Copy, Clone, Debug, derive_new::new)] +pub struct FriReducedOpeningAir { execution_bridge: ExecutionBridge, memory_bridge: MemoryBridge, } @@ -544,94 +542,83 @@ fn elem_to_ext(elem: F) -> [F; EXT_DEG] { ret } -#[derive(Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct FriReducedOpeningRecord { - pub pc: F, - pub start_timestamp: F, - pub instruction: Instruction, - pub alpha_read: RecordId, - pub length_read: RecordId, - pub a_ptr_read: RecordId, - pub is_init_read: RecordId, - pub b_ptr_read: RecordId, - pub a_rws: Vec, - pub b_reads: Vec, - pub result_write: RecordId, +pub struct FriReducedOpeningStep { + pub height: usize, + streams: Arc>>, } -impl FriReducedOpeningRecord { - pub fn get_height(&self) -> usize { - // 2 for instruction rows - self.a_rws.len() + 2 +impl FriReducedOpeningStep { + pub fn new(streams: Arc>>) -> Self { + Self { height: 0, streams } } } -pub struct FriReducedOpeningChip { - air: FriReducedOpeningAir, - pub records: Vec>, - pub height: usize, - offline_memory: Arc>>, - streams: Arc>>, -} -impl FriReducedOpeningChip { - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - offline_memory: Arc>>, - streams: Arc>>, - ) -> Self { - let air = FriReducedOpeningAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - }; - Self { - records: vec![], - air, - height: 0, - offline_memory, - streams, - } +impl TraceStep for FriReducedOpeningStep +where + F: PrimeField32, +{ + fn get_opcode_name(&self, opcode: usize) -> String { + assert_eq!(opcode, FRI_REDUCED_OPENING.global_opcode().as_usize()); + String::from("FRI_REDUCED_OPENING") } -} -impl InstructionExecutor for FriReducedOpeningChip { + fn execute( &mut self, - memory: &mut MemoryController, + state: VmStateMut, CTX>, instruction: &Instruction, - from_state: ExecutionState, - ) -> Result, ExecutionError> { + trace: &mut [F], + trace_offset: &mut usize, + _width: usize, + ) -> Result<()> { let &Instruction { - a: a_ptr_ptr, - b: b_ptr_ptr, - c: length_ptr, - d: alpha_ptr, - e: result_ptr, - f: hint_id_ptr, - g: is_init_ptr, + a, + b, + c, + d, + e, + f, + g, .. } = instruction; - let addr_space = F::from_canonical_u32(AS::Native as u32); - let alpha_read = memory.read(addr_space, alpha_ptr); - let length_read = memory.read_cell(addr_space, length_ptr); - let a_ptr_read = memory.read_cell(addr_space, a_ptr_ptr); - let b_ptr_read = memory.read_cell(addr_space, b_ptr_ptr); - let is_init_read = memory.read_cell(addr_space, is_init_ptr); - let is_init = is_init_read.1.as_canonical_u32(); + let a_ptr_ptr = a.as_canonical_u32(); + let b_ptr_ptr = b.as_canonical_u32(); + let length_ptr = c.as_canonical_u32(); + let alpha_ptr = d.as_canonical_u32(); + let result_ptr = e.as_canonical_u32(); + let hint_id_ptr = f.as_canonical_u32(); + let is_init_ptr = g.as_canonical_u32(); - let hint_id_f = memory.unsafe_read_cell::(addr_space, hint_id_ptr); + let timestamp_start = state.memory.timestamp(); + + // TODO(ayush): there should be a way to avoid this + let mut alpha_aux = MemoryReadAuxCols::new(0, LessThanAuxCols::new([F::ZERO; AUX_LEN])); + let alpha = tracing_read_native(state.memory, alpha_ptr, alpha_aux.as_mut()); + + let mut length_aux = MemoryReadAuxCols::new(0, LessThanAuxCols::new([F::ZERO; AUX_LEN])); + let [length]: [F; 1] = tracing_read_native(state.memory, length_ptr, length_aux.as_mut()); + + let mut a_ptr_aux = MemoryReadAuxCols::new(0, LessThanAuxCols::new([F::ZERO; AUX_LEN])); + let [a_ptr]: [F; 1] = tracing_read_native(state.memory, a_ptr_ptr, a_ptr_aux.as_mut()); + + let mut b_ptr_aux = MemoryReadAuxCols::new(0, LessThanAuxCols::new([F::ZERO; AUX_LEN])); + let [b_ptr]: [F; 1] = tracing_read_native(state.memory, b_ptr_ptr, b_ptr_aux.as_mut()); + + let mut is_init_aux = MemoryReadAuxCols::new(0, LessThanAuxCols::new([F::ZERO; AUX_LEN])); + let [is_init_read]: [F; 1] = + tracing_read_native(state.memory, is_init_ptr, is_init_aux.as_mut()); + let is_init = is_init_read.as_canonical_u32(); + + let [hint_id_f]: [F; 1] = memory_read_native(state.memory.data(), hint_id_ptr); let hint_id = hint_id_f.as_canonical_u32() as usize; - let alpha = alpha_read.1; - let length = length_read.1.as_canonical_u32() as usize; - let a_ptr = a_ptr_read.1; - let b_ptr = b_ptr_read.1; + let length = length.as_canonical_u32() as usize; - let mut a_rws = Vec::with_capacity(length); - let mut b_reads = Vec::with_capacity(length); - let mut result = [F::ZERO; EXT_DEG]; + let write_a = F::ONE - is_init_read; + + // TODO(ayush): why do we need this?should this be incremented only in tracegen execute? + // 2 for instruction rows + self.height += length + 2; let data = if is_init == 0 { let mut streams = self.streams.lock().unwrap(); @@ -640,122 +627,38 @@ impl InstructionExecutor for FriReducedOpeningChip { } else { vec![] }; + + let mut as_and_bs = Vec::with_capacity(length); #[allow(clippy::needless_range_loop)] for i in 0..length { - let a_rw = if is_init == 0 { - let (record_id, _) = - memory.write_cell(addr_space, a_ptr + F::from_canonical_usize(i), data[i]); - (record_id, data[i]) + // First read goes to last row + let start = *trace_offset + (length - i - 1) * OVERALL_WIDTH; + let cols: &mut WorkloadCols = trace[start..start + WL_WIDTH].borrow_mut(); + + let a_ptr_i = (a_ptr + F::from_canonical_usize(i)).as_canonical_u32(); + let [a]: [F; 1] = if is_init == 0 { + tracing_write_native(state.memory, a_ptr_i, &[data[i]], &mut cols.a_aux); + [data[i]] } else { - memory.read_cell(addr_space, a_ptr + F::from_canonical_usize(i)) + tracing_read_native(state.memory, a_ptr_i, cols.a_aux.as_mut()) }; - let b_read = - memory.read::(addr_space, b_ptr + F::from_canonical_usize(EXT_DEG * i)); - a_rws.push(a_rw); - b_reads.push(b_read); - } + let b_ptr_i = (b_ptr + F::from_canonical_usize(EXT_DEG * i)).as_canonical_u32(); + let b = tracing_read_native::(state.memory, b_ptr_i, cols.b_aux.as_mut()); - for (a_rw, b_read) in a_rws.iter().rev().zip_eq(b_reads.iter().rev()) { - let a = a_rw.1; - let b = b_read.1; - // result = result * alpha + (b - a) - result = FieldExtension::add( - FieldExtension::multiply(result, alpha), - FieldExtension::subtract(b, elem_to_ext(a)), - ); + as_and_bs.push((a, b)); } - let (result_write, _) = memory.write(addr_space, result_ptr, &result); - - let record = FriReducedOpeningRecord { - pc: F::from_canonical_u32(from_state.pc), - start_timestamp: F::from_canonical_u32(from_state.timestamp), - instruction: instruction.clone(), - alpha_read: alpha_read.0, - length_read: length_read.0, - a_ptr_read: a_ptr_read.0, - is_init_read: is_init_read.0, - b_ptr_read: b_ptr_read.0, - a_rws: a_rws.into_iter().map(|r| r.0).collect(), - b_reads: b_reads.into_iter().map(|r| r.0).collect(), - result_write, - }; - self.height += record.get_height(); - self.records.push(record); - - Ok(ExecutionState { - pc: from_state.pc + DEFAULT_PC_STEP, - timestamp: memory.timestamp(), - }) - } - - fn get_opcode_name(&self, opcode: usize) -> String { - assert_eq!(opcode, FRI_REDUCED_OPENING.global_opcode().as_usize()); - String::from("FRI_REDUCED_OPENING") - } -} + let mut result = [F::ZERO; EXT_DEG]; + for (i, (a, b)) in as_and_bs.into_iter().rev().enumerate() { + let start = *trace_offset + i * OVERALL_WIDTH; + let cols: &mut WorkloadCols = trace[start..start + WL_WIDTH].borrow_mut(); -fn record_to_rows( - record: FriReducedOpeningRecord, - aux_cols_factory: &MemoryAuxColsFactory, - slice: &mut [F], - memory: &OfflineMemory, -) { - let Instruction { - a: a_ptr_ptr, - b: b_ptr_ptr, - c: length_ptr, - d: alpha_ptr, - e: result_ptr, - f: hint_id_ptr, - g: is_init_ptr, - .. - } = record.instruction; - - let length_read = memory.record_by_id(record.length_read); - let alpha_read = memory.record_by_id(record.alpha_read); - let a_ptr_read = memory.record_by_id(record.a_ptr_read); - let b_ptr_read = memory.record_by_id(record.b_ptr_read); - let is_init_read = memory.record_by_id(record.is_init_read); - let is_init = is_init_read.data_at(0); - let write_a = F::ONE - is_init; - - let length = length_read.data_at(0).as_canonical_u32() as usize; - let alpha: [F; EXT_DEG] = alpha_read.data_slice().try_into().unwrap(); - let a_ptr = a_ptr_read.data_at(0); - let b_ptr = b_ptr_read.data_at(0); - - let mut result = [F::ZERO; EXT_DEG]; - - let alpha_aux = aux_cols_factory.make_read_aux_cols(alpha_read); - let length_aux = aux_cols_factory.make_read_aux_cols(length_read); - let a_ptr_aux = aux_cols_factory.make_read_aux_cols(a_ptr_read); - let b_ptr_aux = aux_cols_factory.make_read_aux_cols(b_ptr_read); - let is_init_aux = aux_cols_factory.make_read_aux_cols(is_init_read); - - let result_aux = aux_cols_factory.make_write_aux_cols(memory.record_by_id(record.result_write)); - - // WorkloadCols - for (i, (&a_record_id, &b_record_id)) in record - .a_rws - .iter() - .rev() - .zip_eq(record.b_reads.iter().rev()) - .enumerate() - { - let a_rw = memory.record_by_id(a_record_id); - let b_read = memory.record_by_id(b_record_id); - let a = a_rw.data_at(0); - let b: [F; EXT_DEG] = b_read.data_slice().try_into().unwrap(); - - let start = i * OVERALL_WIDTH; - let cols: &mut WorkloadCols = slice[start..start + WL_WIDTH].borrow_mut(); - *cols = WorkloadCols { - prefix: PrefixCols { + cols.prefix = PrefixCols { general: GeneralCols { is_workload_row: F::ONE, is_ins_row: F::ZERO, - timestamp: record.start_timestamp + F::from_canonical_usize((length - i) * 2), + timestamp: F::from_canonical_u32(timestamp_start) + + F::from_canonical_usize((length - i) * 2), }, a_or_is_first: a, data: DataCols { @@ -766,133 +669,235 @@ fn record_to_rows( result, alpha, }, - }, - // Generate write aux columns no matter `a` is read or written. When `a` is written, - // `prev_data` is not constrained. - a_aux: if a_rw.prev_data_slice().is_some() { - aux_cols_factory.make_write_aux_cols(a_rw) - } else { - let read_aux = aux_cols_factory.make_read_aux_cols(a_rw); - MemoryWriteAuxCols::from_base(read_aux.get_base(), [F::ZERO]) - }, - b, - b_aux: aux_cols_factory.make_read_aux_cols(b_read), - }; - // result = result * alpha + (b - a) - result = FieldExtension::add( - FieldExtension::multiply(result, alpha), - FieldExtension::subtract(b, elem_to_ext(a)), - ); - } - // Instruction1Cols - { - let start = length * OVERALL_WIDTH; - let cols: &mut Instruction1Cols = slice[start..start + INS_1_WIDTH].borrow_mut(); - *cols = Instruction1Cols { - prefix: PrefixCols { - general: GeneralCols { - is_workload_row: F::ZERO, - is_ins_row: F::ONE, - timestamp: record.start_timestamp, - }, - a_or_is_first: F::ONE, - data: DataCols { - a_ptr, - write_a, - b_ptr, - idx: F::from_canonical_usize(length), - result, - alpha, + }; + cols.b = b; + + // result = result * alpha + (b - a) + result = FieldExtension::add( + FieldExtension::multiply(result, alpha), + FieldExtension::subtract(b, elem_to_ext(a)), + ); + } + + // Instruction1Cols + { + let start = *trace_offset + length * OVERALL_WIDTH; + let cols: &mut Instruction1Cols = trace[start..start + INS_1_WIDTH].borrow_mut(); + *cols = Instruction1Cols { + prefix: PrefixCols { + general: GeneralCols { + is_workload_row: F::ZERO, + is_ins_row: F::ONE, + timestamp: F::from_canonical_u32(timestamp_start), + }, + a_or_is_first: F::ONE, + data: DataCols { + a_ptr, + write_a, + b_ptr, + idx: F::from_canonical_usize(length), + result, + alpha, + }, }, - }, - pc: record.pc, - a_ptr_ptr, - a_ptr_aux, - b_ptr_ptr, - b_ptr_aux, - write_a_x_is_first: write_a, - }; - } - // Instruction2Cols - { - let start = (length + 1) * OVERALL_WIDTH; - let cols: &mut Instruction2Cols = slice[start..start + INS_2_WIDTH].borrow_mut(); - *cols = Instruction2Cols { - general: GeneralCols { + pc: F::from_canonical_u32(*state.pc), + a_ptr_ptr: a, + a_ptr_aux, + b_ptr_ptr: b, + b_ptr_aux, + write_a_x_is_first: write_a, + }; + } + + // Instruction2Cols + { + let start = *trace_offset + (length + 1) * OVERALL_WIDTH; + let cols: &mut Instruction2Cols = trace[start..start + INS_2_WIDTH].borrow_mut(); + cols.general = GeneralCols { is_workload_row: F::ZERO, is_ins_row: F::ONE, - timestamp: record.start_timestamp, - }, - is_first: F::ZERO, - length_ptr, - length_aux, - alpha_ptr, - alpha_aux, - result_ptr, - result_aux, - hint_id_ptr, - is_init_ptr, - is_init_aux, - write_a_x_is_first: F::ZERO, - }; - } -} + timestamp: F::from_canonical_u32(timestamp_start), + }; + cols.is_first = F::ZERO; + cols.length_ptr = c; + cols.length_aux = length_aux; + cols.alpha_ptr = d; + cols.alpha_aux = alpha_aux; + cols.result_ptr = e; + cols.hint_id_ptr = f; + cols.is_init_ptr = g; + cols.is_init_aux = is_init_aux; + cols.write_a_x_is_first = F::ZERO; + + tracing_write_native(state.memory, result_ptr, &result, &mut cols.result_aux); + + // TODO(ayush): this is a bad hack to make length available to fill_trace_row + cols.result_aux.base.timestamp_lt_aux.lower_decomp[0] = + F::from_canonical_u32(length as u32); + } -impl ChipUsageGetter for FriReducedOpeningChip { - fn air_name(&self) -> String { - "FriReducedOpeningAir".to_string() - } + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); - fn current_trace_height(&self) -> usize { - self.height + *trace_offset += (length + 2) * OVERALL_WIDTH; + + Ok(()) } - fn trace_width(&self) -> usize { - OVERALL_WIDTH + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (is_workload_row, is_ins_row) = { + let cols: &GeneralCols = row_slice[..GENERAL_WIDTH].borrow(); + (cols.is_workload_row.is_one(), cols.is_ins_row.is_one()) + }; + + if is_workload_row { + let cols: &mut WorkloadCols = row_slice[..WL_WIDTH].borrow_mut(); + + let timestamp = cols.prefix.general.timestamp.as_canonical_u32(); + mem_helper.fill_from_prev(timestamp + 3, cols.a_aux.as_mut()); + mem_helper.fill_from_prev(timestamp + 4, cols.b_aux.as_mut()); + } + + if is_ins_row { + let is_ins_1_row = row_slice[GENERAL_WIDTH].is_one(); + + if is_ins_1_row { + let cols: &mut Instruction1Cols = row_slice[..INS_1_WIDTH].borrow_mut(); + let timestamp = cols.prefix.general.timestamp.as_canonical_u32(); + + mem_helper.fill_from_prev(timestamp + 2, cols.a_ptr_aux.as_mut()); + mem_helper.fill_from_prev(timestamp + 3, cols.b_ptr_aux.as_mut()); + } else { + let cols: &mut Instruction2Cols = row_slice[..INS_2_WIDTH].borrow_mut(); + let timestamp = cols.general.timestamp.as_canonical_u32(); + + mem_helper.fill_from_prev(timestamp, cols.alpha_aux.as_mut()); + mem_helper.fill_from_prev(timestamp + 1, cols.length_aux.as_mut()); + mem_helper.fill_from_prev(timestamp + 4, cols.is_init_aux.as_mut()); + + // TODO(ayush): this is bad + let length = cols.result_aux.get_base().timestamp_lt_aux.lower_decomp[0]; + mem_helper.fill_from_prev( + timestamp + 5 + 2 * length.as_canonical_u32(), + cols.result_aux.as_mut(), + ); + } + } } } -impl Chip for FriReducedOpeningChip> +impl StepExecutorE1 for FriReducedOpeningStep where - Val: PrimeField32, + F: PrimeField32, { - fn air(&self) -> AirRef { - Arc::new(self.air) - } - fn generate_air_proof_input(self) -> AirProofInput { - let height = next_power_of_two_or_zero(self.height); - let mut flat_trace = Val::::zero_vec(OVERALL_WIDTH * height); - let chunked_trace = { - let sizes: Vec<_> = self - .records - .par_iter() - .map(|record| OVERALL_WIDTH * record.get_height()) - .collect(); - variable_chunks_mut(&mut flat_trace, &sizes) + fn execute_e1( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { + let &Instruction { + a, + b, + c, + d, + e, + f, + g, + .. + } = instruction; + + let a_ptr_ptr = a.as_canonical_u32(); + let b_ptr_ptr = b.as_canonical_u32(); + let length_ptr = c.as_canonical_u32(); + let alpha_ptr = d.as_canonical_u32(); + let result_ptr = e.as_canonical_u32(); + let hint_id_ptr = f.as_canonical_u32(); + let is_init_ptr = g.as_canonical_u32(); + + let alpha = memory_read_native(state.memory, alpha_ptr); + let [length]: [F; 1] = memory_read_native(state.memory, length_ptr); + let [a_ptr]: [F; 1] = memory_read_native(state.memory, a_ptr_ptr); + let [b_ptr]: [F; 1] = memory_read_native(state.memory, b_ptr_ptr); + let [is_init_read]: [F; 1] = memory_read_native(state.memory, is_init_ptr); + let is_init = is_init_read.as_canonical_u32(); + + let [hint_id_f]: [F; 1] = memory_read_native(state.memory, hint_id_ptr); + let hint_id = hint_id_f.as_canonical_u32() as usize; + + let length = length.as_canonical_u32() as usize; + + let data = if is_init == 0 { + let mut streams = self.streams.lock().unwrap(); + let hint_steam = &mut streams.hint_space[hint_id]; + hint_steam.drain(0..length).collect() + } else { + vec![] }; - let memory = self.offline_memory.lock().unwrap(); - let aux_cols_factory = memory.aux_cols_factory(); + let mut as_and_bs = Vec::with_capacity(length); + #[allow(clippy::needless_range_loop)] + for i in 0..length { + let a_ptr_i = (a_ptr + F::from_canonical_usize(i)).as_canonical_u32(); + let [a]: [F; 1] = if is_init == 0 { + memory_write_native(state.memory, a_ptr_i, &[data[i]]); + [data[i]] + } else { + memory_read_native(state.memory, a_ptr_i) + }; + let b_ptr_i = (b_ptr + F::from_canonical_usize(EXT_DEG * i)).as_canonical_u32(); + let b = memory_read_native::(state.memory, b_ptr_i); + + as_and_bs.push((a, b)); + } + + let mut result = [F::ZERO; EXT_DEG]; + for (a, b) in as_and_bs.into_iter().rev() { + // result = result * alpha + (b - a) + result = FieldExtension::add( + FieldExtension::multiply(result, alpha), + FieldExtension::subtract(b, elem_to_ext(a)), + ); + } + + // TODO(ayush): why do we need this?should this be incremented only in tracegen execute? + // 2 for instruction rows + self.height += length + 2; - self.records - .into_par_iter() - .zip_eq(chunked_trace.into_par_iter()) - .for_each(|(record, slice)| { - record_to_rows(record, &aux_cols_factory, slice, &memory); - }); + memory_write_native(state.memory, result_ptr, &result); - let matrix = RowMajorMatrix::new(flat_trace, OVERALL_WIDTH); - AirProofInput::simple_no_pis(matrix) + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + Ok(()) } -} -fn variable_chunks_mut<'a, T>(mut slice: &'a mut [T], sizes: &[usize]) -> Vec<&'a mut [T]> { - let mut result = Vec::with_capacity(sizes.len()); - for &size in sizes { - // split_at_mut guarantees disjoint slices - let (left, right) = slice.split_at_mut(size); - result.push(left); - slice = right; // move forward for the next chunk + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + let &Instruction { + a, + b, + c, + d, + e, + f, + g, + .. + } = instruction; + + let length_ptr = c.as_canonical_u32(); + let [length]: [F; 1] = memory_read_native(state.memory, length_ptr); + + self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += length.as_canonical_u32() + 2; + + Ok(()) } - result } + +pub type FriReducedOpeningChip = + NewVmChipWrapper>; diff --git a/extensions/native/circuit/src/fri/tests.rs b/extensions/native/circuit/src/fri/tests.rs index 97dcdbc532..87fd95350f 100644 --- a/extensions/native/circuit/src/fri/tests.rs +++ b/extensions/native/circuit/src/fri/tests.rs @@ -15,8 +15,27 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::Rng; -use super::{super::field_extension::FieldExtension, elem_to_ext, FriReducedOpeningChip, EXT_DEG}; -use crate::OVERALL_WIDTH; +use crate::fri::OVERALL_WIDTH; + +use super::{ + super::field_extension::FieldExtension, elem_to_ext, FriReducedOpeningAir, + FriReducedOpeningChip, FriReducedOpeningStep, EXT_DEG, +}; + +const MAX_INS_CAPACITY: usize = 1024; +type F = BabyBear; + +fn create_test_chip( + tester: &VmChipTestBuilder, + streams: Arc>>, +) -> FriReducedOpeningChip { + FriReducedOpeningChip::::new( + FriReducedOpeningAir::new(tester.execution_bridge(), tester.memory_bridge()), + FriReducedOpeningStep::new(streams), + MAX_INS_CAPACITY, + tester.memory_helper(), + ) +} fn compute_fri_mat_opening( alpha: [F; EXT_DEG], @@ -44,13 +63,7 @@ fn fri_mat_opening_air_test() { let mut tester = VmChipTestBuilder::default(); let streams = Arc::new(Mutex::new(Streams::default())); - let mut chip = FriReducedOpeningChip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - tester.offline_memory_mutex_arc(), - streams.clone(), - ); + let mut chip = create_test_chip(&tester, streams.clone()); let mut rng = create_seeded_rng(); @@ -85,39 +98,39 @@ fn fri_mat_opening_air_test() { let address_space = 4usize; - /*tracing::debug!( - "{opcode:?} d = {}, e = {}, f = {}, result_addr = {}, addr1 = {}, addr2 = {}, z = {}, x = {}, y = {}", - result_as, as1, as2, result_pointer, address1, address2, result, operand1, operand2, - );*/ + // tracing::debug!( + // "{opcode:?} d = {}, e = {}, f = {}, result_addr = {}, addr1 = {}, addr2 = {}, z = {}, x = {}, y = {}", + // result_as, as1, as2, result_pointer, address1, address2, result, operand1, operand2, + // ); tester.write(address_space, alpha_pointer, alpha); - tester.write_cell( + tester.write( address_space, length_pointer, - BabyBear::from_canonical_usize(length), + [BabyBear::from_canonical_usize(length)], ); - tester.write_cell( + tester.write( address_space, a_pointer_pointer, - BabyBear::from_canonical_usize(a_pointer), + [BabyBear::from_canonical_usize(a_pointer)], ); - tester.write_cell( + tester.write( address_space, b_pointer_pointer, - BabyBear::from_canonical_usize(b_pointer), + [BabyBear::from_canonical_usize(b_pointer)], ); let is_init = rng.gen_range(0..2); - tester.write_cell( + tester.write( address_space, is_init_ptr, - BabyBear::from_canonical_u32(is_init), + [BabyBear::from_canonical_u32(is_init)], ); if is_init == 0 { streams.lock().unwrap().hint_space[0].extend_from_slice(&a); } else { for (i, ai) in a.iter().enumerate() { - tester.write_cell(address_space, a_pointer + i, *ai); + tester.write(address_space, a_pointer + i, [*ai]); } } for (i, bi) in b.iter().enumerate() { @@ -142,7 +155,7 @@ fn fri_mat_opening_air_test() { assert_eq!(result, tester.read(address_space, result_pointer)); // Check that `a` was populated. for (i, ai) in a.iter().enumerate() { - let found = tester.read_cell(address_space, a_pointer + i); + let [found] = tester.read(address_space, a_pointer + i); assert_eq!(*ai, found); } } diff --git a/extensions/native/circuit/src/jal/mod.rs b/extensions/native/circuit/src/jal/mod.rs index 31e342b0f3..7fbeace742 100644 --- a/extensions/native/circuit/src/jal/mod.rs +++ b/extensions/native/circuit/src/jal/mod.rs @@ -1,40 +1,38 @@ use std::{ borrow::{Borrow, BorrowMut}, ops::Deref, - sync::{Arc, Mutex}, }; use openvm_circuit::{ - arch::{ExecutionBridge, ExecutionError, ExecutionState, InstructionExecutor, PcIncOrSet}, + arch::{ + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, + ExecutionBridge, ExecutionError, ExecutionState, NewVmChipWrapper, PcIncOrSet, Result, + StepExecutorE1, TraceStep, VmStateMut, + }, system::memory::{ offline_checker::{MemoryBridge, MemoryWriteAuxCols}, - MemoryAddress, MemoryAuxColsFactory, MemoryController, OfflineMemory, RecordId, + online::{GuestMemory, TracingMemory}, + MemoryAddress, MemoryAuxColsFactory, }, }; -use openvm_circuit_primitives::{ - utils::next_power_of_two_or_zero, - var_range::{ - SharedVariableRangeCheckerChip, VariableRangeCheckerBus, VariableRangeCheckerChip, - }, +use openvm_circuit_primitives::var_range::{ + SharedVariableRangeCheckerChip, VariableRangeCheckerBus, }; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; use openvm_native_compiler::{conversion::AS, NativeJalOpcode, NativeRangeCheckOpcode}; use openvm_stark_backend::{ - config::{StarkGenericConfig, Val}, interaction::InteractionBuilder, p3_air::{Air, AirBuilder, BaseAir}, p3_field::{Field, FieldAlgebra, PrimeField32}, - p3_matrix::{dense::RowMajorMatrix, Matrix}, - p3_maybe_rayon::prelude::*, - prover::types::AirProofInput, + p3_matrix::Matrix, rap::{BaseAirWithPublicValues, PartitionedBaseAir}, - AirRef, Chip, ChipUsageGetter, }; -use serde::{Deserialize, Serialize}; use static_assertions::const_assert_eq; use AS::Native; +use crate::adapters::{memory_read_native, memory_write_native, tracing_write_native}; + #[cfg(test)] mod tests; @@ -57,7 +55,7 @@ struct JalRangeCheckCols { const OVERALL_WIDTH: usize = JalRangeCheckCols::::width(); const_assert_eq!(OVERALL_WIDTH, 12); -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, derive_new::new)] pub struct JalRangeCheckAir { execution_bridge: ExecutionBridge, memory_bridge: MemoryBridge, @@ -136,207 +134,211 @@ where } } -impl JalRangeCheckAir { - fn new( - execution_bridge: ExecutionBridge, - memory_bridge: MemoryBridge, - range_bus: VariableRangeCheckerBus, - ) -> Self { - Self { - execution_bridge, - memory_bridge, - range_bus, - } - } -} - -#[repr(C)] -#[derive(Serialize, Deserialize)] -pub struct JalRangeCheckRecord { - pub state: ExecutionState, - pub a_rw: RecordId, - pub b: u32, - pub c: u8, - pub is_jal: bool, -} - /// Chip for JAL and RANGE_CHECK. These opcodes are logically irrelevant. Putting these opcodes into /// the same chip is just to save columns. -pub struct JalRangeCheckChip { - air: JalRangeCheckAir, - pub records: Vec, - offline_memory: Arc>>, +pub struct JalRangeCheckStep { range_checker_chip: SharedVariableRangeCheckerChip, /// If true, ignore execution errors. debug: bool, } -impl JalRangeCheckChip { - pub fn new( - execution_bridge: ExecutionBridge, - offline_memory: Arc>>, - range_checker_chip: SharedVariableRangeCheckerChip, - ) -> Self { - let memory_bridge = offline_memory.lock().unwrap().memory_bridge(); - let air = JalRangeCheckAir::new(execution_bridge, memory_bridge, range_checker_chip.bus()); +impl JalRangeCheckStep { + pub fn new(range_checker_chip: SharedVariableRangeCheckerChip) -> Self { Self { - air, - records: vec![], - offline_memory, range_checker_chip, debug: false, } } - pub fn with_debug(mut self) -> Self { + pub fn set_debug(&mut self) { self.debug = true; - self } } -impl InstructionExecutor for JalRangeCheckChip { +impl TraceStep for JalRangeCheckStep +where + F: PrimeField32, +{ + fn get_opcode_name(&self, opcode: usize) -> String { + let jal_opcode = NativeJalOpcode::JAL.global_opcode().as_usize(); + let range_check_opcode = NativeRangeCheckOpcode::RANGE_CHECK + .global_opcode() + .as_usize(); + if opcode == jal_opcode { + return String::from("JAL"); + } + if opcode == range_check_opcode { + return String::from("RANGE_CHECK"); + } + panic!("Unknown opcode {}", opcode); + } + fn execute( &mut self, - memory: &mut MemoryController, + state: VmStateMut, CTX>, instruction: &Instruction, - from_state: ExecutionState, - ) -> Result, ExecutionError> { - if instruction.opcode == NativeJalOpcode::JAL.global_opcode() { - let (record_id, _) = memory.write( - F::from_canonical_u32(AS::Native as u32), - instruction.a, - &[F::from_canonical_u32(from_state.pc + DEFAULT_PC_STEP)], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, + ) -> Result<()> { + let &Instruction { + opcode, a, b, c, .. + } = instruction; + + debug_assert!( + opcode == NativeJalOpcode::JAL.global_opcode() + || opcode == NativeRangeCheckOpcode::RANGE_CHECK.global_opcode() + ); + + let row: &mut JalRangeCheckCols = + trace[*trace_offset..*trace_offset + width].borrow_mut(); + + row.state.pc = F::from_canonical_u32(*state.pc); + row.state.timestamp = F::from_canonical_u32(state.memory.timestamp); + + row.a_pointer = a; + row.b = b; + + if opcode == NativeJalOpcode::JAL.global_opcode() { + row.is_jal = F::ONE; + row.c = F::ZERO; + + tracing_write_native( + state.memory, + a.as_canonical_u32(), + &[F::from_canonical_u32( + state.pc.wrapping_add(DEFAULT_PC_STEP), + )], + &mut row.writes_aux, ); - let b = instruction.b.as_canonical_u32(); - self.records.push(JalRangeCheckRecord { - state: from_state, - a_rw: record_id, - b, - c: 0, - is_jal: true, - }); - return Ok(ExecutionState { - pc: (F::from_canonical_u32(from_state.pc) + instruction.b).as_canonical_u32(), - timestamp: memory.timestamp(), - }); - } else if instruction.opcode == NativeRangeCheckOpcode::RANGE_CHECK.global_opcode() { - let d = F::from_canonical_u32(AS::Native as u32); - // This is a read, but we make the record have prev_data - let a_val = memory.unsafe_read_cell::(d, instruction.a); - let (record_id, _) = memory.write(d, instruction.a, &[a_val]); + // TODO(ayush): can this addition be done in u32 instead of F + *state.pc = (F::from_canonical_u32(*state.pc) + b).as_canonical_u32(); + } else if opcode == NativeRangeCheckOpcode::RANGE_CHECK.global_opcode() { + row.is_jal = F::ZERO; + row.c = c; + + let [a_val]: [F; 1] = memory_read_native(state.memory.data(), a.as_canonical_u32()); + tracing_write_native( + state.memory, + a.as_canonical_u32(), + &[a_val], + &mut row.writes_aux, + ); + + // TODO(ayush): should this debug stuff be removed? let a_val = a_val.as_canonical_u32(); - let b = instruction.b.as_canonical_u32(); - let c = instruction.c.as_canonical_u32(); + let b = b.as_canonical_u32(); + let c = c.as_canonical_u32(); + debug_assert!(!self.debug || b <= 16); debug_assert!(!self.debug || c <= 14); + let x = a_val & ((1 << 16) - 1); if !self.debug && x >= 1 << b { - return Err(ExecutionError::Fail { pc: from_state.pc }); + return Err(ExecutionError::Fail { pc: *state.pc }); } let y = a_val >> 16; if !self.debug && y >= 1 << c { - return Err(ExecutionError::Fail { pc: from_state.pc }); + return Err(ExecutionError::Fail { pc: *state.pc }); } - self.records.push(JalRangeCheckRecord { - state: from_state, - a_rw: record_id, - b, - c: c as u8, - is_jal: false, - }); - return Ok(ExecutionState { - pc: from_state.pc + DEFAULT_PC_STEP, - timestamp: memory.timestamp(), - }); - } - panic!("Unknown opcode {}", instruction.opcode); - } - fn get_opcode_name(&self, opcode: usize) -> String { - let jal_opcode = NativeJalOpcode::JAL.global_opcode().as_usize(); - let range_check_opcode = NativeRangeCheckOpcode::RANGE_CHECK - .global_opcode() - .as_usize(); - if opcode == jal_opcode { - return String::from("JAL"); - } - if opcode == range_check_opcode { - return String::from("RANGE_CHECK"); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); } - panic!("Unknown opcode {}", opcode); - } -} -impl ChipUsageGetter for JalRangeCheckChip { - fn air_name(&self) -> String { - "JalRangeCheck".to_string() - } + *trace_offset += width; - fn current_trace_height(&self) -> usize { - self.records.len() + Ok(()) } - fn trace_width(&self) -> usize { - OVERALL_WIDTH + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let row: &mut JalRangeCheckCols<_> = row_slice.borrow_mut(); + + let timestamp = row.state.timestamp.as_canonical_u32(); + mem_helper.fill_from_prev(timestamp, row.writes_aux.as_mut()); + + row.is_range_check = F::ONE - row.is_jal; + + if row.is_range_check.is_one() { + let a_val = row.writes_aux.prev_data()[0]; + let a_val_u32 = a_val.as_canonical_u32(); + let y = a_val_u32 >> 16; + let x = a_val_u32 & ((1 << 16) - 1); + self.range_checker_chip + .add_count(x, row.b.as_canonical_u32() as usize); + self.range_checker_chip + .add_count(y, row.c.as_canonical_u32() as usize); + row.y = F::from_canonical_u32(y); + } } } -impl Chip for JalRangeCheckChip> +impl StepExecutorE1 for JalRangeCheckStep where - Val: PrimeField32, + F: PrimeField32, { - fn air(&self) -> AirRef { - Arc::new(self.air) - } - fn generate_air_proof_input(self) -> AirProofInput { - let height = next_power_of_two_or_zero(self.records.len()); - let mut flat_trace = Val::::zero_vec(OVERALL_WIDTH * height); - let memory = self.offline_memory.lock().unwrap(); - let aux_cols_factory = memory.aux_cols_factory(); - - self.records - .into_par_iter() - .zip(flat_trace.par_chunks_mut(OVERALL_WIDTH)) - .for_each(|(record, slice)| { - record_to_row( - record, - &aux_cols_factory, - self.range_checker_chip.as_ref(), - slice, - &memory, - ); - }); + fn execute_e1( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { + let &Instruction { opcode, a, b, .. } = instruction; + + debug_assert!( + opcode == NativeJalOpcode::JAL.global_opcode() + || opcode == NativeRangeCheckOpcode::RANGE_CHECK.global_opcode() + ); + + if opcode == NativeJalOpcode::JAL.global_opcode() { + memory_write_native( + state.memory, + a.as_canonical_u32(), + &[F::from_canonical_u32( + state.pc.wrapping_add(DEFAULT_PC_STEP), + )], + ); + // TODO(ayush): can this addition be done in u32 instead of F + *state.pc = (F::from_canonical_u32(*state.pc) + b).as_canonical_u32(); + } else if opcode == NativeRangeCheckOpcode::RANGE_CHECK.global_opcode() { + // TODO(ayush): should this not call memory callback? + let [a_val]: [F; 1] = memory_read_native(state.memory, a.as_canonical_u32()); - let matrix = RowMajorMatrix::new(flat_trace, OVERALL_WIDTH); - AirProofInput::simple_no_pis(matrix) + memory_write_native(state.memory, a.as_canonical_u32(), &[a_val]); + + let a_val = a_val.as_canonical_u32(); + let b = instruction.b.as_canonical_u32(); + let c = instruction.c.as_canonical_u32(); + + debug_assert!(!self.debug || b <= 16); + debug_assert!(!self.debug || c <= 14); + + let x = a_val & ((1 << 16) - 1); + if !self.debug && x >= 1 << b { + return Err(ExecutionError::Fail { pc: *state.pc }); + } + let y = a_val >> 16; + if !self.debug && y >= 1 << c { + return Err(ExecutionError::Fail { pc: *state.pc }); + } + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + } + + Ok(()) } -} -fn record_to_row( - record: JalRangeCheckRecord, - aux_cols_factory: &MemoryAuxColsFactory, - range_checker_chip: &VariableRangeCheckerChip, - slice: &mut [F], - memory: &OfflineMemory, -) { - let a_record = memory.record_by_id(record.a_rw); - let col: &mut JalRangeCheckCols<_> = slice.borrow_mut(); - col.is_jal = F::from_bool(record.is_jal); - col.is_range_check = F::from_bool(!record.is_jal); - col.a_pointer = a_record.pointer; - col.state = ExecutionState { - pc: F::from_canonical_u32(record.state.pc), - timestamp: F::from_canonical_u32(record.state.timestamp), - }; - aux_cols_factory.generate_write_aux(a_record, &mut col.writes_aux); - col.b = F::from_canonical_u32(record.b); - if !record.is_jal { - let a_val = a_record.data_at(0); - let a_val_u32 = a_val.as_canonical_u32(); - let y = a_val_u32 >> 16; - let x = a_val_u32 & ((1 << 16) - 1); - range_checker_chip.add_count(x, record.b as usize); - range_checker_chip.add_count(y, record.c as usize); - col.c = F::from_canonical_u32(record.c as u32); - col.y = F::from_canonical_u32(y); + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; + + Ok(()) } } + +pub type JalRangeCheckChip = NewVmChipWrapper; diff --git a/extensions/native/circuit/src/jal/tests.rs b/extensions/native/circuit/src/jal/tests.rs index dd56b73c8f..f2ca52bb07 100644 --- a/extensions/native/circuit/src/jal/tests.rs +++ b/extensions/native/circuit/src/jal/tests.rs @@ -1,6 +1,6 @@ use std::borrow::BorrowMut; -use openvm_circuit::arch::{testing::VmChipTestBuilder, ExecutionBridge}; +use openvm_circuit::arch::testing::VmChipTestBuilder; use openvm_instructions::{ instruction::Instruction, program::{DEFAULT_PC_STEP, PC_BITS}, @@ -16,9 +16,26 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; -use crate::{jal::JalRangeCheckCols, JalRangeCheckChip}; +use crate::jal::{JalRangeCheckChip, JalRangeCheckCols}; + +use super::{JalRangeCheckAir, JalRangeCheckStep}; + +const MAX_INS_CAPACITY: usize = 128; type F = BabyBear; +fn create_test_chip(tester: &VmChipTestBuilder) -> JalRangeCheckChip { + JalRangeCheckChip::::new( + JalRangeCheckAir::new( + tester.execution_bridge(), + tester.memory_bridge(), + tester.range_checker().bus(), + ), + JalRangeCheckStep::new(tester.range_checker().clone()), + MAX_INS_CAPACITY, + tester.memory_helper(), + ) +} + fn set_and_execute( tester: &mut VmChipTestBuilder, chip: &mut JalRangeCheckChip, @@ -61,7 +78,7 @@ fn set_and_execute_range_check( for RangeCheckTestCase { val, x_bit, y_bit } in test_cases { let d = 4usize; - tester.write_cell(d, a, F::from_canonical_u32(val)); + tester.write(d, a, [F::from_canonical_u32(val)]); tester.execute_with_pc( chip, &Instruction::from_usize( @@ -73,19 +90,12 @@ fn set_and_execute_range_check( } } -fn setup() -> (StdRng, VmChipTestBuilder, JalRangeCheckChip) { - let rng = create_seeded_rng(); - let tester = VmChipTestBuilder::default(); - let execution_bridge = ExecutionBridge::new(tester.execution_bus(), tester.program_bus()); - let offline_memory = tester.offline_memory_mutex_arc(); - let range_checker = tester.range_checker(); - let chip = JalRangeCheckChip::::new(execution_bridge, offline_memory, range_checker); - (rng, tester, chip) -} - #[test] fn rand_jal_test() { - let (mut rng, mut tester, mut chip) = setup(); + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let mut chip = create_test_chip(&tester); + let num_tests: usize = 100; for _ in 0..num_tests { set_and_execute(&mut tester, &mut chip, &mut rng, None, None); @@ -97,7 +107,10 @@ fn rand_jal_test() { #[test] fn rand_range_check_test() { - let (mut rng, mut tester, mut chip) = setup(); + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let mut chip = create_test_chip(&tester); + let f = |x: u32, y: u32| RangeCheckTestCase { val: x + y * (1 << 16), x_bit: 32 - x.leading_zeros(), @@ -129,8 +142,11 @@ fn rand_range_check_test() { #[test] fn negative_range_check_test() { { - let (mut rng, mut tester, chip) = setup(); - let mut chip = chip.with_debug(); + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let mut chip = create_test_chip(&tester); + chip.step.set_debug(); + set_and_execute_range_check( &mut tester, &mut chip, @@ -147,8 +163,11 @@ fn negative_range_check_test() { assert!(result.is_err()); } { - let (mut rng, mut tester, chip) = setup(); - let mut chip = chip.with_debug(); + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let mut chip = create_test_chip(&tester); + chip.step.set_debug(); + set_and_execute_range_check( &mut tester, &mut chip, @@ -168,7 +187,10 @@ fn negative_range_check_test() { #[test] fn negative_jal_test() { - let (mut rng, mut tester, mut chip) = setup(); + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let mut chip = create_test_chip(&tester); + set_and_execute(&mut tester, &mut chip, &mut rng, None, None); let tester = tester.build(); diff --git a/extensions/native/circuit/src/lib.rs b/extensions/native/circuit/src/lib.rs index 46c6bc890f..069c19dcf1 100644 --- a/extensions/native/circuit/src/lib.rs +++ b/extensions/native/circuit/src/lib.rs @@ -7,16 +7,16 @@ mod field_extension; mod fri; mod jal; mod loadstore; -mod poseidon2; +// mod poseidon2; pub use branch_eq::*; pub use castf::*; pub use field_arithmetic::*; pub use field_extension::*; -pub use fri::*; +// pub use fri::*; pub use jal::*; pub use loadstore::*; -pub use poseidon2::*; +// pub use poseidon2::*; mod extension; pub use extension::*; diff --git a/extensions/native/circuit/src/loadstore/core.rs b/extensions/native/circuit/src/loadstore/core.rs index 40948f5c60..9d61fa95fb 100644 --- a/extensions/native/circuit/src/loadstore/core.rs +++ b/extensions/native/circuit/src/loadstore/core.rs @@ -1,17 +1,20 @@ use std::{ array, borrow::{Borrow, BorrowMut}, - marker::PhantomData, sync::{Arc, Mutex, OnceLock}, }; use openvm_circuit::{ arch::{ - instructions::LocalOpcode, AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, - ExecutionError, InsExecutorE1, Result, StepExecutorE1, Streams, VmAdapterInterface, - VmCoreAir, VmCoreChip, VmExecutionState, + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, + instructions::LocalOpcode, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ExecutionError, Result, + StepExecutorE1, Streams, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::online::GuestMemory, }; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP}; @@ -49,7 +52,7 @@ pub struct NativeLoadStoreCoreRecord { pub data: [F; NUM_CELLS], } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, derive_new::new)] pub struct NativeLoadStoreCoreAir { pub offset: usize, } @@ -119,76 +122,48 @@ where } #[derive(Debug)] -pub struct NativeLoadStoreStep +pub struct NativeLoadStoreCoreStep where F: Field, { + adapter: A, offset: usize, pub streams: OnceLock>>>, - phantom: PhantomData, -} - -impl Default for NativeLoadStoreStep -where - F: Field, -{ - fn default() -> Self { - Self::new(NativeLoadStoreOpcode::CLASS_OFFSET) - } } -impl NativeLoadStoreStep +impl NativeLoadStoreCoreStep where F: Field, { - pub fn new(offset: usize) -> Self { + pub fn new(adapter: A, offset: usize) -> Self { Self { + adapter, offset, streams: OnceLock::new(), - phantom: PhantomData, } } pub fn set_streams(&mut self, streams: Arc>>) { self.streams.set(streams).unwrap(); } - - #[inline] - pub fn execute_trace_core( - &self, - instruction: &Instruction, - [x, y]: [[u8; NUM_LIMBS]; 2], - core_row: &mut [F], - ) -> [u8; NUM_LIMBS] - where - F: PrimeField32, - { - todo!("Implement execute_trace_core") - } - - pub fn fill_trace_row_core(&self, core_row: &mut [F]) - where - F: PrimeField32, - { - todo!("Implement fill_trace_row_core") - } } -impl TraceStep for NativeLoadStoreStep +impl TraceStep + for NativeLoadStoreCoreStep where F: PrimeField32, A: 'static + for<'a> AdapterTraceStep< F, CTX, - ReadData = [[u8; NUM_LIMBS]; 2], - WriteData = [u8; NUM_LIMBS], - TraceContext<'a> = &'a BitwiseOperationLookupChip, + ReadData = (F, [F; NUM_CELLS]), + WriteData = [F; NUM_CELLS], + TraceContext<'a> = F, >, { fn get_opcode_name(&self, opcode: usize) -> String { format!( "{:?}", - NativeLoadStoreOpcode::from_usize(opcode - self.air.offset) + NativeLoadStoreOpcode::from_usize(opcode - self.offset) ) } @@ -196,124 +171,105 @@ where &mut self, state: VmStateMut, CTX>, instruction: &Instruction, - row_slice: &mut [F], + trace: &mut [F], + trace_offset: &mut usize, + width: usize, ) -> Result<()> { - todo!("Implement execute") + let &Instruction { opcode, .. } = instruction; + + let local_opcode = NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); + + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + A::start(*state.pc, state.memory, adapter_row); + + let (pointer_read, data_read) = self.adapter.read(state.memory, instruction, adapter_row); + + let data = if local_opcode == NativeLoadStoreOpcode::HINT_STOREW { + let mut streams = self.streams.get().unwrap().lock().unwrap(); + if streams.hint_stream.len() < NUM_CELLS { + return Err(ExecutionError::HintOutOfBounds { pc: *state.pc }); + } + array::from_fn(|_| streams.hint_stream.pop_front().unwrap()) + } else { + data_read + }; + + self.adapter + .write(state.memory, instruction, adapter_row, &data); + + let core_row: &mut NativeLoadStoreCoreCols = core_row.borrow_mut(); + + core_row.pointer_read = pointer_read; + core_row.data = data; + core_row.is_loadw = F::from_bool(local_opcode == NativeLoadStoreOpcode::LOADW); + core_row.is_storew = F::from_bool(local_opcode == NativeLoadStoreOpcode::STOREW); + core_row.is_hint_storew = F::from_bool(local_opcode == NativeLoadStoreOpcode::HINT_STOREW); + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + *trace_offset += width; + + Ok(()) } fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { - todo!("Implement fill_trace_row") + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + + let core_row: &mut NativeLoadStoreCoreCols = core_row.borrow_mut(); + self.adapter + .fill_trace_row(mem_helper, core_row.is_hint_storew, adapter_row); } } -impl StepExecutorE1 - for NativeLoadStoreStep +impl StepExecutorE1 for NativeLoadStoreCoreStep where F: PrimeField32, A: 'static - + for<'a> AdapterExecutorE1< - Mem, - F, - ReadData = (F, [F; NUM_CELLS]), - WriteData = [F; NUM_CELLS], - >, + + for<'a> AdapterExecutorE1, { - fn execute_e1( + fn execute_e1( &mut self, - state: &mut VmExecutionState, + state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { - let Instruction { - opcode, - a, - b, - c, - d, - e, - .. - } = instruction; + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { + let Instruction { opcode, .. } = instruction; // Get the local opcode for this instruction - let local_opcode = - NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); + let local_opcode = NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let (pointer_read, data_read) = A::read(&mut state.memory, instruction); + let (_, data_read) = self.adapter.read(state, instruction); let data = if local_opcode == NativeLoadStoreOpcode::HINT_STOREW { let mut streams = self.streams.get().unwrap().lock().unwrap(); if streams.hint_stream.len() < NUM_CELLS { - return Err(ExecutionError::HintOutOfBounds { pc: state.pc }); + return Err(ExecutionError::HintOutOfBounds { pc: *state.pc }); } array::from_fn(|_| streams.hint_stream.pop_front().unwrap()) } else { data_read }; - A::write(&mut state.memory, instruction, &data); + self.adapter.write(state, instruction, &data); - state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); Ok(()) } -} -// impl, const NUM_CELLS: usize> VmCoreChip -// for NativeLoadStoreCoreChip -// where -// I::Reads: Into<(F, [F; NUM_CELLS])>, -// I::Writes: From<[F; NUM_CELLS]>, -// { -// type Record = NativeLoadStoreCoreRecord; -// type Air = NativeLoadStoreCoreAir; - -// fn execute_instruction( -// &self, -// instruction: &Instruction, -// from_pc: u32, -// reads: I::Reads, -// ) -> Result<(AdapterRuntimeContext, Self::Record)> { -// let Instruction { opcode, .. } = *instruction; -// let local_opcode = -// NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)); -// let (pointer_read, data_read) = reads.into(); - -// let data = if local_opcode == NativeLoadStoreOpcode::HINT_STOREW { -// let mut streams = self.streams.get().unwrap().lock().unwrap(); -// if streams.hint_stream.len() < NUM_CELLS { -// return Err(ExecutionError::HintOutOfBounds { pc: from_pc }); -// } -// array::from_fn(|_| streams.hint_stream.pop_front().unwrap()) -// } else { -// data_read -// }; - -// let output = AdapterRuntimeContext::without_pc(data); -// let record = NativeLoadStoreCoreRecord { -// opcode: NativeLoadStoreOpcode::from_usize(opcode.local_opcode_idx(self.air.offset)), -// pointer_read, -// data, -// }; -// Ok((output, record)) -// } - -// fn get_opcode_name(&self, opcode: usize) -> String { -// format!( -// "{:?}", -// NativeLoadStoreOpcode::from_usize(opcode - self.air.offset) -// ) -// } - -// fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { -// let cols: &mut NativeLoadStoreCoreCols<_, NUM_CELLS> = row_slice.borrow_mut(); -// cols.is_loadw = F::from_bool(record.opcode == NativeLoadStoreOpcode::LOADW); -// cols.is_storew = F::from_bool(record.opcode == NativeLoadStoreOpcode::STOREW); -// cols.is_hint_storew = F::from_bool(record.opcode == NativeLoadStoreOpcode::HINT_STOREW); - -// cols.pointer_read = record.pointer_read; -// cols.data = record.data; -// } - -// fn air(&self) -> &Self::Air { -// &self.air -// } -// } + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> Result<()> { + self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; + + Ok(()) + } +} diff --git a/extensions/native/circuit/src/loadstore/mod.rs b/extensions/native/circuit/src/loadstore/mod.rs index 15f85c6582..e9dadaa038 100644 --- a/extensions/native/circuit/src/loadstore/mod.rs +++ b/extensions/native/circuit/src/loadstore/mod.rs @@ -1,4 +1,4 @@ -use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper, VmChipWrapper}; +use openvm_circuit::arch::{NewVmChipWrapper, VmAirWrapper}; #[cfg(test)] mod tests; @@ -7,13 +7,12 @@ mod core; pub use core::*; use super::adapters::loadstore_native_adapter::{ - NativeLoadStoreAdapterAir, NativeLoadStoreAdapterChip, + NativeLoadStoreAdapterAir, NativeLoadStoreAdapterStep, }; -use crate::adapters::loadstore_native_adapter::NativeLoadStoreAdapterStep; pub type NativeLoadStoreAir = VmAirWrapper, NativeLoadStoreCoreAir>; -pub type NativeLoadStoreStepWithAdapter = - NativeLoadStoreCoreStep>; +pub type NativeLoadStoreStep = + NativeLoadStoreCoreStep, F, NUM_CELLS>; pub type NativeLoadStoreChip = - NewVmChipWrapper, NativeLoadStoreStepWithAdapter>; + NewVmChipWrapper, NativeLoadStoreStep>; diff --git a/extensions/native/circuit/src/loadstore/tests.rs b/extensions/native/circuit/src/loadstore/tests.rs index cd653c2fc0..18814c9af0 100644 --- a/extensions/native/circuit/src/loadstore/tests.rs +++ b/extensions/native/circuit/src/loadstore/tests.rs @@ -1,17 +1,19 @@ use std::sync::{Arc, Mutex}; -use openvm_circuit::arch::{testing::VmChipTestBuilder, Streams}; +use openvm_circuit::arch::{testing::VmChipTestBuilder, Streams, VmAirWrapper}; use openvm_instructions::{instruction::Instruction, LocalOpcode}; use openvm_native_compiler::NativeLoadStoreOpcode::{self, *}; use openvm_stark_backend::p3_field::{FieldAlgebra, PrimeField32}; use openvm_stark_sdk::{config::setup_tracing, p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; -use super::{ - super::adapters::loadstore_native_adapter::NativeLoadStoreAdapterChip, NativeLoadStoreChip, - NativeLoadStoreCoreChip, +use crate::adapters::loadstore_native_adapter::{ + NativeLoadStoreAdapterAir, NativeLoadStoreAdapterStep, }; +use super::{NativeLoadStoreChip, NativeLoadStoreCoreAir, NativeLoadStoreCoreStep}; + +const MAX_INS_CAPACITY: usize = 128; type F = BabyBear; #[derive(Debug)] @@ -28,20 +30,22 @@ struct TestData { is_hint: bool, } -fn setup() -> (StdRng, VmChipTestBuilder, NativeLoadStoreChip) { - let rng = create_seeded_rng(); - let tester = VmChipTestBuilder::default(); - - let adapter = NativeLoadStoreAdapterChip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - NativeLoadStoreOpcode::CLASS_OFFSET, +fn create_test_chip(tester: &VmChipTestBuilder) -> NativeLoadStoreChip { + let mut chip = NativeLoadStoreChip::::new( + VmAirWrapper::new( + NativeLoadStoreAdapterAir::new(tester.memory_bridge(), tester.execution_bridge()), + NativeLoadStoreCoreAir::new(NativeLoadStoreOpcode::CLASS_OFFSET), + ), + NativeLoadStoreCoreStep::new( + NativeLoadStoreAdapterStep::new(NativeLoadStoreOpcode::CLASS_OFFSET), + NativeLoadStoreOpcode::CLASS_OFFSET, + ), + MAX_INS_CAPACITY, + tester.memory_helper(), ); - let mut inner = NativeLoadStoreCoreChip::new(NativeLoadStoreOpcode::CLASS_OFFSET); - inner.set_streams(Arc::new(Mutex::new(Streams::default()))); - let chip = NativeLoadStoreChip::::new(adapter, inner, tester.offline_memory_mutex_arc()); - (rng, tester, chip) + chip.step + .set_streams(Arc::new(Mutex::new(Streams::default()))); + chip } fn gen_test_data(rng: &mut StdRng, opcode: NativeLoadStoreOpcode) -> TestData { @@ -102,7 +106,7 @@ fn set_values( } if data.is_hint { for _ in 0..data.e.as_canonical_u32() { - chip.core + chip.step .streams .get() .unwrap() @@ -164,7 +168,11 @@ fn set_and_execute( #[test] fn rand_native_loadstore_test() { setup_tracing(); - let (mut rng, mut tester, mut chip) = setup(); + + let mut rng = create_seeded_rng(); + let mut tester = VmChipTestBuilder::default(); + let mut chip = create_test_chip(&tester); + for _ in 0..20 { set_and_execute(&mut tester, &mut chip, &mut rng, STOREW); set_and_execute(&mut tester, &mut chip, &mut rng, HINT_STOREW); diff --git a/extensions/native/circuit/src/poseidon2/trace.rs b/extensions/native/circuit/src/poseidon2/trace.rs index df8547767f..a33e600404 100644 --- a/extensions/native/circuit/src/poseidon2/trace.rs +++ b/extensions/native/circuit/src/poseidon2/trace.rs @@ -1,6 +1,6 @@ use std::{borrow::BorrowMut, sync::Arc}; -use openvm_circuit::system::memory::{MemoryAuxColsFactory, OfflineMemory}; +use openvm_circuit::system::memory::MemoryAuxColsFactory; use openvm_circuit_primitives::utils::next_power_of_two_or_zero; use openvm_instructions::{instruction::Instruction, LocalOpcode}; use openvm_native_compiler::Poseidon2Opcode::COMP_POS2; diff --git a/extensions/rv32im/circuit/src/adapters/alu.rs b/extensions/rv32im/circuit/src/adapters/alu.rs index 242a11f90b..271405fbab 100644 --- a/extensions/rv32im/circuit/src/adapters/alu.rs +++ b/extensions/rv32im/circuit/src/adapters/alu.rs @@ -31,7 +31,7 @@ use openvm_stark_backend::{ use super::{ tracing_read, tracing_read_imm, tracing_write, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS, }; -use crate::adapters::{memory_read_from_state, memory_write_from_state}; +use crate::adapters::{imm_to_bytes, memory_read_from_state, memory_write_from_state}; #[repr(C)] #[derive(AlignedBorrow)] @@ -309,11 +309,7 @@ where memory_read_from_state(state, RV32_REGISTER_AS, c.as_canonical_u32()); rs2 } else { - let imm = c.as_canonical_u32(); - debug_assert_eq!(imm >> 24, 0); - let mut imm_le = imm.to_le_bytes(); - imm_le[3] = imm_le[2]; - imm_le + imm_to_bytes(c.as_canonical_u32()) }; [rs1, rs2] diff --git a/extensions/rv32im/circuit/src/adapters/mod.rs b/extensions/rv32im/circuit/src/adapters/mod.rs index 484c646a52..388bfb9d32 100644 --- a/extensions/rv32im/circuit/src/adapters/mod.rs +++ b/extensions/rv32im/circuit/src/adapters/mod.rs @@ -55,6 +55,14 @@ pub fn decompose(value: u32) -> [F; RV32_REGISTER_NUM_LIMBS] { }) } +#[inline(always)] +pub fn imm_to_bytes(imm: u32) -> [u8; RV32_REGISTER_NUM_LIMBS] { + debug_assert_eq!(imm >> 24, 0); + let mut imm_le = imm.to_le_bytes(); + imm_le[3] = imm_le[2]; + imm_le +} + #[inline(always)] pub fn memory_read(memory: &GuestMemory, address_space: u32, ptr: u32) -> [u8; N] { debug_assert!( @@ -99,7 +107,7 @@ pub fn memory_read_from_state( where Ctx: E1E2ExecutionCtx, { - state.ctx.on_memory_operation(address_space, ptr, N); + state.ctx.on_memory_operation(address_space, ptr, N as u32); memory_read(state.memory, address_space, ptr) } @@ -113,7 +121,7 @@ pub fn memory_write_from_state( ) where Ctx: E1E2ExecutionCtx, { - state.ctx.on_memory_operation(address_space, ptr, N); + state.ctx.on_memory_operation(address_space, ptr, N as u32); memory_write(state.memory, address_space, ptr, data) } @@ -177,6 +185,20 @@ where data } +#[inline(always)] +pub fn tracing_read_imm( + memory: &mut TracingMemory, + imm: u32, + imm_mut: &mut F, +) -> [u8; RV32_REGISTER_NUM_LIMBS] +where + F: PrimeField32, +{ + *imm_mut = F::from_canonical_u32(imm); + memory.increment_timestamp(); + imm_to_bytes(imm) +} + /// Writes `reg_ptr, reg_val` into memory and records the memory access in mutable buffer. /// Trace generation relevant to this memory access can be done fully from the recorded buffer. #[inline(always)] @@ -213,27 +235,6 @@ pub fn tracing_write_with_base_aux( base_aux_cols.set_prev(F::from_canonical_u32(t_prev)); } -#[inline(always)] -pub fn tracing_read_imm( - memory: &mut TracingMemory, - imm: u32, - imm_mut: &mut F, -) -> [u8; RV32_REGISTER_NUM_LIMBS] -where - F: PrimeField32, -{ - *imm_mut = F::from_canonical_u32(imm); - debug_assert_eq!(imm >> 24, 0); // highest byte should be zero to prevent overflow - - memory.increment_timestamp(); - - let mut imm_le = imm.to_le_bytes(); - // Important: we set the highest byte equal to the second highest byte, using the assumption - // that imm is at most 24 bits - imm_le[3] = imm_le[2]; - imm_le -} - // TODO: delete /// Read register value as [RV32_REGISTER_NUM_LIMBS] limbs from memory. /// Returns the read record and the register value as u32. diff --git a/extensions/rv32im/circuit/src/auipc/core.rs b/extensions/rv32im/circuit/src/auipc/core.rs index e8cd98ac4a..7e037df397 100644 --- a/extensions/rv32im/circuit/src/auipc/core.rs +++ b/extensions/rv32im/circuit/src/auipc/core.rs @@ -193,12 +193,12 @@ where } } -pub struct Rv32AuipcStep { +pub struct Rv32AuipcCoreStep { adapter: A, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, } -impl Rv32AuipcStep { +impl Rv32AuipcCoreStep { pub fn new( adapter: A, bitwise_lookup_chip: SharedBitwiseOperationLookupChip, @@ -210,7 +210,7 @@ impl Rv32AuipcStep { } } -impl TraceStep for Rv32AuipcStep +impl TraceStep for Rv32AuipcCoreStep where F: PrimeField32, A: 'static @@ -304,7 +304,7 @@ where } } -impl StepExecutorE1 for Rv32AuipcStep +impl StepExecutorE1 for Rv32AuipcCoreStep where F: PrimeField32, A: 'static @@ -339,8 +339,8 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - state.ctx.trace_heights[chip_index] += 1; self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; Ok(()) } diff --git a/extensions/rv32im/circuit/src/auipc/mod.rs b/extensions/rv32im/circuit/src/auipc/mod.rs index 15036753bd..f2aa252ee8 100644 --- a/extensions/rv32im/circuit/src/auipc/mod.rs +++ b/extensions/rv32im/circuit/src/auipc/mod.rs @@ -9,5 +9,5 @@ pub use core::*; mod tests; pub type Rv32AuipcAir = VmAirWrapper; -pub type Rv32AuipcStepWithAdapter = Rv32AuipcStep; -pub type Rv32AuipcChip = NewVmChipWrapper; +pub type Rv32AuipcStep = Rv32AuipcCoreStep; +pub type Rv32AuipcChip = NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/base_alu/core.rs b/extensions/rv32im/circuit/src/base_alu/core.rs index 7e87739569..b63ef95479 100644 --- a/extensions/rv32im/circuit/src/base_alu/core.rs +++ b/extensions/rv32im/circuit/src/base_alu/core.rs @@ -311,8 +311,8 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - state.ctx.trace_heights[chip_index] += 1; self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; Ok(()) } diff --git a/extensions/rv32im/circuit/src/branch_eq/core.rs b/extensions/rv32im/circuit/src/branch_eq/core.rs index b95bb0f79b..91547cf3f1 100644 --- a/extensions/rv32im/circuit/src/branch_eq/core.rs +++ b/extensions/rv32im/circuit/src/branch_eq/core.rs @@ -262,8 +262,8 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - state.ctx.trace_heights[chip_index] += 1; self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; Ok(()) } diff --git a/extensions/rv32im/circuit/src/branch_eq/mod.rs b/extensions/rv32im/circuit/src/branch_eq/mod.rs index 0ed0d44c1d..9172197e2a 100644 --- a/extensions/rv32im/circuit/src/branch_eq/mod.rs +++ b/extensions/rv32im/circuit/src/branch_eq/mod.rs @@ -11,7 +11,5 @@ mod tests; pub type Rv32BranchEqualAir = VmAirWrapper>; -pub type Rv32BranchEqualStepWithAdapter = - BranchEqualStep; -pub type Rv32BranchEqualChip = - NewVmChipWrapper; +pub type Rv32BranchEqualStep = BranchEqualStep; +pub type Rv32BranchEqualChip = NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/branch_lt/core.rs b/extensions/rv32im/circuit/src/branch_lt/core.rs index 6240c064f5..9a777b2d1a 100644 --- a/extensions/rv32im/circuit/src/branch_lt/core.rs +++ b/extensions/rv32im/circuit/src/branch_lt/core.rs @@ -391,8 +391,8 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - state.ctx.trace_heights[chip_index] += 1; self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; Ok(()) } diff --git a/extensions/rv32im/circuit/src/branch_lt/mod.rs b/extensions/rv32im/circuit/src/branch_lt/mod.rs index 094e72afc9..dba3751be2 100644 --- a/extensions/rv32im/circuit/src/branch_lt/mod.rs +++ b/extensions/rv32im/circuit/src/branch_lt/mod.rs @@ -13,7 +13,7 @@ pub type Rv32BranchLessThanAir = VmAirWrapper< Rv32BranchAdapterAir, BranchLessThanCoreAir, >; -pub type Rv32BranchLessThanStepWithAdapter = +pub type Rv32BranchLessThanStep = BranchLessThanStep; pub type Rv32BranchLessThanChip = - NewVmChipWrapper; + NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/divrem/core.rs b/extensions/rv32im/circuit/src/divrem/core.rs index 01585ab2af..8850681e31 100644 --- a/extensions/rv32im/circuit/src/divrem/core.rs +++ b/extensions/rv32im/circuit/src/divrem/core.rs @@ -586,8 +586,8 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - state.ctx.trace_heights[chip_index] += 1; self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; Ok(()) } diff --git a/extensions/rv32im/circuit/src/hintstore/mod.rs b/extensions/rv32im/circuit/src/hintstore/mod.rs index e770abc4fe..460c259787 100644 --- a/extensions/rv32im/circuit/src/hintstore/mod.rs +++ b/extensions/rv32im/circuit/src/hintstore/mod.rs @@ -39,7 +39,10 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use crate::adapters::{decompose, memory_read, memory_write, tracing_read, tracing_write}; +use crate::adapters::{ + decompose, memory_read, memory_read_from_state, memory_write, memory_write_from_state, + tracing_read, tracing_write, +}; #[cfg(test)] mod tests; @@ -475,22 +478,16 @@ where let local_opcode = Rv32HintStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); - let mem_ptr_limbs = memory_read( - state.memory, - RV32_REGISTER_AS, - mem_ptr_ptr.as_canonical_u32(), - ); + let mem_ptr_limbs = + memory_read_from_state(state, RV32_REGISTER_AS, mem_ptr_ptr.as_canonical_u32()); let mem_ptr = u32::from_le_bytes(mem_ptr_limbs); debug_assert!(mem_ptr <= (1 << self.pointer_max_bits)); let num_words = if local_opcode == HINT_STOREW { 1 } else { - let num_words_limbs = memory_read( - state.memory, - RV32_REGISTER_AS, - num_words_ptr.as_canonical_u32(), - ); + let num_words_limbs = + memory_read_from_state(state, RV32_REGISTER_AS, num_words_ptr.as_canonical_u32()); u32::from_le_bytes(num_words_limbs) }; debug_assert_ne!(num_words, 0); @@ -505,8 +502,8 @@ where let data: [u8; RV32_REGISTER_NUM_LIMBS] = std::array::from_fn(|_| { streams.hint_stream.pop_front().unwrap().as_canonical_u32() as u8 }); - memory_write( - state.memory, + memory_write_from_state( + state, RV32_MEMORY_AS, mem_ptr + (RV32_REGISTER_NUM_LIMBS as u32 * word_index), &data, @@ -524,13 +521,18 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - // TODO(ayush): remove duplication let &Instruction { opcode, a: num_words_ptr, + b: mem_ptr_ptr, + d, + e, .. } = instruction; + debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); + debug_assert_eq!(e.as_canonical_u32(), RV32_MEMORY_AS); + let local_opcode = Rv32HintStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); let num_words = if local_opcode == HINT_STOREW { @@ -544,8 +546,8 @@ where u32::from_le_bytes(num_words_limbs) }; - state.ctx.trace_heights[chip_index] += num_words as usize; self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += num_words; Ok(()) } diff --git a/extensions/rv32im/circuit/src/jal_lui/core.rs b/extensions/rv32im/circuit/src/jal_lui/core.rs index e210ad4eb3..836223316e 100644 --- a/extensions/rv32im/circuit/src/jal_lui/core.rs +++ b/extensions/rv32im/circuit/src/jal_lui/core.rs @@ -145,12 +145,12 @@ where } } -pub struct Rv32JalLuiStep { +pub struct Rv32JalLuiCoreStep { adapter: A, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, } -impl Rv32JalLuiStep { +impl Rv32JalLuiCoreStep { pub fn new( adapter: A, bitwise_lookup_chip: SharedBitwiseOperationLookupChip, @@ -162,7 +162,7 @@ impl Rv32JalLuiStep { } } -impl TraceStep for Rv32JalLuiStep +impl TraceStep for Rv32JalLuiCoreStep where F: PrimeField32, A: 'static @@ -249,7 +249,7 @@ where } } -impl StepExecutorE1 for Rv32JalLuiStep +impl StepExecutorE1 for Rv32JalLuiCoreStep where F: PrimeField32, A: 'static @@ -296,8 +296,8 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - state.ctx.trace_heights[chip_index] += 1; self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; Ok(()) } diff --git a/extensions/rv32im/circuit/src/jal_lui/mod.rs b/extensions/rv32im/circuit/src/jal_lui/mod.rs index cd10a47fca..85b7b3ce35 100644 --- a/extensions/rv32im/circuit/src/jal_lui/mod.rs +++ b/extensions/rv32im/circuit/src/jal_lui/mod.rs @@ -9,5 +9,5 @@ pub use core::*; mod tests; pub type Rv32JalLuiAir = VmAirWrapper; -pub type Rv32JalLuiStepWithAdapter = Rv32JalLuiStep; -pub type Rv32JalLuiChip = NewVmChipWrapper; +pub type Rv32JalLuiStep = Rv32JalLuiCoreStep; +pub type Rv32JalLuiChip = NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/jalr/core.rs b/extensions/rv32im/circuit/src/jalr/core.rs index 2f016d826c..1c543718ed 100644 --- a/extensions/rv32im/circuit/src/jalr/core.rs +++ b/extensions/rv32im/circuit/src/jalr/core.rs @@ -176,13 +176,13 @@ where } } -pub struct Rv32JalrStep { +pub struct Rv32JalrCoreStep { adapter: A, pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip, pub range_checker_chip: SharedVariableRangeCheckerChip, } -impl Rv32JalrStep { +impl Rv32JalrCoreStep { pub fn new( adapter: A, bitwise_lookup_chip: SharedBitwiseOperationLookupChip, @@ -197,7 +197,7 @@ impl Rv32JalrStep { } } -impl TraceStep for Rv32JalrStep +impl TraceStep for Rv32JalrCoreStep where F: PrimeField32, A: 'static @@ -322,7 +322,7 @@ where } } -impl StepExecutorE1 for Rv32JalrStep +impl StepExecutorE1 for Rv32JalrCoreStep where F: PrimeField32, A: 'static @@ -369,8 +369,8 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - state.ctx.trace_heights[chip_index] += 1; self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; Ok(()) } diff --git a/extensions/rv32im/circuit/src/jalr/mod.rs b/extensions/rv32im/circuit/src/jalr/mod.rs index 5bf0bf7543..f3e6f9d9a3 100644 --- a/extensions/rv32im/circuit/src/jalr/mod.rs +++ b/extensions/rv32im/circuit/src/jalr/mod.rs @@ -9,5 +9,5 @@ pub use core::*; mod tests; pub type Rv32JalrAir = VmAirWrapper; -pub type Rv32JalrStepWithAdapter = Rv32JalrStep; -pub type Rv32JalrChip = NewVmChipWrapper; +pub type Rv32JalrStep = Rv32JalrCoreStep; +pub type Rv32JalrChip = NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/less_than/core.rs b/extensions/rv32im/circuit/src/less_than/core.rs index 1ddd266ab6..fddad57900 100644 --- a/extensions/rv32im/circuit/src/less_than/core.rs +++ b/extensions/rv32im/circuit/src/less_than/core.rs @@ -369,8 +369,8 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - state.ctx.trace_heights[chip_index] += 1; self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; Ok(()) } diff --git a/extensions/rv32im/circuit/src/load_sign_extend/core.rs b/extensions/rv32im/circuit/src/load_sign_extend/core.rs index 992229b893..5142502200 100644 --- a/extensions/rv32im/circuit/src/load_sign_extend/core.rs +++ b/extensions/rv32im/circuit/src/load_sign_extend/core.rs @@ -354,8 +354,8 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - state.ctx.trace_heights[chip_index] += 1; self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; Ok(()) } diff --git a/extensions/rv32im/circuit/src/loadstore/core.rs b/extensions/rv32im/circuit/src/loadstore/core.rs index dc9be8aac5..9b9eeb59ab 100644 --- a/extensions/rv32im/circuit/src/loadstore/core.rs +++ b/extensions/rv32im/circuit/src/loadstore/core.rs @@ -413,8 +413,8 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - state.ctx.trace_heights[chip_index] += 1; self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; Ok(()) } diff --git a/extensions/rv32im/circuit/src/loadstore/tests.rs b/extensions/rv32im/circuit/src/loadstore/tests.rs index b5793b4b75..213e19d555 100644 --- a/extensions/rv32im/circuit/src/loadstore/tests.rs +++ b/extensions/rv32im/circuit/src/loadstore/tests.rs @@ -39,7 +39,9 @@ type F = BabyBear; fn create_test_chip(tester: &mut VmChipTestBuilder) -> Rv32LoadStoreChip { let range_checker_chip = tester.range_checker(); - let chip = Rv32LoadStoreChip::::new( + + + Rv32LoadStoreChip::::new( VmAirWrapper::new( Rv32LoadStoreAdapterAir::new( tester.memory_bridge(), @@ -56,9 +58,7 @@ fn create_test_chip(tester: &mut VmChipTestBuilder) -> Rv32LoadStoreChip { ), MAX_INS_CAPACITY, tester.memory_helper(), - ); - - chip + ) } #[allow(clippy::too_many_arguments)] diff --git a/extensions/rv32im/circuit/src/mul/core.rs b/extensions/rv32im/circuit/src/mul/core.rs index 32ce87af1b..0aa431baa8 100644 --- a/extensions/rv32im/circuit/src/mul/core.rs +++ b/extensions/rv32im/circuit/src/mul/core.rs @@ -269,8 +269,8 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - state.ctx.trace_heights[chip_index] += 1; self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; Ok(()) } diff --git a/extensions/rv32im/circuit/src/mulh/core.rs b/extensions/rv32im/circuit/src/mulh/core.rs index 361a912b02..354db4ae5a 100644 --- a/extensions/rv32im/circuit/src/mulh/core.rs +++ b/extensions/rv32im/circuit/src/mulh/core.rs @@ -357,8 +357,8 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - state.ctx.trace_heights[chip_index] += 1; self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; Ok(()) } diff --git a/extensions/rv32im/circuit/src/shift/core.rs b/extensions/rv32im/circuit/src/shift/core.rs index e8f903bd92..b53131482c 100644 --- a/extensions/rv32im/circuit/src/shift/core.rs +++ b/extensions/rv32im/circuit/src/shift/core.rs @@ -427,8 +427,8 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - state.ctx.trace_heights[chip_index] += 1; self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; Ok(()) } diff --git a/extensions/sha256/circuit/src/sha256_chip/mod.rs b/extensions/sha256/circuit/src/sha256_chip/mod.rs index cad58e807c..b87069b2e2 100644 --- a/extensions/sha256/circuit/src/sha256_chip/mod.rs +++ b/extensions/sha256/circuit/src/sha256_chip/mod.rs @@ -3,7 +3,8 @@ use openvm_circuit::{ arch::{ - execution_mode::metered::MeteredCtx, NewVmChipWrapper, Result, StepExecutorE1, VmStateMut, + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, + NewVmChipWrapper, Result, StepExecutorE1, VmStateMut, }, system::memory::online::GuestMemory, }; @@ -12,11 +13,14 @@ use openvm_circuit_primitives::{ }; use openvm_instructions::{ instruction::Instruction, + program::DEFAULT_PC_STEP, riscv::{RV32_CELL_BITS, RV32_MEMORY_AS, RV32_REGISTER_AS}, LocalOpcode, }; -use openvm_rv32im_circuit::adapters::{memory_write, new_read_rv32_register}; -use openvm_sha256_air::{Sha256StepHelper, SHA256_BLOCK_BITS}; +use openvm_rv32im_circuit::adapters::{ + memory_read_from_state, memory_write_from_state, new_read_rv32_register_from_state, +}; +use openvm_sha256_air::{Sha256StepHelper, SHA256_BLOCK_BITS, SHA256_ROWS_PER_BLOCK}; use openvm_sha256_transpiler::Rv32Sha256Opcode; use openvm_stark_backend::p3_field::PrimeField32; use sha2::{Digest, Sha256}; @@ -74,7 +78,10 @@ impl StepExecutorE1 for Sha256VmStep { &mut self, state: &mut VmStateMut, instruction: &Instruction, - ) -> Result<()> { + ) -> Result<()> + where + Ctx: E1E2ExecutionCtx, + { let &Instruction { opcode, a, @@ -90,30 +97,80 @@ impl StepExecutorE1 for Sha256VmStep { debug_assert_eq!(local_opcode, Rv32Sha256Opcode::SHA256.local_usize()); debug_assert_eq!(d, RV32_REGISTER_AS); debug_assert_eq!(e, RV32_MEMORY_AS); - let dst = new_read_rv32_register(state.memory, d, a.as_canonical_u32()); - let src = new_read_rv32_register(state.memory, d, b.as_canonical_u32()); - let len = new_read_rv32_register(state.memory, d, c.as_canonical_u32()); + let dst = new_read_rv32_register_from_state(state, d, a.as_canonical_u32()); + let src = new_read_rv32_register_from_state(state, d, b.as_canonical_u32()); + let len = new_read_rv32_register_from_state(state, d, c.as_canonical_u32()); debug_assert!(src + len <= (1 << self.pointer_max_bits)); let mut hasher = Sha256::new(); - let message: Vec = state - .memory - .memory - .read_range_generic((e, src), len as usize); + // TODO(ayush): read in a single call + let mut message = Vec::with_capacity(len as usize); + for offset in (0..len as usize).step_by(SHA256_READ_SIZE) { + let read = memory_read_from_state::<_, SHA256_READ_SIZE>(state, e, src + offset as u32); + let copy_len = std::cmp::min(SHA256_READ_SIZE, (len as usize) - offset); + message.extend_from_slice(&read[..copy_len]); + } hasher.update(&message); - memory_write(state.memory, e, dst, hasher.finalize().as_ref()); + let output = hasher.finalize(); + memory_write_from_state(state, e, dst, output.as_ref()); + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + Ok(()) } fn execute_metered( &mut self, - _state: &mut VmStateMut, - _instruction: &Instruction, - _chip_index: usize, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, ) -> Result<()> { - todo!() + let &Instruction { + opcode, + a, + b, + c, + d, + e, + .. + } = instruction; + + let d = d.as_canonical_u32(); + let e = e.as_canonical_u32(); + let local_opcode = opcode.local_opcode_idx(self.offset); + + debug_assert_eq!(local_opcode, Rv32Sha256Opcode::SHA256.local_usize()); + debug_assert_eq!(d, RV32_REGISTER_AS); + debug_assert_eq!(e, RV32_MEMORY_AS); + + let dst = new_read_rv32_register_from_state(state, d, a.as_canonical_u32()); + let src = new_read_rv32_register_from_state(state, d, b.as_canonical_u32()); + let len = new_read_rv32_register_from_state(state, d, c.as_canonical_u32()); + + debug_assert!(src + len <= (1 << self.pointer_max_bits)); + + let num_blocks = ((len << 3) as usize + 1 + 64).div_ceil(SHA256_BLOCK_BITS); + + let mut message = Vec::with_capacity(len as usize); + for offset in (0..len as usize).step_by(SHA256_READ_SIZE) { + let read = memory_read_from_state::<_, SHA256_READ_SIZE>(state, e, src + offset as u32); + let copy_len = std::cmp::min(SHA256_READ_SIZE, (len as usize) - offset); + message.extend_from_slice(&read[..copy_len]); + } + + let mut hasher = Sha256::new(); + hasher.update(&message); + + let output = hasher.finalize(); + memory_write_from_state(state, e, dst, output.as_ref()); + + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + + state.ctx.trace_heights[chip_index] += (num_blocks * SHA256_ROWS_PER_BLOCK) as u32; + + Ok(()) } } From b1387a3f73f89f7e4e9df318b162ce713507c22d Mon Sep 17 00:00:00 2001 From: Arayi Khalatyan <127004086+arayikhalatyan@users.noreply.github.com> Date: Thu, 22 May 2025 01:05:38 -0400 Subject: [PATCH 32/49] fix: integration test (#1679) All the integration tests should pass now. Merged with Native's PR so some of the tests could compile, I think for simplicity this can be merged after the native's PR. --------- Co-authored-by: Ayush Shukla Co-authored-by: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> --- benchmarks/execute/benches/execute.rs | 24 ++++---- crates/circuits/mod-builder/src/core_chip.rs | 3 + crates/sdk/src/prover/vm/local.rs | 2 +- .../circuit/src/weierstrass_chip/double.rs | 2 +- .../ecc/circuit/src/weierstrass_extension.rs | 39 ++++++------- extensions/keccak256/circuit/src/lib.rs | 1 - .../src/adapters/alu_native_adapter.rs | 3 +- .../src/adapters/branch_native_adapter.rs | 13 +++-- .../circuit/src/field_extension/tests.rs | 3 +- extensions/native/circuit/src/fri/tests.rs | 9 ++- extensions/native/circuit/src/jal/tests.rs | 3 +- .../native/circuit/src/loadstore/tests.rs | 3 +- .../pairing/circuit/src/pairing_extension.rs | 55 +++++++++---------- .../rv32im/circuit/src/loadstore/tests.rs | 1 - 14 files changed, 74 insertions(+), 87 deletions(-) diff --git a/benchmarks/execute/benches/execute.rs b/benchmarks/execute/benches/execute.rs index 06b99fe430..7ff60c5d95 100644 --- a/benchmarks/execute/benches/execute.rs +++ b/benchmarks/execute/benches/execute.rs @@ -1,31 +1,29 @@ use eyre::Result; use openvm_benchmarks_utils::{get_elf_path, get_programs_dir, read_elf_file}; -use openvm_bigint_circuit::Int256; +use openvm_bigint_circuit::{Int256, Int256Executor, Int256Periphery}; use openvm_bigint_transpiler::Int256TranspilerExtension; use openvm_circuit::{ arch::{instructions::exe::VmExe, SystemConfig, VmExecutor}, derive::VmConfig, }; -use openvm_keccak256_circuit::Keccak256; +use openvm_keccak256_circuit::{Keccak256, Keccak256Executor, Keccak256Periphery}; use openvm_keccak256_transpiler::Keccak256TranspilerExtension; -use openvm_rv32im_circuit::{Rv32I, Rv32Io, Rv32M}; +use openvm_rv32im_circuit::{ + Rv32I, Rv32IExecutor, Rv32IPeriphery, Rv32Io, Rv32IoExecutor, Rv32IoPeriphery, Rv32M, + Rv32MExecutor, Rv32MPeriphery, +}; use openvm_rv32im_transpiler::{ Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, }; -use openvm_sha256_circuit::Sha256; +use openvm_sha256_circuit::{Sha256, Sha256Executor, Sha256Periphery}; use openvm_sha256_transpiler::Sha256TranspilerExtension; -use openvm_stark_sdk::p3_baby_bear::BabyBear; +use openvm_stark_sdk::{ + openvm_stark_backend::{self, p3_field::PrimeField32}, + p3_baby_bear::BabyBear, +}; use openvm_transpiler::{transpiler::Transpiler, FromElf}; use serde::{Deserialize, Serialize}; -use openvm_bigint_circuit::{Int256Executor, Int256Periphery}; -use openvm_keccak256_circuit::{Keccak256Executor, Keccak256Periphery}; -use openvm_rv32im_circuit::{ - Rv32IExecutor, Rv32IPeriphery, Rv32IoExecutor, Rv32IoPeriphery, Rv32MExecutor, Rv32MPeriphery, -}; -use openvm_sha256_circuit::{Sha256Executor, Sha256Periphery}; -use openvm_stark_sdk::openvm_stark_backend::{self, p3_field::PrimeField32}; - static AVAILABLE_PROGRAMS: &[&str] = &[ "fibonacci_recursive", "fibonacci_iterative", diff --git a/crates/circuits/mod-builder/src/core_chip.rs b/crates/circuits/mod-builder/src/core_chip.rs index 4e9bbf3b44..1f219c62f7 100644 --- a/crates/circuits/mod-builder/src/core_chip.rs +++ b/crates/circuits/mod-builder/src/core_chip.rs @@ -289,6 +289,9 @@ where // We will be setting is_valid = 0. That forces all flags be 0 (otherwise setup will be -1). // We generate a dummy row with all flags set to 0, then we set is_valid = 0. fn fill_dummy_trace_row(&self, _mem_helper: &MemoryAuxColsFactory, row: &mut [F]) { + if !self.should_finalize { + return; + } let inputs: Vec = vec![BigUint::zero(); self.num_inputs()]; let flags: Vec = vec![false; self.num_flags()]; let core_row = &mut row[A::WIDTH..]; diff --git a/crates/sdk/src/prover/vm/local.rs b/crates/sdk/src/prover/vm/local.rs index 285396ff09..85c604bb98 100644 --- a/crates/sdk/src/prover/vm/local.rs +++ b/crates/sdk/src/prover/vm/local.rs @@ -99,7 +99,7 @@ where exe.clone(), input.clone(), |seg_idx, mut seg| { - final_memory = mem::take(&mut seg.control.final_memory); + final_memory = mem::take(&mut seg.ctrl.final_memory); let proof_input = info_span!("trace_gen", segment = seg_idx) .in_scope(|| seg.generate_proof_input(Some(committed_program.clone())))?; info_span!("prove_segment", segment = seg_idx) diff --git a/extensions/ecc/circuit/src/weierstrass_chip/double.rs b/extensions/ecc/circuit/src/weierstrass_chip/double.rs index 975925f698..e0478600c0 100644 --- a/extensions/ecc/circuit/src/weierstrass_chip/double.rs +++ b/extensions/ecc/circuit/src/weierstrass_chip/double.rs @@ -101,7 +101,7 @@ impl vec![], range_checker, "EcDouble", - false, + true, ); Self(WeierstrassChip::new(air, step, height, mem_helper)) } diff --git a/extensions/ecc/circuit/src/weierstrass_extension.rs b/extensions/ecc/circuit/src/weierstrass_extension.rs index 1db3e5a9c6..c2ce65406c 100644 --- a/extensions/ecc/circuit/src/weierstrass_extension.rs +++ b/extensions/ecc/circuit/src/weierstrass_extension.rs @@ -184,7 +184,7 @@ impl VmExtension for WeierstrassExtension { ); inventory.add_executor( - WeierstrassExtensionExecutor::EcAddNeRv32_32(add_ne_chip), + WeierstrassExtensionExecutor::EcAddNeRv32_48(add_ne_chip), ec_add_ne_opcodes .clone() .map(|x| VmOpcode::from_usize(x + start_offset)), @@ -202,7 +202,7 @@ impl VmExtension for WeierstrassExtension { MAX_INS_CAPACITY, ); inventory.add_executor( - WeierstrassExtensionExecutor::EcDoubleRv32_32(double_chip), + WeierstrassExtensionExecutor::EcDoubleRv32_48(double_chip), ec_double_opcodes .clone() .map(|x| VmOpcode::from_usize(x + start_offset)), @@ -237,11 +237,14 @@ pub(crate) mod phantom { use num_traits::{FromPrimitive, One}; use openvm_circuit::{ arch::{PhantomSubExecutor, Streams}, - system::memory::{online::GuestMemory, MemoryController}, + system::memory::online::GuestMemory, }; use openvm_ecc_guest::weierstrass::DecompressionHint; - use openvm_instructions::{riscv::RV32_MEMORY_AS, PhantomDiscriminant}; - use openvm_rv32im_circuit::adapters::unsafe_read_rv32_register; + use openvm_instructions::{ + riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}, + PhantomDiscriminant, + }; + use openvm_rv32im_circuit::adapters::new_read_rv32_register; use openvm_stark_backend::p3_field::PrimeField32; use rand::{rngs::StdRng, SeedableRng}; @@ -268,7 +271,7 @@ pub(crate) mod phantom { b: u32, c_upper: u16, ) -> eyre::Result<()> { - /*let c_idx = c_upper as usize; + let c_idx = c_upper as usize; if c_idx >= self.supported_curves.len() { bail!( "Curve index {c_idx} out of range: {} supported curves", @@ -276,7 +279,7 @@ pub(crate) mod phantom { ); } let curve = &self.supported_curves[c_idx]; - let rs1 = unsafe_read_rv32_register(memory, a); + let rs1 = new_read_rv32_register(memory, RV32_REGISTER_AS, a); let num_limbs: usize = if curve.modulus.bits().div_ceil(8) <= 32 { 32 } else if curve.modulus.bits().div_ceil(8) <= 48 { @@ -284,20 +287,14 @@ pub(crate) mod phantom { } else { bail!("Modulus too large") }; - let mut x_limbs: Vec = Vec::with_capacity(num_limbs); - for i in 0..num_limbs { - let limb = memory.unsafe_read_cell::( - F::from_canonical_u32(RV32_MEMORY_AS), - F::from_canonical_u32(rs1 + i as u32), - ); - x_limbs.push(limb); - } + let x_limbs: Vec = memory + .memory + .read_range_generic((RV32_MEMORY_AS, rs1), num_limbs); let x = BigUint::from_bytes_le(&x_limbs); - let rs2 = unsafe_read_rv32_register(memory, b); - let rec_id = memory.unsafe_read_cell::( - F::from_canonical_u32(RV32_MEMORY_AS), - F::from_canonical_u32(rs2), - ); + let rs2 = new_read_rv32_register(memory, RV32_REGISTER_AS, b); + let rec_id = memory + .memory + .read_range_generic::((RV32_MEMORY_AS, rs2), 1)[0]; let hint = self.decompress_point(x, rec_id & 1 == 1, c_idx); let hint_bytes = once(F::from_bool(hint.possible)) .chain(repeat(F::ZERO)) @@ -312,7 +309,7 @@ pub(crate) mod phantom { ) .collect(); streams.hint_stream = hint_bytes; - */ + Ok(()) } } diff --git a/extensions/keccak256/circuit/src/lib.rs b/extensions/keccak256/circuit/src/lib.rs index b1fefcf20f..2f602d685c 100644 --- a/extensions/keccak256/circuit/src/lib.rs +++ b/extensions/keccak256/circuit/src/lib.rs @@ -3,7 +3,6 @@ use openvm_circuit_primitives::bitwise_op_lookup::SharedBitwiseOperationLookupChip; use openvm_stark_backend::p3_field::PrimeField32; - use p3_keccak_air::NUM_ROUNDS; use tiny_keccak::{Hasher, Keccak}; diff --git a/extensions/native/circuit/src/adapters/alu_native_adapter.rs b/extensions/native/circuit/src/adapters/alu_native_adapter.rs index 74ac396091..55232950db 100644 --- a/extensions/native/circuit/src/adapters/alu_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/alu_native_adapter.rs @@ -24,13 +24,12 @@ use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra, PrimeField32}, }; +use super::tracing_write_native; use crate::adapters::{ memory_read_or_imm_native_from_state, memory_write_native_from_state, tracing_read_or_imm_native, }; -use super::tracing_write_native; - #[repr(C)] #[derive(AlignedBorrow)] pub struct AluNativeAdapterCols { diff --git a/extensions/native/circuit/src/adapters/branch_native_adapter.rs b/extensions/native/circuit/src/adapters/branch_native_adapter.rs index 4c553d399e..d764f9ad42 100644 --- a/extensions/native/circuit/src/adapters/branch_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/branch_native_adapter.rs @@ -1,12 +1,13 @@ -use std::borrow::{Borrow, BorrowMut}; -use std::mem::size_of; +use std::{ + borrow::{Borrow, BorrowMut}, + mem::size_of, +}; -use openvm_circuit::arch::execution_mode::E1E2ExecutionCtx; -use openvm_circuit::arch::VmStateMut; use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, - ExecutionBridge, ExecutionState, ImmInstruction, VmAdapterAir, + execution_mode::E1E2ExecutionCtx, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, + BasicAdapterInterface, ExecutionBridge, ExecutionState, ImmInstruction, VmAdapterAir, + VmStateMut, }, system::memory::{ offline_checker::{MemoryBridge, MemoryReadOrImmediateAuxCols}, diff --git a/extensions/native/circuit/src/field_extension/tests.rs b/extensions/native/circuit/src/field_extension/tests.rs index ef197d8aa3..e158957415 100644 --- a/extensions/native/circuit/src/field_extension/tests.rs +++ b/extensions/native/circuit/src/field_extension/tests.rs @@ -19,12 +19,11 @@ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::Rng; use strum::EnumCount; +use super::{FieldExtension, FieldExtensionChip, FieldExtensionCoreAir, FieldExtensionCoreStep}; use crate::adapters::native_vectorized_adapter::{ NativeVectorizedAdapterAir, NativeVectorizedAdapterStep, }; -use super::{FieldExtension, FieldExtensionChip, FieldExtensionCoreAir, FieldExtensionCoreStep}; - const MAX_INS_CAPACITY: usize = 128; type F = BabyBear; diff --git a/extensions/native/circuit/src/fri/tests.rs b/extensions/native/circuit/src/fri/tests.rs index 87fd95350f..5779be638c 100644 --- a/extensions/native/circuit/src/fri/tests.rs +++ b/extensions/native/circuit/src/fri/tests.rs @@ -15,12 +15,11 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::Rng; -use crate::fri::OVERALL_WIDTH; - use super::{ super::field_extension::FieldExtension, elem_to_ext, FriReducedOpeningAir, FriReducedOpeningChip, FriReducedOpeningStep, EXT_DEG, }; +use crate::fri::OVERALL_WIDTH; const MAX_INS_CAPACITY: usize = 1024; type F = BabyBear; @@ -99,9 +98,9 @@ fn fri_mat_opening_air_test() { let address_space = 4usize; // tracing::debug!( - // "{opcode:?} d = {}, e = {}, f = {}, result_addr = {}, addr1 = {}, addr2 = {}, z = {}, x = {}, y = {}", - // result_as, as1, as2, result_pointer, address1, address2, result, operand1, operand2, - // ); + // "{opcode:?} d = {}, e = {}, f = {}, result_addr = {}, addr1 = {}, addr2 = {}, z = {}, + // x = {}, y = {}", result_as, as1, as2, result_pointer, address1, address2, + // result, operand1, operand2, ); tester.write(address_space, alpha_pointer, alpha); tester.write( diff --git a/extensions/native/circuit/src/jal/tests.rs b/extensions/native/circuit/src/jal/tests.rs index f2ca52bb07..2494264c22 100644 --- a/extensions/native/circuit/src/jal/tests.rs +++ b/extensions/native/circuit/src/jal/tests.rs @@ -16,9 +16,8 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; -use crate::jal::{JalRangeCheckChip, JalRangeCheckCols}; - use super::{JalRangeCheckAir, JalRangeCheckStep}; +use crate::jal::{JalRangeCheckChip, JalRangeCheckCols}; const MAX_INS_CAPACITY: usize = 128; type F = BabyBear; diff --git a/extensions/native/circuit/src/loadstore/tests.rs b/extensions/native/circuit/src/loadstore/tests.rs index 18814c9af0..af906cdfb9 100644 --- a/extensions/native/circuit/src/loadstore/tests.rs +++ b/extensions/native/circuit/src/loadstore/tests.rs @@ -7,12 +7,11 @@ use openvm_stark_backend::p3_field::{FieldAlgebra, PrimeField32}; use openvm_stark_sdk::{config::setup_tracing, p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; +use super::{NativeLoadStoreChip, NativeLoadStoreCoreAir, NativeLoadStoreCoreStep}; use crate::adapters::loadstore_native_adapter::{ NativeLoadStoreAdapterAir, NativeLoadStoreAdapterStep, }; -use super::{NativeLoadStoreChip, NativeLoadStoreCoreAir, NativeLoadStoreCoreStep}; - const MAX_INS_CAPACITY: usize = 128; type F = BabyBear; diff --git a/extensions/pairing/circuit/src/pairing_extension.rs b/extensions/pairing/circuit/src/pairing_extension.rs index b39fd9e096..763da36e82 100644 --- a/extensions/pairing/circuit/src/pairing_extension.rs +++ b/extensions/pairing/circuit/src/pairing_extension.rs @@ -94,7 +94,7 @@ pub(crate) mod phantom { use eyre::bail; use openvm_circuit::{ arch::{PhantomSubExecutor, Streams}, - system::memory::MemoryController, + system::memory::online::GuestMemory, }; use openvm_ecc_guest::{algebra::field::FieldExtension, halo2curves::ff, AffinePoint}; use openvm_instructions::{ @@ -106,7 +106,7 @@ pub(crate) mod phantom { bn254::BN254_NUM_LIMBS, pairing::{FinalExp, MultiMillerLoop}, }; - use openvm_rv32im_circuit::adapters::unsafe_read_rv32_register; + use openvm_rv32im_circuit::adapters::{memory_read, new_read_rv32_register}; use openvm_stark_backend::p3_field::PrimeField32; use super::PairingCurve; @@ -116,43 +116,40 @@ pub(crate) mod phantom { impl PhantomSubExecutor for PairingHintSubEx { fn phantom_execute( &mut self, - memory: &MemoryController, + memory: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, - a: F, - b: F, + a: u32, + b: u32, c_upper: u16, ) -> eyre::Result<()> { - let rs1 = unsafe_read_rv32_register(memory, a); - let rs2 = unsafe_read_rv32_register(memory, b); + let rs1 = new_read_rv32_register(memory, 1, a); + let rs2 = new_read_rv32_register(memory, 1, b); hint_pairing(memory, &mut streams.hint_stream, rs1, rs2, c_upper) } } fn hint_pairing( - memory: &MemoryController, + memory: &GuestMemory, hint_stream: &mut VecDeque, rs1: u32, rs2: u32, c_upper: u16, ) -> eyre::Result<()> { - let p_ptr = u32::from_le_bytes(memory.unsafe_read::( - F::from_canonical_u32(RV32_MEMORY_AS), - F::from_canonical_u32(rs1), - )); + let p_ptr = u32::from_le_bytes(memory_read(memory, RV32_MEMORY_AS, rs1)); // len in bytes - let p_len = u32::from_le_bytes(memory.unsafe_read::( - F::from_canonical_u32(RV32_MEMORY_AS), - F::from_canonical_u32(rs1 + RV32_REGISTER_NUM_LIMBS as u32), - )); - let q_ptr = u32::from_le_bytes(memory.unsafe_read::( - F::from_canonical_u32(RV32_MEMORY_AS), - F::from_canonical_u32(rs2), + let p_len = u32::from_le_bytes(memory_read( + memory, + RV32_MEMORY_AS, + rs1 + RV32_REGISTER_NUM_LIMBS as u32, )); + + let q_ptr = u32::from_le_bytes(memory_read(memory, RV32_MEMORY_AS, rs2)); // len in bytes - let q_len = u32::from_le_bytes(memory.unsafe_read::( - F::from_canonical_u32(RV32_MEMORY_AS), - F::from_canonical_u32(rs2 + RV32_REGISTER_NUM_LIMBS as u32), + let q_len = u32::from_le_bytes(memory_read( + memory, + RV32_MEMORY_AS, + rs2 + RV32_REGISTER_NUM_LIMBS as u32, )); match PairingCurve::from_repr(c_upper as usize) { @@ -248,19 +245,17 @@ pub(crate) mod phantom { } fn read_fp( - memory: &MemoryController, + memory: &GuestMemory, ptr: u32, ) -> eyre::Result where Fp::Repr: From<[u8; N]>, { - let mut repr = [0u8; N]; - for (i, byte) in repr.iter_mut().enumerate() { - *byte = memory.unsafe_read_cell::( - F::from_canonical_u32(RV32_MEMORY_AS), - F::from_canonical_u32(ptr + i as u32), - ); - } + let repr: [u8; N] = memory + .memory + .read_range_generic((RV32_MEMORY_AS, ptr), N) + .try_into() + .unwrap(); Fp::from_repr(repr.into()) .into_option() .ok_or(eyre::eyre!("bad ff::PrimeField repr")) diff --git a/extensions/rv32im/circuit/src/loadstore/tests.rs b/extensions/rv32im/circuit/src/loadstore/tests.rs index 213e19d555..e090570cb4 100644 --- a/extensions/rv32im/circuit/src/loadstore/tests.rs +++ b/extensions/rv32im/circuit/src/loadstore/tests.rs @@ -39,7 +39,6 @@ type F = BabyBear; fn create_test_chip(tester: &mut VmChipTestBuilder) -> Rv32LoadStoreChip { let range_checker_chip = tester.range_checker(); - Rv32LoadStoreChip::::new( VmAirWrapper::new( From ae501acc92dda4b1985575f6c207f009fc72aaff Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Thu, 22 May 2025 06:13:53 -0400 Subject: [PATCH 33/49] feat(new-execution): add interfaces for running different execute functions (#1677) - this is targeting `feat/new-exec-native-ext` since it was built on top of it --- benchmarks/execute/Cargo.toml | 1 + benchmarks/execute/benches/execute.rs | 26 +- benchmarks/execute/src/main.rs | 78 ++-- crates/vm/src/arch/execution_mode/e1.rs | 14 +- .../src/arch/execution_mode/metered/exact.rs | 2 +- .../vm/src/arch/execution_mode/metered/mod.rs | 164 +++++++-- .../src/arch/execution_mode/tracegen/mod.rs | 7 + .../arch/execution_mode/tracegen/normal.rs | 103 ++++++ .../{tracegen.rs => tracegen/segmentation.rs} | 19 +- crates/vm/src/arch/segment.rs | 6 +- crates/vm/src/arch/vm.rs | 344 ++++++++++-------- crates/vm/src/system/connector/mod.rs | 1 + extensions/keccak256/circuit/src/trace.rs | 4 + .../sha256/circuit/src/sha256_chip/trace.rs | 4 + 14 files changed, 526 insertions(+), 247 deletions(-) create mode 100644 crates/vm/src/arch/execution_mode/tracegen/mod.rs create mode 100644 crates/vm/src/arch/execution_mode/tracegen/normal.rs rename crates/vm/src/arch/execution_mode/{tracegen.rs => tracegen/segmentation.rs} (91%) diff --git a/benchmarks/execute/Cargo.toml b/benchmarks/execute/Cargo.toml index bdcc539092..d1037ea75b 100644 --- a/benchmarks/execute/Cargo.toml +++ b/benchmarks/execute/Cargo.toml @@ -23,6 +23,7 @@ openvm-keccak256-transpiler.workspace = true openvm-sha256-circuit.workspace = true openvm-sha256-transpiler.workspace = true +# bitcode.workspace = true clap = { version = "4.5.9", features = ["derive", "env"] } eyre.workspace = true tracing.workspace = true diff --git a/benchmarks/execute/benches/execute.rs b/benchmarks/execute/benches/execute.rs index 7ff60c5d95..aab46d1ca2 100644 --- a/benchmarks/execute/benches/execute.rs +++ b/benchmarks/execute/benches/execute.rs @@ -1,29 +1,31 @@ use eyre::Result; use openvm_benchmarks_utils::{get_elf_path, get_programs_dir, read_elf_file}; -use openvm_bigint_circuit::{Int256, Int256Executor, Int256Periphery}; +use openvm_bigint_circuit::Int256; use openvm_bigint_transpiler::Int256TranspilerExtension; use openvm_circuit::{ arch::{instructions::exe::VmExe, SystemConfig, VmExecutor}, derive::VmConfig, }; -use openvm_keccak256_circuit::{Keccak256, Keccak256Executor, Keccak256Periphery}; +use openvm_keccak256_circuit::Keccak256; use openvm_keccak256_transpiler::Keccak256TranspilerExtension; -use openvm_rv32im_circuit::{ - Rv32I, Rv32IExecutor, Rv32IPeriphery, Rv32Io, Rv32IoExecutor, Rv32IoPeriphery, Rv32M, - Rv32MExecutor, Rv32MPeriphery, -}; +use openvm_rv32im_circuit::{Rv32I, Rv32Io, Rv32M}; use openvm_rv32im_transpiler::{ Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, }; -use openvm_sha256_circuit::{Sha256, Sha256Executor, Sha256Periphery}; +use openvm_sha256_circuit::Sha256; use openvm_sha256_transpiler::Sha256TranspilerExtension; -use openvm_stark_sdk::{ - openvm_stark_backend::{self, p3_field::PrimeField32}, - p3_baby_bear::BabyBear, -}; +use openvm_stark_sdk::p3_baby_bear::BabyBear; use openvm_transpiler::{transpiler::Transpiler, FromElf}; use serde::{Deserialize, Serialize}; +use openvm_bigint_circuit::{Int256Executor, Int256Periphery}; +use openvm_keccak256_circuit::{Keccak256Executor, Keccak256Periphery}; +use openvm_rv32im_circuit::{ + Rv32IExecutor, Rv32IPeriphery, Rv32IoExecutor, Rv32IoPeriphery, Rv32MExecutor, Rv32MPeriphery, +}; +use openvm_sha256_circuit::{Sha256Executor, Sha256Periphery}; +use openvm_stark_sdk::openvm_stark_backend::{self, p3_field::PrimeField32}; + static AVAILABLE_PROGRAMS: &[&str] = &[ "fibonacci_recursive", "fibonacci_iterative", @@ -96,7 +98,7 @@ fn run_program(program: &str) -> Result<()> { let executor = VmExecutor::new(vm_config); executor - .execute_e1(exe, vec![]) + .execute_e1(exe, vec![], None) .expect("Failed to execute program"); Ok(()) diff --git a/benchmarks/execute/src/main.rs b/benchmarks/execute/src/main.rs index 37426b0ab5..5147ab7629 100644 --- a/benchmarks/execute/src/main.rs +++ b/benchmarks/execute/src/main.rs @@ -4,7 +4,10 @@ use openvm_benchmarks_utils::{get_elf_path, get_programs_dir, read_elf_file}; use openvm_bigint_circuit::{Int256, Int256Executor, Int256Periphery, Int256Rv32Config}; use openvm_bigint_transpiler::Int256TranspilerExtension; use openvm_circuit::{ - arch::{instructions::exe::VmExe, SystemConfig, VirtualMachine, VmExecutor}, + arch::{ + execution_mode::metered::Segment, instructions::exe::VmExe, SystemConfig, VirtualMachine, + VmExecutor, VmExecutorResult, + }, derive::VmConfig, }; use openvm_keccak256_circuit::{ @@ -22,8 +25,14 @@ use openvm_sha256_circuit::{Sha256, Sha256Executor, Sha256Periphery}; use openvm_sha256_transpiler::Sha256TranspilerExtension; use openvm_stark_sdk::{ bench::run_with_metric_collection, - config::baby_bear_poseidon2::default_engine, - openvm_stark_backend::{self, p3_field::PrimeField32}, + config::{ + baby_bear_blake3::BabyBearBlake3Config, + baby_bear_poseidon2::{default_engine, BabyBearPoseidon2Config}, + }, + openvm_stark_backend::{ + self, + p3_field::{FieldExtensionAlgebra, PrimeField32}, + }, p3_baby_bear::BabyBear, }; use openvm_transpiler::{transpiler::Transpiler, FromElf}; @@ -32,17 +41,17 @@ use serde::{Deserialize, Serialize}; // const DEFAULT_APP_CONFIG_PATH: &str = "./openvm.toml"; static AVAILABLE_PROGRAMS: &[&str] = &[ - // "fibonacci_recursive", - // "fibonacci_iterative", - // "quicksort", - // "bubblesort", - // "factorial_iterative_u256", - // "revm_snailtracer", + "fibonacci_recursive", + "fibonacci_iterative", + "quicksort", + "bubblesort", + "factorial_iterative_u256", + "revm_snailtracer", "keccak256", - // "keccak256_iter", - // "sha256", - // "sha256_iter", - // "revm_transfer", + "keccak256_iter", + "sha256", + "sha256_iter", + "revm_transfer", // "pairing", ]; @@ -173,20 +182,19 @@ fn main() -> Result<()> { let exe = VmExe::from_elf(elf, transpiler)?; + let vm = VirtualMachine::new(default_engine(), vm_config.clone()); + let pk = vm.keygen(); let (widths, interactions): (Vec, Vec) = { - let vm = VirtualMachine::new(default_engine(), vm_config.clone()); - let pk = vm.keygen(); let vk = pk.get_vk(); vk.inner .per_air .iter() .map(|vk| { - // TODO(ayush): figure out which width to use - // let total_width = vk.params.width.preprocessed.unwrap_or(0) - // + vk.params.width.cached_mains.iter().sum::() - // + vk.params.width.common_main - // + vk.params.width.after_challenge.iter().sum::(); - let total_width = vk.params.width.main_widths().iter().sum::(); + let total_width = vk.params.width.preprocessed.unwrap_or(0) + + vk.params.width.cached_mains.iter().sum::() + + vk.params.width.common_main + // TODO(ayush): no magic value 4. should come from stark config + + vk.params.width.after_challenge.iter().sum::() * 4; (total_width, vk.symbolic_constraints.interactions.len()) }) .unzip() @@ -194,10 +202,36 @@ fn main() -> Result<()> { let executor = VmExecutor::new(vm_config); executor - .execute_e1(exe.clone(), vec![]) + .execute_e1(exe.clone(), vec![], None) + // .execute(exe.clone(), vec![]) // .execute_metered(exe.clone(), vec![], widths, interactions) .expect("Failed to execute program"); + // // E2 to find segment points + // let segments = executor.execute_metered(exe.clone(), vec![], widths, interactions)?; + // for Segment { + // clk_start, + // num_cycles, + // .. + // } in segments + // { + // // E1 till clk_start + // let state = executor.execute_e1(exe.clone(), vec![], Some(clk_start))?; + // assert!(state.clk == clk_start); + // // E3/tracegen from clk_start for num_cycles beginning with state + // let mut result = executor.execute_and_generate_segment::( + // exe.clone(), + // state, + // num_cycles, + // )?; + // // let proof_input = result.per_segment.pop().unwrap(); + // // let proof = tracing::info_span!("prove_single") + // // .in_scope(|| vm.prove_single(&pk, proof_input)); + + // // let proof_bytes = bitcode::serialize(&proof)?; + // // tracing::info!("Proof size: {} bytes", proof_bytes.len()); + // } + tracing::info!("Completed program: {}", program); } tracing::info!("All programs executed successfully"); diff --git a/crates/vm/src/arch/execution_mode/e1.rs b/crates/vm/src/arch/execution_mode/e1.rs index 15592c81b2..2bb5a85a88 100644 --- a/crates/vm/src/arch/execution_mode/e1.rs +++ b/crates/vm/src/arch/execution_mode/e1.rs @@ -13,8 +13,10 @@ impl E1E2ExecutionCtx for E1Ctx { } /// Implementation of the ExecutionControl trait using the old segmentation strategy -#[derive(Default)] -pub struct E1ExecutionControl; +#[derive(Default, derive_new::new)] +pub struct E1ExecutionControl { + pub clk_end: Option, +} impl ExecutionControl for E1ExecutionControl where @@ -26,10 +28,14 @@ where fn should_suspend( &mut self, - _state: &mut VmSegmentState, + state: &mut VmSegmentState, _chip_complex: &VmChipComplex, ) -> bool { - false + if let Some(clk_end) = self.clk_end { + state.clk >= clk_end + } else { + false + } } fn on_start( diff --git a/crates/vm/src/arch/execution_mode/metered/exact.rs b/crates/vm/src/arch/execution_mode/metered/exact.rs index c88e7cad17..7640960813 100644 --- a/crates/vm/src/arch/execution_mode/metered/exact.rs +++ b/crates/vm/src/arch/execution_mode/metered/exact.rs @@ -284,7 +284,7 @@ impl E1E2ExecutionCtx for MeteredCtxExact { } fn apply_single_adapter_heights_update(trace_heights: &mut [u32], size: u32) { - let size_bits = size.ilog2() as u32; + let size_bits = size.ilog2(); for adapter_bits in (3..=size_bits).rev() { trace_heights[adapter_bits as usize - 1] += 1 << (size_bits - adapter_bits); } diff --git a/crates/vm/src/arch/execution_mode/metered/mod.rs b/crates/vm/src/arch/execution_mode/metered/mod.rs index ac1f8f46bf..ef6bd49397 100644 --- a/crates/vm/src/arch/execution_mode/metered/mod.rs +++ b/crates/vm/src/arch/execution_mode/metered/mod.rs @@ -5,6 +5,7 @@ pub mod exact; pub use bounded::MeteredCtxBounded as MeteredCtx; use openvm_instructions::instruction::Instruction; use openvm_stark_backend::{p3_field::PrimeField32, ChipUsageGetter}; +use p3_baby_bear::BabyBear; use crate::arch::{ execution_control::ExecutionControl, ChipId, ExecutionError, InsExecutorE1, VmChipComplex, @@ -13,30 +14,43 @@ use crate::arch::{ }; /// Check segment every 100 instructions. -const SEGMENT_CHECK_INTERVAL: usize = 100; +const SEGMENT_CHECK_INTERVAL: u64 = 100; // TODO(ayush): fix these values const MAX_TRACE_HEIGHT: u32 = DEFAULT_MAX_SEGMENT_LEN as u32; const MAX_TRACE_CELLS: usize = DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT; -const MAX_INTERACTIONS: usize = DEFAULT_MAX_SEGMENT_LEN * 100; +const MAX_INTERACTIONS: usize = BabyBear::ORDER_U32 as usize; + +#[derive(derive_new::new, Debug)] +pub struct Segment { + pub clk_start: u64, + pub num_cycles: u64, + pub trace_heights: Vec, +} pub struct MeteredExecutionControl<'a> { + // Constants + air_names: &'a [String], pub widths: &'a [usize], pub interactions: &'a [usize], - pub since_last_segment_check: usize, + // State + // TODO(ayush): should probably be in metered ctx + pub clk_last_segment_check: u64, + pub segments: Vec, } impl<'a> MeteredExecutionControl<'a> { - pub fn new(widths: &'a [usize], interactions: &'a [usize]) -> Self { + pub fn new(air_names: &'a [String], widths: &'a [usize], interactions: &'a [usize]) -> Self { Self { + air_names, widths, interactions, - since_last_segment_check: 0, + clk_last_segment_check: 0, + segments: vec![], } } /// Calculate the total cells used based on trace heights and widths - // TODO(ayush): account for preprocessed and permutation columns fn calculate_total_cells(&self, trace_heights: &[u32]) -> usize { trace_heights .iter() @@ -54,36 +68,19 @@ impl<'a> MeteredExecutionControl<'a> { .map(|(&height, &interactions)| (height + 1) as usize * interactions) .sum() } -} - -impl ExecutionControl for MeteredExecutionControl<'_> -where - F: PrimeField32, - VC: VmConfig, - VC::Executor: InsExecutorE1, -{ - type Ctx = MeteredCtx; - - fn should_suspend( - &mut self, - state: &mut VmSegmentState, - _chip_complex: &VmChipComplex, - ) -> bool { - // Avoid checking segment too often. - if self.since_last_segment_check != SEGMENT_CHECK_INTERVAL { - self.since_last_segment_check += 1; - return false; - } - self.since_last_segment_check = 0; + fn should_segment(&mut self, state: &mut VmSegmentState) -> bool { let trace_heights = state.ctx.trace_heights_if_finalized(); - - let max_height = trace_heights.iter().map(|&h| h.next_power_of_two()).max(); - if let Some(height) = max_height { - if height > MAX_TRACE_HEIGHT { + for (i, &height) in trace_heights.iter().enumerate() { + let padded_height = height.next_power_of_two(); + if padded_height > MAX_TRACE_HEIGHT { tracing::info!( - "Suspending execution: trace height ({}) exceeds maximum ({})", - height, + "Segment {:2} | clk {:9} | chip {} ({}) height ({:8}) > max ({:8})", + self.segments.len(), + self.clk_last_segment_check, + i, + self.air_names[i], + padded_height, MAX_TRACE_HEIGHT ); return true; @@ -93,7 +90,9 @@ where let total_cells = self.calculate_total_cells(&trace_heights); if total_cells > MAX_TRACE_CELLS { tracing::info!( - "Suspending execution: total cells ({}) exceeds maximum ({})", + "Segment {:2} | clk {:9} | total cells ({:10}) > max ({:10})", + self.segments.len(), + self.clk_last_segment_check, total_cells, MAX_TRACE_CELLS ); @@ -103,7 +102,9 @@ where let total_interactions = self.calculate_total_interactions(&trace_heights); if total_interactions > MAX_INTERACTIONS { tracing::info!( - "Suspending execution: total interactions ({}) exceeds maximum ({})", + "Segment {:2} | clk {:9} | total interactions ({:11}) > max ({:11})", + self.segments.len(), + self.clk_last_segment_check, total_interactions, MAX_INTERACTIONS ); @@ -113,11 +114,20 @@ where false } - fn on_start( + fn reset_segment( &mut self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, - ) { + ) where + F: PrimeField32, + VC: VmConfig, + { + state.ctx.leaf_indices.clear(); + + // TODO(ayush): only reset trace heights for chips that are not constant height instead + // of refilling again + state.ctx.trace_heights.fill(0); + // Program | Connector | Public Values | Memory ... | Executors (except Public Values) | // Range Checker state.ctx.trace_heights[PROGRAM_AIR_ID] = @@ -157,6 +167,63 @@ where } } + fn check_segment_limits( + &mut self, + state: &mut VmSegmentState, + chip_complex: &mut VmChipComplex, + ) where + F: PrimeField32, + VC: VmConfig, + { + // Avoid checking segment too often. + if state.clk < self.clk_last_segment_check + SEGMENT_CHECK_INTERVAL { + return; + } + + if self.should_segment(state) { + let clk_start = self + .segments + .last() + .map_or(0, |s| s.clk_start + s.num_cycles); + let segment = Segment { + clk_start, + num_cycles: self.clk_last_segment_check - clk_start, + // TODO(ayush): this is trace heights after overflow so an overestimate + trace_heights: state.ctx.trace_heights.clone(), + }; + self.segments.push(segment); + + self.reset_segment::(state, chip_complex); + } + + self.clk_last_segment_check = state.clk; + } +} + +impl ExecutionControl for MeteredExecutionControl<'_> +where + F: PrimeField32, + VC: VmConfig, + VC::Executor: InsExecutorE1, +{ + type Ctx = MeteredCtx; + + fn should_suspend( + &mut self, + _state: &mut VmSegmentState, + _chip_complex: &VmChipComplex, + ) -> bool { + false + } + + fn on_start( + &mut self, + state: &mut VmSegmentState, + chip_complex: &mut VmChipComplex, + ) { + self.reset_segment::(state, chip_complex); + } + fn on_suspend_or_terminate( &mut self, state: &mut VmSegmentState, @@ -164,6 +231,24 @@ where _exit_code: Option, ) { state.ctx.finalize_access_adapter_heights(); + + tracing::info!( + "Segment {:2} | clk {:9} | terminated", + self.segments.len(), + state.clk, + ); + // Add the last segment + let clk_start = self + .segments + .last() + .map_or(0, |s| s.clk_start + s.num_cycles); + let segment = Segment { + clk_start, + num_cycles: state.clk - clk_start, + // TODO(ayush): this is trace heights after overflow so an overestimate + trace_heights: state.ctx.trace_heights.clone(), + }; + self.segments.push(segment); } /// Execute a single instruction @@ -176,6 +261,9 @@ where where F: PrimeField32, { + // Check if segmentation needs to happen + self.check_segment_limits::(state, chip_complex); + let &Instruction { opcode, .. } = instruction; let mut offset = if chip_complex.config().has_public_values_chip() { diff --git a/crates/vm/src/arch/execution_mode/tracegen/mod.rs b/crates/vm/src/arch/execution_mode/tracegen/mod.rs new file mode 100644 index 0000000000..ea03d7966d --- /dev/null +++ b/crates/vm/src/arch/execution_mode/tracegen/mod.rs @@ -0,0 +1,7 @@ +mod normal; +mod segmentation; + +pub use normal::TracegenExecutionControl; +pub use segmentation::TracegenExecutionControlWithSegmentation; + +pub type TracegenCtx = (); diff --git a/crates/vm/src/arch/execution_mode/tracegen/normal.rs b/crates/vm/src/arch/execution_mode/tracegen/normal.rs new file mode 100644 index 0000000000..c2331caa85 --- /dev/null +++ b/crates/vm/src/arch/execution_mode/tracegen/normal.rs @@ -0,0 +1,103 @@ +use openvm_instructions::instruction::Instruction; +use openvm_stark_backend::p3_field::PrimeField32; + +use crate::{ + arch::{ + execution_control::ExecutionControl, ExecutionError, ExecutionState, InstructionExecutor, + VmChipComplex, VmConfig, VmSegmentState, + }, + system::memory::{MemoryImage, INITIAL_TIMESTAMP}, +}; + +pub type TracegenCtx = (); + +/// Implementation of the ExecutionControl trait using the old segmentation strategy +pub struct TracegenExecutionControl { + // State + pub clk_end: u64, + // TODO(ayush): do we need this if only executing one segment? + pub final_memory: Option, +} + +impl TracegenExecutionControl { + pub fn new(clk_end: u64) -> Self { + Self { + clk_end, + final_memory: None, + } + } +} + +impl ExecutionControl for TracegenExecutionControl +where + F: PrimeField32, + VC: VmConfig, +{ + type Ctx = TracegenCtx; + + fn should_suspend( + &mut self, + state: &mut VmSegmentState, + _chip_complex: &VmChipComplex, + ) -> bool { + state.clk >= self.clk_end + } + + fn on_start( + &mut self, + state: &mut VmSegmentState, + chip_complex: &mut VmChipComplex, + ) { + chip_complex + .connector_chip_mut() + .begin(ExecutionState::new(state.pc, INITIAL_TIMESTAMP + 1)); + } + + fn on_suspend_or_terminate( + &mut self, + state: &mut VmSegmentState, + chip_complex: &mut VmChipComplex, + exit_code: Option, + ) { + // TODO(ayush): this should ideally not be here + self.final_memory = Some(chip_complex.base.memory_controller.memory_image().clone()); + + let timestamp = chip_complex.memory_controller().timestamp(); + chip_complex + .connector_chip_mut() + .end(ExecutionState::new(state.pc, timestamp), exit_code); + } + + /// Execute a single instruction + fn execute_instruction( + &mut self, + state: &mut VmSegmentState, + instruction: &Instruction, + chip_complex: &mut VmChipComplex, + ) -> Result<(), ExecutionError> + where + F: PrimeField32, + { + let timestamp = chip_complex.memory_controller().timestamp(); + + let &Instruction { opcode, .. } = instruction; + + if let Some(executor) = chip_complex.inventory.get_mut_executor(&opcode) { + let memory_controller = &mut chip_complex.base.memory_controller; + let new_state = executor.execute( + memory_controller, + instruction, + ExecutionState::new(state.pc, timestamp), + )?; + state.pc = new_state.pc; + } else { + return Err(ExecutionError::DisabledOperation { + pc: state.pc, + opcode, + }); + }; + state.clk += 1; + + Ok(()) + } +} diff --git a/crates/vm/src/arch/execution_mode/tracegen.rs b/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs similarity index 91% rename from crates/vm/src/arch/execution_mode/tracegen.rs rename to crates/vm/src/arch/execution_mode/tracegen/segmentation.rs index 146cb58652..efa7e9cec9 100644 --- a/crates/vm/src/arch/execution_mode/tracegen.rs +++ b/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs @@ -6,22 +6,24 @@ use crate::{ execution_control::ExecutionControl, ExecutionError, ExecutionState, InstructionExecutor, VmChipComplex, VmConfig, VmSegmentState, }, - system::memory::MemoryImage, + system::memory::{MemoryImage, INITIAL_TIMESTAMP}, }; +use super::TracegenCtx; + /// Check segment every 100 instructions. const SEGMENT_CHECK_INTERVAL: usize = 100; -pub type TracegenCtx = (); - /// Implementation of the ExecutionControl trait using the old segmentation strategy -pub struct TracegenExecutionControl { - pub since_last_segment_check: usize, +pub struct TracegenExecutionControlWithSegmentation { + // Constant air_names: Vec, + // State + pub since_last_segment_check: usize, pub final_memory: Option, } -impl TracegenExecutionControl { +impl TracegenExecutionControlWithSegmentation { pub fn new(air_names: Vec) -> Self { Self { since_last_segment_check: 0, @@ -31,7 +33,7 @@ impl TracegenExecutionControl { } } -impl ExecutionControl for TracegenExecutionControl +impl ExecutionControl for TracegenExecutionControlWithSegmentation where F: PrimeField32, VC: VmConfig, @@ -61,10 +63,9 @@ where state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ) { - let timestamp = chip_complex.memory_controller().timestamp(); chip_complex .connector_chip_mut() - .begin(ExecutionState::new(state.pc, timestamp)); + .begin(ExecutionState::new(state.pc, INITIAL_TIMESTAMP + 1)); } fn on_suspend_or_terminate( diff --git a/crates/vm/src/arch/segment.rs b/crates/vm/src/arch/segment.rs index 142ed0a6b1..61456b5b25 100644 --- a/crates/vm/src/arch/segment.rs +++ b/crates/vm/src/arch/segment.rs @@ -113,9 +113,6 @@ where self.ctrl.on_start(state, &mut self.chip_complex); loop { - // Fetch, decode and execute single instruction - self.execute_instruction(state, &mut prev_backtrace)?; - if let Some(exit_code) = state.exit_code { self.ctrl .on_terminate(state, &mut self.chip_complex, exit_code); @@ -125,6 +122,9 @@ where self.ctrl.on_suspend(state, &mut self.chip_complex); break; } + + // Fetch, decode and execute single instruction + self.execute_instruction(state, &mut prev_backtrace)?; } Ok(()) diff --git a/crates/vm/src/arch/vm.rs b/crates/vm/src/arch/vm.rs index 0a6392179a..cfd4503960 100644 --- a/crates/vm/src/arch/vm.rs +++ b/crates/vm/src/arch/vm.rs @@ -20,6 +20,7 @@ use thiserror::Error; use tracing::info_span; use super::{ + execution_mode::{metered::Segment, tracegen::TracegenExecutionControlWithSegmentation}, ExecutionError, InsExecutorE1, VmChipComplex, VmComplexTraceHeights, VmConfig, VmInventoryError, CONNECTOR_AIR_ID, MERKLE_AIR_ID, PROGRAM_AIR_ID, PROGRAM_CACHED_TRACE_INDEX, }; @@ -132,13 +133,12 @@ impl VmState { } } -type TracegenVmSegmentExecutor = VmSegmentExecutor; pub struct VmExecutorOneSegmentResult where F: PrimeField32, VC: VmConfig, { - pub segment: TracegenVmSegmentExecutor, + pub segment: VmSegmentExecutor, pub next_state: Option>, } @@ -183,7 +183,10 @@ where &self, exe: impl Into>, input: impl Into>, - mut f: impl FnMut(usize, TracegenVmSegmentExecutor) -> Result, + mut f: impl FnMut( + usize, + VmSegmentExecutor, + ) -> Result, map_err: impl Fn(ExecutionError) -> E, ) -> Result, E> { let mem_config = self.config.system().memory_config; @@ -227,7 +230,10 @@ where &self, exe: impl Into>, input: impl Into>, - ) -> Result>, ExecutionError> { + ) -> Result< + Vec>, + ExecutionError, + > { self.execute_and_then(exe, input, |_, seg| Ok(seg), |err| err) } @@ -249,8 +255,8 @@ where Some(from_state.memory), ) .unwrap(); - let ctrl = TracegenExecutionControl::new(chip_complex.air_names()); - let mut segment = TracegenVmSegmentExecutor::new( + let ctrl = TracegenExecutionControlWithSegmentation::new(chip_complex.air_names()); + let mut segment = VmSegmentExecutor::new( chip_complex, self.trace_height_constraints.clone(), exe.fn_bounds.clone(), @@ -338,7 +344,8 @@ where &self, exe: impl Into>, input: impl Into>, - ) -> Result<(), ExecutionError> + num_cycles: Option, + ) -> Result, ExecutionError> where VC::Executor: InsExecutorE1, { @@ -351,73 +358,61 @@ where exe.init_memory.clone(), ); - let pc = exe.pc_start; - let mut state = VmState::new(0, pc, memory, input); - let mut segment_idx = 0; - - loop { - let _span = info_span!("execute_segment", segment = segment_idx).entered(); + let state = VmState::new(0, exe.pc_start, memory, input); - let chip_complex = create_and_initialize_chip_complex( - &self.config, - exe.program.clone(), - state.input, - None, - ) - .unwrap(); - let mut segment = VmSegmentExecutor::::new( - chip_complex, - self.trace_height_constraints.clone(), - exe.fn_bounds.clone(), - E1ExecutionControl, - ); - #[cfg(feature = "bench-metrics")] - { - segment.metrics = state.metrics; - } + let _span = info_span!("execute_e1_until_cycle").entered(); - let mut exec_state = VmSegmentState::new( - state.clk, - state.pc, - Some(GuestMemory::new(state.memory)), - (), - ); - metrics_span("execute_time_ms", || { - segment.execute_from_state(&mut exec_state) - })?; - - if let Some(exit_code) = exec_state.exit_code { - // Check exit code for the final segment - if exit_code != ExitCode::Success as u32 { - return Err(ExecutionError::FailedWithExitCode(exit_code)); - } - tracing::debug!("Execution completed in {} segments", segment_idx + 1); - #[cfg(feature = "bench-metrics")] - metrics::counter!("num_segments").absolute((segment_idx + 1) as u64); - return Ok(()); - } + let chip_complex = create_and_initialize_chip_complex( + &self.config, + exe.program.clone(), + state.input, + None, + ) + .unwrap(); + let mut segment = VmSegmentExecutor::::new( + chip_complex, + self.trace_height_constraints.clone(), + exe.fn_bounds.clone(), + E1ExecutionControl::new(num_cycles), + ); + #[cfg(feature = "bench-metrics")] + { + segment.metrics = state.metrics; + } - assert!( - self.continuation_enabled(), - "multiple segments require to enable continuations" - ); + let mut exec_state = VmSegmentState::new( + state.clk, + state.pc, + Some(GuestMemory::new(state.memory)), + (), + ); + metrics_span("execute_time_ms", || { + segment.execute_from_state(&mut exec_state) + })?; - let streams = segment.chip_complex.take_streams(); + if let Some(end_cycle) = num_cycles { + assert_eq!(exec_state.clk, end_cycle); + } else { + match exec_state.exit_code { + Some(code) => { + if code != ExitCode::Success as u32 { + return Err(ExecutionError::FailedWithExitCode(code)); + } + } + None => return Err(ExecutionError::DidNotTerminate), + }; + } + let state = VmState { + clk: exec_state.clk, + pc: exec_state.pc, + memory: exec_state.memory.unwrap().memory, + input: segment.chip_complex.take_streams(), #[cfg(feature = "bench-metrics")] - let metrics = segment.metrics.partial_take(); - - state = VmState { - clk: exec_state.clk, - pc: exec_state.pc, - memory: exec_state.memory.unwrap().memory, - input: streams, - #[cfg(feature = "bench-metrics")] - metrics, - }; + metrics: segment.metrics.partial_take(), + }; - segment_idx += 1; - } + Ok(state) } pub fn execute_metered( @@ -426,116 +421,93 @@ where input: impl Into>, widths: Vec, interactions: Vec, - ) -> Result<(), ExecutionError> + ) -> Result, ExecutionError> where VC::Executor: InsExecutorE1, { let mem_config = self.config.system().memory_config; let exe = exe.into(); + let memory = AddressMap::from_sparse( mem_config.as_offset, 1 << mem_config.as_height, 1 << mem_config.pointer_max_bits, exe.init_memory.clone(), ); + let state = VmState::new(0, exe.pc_start, memory, input); - let pc = exe.pc_start; - let mut state = VmState::new(0, pc, memory, input); - let mut segment_idx = 0; - - loop { - let _span = info_span!("execute_segment", segment = segment_idx).entered(); + let _span = info_span!("execute_metered").entered(); - let chip_complex = create_and_initialize_chip_complex( - &self.config, - exe.program.clone(), - state.input, - None, - ) - .unwrap(); - let ctrl = MeteredExecutionControl::new(&widths, &interactions); - let mut segment = VmSegmentExecutor::::new( - chip_complex, - self.trace_height_constraints.clone(), - exe.fn_bounds.clone(), - ctrl, - ); + let chip_complex = create_and_initialize_chip_complex( + &self.config, + exe.program.clone(), + state.input, + None, + ) + .unwrap(); + let air_names = chip_complex.air_names(); + let ctrl = MeteredExecutionControl::new(&air_names, &widths, &interactions); + let mut executor = VmSegmentExecutor::::new( + chip_complex, + self.trace_height_constraints.clone(), + exe.fn_bounds.clone(), + ctrl, + ); - #[cfg(feature = "bench-metrics")] - { - segment.metrics = state.metrics; - } + #[cfg(feature = "bench-metrics")] + { + executor.metrics = state.metrics; + } - let continuations_enabled = segment + let continuations_enabled = executor + .chip_complex + .memory_controller() + .continuation_enabled(); + let num_access_adapters = executor + .chip_complex + .memory_controller() + .access_adapters + .num_access_adapters(); + let ctx = MeteredCtx::new( + widths.len(), + continuations_enabled, + num_access_adapters as u8, + executor .chip_complex .memory_controller() - .continuation_enabled(); - let num_access_adapters = segment + .memory + .min_block_size + .iter() + .map(|&x| log2_strict_usize(x as usize) as u8) + .collect(), + executor .chip_complex .memory_controller() - .access_adapters - .num_access_adapters(); - let ctx = MeteredCtx::new( - widths.len(), - continuations_enabled, - num_access_adapters as u8, - segment - .chip_complex - .memory_controller() - .memory - .min_block_size - .iter() - .map(|&x| log2_strict_usize(x as usize) as u8) - .collect(), - segment - .chip_complex - .memory_controller() - .mem_config() - .memory_dimensions(), - ); - - let mut exec_state = VmSegmentState::new( - state.clk, - state.pc, - Some(GuestMemory::new(state.memory)), - ctx, - ); - metrics_span("execute_time_ms", || { - segment.execute_from_state(&mut exec_state) - })?; - - if let Some(exit_code) = exec_state.exit_code { - // Check exit code for the final segment - if exit_code != ExitCode::Success as u32 { - return Err(ExecutionError::FailedWithExitCode(exit_code)); - } - tracing::debug!("Execution completed in {} segments", segment_idx + 1); - #[cfg(feature = "bench-metrics")] - metrics::counter!("num_segments").absolute((segment_idx + 1) as u64); - return Ok(()); - } - - assert!( - self.continuation_enabled(), - "multiple segments require to enable continuations" - ); - - let streams = segment.chip_complex.take_streams(); + .mem_config() + .memory_dimensions(), + ); - #[cfg(feature = "bench-metrics")] - let metrics = segment.metrics.partial_take(); + let mut exec_state = VmSegmentState::new( + state.clk, + state.pc, + Some(GuestMemory::new(state.memory)), + ctx, + ); + metrics_span("execute_time_ms", || { + executor.execute_from_state(&mut exec_state) + })?; - state = VmState { - clk: exec_state.clk, - pc: exec_state.pc, - memory: exec_state.memory.unwrap().memory, - input: streams, - #[cfg(feature = "bench-metrics")] - metrics, - }; + // Check exit code + match exec_state.exit_code { + Some(code) => { + if code != ExitCode::Success as u32 { + return Err(ExecutionError::FailedWithExitCode(code)); + } + } + None => return Err(ExecutionError::DidNotTerminate), + }; - segment_idx += 1; - } + Ok(executor.ctrl.segments) } pub fn execute_and_generate( @@ -551,6 +523,61 @@ where self.execute_and_generate_impl(exe.into(), None, input) } + pub fn execute_and_generate_segment( + &self, + exe: impl Into>, + state: VmState, + num_cycles: u64, + ) -> Result, GenerationError> + where + Domain: PolynomialSpace, + VC::Executor: Chip, + VC::Periphery: Chip, + { + let _span = info_span!("execute_and_generate_segment").entered(); + + let exe = exe.into(); + let chip_complex = create_and_initialize_chip_complex( + &self.config, + exe.program.clone(), + state.input, + Some(state.memory), + ) + .unwrap(); + let ctrl = TracegenExecutionControl::new(state.clk + num_cycles); + let mut segment = VmSegmentExecutor::<_, VC, _>::new( + chip_complex, + self.trace_height_constraints.clone(), + exe.fn_bounds.clone(), + ctrl, + ); + + // TODO(ayush): do i need this? + if let Some(overridden_heights) = self.overridden_heights.as_ref() { + segment.set_override_trace_heights(overridden_heights.clone()); + } + + let mut exec_state = VmSegmentState::new(state.clk, state.pc, None, ()); + metrics_span("execute_from_state", || { + segment.execute_from_state(&mut exec_state) + })?; + + assert_eq!( + exec_state.pc, + segment.chip_complex.connector_chip().boundary_states[1] + .unwrap() + .pc + ); + + let proof_input = tracing::info_span!("generate_proof_input") + .in_scope(|| segment.generate_proof_input(None))?; + + Ok(VmExecutorResult { + per_segment: vec![proof_input], + final_memory: None, + }) + } + pub fn execute_and_generate_with_cached_program( &self, committed_exe: Arc>, @@ -702,7 +729,8 @@ where &self, exe: VmExe, input: impl Into>, - ) -> Result, ExecutionError> { + ) -> Result, ExecutionError> + { let chip_complex = create_and_initialize_chip_complex( &self.config, exe.program.clone(), @@ -710,8 +738,8 @@ where None, ) .unwrap(); - let ctrl = TracegenExecutionControl::new(chip_complex.air_names()); - let mut segment = TracegenVmSegmentExecutor::new( + let ctrl = TracegenExecutionControlWithSegmentation::new(chip_complex.air_names()); + let mut segment = VmSegmentExecutor::new( chip_complex, self.trace_height_constraints.clone(), exe.fn_bounds.clone(), diff --git a/crates/vm/src/system/connector/mod.rs b/crates/vm/src/system/connector/mod.rs index dc9ff88ea2..fd97d7e55d 100644 --- a/crates/vm/src/system/connector/mod.rs +++ b/crates/vm/src/system/connector/mod.rs @@ -215,6 +215,7 @@ impl VmConnectorChip { pub fn begin(&mut self, state: ExecutionState) { self.boundary_states[0] = Some(ConnectorCols { pc: state.pc, + // TODO(ayush): should this be hardcoded to INITIAL_TIMESTAMP? timestamp: state.timestamp, is_terminate: 0, exit_code: 0, diff --git a/extensions/keccak256/circuit/src/trace.rs b/extensions/keccak256/circuit/src/trace.rs index bf28db55bd..52ed933375 100644 --- a/extensions/keccak256/circuit/src/trace.rs +++ b/extensions/keccak256/circuit/src/trace.rs @@ -171,6 +171,10 @@ impl TraceStep for KeccakVmStep { Self: Send + Sync, F: Send + Sync, { + if rows_used == 0 { + return; + } + let num_blocks = rows_used.div_ceil(NUM_ROUNDS); let mut states = Vec::with_capacity(num_blocks); let mut state = [0u64; 25]; diff --git a/extensions/sha256/circuit/src/sha256_chip/trace.rs b/extensions/sha256/circuit/src/sha256_chip/trace.rs index 24d4799d3d..e69e748073 100644 --- a/extensions/sha256/circuit/src/sha256_chip/trace.rs +++ b/extensions/sha256/circuit/src/sha256_chip/trace.rs @@ -188,6 +188,10 @@ impl TraceStep for Sha256VmStep { Self: Send + Sync, F: Send + Sync, { + if rows_used == 0 { + return; + } + let mem_ptr_shift: u32 = 1 << (RV32_REGISTER_NUM_LIMBS * RV32_CELL_BITS - self.pointer_max_bits); From a32e4bea6460036fa32e9bfa93ae7bba49e86865 Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Thu, 22 May 2025 13:15:25 -0400 Subject: [PATCH 34/49] feat: port public values chip and native adapter (#1681) Fixed todos related to porting public values chip and native adapter. I'm guessing these are used in recursion --------- Co-authored-by: Arayi Khalatyan <127004086+arayikhalatyan@users.noreply.github.com> --- benchmarks/execute/examples/regex_execute.rs | 58 ++--- benchmarks/execute/src/main.rs | 58 ++--- .../execution_mode/tracegen/segmentation.rs | 1 + crates/vm/src/arch/extensions.rs | 10 +- crates/vm/src/arch/vm.rs | 2 +- crates/vm/src/system/memory/merkle/mod.rs | 5 +- crates/vm/src/system/memory/mod.rs | 5 +- .../src/system/memory/tree/public_values.rs | 3 + crates/vm/src/system/native_adapter/mod.rs | 229 ++++++------------ crates/vm/src/system/native_adapter/util.rs | 98 ++++++++ crates/vm/src/system/public_values/core.rs | 158 ++++-------- crates/vm/tests/integration_test.rs | 50 ++-- .../circuit/src/adapters/convert_adapter.rs | 4 +- extensions/native/circuit/src/fri/mod.rs | 11 +- .../rv32im/circuit/src/hintstore/mod.rs | 10 +- 15 files changed, 328 insertions(+), 374 deletions(-) create mode 100644 crates/vm/src/system/native_adapter/util.rs diff --git a/benchmarks/execute/examples/regex_execute.rs b/benchmarks/execute/examples/regex_execute.rs index 59705a19fd..3a6fd4162f 100644 --- a/benchmarks/execute/examples/regex_execute.rs +++ b/benchmarks/execute/examples/regex_execute.rs @@ -1,35 +1,35 @@ -use openvm_circuit::arch::{instructions::exe::VmExe, VmExecutor}; -use openvm_keccak256_circuit::Keccak256Rv32Config; -use openvm_keccak256_transpiler::Keccak256TranspilerExtension; -use openvm_rv32im_transpiler::{ - Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, -}; -use openvm_sdk::StdIn; -use openvm_stark_sdk::p3_baby_bear::BabyBear; -use openvm_transpiler::{ - elf::Elf, openvm_platform::memory::MEM_SIZE, transpiler::Transpiler, FromElf, -}; +// use openvm_circuit::arch::{instructions::exe::VmExe, VmExecutor}; +// use openvm_keccak256_circuit::Keccak256Rv32Config; +// use openvm_keccak256_transpiler::Keccak256TranspilerExtension; +// use openvm_rv32im_transpiler::{ +// Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, +// }; +// use openvm_sdk::StdIn; +// use openvm_stark_sdk::p3_baby_bear::BabyBear; +// use openvm_transpiler::{ +// elf::Elf, openvm_platform::memory::MEM_SIZE, transpiler::Transpiler, FromElf, +// }; fn main() { - let elf = Elf::decode(include_bytes!("regex-elf"), MEM_SIZE as u32).unwrap(); - let exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(Keccak256TranspilerExtension), - ) - .unwrap(); + // let elf = Elf::decode(include_bytes!("regex-elf"), MEM_SIZE as u32).unwrap(); + // let exe = VmExe::from_elf( + // elf, + // Transpiler::::default() + // .with_extension(Rv32ITranspilerExtension) + // .with_extension(Rv32MTranspilerExtension) + // .with_extension(Rv32IoTranspilerExtension) + // .with_extension(Keccak256TranspilerExtension), + // ) + // .unwrap(); - let config = Keccak256Rv32Config::default(); - let executor = VmExecutor::::new(config); + // let config = Keccak256Rv32Config::default(); + // let executor = VmExecutor::::new(config); - let data = include_str!("../../guest/regex/regex_email.txt"); + // let data = include_str!("../../guest/regex/regex_email.txt"); - let timer = std::time::Instant::now(); - executor - .execute(exe.clone(), StdIn::from_bytes(data.as_bytes())) - .unwrap(); - println!("execute_time: {:?}", timer.elapsed()); + // let timer = std::time::Instant::now(); + // executor + // .execute(exe.clone(), StdIn::from_bytes(data.as_bytes())) + // .unwrap(); + // println!("execute_time: {:?}", timer.elapsed()); } diff --git a/benchmarks/execute/src/main.rs b/benchmarks/execute/src/main.rs index 5147ab7629..1c17e87a67 100644 --- a/benchmarks/execute/src/main.rs +++ b/benchmarks/execute/src/main.rs @@ -1,18 +1,13 @@ -use clap::{Parser, ValueEnum}; +use clap::Parser; use eyre::Result; use openvm_benchmarks_utils::{get_elf_path, get_programs_dir, read_elf_file}; -use openvm_bigint_circuit::{Int256, Int256Executor, Int256Periphery, Int256Rv32Config}; +use openvm_bigint_circuit::{Int256, Int256Executor, Int256Periphery}; use openvm_bigint_transpiler::Int256TranspilerExtension; use openvm_circuit::{ - arch::{ - execution_mode::metered::Segment, instructions::exe::VmExe, SystemConfig, VirtualMachine, - VmExecutor, VmExecutorResult, - }, + arch::{instructions::exe::VmExe, SystemConfig, VmExecutor}, derive::VmConfig, }; -use openvm_keccak256_circuit::{ - Keccak256, Keccak256Executor, Keccak256Periphery, Keccak256Rv32Config, -}; +use openvm_keccak256_circuit::{Keccak256, Keccak256Executor, Keccak256Periphery}; use openvm_keccak256_transpiler::Keccak256TranspilerExtension; use openvm_rv32im_circuit::{ Rv32I, Rv32IExecutor, Rv32IPeriphery, Rv32Io, Rv32IoExecutor, Rv32IoPeriphery, Rv32M, @@ -25,14 +20,7 @@ use openvm_sha256_circuit::{Sha256, Sha256Executor, Sha256Periphery}; use openvm_sha256_transpiler::Sha256TranspilerExtension; use openvm_stark_sdk::{ bench::run_with_metric_collection, - config::{ - baby_bear_blake3::BabyBearBlake3Config, - baby_bear_poseidon2::{default_engine, BabyBearPoseidon2Config}, - }, - openvm_stark_backend::{ - self, - p3_field::{FieldExtensionAlgebra, PrimeField32}, - }, + openvm_stark_backend::{self, p3_field::PrimeField32}, p3_baby_bear::BabyBear, }; use openvm_transpiler::{transpiler::Transpiler, FromElf}; @@ -182,24 +170,6 @@ fn main() -> Result<()> { let exe = VmExe::from_elf(elf, transpiler)?; - let vm = VirtualMachine::new(default_engine(), vm_config.clone()); - let pk = vm.keygen(); - let (widths, interactions): (Vec, Vec) = { - let vk = pk.get_vk(); - vk.inner - .per_air - .iter() - .map(|vk| { - let total_width = vk.params.width.preprocessed.unwrap_or(0) - + vk.params.width.cached_mains.iter().sum::() - + vk.params.width.common_main - // TODO(ayush): no magic value 4. should come from stark config - + vk.params.width.after_challenge.iter().sum::() * 4; - (total_width, vk.symbolic_constraints.interactions.len()) - }) - .unzip() - }; - let executor = VmExecutor::new(vm_config); executor .execute_e1(exe.clone(), vec![], None) @@ -207,6 +177,24 @@ fn main() -> Result<()> { // .execute_metered(exe.clone(), vec![], widths, interactions) .expect("Failed to execute program"); + // let vm = VirtualMachine::new(default_engine(), vm_config.clone()); + // let pk = vm.keygen(); + // let (widths, interactions): (Vec, Vec) = { + // let vk = pk.get_vk(); + // vk.inner + // .per_air + // .iter() + // .map(|vk| { + // let total_width = vk.params.width.preprocessed.unwrap_or(0) + // + vk.params.width.cached_mains.iter().sum::() + // + vk.params.width.common_main + // // TODO(ayush): no magic value 4. should come from stark config + // + vk.params.width.after_challenge.iter().sum::() * 4; + // (total_width, vk.symbolic_constraints.interactions.len()) + // }) + // .unzip() + // }; + // // E2 to find segment points // let segments = executor.execute_metered(exe.clone(), vec![], widths, interactions)?; // for Segment { diff --git a/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs b/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs index efa7e9cec9..431a8a7bf4 100644 --- a/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs +++ b/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs @@ -14,6 +14,7 @@ use super::TracegenCtx; /// Check segment every 100 instructions. const SEGMENT_CHECK_INTERVAL: usize = 100; +// TODO(ayush): fix this name since it's a mouthful /// Implementation of the ExecutionControl trait using the old segmentation strategy pub struct TracegenExecutionControlWithSegmentation { // Constant diff --git a/crates/vm/src/arch/extensions.rs b/crates/vm/src/arch/extensions.rs index 7f62f2046f..fd34a1815a 100644 --- a/crates/vm/src/arch/extensions.rs +++ b/crates/vm/src/arch/extensions.rs @@ -592,7 +592,7 @@ impl SystemComplex { assert_eq!(inventory.executors().len(), Self::PV_EXECUTOR_IDX); // TODO(ayush): this should be decided after e2 execution - const MAX_INS_CAPACITY: usize = 0; + const MAX_INS_CAPACITY: usize = 1 << 22; let chip = PublicValuesChip::new( VmAirWrapper::new( NativeAdapterAir::new( @@ -604,7 +604,11 @@ impl SystemComplex { config.max_constraint_degree as u32 - 1, ), ), - PublicValuesCoreStep::new(NativeAdapterStep::new(), config.num_public_values), + PublicValuesCoreStep::new( + NativeAdapterStep::new(), + config.num_public_values, + config.max_constraint_degree as u32 - 1, + ), MAX_INS_CAPACITY, memory_controller.helper(), ); @@ -869,7 +873,7 @@ impl VmChipComplex { } /// Return air names of all chips in order. - pub(crate) fn air_names(&self) -> Vec + pub fn air_names(&self) -> Vec where E: ChipUsageGetter, P: ChipUsageGetter, diff --git a/crates/vm/src/arch/vm.rs b/crates/vm/src/arch/vm.rs index cfd4503960..a9aa38ccd1 100644 --- a/crates/vm/src/arch/vm.rs +++ b/crates/vm/src/arch/vm.rs @@ -124,9 +124,9 @@ impl VmState { pub fn new(clk: u64, pc: u32, memory: MemoryImage, input: impl Into>) -> Self { Self { clk, + pc, memory, input: input.into(), - pc, #[cfg(feature = "bench-metrics")] metrics: VmMetrics::default(), } diff --git a/crates/vm/src/system/memory/merkle/mod.rs b/crates/vm/src/system/memory/merkle/mod.rs index e9c7c49e8c..4eac44bf81 100644 --- a/crates/vm/src/system/memory/merkle/mod.rs +++ b/crates/vm/src/system/memory/merkle/mod.rs @@ -11,8 +11,9 @@ pub use air::*; pub use columns::*; pub(super) use trace::SerialReceiver; -#[cfg(test)] -mod tests; +// TODO: add back +// #[cfg(test)] +// mod tests; pub struct MemoryMerkleChip { pub air: MemoryMerkleAir, diff --git a/crates/vm/src/system/memory/mod.rs b/crates/vm/src/system/memory/mod.rs index 0020e0708d..1001520635 100644 --- a/crates/vm/src/system/memory/mod.rs +++ b/crates/vm/src/system/memory/mod.rs @@ -7,8 +7,9 @@ pub mod offline_checker; pub mod online; pub mod paged_vec; mod persistent; -#[cfg(test)] -mod tests; +// TODO: add back +// #[cfg(test)] +// mod tests; pub mod tree; mod volatile; diff --git a/crates/vm/src/system/memory/tree/public_values.rs b/crates/vm/src/system/memory/tree/public_values.rs index cb62c4917a..6df24ec4e0 100644 --- a/crates/vm/src/system/memory/tree/public_values.rs +++ b/crates/vm/src/system/memory/tree/public_values.rs @@ -199,6 +199,8 @@ pub fn extract_public_values( public_values } +// TODO: add back +/* #[cfg(test)] mod tests { use openvm_stark_backend::p3_field::FieldAlgebra; @@ -244,3 +246,4 @@ mod tests { .unwrap(); } } +*/ diff --git a/crates/vm/src/system/native_adapter/mod.rs b/crates/vm/src/system/native_adapter/mod.rs index 18f759a5ec..25729e9376 100644 --- a/crates/vm/src/system/native_adapter/mod.rs +++ b/crates/vm/src/system/native_adapter/mod.rs @@ -1,3 +1,5 @@ +mod util; + use std::{ borrow::{Borrow, BorrowMut}, marker::PhantomData, @@ -5,15 +7,12 @@ use std::{ use openvm_circuit::{ arch::{ - AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge, - ExecutionBus, ExecutionState, MinimalInstruction, Result, VmAdapterAir, VmAdapterInterface, + AdapterAirContext, BasicAdapterInterface, ExecutionBridge, ExecutionState, + MinimalInstruction, VmAdapterAir, }, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadOrImmediateAuxCols, MemoryWriteAuxCols}, - MemoryAddress, MemoryController, - }, - program::ProgramBus, + system::memory::{ + offline_checker::{MemoryBridge, MemoryReadOrImmediateAuxCols, MemoryWriteAuxCols}, + MemoryAddress, }, }; use openvm_circuit_primitives_derive::AlignedBorrow; @@ -23,47 +22,14 @@ use openvm_stark_backend::{ p3_air::BaseAir, p3_field::{Field, FieldAlgebra, PrimeField32}, }; -use serde::{Deserialize, Serialize}; -use serde_big_array::BigArray; +use util::{tracing_read_or_imm_native, tracing_write_native, AS_NATIVE}; use super::memory::{online::TracingMemory, MemoryAuxColsFactory}; use crate::{ arch::{execution_mode::E1E2ExecutionCtx, AdapterExecutorE1, AdapterTraceStep, VmStateMut}, - system::memory::{online::GuestMemory, RecordId}, + system::memory::online::GuestMemory, }; -#[repr(C)] -#[derive(Debug, Serialize, Deserialize)] -pub struct NativeReadRecord { - #[serde(with = "BigArray")] - pub reads: [(RecordId, [F; 1]); R], -} - -impl NativeReadRecord { - pub fn b(&self) -> &[F; 1] { - &self.reads[0].1 - } - - pub fn c(&self) -> &[F; 1] { - &self.reads[1].1 - } -} - -#[repr(C)] -#[derive(Debug, Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct NativeWriteRecord { - pub from_state: ExecutionState, - #[serde(with = "BigArray")] - pub writes: [(RecordId, [F; 1]); W], -} - -impl NativeWriteRecord { - pub fn a(&self) -> &[F; 1] { - &self.writes[0].1 - } -} - #[repr(C)] #[derive(AlignedBorrow)] pub struct NativeAdapterReadCols { @@ -214,7 +180,34 @@ where instruction: &Instruction, adapter_row: &mut [F], ) -> Self::ReadData { - todo!("Implement read operation"); + assert!(R <= 2); + + let &Instruction { b, e, f, c, .. } = instruction; + + let cols: &mut NativeAdapterCols<_, R, W> = adapter_row.borrow_mut(); + + let mut reads = [[F::ZERO; 1]; R]; + if R >= 1 { + cols.reads_aux[0].address.pointer = b; + reads[0][0] = tracing_read_or_imm_native( + memory, + e.as_canonical_u32(), + b, + &mut cols.reads_aux[0].address.address_space, + &mut cols.reads_aux[0].read_aux, + ); + } + if R >= 2 { + cols.reads_aux[1].address.pointer = c; + reads[1][0] = tracing_read_or_imm_native( + memory, + f.as_canonical_u32(), + c, + &mut cols.reads_aux[1].address.address_space, + &mut cols.reads_aux[1].read_aux, + ); + } + reads } #[inline(always)] @@ -225,17 +218,54 @@ where adapter_row: &mut [F], data: &Self::WriteData, ) { - todo!("Implement write operation"); + assert!(W <= 1); + + let &Instruction { a, d, .. } = instruction; + + debug_assert_eq!(d.as_canonical_u32(), AS_NATIVE); + + let cols: &mut NativeAdapterCols<_, R, W> = adapter_row.borrow_mut(); + + if W >= 1 { + cols.writes_aux[0].address.address_space = F::from_canonical_u32(AS_NATIVE); + cols.writes_aux[0].address.pointer = a; + tracing_write_native( + memory, + a.as_canonical_u32(), + &data[0], + &mut cols.writes_aux[0].write_aux, + ); + } } #[inline(always)] fn fill_trace_row( &self, mem_helper: &MemoryAuxColsFactory, - bitwise_lookup_chip: Self::TraceContext<'_>, + _ctx: Self::TraceContext<'_>, adapter_row: &mut [F], ) { - todo!("Implement fill_trace_row operation"); + let adapter_row: &mut NativeAdapterCols<_, R, W> = adapter_row.borrow_mut(); + + let mut timestamp = adapter_row.from_state.timestamp.as_canonical_u32(); + + for read_aux in &mut adapter_row.reads_aux { + mem_helper.fill_from_prev(timestamp, &mut read_aux.read_aux.base); + timestamp += 1; + + if read_aux.address.address_space.is_zero() { + read_aux.read_aux.is_immediate = F::ONE; + read_aux.read_aux.is_zero_aux = F::ZERO; + } else { + read_aux.read_aux.is_immediate = F::ZERO; + read_aux.read_aux.is_zero_aux = read_aux.address.address_space.inverse(); + } + } + + for write_aux in &mut adapter_row.writes_aux { + mem_helper.fill_from_prev(timestamp, write_aux.write_aux.as_mut()); + timestamp += 1; + } } } @@ -300,108 +330,3 @@ where } } } - -// impl VmAdapterChip for NativeAdapterStep -// where -// F: PrimeField32, -// { -// type ReadRecord = NativeReadRecord; -// type WriteRecord = NativeWriteRecord; -// type Air = NativeAdapterAir; -// type Interface = BasicAdapterInterface, R, W, 1, 1>; - -// fn preprocess( -// &mut self, -// memory: &mut MemoryController, -// instruction: &Instruction, -// ) -> Result<( -// >::Reads, -// Self::ReadRecord, -// )> { -// assert!(R <= 2); -// let Instruction { b, c, e, f, .. } = *instruction; - -// let mut reads = Vec::with_capacity(R); -// if R >= 1 { -// reads.push(unsafe { -// memory -// .memory_image() -// .read::(e.as_canonical_u32(), b.as_canonical_u32()) -// }); -// } -// if R >= 2 { -// reads.push(unsafe { -// memory -// .memory_image() -// .read::(f.as_canonical_u32(), c.as_canonical_u32()) -// }); -// } -// Ok(( -// reads.try_into().unwrap(), -// Self::ReadRecord { reads: todo!() }, -// )) -// } - -// fn postprocess( -// &mut self, -// memory: &mut MemoryController, -// instruction: &Instruction, -// from_state: ExecutionState, -// output: AdapterRuntimeContext, -// _read_record: &Self::ReadRecord, -// ) -> Result<(ExecutionState, Self::WriteRecord)> { -// assert!(W <= 1); -// let Instruction { a, d, .. } = *instruction; -// let mut writes = Vec::with_capacity(W); -// if W >= 1 { -// todo!(); -// // let (record_id, _) = memory.write(d, a, &output.writes[0]); -// // writes.push((record_id, output.writes[0])); -// } - -// Ok(( -// ExecutionState { -// pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP), -// timestamp: memory.timestamp(), -// }, -// Self::WriteRecord { -// from_state, -// writes: writes.try_into().unwrap(), -// }, -// )) -// } - -// fn generate_trace_row( -// &self, -// row_slice: &mut [F], -// read_record: Self::ReadRecord, -// write_record: Self::WriteRecord, -// memory: &OfflineMemory, -// ) { -// let row_slice: &mut NativeAdapterCols<_, R, W> = row_slice.borrow_mut(); -// let aux_cols_factory = memory.aux_cols_factory(); - -// row_slice.from_state = write_record.from_state.map(F::from_canonical_u32); - -// for (i, read) in read_record.reads.iter().enumerate() { -// let (id, _) = read; -// let record = memory.record_by_id(*id); -// aux_cols_factory -// .generate_read_or_immediate_aux(record, &mut row_slice.reads_aux[i].read_aux); -// row_slice.reads_aux[i].address = -// MemoryAddress::new(record.address_space, record.pointer); -// } - -// for (i, write) in write_record.writes.iter().enumerate() { -// let (id, _) = write; -// let record = memory.record_by_id(*id); -// aux_cols_factory.generate_write_aux(record, &mut row_slice.writes_aux[i].write_aux); -// row_slice.writes_aux[i].address = -// MemoryAddress::new(record.address_space, record.pointer); -// } -// } - -// fn air(&self) -> &Self::Air { -// &self.air -// } -// } diff --git a/crates/vm/src/system/native_adapter/util.rs b/crates/vm/src/system/native_adapter/util.rs new file mode 100644 index 0000000000..5db1a5a050 --- /dev/null +++ b/crates/vm/src/system/native_adapter/util.rs @@ -0,0 +1,98 @@ +// TODO(ayush): this whole file is copied from extensions/native and shouldn't be here +use openvm_circuit::system::memory::{ + offline_checker::{MemoryBaseAuxCols, MemoryReadOrImmediateAuxCols, MemoryWriteAuxCols}, + online::TracingMemory, +}; +use openvm_stark_backend::p3_field::PrimeField32; + +// TODO(ayush): should be imported from somewhere +const AS_IMMEDIATE: u32 = 0; +pub(super) const AS_NATIVE: u32 = 4; + +/// Atomic read operation which increments the timestamp by 1. +/// Returns `(t_prev, [ptr:BLOCK_SIZE]_4)` where `t_prev` is the timestamp of the last memory +/// access. +#[inline(always)] +fn timed_read( + memory: &mut TracingMemory, + ptr: u32, +) -> (u32, [F; BLOCK_SIZE]) +where + F: PrimeField32, +{ + // SAFETY: + // - address space `Native` will always have cell type `F` and minimum alignment of `1` + unsafe { memory.read::(AS_NATIVE, ptr) } +} + +#[inline(always)] +fn timed_write( + memory: &mut TracingMemory, + ptr: u32, + vals: &[F; BLOCK_SIZE], +) -> (u32, [F; BLOCK_SIZE]) +where + F: PrimeField32, +{ + // SAFETY: + // - address space `Native` will always have cell type `F` and minimum alignment of `1` + unsafe { memory.write::(AS_NATIVE, ptr, vals) } +} + +/// Reads register value at `ptr` from memory and records the memory access in mutable buffer. +/// Trace generation relevant to this memory access can be done fully from the recorded buffer. +#[inline(always)] +pub fn tracing_read_native( + memory: &mut TracingMemory, + ptr: u32, + aux_cols: &mut MemoryBaseAuxCols, +) -> [F; BLOCK_SIZE] +where + F: PrimeField32, +{ + let (t_prev, data) = timed_read(memory, ptr); + aux_cols.set_prev(F::from_canonical_u32(t_prev)); + data +} + +/// Writes `ptr, vals` into memory and records the memory access in mutable buffer. +/// Trace generation relevant to this memory access can be done fully from the recorded buffer. +#[inline(always)] +pub fn tracing_write_native( + memory: &mut TracingMemory, + ptr: u32, + vals: &[F; BLOCK_SIZE], + aux_cols: &mut MemoryWriteAuxCols, +) where + F: PrimeField32, +{ + let (t_prev, data_prev) = timed_write(memory, ptr, vals); + aux_cols.set_prev(F::from_canonical_u32(t_prev), data_prev); +} + +/// Reads value at `_ptr` from memory and records the memory access in mutable buffer. +/// Trace generation relevant to this memory access can be done fully from the recorded buffer. +#[inline(always)] +pub fn tracing_read_or_imm_native( + memory: &mut TracingMemory, + addr_space: u32, + ptr_or_imm: F, + addr_space_mut: &mut F, + aux_cols: &mut MemoryReadOrImmediateAuxCols, +) -> F +where + F: PrimeField32, +{ + debug_assert!(addr_space == AS_IMMEDIATE || addr_space == AS_NATIVE); + + if addr_space == AS_IMMEDIATE { + *addr_space_mut = F::ZERO; + memory.increment_timestamp(); + ptr_or_imm + } else { + *addr_space_mut = F::from_canonical_u32(AS_NATIVE); + let data: [F; 1] = + tracing_read_native(memory, ptr_or_imm.as_canonical_u32(), &mut aux_cols.base); + data[0] + } +} diff --git a/crates/vm/src/system/public_values/core.rs b/crates/vm/src/system/public_values/core.rs index 770475ccee..0e54624c32 100644 --- a/crates/vm/src/system/public_values/core.rs +++ b/crates/vm/src/system/public_values/core.rs @@ -1,4 +1,4 @@ -use std::{borrow::BorrowMut, sync::Mutex}; +use std::sync::Mutex; use openvm_circuit_primitives::{encoder::Encoder, SubAir}; use openvm_instructions::{ @@ -17,10 +17,9 @@ use serde::{Deserialize, Serialize}; use crate::{ arch::{ - execution_mode::{e1::E1Ctx, metered::MeteredCtx, E1E2ExecutionCtx}, - AdapterAirContext, AdapterExecutorE1, AdapterRuntimeContext, AdapterTraceStep, - BasicAdapterInterface, MinimalInstruction, Result, StepExecutorE1, TraceStep, - VmAdapterInterface, VmCoreAir, VmCoreChip, VmStateMut, + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, + AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, BasicAdapterInterface, + MinimalInstruction, Result, StepExecutorE1, TraceStep, VmCoreAir, VmStateMut, }, system::{ memory::{ @@ -31,7 +30,6 @@ use crate::{ }, }; pub(crate) type AdapterInterface = BasicAdapterInterface, 2, 0, 1, 1>; -pub(crate) type AdapterInterfaceReads = as VmAdapterInterface>::Reads; #[derive(Clone, Debug)] pub struct PublicValuesCoreAir { @@ -120,6 +118,8 @@ pub struct PublicValuesRecord { /// the proof but in the perspective of constraints, it could be any value. pub struct PublicValuesCoreStep { adapter: A, + // TODO(ayush): put air here and take from air + encoder: Encoder, // Mutex is to make the struct Sync. But it actually won't be accessed by multiple threads. pub(crate) custom_pvs: Mutex>>, } @@ -131,9 +131,10 @@ where /// **Note:** `max_degree` is the maximum degree of the constraint polynomials to represent the /// flags. If you want the overall AIR's constraint degree to be `<= max_constraint_degree`, /// then typically you should set `max_degree` to `max_constraint_degree - 1`. - pub fn new(adapter: A, num_custom_pvs: usize) -> Self { + pub fn new(adapter: A, num_custom_pvs: usize, max_degree: u32) -> Self { Self { adapter, + encoder: Encoder::new(num_custom_pvs, max_degree, true), custom_pvs: Mutex::new(vec![None; num_custom_pvs]), } } @@ -169,32 +170,51 @@ where trace_offset: &mut usize, width: usize, ) -> Result<()> { - todo!("Implement execute function"); - } + let row_slice = &mut trace[*trace_offset..*trace_offset + width]; + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { - todo!("Implement fill_trace_row function"); + A::start(*state.pc, state.memory, adapter_row); - // let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; + let [[value], [index]] = self.adapter.read(state.memory, instruction, adapter_row); + { + let idx: usize = index.as_canonical_u32() as usize; + let mut custom_pvs = self.custom_pvs.lock().unwrap(); - // self.adapter.fill_trace_row(mem_helper, (), adapter_row); + if custom_pvs[idx].is_none() { + custom_pvs[idx] = Some(value); + } else { + // Not a hard constraint violation when publishing the same value twice but the + // program should avoid that. + panic!("Custom public value {} already set", idx); + } + } - // let core_row: &mut PublicValuesCoreColsView<_, F> = core_row.borrow_mut(); + let cols = PublicValuesCoreColsView::<_, &mut F>::borrow_mut(core_row); + debug_assert_eq!(cols.width(), width - A::WIDTH); - // // TODO(ayush): add this check - // // debug_assert_eq!(core_row.width(), BaseAir::::width(&self.air)); + *cols.value = value; + *cols.index = index; - // core_row.is_valid = F::ONE; - // core_row.value = record.value; - // core_row.index = record.index; + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + *trace_offset += width; - // let idx: usize = record.index.as_canonical_u32() as usize; + Ok(()) + } - // let pt = self.air.encoder.get_flag_pt(idx); + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let (adapter_row, core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) }; - // for (i, var) in core_row.custom_pv_vars.iter_mut().enumerate() { - // *var = F::from_canonical_u32(pt[i]); - // } + self.adapter.fill_trace_row(mem_helper, (), adapter_row); + + let cols = PublicValuesCoreColsView::<_, &mut F>::borrow_mut(core_row); + + *cols.is_valid = F::ONE; + + let idx: usize = cols.index.as_canonical_u32() as usize; + let pt = self.encoder.get_flag_pt(idx); + for (i, var) in cols.custom_pv_vars.into_iter().enumerate() { + *var = F::from_canonical_u32(pt[i]); + } } fn generate_public_values(&self) -> Vec { @@ -242,97 +262,11 @@ where &mut self, state: &mut VmStateMut, instruction: &Instruction, - _chip_index: usize, + chip_index: usize, ) -> Result<()> { self.execute_e1(state, instruction)?; + state.ctx.trace_heights[chip_index] += 1; Ok(()) } } - -// /// ATTENTION: If a specific public value is not provided, a default 0 will be used when -// generating /// the proof but in the perspective of constraints, it could be any value. -// pub struct PublicValuesCoreChip { -// air: PublicValuesCoreAir, -// // Mutex is to make the struct Sync. But it actually won't be accessed by multiple threads. -// pub(crate) custom_pvs: Mutex>>, -// } - -// impl PublicValuesCoreChip { -// /// **Note:** `max_degree` is the maximum degree of the constraint polynomials to represent -// the /// flags. If you want the overall AIR's constraint degree to be `<= -// max_constraint_degree`, /// then typically you should set `max_degree` to -// `max_constraint_degree - 1`. pub fn new(num_custom_pvs: usize, max_degree: u32) -> Self { -// Self { -// air: PublicValuesCoreAir::new(num_custom_pvs, max_degree), -// custom_pvs: Mutex::new(vec![None; num_custom_pvs]), -// } -// } -// pub fn get_custom_public_values(&self) -> Vec> { -// self.custom_pvs.lock().unwrap().clone() -// } -// } - -// impl VmCoreChip> for PublicValuesCoreChip { -// type Record = PublicValuesRecord; -// type Air = PublicValuesCoreAir; - -// #[allow(clippy::type_complexity)] -// fn execute_instruction( -// &self, -// _instruction: &Instruction, -// _from_pc: u32, -// reads: AdapterInterfaceReads, -// ) -> Result<(AdapterRuntimeContext>, Self::Record)> { -// let [[value], [index]] = reads; -// { -// let idx: usize = index.as_canonical_u32() as usize; -// let mut custom_pvs = self.custom_pvs.lock().unwrap(); - -// if custom_pvs[idx].is_none() { -// custom_pvs[idx] = Some(value); -// } else { -// // Not a hard constraint violation when publishing the same value twice but the -// // program should avoid that. -// panic!("Custom public value {} already set", idx); -// } -// } -// let output = AdapterRuntimeContext { -// to_pc: None, -// writes: [], -// }; -// let record = Self::Record { value, index }; -// Ok((output, record)) -// } - -// fn get_opcode_name(&self, opcode: usize) -> String { -// format!( -// "{:?}", -// PublishOpcode::from_usize(opcode - PublishOpcode::CLASS_OFFSET) -// ) -// } - -// fn generate_trace_row(&self, row_slice: &mut [F], record: Self::Record) { -// let mut cols = PublicValuesCoreColsView::<_, &mut F>::borrow_mut(row_slice); -// debug_assert_eq!(cols.width(), BaseAir::::width(&self.air)); -// *cols.is_valid = F::ONE; -// *cols.value = record.value; -// *cols.index = record.index; -// let idx: usize = record.index.as_canonical_u32() as usize; -// let pt = self.air.encoder.get_flag_pt(idx); -// for (i, var) in cols.custom_pv_vars.iter_mut().enumerate() { -// **var = F::from_canonical_u32(pt[i]); -// } -// } - -// fn generate_public_values(&self) -> Vec { -// self.get_custom_public_values() -// .into_iter() -// .map(|x| x.unwrap_or(F::ZERO)) -// .collect() -// } - -// fn air(&self) -> &Self::Air { -// &self.air -// } -// } diff --git a/crates/vm/tests/integration_test.rs b/crates/vm/tests/integration_test.rs index 76058d21b0..20d8f3f8e2 100644 --- a/crates/vm/tests/integration_test.rs +++ b/crates/vm/tests/integration_test.rs @@ -7,10 +7,12 @@ use std::{ use openvm_circuit::{ arch::{ + create_and_initialize_chip_complex, + execution_mode::tracegen::TracegenExecutionControlWithSegmentation, hasher::{poseidon2::vm_poseidon2_hasher, Hasher}, ChipId, MemoryConfig, SingleSegmentVmExecutor, SystemConfig, SystemTraceHeights, - TracegenVmExecutionState, TracegenVmSegmentExecutor, VirtualMachine, VmComplexTraceHeights, - VmConfig, VmInventoryTraceHeights, + VirtualMachine, VmComplexTraceHeights, VmConfig, VmInventoryTraceHeights, + VmSegmentExecutor, VmSegmentState, }, system::{ memory::{MemoryTraceHeights, VolatileMemoryTraceHeights, CHUNK}, @@ -713,19 +715,24 @@ fn test_hint_load_1() { let program = Program::from_instructions(&instructions); - let mut segment = TracegenVmSegmentExecutor::new( + let chip_complex = create_and_initialize_chip_complex( &test_native_config(), program, vec![vec![F::ONE, F::TWO]].into(), None, + ) + .unwrap(); + let ctrl = TracegenExecutionControlWithSegmentation::new(chip_complex.air_names()); + let mut segment = VmSegmentExecutor::::new( + chip_complex, vec![], Default::default(), + ctrl, ); - let mut vm_state = TracegenVmExecutionState::from_pc_and_memory_controller( - 0, - segment.chip_complex.memory_controller(), - ); - segment.execute_from_state(&mut vm_state).unwrap(); + + let mut exec_state = VmSegmentState::new(0, 0, None, ()); + segment.execute_from_state(&mut exec_state).unwrap(); + let streams = segment.chip_complex.take_streams(); assert!(streams.input_stream.is_empty()); assert_eq!(streams.hint_stream, VecDeque::from(vec![F::ZERO])); @@ -754,26 +761,33 @@ fn test_hint_load_2() { let program = Program::from_instructions(&instructions); - let mut segment = TracegenVmSegmentExecutor::new( + let chip_complex = create_and_initialize_chip_complex( &test_native_config(), program, vec![vec![F::ONE, F::TWO], vec![F::TWO, F::ONE]].into(), None, + ) + .unwrap(); + let ctrl = TracegenExecutionControlWithSegmentation::new(chip_complex.air_names()); + let mut segment = VmSegmentExecutor::::new( + chip_complex, vec![], Default::default(), + ctrl, ); - let mut vm_state = TracegenVmExecutionState::from_pc_and_memory_controller( - 0, - segment.chip_complex.memory_controller(), - ); - segment.execute_from_state(&mut vm_state).unwrap(); - assert_eq!( + + let mut exec_state = VmSegmentState::new(0, 0, None, ()); + segment.execute_from_state(&mut exec_state).unwrap(); + + let [read] = unsafe { segment .chip_complex .memory_controller() - .unsafe_read_cell::(F::from_canonical_usize(4), F::from_canonical_usize(32)), - F::ZERO - ); + .memory + .data + .read::(4, 32) + }; + assert_eq!(read, F::ZERO); let streams = segment.chip_complex.take_streams(); assert!(streams.input_stream.is_empty()); assert_eq!(streams.hint_stream, VecDeque::from(vec![F::ONE])); diff --git a/extensions/native/circuit/src/adapters/convert_adapter.rs b/extensions/native/circuit/src/adapters/convert_adapter.rs index 74550df1b8..fe1ee70778 100644 --- a/extensions/native/circuit/src/adapters/convert_adapter.rs +++ b/extensions/native/circuit/src/adapters/convert_adapter.rs @@ -20,14 +20,14 @@ use openvm_instructions::{ instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_MEMORY_AS, }; use openvm_native_compiler::conversion::AS; -use openvm_rv32im_circuit::adapters::{memory_write, memory_write_from_state, tracing_write}; +use openvm_rv32im_circuit::adapters::{memory_write_from_state, tracing_write}; use openvm_stark_backend::{ interaction::InteractionBuilder, p3_air::BaseAir, p3_field::{Field, FieldAlgebra, PrimeField32}, }; -use crate::adapters::{memory_read_native, memory_read_native_from_state, tracing_read_native}; +use crate::adapters::{memory_read_native_from_state, tracing_read_native}; #[repr(C)] #[derive(AlignedBorrow)] diff --git a/extensions/native/circuit/src/fri/mod.rs b/extensions/native/circuit/src/fri/mod.rs index 2886589827..b970cfa557 100644 --- a/extensions/native/circuit/src/fri/mod.rs +++ b/extensions/native/circuit/src/fri/mod.rs @@ -878,16 +878,7 @@ where instruction: &Instruction, chip_index: usize, ) -> Result<()> { - let &Instruction { - a, - b, - c, - d, - e, - f, - g, - .. - } = instruction; + let &Instruction { c, .. } = instruction; let length_ptr = c.as_canonical_u32(); let [length]: [F; 1] = memory_read_native(state.memory, length_ptr); diff --git a/extensions/rv32im/circuit/src/hintstore/mod.rs b/extensions/rv32im/circuit/src/hintstore/mod.rs index 460c259787..6917862b62 100644 --- a/extensions/rv32im/circuit/src/hintstore/mod.rs +++ b/extensions/rv32im/circuit/src/hintstore/mod.rs @@ -40,8 +40,8 @@ use openvm_stark_backend::{ use serde::{Deserialize, Serialize}; use crate::adapters::{ - decompose, memory_read, memory_read_from_state, memory_write, memory_write_from_state, - tracing_read, tracing_write, + decompose, memory_read, memory_read_from_state, memory_write_from_state, tracing_read, + tracing_write, }; #[cfg(test)] @@ -524,15 +524,9 @@ where let &Instruction { opcode, a: num_words_ptr, - b: mem_ptr_ptr, - d, - e, .. } = instruction; - debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS); - debug_assert_eq!(e.as_canonical_u32(), RV32_MEMORY_AS); - let local_opcode = Rv32HintStoreOpcode::from_usize(opcode.local_opcode_idx(self.offset)); let num_words = if local_opcode == HINT_STOREW { From fe29323a836251f214d9871a485de86f0197a1eb Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Thu, 22 May 2025 16:47:43 -0400 Subject: [PATCH 35/49] chore: bump stark backend (#1682) Bumped to v1.0.1 (same as main) --- Cargo.lock | 19 +++++++++++++++++-- Cargo.toml | 4 ++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e6472bfbc4..ccce87fa32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5587,7 +5587,7 @@ dependencies = [ [[package]] name = "openvm-stark-backend" version = "1.0.0" -source = "git+https://github.com/openvm-org/stark-backend.git?tag=v1.0.0#884f8e6aabf72bde00dc51f1f1121277bff73b1e" +source = "git+https://github.com/openvm-org/stark-backend.git?tag=v1.0.1#e540dbdd09ef20db7207ad7f2674bece75a2b803" dependencies = [ "bitcode", "cfg-if", @@ -5615,7 +5615,7 @@ dependencies = [ [[package]] name = "openvm-stark-sdk" version = "1.0.0" -source = "git+https://github.com/openvm-org/stark-backend.git?tag=v1.0.0#884f8e6aabf72bde00dc51f1f1121277bff73b1e" +source = "git+https://github.com/openvm-org/stark-backend.git?tag=v1.0.1#e540dbdd09ef20db7207ad7f2674bece75a2b803" dependencies = [ "derivative", "derive_more 0.99.20", @@ -5632,6 +5632,7 @@ dependencies = [ "p3-fri", "p3-goldilocks", "p3-keccak", + "p3-koala-bear", "p3-merkle-tree", "p3-poseidon", "p3-poseidon2", @@ -5913,6 +5914,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "p3-koala-bear" +version = "0.1.0" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=1ba4e5c#1ba4e5c40417f4f7aae86bcca56b6484b4b2490b" +dependencies = [ + "p3-field", + "p3-mds", + "p3-monty-31", + "p3-poseidon2", + "p3-symmetric", + "rand 0.8.5", + "serde", +] + [[package]] name = "p3-matrix" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 3f79027b71..87eafe6f98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,8 +107,8 @@ lto = "thin" [workspace.dependencies] # Stark Backend -openvm-stark-backend = { git = "https://github.com/openvm-org/stark-backend.git", tag = "v1.0.0", default-features = false } -openvm-stark-sdk = { git = "https://github.com/openvm-org/stark-backend.git", tag = "v1.0.0", default-features = false } +openvm-stark-backend = { git = "https://github.com/openvm-org/stark-backend.git", tag = "v1.0.1", default-features = false } +openvm-stark-sdk = { git = "https://github.com/openvm-org/stark-backend.git", tag = "v1.0.1", default-features = false } # OpenVM openvm-sdk = { path = "crates/sdk", default-features = false } From 61913deb9813a230c8eca71a5a4d1636d9fc6111 Mon Sep 17 00:00:00 2001 From: Lun-Kai Hsu Date: Thu, 22 May 2025 15:25:24 -0700 Subject: [PATCH 36/49] fix: add InsExecutorE1 to sdk vc executor (#1684) --- crates/sdk/src/config/global.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/sdk/src/config/global.rs b/crates/sdk/src/config/global.rs index 532c9b8d1c..02b2fbb8b4 100644 --- a/crates/sdk/src/config/global.rs +++ b/crates/sdk/src/config/global.rs @@ -12,7 +12,7 @@ use openvm_circuit::{ SystemConfig, SystemExecutor, SystemPeriphery, VmChipComplex, VmConfig, VmInventoryError, }, circuit_derive::{Chip, ChipUsageGetter}, - derive::{AnyEnum, InstructionExecutor}, + derive::{AnyEnum, InsExecutorE1, InstructionExecutor}, }; use openvm_ecc_circuit::{ WeierstrassExtension, WeierstrassExtensionExecutor, WeierstrassExtensionPeriphery, @@ -63,7 +63,7 @@ pub struct SdkVmConfig { pub ecc: Option, } -#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)] +#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum, InsExecutorE1)] pub enum SdkVmConfigExecutor { #[any_enum] System(SystemExecutor), From f188a938f16c82af076961aafea633e9a1cf3845 Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Thu, 22 May 2025 20:11:01 -0400 Subject: [PATCH 37/49] fix(new-execution): return final memory (#1685) --- crates/vm/src/arch/vm.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/vm/src/arch/vm.rs b/crates/vm/src/arch/vm.rs index a9aa38ccd1..bcdf433acd 100644 --- a/crates/vm/src/arch/vm.rs +++ b/crates/vm/src/arch/vm.rs @@ -569,12 +569,14 @@ where .pc ); + // TODO(ayush): avoid cloning + let final_memory = segment.ctrl.final_memory.clone(); let proof_input = tracing::info_span!("generate_proof_input") .in_scope(|| segment.generate_proof_input(None))?; Ok(VmExecutorResult { per_segment: vec![proof_input], - final_memory: None, + final_memory, }) } From 46e36b0c517fd0c206e1463a5ac32e37c508686f Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Fri, 23 May 2025 11:26:38 -0400 Subject: [PATCH 38/49] fix(new-execution): add missing pc increment (#1687) - added a missing pc increment step in `ModularIsEqualStep` --- crates/vm/src/arch/execution_mode/metered/mod.rs | 3 +-- extensions/algebra/circuit/src/modular_chip/is_eq.rs | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/vm/src/arch/execution_mode/metered/mod.rs b/crates/vm/src/arch/execution_mode/metered/mod.rs index ef6bd49397..3a62973b4d 100644 --- a/crates/vm/src/arch/execution_mode/metered/mod.rs +++ b/crates/vm/src/arch/execution_mode/metered/mod.rs @@ -264,8 +264,6 @@ where // Check if segmentation needs to happen self.check_segment_limits::(state, chip_complex); - let &Instruction { opcode, .. } = instruction; - let mut offset = if chip_complex.config().has_public_values_chip() { PUBLIC_VALUES_AIR_ID + 1 } else { @@ -273,6 +271,7 @@ where }; offset += chip_complex.memory_controller().num_airs(); + let &Instruction { opcode, .. } = instruction; if let Some((executor, i)) = chip_complex.inventory.get_mut_executor_with_index(&opcode) { let mut vm_state = VmStateMut { pc: &mut state.pc, diff --git a/extensions/algebra/circuit/src/modular_chip/is_eq.rs b/extensions/algebra/circuit/src/modular_chip/is_eq.rs index e9ef70d3f1..b0dad5d7f5 100644 --- a/extensions/algebra/circuit/src/modular_chip/is_eq.rs +++ b/extensions/algebra/circuit/src/modular_chip/is_eq.rs @@ -456,6 +456,8 @@ where self.adapter.write(state, instruction, &write_data.into()); + *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); + Ok(()) } From cb2a5335180a815a2242a92b84e6a47b55bb46bd Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Fri, 23 May 2025 14:13:33 -0400 Subject: [PATCH 39/49] fix(new-execution): properly calculate total max cells (#1688) - total cells = max_cells_per_chip * total_chips --- crates/vm/src/arch/execution_mode/metered/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/vm/src/arch/execution_mode/metered/mod.rs b/crates/vm/src/arch/execution_mode/metered/mod.rs index 3a62973b4d..389fb2c1ed 100644 --- a/crates/vm/src/arch/execution_mode/metered/mod.rs +++ b/crates/vm/src/arch/execution_mode/metered/mod.rs @@ -18,7 +18,7 @@ const SEGMENT_CHECK_INTERVAL: u64 = 100; // TODO(ayush): fix these values const MAX_TRACE_HEIGHT: u32 = DEFAULT_MAX_SEGMENT_LEN as u32; -const MAX_TRACE_CELLS: usize = DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT; +const MAX_TRACE_CELLS_PER_CHIP: usize = DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT; const MAX_INTERACTIONS: usize = BabyBear::ORDER_U32 as usize; #[derive(derive_new::new, Debug)] @@ -71,6 +71,7 @@ impl<'a> MeteredExecutionControl<'a> { fn should_segment(&mut self, state: &mut VmSegmentState) -> bool { let trace_heights = state.ctx.trace_heights_if_finalized(); + let max_trace_cells = MAX_TRACE_CELLS_PER_CHIP * trace_heights.len(); for (i, &height) in trace_heights.iter().enumerate() { let padded_height = height.next_power_of_two(); if padded_height > MAX_TRACE_HEIGHT { @@ -88,13 +89,13 @@ impl<'a> MeteredExecutionControl<'a> { } let total_cells = self.calculate_total_cells(&trace_heights); - if total_cells > MAX_TRACE_CELLS { + if total_cells > max_trace_cells { tracing::info!( "Segment {:2} | clk {:9} | total cells ({:10}) > max ({:10})", self.segments.len(), self.clk_last_segment_check, total_cells, - MAX_TRACE_CELLS + max_trace_cells ); return true; } From cc62f8646cece89456d6001041c92c931057705e Mon Sep 17 00:00:00 2001 From: Xinding Wei Date: Fri, 23 May 2025 15:49:30 -0700 Subject: [PATCH 40/49] feat: Poseidon2 chip (#1672) Rewrite native Poseidon2 chip for execution/tacegen. --------- Co-authored-by: Alexander Golovanov --- benchmarks/execute/benches/execute.rs | 24 +- benchmarks/execute/src/main.rs | 3 +- .../execution_mode/tracegen/segmentation.rs | 3 +- crates/vm/src/arch/segment.rs | 1 - crates/vm/src/system/memory/online.rs | 10 +- extensions/native/circuit/src/extension.rs | 136 +- extensions/native/circuit/src/lib.rs | 2 +- .../native/circuit/src/poseidon2/air.rs | 12 +- .../native/circuit/src/poseidon2/chip.rs | 1132 ++++++++++++----- .../native/circuit/src/poseidon2/mod.rs | 43 +- .../native/circuit/src/poseidon2/tests.rs | 27 +- .../native/circuit/src/poseidon2/trace.rs | 485 ------- 12 files changed, 954 insertions(+), 924 deletions(-) delete mode 100644 extensions/native/circuit/src/poseidon2/trace.rs diff --git a/benchmarks/execute/benches/execute.rs b/benchmarks/execute/benches/execute.rs index aab46d1ca2..995c2bbbd8 100644 --- a/benchmarks/execute/benches/execute.rs +++ b/benchmarks/execute/benches/execute.rs @@ -1,31 +1,29 @@ use eyre::Result; use openvm_benchmarks_utils::{get_elf_path, get_programs_dir, read_elf_file}; -use openvm_bigint_circuit::Int256; +use openvm_bigint_circuit::{Int256, Int256Executor, Int256Periphery}; use openvm_bigint_transpiler::Int256TranspilerExtension; use openvm_circuit::{ arch::{instructions::exe::VmExe, SystemConfig, VmExecutor}, derive::VmConfig, }; -use openvm_keccak256_circuit::Keccak256; +use openvm_keccak256_circuit::{Keccak256, Keccak256Executor, Keccak256Periphery}; use openvm_keccak256_transpiler::Keccak256TranspilerExtension; -use openvm_rv32im_circuit::{Rv32I, Rv32Io, Rv32M}; +use openvm_rv32im_circuit::{ + Rv32I, Rv32IExecutor, Rv32IPeriphery, Rv32Io, Rv32IoExecutor, Rv32IoPeriphery, Rv32M, + Rv32MExecutor, Rv32MPeriphery, +}; use openvm_rv32im_transpiler::{ Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, }; -use openvm_sha256_circuit::Sha256; +use openvm_sha256_circuit::{Sha256, Sha256Executor, Sha256Periphery}; use openvm_sha256_transpiler::Sha256TranspilerExtension; -use openvm_stark_sdk::p3_baby_bear::BabyBear; +use openvm_stark_sdk::{ + openvm_stark_backend::{self, p3_field::PrimeField32}, + p3_baby_bear::BabyBear, +}; use openvm_transpiler::{transpiler::Transpiler, FromElf}; use serde::{Deserialize, Serialize}; -use openvm_bigint_circuit::{Int256Executor, Int256Periphery}; -use openvm_keccak256_circuit::{Keccak256Executor, Keccak256Periphery}; -use openvm_rv32im_circuit::{ - Rv32IExecutor, Rv32IPeriphery, Rv32IoExecutor, Rv32IoPeriphery, Rv32MExecutor, Rv32MPeriphery, -}; -use openvm_sha256_circuit::{Sha256Executor, Sha256Periphery}; -use openvm_stark_sdk::openvm_stark_backend::{self, p3_field::PrimeField32}; - static AVAILABLE_PROGRAMS: &[&str] = &[ "fibonacci_recursive", "fibonacci_iterative", diff --git a/benchmarks/execute/src/main.rs b/benchmarks/execute/src/main.rs index 1c17e87a67..bbeb4ddba5 100644 --- a/benchmarks/execute/src/main.rs +++ b/benchmarks/execute/src/main.rs @@ -207,7 +207,8 @@ fn main() -> Result<()> { // let state = executor.execute_e1(exe.clone(), vec![], Some(clk_start))?; // assert!(state.clk == clk_start); // // E3/tracegen from clk_start for num_cycles beginning with state - // let mut result = executor.execute_and_generate_segment::( + // let mut result = + // executor.execute_and_generate_segment::( // exe.clone(), // state, // num_cycles, diff --git a/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs b/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs index 431a8a7bf4..f193dc2059 100644 --- a/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs +++ b/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs @@ -1,6 +1,7 @@ use openvm_instructions::instruction::Instruction; use openvm_stark_backend::p3_field::PrimeField32; +use super::TracegenCtx; use crate::{ arch::{ execution_control::ExecutionControl, ExecutionError, ExecutionState, InstructionExecutor, @@ -9,8 +10,6 @@ use crate::{ system::memory::{MemoryImage, INITIAL_TIMESTAMP}, }; -use super::TracegenCtx; - /// Check segment every 100 instructions. const SEGMENT_CHECK_INTERVAL: usize = 100; diff --git a/crates/vm/src/arch/segment.rs b/crates/vm/src/arch/segment.rs index 61456b5b25..af57ad7fc9 100644 --- a/crates/vm/src/arch/segment.rs +++ b/crates/vm/src/arch/segment.rs @@ -126,7 +126,6 @@ where // Fetch, decode and execute single instruction self.execute_instruction(state, &mut prev_backtrace)?; } - Ok(()) } diff --git a/crates/vm/src/system/memory/online.rs b/crates/vm/src/system/memory/online.rs index 38d78c5f2c..b1e8a3b642 100644 --- a/crates/vm/src/system/memory/online.rs +++ b/crates/vm/src/system/memory/online.rs @@ -386,14 +386,12 @@ impl TracingMemory { /// actions. In the end of this process, we have this segment intact in our `meta`. /// /// Caller must ensure alignment (e.g. via `assert_alignment`) prior to calling this function. - fn prev_access_time( + fn prev_access_time( &mut self, address_space: usize, pointer: usize, align: usize, ) -> u32 { - let size = size_of::(); - let seg_size = align * size; let num_segs = BLOCK_SIZE / align; let begin = pointer / align; @@ -448,7 +446,7 @@ impl TracingMemory { .fill(current_metadata.timestamp); if current_metadata.block_size > align as u32 { // Split - let address = MemoryAddress::new(address_space as u32, (cur_ptr * seg_size) as u32); + let address = MemoryAddress::new(address_space as u32, (cur_ptr * align) as u32); let values = (0..current_metadata.block_size as usize) .map(|i| { self.data @@ -511,7 +509,7 @@ impl TracingMemory { { self.assert_alignment(BLOCK_SIZE, ALIGN, address_space, pointer); let t_prev = - self.prev_access_time::(address_space as usize, pointer as usize, ALIGN); + self.prev_access_time::(address_space as usize, pointer as usize, ALIGN); let t_curr = self.timestamp; self.timestamp += 1; let values = self.data.read(address_space, pointer); @@ -559,7 +557,7 @@ impl TracingMemory { { self.assert_alignment(BLOCK_SIZE, ALIGN, address_space, pointer); let t_prev = - self.prev_access_time::(address_space as usize, pointer as usize, ALIGN); + self.prev_access_time::(address_space as usize, pointer as usize, ALIGN); let values_prev = self.data.replace(address_space, pointer, values); let t_curr = self.timestamp; self.timestamp += 1; diff --git a/extensions/native/circuit/src/extension.rs b/extensions/native/circuit/src/extension.rs index a6a103a18a..e76fb4fa49 100644 --- a/extensions/native/circuit/src/extension.rs +++ b/extensions/native/circuit/src/extension.rs @@ -30,7 +30,12 @@ use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; -use crate::{adapters::*, phantom::*, *}; +use crate::{ + adapters::*, + phantom::*, + poseidon2::{air::VerifyBatchBus, new_native_poseidon2_chip, NativePoseidon2Chip}, + *, +}; // TODO(ayush): this should be decided after e2 execution const MAX_INS_CAPACITY: usize = 1 << 22; @@ -72,7 +77,7 @@ pub enum NativeExecutor { FieldArithmetic(FieldArithmeticChip), FieldExtension(FieldExtensionChip), FriReducedOpening(FriReducedOpeningChip), - // VerifyBatch(NativePoseidon2Chip), + VerifyBatch(NativePoseidon2Chip), } #[derive(From, ChipUsageGetter, Chip, AnyEnum)] @@ -229,20 +234,23 @@ impl VmExtension for Native { FriOpcode::iter().map(|x| x.global_opcode()), )?; - // let poseidon2_chip = NativePoseidon2Chip::new( - // builder.system_port(), - // Poseidon2Config::default(), - // VerifyBatchBus::new(builder.new_bus_idx()), - // builder.streams().clone(), - // ); - // inventory.add_executor( - // poseidon2_chip, - // [ - // VerifyBatchOpcode::VERIFY_BATCH.global_opcode(), - // Poseidon2Opcode::PERM_POS2.global_opcode(), - // Poseidon2Opcode::COMP_POS2.global_opcode(), - // ], - // )?; + let poseidon2_chip = new_native_poseidon2_chip( + builder.system_port(), + Poseidon2Config::default(), + VerifyBatchBus::new(builder.new_bus_idx()), + builder.streams().clone(), + // TODO: this may use too much memory. + MAX_INS_CAPACITY, + builder.system_base().memory_controller.helper(), + ); + inventory.add_executor( + poseidon2_chip, + [ + VerifyBatchOpcode::VERIFY_BATCH.global_opcode(), + Poseidon2Opcode::PERM_POS2.global_opcode(), + Poseidon2Opcode::COMP_POS2.global_opcode(), + ], + )?; builder.add_phantom_sub_executor( NativeHintInputSubEx, @@ -254,15 +262,15 @@ impl VmExtension for Native { PhantomDiscriminant(NativePhantom::HintFelt as u16), )?; - // builder.add_phantom_sub_executor( - // NativeHintBitsSubEx, - // PhantomDiscriminant(NativePhantom::HintBits as u16), - // )?; + builder.add_phantom_sub_executor( + NativeHintBitsSubEx, + PhantomDiscriminant(NativePhantom::HintBits as u16), + )?; - // builder.add_phantom_sub_executor( - // NativePrintSubEx, - // PhantomDiscriminant(NativePhantom::Print as u16), - // )?; + builder.add_phantom_sub_executor( + NativePrintSubEx, + PhantomDiscriminant(NativePhantom::Print as u16), + )?; builder.add_phantom_sub_executor( NativeHintLoadSubEx, @@ -336,47 +344,45 @@ pub(crate) mod phantom { } } - // impl PhantomSubExecutor for NativePrintSubEx { - // fn phantom_execute( - // &mut self, - // memory: &GuestMemory, - // _: &mut Streams, - // _: PhantomDiscriminant, - // a: u32, - // _: u32, - // c_upper: u16, - // ) -> eyre::Result<()> { - // let addr_space = F::from_canonical_u16(c_upper); - // let value = memory.unsafe_read_cell::(addr_space, a); - // println!("{}", value); - // Ok(()) - // } - // } - - // impl PhantomSubExecutor for NativeHintBitsSubEx { - // fn phantom_execute( - // &mut self, - // memory: &GuestMemory, - // streams: &mut Streams, - // _: PhantomDiscriminant, - // a: u32, - // len: u32, - // c_upper: u16, - // ) -> eyre::Result<()> { - // let addr_space = F::from_canonical_u16(c_upper); - // let val = memory.unsafe_read_cell::(addr_space, a); - // let mut val = val.as_canonical_u32(); - - // assert!(streams.hint_stream.is_empty()); - // for _ in 0..len { - // streams - // .hint_stream - // .push_back(F::from_canonical_u32(val & 1)); - // val >>= 1; - // } - // Ok(()) - // } - // } + impl PhantomSubExecutor for NativePrintSubEx { + fn phantom_execute( + &mut self, + memory: &GuestMemory, + _: &mut Streams, + _: PhantomDiscriminant, + a: u32, + _: u32, + c_upper: u16, + ) -> eyre::Result<()> { + let [value] = unsafe { memory.read::(c_upper as u32, a) }; + println!("{}", value); + Ok(()) + } + } + + impl PhantomSubExecutor for NativeHintBitsSubEx { + fn phantom_execute( + &mut self, + memory: &GuestMemory, + streams: &mut Streams, + _: PhantomDiscriminant, + a: u32, + len: u32, + c_upper: u16, + ) -> eyre::Result<()> { + let [val] = unsafe { memory.read::(c_upper as u32, a) }; + let mut val = val.as_canonical_u32(); + + assert!(streams.hint_stream.is_empty()); + for _ in 0..len { + streams + .hint_stream + .push_back(F::from_canonical_u32(val & 1)); + val >>= 1; + } + Ok(()) + } + } impl PhantomSubExecutor for NativeHintLoadSubEx { fn phantom_execute( diff --git a/extensions/native/circuit/src/lib.rs b/extensions/native/circuit/src/lib.rs index 069c19dcf1..4e16d00233 100644 --- a/extensions/native/circuit/src/lib.rs +++ b/extensions/native/circuit/src/lib.rs @@ -7,7 +7,7 @@ mod field_extension; mod fri; mod jal; mod loadstore; -// mod poseidon2; +mod poseidon2; pub use branch_eq::*; pub use castf::*; diff --git a/extensions/native/circuit/src/poseidon2/air.rs b/extensions/native/circuit/src/poseidon2/air.rs index 5ed28abd60..9d24966fe7 100644 --- a/extensions/native/circuit/src/poseidon2/air.rs +++ b/extensions/native/circuit/src/poseidon2/air.rs @@ -20,15 +20,13 @@ use openvm_stark_backend::{ rap::{BaseAirWithPublicValues, PartitionedBaseAir}, }; -use crate::{ +use crate::poseidon2::{ chip::{NUM_INITIAL_READS, NUM_SIMPLE_ACCESSES}, - poseidon2::{ - columns::{ - InsideRowSpecificCols, NativePoseidon2Cols, SimplePoseidonSpecificCols, - TopLevelSpecificCols, - }, - CHUNK, + columns::{ + InsideRowSpecificCols, NativePoseidon2Cols, SimplePoseidonSpecificCols, + TopLevelSpecificCols, }, + CHUNK, }; #[derive(Clone, Debug)] diff --git a/extensions/native/circuit/src/poseidon2/chip.rs b/extensions/native/circuit/src/poseidon2/chip.rs index a25b553c0a..627a5b6c67 100644 --- a/extensions/native/circuit/src/poseidon2/chip.rs +++ b/extensions/native/circuit/src/poseidon2/chip.rs @@ -1,10 +1,17 @@ -use std::sync::{Arc, Mutex}; +use std::{ + borrow::{Borrow, BorrowMut}, + sync::{Arc, Mutex}, +}; use openvm_circuit::{ arch::{ - ExecutionBridge, ExecutionError, ExecutionState, InstructionExecutor, Streams, SystemPort, + execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, + StepExecutorE1, Streams, TraceStep, VmStateMut, + }, + system::memory::{ + online::{GuestMemory, TracingMemory}, + MemoryAuxColsFactory, }, - system::memory::{MemoryController, OfflineMemory, RecordId}, }; use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode}; use openvm_native_compiler::{ @@ -12,151 +19,40 @@ use openvm_native_compiler::{ Poseidon2Opcode::{COMP_POS2, PERM_POS2}, VerifyBatchOpcode::VERIFY_BATCH, }; -use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubAir, Poseidon2SubChip}; +use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubChip}; use openvm_stark_backend::{ + p3_air::BaseAir, p3_field::{Field, PrimeField32}, p3_maybe_rayon::prelude::{ParallelIterator, ParallelSlice}, }; -use serde::{Deserialize, Serialize}; -use crate::poseidon2::{ - air::{NativePoseidon2Air, VerifyBatchBus}, - CHUNK, +use crate::{ + adapters::{ + memory_read_native, memory_write_native, tracing_read_native, tracing_write_native, + }, + poseidon2::{ + columns::{ + InsideRowSpecificCols, NativePoseidon2Cols, SimplePoseidonSpecificCols, + TopLevelSpecificCols, + }, + CHUNK, + }, }; -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct VerifyBatchRecord { - pub from_state: ExecutionState, - pub instruction: Instruction, - - pub dim_base_pointer: F, - pub opened_base_pointer: F, - pub opened_length: usize, - pub index_base_pointer: F, - pub commit_pointer: F, - - pub dim_base_pointer_read: RecordId, - pub opened_base_pointer_read: RecordId, - pub opened_length_read: RecordId, - pub index_base_pointer_read: RecordId, - pub commit_pointer_read: RecordId, - - pub commit_read: RecordId, - pub initial_log_height: usize, - pub top_level: Vec>, -} - -impl VerifyBatchRecord { - pub fn opened_element_size_inv(&self) -> F { - self.instruction.g - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct TopLevelRecord { - // must be present in first record - pub incorporate_row: Option>, - // must be present in all bust last record - pub incorporate_sibling: Option>, -} - -#[repr(C)] -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct IncorporateSiblingRecord { - pub read_sibling_is_on_right: RecordId, - pub sibling_is_on_right: bool, - pub p2_input: [F; 2 * CHUNK], -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct IncorporateRowRecord { - pub chunks: Vec>, - pub initial_opened_index: usize, - pub final_opened_index: usize, - pub initial_height_read: RecordId, - pub final_height_read: RecordId, - pub p2_input: [F; 2 * CHUNK], -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct InsideRowRecord { - pub cells: Vec, - pub p2_input: [F; 2 * CHUNK], -} - -#[repr(C)] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct CellRecord { - pub read: RecordId, - pub opened_index: usize, - pub read_row_pointer_and_length: Option, - pub row_pointer: usize, - pub row_end: usize, -} - -#[repr(C)] -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct SimplePoseidonRecord { - pub from_state: ExecutionState, - pub instruction: Instruction, - - pub read_input_pointer_1: RecordId, - pub read_input_pointer_2: Option, - pub read_output_pointer: RecordId, - pub read_data_1: RecordId, - pub read_data_2: RecordId, - pub write_data_1: RecordId, - pub write_data_2: Option, - - pub input_pointer_1: F, - pub input_pointer_2: F, - pub output_pointer: F, - pub p2_input: [F; 2 * CHUNK], -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -#[serde(bound = "F: Field")] -pub struct NativePoseidon2RecordSet { - pub verify_batch_records: Vec>, - pub simple_permute_records: Vec>, -} - -pub struct NativePoseidon2Chip { - pub(super) air: NativePoseidon2Air, - pub record_set: NativePoseidon2RecordSet, - pub height: usize, - pub(super) offline_memory: Arc>>, +pub struct NativePoseidon2Step { + // pre-computed Poseidon2 sub cols for dummy rows. + empty_poseidon2_sub_cols: Vec, pub(super) subchip: Poseidon2SubChip, pub(super) streams: Arc>>, } -impl NativePoseidon2Chip { - pub fn new( - port: SystemPort, - offline_memory: Arc>>, - poseidon2_config: Poseidon2Config, - verify_batch_bus: VerifyBatchBus, - streams: Arc>>, - ) -> Self { - let air = NativePoseidon2Air { - execution_bridge: ExecutionBridge::new(port.execution_bus, port.program_bus), - memory_bridge: port.memory_bridge, - internal_bus: verify_batch_bus, - subair: Arc::new(Poseidon2SubAir::new(poseidon2_config.constants.into())), - address_space: F::from_canonical_u32(AS::Native as u32), - }; +impl NativePoseidon2Step { + pub fn new(poseidon2_config: Poseidon2Config, streams: Arc>>) -> Self { + let subchip = Poseidon2SubChip::new(poseidon2_config.constants); + let empty_poseidon2_sub_cols = subchip.generate_trace(vec![[F::ZERO; CHUNK * 2]]).values; Self { - record_set: Default::default(), - air, - height: 0, - offline_memory, - subchip: Poseidon2SubChip::new(poseidon2_config.constants), + empty_poseidon2_sub_cols, + subchip, streams, } } @@ -172,18 +68,26 @@ impl NativePoseidon2Chip InstructionExecutor - for NativePoseidon2Chip +impl TraceStep + for NativePoseidon2Step { fn execute( &mut self, - memory: &mut MemoryController, + state: VmStateMut, CTX>, instruction: &Instruction, - from_state: ExecutionState, - ) -> Result, ExecutionError> { + trace: &mut [F], + trace_offset: &mut usize, + width: usize, + ) -> openvm_circuit::arch::Result<()> { + debug_assert_eq!(width, NativePoseidon2Cols::::width()); + let init_timestamp_u32 = state.memory.timestamp; if instruction.opcode == PERM_POS2.global_opcode() || instruction.opcode == COMP_POS2.global_opcode() { + let cols: &mut NativePoseidon2Cols = + trace[*trace_offset..*trace_offset + width].borrow_mut(); + let simple_cols: &mut SimplePoseidonSpecificCols = + cols.specific[..SimplePoseidonSpecificCols::::width()].borrow_mut(); let &Instruction { a: output_register, b: input_register_1, @@ -192,24 +96,45 @@ impl InstructionExecutor e: data_address_space, .. } = instruction; + debug_assert_eq!( + register_address_space, + F::from_canonical_u32(AS::Native as u32) + ); + debug_assert_eq!(data_address_space, F::from_canonical_u32(AS::Native as u32)); + let [output_pointer]: [F; 1] = tracing_read_native( + state.memory, + output_register.as_canonical_u32(), + simple_cols.read_output_pointer.as_mut(), + ); + let output_pointer_u32 = output_pointer.as_canonical_u32(); + let [input_pointer_1]: [F; 1] = tracing_read_native( + state.memory, + input_register_1.as_canonical_u32(), + simple_cols.read_input_pointer_1.as_mut(), + ); + let input_pointer_1_u32 = input_pointer_1.as_canonical_u32(); + let [input_pointer_2]: [F; 1] = if instruction.opcode == PERM_POS2.global_opcode() { + state.memory.increment_timestamp(); + [input_pointer_1 + F::from_canonical_usize(CHUNK)] + } else { + tracing_read_native( + state.memory, + input_register_2.as_canonical_u32(), + simple_cols.read_input_pointer_2.as_mut(), + ) + }; + let input_pointer_2_u32 = input_pointer_2.as_canonical_u32(); + let data_1: [F; CHUNK] = tracing_read_native( + state.memory, + input_pointer_1_u32, + simple_cols.read_data_1.as_mut(), + ); + let data_2: [F; CHUNK] = tracing_read_native( + state.memory, + input_pointer_2_u32, + simple_cols.read_data_2.as_mut(), + ); - let (read_output_pointer, output_pointer) = - memory.read_cell(register_address_space, output_register); - let (read_input_pointer_1, input_pointer_1) = - memory.read_cell(register_address_space, input_register_1); - let (read_input_pointer_2, input_pointer_2) = - if instruction.opcode == PERM_POS2.global_opcode() { - memory.increment_timestamp(); - (None, input_pointer_1 + F::from_canonical_usize(CHUNK)) - } else { - let (read_input_pointer_2, input_pointer_2) = - memory.read_cell(register_address_space, input_register_2); - (Some(read_input_pointer_2), input_pointer_2) - }; - let (read_data_1, data_1) = - memory.read::(data_address_space, input_pointer_1); - let (read_data_2, data_2) = - memory.read::(data_address_space, input_pointer_2); let p2_input = std::array::from_fn(|i| { if i < CHUNK { data_1[i] @@ -218,50 +143,54 @@ impl InstructionExecutor } }); let output = self.subchip.permute(p2_input); - let (write_data_1, _) = memory.write::( - data_address_space, - output_pointer, + tracing_write_native( + state.memory, + output_pointer_u32, &std::array::from_fn(|i| output[i]), + &mut simple_cols.write_data_1, ); - let write_data_2 = if instruction.opcode == PERM_POS2.global_opcode() { - Some( - memory - .write::( - data_address_space, - output_pointer + F::from_canonical_usize(CHUNK), - &std::array::from_fn(|i| output[CHUNK + i]), - ) - .0, - ) + if instruction.opcode == PERM_POS2.global_opcode() { + tracing_write_native( + state.memory, + output_pointer_u32 + CHUNK as u32, + &std::array::from_fn(|i| output[i + CHUNK]), + &mut simple_cols.write_data_2, + ); } else { - memory.increment_timestamp(); - None - }; - - assert_eq!( - memory.timestamp(), - from_state.timestamp + NUM_SIMPLE_ACCESSES + state.memory.increment_timestamp(); + } + debug_assert_eq!( + state.memory.timestamp, + init_timestamp_u32 + NUM_SIMPLE_ACCESSES ); - - self.record_set - .simple_permute_records - .push(SimplePoseidonRecord { - from_state, - instruction: instruction.clone(), - read_input_pointer_1, - read_input_pointer_2, - read_output_pointer, - read_data_1, - read_data_2, - write_data_1, - write_data_2, - input_pointer_1, - input_pointer_2, - output_pointer, - p2_input, - }); - self.height += 1; + cols.incorporate_row = F::ZERO; + cols.incorporate_sibling = F::ZERO; + cols.inside_row = F::ZERO; + cols.simple = F::ONE; + cols.end_inside_row = F::ZERO; + cols.end_top_level = F::ZERO; + cols.is_exhausted = [F::ZERO; CHUNK - 1]; + cols.start_timestamp = F::from_canonical_u32(init_timestamp_u32); + + cols.inner.inputs = p2_input; + simple_cols.pc = F::from_canonical_u32(*state.pc); + simple_cols.is_compress = F::from_bool(instruction.opcode == COMP_POS2.global_opcode()); + simple_cols.output_register = output_register; + simple_cols.input_register_1 = input_register_1; + simple_cols.input_register_2 = input_register_2; + simple_cols.output_pointer = output_pointer; + simple_cols.input_pointer_1 = input_pointer_1; + simple_cols.input_pointer_2 = input_pointer_2; + + *trace_offset += width; } else if instruction.opcode == VERIFY_BATCH.global_opcode() { + let init_timestamp = F::from_canonical_u32(init_timestamp_u32); + let mut col_buffer = + vec![F::ZERO; NativePoseidon2Cols::::width()]; + let last_top_level_cols: &mut NativePoseidon2Cols = + col_buffer.as_mut_slice().borrow_mut(); + let ltl_specific_cols: &mut TopLevelSpecificCols = + last_top_level_cols.specific[..TopLevelSpecificCols::::width()].borrow_mut(); let &Instruction { a: dim_register, b: opened_register, @@ -272,35 +201,116 @@ impl InstructionExecutor g: opened_element_size_inv, .. } = instruction; - let address_space = self.air.address_space; // calc inverse fast assuming opened_element_size in {1, 4} let mut opened_element_size = F::ONE; while opened_element_size * opened_element_size_inv != F::ONE { opened_element_size += F::ONE; } - let proof_id = memory.unsafe_read_cell::(address_space, proof_id_ptr); - let (dim_base_pointer_read, dim_base_pointer) = - memory.read_cell(address_space, dim_register); - let (opened_base_pointer_read, opened_base_pointer) = - memory.read_cell(address_space, opened_register); - let (opened_length_read, opened_length) = - memory.read_cell(address_space, opened_length_register); - let (index_base_pointer_read, index_base_pointer) = - memory.read_cell(address_space, index_register); - let (commit_pointer_read, commit_pointer) = - memory.read_cell(address_space, commit_register); - let (commit_read, commit) = memory.read::(address_space, commit_pointer); + let [proof_id]: [F; 1] = + memory_read_native(state.memory.data(), proof_id_ptr.as_canonical_u32()); + let [dim_base_pointer]: [F; 1] = tracing_read_native( + state.memory, + dim_register.as_canonical_u32(), + ltl_specific_cols.dim_base_pointer_read.as_mut(), + ); + let dim_base_pointer_u32 = dim_base_pointer.as_canonical_u32(); + let [opened_base_pointer]: [F; 1] = tracing_read_native( + state.memory, + opened_register.as_canonical_u32(), + ltl_specific_cols.opened_base_pointer_read.as_mut(), + ); + let opened_base_pointer_u32 = opened_base_pointer.as_canonical_u32(); + let [opened_length]: [F; 1] = tracing_read_native( + state.memory, + opened_length_register.as_canonical_u32(), + ltl_specific_cols.opened_length_read.as_mut(), + ); + let [index_base_pointer]: [F; 1] = tracing_read_native( + state.memory, + index_register.as_canonical_u32(), + ltl_specific_cols.index_base_pointer_read.as_mut(), + ); + let index_base_pointer_u32 = index_base_pointer.as_canonical_u32(); + let [commit_pointer]: [F; 1] = tracing_read_native( + state.memory, + commit_register.as_canonical_u32(), + ltl_specific_cols.commit_pointer_read.as_mut(), + ); + let commit = tracing_read_native( + state.memory, + commit_pointer.as_canonical_u32(), + ltl_specific_cols.commit_read.as_mut(), + ); let opened_length = opened_length.as_canonical_u32() as usize; + let [initial_log_height]: [F; 1] = + memory_read_native(state.memory.data(), dim_base_pointer_u32); + let initial_log_height_u32 = initial_log_height.as_canonical_u32(); + let mut log_height = initial_log_height_u32 as i32; + + // Number of non-inside rows, this is used to compute the offset of the inside row + // section. + let num_non_inside_rows = { + let mut log_height = initial_log_height_u32 as i32; + let mut opened_index = 0; + let mut num_non_inside_rows = 0; + while log_height >= 0 { + if opened_index < opened_length + && memory_read_native::( + state.memory.data(), + dim_base_pointer_u32 + opened_index as u32, + )[0] == F::from_canonical_u32(log_height as u32) + { + let mut row_pointer = 0; + let mut row_end = 0; + let mut is_first_in_segment = true; + + loop { + let mut cell_idx = 0; + for _ in 0..CHUNK { + if is_first_in_segment || row_pointer == row_end { + if is_first_in_segment { + is_first_in_segment = false; + } else { + opened_index += 1; + if opened_index == opened_length + || memory_read_native::( + state.memory.data(), + dim_base_pointer_u32 + opened_index as u32, + )[0] != F::from_canonical_u32(log_height as u32) + { + break; + } + } + let [new_row_pointer, row_len]: [F; 2] = memory_read_native( + state.memory.data(), + opened_base_pointer_u32 + 2 * opened_index as u32, + ); + row_pointer = new_row_pointer.as_canonical_u32() as usize; + row_end = row_pointer + + (opened_element_size * row_len).as_canonical_u32() + as usize; + } + cell_idx += 1; + row_pointer += 1; + } - let initial_log_height = memory - .unsafe_read_cell::(address_space, dim_base_pointer) - .as_canonical_u32(); - let mut log_height = initial_log_height as i32; - let mut sibling_index = 0; + if cell_idx < CHUNK { + break; + } + } + num_non_inside_rows += 1; + } + if log_height != 0 { + num_non_inside_rows += 1; + } + log_height -= 1; + } + num_non_inside_rows + }; + let mut proof_index = 0; let mut opened_index = 0; - let mut top_level = vec![]; let mut root = [F::ZERO; CHUNK]; let sibling_proof: Vec<[F; CHUNK]> = { @@ -312,18 +322,21 @@ impl InstructionExecutor .collect() }; + let mut inside_row_offset = *trace_offset + num_non_inside_rows * width; + let mut non_inside_row_offset = *trace_offset; + while log_height >= 0 { - let incorporate_row = if opened_index < opened_length - && memory.unsafe_read_cell::( - address_space, - dim_base_pointer + F::from_canonical_usize(opened_index), - ) == F::from_canonical_u32(log_height as u32) + if opened_index < opened_length + && memory_read_native::( + state.memory.data(), + dim_base_pointer_u32 + opened_index as u32, + )[0] == F::from_canonical_u32(log_height as u32) { + state + .memory + .increment_timestamp_by(NUM_INITIAL_READS as u32); + let incorporate_start_timestamp = state.memory.timestamp; let initial_opened_index = opened_index; - for _ in 0..NUM_INITIAL_READS { - memory.increment_timestamp(); - } - let mut chunks = vec![]; let mut row_pointer = 0; let mut row_end = 0; @@ -334,166 +347,365 @@ impl InstructionExecutor let mut is_first_in_segment = true; loop { - let mut cells = vec![]; + let inside_cols: &mut NativePoseidon2Cols = + trace[inside_row_offset..inside_row_offset + width].borrow_mut(); + let inside_specific_cols: &mut InsideRowSpecificCols = inside_cols + .specific[..InsideRowSpecificCols::::width()] + .borrow_mut(); + let start_timestamp_u32 = state.memory.timestamp; + + let mut cells_idx = 0; for chunk_elem in rolling_hash.iter_mut().take(CHUNK) { - let read_row_pointer_and_length = if is_first_in_segment - || row_pointer == row_end - { + let cell_cols = &mut inside_specific_cols.cells[cells_idx]; + if is_first_in_segment || row_pointer == row_end { if is_first_in_segment { is_first_in_segment = false; } else { opened_index += 1; if opened_index == opened_length - || memory.unsafe_read_cell::( - address_space, - dim_base_pointer - + F::from_canonical_usize(opened_index), - ) != F::from_canonical_u32(log_height as u32) + || memory_read_native::( + state.memory.data(), + dim_base_pointer_u32 + opened_index as u32, + )[0] != F::from_canonical_u32(log_height as u32) { break; } } - let (result, [new_row_pointer, row_len]) = memory.read::( - address_space, - opened_base_pointer + F::from_canonical_usize(2 * opened_index), + let [new_row_pointer, row_len]: [F; 2] = tracing_read_native( + state.memory, + opened_base_pointer_u32 + 2 * opened_index as u32, + cell_cols.read_row_pointer_and_length.as_mut(), ); row_pointer = new_row_pointer.as_canonical_u32() as usize; row_end = row_pointer + (opened_element_size * row_len).as_canonical_u32() as usize; - Some(result) + cell_cols.is_first_in_row = F::ONE; } else { - memory.increment_timestamp(); - None - }; - let (read, value) = memory - .read_cell(address_space, F::from_canonical_usize(row_pointer)); - cells.push(CellRecord { - read, - opened_index, - read_row_pointer_and_length, - row_pointer, - row_end, - }); + state.memory.increment_timestamp(); + } + let [value]: [F; 1] = tracing_read_native( + state.memory, + row_pointer as u32, + cell_cols.read.as_mut(), + ); + + cell_cols.opened_index = F::from_canonical_usize(opened_index); + cell_cols.row_pointer = F::from_canonical_usize(row_pointer); + cell_cols.row_end = F::from_canonical_usize(row_end); + *chunk_elem = value; row_pointer += 1; + cells_idx += 1; } - if cells.is_empty() { + if cells_idx == 0 { break; } - let cells_len = cells.len(); - chunks.push(InsideRowRecord { - cells, - p2_input: rolling_hash, - }); - self.height += 1; + let p2_input = rolling_hash; prev_rolling_hash = Some(rolling_hash); self.subchip.permute_mut(&mut rolling_hash); - if cells_len < CHUNK { - for _ in 0..CHUNK - cells_len { - memory.increment_timestamp(); - memory.increment_timestamp(); + if cells_idx < CHUNK { + state + .memory + .increment_timestamp_by(2 * (CHUNK - cells_idx) as u32); + } + + inside_row_offset += width; + inside_cols.inner.inputs = p2_input; + inside_cols.incorporate_row = F::ZERO; + inside_cols.incorporate_sibling = F::ZERO; + inside_cols.inside_row = F::ONE; + inside_cols.simple = F::ZERO; + // `end_inside_row` of the last row will be set to 1 after this loop. + inside_cols.end_inside_row = F::ZERO; + inside_cols.end_top_level = F::ZERO; + inside_cols.opened_element_size_inv = opened_element_size_inv; + inside_cols.very_first_timestamp = + F::from_canonical_u32(incorporate_start_timestamp); + inside_cols.start_timestamp = F::from_canonical_u32(start_timestamp_u32); + + inside_cols.initial_opened_index = + F::from_canonical_usize(initial_opened_index); + inside_cols.opened_base_pointer = opened_base_pointer; + if cells_idx < CHUNK { + let exhausted_opened_idx = F::from_canonical_usize(opened_index - 1); + for exhausted_idx in cells_idx..CHUNK { + inside_cols.is_exhausted[exhausted_idx - 1] = F::ONE; + inside_specific_cols.cells[exhausted_idx].opened_index = + exhausted_opened_idx; } break; } } + { + let inside_cols: &mut NativePoseidon2Cols = + trace[inside_row_offset - width..inside_row_offset].borrow_mut(); + inside_cols.end_inside_row = F::ONE; + } + + let incorporate_cols: &mut NativePoseidon2Cols = + trace[non_inside_row_offset..non_inside_row_offset + width].borrow_mut(); + let top_level_specific_cols: &mut TopLevelSpecificCols = incorporate_cols + .specific[..TopLevelSpecificCols::::width()] + .borrow_mut(); + let final_opened_index = opened_index - 1; - let (initial_height_read, height_check) = memory.read_cell( - address_space, - dim_base_pointer + F::from_canonical_usize(initial_opened_index), + let [height_check]: [F; 1] = tracing_read_native( + state.memory, + dim_base_pointer_u32 + initial_opened_index as u32, + top_level_specific_cols + .read_initial_height_or_sibling_is_on_right + .as_mut(), ); assert_eq!(height_check, F::from_canonical_u32(log_height as u32)); - let (final_height_read, height_check) = memory.read_cell( - address_space, - dim_base_pointer + F::from_canonical_usize(final_opened_index), + let final_height_read_timestamp = state.memory.timestamp; + let [height_check]: [F; 1] = tracing_read_native( + state.memory, + dim_base_pointer_u32 + final_opened_index as u32, + top_level_specific_cols.read_final_height.as_mut(), ); assert_eq!(height_check, F::from_canonical_u32(log_height as u32)); let hash: [F; CHUNK] = std::array::from_fn(|i| rolling_hash[i]); - - let (p2_input, new_root) = if log_height as u32 == initial_log_height { + let (p2_input, new_root) = if log_height as u32 == initial_log_height_u32 { (prev_rolling_hash.unwrap(), hash) } else { self.compress(root, hash) }; root = new_root; - self.height += 1; - Some(IncorporateRowRecord { - chunks, - initial_opened_index, - final_opened_index, - initial_height_read, - final_height_read, - p2_input, - }) - } else { - None - }; - - let incorporate_sibling = if log_height == 0 { - None - } else { - for _ in 0..NUM_INITIAL_READS { - memory.increment_timestamp(); - } + non_inside_row_offset += width; + + incorporate_cols.incorporate_row = F::ONE; + incorporate_cols.incorporate_sibling = F::ZERO; + incorporate_cols.inside_row = F::ZERO; + incorporate_cols.simple = F::ZERO; + incorporate_cols.end_inside_row = F::ZERO; + incorporate_cols.end_top_level = F::ZERO; + incorporate_cols.start_top_level = F::from_bool(proof_index == 0); + incorporate_cols.opened_element_size_inv = opened_element_size_inv; + incorporate_cols.very_first_timestamp = init_timestamp; + incorporate_cols.start_timestamp = F::from_canonical_u32( + incorporate_start_timestamp - NUM_INITIAL_READS as u32, + ); + top_level_specific_cols.end_timestamp = + F::from_canonical_u32(final_height_read_timestamp + 1); + + incorporate_cols.inner.inputs = p2_input; + incorporate_cols.initial_opened_index = + F::from_canonical_usize(initial_opened_index); + top_level_specific_cols.final_opened_index = + F::from_canonical_usize(final_opened_index); + top_level_specific_cols.log_height = F::from_canonical_u32(log_height as u32); + top_level_specific_cols.opened_length = F::from_canonical_usize(opened_length); + top_level_specific_cols.dim_base_pointer = dim_base_pointer; + incorporate_cols.opened_base_pointer = opened_base_pointer; + top_level_specific_cols.index_base_pointer = index_base_pointer; + top_level_specific_cols.proof_index = F::from_canonical_usize(proof_index); + } - let (read_sibling_is_on_right, sibling_is_on_right) = memory.read_cell( - address_space, - index_base_pointer + F::from_canonical_usize(sibling_index), + if log_height != 0 { + let row_start_timestamp = state.memory.timestamp; + state + .memory + .increment_timestamp_by(NUM_INITIAL_READS as u32); + + let sibling_cols: &mut NativePoseidon2Cols = + trace[non_inside_row_offset..non_inside_row_offset + width].borrow_mut(); + let top_level_specific_cols: &mut TopLevelSpecificCols = + sibling_cols.specific[..TopLevelSpecificCols::::width()].borrow_mut(); + + let read_sibling_is_on_right_timestamp = state.memory.timestamp; + let [sibling_is_on_right]: [F; 1] = tracing_read_native( + state.memory, + index_base_pointer_u32 + proof_index as u32, + top_level_specific_cols + .read_initial_height_or_sibling_is_on_right + .as_mut(), ); - let sibling_is_on_right = sibling_is_on_right == F::ONE; - let sibling = sibling_proof[sibling_index]; - let (p2_input, new_root) = if sibling_is_on_right { + let sibling = sibling_proof[proof_index]; + let (p2_input, new_root) = if sibling_is_on_right == F::ONE { self.compress(sibling, root) } else { self.compress(root, sibling) }; root = new_root; - self.height += 1; - Some(IncorporateSiblingRecord { - read_sibling_is_on_right, - sibling_is_on_right, - p2_input, - }) + non_inside_row_offset += width; + + sibling_cols.inner.inputs = p2_input; + + sibling_cols.incorporate_row = F::ZERO; + sibling_cols.incorporate_sibling = F::ONE; + sibling_cols.inside_row = F::ZERO; + sibling_cols.simple = F::ZERO; + sibling_cols.end_inside_row = F::ZERO; + sibling_cols.end_top_level = F::ZERO; + sibling_cols.start_top_level = F::ZERO; + sibling_cols.opened_element_size_inv = opened_element_size_inv; + sibling_cols.very_first_timestamp = init_timestamp; + sibling_cols.start_timestamp = F::from_canonical_u32(row_start_timestamp); + + top_level_specific_cols.end_timestamp = + F::from_canonical_u32(read_sibling_is_on_right_timestamp + 1); + sibling_cols.initial_opened_index = F::from_canonical_usize(opened_index); + top_level_specific_cols.final_opened_index = + F::from_canonical_usize(opened_index - 1); + top_level_specific_cols.log_height = F::from_canonical_u32(log_height as u32); + top_level_specific_cols.opened_length = F::from_canonical_usize(opened_length); + top_level_specific_cols.dim_base_pointer = dim_base_pointer; + sibling_cols.opened_base_pointer = opened_base_pointer; + top_level_specific_cols.index_base_pointer = index_base_pointer; + + top_level_specific_cols.proof_index = F::from_canonical_usize(proof_index); + top_level_specific_cols.sibling_is_on_right = sibling_is_on_right; }; - top_level.push(TopLevelRecord { - incorporate_row, - incorporate_sibling, - }); - log_height -= 1; - sibling_index += 1; + proof_index += 1; } - + let ltl_trace_cols: &mut NativePoseidon2Cols = + trace[non_inside_row_offset - width..non_inside_row_offset].borrow_mut(); + let ltl_trace_specific_cols: &mut TopLevelSpecificCols = + ltl_trace_cols.specific[..TopLevelSpecificCols::::width()].borrow_mut(); + ltl_trace_cols.end_top_level = F::ONE; + ltl_trace_specific_cols.pc = F::from_canonical_u32(*state.pc); + ltl_trace_specific_cols.dim_register = dim_register; + ltl_trace_specific_cols.opened_register = opened_register; + ltl_trace_specific_cols.opened_length_register = opened_length_register; + ltl_trace_specific_cols.proof_id = proof_id_ptr; + ltl_trace_specific_cols.index_register = index_register; + ltl_trace_specific_cols.commit_register = commit_register; + ltl_trace_specific_cols.commit_pointer = commit_pointer; + ltl_trace_specific_cols.dim_base_pointer_read = ltl_specific_cols.dim_base_pointer_read; + ltl_trace_specific_cols.opened_base_pointer_read = + ltl_specific_cols.opened_base_pointer_read; + ltl_trace_specific_cols.opened_length_read = ltl_specific_cols.opened_length_read; + ltl_trace_specific_cols.index_base_pointer_read = + ltl_specific_cols.index_base_pointer_read; + ltl_trace_specific_cols.commit_pointer_read = ltl_specific_cols.commit_pointer_read; + ltl_trace_specific_cols.commit_read = ltl_specific_cols.commit_read; + + *trace_offset = inside_row_offset; assert_eq!(commit, root); - self.record_set - .verify_batch_records - .push(VerifyBatchRecord { - from_state, - instruction: instruction.clone(), - dim_base_pointer, - opened_base_pointer, - opened_length, - index_base_pointer, - commit_pointer, - dim_base_pointer_read, - opened_base_pointer_read, - opened_length_read, - index_base_pointer_read, - commit_pointer_read, - commit_read, - initial_log_height: initial_log_height as usize, - top_level, - }); } else { unreachable!() } - Ok(ExecutionState { - pc: from_state.pc + DEFAULT_PC_STEP, - timestamp: memory.timestamp(), - }) + + *state.pc += DEFAULT_PC_STEP; + Ok(()) + } + + fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let inner_cols = { + let cols: &NativePoseidon2Cols = row_slice.as_ref().borrow(); + &self.subchip.generate_trace(vec![cols.inner.inputs]).values + }; + let inner_width = self.subchip.air.width(); + row_slice[..inner_width].copy_from_slice(&inner_cols); + let cols: &mut NativePoseidon2Cols = row_slice.borrow_mut(); + + // Simple poseidon2 row + if cols.simple.is_one() { + let simple_cols: &mut SimplePoseidonSpecificCols = + cols.specific[..SimplePoseidonSpecificCols::::width()].borrow_mut(); + let start_timestamp_u32 = cols.start_timestamp.as_canonical_u32(); + mem_helper.fill_from_prev( + start_timestamp_u32, + simple_cols.read_output_pointer.as_mut(), + ); + mem_helper.fill_from_prev( + start_timestamp_u32 + 1, + simple_cols.read_input_pointer_1.as_mut(), + ); + if simple_cols.is_compress.is_one() { + mem_helper.fill_from_prev( + start_timestamp_u32 + 2, + simple_cols.read_input_pointer_2.as_mut(), + ); + } + mem_helper.fill_from_prev(start_timestamp_u32 + 3, simple_cols.read_data_1.as_mut()); + mem_helper.fill_from_prev(start_timestamp_u32 + 4, simple_cols.read_data_2.as_mut()); + mem_helper.fill_from_prev(start_timestamp_u32 + 5, simple_cols.write_data_1.as_mut()); + if simple_cols.is_compress.is_zero() { + mem_helper + .fill_from_prev(start_timestamp_u32 + 6, simple_cols.write_data_2.as_mut()); + } + } else if cols.inside_row.is_one() { + let inside_row_specific_cols: &mut InsideRowSpecificCols = + cols.specific[..InsideRowSpecificCols::::width()].borrow_mut(); + let start_timestamp_u32 = cols.start_timestamp.as_canonical_u32(); + for (i, cell) in inside_row_specific_cols.cells.iter_mut().enumerate() { + if i > 0 && cols.is_exhausted[i - 1].is_one() { + break; + } + if cell.is_first_in_row.is_one() { + mem_helper.fill_from_prev( + start_timestamp_u32 + 2 * i as u32, + cell.read_row_pointer_and_length.as_mut(), + ); + } + mem_helper + .fill_from_prev(start_timestamp_u32 + 2 * i as u32 + 1, cell.read.as_mut()); + } + } else { + let top_level_specific_cols: &mut TopLevelSpecificCols = + cols.specific[..TopLevelSpecificCols::::width()].borrow_mut(); + let start_timestamp_u32 = cols.start_timestamp.as_canonical_u32(); + if cols.end_top_level.is_one() { + let very_start_timestamp_u32 = cols.very_first_timestamp.as_canonical_u32(); + mem_helper.fill_from_prev( + very_start_timestamp_u32, + top_level_specific_cols.dim_base_pointer_read.as_mut(), + ); + mem_helper.fill_from_prev( + very_start_timestamp_u32 + 1, + top_level_specific_cols.opened_base_pointer_read.as_mut(), + ); + mem_helper.fill_from_prev( + very_start_timestamp_u32 + 2, + top_level_specific_cols.opened_length_read.as_mut(), + ); + mem_helper.fill_from_prev( + very_start_timestamp_u32 + 3, + top_level_specific_cols.index_base_pointer_read.as_mut(), + ); + mem_helper.fill_from_prev( + very_start_timestamp_u32 + 4, + top_level_specific_cols.commit_pointer_read.as_mut(), + ); + mem_helper.fill_from_prev( + very_start_timestamp_u32 + 5, + top_level_specific_cols.commit_read.as_mut(), + ); + } + if cols.incorporate_row.is_one() { + let end_timestamp = top_level_specific_cols.end_timestamp.as_canonical_u32(); + mem_helper.fill_from_prev( + end_timestamp - 2, + top_level_specific_cols + .read_initial_height_or_sibling_is_on_right + .as_mut(), + ); + mem_helper.fill_from_prev( + end_timestamp - 1, + top_level_specific_cols.read_final_height.as_mut(), + ); + } else if cols.incorporate_sibling.is_one() { + mem_helper.fill_from_prev( + start_timestamp_u32 + NUM_INITIAL_READS as u32, + top_level_specific_cols + .read_initial_height_or_sibling_is_on_right + .as_mut(), + ); + } else { + unreachable!() + } + } + } + + fn fill_dummy_trace_row(&self, _mem_helper: &MemoryAuxColsFactory, row_slice: &mut [F]) { + let width = self.subchip.air.width(); + row_slice[..width].copy_from_slice(&self.empty_poseidon2_sub_cols); } fn get_opcode_name(&self, opcode: usize) -> String { @@ -508,3 +720,263 @@ impl InstructionExecutor } } } +impl StepExecutorE1 + for NativePoseidon2Step +{ + fn execute_e1( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> openvm_circuit::arch::Result<()> + where + Ctx: E1E2ExecutionCtx, + { + self.execute_e1_impl(state, instruction); + Ok(()) + } + + fn execute_metered( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + chip_index: usize, + ) -> openvm_circuit::arch::Result<()> { + let height = self.execute_e1_impl(state, instruction); + state.ctx.trace_heights[chip_index] += height as u32; + + Ok(()) + } +} + +impl NativePoseidon2Step { + /// Returns the number of used rows. + fn execute_e1_impl( + &mut self, + state: &mut VmStateMut, + instruction: &Instruction, + ) -> usize + where + Ctx: E1E2ExecutionCtx, + { + let mut height = 0; + if instruction.opcode == PERM_POS2.global_opcode() + || instruction.opcode == COMP_POS2.global_opcode() + { + let &Instruction { + a: output_register, + b: input_register_1, + c: input_register_2, + d: register_address_space, + e: data_address_space, + .. + } = instruction; + debug_assert_eq!( + register_address_space, + F::from_canonical_u32(AS::Native as u32) + ); + debug_assert_eq!(data_address_space, F::from_canonical_u32(AS::Native as u32)); + let [output_pointer]: [F; 1] = + memory_read_native(state.memory, output_register.as_canonical_u32()); + let [input_pointer_1]: [F; 1] = + memory_read_native(state.memory, input_register_1.as_canonical_u32()); + let [input_pointer_2] = if instruction.opcode == PERM_POS2.global_opcode() { + [input_pointer_1 + F::from_canonical_usize(CHUNK)] + } else { + memory_read_native(state.memory, input_register_2.as_canonical_u32()) + }; + let data_1: [F; CHUNK] = + memory_read_native(state.memory, input_pointer_1.as_canonical_u32()); + let data_2: [F; CHUNK] = + memory_read_native(state.memory, input_pointer_2.as_canonical_u32()); + + let p2_input = std::array::from_fn(|i| { + if i < CHUNK { + data_1[i] + } else { + data_2[i - CHUNK] + } + }); + let output = self.subchip.permute(p2_input); + let output_pointer_u32 = output_pointer.as_canonical_u32(); + memory_write_native::( + state.memory, + output_pointer_u32, + &std::array::from_fn(|i| output[i]), + ); + if instruction.opcode == PERM_POS2.global_opcode() { + memory_write_native::( + state.memory, + output_pointer_u32 + CHUNK as u32, + &std::array::from_fn(|i| output[i + CHUNK]), + ); + } + + height += 1; + } else if instruction.opcode == VERIFY_BATCH.global_opcode() { + // TODO: Add a flag `optimistic_execution`. When the flag is true, we trust all inputs + // and skip all input validation computation during E1 execution. + let &Instruction { + a: dim_register, + b: opened_register, + c: opened_length_register, + d: proof_id_ptr, + e: index_register, + f: commit_register, + g: opened_element_size_inv, + .. + } = instruction; + // calc inverse fast assuming opened_element_size in {1, 4} + let mut opened_element_size = F::ONE; + while opened_element_size * opened_element_size_inv != F::ONE { + opened_element_size += F::ONE; + } + + let [proof_id]: [F; 1] = + memory_read_native(state.memory, proof_id_ptr.as_canonical_u32()); + let [dim_base_pointer]: [F; 1] = + memory_read_native(state.memory, dim_register.as_canonical_u32()); + let dim_base_pointer_u32 = dim_base_pointer.as_canonical_u32(); + let [opened_base_pointer]: [F; 1] = + memory_read_native(state.memory, opened_register.as_canonical_u32()); + let opened_base_pointer_u32 = opened_base_pointer.as_canonical_u32(); + let [opened_length]: [F; 1] = + memory_read_native(state.memory, opened_length_register.as_canonical_u32()); + let [index_base_pointer]: [F; 1] = + memory_read_native(state.memory, index_register.as_canonical_u32()); + let index_base_pointer_u32 = index_base_pointer.as_canonical_u32(); + let [commit_pointer]: [F; 1] = + memory_read_native(state.memory, commit_register.as_canonical_u32()); + let commit: [F; CHUNK] = + memory_read_native(state.memory, commit_pointer.as_canonical_u32()); + + let opened_length = opened_length.as_canonical_u32() as usize; + + let initial_log_height = { + let [height]: [F; 1] = memory_read_native(state.memory, dim_base_pointer_u32); + height.as_canonical_u32() + }; + + let mut log_height = initial_log_height as i32; + let mut sibling_index = 0; + let mut opened_index = 0; + + let mut root = [F::ZERO; CHUNK]; + let sibling_proof: Vec<[F; CHUNK]> = { + let streams = self.streams.lock().unwrap(); + let proof_idx = proof_id.as_canonical_u32() as usize; + streams.hint_space[proof_idx] + .par_chunks(CHUNK) + .map(|c| c.try_into().unwrap()) + .collect() + }; + + while log_height >= 0 { + if opened_index < opened_length + && memory_read_native::( + state.memory, + dim_base_pointer_u32 + opened_index as u32, + )[0] == F::from_canonical_u32(log_height as u32) + { + let initial_opened_index = opened_index; + + let mut row_pointer = 0; + let mut row_end = 0; + + let mut rolling_hash = [F::ZERO; 2 * CHUNK]; + + let mut is_first_in_segment = true; + + loop { + let mut cells_len = 0; + for chunk_elem in rolling_hash.iter_mut().take(CHUNK) { + if is_first_in_segment || row_pointer == row_end { + if is_first_in_segment { + is_first_in_segment = false; + } else { + opened_index += 1; + if opened_index == opened_length + || memory_read_native::( + state.memory, + dim_base_pointer_u32 + opened_index as u32, + )[0] != F::from_canonical_u32(log_height as u32) + { + break; + } + } + let [new_row_pointer, row_len]: [F; 2] = memory_read_native( + state.memory, + opened_base_pointer_u32 + 2 * opened_index as u32, + ); + row_pointer = new_row_pointer.as_canonical_u32() as usize; + row_end = row_pointer + + (opened_element_size * row_len).as_canonical_u32() as usize; + } + let [value]: [F; 1] = + memory_read_native(state.memory, row_pointer as u32); + cells_len += 1; + *chunk_elem = value; + row_pointer += 1; + } + if cells_len == 0 { + break; + } + height += 1; + self.subchip.permute_mut(&mut rolling_hash); + if cells_len < CHUNK { + break; + } + } + let final_opened_index = opened_index - 1; + let [height_check]: [F; 1] = memory_read_native( + state.memory, + dim_base_pointer_u32 + initial_opened_index as u32, + ); + assert_eq!(height_check, F::from_canonical_u32(log_height as u32)); + let [height_check]: [F; 1] = memory_read_native( + state.memory, + dim_base_pointer_u32 + final_opened_index as u32, + ); + assert_eq!(height_check, F::from_canonical_u32(log_height as u32)); + + let hash: [F; CHUNK] = std::array::from_fn(|i| rolling_hash[i]); + + let new_root = if log_height as u32 == initial_log_height { + hash + } else { + self.compress(root, hash).1 + }; + root = new_root; + + height += 1; + } + + if log_height != 0 { + let [sibling_is_on_right]: [F; 1] = memory_read_native( + state.memory, + index_base_pointer_u32 + sibling_index as u32, + ); + let sibling_is_on_right = sibling_is_on_right == F::ONE; + let sibling = sibling_proof[sibling_index]; + let new_root = if sibling_is_on_right { + self.compress(sibling, root).1 + } else { + self.compress(root, sibling).1 + }; + root = new_root; + + height += 1; + } + + log_height -= 1; + sibling_index += 1; + } + + assert_eq!(commit, root); + } else { + unreachable!() + } + *state.pc += DEFAULT_PC_STEP; + + height + } +} diff --git a/extensions/native/circuit/src/poseidon2/mod.rs b/extensions/native/circuit/src/poseidon2/mod.rs index af503e20f4..7dde941126 100644 --- a/extensions/native/circuit/src/poseidon2/mod.rs +++ b/extensions/native/circuit/src/poseidon2/mod.rs @@ -1,8 +1,49 @@ +use std::sync::{Arc, Mutex}; + +use openvm_circuit::{ + arch::{ExecutionBridge, NewVmChipWrapper, Streams, SystemPort}, + system::memory::SharedMemoryHelper, +}; +use openvm_native_compiler::conversion::AS; +use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubAir}; +use openvm_stark_backend::p3_field::PrimeField32; + +use crate::poseidon2::{ + air::{NativePoseidon2Air, VerifyBatchBus}, + chip::NativePoseidon2Step, +}; + pub mod air; pub mod chip; mod columns; #[cfg(test)] mod tests; -mod trace; const CHUNK: usize = 8; +pub type NativePoseidon2Chip = NewVmChipWrapper< + F, + NativePoseidon2Air, + NativePoseidon2Step, +>; + +pub fn new_native_poseidon2_chip( + port: SystemPort, + poseidon2_config: Poseidon2Config, + verify_batch_bus: VerifyBatchBus, + streams: Arc>>, + max_ins_capacity: usize, + mem_helper: SharedMemoryHelper, +) -> NativePoseidon2Chip { + NativePoseidon2Chip::::new( + NativePoseidon2Air { + execution_bridge: ExecutionBridge::new(port.execution_bus, port.program_bus), + memory_bridge: port.memory_bridge, + internal_bus: verify_batch_bus, + subair: Arc::new(Poseidon2SubAir::new(poseidon2_config.constants.into())), + address_space: F::from_canonical_u32(AS::Native as u32), + }, + NativePoseidon2Step::new(poseidon2_config, streams), + max_ins_capacity, + mem_helper, + ) +} diff --git a/extensions/native/circuit/src/poseidon2/tests.rs b/extensions/native/circuit/src/poseidon2/tests.rs index 32a0e483a3..b81af91380 100644 --- a/extensions/native/circuit/src/poseidon2/tests.rs +++ b/extensions/native/circuit/src/poseidon2/tests.rs @@ -34,11 +34,12 @@ use rand::{rngs::StdRng, Rng}; use super::air::VerifyBatchBus; use crate::{ - poseidon2::{chip::NativePoseidon2Chip, CHUNK}, + poseidon2::{new_native_poseidon2_chip, CHUNK}, NativeConfig, }; const VERIFY_BATCH_BUS: VerifyBatchBus = VerifyBatchBus::new(7); +const MAX_INS_CAPACITY: usize = 1 << 15; fn compute_commit( dim: &[usize], @@ -155,12 +156,13 @@ fn test(cases: [Case; N]) { let mut tester = VmChipTestBuilder::default(); let streams = Arc::new(Mutex::new(Streams::default())); - let mut chip = NativePoseidon2Chip::::new( + let mut chip = new_native_poseidon2_chip::( tester.system_port(), - tester.offline_memory_mutex_arc(), Poseidon2Config::default(), VERIFY_BATCH_BUS, streams.clone(), + MAX_INS_CAPACITY, + tester.memory_helper(), ); let mut rng = create_seeded_rng(); @@ -174,7 +176,7 @@ fn test(cases: [Case; N]) { random_instance(&mut rng, row_lengths, opened_element_size, |left, right| { let concatenated = std::array::from_fn(|i| if i < CHUNK { left[i] } else { right[i - CHUNK] }); - let permuted = chip.subchip.permute(concatenated); + let permuted = chip.step.subchip.permute(concatenated); ( std::array::from_fn(|i| permuted[i]), std::array::from_fn(|i| permuted[i + CHUNK]), @@ -218,7 +220,7 @@ fn test(cases: [Case; N]) { [row_pointer, opened_row.len() / opened_element_size], ); for (j, &opened_value) in opened_row.iter().enumerate() { - tester.write_cell(address_space, row_pointer + j, opened_value); + tester.write(address_space, row_pointer + j, [opened_value]); } } streams @@ -226,7 +228,7 @@ fn test(cases: [Case; N]) { .push(proof.iter().flatten().copied().collect()); drop(streams); for (i, &bit) in sibling_is_on_right.iter().enumerate() { - tester.write_cell(address_space, index_base_pointer + i, F::from_bool(bit)); + tester.write(address_space, index_base_pointer + i, [F::from_bool(bit)]); } tester.write(address_space, commit_pointer, commit); @@ -385,12 +387,13 @@ fn tester_with_random_poseidon2_ops(num_ops: usize) -> VmChipTester::new( + let mut chip = new_native_poseidon2_chip::( tester.system_port(), - tester.offline_memory_mutex_arc(), Poseidon2Config::default(), VERIFY_BATCH_BUS, streams.clone(), + MAX_INS_CAPACITY, + tester.memory_helper(), ); let mut rng = create_seeded_rng(); @@ -417,12 +420,12 @@ fn tester_with_random_poseidon2_ops(num_ops: usize) -> VmChipTester ChipUsageGetter - for NativePoseidon2Chip -{ - fn air_name(&self) -> String { - "VerifyBatchAir".to_string() - } - - fn current_trace_height(&self) -> usize { - self.height - } - - fn trace_width(&self) -> usize { - NativePoseidon2Cols::::width() - } -} - -impl NativePoseidon2Chip { - fn generate_subair_cols(&self, input: [F; 2 * CHUNK], cols: &mut [F]) { - let inner_trace = self.subchip.generate_trace(vec![input]); - let inner_width = self.air.subair.width(); - cols[..inner_width].copy_from_slice(inner_trace.values.as_slice()); - } - #[allow(clippy::too_many_arguments)] - fn incorporate_sibling_record_to_row( - &self, - record: &IncorporateSiblingRecord, - aux_cols_factory: &MemoryAuxColsFactory, - slice: &mut [F], - memory: &OfflineMemory, - parent: &VerifyBatchRecord, - proof_index: usize, - opened_index: usize, - log_height: usize, - ) { - let &IncorporateSiblingRecord { - read_sibling_is_on_right, - sibling_is_on_right, - p2_input, - } = record; - - let read_sibling_is_on_right = memory.record_by_id(read_sibling_is_on_right); - - self.generate_subair_cols(p2_input, slice); - let cols: &mut NativePoseidon2Cols = slice.borrow_mut(); - cols.incorporate_row = F::ZERO; - cols.incorporate_sibling = F::ONE; - cols.inside_row = F::ZERO; - cols.simple = F::ZERO; - cols.end_inside_row = F::ZERO; - cols.end_top_level = F::ZERO; - cols.start_top_level = F::ZERO; - cols.opened_element_size_inv = parent.opened_element_size_inv(); - cols.very_first_timestamp = F::from_canonical_u32(parent.from_state.timestamp); - cols.start_timestamp = - F::from_canonical_u32(read_sibling_is_on_right.timestamp - NUM_INITIAL_READS as u32); - - let specific: &mut TopLevelSpecificCols = - cols.specific[..TopLevelSpecificCols::::width()].borrow_mut(); - - specific.end_timestamp = - F::from_canonical_usize(read_sibling_is_on_right.timestamp as usize + 1); - cols.initial_opened_index = F::from_canonical_usize(opened_index); - specific.final_opened_index = F::from_canonical_usize(opened_index - 1); - specific.log_height = F::from_canonical_usize(log_height); - specific.opened_length = F::from_canonical_usize(parent.opened_length); - specific.dim_base_pointer = parent.dim_base_pointer; - cols.opened_base_pointer = parent.opened_base_pointer; - specific.index_base_pointer = parent.index_base_pointer; - - specific.proof_index = F::from_canonical_usize(proof_index); - aux_cols_factory.generate_read_aux( - read_sibling_is_on_right, - &mut specific.read_initial_height_or_sibling_is_on_right, - ); - specific.sibling_is_on_right = F::from_bool(sibling_is_on_right); - } - fn correct_last_top_level_row( - &self, - record: &VerifyBatchRecord, - aux_cols_factory: &MemoryAuxColsFactory, - slice: &mut [F], - memory: &OfflineMemory, - ) { - let &VerifyBatchRecord { - from_state, - commit_pointer, - dim_base_pointer_read, - opened_base_pointer_read, - opened_length_read, - index_base_pointer_read, - commit_pointer_read, - commit_read, - .. - } = record; - let instruction = &record.instruction; - let cols: &mut NativePoseidon2Cols = slice.borrow_mut(); - cols.end_top_level = F::ONE; - - let specific: &mut TopLevelSpecificCols = - cols.specific[..TopLevelSpecificCols::::width()].borrow_mut(); - - specific.pc = F::from_canonical_u32(from_state.pc); - specific.dim_register = instruction.a; - specific.opened_register = instruction.b; - specific.opened_length_register = instruction.c; - specific.proof_id = instruction.d; - specific.index_register = instruction.e; - specific.commit_register = instruction.f; - specific.commit_pointer = commit_pointer; - aux_cols_factory.generate_read_aux( - memory.record_by_id(dim_base_pointer_read), - &mut specific.dim_base_pointer_read, - ); - aux_cols_factory.generate_read_aux( - memory.record_by_id(opened_base_pointer_read), - &mut specific.opened_base_pointer_read, - ); - aux_cols_factory.generate_read_aux( - memory.record_by_id(opened_length_read), - &mut specific.opened_length_read, - ); - aux_cols_factory.generate_read_aux( - memory.record_by_id(index_base_pointer_read), - &mut specific.index_base_pointer_read, - ); - aux_cols_factory.generate_read_aux( - memory.record_by_id(commit_pointer_read), - &mut specific.commit_pointer_read, - ); - aux_cols_factory - .generate_read_aux(memory.record_by_id(commit_read), &mut specific.commit_read); - } - #[allow(clippy::too_many_arguments)] - fn incorporate_row_record_to_row( - &self, - record: &IncorporateRowRecord, - aux_cols_factory: &MemoryAuxColsFactory, - slice: &mut [F], - memory: &OfflineMemory, - parent: &VerifyBatchRecord, - proof_index: usize, - log_height: usize, - ) { - let &IncorporateRowRecord { - initial_opened_index, - final_opened_index, - initial_height_read, - final_height_read, - p2_input, - .. - } = record; - - let initial_height_read = memory.record_by_id(initial_height_read); - let final_height_read = memory.record_by_id(final_height_read); - - self.generate_subair_cols(p2_input, slice); - let cols: &mut NativePoseidon2Cols = slice.borrow_mut(); - cols.incorporate_row = F::ONE; - cols.incorporate_sibling = F::ZERO; - cols.inside_row = F::ZERO; - cols.simple = F::ZERO; - cols.end_inside_row = F::ZERO; - cols.end_top_level = F::ZERO; - cols.start_top_level = F::from_bool(proof_index == 0); - cols.opened_element_size_inv = parent.opened_element_size_inv(); - cols.very_first_timestamp = F::from_canonical_u32(parent.from_state.timestamp); - cols.start_timestamp = F::from_canonical_u32( - memory - .record_by_id( - record.chunks[0].cells[0] - .read_row_pointer_and_length - .unwrap(), - ) - .timestamp - - NUM_INITIAL_READS as u32, - ); - let specific: &mut TopLevelSpecificCols = - cols.specific[..TopLevelSpecificCols::::width()].borrow_mut(); - - specific.end_timestamp = F::from_canonical_u32(final_height_read.timestamp + 1); - - cols.initial_opened_index = F::from_canonical_usize(initial_opened_index); - specific.final_opened_index = F::from_canonical_usize(final_opened_index); - specific.log_height = F::from_canonical_usize(log_height); - specific.opened_length = F::from_canonical_usize(parent.opened_length); - specific.dim_base_pointer = parent.dim_base_pointer; - cols.opened_base_pointer = parent.opened_base_pointer; - specific.index_base_pointer = parent.index_base_pointer; - - specific.proof_index = F::from_canonical_usize(proof_index); - aux_cols_factory.generate_read_aux( - initial_height_read, - &mut specific.read_initial_height_or_sibling_is_on_right, - ); - aux_cols_factory.generate_read_aux(final_height_read, &mut specific.read_final_height); - } - #[allow(clippy::too_many_arguments)] - fn inside_row_record_to_row( - &self, - record: &InsideRowRecord, - aux_cols_factory: &MemoryAuxColsFactory, - slice: &mut [F], - memory: &OfflineMemory, - parent: &IncorporateRowRecord, - grandparent: &VerifyBatchRecord, - is_last: bool, - ) { - let InsideRowRecord { cells, p2_input } = record; - - self.generate_subair_cols(*p2_input, slice); - let cols: &mut NativePoseidon2Cols = slice.borrow_mut(); - cols.incorporate_row = F::ZERO; - cols.incorporate_sibling = F::ZERO; - cols.inside_row = F::ONE; - cols.simple = F::ZERO; - cols.end_inside_row = F::from_bool(is_last); - cols.end_top_level = F::ZERO; - cols.opened_element_size_inv = grandparent.opened_element_size_inv(); - cols.very_first_timestamp = F::from_canonical_u32( - memory - .record_by_id( - parent.chunks[0].cells[0] - .read_row_pointer_and_length - .unwrap(), - ) - .timestamp, - ); - cols.start_timestamp = - F::from_canonical_u32(memory.record_by_id(cells[0].read).timestamp - 1); - let specific: &mut InsideRowSpecificCols = - cols.specific[..InsideRowSpecificCols::::width()].borrow_mut(); - - for (record, cell) in cells.iter().zip(specific.cells.iter_mut()) { - let &CellRecord { - read, - opened_index, - read_row_pointer_and_length, - row_pointer, - row_end, - } = record; - aux_cols_factory.generate_read_aux(memory.record_by_id(read), &mut cell.read); - cell.opened_index = F::from_canonical_usize(opened_index); - if let Some(read_row_pointer_and_length) = read_row_pointer_and_length { - aux_cols_factory.generate_read_aux( - memory.record_by_id(read_row_pointer_and_length), - &mut cell.read_row_pointer_and_length, - ); - } - cell.row_pointer = F::from_canonical_usize(row_pointer); - cell.row_end = F::from_canonical_usize(row_end); - cell.is_first_in_row = F::from_bool(read_row_pointer_and_length.is_some()); - } - - for cell in specific.cells.iter_mut().skip(cells.len()) { - cell.opened_index = F::from_canonical_usize(parent.final_opened_index); - } - - cols.is_exhausted = std::array::from_fn(|i| F::from_bool(i + 1 >= cells.len())); - - cols.initial_opened_index = F::from_canonical_usize(parent.initial_opened_index); - cols.opened_base_pointer = grandparent.opened_base_pointer; - } - // returns number of used cells - fn verify_batch_record_to_rows( - &self, - record: &VerifyBatchRecord, - aux_cols_factory: &MemoryAuxColsFactory, - slice: &mut [F], - memory: &OfflineMemory, - ) -> usize { - let width = NativePoseidon2Cols::::width(); - let mut used_cells = 0; - - let mut opened_index = 0; - for (proof_index, top_level) in record.top_level.iter().enumerate() { - let log_height = record.initial_log_height - proof_index; - if let Some(incorporate_row) = &top_level.incorporate_row { - self.incorporate_row_record_to_row( - incorporate_row, - aux_cols_factory, - &mut slice[used_cells..used_cells + width], - memory, - record, - proof_index, - log_height, - ); - opened_index = incorporate_row.final_opened_index + 1; - used_cells += width; - } - if let Some(incorporate_sibling) = &top_level.incorporate_sibling { - self.incorporate_sibling_record_to_row( - incorporate_sibling, - aux_cols_factory, - &mut slice[used_cells..used_cells + width], - memory, - record, - proof_index, - opened_index, - log_height, - ); - used_cells += width; - } - } - self.correct_last_top_level_row( - record, - aux_cols_factory, - &mut slice[used_cells - width..used_cells], - memory, - ); - - for top_level in record.top_level.iter() { - if let Some(incorporate_row) = &top_level.incorporate_row { - for (i, chunk) in incorporate_row.chunks.iter().enumerate() { - self.inside_row_record_to_row( - chunk, - aux_cols_factory, - &mut slice[used_cells..used_cells + width], - memory, - incorporate_row, - record, - i == incorporate_row.chunks.len() - 1, - ); - used_cells += width; - } - } - } - - used_cells - } - fn simple_record_to_row( - &self, - record: &SimplePoseidonRecord, - aux_cols_factory: &MemoryAuxColsFactory, - slice: &mut [F], - memory: &OfflineMemory, - ) { - let &SimplePoseidonRecord { - from_state, - instruction: - Instruction { - opcode, - a: output_register, - b: input_register_1, - c: input_register_2, - .. - }, - read_input_pointer_1, - read_input_pointer_2, - read_output_pointer, - read_data_1, - read_data_2, - write_data_1, - write_data_2, - input_pointer_1, - input_pointer_2, - output_pointer, - p2_input, - } = record; - - let read_input_pointer_1 = memory.record_by_id(read_input_pointer_1); - let read_output_pointer = memory.record_by_id(read_output_pointer); - let read_data_1 = memory.record_by_id(read_data_1); - let read_data_2 = memory.record_by_id(read_data_2); - let write_data_1 = memory.record_by_id(write_data_1); - - self.generate_subair_cols(p2_input, slice); - let cols: &mut NativePoseidon2Cols = slice.borrow_mut(); - cols.incorporate_row = F::ZERO; - cols.incorporate_sibling = F::ZERO; - cols.inside_row = F::ZERO; - cols.simple = F::ONE; - cols.end_inside_row = F::ZERO; - cols.end_top_level = F::ZERO; - cols.is_exhausted = [F::ZERO; CHUNK - 1]; - - cols.start_timestamp = F::from_canonical_u32(from_state.timestamp); - let specific: &mut SimplePoseidonSpecificCols = - cols.specific[..SimplePoseidonSpecificCols::::width()].borrow_mut(); - - specific.pc = F::from_canonical_u32(from_state.pc); - specific.is_compress = F::from_bool(opcode == COMP_POS2.global_opcode()); - specific.output_register = output_register; - specific.input_register_1 = input_register_1; - specific.input_register_2 = input_register_2; - specific.output_pointer = output_pointer; - specific.input_pointer_1 = input_pointer_1; - specific.input_pointer_2 = input_pointer_2; - aux_cols_factory.generate_read_aux(read_output_pointer, &mut specific.read_output_pointer); - aux_cols_factory - .generate_read_aux(read_input_pointer_1, &mut specific.read_input_pointer_1); - aux_cols_factory.generate_read_aux(read_data_1, &mut specific.read_data_1); - aux_cols_factory.generate_read_aux(read_data_2, &mut specific.read_data_2); - aux_cols_factory.generate_write_aux(write_data_1, &mut specific.write_data_1); - - if opcode == COMP_POS2.global_opcode() { - let read_input_pointer_2 = memory.record_by_id(read_input_pointer_2.unwrap()); - aux_cols_factory - .generate_read_aux(read_input_pointer_2, &mut specific.read_input_pointer_2); - } else { - let write_data_2 = memory.record_by_id(write_data_2.unwrap()); - aux_cols_factory.generate_write_aux(write_data_2, &mut specific.write_data_2); - } - } - - fn generate_trace(self) -> RowMajorMatrix { - let width = self.trace_width(); - let height = next_power_of_two_or_zero(self.height); - let mut flat_trace = F::zero_vec(width * height); - - let memory = self.offline_memory.lock().unwrap(); - - let aux_cols_factory = memory.aux_cols_factory(); - - let mut used_cells = 0; - for record in self.record_set.verify_batch_records.iter() { - used_cells += self.verify_batch_record_to_rows( - record, - &aux_cols_factory, - &mut flat_trace[used_cells..], - &memory, - ); - } - for record in self.record_set.simple_permute_records.iter() { - self.simple_record_to_row( - record, - &aux_cols_factory, - &mut flat_trace[used_cells..used_cells + width], - &memory, - ); - used_cells += width; - } - // poseidon2 constraints are always checked - // following can be optimized to only hash [0; _] once - flat_trace[used_cells..] - .par_chunks_mut(width) - .for_each(|row| { - self.generate_subair_cols([F::ZERO; 2 * CHUNK], row); - }); - - RowMajorMatrix::new(flat_trace, width) - } -} - -impl Chip - for NativePoseidon2Chip, SBOX_REGISTERS> -where - Val: PrimeField32, -{ - fn air(&self) -> AirRef { - Arc::new(self.air.clone()) - } - fn generate_air_proof_input(self) -> AirProofInput { - AirProofInput::simple_no_pis(self.generate_trace()) - } -} From e9bc13d38bf1676cbd81ffa47226a838b2f06a55 Mon Sep 17 00:00:00 2001 From: Golovanov399 Date: Sat, 24 May 2025 03:26:26 +0300 Subject: [PATCH 41/49] fix: Fix setting initial memory (#1690) We sometimes call `set_initial_memory` even with `Volatile` interface chip. Don't know if this alone is a good design, but inside this function we reset the tracing memory to use 8 as `initial_block_size` and then checked that in the volatile case the memory is empty, but the initial block size was already overwritten. This change fixes it. --- crates/vm/src/system/memory/controller/mod.rs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/vm/src/system/memory/controller/mod.rs b/crates/vm/src/system/memory/controller/mod.rs index 66d8cdde7c..6bc186e0b8 100644 --- a/crates/vm/src/system/memory/controller/mod.rs +++ b/crates/vm/src/system/memory/controller/mod.rs @@ -304,26 +304,26 @@ impl MemoryController { if self.timestamp() > INITIAL_TIMESTAMP + 1 { panic!("Cannot set initial memory after first timestamp"); } - - self.memory = TracingMemory::new( - &self.mem_config, - self.range_checker.clone(), - self.memory_bus, - CHUNK, - ) - .with_image(memory.clone(), self.mem_config.access_capacity); + if memory.is_empty() { + return; + } match &mut self.interface_chip { MemoryInterface::Volatile { .. } => { - assert!( - memory.is_empty(), - "Cannot set initial memory for volatile memory" - ); + panic!("Cannot set initial memory for volatile memory"); } MemoryInterface::Persistent { initial_memory, .. } => { - *initial_memory = memory; + *initial_memory = memory.clone(); } } + + self.memory = TracingMemory::new( + &self.mem_config, + self.range_checker.clone(), + self.memory_bus, + CHUNK, + ) + .with_image(memory, self.mem_config.access_capacity); } pub fn memory_bridge(&self) -> MemoryBridge { From c9c724048044d7ad52ce0f235200cebefc842fda Mon Sep 17 00:00:00 2001 From: Xinding Wei Date: Thu, 29 May 2025 23:41:28 -0700 Subject: [PATCH 42/49] refactor: VM Executor for E1/E2 (#1699) - Add `InterpretedInstance` for E1/E2. - Enforce `ExecutionControl` to be stateless. - `InterpretedInstance` E2 is not tested because context initialization is tricky. Will address in the future PR. close INT-4043 --------- Co-authored-by: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> --- crates/sdk/src/prover/vm/local.rs | 11 +- crates/vm/src/arch/execution_control.rs | 18 ++- crates/vm/src/arch/execution_mode/e1.rs | 12 +- .../arch/execution_mode/metered/bounded.rs | 11 ++ .../vm/src/arch/execution_mode/metered/mod.rs | 69 +++++------- .../src/arch/execution_mode/tracegen/mod.rs | 4 +- .../arch/execution_mode/tracegen/normal.rs | 24 ++-- .../execution_mode/tracegen/segmentation.rs | 33 +++--- crates/vm/src/arch/interpreter.rs | 106 ++++++++++++++++++ crates/vm/src/arch/mod.rs | 1 + crates/vm/src/arch/vm.rs | 52 ++++++--- crates/vm/tests/integration_test.rs | 86 +++++++++++++- 12 files changed, 327 insertions(+), 100 deletions(-) create mode 100644 crates/vm/src/arch/interpreter.rs diff --git a/crates/sdk/src/prover/vm/local.rs b/crates/sdk/src/prover/vm/local.rs index 85c604bb98..1e3f74b855 100644 --- a/crates/sdk/src/prover/vm/local.rs +++ b/crates/sdk/src/prover/vm/local.rs @@ -98,8 +98,15 @@ where match vm.executor.execute_and_then( exe.clone(), input.clone(), - |seg_idx, mut seg| { - final_memory = mem::take(&mut seg.ctrl.final_memory); + |seg_idx, seg| { + final_memory = Some( + seg.chip_complex + .memory_controller() + .memory + .data + .memory + .clone(), + ); let proof_input = info_span!("trace_gen", segment = seg_idx) .in_scope(|| seg.generate_proof_input(Some(committed_program.clone())))?; info_span!("prove_segment", segment = seg_idx) diff --git a/crates/vm/src/arch/execution_control.rs b/crates/vm/src/arch/execution_control.rs index 1be6a166fe..afd46373d8 100644 --- a/crates/vm/src/arch/execution_control.rs +++ b/crates/vm/src/arch/execution_control.rs @@ -4,6 +4,10 @@ use openvm_stark_backend::p3_field::PrimeField32; use super::{ExecutionError, VmChipComplex, VmConfig, VmSegmentState}; /// Trait for execution control, determining segmentation and stopping conditions +/// Invariants: +/// - `ExecutionControl` should be stateless. +/// - For E3/E4, `ExecutionControl` is for a specific execution and cannot be used for another +/// execution with different inputs or segmentation criteria. pub trait ExecutionControl where F: PrimeField32, @@ -12,30 +16,32 @@ where /// Host context type Ctx; + fn initialize_context(&self) -> Self::Ctx; + /// Determines if execution should suspend fn should_suspend( - &mut self, + &self, state: &mut VmSegmentState, chip_complex: &VmChipComplex, ) -> bool; /// Called before execution begins fn on_start( - &mut self, + &self, state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ); /// Called after suspend or terminate fn on_suspend_or_terminate( - &mut self, + &self, state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, exit_code: Option, ); fn on_suspend( - &mut self, + &self, state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ) { @@ -43,7 +49,7 @@ where } fn on_terminate( - &mut self, + &self, state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, exit_code: u32, @@ -54,7 +60,7 @@ where /// Execute a single instruction // TODO(ayush): change instruction to Instruction / PInstruction fn execute_instruction( - &mut self, + &self, state: &mut VmSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, diff --git a/crates/vm/src/arch/execution_mode/e1.rs b/crates/vm/src/arch/execution_mode/e1.rs index 2bb5a85a88..1e72d22ae8 100644 --- a/crates/vm/src/arch/execution_mode/e1.rs +++ b/crates/vm/src/arch/execution_mode/e1.rs @@ -26,8 +26,12 @@ where { type Ctx = E1Ctx; + fn initialize_context(&self) -> Self::Ctx { + () + } + fn should_suspend( - &mut self, + &self, state: &mut VmSegmentState, _chip_complex: &VmChipComplex, ) -> bool { @@ -39,14 +43,14 @@ where } fn on_start( - &mut self, + &self, _state: &mut VmSegmentState, _chip_complex: &mut VmChipComplex, ) { } fn on_suspend_or_terminate( - &mut self, + &self, _state: &mut VmSegmentState, _chip_complex: &mut VmChipComplex, _exit_code: Option, @@ -55,7 +59,7 @@ where /// Execute a single instruction fn execute_instruction( - &mut self, + &self, state: &mut VmSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, diff --git a/crates/vm/src/arch/execution_mode/metered/bounded.rs b/crates/vm/src/arch/execution_mode/metered/bounded.rs index 0a933a5493..882cc96f71 100644 --- a/crates/vm/src/arch/execution_mode/metered/bounded.rs +++ b/crates/vm/src/arch/execution_mode/metered/bounded.rs @@ -19,6 +19,8 @@ pub struct MeteredCtxBounded { // Indices of leaf nodes in the memory merkle tree pub leaf_indices: Vec, + pub clk_last_segment_check: u64, + pub segments: Vec, } impl MeteredCtxBounded { @@ -36,6 +38,8 @@ impl MeteredCtxBounded { as_byte_alignment_bits, memory_dimensions, leaf_indices: Vec::new(), + clk_last_segment_check: 0, + segments: Vec::new(), } } } @@ -181,3 +185,10 @@ fn calculate_merkle_node_updates( diff } + +#[derive(derive_new::new, Debug)] +pub struct Segment { + pub clk_start: u64, + pub num_cycles: u64, + pub trace_heights: Vec, +} diff --git a/crates/vm/src/arch/execution_mode/metered/mod.rs b/crates/vm/src/arch/execution_mode/metered/mod.rs index 389fb2c1ed..86cc944493 100644 --- a/crates/vm/src/arch/execution_mode/metered/mod.rs +++ b/crates/vm/src/arch/execution_mode/metered/mod.rs @@ -8,9 +8,10 @@ use openvm_stark_backend::{p3_field::PrimeField32, ChipUsageGetter}; use p3_baby_bear::BabyBear; use crate::arch::{ - execution_control::ExecutionControl, ChipId, ExecutionError, InsExecutorE1, VmChipComplex, - VmConfig, VmSegmentState, VmStateMut, CONNECTOR_AIR_ID, DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT, - DEFAULT_MAX_SEGMENT_LEN, PROGRAM_AIR_ID, PUBLIC_VALUES_AIR_ID, + execution_control::ExecutionControl, execution_mode::metered::bounded::Segment, ChipId, + ExecutionError, InsExecutorE1, VmChipComplex, VmConfig, VmSegmentState, VmStateMut, + CONNECTOR_AIR_ID, DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT, DEFAULT_MAX_SEGMENT_LEN, + PROGRAM_AIR_ID, PUBLIC_VALUES_AIR_ID, }; /// Check segment every 100 instructions. @@ -21,22 +22,11 @@ const MAX_TRACE_HEIGHT: u32 = DEFAULT_MAX_SEGMENT_LEN as u32; const MAX_TRACE_CELLS_PER_CHIP: usize = DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT; const MAX_INTERACTIONS: usize = BabyBear::ORDER_U32 as usize; -#[derive(derive_new::new, Debug)] -pub struct Segment { - pub clk_start: u64, - pub num_cycles: u64, - pub trace_heights: Vec, -} - pub struct MeteredExecutionControl<'a> { // Constants air_names: &'a [String], pub widths: &'a [usize], pub interactions: &'a [usize], - // State - // TODO(ayush): should probably be in metered ctx - pub clk_last_segment_check: u64, - pub segments: Vec, } impl<'a> MeteredExecutionControl<'a> { @@ -45,8 +35,6 @@ impl<'a> MeteredExecutionControl<'a> { air_names, widths, interactions, - clk_last_segment_check: 0, - segments: vec![], } } @@ -69,7 +57,7 @@ impl<'a> MeteredExecutionControl<'a> { .sum() } - fn should_segment(&mut self, state: &mut VmSegmentState) -> bool { + fn should_segment(&self, state: &mut VmSegmentState) -> bool { let trace_heights = state.ctx.trace_heights_if_finalized(); let max_trace_cells = MAX_TRACE_CELLS_PER_CHIP * trace_heights.len(); for (i, &height) in trace_heights.iter().enumerate() { @@ -77,8 +65,8 @@ impl<'a> MeteredExecutionControl<'a> { if padded_height > MAX_TRACE_HEIGHT { tracing::info!( "Segment {:2} | clk {:9} | chip {} ({}) height ({:8}) > max ({:8})", - self.segments.len(), - self.clk_last_segment_check, + state.ctx.segments.len(), + state.ctx.clk_last_segment_check, i, self.air_names[i], padded_height, @@ -92,8 +80,8 @@ impl<'a> MeteredExecutionControl<'a> { if total_cells > max_trace_cells { tracing::info!( "Segment {:2} | clk {:9} | total cells ({:10}) > max ({:10})", - self.segments.len(), - self.clk_last_segment_check, + state.ctx.segments.len(), + state.ctx.clk_last_segment_check, total_cells, max_trace_cells ); @@ -104,8 +92,8 @@ impl<'a> MeteredExecutionControl<'a> { if total_interactions > MAX_INTERACTIONS { tracing::info!( "Segment {:2} | clk {:9} | total interactions ({:11}) > max ({:11})", - self.segments.len(), - self.clk_last_segment_check, + state.ctx.segments.len(), + state.ctx.clk_last_segment_check, total_interactions, MAX_INTERACTIONS ); @@ -116,7 +104,7 @@ impl<'a> MeteredExecutionControl<'a> { } fn reset_segment( - &mut self, + &self, state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ) where @@ -169,7 +157,7 @@ impl<'a> MeteredExecutionControl<'a> { } fn check_segment_limits( - &mut self, + &self, state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ) where @@ -177,27 +165,27 @@ impl<'a> MeteredExecutionControl<'a> { VC: VmConfig, { // Avoid checking segment too often. - if state.clk < self.clk_last_segment_check + SEGMENT_CHECK_INTERVAL { + if state.clk < state.ctx.clk_last_segment_check + SEGMENT_CHECK_INTERVAL { return; } if self.should_segment(state) { - let clk_start = self + let clk_start = state + .ctx .segments .last() .map_or(0, |s| s.clk_start + s.num_cycles); let segment = Segment { clk_start, - num_cycles: self.clk_last_segment_check - clk_start, + num_cycles: state.ctx.clk_last_segment_check - clk_start, // TODO(ayush): this is trace heights after overflow so an overestimate trace_heights: state.ctx.trace_heights.clone(), }; - self.segments.push(segment); - + state.ctx.segments.push(segment); self.reset_segment::(state, chip_complex); } - self.clk_last_segment_check = state.clk; + state.ctx.clk_last_segment_check = state.clk; } } @@ -209,8 +197,12 @@ where { type Ctx = MeteredCtx; + fn initialize_context(&self) -> Self::Ctx { + todo!() + } + fn should_suspend( - &mut self, + &self, _state: &mut VmSegmentState, _chip_complex: &VmChipComplex, ) -> bool { @@ -218,7 +210,7 @@ where } fn on_start( - &mut self, + &self, state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ) { @@ -226,7 +218,7 @@ where } fn on_suspend_or_terminate( - &mut self, + &self, state: &mut VmSegmentState, _chip_complex: &mut VmChipComplex, _exit_code: Option, @@ -235,11 +227,12 @@ where tracing::info!( "Segment {:2} | clk {:9} | terminated", - self.segments.len(), + state.ctx.segments.len(), state.clk, ); // Add the last segment - let clk_start = self + let clk_start = state + .ctx .segments .last() .map_or(0, |s| s.clk_start + s.num_cycles); @@ -249,12 +242,12 @@ where // TODO(ayush): this is trace heights after overflow so an overestimate trace_heights: state.ctx.trace_heights.clone(), }; - self.segments.push(segment); + state.ctx.segments.push(segment); } /// Execute a single instruction fn execute_instruction( - &mut self, + &self, state: &mut VmSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, diff --git a/crates/vm/src/arch/execution_mode/tracegen/mod.rs b/crates/vm/src/arch/execution_mode/tracegen/mod.rs index ea03d7966d..416c6a0910 100644 --- a/crates/vm/src/arch/execution_mode/tracegen/mod.rs +++ b/crates/vm/src/arch/execution_mode/tracegen/mod.rs @@ -4,4 +4,6 @@ mod segmentation; pub use normal::TracegenExecutionControl; pub use segmentation::TracegenExecutionControlWithSegmentation; -pub type TracegenCtx = (); +pub struct TracegenCtx { + pub since_last_segment_check: usize, +} diff --git a/crates/vm/src/arch/execution_mode/tracegen/normal.rs b/crates/vm/src/arch/execution_mode/tracegen/normal.rs index c2331caa85..ee5d274a6f 100644 --- a/crates/vm/src/arch/execution_mode/tracegen/normal.rs +++ b/crates/vm/src/arch/execution_mode/tracegen/normal.rs @@ -6,7 +6,7 @@ use crate::{ execution_control::ExecutionControl, ExecutionError, ExecutionState, InstructionExecutor, VmChipComplex, VmConfig, VmSegmentState, }, - system::memory::{MemoryImage, INITIAL_TIMESTAMP}, + system::memory::INITIAL_TIMESTAMP, }; pub type TracegenCtx = (); @@ -15,16 +15,11 @@ pub type TracegenCtx = (); pub struct TracegenExecutionControl { // State pub clk_end: u64, - // TODO(ayush): do we need this if only executing one segment? - pub final_memory: Option, } impl TracegenExecutionControl { pub fn new(clk_end: u64) -> Self { - Self { - clk_end, - final_memory: None, - } + Self { clk_end } } } @@ -35,8 +30,12 @@ where { type Ctx = TracegenCtx; + fn initialize_context(&self) -> Self::Ctx { + () + } + fn should_suspend( - &mut self, + &self, state: &mut VmSegmentState, _chip_complex: &VmChipComplex, ) -> bool { @@ -44,7 +43,7 @@ where } fn on_start( - &mut self, + &self, state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ) { @@ -54,14 +53,11 @@ where } fn on_suspend_or_terminate( - &mut self, + &self, state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, exit_code: Option, ) { - // TODO(ayush): this should ideally not be here - self.final_memory = Some(chip_complex.base.memory_controller.memory_image().clone()); - let timestamp = chip_complex.memory_controller().timestamp(); chip_complex .connector_chip_mut() @@ -70,7 +66,7 @@ where /// Execute a single instruction fn execute_instruction( - &mut self, + &self, state: &mut VmSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, diff --git a/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs b/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs index f193dc2059..a12c0eb368 100644 --- a/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs +++ b/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs @@ -18,18 +18,11 @@ const SEGMENT_CHECK_INTERVAL: usize = 100; pub struct TracegenExecutionControlWithSegmentation { // Constant air_names: Vec, - // State - pub since_last_segment_check: usize, - pub final_memory: Option, } impl TracegenExecutionControlWithSegmentation { pub fn new(air_names: Vec) -> Self { - Self { - since_last_segment_check: 0, - air_names, - final_memory: None, - } + Self { air_names } } } @@ -40,17 +33,22 @@ where { type Ctx = TracegenCtx; + fn initialize_context(&self) -> Self::Ctx { + Self::Ctx { + since_last_segment_check: 0, + } + } fn should_suspend( - &mut self, - _state: &mut VmSegmentState, + &self, + state: &mut VmSegmentState, chip_complex: &VmChipComplex, ) -> bool { // Avoid checking segment too often. - if self.since_last_segment_check != SEGMENT_CHECK_INTERVAL { - self.since_last_segment_check += 1; + if state.ctx.since_last_segment_check != SEGMENT_CHECK_INTERVAL { + state.ctx.since_last_segment_check += 1; return false; } - self.since_last_segment_check = 0; + state.ctx.since_last_segment_check = 0; chip_complex.config().segmentation_strategy.should_segment( &self.air_names, &chip_complex.dynamic_trace_heights().collect::>(), @@ -59,7 +57,7 @@ where } fn on_start( - &mut self, + &self, state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ) { @@ -69,14 +67,11 @@ where } fn on_suspend_or_terminate( - &mut self, + &self, state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, exit_code: Option, ) { - // TODO(ayush): this should ideally not be here - self.final_memory = Some(chip_complex.base.memory_controller.memory_image().clone()); - let timestamp = chip_complex.memory_controller().timestamp(); chip_complex .connector_chip_mut() @@ -85,7 +80,7 @@ where /// Execute a single instruction fn execute_instruction( - &mut self, + &self, state: &mut VmSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, diff --git a/crates/vm/src/arch/interpreter.rs b/crates/vm/src/arch/interpreter.rs new file mode 100644 index 0000000000..3268b5492b --- /dev/null +++ b/crates/vm/src/arch/interpreter.rs @@ -0,0 +1,106 @@ +use openvm_instructions::{exe::VmExe, program::Program, LocalOpcode, SystemOpcode}; +use openvm_stark_backend::p3_field::{Field, PrimeField32}; + +use crate::{ + arch::{ + execution_control::ExecutionControl, execution_mode::E1E2ExecutionCtx, ExecutionError, + Streams, VmChipComplex, VmConfig, VmSegmentState, + }, + system::memory::{online::GuestMemory, AddressMap}, +}; + +/// VM pure executor(E1/E2 executor) which doesn't consider trace generation. +/// Note: This executor doesn't hold any VM state and can be used for multiple execution. +pub struct InterpretedInstance> { + exe: VmExe, + vm_config: VC, +} + +impl> InterpretedInstance { + pub fn new(vm_config: VC, exe: impl Into>) -> Self { + let exe = exe.into(); + Self { exe, vm_config } + } + + /// Execute the VM program with the given execution control and inputs. Returns the final VM + /// state with the `ExecutionControl` context. + pub fn execute>( + &self, + ctrl: CTRL, + inputs: impl Into>, + ) -> Result, ExecutionError> + where + CTRL::Ctx: E1E2ExecutionCtx, + { + // Initialize the chip complex + let mut chip_complex = self.vm_config.create_chip_complex().unwrap(); + let inputs = inputs.into(); + chip_complex.set_streams(inputs); + // Initialize the memory + let memory = if self.vm_config.system().continuation_enabled { + let mem_config = self.vm_config.system().memory_config; + Some(GuestMemory::new(AddressMap::from_sparse( + mem_config.as_offset, + 1 << mem_config.as_height, + 1 << mem_config.pointer_max_bits, + self.exe.init_memory.clone(), + ))) + } else { + Some(GuestMemory::new(Default::default())) + }; + + // Initialize the context + let ctx = ctrl.initialize_context(); + let mut vm_state = VmSegmentState { + clk: 0, + pc: self.exe.pc_start, + memory, + exit_code: None, + ctx, + }; + + // Start execution + ctrl.on_start(&mut vm_state, &mut chip_complex); + let program = &self.exe.program; + + loop { + if ctrl.should_suspend(&mut vm_state, &chip_complex) { + ctrl.on_suspend(&mut vm_state, &mut chip_complex); + } + + // Fetch the next instruction + let pc = vm_state.pc; + let pc_index = get_pc_index(program, vm_state.pc)?; + let (inst, _) = program.get_instruction_and_debug_info(pc_index).ok_or( + ExecutionError::PcNotFound { + pc, + step: program.step, + pc_base: program.pc_base, + program_len: program.len(), + }, + )?; + if inst.opcode == SystemOpcode::TERMINATE.global_opcode() { + let exit_code = inst.c.as_canonical_u32(); + vm_state.exit_code = Some(exit_code); + ctrl.on_terminate(&mut vm_state, &mut chip_complex, exit_code); + return Ok(vm_state); + } + ctrl.execute_instruction(&mut vm_state, inst, &mut chip_complex)?; + } + } +} + +fn get_pc_index(program: &Program, pc: u32) -> Result { + let step = program.step; + let pc_base = program.pc_base; + let pc_index = ((pc - pc_base) / step) as usize; + if !(0..program.len()).contains(&pc_index) { + return Err(ExecutionError::PcOutOfBounds { + pc, + step, + pc_base, + program_len: program.len(), + }); + } + Ok(pc_index) +} diff --git a/crates/vm/src/arch/mod.rs b/crates/vm/src/arch/mod.rs index fdb2b7e49a..e34d65af06 100644 --- a/crates/vm/src/arch/mod.rs +++ b/crates/vm/src/arch/mod.rs @@ -19,6 +19,7 @@ pub mod vm; pub use openvm_instructions as instructions; pub mod hasher; +pub mod interpreter; /// Testing framework #[cfg(any(test, feature = "test-utils"))] pub mod testing; diff --git a/crates/vm/src/arch/vm.rs b/crates/vm/src/arch/vm.rs index bcdf433acd..5ac316b6c8 100644 --- a/crates/vm/src/arch/vm.rs +++ b/crates/vm/src/arch/vm.rs @@ -20,18 +20,19 @@ use thiserror::Error; use tracing::info_span; use super::{ - execution_mode::{metered::Segment, tracegen::TracegenExecutionControlWithSegmentation}, - ExecutionError, InsExecutorE1, VmChipComplex, VmComplexTraceHeights, VmConfig, - VmInventoryError, CONNECTOR_AIR_ID, MERKLE_AIR_ID, PROGRAM_AIR_ID, PROGRAM_CACHED_TRACE_INDEX, + execution_mode::tracegen::TracegenExecutionControlWithSegmentation, ExecutionError, + InsExecutorE1, VmChipComplex, VmComplexTraceHeights, VmConfig, VmInventoryError, + CONNECTOR_AIR_ID, MERKLE_AIR_ID, PROGRAM_AIR_ID, PROGRAM_CACHED_TRACE_INDEX, }; #[cfg(feature = "bench-metrics")] use crate::metrics::VmMetrics; use crate::{ arch::{ + execution_control::ExecutionControl, execution_mode::{ e1::E1ExecutionControl, - metered::{MeteredCtx, MeteredExecutionControl}, - tracegen::TracegenExecutionControl, + metered::{bounded::Segment, MeteredCtx, MeteredExecutionControl}, + tracegen::{TracegenCtx, TracegenExecutionControl}, }, hasher::poseidon2::vm_poseidon2_hasher, VmSegmentExecutor, VmSegmentState, @@ -256,6 +257,7 @@ where ) .unwrap(); let ctrl = TracegenExecutionControlWithSegmentation::new(chip_complex.air_names()); + let ctx = ExecutionControl::::initialize_context(&ctrl); let mut segment = VmSegmentExecutor::new( chip_complex, self.trace_height_constraints.clone(), @@ -271,7 +273,7 @@ where segment.set_override_trace_heights(overridden_heights.clone()); } - let mut exec_state = VmSegmentState::new(from_state.clk, from_state.pc, None, ()); + let mut exec_state = VmSegmentState::new(from_state.clk, from_state.pc, None, ctx); metrics_span("execute_time_ms", || { segment.execute_from_state(&mut exec_state) })?; @@ -298,7 +300,12 @@ where let metrics = segment.metrics.partial_take(); // TODO(ayush): this can probably be avoided - let memory = segment.ctrl.final_memory.as_ref().unwrap().clone(); + let memory = segment + .chip_complex + .base + .memory_controller + .memory_image() + .clone(); Ok(VmExecutorOneSegmentResult { segment, next_state: Some(VmState { @@ -328,7 +335,13 @@ where |err| err, )?; let last = last.expect("at least one segment must be executed"); - let final_memory = last.ctrl.final_memory; + let final_memory = Some( + last.chip_complex + .base + .memory_controller + .memory_image() + .clone(), + ); let end_state = last.chip_complex.connector_chip().boundary_states[1].expect("end state must be set"); if end_state.is_terminate != 1 { @@ -507,7 +520,7 @@ where None => return Err(ExecutionError::DidNotTerminate), }; - Ok(executor.ctrl.segments) + todo!("record segments") } pub fn execute_and_generate( @@ -569,8 +582,14 @@ where .pc ); - // TODO(ayush): avoid cloning - let final_memory = segment.ctrl.final_memory.clone(); + let final_memory = Some( + segment + .chip_complex + .base + .memory_controller + .memory_image() + .clone(), + ); let proof_input = tracing::info_span!("generate_proof_input") .in_scope(|| segment.generate_proof_input(None))?; @@ -615,7 +634,7 @@ where |seg_idx, mut seg| { // Note: this will only be Some on the last segment; otherwise it is // already moved into next segment state - final_memory = mem::take(&mut seg.ctrl.final_memory); + final_memory = Some(seg.chip_complex.memory_controller().memory_image().clone()); tracing::info_span!("trace_gen", segment = seg_idx) .in_scope(|| seg.generate_proof_input(committed_program.clone())) }, @@ -752,7 +771,14 @@ where segment.set_override_trace_heights(overridden_heights.clone()); } - let mut exec_state = VmSegmentState::new(0, exe.pc_start, None, ()); + let mut exec_state = VmSegmentState::new( + 0, + exe.pc_start, + None, + TracegenCtx { + since_last_segment_check: 0, + }, + ); metrics_span("execute_time_ms", || { segment.execute_from_state(&mut exec_state) })?; diff --git a/crates/vm/tests/integration_test.rs b/crates/vm/tests/integration_test.rs index 20d8f3f8e2..b41c2e6e36 100644 --- a/crates/vm/tests/integration_test.rs +++ b/crates/vm/tests/integration_test.rs @@ -8,8 +8,12 @@ use std::{ use openvm_circuit::{ arch::{ create_and_initialize_chip_complex, - execution_mode::tracegen::TracegenExecutionControlWithSegmentation, + execution_control::ExecutionControl, + execution_mode::{ + e1::E1ExecutionControl, tracegen::TracegenExecutionControlWithSegmentation, + }, hasher::{poseidon2::vm_poseidon2_hasher, Hasher}, + interpreter::InterpretedInstance, ChipId, MemoryConfig, SingleSegmentVmExecutor, SystemConfig, SystemTraceHeights, VirtualMachine, VmComplexTraceHeights, VmConfig, VmInventoryTraceHeights, VmSegmentExecutor, VmSegmentState, @@ -723,6 +727,7 @@ fn test_hint_load_1() { ) .unwrap(); let ctrl = TracegenExecutionControlWithSegmentation::new(chip_complex.air_names()); + let ctx = ExecutionControl::::initialize_context(&ctrl); let mut segment = VmSegmentExecutor::::new( chip_complex, vec![], @@ -730,7 +735,7 @@ fn test_hint_load_1() { ctrl, ); - let mut exec_state = VmSegmentState::new(0, 0, None, ()); + let mut exec_state = VmSegmentState::new(0, 0, None, ctx); segment.execute_from_state(&mut exec_state).unwrap(); let streams = segment.chip_complex.take_streams(); @@ -769,6 +774,7 @@ fn test_hint_load_2() { ) .unwrap(); let ctrl = TracegenExecutionControlWithSegmentation::new(chip_complex.air_names()); + let ctx = ExecutionControl::::initialize_context(&ctrl); let mut segment = VmSegmentExecutor::::new( chip_complex, vec![], @@ -776,7 +782,7 @@ fn test_hint_load_2() { ctrl, ); - let mut exec_state = VmSegmentState::new(0, 0, None, ()); + let mut exec_state = VmSegmentState::new(0, 0, None, ctx); segment.execute_from_state(&mut exec_state).unwrap(); let [read] = unsafe { @@ -796,3 +802,77 @@ fn test_hint_load_2() { vec![vec![F::ONE, F::TWO], vec![F::TWO, F::ONE]] ); } + +#[test] +fn test_vm_pure_execution_non_continuation() { + type F = BabyBear; + let n = 6; + /* + Instruction 0 assigns word[0]_4 to n. + Instruction 4 terminates + The remainder is a loop that decrements word[0]_4 until it reaches 0, then terminates. + Instruction 1 checks if word[0]_4 is 0 yet, and if so sets pc to 5 in order to terminate + Instruction 2 decrements word[0]_4 (using word[1]_4) + Instruction 3 uses JAL as a simple jump to go back to instruction 1 (repeating the loop). + */ + let instructions = vec![ + // word[0]_4 <- word[n]_0 + Instruction::large_from_isize(ADD.global_opcode(), 0, n, 0, 4, 0, 0, 0), + // if word[0]_4 == 0 then pc += 3 * DEFAULT_PC_STEP + Instruction::from_isize( + NativeBranchEqualOpcode(BEQ).global_opcode(), + 0, + 0, + 3 * DEFAULT_PC_STEP as isize, + 4, + 0, + ), + // word[0]_4 <- word[0]_4 - word[1]_4 + Instruction::large_from_isize(SUB.global_opcode(), 0, 0, 1, 4, 4, 0, 0), + // word[2]_4 <- pc + DEFAULT_PC_STEP, pc -= 2 * DEFAULT_PC_STEP + Instruction::from_isize( + JAL.global_opcode(), + 2, + -2 * DEFAULT_PC_STEP as isize, + 0, + 4, + 0, + ), + // terminate + Instruction::from_isize(TERMINATE.global_opcode(), 0, 0, 0, 0, 0), + ]; + + let program = Program::from_instructions(&instructions); + + let executor = InterpretedInstance::::new(test_native_config(), program); + executor + .execute(E1ExecutionControl::new(None), vec![]) + .expect("Failed to execute"); +} + +#[test] +fn test_vm_pure_execution_continuation() { + type F = BabyBear; + let instructions = vec![ + Instruction::large_from_isize(ADD.global_opcode(), 0, 0, 1, 4, 0, 0, 0), + Instruction::large_from_isize(ADD.global_opcode(), 1, 0, 2, 4, 0, 0, 0), + Instruction::large_from_isize(ADD.global_opcode(), 2, 0, 1, 4, 0, 0, 0), + Instruction::large_from_isize(ADD.global_opcode(), 3, 0, 2, 4, 0, 0, 0), + Instruction::large_from_isize(ADD.global_opcode(), 4, 0, 2, 4, 0, 0, 0), + Instruction::large_from_isize(ADD.global_opcode(), 5, 0, 1, 4, 0, 0, 0), + Instruction::large_from_isize(ADD.global_opcode(), 6, 0, 1, 4, 0, 0, 0), + Instruction::large_from_isize(ADD.global_opcode(), 7, 0, 2, 4, 0, 0, 0), + Instruction::from_isize(FE4ADD.global_opcode(), 8, 0, 4, 4, 4), + Instruction::from_isize(FE4ADD.global_opcode(), 8, 0, 4, 4, 4), + Instruction::from_isize(FE4SUB.global_opcode(), 12, 0, 4, 4, 4), + Instruction::from_isize(BBE4MUL.global_opcode(), 12, 0, 4, 4, 4), + Instruction::from_isize(BBE4DIV.global_opcode(), 12, 0, 4, 4, 4), + Instruction::from_isize(TERMINATE.global_opcode(), 0, 0, 0, 0, 0), + ]; + + let program = Program::from_instructions(&instructions); + let executor = InterpretedInstance::::new(test_native_continuations_config(), program); + executor + .execute(E1ExecutionControl::new(None), vec![]) + .expect("Failed to execute"); +} From 3f3147bfbd60daace5fbb1c7807c0f42aaa0e0bb Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Fri, 30 May 2025 13:41:36 +0200 Subject: [PATCH 43/49] fix(new-execution): use unpadded height for segmentation (#1698) - use unpadded height when calculating max height and total cells during segmentation - use default segmentation thresholds from reth benchmark --- .../src/arch/execution_mode/metered/exact.rs | 1 + .../vm/src/arch/execution_mode/metered/mod.rs | 19 ++++++++----------- extensions/rv32im/circuit/src/extension.rs | 2 +- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/crates/vm/src/arch/execution_mode/metered/exact.rs b/crates/vm/src/arch/execution_mode/metered/exact.rs index 7640960813..0acfc64ad3 100644 --- a/crates/vm/src/arch/execution_mode/metered/exact.rs +++ b/crates/vm/src/arch/execution_mode/metered/exact.rs @@ -84,6 +84,7 @@ impl MeteredCtxExact { } } + // TODO(ayush): fix this for native #[allow(clippy::type_complexity)] fn calculate_splits_and_merges( &self, diff --git a/crates/vm/src/arch/execution_mode/metered/mod.rs b/crates/vm/src/arch/execution_mode/metered/mod.rs index 86cc944493..94d74e3751 100644 --- a/crates/vm/src/arch/execution_mode/metered/mod.rs +++ b/crates/vm/src/arch/execution_mode/metered/mod.rs @@ -10,16 +10,15 @@ use p3_baby_bear::BabyBear; use crate::arch::{ execution_control::ExecutionControl, execution_mode::metered::bounded::Segment, ChipId, ExecutionError, InsExecutorE1, VmChipComplex, VmConfig, VmSegmentState, VmStateMut, - CONNECTOR_AIR_ID, DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT, DEFAULT_MAX_SEGMENT_LEN, - PROGRAM_AIR_ID, PUBLIC_VALUES_AIR_ID, + CONNECTOR_AIR_ID, PROGRAM_AIR_ID, PUBLIC_VALUES_AIR_ID, }; /// Check segment every 100 instructions. const SEGMENT_CHECK_INTERVAL: u64 = 100; // TODO(ayush): fix these values -const MAX_TRACE_HEIGHT: u32 = DEFAULT_MAX_SEGMENT_LEN as u32; -const MAX_TRACE_CELLS_PER_CHIP: usize = DEFAULT_MAX_CELLS_PER_CHIP_IN_SEGMENT; +const MAX_TRACE_HEIGHT: u32 = (1 << 23) - 100; +const MAX_TRACE_CELLS: usize = 1_200_000_000; // 1.2B const MAX_INTERACTIONS: usize = BabyBear::ORDER_U32 as usize; pub struct MeteredExecutionControl<'a> { @@ -43,7 +42,7 @@ impl<'a> MeteredExecutionControl<'a> { trace_heights .iter() .zip(self.widths) - .map(|(&height, &width)| height.next_power_of_two() as usize * width) + .map(|(&height, &width)| height as usize * width) .sum() } @@ -59,17 +58,15 @@ impl<'a> MeteredExecutionControl<'a> { fn should_segment(&self, state: &mut VmSegmentState) -> bool { let trace_heights = state.ctx.trace_heights_if_finalized(); - let max_trace_cells = MAX_TRACE_CELLS_PER_CHIP * trace_heights.len(); for (i, &height) in trace_heights.iter().enumerate() { - let padded_height = height.next_power_of_two(); - if padded_height > MAX_TRACE_HEIGHT { + if height > MAX_TRACE_HEIGHT { tracing::info!( "Segment {:2} | clk {:9} | chip {} ({}) height ({:8}) > max ({:8})", state.ctx.segments.len(), state.ctx.clk_last_segment_check, i, self.air_names[i], - padded_height, + height, MAX_TRACE_HEIGHT ); return true; @@ -77,13 +74,13 @@ impl<'a> MeteredExecutionControl<'a> { } let total_cells = self.calculate_total_cells(&trace_heights); - if total_cells > max_trace_cells { + if total_cells > MAX_TRACE_CELLS { tracing::info!( "Segment {:2} | clk {:9} | total cells ({:10}) > max ({:10})", state.ctx.segments.len(), state.ctx.clk_last_segment_check, total_cells, - max_trace_cells + MAX_TRACE_CELLS ); return true; } diff --git a/extensions/rv32im/circuit/src/extension.rs b/extensions/rv32im/circuit/src/extension.rs index 844840e1b3..9870f0a71b 100644 --- a/extensions/rv32im/circuit/src/extension.rs +++ b/extensions/rv32im/circuit/src/extension.rs @@ -25,7 +25,7 @@ use strum::IntoEnumIterator; use crate::{adapters::*, *}; // TODO(ayush): this should be decided after e2 execution -const MAX_INS_CAPACITY: usize = 1 << 22; +const MAX_INS_CAPACITY: usize = 1 << 23; /// Config for a VM with base extension and IO extension #[derive(Clone, Debug, VmConfig, derive_new::new, Serialize, Deserialize)] From 9b217c5c7fe4304656fe63071266f38f2bc19e0b Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Fri, 30 May 2025 13:21:57 -0700 Subject: [PATCH 44/49] fix: fixes to get e2e benchmark working (#1696) also fixes `openvm-circuit` integration tests - [x] fix `test_vm_hint` --------- Co-authored-by: Alexander Golovanov Co-authored-by: Ayush Shukla Co-authored-by: Arayi --- .github/workflows/benchmark-call.yml | 2 +- .github/workflows/cli.yml | 8 +- crates/sdk/src/prover/vm/local.rs | 9 +- crates/vm/src/arch/extensions.rs | 2 +- crates/vm/src/arch/vm.rs | 3 +- crates/vm/src/system/memory/adapter/mod.rs | 171 ++++-------------- .../src/system/memory/controller/interface.rs | 14 -- crates/vm/src/system/memory/controller/mod.rs | 159 +++------------- crates/vm/src/system/memory/merkle/mod.rs | 28 +-- crates/vm/src/system/memory/merkle/trace.rs | 4 +- crates/vm/src/system/memory/merkle/tree.rs | 31 +++- crates/vm/src/system/memory/online.rs | 4 - crates/vm/tests/integration_test.rs | 2 +- docs/specs/ISA.md | 2 +- examples/i256/src/main.rs | 2 + examples/keccak/src/main.rs | 2 + examples/sha256/src/main.rs | 2 + examples/u256/src/main.rs | 2 + .../src/adapters/alu_native_adapter.rs | 2 + extensions/native/circuit/src/adapters/mod.rs | 6 +- extensions/native/circuit/src/extension.rs | 2 +- extensions/rv32im/circuit/src/adapters/mod.rs | 25 +-- .../rv32im/circuit/src/hintstore/mod.rs | 15 +- 23 files changed, 134 insertions(+), 363 deletions(-) diff --git a/.github/workflows/benchmark-call.yml b/.github/workflows/benchmark-call.yml index 737e1c81ed..c6f9755457 100644 --- a/.github/workflows/benchmark-call.yml +++ b/.github/workflows/benchmark-call.yml @@ -121,7 +121,7 @@ jobs: - runs-on - runner=${{ inputs.instance_type }} - run-id=${{ github.run_id }} - - family=m7 + - family=r7 - tag=bench-${{ inputs.benchmark_name }}-${{ github.run_id }}-${{ github.run_number }}-${{ github.run_attempt }} - extras=s3-cache steps: diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 4602e9c05f..4150a1842e 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -38,7 +38,8 @@ jobs: cache-on-failure: true - uses: taiki-e/install-action@nextest - name: Install solc # svm should support arm64 linux - run: (hash svm 2>/dev/null || cargo install --version 0.2.23 svm-rs) && svm install 0.8.19 && solc --version + run: | + (hash svm 2>/dev/null || cargo install --version 0.2.23 svm-rs) && svm install 0.8.19 && solc --version - name: Install cargo-openvm working-directory: crates/cli @@ -58,6 +59,11 @@ jobs: run: | for dir in */; do if [ -f "${dir}Cargo.toml" ]; then + echo "Editing ${dir}Cargo.toml to use the current branch" + sed -i \ + -e "s|\(git = \"https://github.com/openvm-org/openvm.git\"\)|\1, branch = \"${{ github.head_ref }}\"|" \ + "${dir}Cargo.toml" + echo "Building ${dir%/}" cd "$dir" cargo openvm build && cargo openvm run diff --git a/crates/sdk/src/prover/vm/local.rs b/crates/sdk/src/prover/vm/local.rs index 1e3f74b855..9177a3092e 100644 --- a/crates/sdk/src/prover/vm/local.rs +++ b/crates/sdk/src/prover/vm/local.rs @@ -109,8 +109,13 @@ where ); let proof_input = info_span!("trace_gen", segment = seg_idx) .in_scope(|| seg.generate_proof_input(Some(committed_program.clone())))?; - info_span!("prove_segment", segment = seg_idx) - .in_scope(|| Ok(vm.engine.prove(&self.pk.vm_pk, proof_input))) + info_span!("prove_segment", segment = seg_idx).in_scope(|| { + let proof = vm.engine.prove(&self.pk.vm_pk, proof_input); + vm.engine + .verify(&self.pk.vm_pk.get_vk(), &proof) + .expect("verification failed"); + Ok(proof) + }) }, GenerationError::Execution, ) { diff --git a/crates/vm/src/arch/extensions.rs b/crates/vm/src/arch/extensions.rs index fd34a1815a..c48f26258a 100644 --- a/crates/vm/src/arch/extensions.rs +++ b/crates/vm/src/arch/extensions.rs @@ -592,7 +592,7 @@ impl SystemComplex { assert_eq!(inventory.executors().len(), Self::PV_EXECUTOR_IDX); // TODO(ayush): this should be decided after e2 execution - const MAX_INS_CAPACITY: usize = 1 << 22; + const MAX_INS_CAPACITY: usize = 1 << 23; let chip = PublicValuesChip::new( VmAirWrapper::new( NativeAdapterAir::new( diff --git a/crates/vm/src/arch/vm.rs b/crates/vm/src/arch/vm.rs index 5ac316b6c8..1fe0b56c95 100644 --- a/crates/vm/src/arch/vm.rs +++ b/crates/vm/src/arch/vm.rs @@ -479,7 +479,8 @@ where let num_access_adapters = executor .chip_complex .memory_controller() - .access_adapters + .memory + .access_adapter_inventory .num_access_adapters(); let ctx = MeteredCtx::new( widths.len(), diff --git a/crates/vm/src/system/memory/adapter/mod.rs b/crates/vm/src/system/memory/adapter/mod.rs index ea76117066..feb2947dbf 100644 --- a/crates/vm/src/system/memory/adapter/mod.rs +++ b/crates/vm/src/system/memory/adapter/mod.rs @@ -4,7 +4,8 @@ pub use air::*; pub use columns::*; use enum_dispatch::enum_dispatch; use openvm_circuit_primitives::{ - is_less_than::IsLtSubAir, var_range::SharedVariableRangeCheckerChip, TraceSubRowGenerator, + is_less_than::IsLtSubAir, utils::next_power_of_two_or_zero, + var_range::SharedVariableRangeCheckerChip, TraceSubRowGenerator, }; use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; use openvm_stark_backend::{ @@ -13,7 +14,6 @@ use openvm_stark_backend::{ p3_commit::PolynomialSpace, p3_field::PrimeField32, p3_matrix::{dense::RowMajorMatrix, Matrix}, - p3_util::log2_strict_usize, prover::types::AirProofInput, AirRef, Chip, ChipUsageGetter, }; @@ -64,39 +64,6 @@ impl AccessAdapterInventory { chip.set_override_trace_heights(oh); } } - pub fn add_record(&mut self, record: AccessAdapterRecord) { - let n = record.data.len(); - let idx = log2_strict_usize(n) - 1; - let chip = &mut self.chips[idx]; - debug_assert!(chip.n() == n); - chip.add_record(record); - } - - pub fn extend_records(&mut self, records: Vec>) { - for record in records { - self.add_record(record); - } - } - - pub fn set_trace(&mut self, index: usize, trace: Vec, width: usize) - where - F: PrimeField32, - { - let trace = RowMajorMatrix::new(trace, width); - self.chips[index].set_trace(trace); - } - - #[cfg(test)] - pub fn records_for_n(&self, n: usize) -> &[AccessAdapterRecord] { - let idx = log2_strict_usize(n) - 1; - let chip = &self.chips[idx]; - chip.records() - } - - #[cfg(test)] - pub fn total_records(&self) -> usize { - self.chips.iter().map(|chip| chip.records().len()).sum() - } pub fn get_heights(&self) -> Vec { self.chips @@ -160,12 +127,11 @@ impl AccessAdapterInventory { address: MemoryAddress, values: &[F], timestamp: u32, - row_slice: &mut [F], ) where F: PrimeField32, { let index = get_chip_index(values.len()); - self.chips[index].execute_split(address, values, timestamp, row_slice); + self.chips[index].execute_split(address, values, timestamp); } pub(crate) fn execute_merge( @@ -174,18 +140,11 @@ impl AccessAdapterInventory { values: &[F], left_timestamp: u32, right_timestamp: u32, - row_slice: &mut [F], ) where F: PrimeField32, { let index = get_chip_index(values.len()); - self.chips[index].execute_merge( - address, - values, - left_timestamp, - right_timestamp, - row_slice, - ); + self.chips[index].execute_merge(address, values, left_timestamp, right_timestamp); } } @@ -210,22 +169,13 @@ pub struct AccessAdapterRecord { #[enum_dispatch] pub trait GenericAccessAdapterChipTrait { fn set_override_trace_heights(&mut self, overridden_height: usize); - fn add_record(&mut self, record: AccessAdapterRecord); fn n(&self) -> usize; fn generate_trace(self) -> RowMajorMatrix - where - F: PrimeField32; - fn set_trace(&mut self, trace: RowMajorMatrix) where F: PrimeField32; - fn execute_split( - &mut self, - address: MemoryAddress, - values: &[F], - timestamp: u32, - row_slice: &mut [F], - ) where + fn execute_split(&mut self, address: MemoryAddress, values: &[F], timestamp: u32) + where F: PrimeField32; fn execute_merge( @@ -234,7 +184,6 @@ pub trait GenericAccessAdapterChipTrait { values: &[F], left_timestamp: u32, right_timestamp: u32, - row_slice: &mut [F], ) where F: PrimeField32; } @@ -268,24 +217,12 @@ impl GenericAccessAdapterChip { _ => panic!("Only supports N in (2, 4, 8, 16, 32)"), } } - - #[cfg(test)] - fn records(&self) -> &[AccessAdapterRecord] { - match &self { - GenericAccessAdapterChip::N2(chip) => &chip.records, - GenericAccessAdapterChip::N4(chip) => &chip.records, - GenericAccessAdapterChip::N8(chip) => &chip.records, - GenericAccessAdapterChip::N16(chip) => &chip.records, - GenericAccessAdapterChip::N32(chip) => &chip.records, - } - } } pub struct AccessAdapterChip { air: AccessAdapterAir, range_checker: SharedVariableRangeCheckerChip, - pub records: Vec>, - trace: RowMajorMatrix, + trace_cursor: Cursor>, overridden_height: Option, } @@ -299,8 +236,7 @@ impl AccessAdapterChip { Self { air: AccessAdapterAir:: { memory_bus, lt_air }, range_checker, - records: vec![], - trace: RowMajorMatrix::new(Vec::new(), 0), + trace_cursor: Cursor::new(Vec::new()), overridden_height: None, } } @@ -309,9 +245,6 @@ impl GenericAccessAdapterChipTrait for AccessAdapterChip) { - self.records.push(record); - } fn n(&self) -> usize { N } @@ -319,70 +252,36 @@ impl GenericAccessAdapterChipTrait for AccessAdapterChip= height, + "Overridden height {oh} is less than the required height {height}" + ); + oh + } else { + height + }; + let padded_height = next_power_of_two_or_zero(padded_height); + trace.pad_to_height(padded_height, F::ZERO); trace // TODO(AG): everything related to the calculated trace height // needs to be in memory controller, who owns these traces. - - // let width = BaseAir::::width(&self.air); - // let height = if let Some(oh) = self.overridden_height { - // assert!( - // oh >= self.records.len(), - // "Overridden height is less than the required height" - // ); - // oh - // } else { - // self.records.len() - // }; - // let height = next_power_of_two_or_zero(height); - // let mut values = F::zero_vec(height * width); - - // values - // .par_chunks_mut(width) - // .zip(self.records.into_par_iter()) - // .for_each(|(row, record)| { - // let row: &mut AccessAdapterCols = row.borrow_mut(); - - // row.is_valid = F::ONE; - // row.values = record.data.try_into().unwrap(); - // row.address = MemoryAddress::new(record.address_space, record.start_index); - - // let (left_timestamp, right_timestamp) = match record.kind { - // AccessAdapterRecordKind::Split => (record.timestamp, record.timestamp), - // AccessAdapterRecordKind::Merge { - // left_timestamp, - // right_timestamp, - // } => (left_timestamp, right_timestamp), - // }; - // debug_assert_eq!(max(left_timestamp, right_timestamp), record.timestamp); - - // row.left_timestamp = F::from_canonical_u32(left_timestamp); - // row.right_timestamp = F::from_canonical_u32(right_timestamp); - // row.is_split = F::from_bool(record.kind == AccessAdapterRecordKind::Split); - - // self.air.lt_air.generate_subrow( - // (self.range_checker.as_ref(), left_timestamp, right_timestamp), - // (&mut row.lt_aux, &mut row.is_right_larger), - // ); - // }); - // RowMajorMatrix::new(values, width) - } - - fn set_trace(&mut self, trace: RowMajorMatrix) { - self.trace = trace; } - fn execute_split( - &mut self, - address: MemoryAddress, - values: &[F], - timestamp: u32, - row_slice: &mut [F], - ) where + fn execute_split(&mut self, address: MemoryAddress, values: &[F], timestamp: u32) + where F: PrimeField32, { + let row_slice = { + let begin = self.trace_cursor.position() as usize; + let end = begin + self.trace_width(); + self.trace_cursor.get_mut().resize(end, F::ZERO); + self.trace_cursor.set_position(end as u64); + &mut self.trace_cursor.get_mut()[begin..end] + }; let row: &mut AccessAdapterCols = row_slice.borrow_mut(); row.is_valid = F::ONE; row.is_split = F::ONE; @@ -418,10 +317,16 @@ impl GenericAccessAdapterChipTrait for AccessAdapterChip = row_slice.borrow_mut(); row.is_valid = F::ONE; row.is_split = F::ZERO; @@ -471,7 +376,7 @@ impl ChipUsageGetter for AccessAdapterChip { } fn current_trace_height(&self) -> usize { - self.records.len() + self.trace_cursor.position() as usize / self.trace_width() } fn trace_width(&self) -> usize { diff --git a/crates/vm/src/system/memory/controller/interface.rs b/crates/vm/src/system/memory/controller/interface.rs index b00171a3c2..5a06e3cfbc 100644 --- a/crates/vm/src/system/memory/controller/interface.rs +++ b/crates/vm/src/system/memory/controller/interface.rs @@ -18,20 +18,6 @@ pub enum MemoryInterface { } impl MemoryInterface { - pub fn touch_range(&mut self, addr_space: u32, pointer: u32, len: u32) { - match self { - MemoryInterface::Volatile { .. } => {} - MemoryInterface::Persistent { - boundary_chip, - merkle_chip, - .. - } => { - boundary_chip.touch_range(addr_space, pointer, len); - merkle_chip.touch_range(addr_space, pointer, len); - } - } - } - pub fn compression_bus(&self) -> Option { match self { MemoryInterface::Volatile { .. } => None, diff --git a/crates/vm/src/system/memory/controller/mod.rs b/crates/vm/src/system/memory/controller/mod.rs index 6bc186e0b8..533572d0df 100644 --- a/crates/vm/src/system/memory/controller/mod.rs +++ b/crates/vm/src/system/memory/controller/mod.rs @@ -31,7 +31,6 @@ use super::{ use crate::{ arch::{hasher::HasherChip, MemoryConfig}, system::memory::{ - adapter::AccessAdapterInventory, dimensions::MemoryDimensions, merkle::{MemoryMerkleChip, SerialReceiver}, offline_checker::{MemoryBaseAuxCols, MemoryBridge, MemoryBus, AUX_LEN}, @@ -51,10 +50,6 @@ pub const MERKLE_AIR_OFFSET: usize = 1; /// The offset of the boundary AIR in AIRs of MemoryController. pub const BOUNDARY_AIR_OFFSET: usize = 0; -#[repr(C)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub struct RecordId(pub usize); - pub type MemoryImage = AddressMap; #[repr(C)] @@ -92,7 +87,6 @@ pub struct MemoryController { range_checker_bus: VariableRangeCheckerBus, // addr_space -> Memory data structure pub memory: TracingMemory, - pub access_adapters: AccessAdapterInventory, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -215,12 +209,6 @@ impl MemoryController { ), }, memory: TracingMemory::new(&mem_config, range_checker.clone(), memory_bus, 1), - access_adapters: AccessAdapterInventory::new( - range_checker.clone(), - memory_bus, - mem_config.clk_max_bits, - mem_config.max_access_adapter_n, - ), range_checker, range_checker_bus, } @@ -259,12 +247,6 @@ impl MemoryController { interface_chip, memory: TracingMemory::new(&mem_config, range_checker.clone(), memory_bus, CHUNK), /* it is expected that the memory will be * set later */ - access_adapters: AccessAdapterInventory::new( - range_checker.clone(), - memory_bus, - mem_config.clk_max_bits, - mem_config.max_access_adapter_n, - ), range_checker, range_checker_bus, } @@ -279,7 +261,8 @@ impl MemoryController { MemoryInterface::Volatile { boundary_chip } => match overridden_heights { MemoryTraceHeights::Volatile(oh) => { boundary_chip.set_overridden_height(oh.boundary); - self.access_adapters + self.memory + .access_adapter_inventory .set_override_trace_heights(oh.access_adapters); } _ => panic!("Expect overridden_heights to be MemoryTraceHeights::Volatile"), @@ -292,7 +275,8 @@ impl MemoryController { MemoryTraceHeights::Persistent(oh) => { boundary_chip.set_overridden_height(oh.boundary); merkle_chip.set_overridden_height(oh.merkle); - self.access_adapters + self.memory + .access_adapter_inventory .set_override_trace_heights(oh.access_adapters); } _ => panic!("Expect overridden_heights to be MemoryTraceHeights::Persistent"), @@ -334,77 +318,6 @@ impl MemoryController { ) } - pub fn read_cell(&mut self, address_space: F, pointer: F) -> (RecordId, F) { - let (record_id, [data]) = self.read(address_space, pointer); - (record_id, data) - } - - // TEMP[jpw]: Function is safe temporarily for refactoring - /// # Safety - /// The type `T` must be stack-allocated `repr(C)` or `repr(transparent)`, and it must be the - /// exact type used to represent a single memory cell in address space `address_space`. For - /// standard usage, `T` is either `u8` or `F` where `F` is the base field of the ZK backend. - pub fn read( - &mut self, - address_space: F, - pointer: F, - ) -> (RecordId, [T; N]) { - let address_space_u32 = address_space.as_canonical_u32(); - let ptr_u32 = pointer.as_canonical_u32(); - assert!( - address_space == F::ZERO || ptr_u32 < (1 << self.mem_config.pointer_max_bits), - "memory out of bounds: {ptr_u32:?}", - ); - todo!() - // let (record_id, values) = unsafe { self.memory.read::(address_space_u32, ptr_u32) - // }; - - // (record_id, values) - } - - /// Reads a word directly from memory without updating internal state. - /// - /// Any value returned is unconstrained. - pub fn unsafe_read_cell(&self, addr_space: F, ptr: F) -> T { - self.unsafe_read::(addr_space, ptr)[0] - } - - /// Reads a word directly from memory without updating internal state. - /// - /// Any value returned is unconstrained. - pub fn unsafe_read(&self, addr_space: F, ptr: F) -> [T; N] { - let addr_space = addr_space.as_canonical_u32(); - let ptr = ptr.as_canonical_u32(); - todo!() - // unsafe { array::from_fn(|i| self.memory.get::(addr_space, ptr + i as u32)) } - } - - /// Writes `data` to the given cell. - /// - /// Returns the `RecordId` and previous data. - pub fn write_cell(&mut self, address_space: F, pointer: F, data: T) -> (RecordId, T) { - let (record_id, [data]) = self.write(address_space, pointer, &[data]); - (record_id, data) - } - - pub fn write( - &mut self, - address_space: F, - pointer: F, - data: &[T; N], - ) -> (RecordId, [T; N]) { - debug_assert_ne!(address_space, F::ZERO); - let address_space_u32 = address_space.as_canonical_u32(); - let ptr_u32 = pointer.as_canonical_u32(); - assert!( - ptr_u32 < (1 << self.mem_config.pointer_max_bits), - "memory out of bounds: {ptr_u32:?}", - ); - - todo!() - // unsafe { self.memory.write::(address_space_u32, ptr_u32, data) } - } - pub fn helper(&self) -> SharedMemoryHelper { let range_bus = self.range_checker.bus(); SharedMemoryHelper { @@ -572,12 +485,6 @@ impl MemoryController { ); } - for i in 0..self.access_adapters.num_access_adapters() { - let width = self.memory.adapter_inventory_trace_cursor.width(i); - let trace = self.memory.adapter_inventory_trace_cursor.extract_trace(i); - self.access_adapters.set_trace(i, trace, width); - } - final_memory } @@ -632,12 +539,8 @@ impl MemoryController { { let mut ret = Vec::new(); - let Self { - interface_chip, - access_adapters, - .. - } = self; - match interface_chip { + let access_adapters = self.memory.access_adapter_inventory; + match self.interface_chip { MemoryInterface::Volatile { boundary_chip } => { ret.push(boundary_chip.generate_air_proof_input()); } @@ -678,7 +581,7 @@ impl MemoryController { airs.push(merkle_chip.air()); } } - airs.extend(self.access_adapters.airs()); + airs.extend(self.memory.access_adapter_inventory.airs()); airs } @@ -689,7 +592,7 @@ impl MemoryController { if self.continuation_enabled() { num_airs += 1; } - num_airs += self.access_adapters.num_access_adapters(); + num_airs += self.memory.access_adapter_inventory.num_access_adapters(); num_airs } @@ -698,7 +601,7 @@ impl MemoryController { if self.continuation_enabled() { air_names.push("Merkle".to_string()); } - air_names.extend(self.access_adapters.air_names()); + air_names.extend(self.memory.access_adapter_inventory.air_names()); air_names } @@ -707,7 +610,7 @@ impl MemoryController { } pub fn get_memory_trace_heights(&self) -> MemoryTraceHeights { - let access_adapters = self.access_adapters.get_heights(); + let access_adapters = self.memory.access_adapter_inventory.get_heights(); match &self.interface_chip { MemoryInterface::Volatile { boundary_chip } => { MemoryTraceHeights::Volatile(VolatileMemoryTraceHeights { @@ -728,7 +631,7 @@ impl MemoryController { } pub fn get_dummy_memory_trace_heights(&self) -> MemoryTraceHeights { - let access_adapters = vec![1; self.access_adapters.num_access_adapters()]; + let access_adapters = vec![1; self.memory.access_adapter_inventory.num_access_adapters()]; match &self.interface_chip { MemoryInterface::Volatile { .. } => { MemoryTraceHeights::Volatile(VolatileMemoryTraceHeights { @@ -761,7 +664,7 @@ impl MemoryController { ret.push(merkle_chip.current_trace_cells()); } } - ret.extend(self.access_adapters.get_cells()); + ret.extend(self.memory.access_adapter_inventory.get_cells()); ret } } @@ -805,18 +708,6 @@ impl MemoryAuxColsFactory<'_, F> { &mut buffer.lower_decomp, ); } - - fn generate_timestamp_lt_cols( - &self, - prev_timestamp: u32, - timestamp: u32, - ) -> LessThanAuxCols { - debug_assert!(prev_timestamp < timestamp); - let mut decomp = [F::ZERO; AUX_LEN]; - self.timestamp_lt_air - .generate_subrow((self.range_checker, prev_timestamp, timestamp), &mut decomp); - LessThanAuxCols::new(decomp) - } } impl SharedMemoryHelper { @@ -836,7 +727,7 @@ mod tests { }; use openvm_stark_backend::{interaction::BusIndex, p3_field::FieldAlgebra}; use openvm_stark_sdk::p3_baby_bear::BabyBear; - use rand::{prelude::SliceRandom, thread_rng, Rng}; + use rand::{thread_rng, Rng}; use super::MemoryController; use crate::{ @@ -855,7 +746,7 @@ mod tests { let range_bus = VariableRangeCheckerBus::new(RANGE_CHECKER_BUS, memory_config.decomp); let range_checker = SharedVariableRangeCheckerChip::new(range_bus); - let mut memory_controller = MemoryController::with_volatile_memory( + let mut memory_controller = MemoryController::::with_volatile_memory( memory_bus, memory_config, range_checker.clone(), @@ -863,19 +754,29 @@ mod tests { let mut rng = thread_rng(); for _ in 0..1000 { - let address_space = F::from_canonical_u32(*[1, 2].choose(&mut rng).unwrap()); - let pointer = - F::from_canonical_u32(rng.gen_range(0..1 << memory_config.pointer_max_bits)); + // TODO[jpw]: test other address spaces? + let address_space = 4u32; + let pointer = rng.gen_range(0..1 << memory_config.pointer_max_bits); if rng.gen_bool(0.5) { let data = F::from_canonical_u32(rng.gen_range(0..1 << 30)); - memory_controller.write(address_space, pointer, &[data]); + // address space is 4 so cell type is `F` + unsafe { + memory_controller + .memory + .write::(address_space, pointer, &[data]); + } } else { - memory_controller.read::(address_space, pointer); + unsafe { + memory_controller + .memory + .read::(address_space, pointer); + } } } assert!(memory_controller - .access_adapters + .memory + .access_adapter_inventory .get_heights() .iter() .all(|&h| h == 0)); diff --git a/crates/vm/src/system/memory/merkle/mod.rs b/crates/vm/src/system/memory/merkle/mod.rs index 4eac44bf81..35563969b6 100644 --- a/crates/vm/src/system/memory/merkle/mod.rs +++ b/crates/vm/src/system/memory/merkle/mod.rs @@ -18,8 +18,9 @@ pub(super) use trace::SerialReceiver; pub struct MemoryMerkleChip { pub air: MemoryMerkleAir, touched_nodes: FxHashSet<(usize, u32, u32)>, - num_touched_nonleaves: usize, final_state: Option>, + // TODO(AG): how are these two different? Doesn't one just end up being copied to the other? + trace_height: Option, overridden_height: Option, } #[derive(Debug)] @@ -48,37 +49,14 @@ impl MemoryMerkleChip { compression_bus, }, touched_nodes, - num_touched_nonleaves: 1, final_state: None, + trace_height: None, overridden_height: None, } } pub fn set_overridden_height(&mut self, override_height: usize) { self.overridden_height = Some(override_height); } - - fn touch_node(&mut self, height: usize, as_label: u32, address_label: u32) { - if self.touched_nodes.insert((height, as_label, address_label)) { - assert_ne!(height, self.air.memory_dimensions.overall_height()); - if height != 0 { - self.num_touched_nonleaves += 1; - } - if height >= self.air.memory_dimensions.address_height { - self.touch_node(height + 1, as_label / 2, address_label); - } else { - self.touch_node(height + 1, as_label, address_label / 2); - } - } - } - - pub fn touch_range(&mut self, address_space: u32, address: u32, len: u32) { - let as_label = address_space - self.air.memory_dimensions.as_offset; - let first_address_label = address / CHUNK as u32; - let last_address_label = (address + len - 1) / CHUNK as u32; - for address_label in first_address_label..=last_address_label { - self.touch_node(0, as_label, address_label); - } - } } fn memory_to_partition( diff --git a/crates/vm/src/system/memory/merkle/trace.rs b/crates/vm/src/system/memory/merkle/trace.rs index 5a5dfc35e0..22e3ffa7ef 100644 --- a/crates/vm/src/system/memory/merkle/trace.rs +++ b/crates/vm/src/system/memory/merkle/trace.rs @@ -34,6 +34,7 @@ impl MemoryMerkleChip { assert!(self.final_state.is_none(), "Merkle chip already finalized"); let mut tree = MerkleTree::from_memory(initial_memory, &self.air.memory_dimensions, hasher); self.final_state = Some(tree.finalize(hasher, final_memory, &self.air.memory_dimensions)); + self.trace_height = Some(self.final_state.as_ref().unwrap().rows.len()); } } @@ -87,7 +88,8 @@ impl ChipUsageGetter for MemoryMerkleChip usize { - 2 * self.num_touched_nonleaves + // TODO is it ok? + self.trace_height.unwrap_or(0) } fn trace_width(&self) -> usize { diff --git a/crates/vm/src/system/memory/merkle/tree.rs b/crates/vm/src/system/memory/merkle/tree.rs index 74f3d54730..4a413b6dc3 100644 --- a/crates/vm/src/system/memory/merkle/tree.rs +++ b/crates/vm/src/system/memory/merkle/tree.rs @@ -189,16 +189,21 @@ impl MerkleTree { md: &MemoryDimensions, ) -> FinalState { let init_root = self.get_node(1); - let layer: Vec<_> = touched - .iter() - .map(|((addr_sp, ptr), v)| { - ( - (1 << self.height) + md.label_to_index((*addr_sp, *ptr / CHUNK as u32)), - hasher.hash(v), - ) - }) - .collect(); - let mut rows = Vec::with_capacity(if touched.is_empty() { + let layer: Vec<_> = if !touched.is_empty() { + touched + .iter() + .map(|((addr_sp, ptr), v)| { + ( + (1 << self.height) + md.label_to_index((*addr_sp, *ptr / CHUNK as u32)), + hasher.hash(v), + ) + }) + .collect() + } else { + let index = 1 << self.height; + vec![(index, self.get_node(index))] + }; + let mut rows = Vec::with_capacity(if layer.is_empty() { 0 } else { layer @@ -211,6 +216,12 @@ impl MerkleTree { self.process_layers(layer, md, Some(&mut rows), |left, right| { hasher.compress_and_record(left, right) }); + if touched.is_empty() { + // If we made an artificial touch, we need to change the direction changes for the + // leaves + rows[1].left_direction_different = F::ONE; + rows[1].right_direction_different = F::ONE; + } let final_root = self.get_node(1); FinalState { rows, diff --git a/crates/vm/src/system/memory/online.rs b/crates/vm/src/system/memory/online.rs index b1e8a3b642..2d6a292729 100644 --- a/crates/vm/src/system/memory/online.rs +++ b/crates/vm/src/system/memory/online.rs @@ -192,7 +192,6 @@ pub struct TracingMemory { /// all memory accesses in `addr_space` must be aligned to this block size. pub min_block_size: Vec, pub access_adapter_inventory: AccessAdapterInventory, - pub adapter_inventory_trace_cursor: AdapterInventoryTraceCursor, } impl TracingMemory { @@ -234,7 +233,6 @@ impl TracingMemory { mem_config.clk_max_bits, mem_config.max_access_adapter_n, ), - adapter_inventory_trace_cursor: AdapterInventoryTraceCursor::new(num_addr_sp), } } @@ -300,7 +298,6 @@ impl TracingMemory { }, &values[i..i + size], timestamp, - self.adapter_inventory_trace_cursor.get_row_slice(size), ); } } @@ -346,7 +343,6 @@ impl TracingMemory { &values[i..i + size], *left_timestamp, *right_timestamp, - self.adapter_inventory_trace_cursor.get_row_slice(size), ); } } diff --git a/crates/vm/tests/integration_test.rs b/crates/vm/tests/integration_test.rs index b41c2e6e36..a726a74830 100644 --- a/crates/vm/tests/integration_test.rs +++ b/crates/vm/tests/integration_test.rs @@ -662,7 +662,7 @@ fn test_vm_hint() { Instruction::from_isize(LOADW.global_opcode(), 38, 0, 32, 4, 4), Instruction::large_from_isize(ADD.global_opcode(), 44, 20, 0, 4, 4, 0, 0), Instruction::from_isize(MUL.global_opcode(), 24, 38, 1, 4, 4), - Instruction::large_from_isize(ADD.global_opcode(), 20, 20, 24, 4, 4, 1, 0), + Instruction::large_from_isize(ADD.global_opcode(), 20, 20, 24, 4, 4, 4, 0), Instruction::large_from_isize(ADD.global_opcode(), 50, 16, 0, 4, 4, 0, 0), Instruction::from_isize( JAL.global_opcode(), diff --git a/docs/specs/ISA.md b/docs/specs/ISA.md index 1bc3d0c4a0..12c8db8ce6 100644 --- a/docs/specs/ISA.md +++ b/docs/specs/ISA.md @@ -461,7 +461,7 @@ reads but not allowed for writes. When using immediates, we interpret `[a]_0` as #### Field Arithmetic -This instruction set does native field operations. Below, `e,f` may be any address space. +This instruction set does native field operations. Below, `e,f` must be either `0` or `4`. When either `e` or `f` is zero, `[b]_0` and `[c]_0` should be interpreted as the immediates `b` and `c`, respectively. diff --git a/examples/i256/src/main.rs b/examples/i256/src/main.rs index 0cd58e82e8..7122badb9a 100644 --- a/examples/i256/src/main.rs +++ b/examples/i256/src/main.rs @@ -1,4 +1,6 @@ #![allow(clippy::needless_range_loop)] +openvm::entry!(main); + use core::array; use openvm_bigint_guest::I256; diff --git a/examples/keccak/src/main.rs b/examples/keccak/src/main.rs index b17be3fd8f..b28e4daa35 100644 --- a/examples/keccak/src/main.rs +++ b/examples/keccak/src/main.rs @@ -1,3 +1,5 @@ +openvm::entry!(main); + // ANCHOR: imports use core::hint::black_box; diff --git a/examples/sha256/src/main.rs b/examples/sha256/src/main.rs index 502a12366b..30283badd1 100644 --- a/examples/sha256/src/main.rs +++ b/examples/sha256/src/main.rs @@ -1,3 +1,5 @@ +openvm::entry!(main); + // ANCHOR: imports use core::hint::black_box; diff --git a/examples/u256/src/main.rs b/examples/u256/src/main.rs index 37e0e17ca9..2f262fe324 100644 --- a/examples/u256/src/main.rs +++ b/examples/u256/src/main.rs @@ -1,4 +1,6 @@ #![allow(clippy::needless_range_loop)] +openvm::entry!(main); + use core::array; use openvm_bigint_guest::U256; diff --git a/extensions/native/circuit/src/adapters/alu_native_adapter.rs b/extensions/native/circuit/src/adapters/alu_native_adapter.rs index 55232950db..1387f16116 100644 --- a/extensions/native/circuit/src/adapters/alu_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/alu_native_adapter.rs @@ -74,6 +74,8 @@ impl VmAdapterAir for AluNativeAdapterAir { let native_as = AB::Expr::from_canonical_u32(AS::Native as u32); + // TODO: we assume address space is either 0 or 4, should we add a + // constraint for that? self.memory_bridge .read_or_immediate( MemoryAddress::new(cols.e_as, cols.b_pointer), diff --git a/extensions/native/circuit/src/adapters/mod.rs b/extensions/native/circuit/src/adapters/mod.rs index 6041f5861c..fcacf06000 100644 --- a/extensions/native/circuit/src/adapters/mod.rs +++ b/extensions/native/circuit/src/adapters/mod.rs @@ -176,7 +176,11 @@ pub fn tracing_read_or_imm_native( where F: PrimeField32, { - debug_assert!(addr_space == AS::Immediate as u32 || addr_space == AS::Native as u32); + debug_assert!( + addr_space == AS::Immediate as u32 || addr_space == AS::Native as u32, + "addr_space={} is not valid", + addr_space + ); if addr_space == AS::Immediate as u32 { *addr_space_mut = F::ZERO; diff --git a/extensions/native/circuit/src/extension.rs b/extensions/native/circuit/src/extension.rs index e76fb4fa49..8e930c6a86 100644 --- a/extensions/native/circuit/src/extension.rs +++ b/extensions/native/circuit/src/extension.rs @@ -38,7 +38,7 @@ use crate::{ }; // TODO(ayush): this should be decided after e2 execution -const MAX_INS_CAPACITY: usize = 1 << 22; +const MAX_INS_CAPACITY: usize = 1 << 23; #[derive(Clone, Debug, Serialize, Deserialize, VmConfig, derive_new::new)] pub struct NativeConfig { diff --git a/extensions/rv32im/circuit/src/adapters/mod.rs b/extensions/rv32im/circuit/src/adapters/mod.rs index 388bfb9d32..3de1add4b1 100644 --- a/extensions/rv32im/circuit/src/adapters/mod.rs +++ b/extensions/rv32im/circuit/src/adapters/mod.rs @@ -6,7 +6,6 @@ use openvm_circuit::{ offline_checker::{MemoryBaseAuxCols, MemoryReadAuxCols, MemoryWriteAuxCols}, online::{GuestMemory, TracingMemory}, tree::public_values::PUBLIC_VALUES_AS, - MemoryController, RecordId, }, }; use openvm_instructions::riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS}; @@ -235,27 +234,14 @@ pub fn tracing_write_with_base_aux( base_aux_cols.set_prev(F::from_canonical_u32(t_prev)); } -// TODO: delete -/// Read register value as [RV32_REGISTER_NUM_LIMBS] limbs from memory. -/// Returns the read record and the register value as u32. -/// Does not make any range check calls. -pub fn read_rv32_register( - memory: &mut MemoryController, - address_space: F, - pointer: F, -) -> (RecordId, u32) { - debug_assert_eq!(address_space, F::ONE); - let record = memory.read::(address_space, pointer); - let val = u32::from_le_bytes(record.1); - (record.0, val) -} - +// TODO: remove new_ #[inline(always)] pub fn new_read_rv32_register(memory: &GuestMemory, address_space: u32, ptr: u32) -> u32 { u32::from_le_bytes(memory_read(memory, address_space, ptr)) } // TODO(AG): if "register", why `address_space` is not hardcoded to be 1? +// TODO(jpw): remove new_ #[inline(always)] pub fn new_read_rv32_register_from_state( state: &mut VmStateMut, @@ -268,13 +254,6 @@ where u32::from_le_bytes(memory_read_from_state(state, address_space, ptr)) } -/// Peeks at the value of a register without updating the memory state or incrementing the -/// timestamp. -pub fn unsafe_read_rv32_register(memory: &MemoryController, pointer: F) -> u32 { - let data = memory.unsafe_read::(F::ONE, pointer); - u32::from_le_bytes(data) -} - pub fn abstract_compose>( data: [V; RV32_REGISTER_NUM_LIMBS], ) -> T { diff --git a/extensions/rv32im/circuit/src/hintstore/mod.rs b/extensions/rv32im/circuit/src/hintstore/mod.rs index 6917862b62..c2c465e600 100644 --- a/extensions/rv32im/circuit/src/hintstore/mod.rs +++ b/extensions/rv32im/circuit/src/hintstore/mod.rs @@ -12,7 +12,7 @@ use openvm_circuit::{ system::memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, online::{GuestMemory, TracingMemory}, - MemoryAddress, MemoryAuxColsFactory, RecordId, + MemoryAddress, MemoryAuxColsFactory, }, }; use openvm_circuit_primitives::{ @@ -264,19 +264,6 @@ impl Air for Rv32HintStoreAir { } } -#[derive(Serialize, Deserialize)] -#[serde(bound = "F: Field")] -pub struct Rv32HintStoreRecord { - pub from_state: ExecutionState, - pub instruction: Instruction, - pub mem_ptr_read: RecordId, - pub mem_ptr: u32, - pub num_words: u32, - - pub num_words_read: Option, - pub hints: Vec<([F; RV32_REGISTER_NUM_LIMBS], RecordId)>, -} - pub struct Rv32HintStoreStep { pointer_max_bits: usize, offset: usize, From a7f81dccb422fd45837ab83f7f939c118d0ed7cd Mon Sep 17 00:00:00 2001 From: Xinding Wei Date: Fri, 30 May 2025 15:26:38 -0700 Subject: [PATCH 45/49] refactor: Make InsExecutorE1 Stateless (#1700) Enforce `InsExecutorE1` to be stateless so it can be easily used in interpreter. All chips except `Rv32HintRandomSubEx` don't need any changes. --- crates/circuits/mod-builder/src/core_chip.rs | 4 ++-- crates/vm/derive/src/lib.rs | 8 ++++---- crates/vm/src/arch/execution.rs | 10 +++++----- crates/vm/src/arch/integration_api.rs | 8 ++++---- crates/vm/src/system/phantom/mod.rs | 15 +++++++------- crates/vm/src/system/public_values/core.rs | 4 ++-- .../algebra/circuit/src/modular_chip/is_eq.rs | 4 ++-- .../ecc/circuit/src/weierstrass_extension.rs | 4 ++-- extensions/keccak256/circuit/src/lib.rs | 4 ++-- .../native/circuit/src/branch_eq/core.rs | 4 ++-- extensions/native/circuit/src/castf/core.rs | 4 ++-- extensions/native/circuit/src/extension.rs | 10 +++++----- .../circuit/src/field_arithmetic/core.rs | 4 ++-- .../circuit/src/field_extension/core.rs | 4 ++-- extensions/native/circuit/src/fri/mod.rs | 15 +++----------- extensions/native/circuit/src/jal/mod.rs | 4 ++-- .../native/circuit/src/loadstore/core.rs | 4 ++-- .../native/circuit/src/poseidon2/chip.rs | 6 +++--- .../pairing/circuit/src/pairing_extension.rs | 2 +- extensions/rv32im/circuit/src/auipc/core.rs | 4 ++-- .../rv32im/circuit/src/base_alu/core.rs | 4 ++-- .../rv32im/circuit/src/branch_eq/core.rs | 4 ++-- .../rv32im/circuit/src/branch_lt/core.rs | 4 ++-- extensions/rv32im/circuit/src/divrem/core.rs | 4 ++-- extensions/rv32im/circuit/src/extension.rs | 20 +++++++++++++------ .../rv32im/circuit/src/hintstore/mod.rs | 4 ++-- extensions/rv32im/circuit/src/jal_lui/core.rs | 4 ++-- extensions/rv32im/circuit/src/jalr/core.rs | 4 ++-- .../rv32im/circuit/src/less_than/core.rs | 4 ++-- .../circuit/src/load_sign_extend/core.rs | 4 ++-- .../rv32im/circuit/src/loadstore/core.rs | 4 ++-- extensions/rv32im/circuit/src/mul/core.rs | 4 ++-- extensions/rv32im/circuit/src/mulh/core.rs | 4 ++-- extensions/rv32im/circuit/src/shift/core.rs | 4 ++-- .../sha256/circuit/src/sha256_chip/mod.rs | 4 ++-- 35 files changed, 98 insertions(+), 100 deletions(-) diff --git a/crates/circuits/mod-builder/src/core_chip.rs b/crates/circuits/mod-builder/src/core_chip.rs index 1f219c62f7..c834ab7ef6 100644 --- a/crates/circuits/mod-builder/src/core_chip.rs +++ b/crates/circuits/mod-builder/src/core_chip.rs @@ -311,7 +311,7 @@ where + for<'a> AdapterExecutorE1>, WriteData: From>>, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -327,7 +327,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/crates/vm/derive/src/lib.rs b/crates/vm/derive/src/lib.rs index a7913893df..b5b4a38bcf 100644 --- a/crates/vm/derive/src/lib.rs +++ b/crates/vm/derive/src/lib.rs @@ -143,7 +143,7 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { quote! { impl #impl_generics ::openvm_circuit::arch::InsExecutorE1 for #name #ty_generics #where_clause { fn execute_e1( - &mut self, + &self, state: &mut ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, Ctx>, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction, ) -> ::openvm_circuit::arch::Result<()> @@ -154,7 +154,7 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { } fn execute_metered( - &mut self, + &self, state: &mut ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, ::openvm_circuit::arch::execution_mode::metered::MeteredCtx>, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction, chip_index: usize, @@ -206,7 +206,7 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { quote! { impl #impl_generics ::openvm_circuit::arch::InsExecutorE1<#first_ty_generic> for #name #ty_generics { fn execute_e1( - &mut self, + &self, state: &mut ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, Ctx>, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction<#first_ty_generic>, ) -> ::openvm_circuit::arch::Result<()> @@ -219,7 +219,7 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { } fn execute_metered( - &mut self, + &self, state: &mut ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, ::openvm_circuit::arch::execution_mode::metered::MeteredCtx>, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction<#first_ty_generic>, chip_index: usize, diff --git a/crates/vm/src/arch/execution.rs b/crates/vm/src/arch/execution.rs index 6ac124aa42..f51c44f431 100644 --- a/crates/vm/src/arch/execution.rs +++ b/crates/vm/src/arch/execution.rs @@ -117,7 +117,7 @@ pub trait InstructionExecutor { /// New trait for instruction execution pub trait InsExecutorE1 { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -126,7 +126,7 @@ pub trait InsExecutorE1 { Ctx: E1E2ExecutionCtx; fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, @@ -140,7 +140,7 @@ where C: InsExecutorE1, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -152,7 +152,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, @@ -391,7 +391,7 @@ impl From<(u32, Option)> for PcIncOrSet { /// `a,b` and `c_upper = c.as_canonical_u32() >> 16`. pub trait PhantomSubExecutor: Send { fn phantom_execute( - &mut self, + &self, memory: &GuestMemory, streams: &mut Streams, discriminant: PhantomDiscriminant, diff --git a/crates/vm/src/arch/integration_api.rs b/crates/vm/src/arch/integration_api.rs index 4294458b22..5ea8e11cc7 100644 --- a/crates/vm/src/arch/integration_api.rs +++ b/crates/vm/src/arch/integration_api.rs @@ -410,7 +410,7 @@ where // TODO: Rename core/step to operator pub trait StepExecutorE1 { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -418,7 +418,7 @@ pub trait StepExecutorE1 { Ctx: E1E2ExecutionCtx; fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, @@ -431,7 +431,7 @@ where S: StepExecutorE1, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -442,7 +442,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/crates/vm/src/system/phantom/mod.rs b/crates/vm/src/system/phantom/mod.rs index cdf6f0dfa0..a49a227baf 100644 --- a/crates/vm/src/system/phantom/mod.rs +++ b/crates/vm/src/system/phantom/mod.rs @@ -130,7 +130,7 @@ where F: PrimeField32, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<(), ExecutionError> @@ -148,17 +148,16 @@ where // If not a system phantom sub-instruction (which is handled in // ExecutionSegment), look for a phantom sub-executor to handle it. if SysPhantom::from_repr(discriminant.0).is_none() { - let sub_executor = self - .phantom_executors - .get_mut(&discriminant) - .ok_or_else(|| ExecutionError::PhantomNotFound { + let sub_executor = self.phantom_executors.get(&discriminant).ok_or_else(|| { + ExecutionError::PhantomNotFound { pc: *state.pc, discriminant, - })?; + } + })?; let mut streams = self.streams.get().unwrap().lock().unwrap(); // TODO(ayush): implement phantom subexecutor for new traits sub_executor - .as_mut() + .as_ref() .phantom_execute( state.memory, &mut streams, @@ -180,7 +179,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, _chip_index: usize, diff --git a/crates/vm/src/system/public_values/core.rs b/crates/vm/src/system/public_values/core.rs index 0e54624c32..472dd66120 100644 --- a/crates/vm/src/system/public_values/core.rs +++ b/crates/vm/src/system/public_values/core.rs @@ -231,7 +231,7 @@ where A: 'static + for<'a> AdapterExecutorE1, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -259,7 +259,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/algebra/circuit/src/modular_chip/is_eq.rs b/extensions/algebra/circuit/src/modular_chip/is_eq.rs index b0dad5d7f5..6e53552f5c 100644 --- a/extensions/algebra/circuit/src/modular_chip/is_eq.rs +++ b/extensions/algebra/circuit/src/modular_chip/is_eq.rs @@ -424,7 +424,7 @@ where >, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -462,7 +462,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/ecc/circuit/src/weierstrass_extension.rs b/extensions/ecc/circuit/src/weierstrass_extension.rs index c2ce65406c..082cff3ff6 100644 --- a/extensions/ecc/circuit/src/weierstrass_extension.rs +++ b/extensions/ecc/circuit/src/weierstrass_extension.rs @@ -263,7 +263,7 @@ pub(crate) mod phantom { impl PhantomSubExecutor for DecompressHintSubEx { fn phantom_execute( - &mut self, + &self, memory: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, @@ -440,7 +440,7 @@ pub(crate) mod phantom { impl PhantomSubExecutor for NonQrHintSubEx { fn phantom_execute( - &mut self, + &self, _: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, diff --git a/extensions/keccak256/circuit/src/lib.rs b/extensions/keccak256/circuit/src/lib.rs index 2f602d685c..6c3300ab01 100644 --- a/extensions/keccak256/circuit/src/lib.rs +++ b/extensions/keccak256/circuit/src/lib.rs @@ -93,7 +93,7 @@ impl KeccakVmStep { impl StepExecutorE1 for KeccakVmStep { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -141,7 +141,7 @@ impl StepExecutorE1 for KeccakVmStep { } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/native/circuit/src/branch_eq/core.rs b/extensions/native/circuit/src/branch_eq/core.rs index d1b9359815..6e9dd1f8c1 100644 --- a/extensions/native/circuit/src/branch_eq/core.rs +++ b/extensions/native/circuit/src/branch_eq/core.rs @@ -110,7 +110,7 @@ where A: 'static + for<'a> AdapterExecutorE1, WriteData = ()>, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -139,7 +139,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/native/circuit/src/castf/core.rs b/extensions/native/circuit/src/castf/core.rs index 0c096b402a..eb05f5419a 100644 --- a/extensions/native/circuit/src/castf/core.rs +++ b/extensions/native/circuit/src/castf/core.rs @@ -199,7 +199,7 @@ where + for<'a> AdapterExecutorE1, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -225,7 +225,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/native/circuit/src/extension.rs b/extensions/native/circuit/src/extension.rs index 8e930c6a86..8fde829efb 100644 --- a/extensions/native/circuit/src/extension.rs +++ b/extensions/native/circuit/src/extension.rs @@ -298,7 +298,7 @@ pub(crate) mod phantom { impl PhantomSubExecutor for NativeHintInputSubEx { fn phantom_execute( - &mut self, + &self, _: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, @@ -323,7 +323,7 @@ pub(crate) mod phantom { impl PhantomSubExecutor for NativeHintSliceSubEx { fn phantom_execute( - &mut self, + &self, _: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, @@ -346,7 +346,7 @@ pub(crate) mod phantom { impl PhantomSubExecutor for NativePrintSubEx { fn phantom_execute( - &mut self, + &self, memory: &GuestMemory, _: &mut Streams, _: PhantomDiscriminant, @@ -362,7 +362,7 @@ pub(crate) mod phantom { impl PhantomSubExecutor for NativeHintBitsSubEx { fn phantom_execute( - &mut self, + &self, memory: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, @@ -386,7 +386,7 @@ pub(crate) mod phantom { impl PhantomSubExecutor for NativeHintLoadSubEx { fn phantom_execute( - &mut self, + &self, _: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, diff --git a/extensions/native/circuit/src/field_arithmetic/core.rs b/extensions/native/circuit/src/field_arithmetic/core.rs index 2ae4811450..46cd317edb 100644 --- a/extensions/native/circuit/src/field_arithmetic/core.rs +++ b/extensions/native/circuit/src/field_arithmetic/core.rs @@ -208,7 +208,7 @@ where A: 'static + for<'a> AdapterExecutorE1, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -232,7 +232,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/native/circuit/src/field_extension/core.rs b/extensions/native/circuit/src/field_extension/core.rs index 2b77bdc7cc..3ad3ce7837 100644 --- a/extensions/native/circuit/src/field_extension/core.rs +++ b/extensions/native/circuit/src/field_extension/core.rs @@ -236,7 +236,7 @@ where + for<'a> AdapterExecutorE1, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -264,7 +264,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/native/circuit/src/fri/mod.rs b/extensions/native/circuit/src/fri/mod.rs index b970cfa557..46f217fc85 100644 --- a/extensions/native/circuit/src/fri/mod.rs +++ b/extensions/native/circuit/src/fri/mod.rs @@ -543,13 +543,12 @@ fn elem_to_ext(elem: F) -> [F; EXT_DEG] { } pub struct FriReducedOpeningStep { - pub height: usize, streams: Arc>>, } impl FriReducedOpeningStep { pub fn new(streams: Arc>>) -> Self { - Self { height: 0, streams } + Self { streams } } } @@ -616,10 +615,6 @@ where let write_a = F::ONE - is_init_read; - // TODO(ayush): why do we need this?should this be incremented only in tracegen execute? - // 2 for instruction rows - self.height += length + 2; - let data = if is_init == 0 { let mut streams = self.streams.lock().unwrap(); let hint_steam = &mut streams.hint_space[hint_id]; @@ -790,7 +785,7 @@ where F: PrimeField32, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -861,10 +856,6 @@ where ); } - // TODO(ayush): why do we need this?should this be incremented only in tracegen execute? - // 2 for instruction rows - self.height += length + 2; - memory_write_native(state.memory, result_ptr, &result); *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP); @@ -873,7 +864,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/native/circuit/src/jal/mod.rs b/extensions/native/circuit/src/jal/mod.rs index 7fbeace742..bf3c9033f4 100644 --- a/extensions/native/circuit/src/jal/mod.rs +++ b/extensions/native/circuit/src/jal/mod.rs @@ -276,7 +276,7 @@ where F: PrimeField32, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -329,7 +329,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/native/circuit/src/loadstore/core.rs b/extensions/native/circuit/src/loadstore/core.rs index 9d61fa95fb..ca3c22ff54 100644 --- a/extensions/native/circuit/src/loadstore/core.rs +++ b/extensions/native/circuit/src/loadstore/core.rs @@ -230,7 +230,7 @@ where + for<'a> AdapterExecutorE1, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -262,7 +262,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/native/circuit/src/poseidon2/chip.rs b/extensions/native/circuit/src/poseidon2/chip.rs index 627a5b6c67..dc325b2b72 100644 --- a/extensions/native/circuit/src/poseidon2/chip.rs +++ b/extensions/native/circuit/src/poseidon2/chip.rs @@ -724,7 +724,7 @@ impl StepExecutorE1 for NativePoseidon2Step { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> openvm_circuit::arch::Result<()> @@ -736,7 +736,7 @@ impl StepExecutorE1 } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, @@ -751,7 +751,7 @@ impl StepExecutorE1 impl NativePoseidon2Step { /// Returns the number of used rows. fn execute_e1_impl( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> usize diff --git a/extensions/pairing/circuit/src/pairing_extension.rs b/extensions/pairing/circuit/src/pairing_extension.rs index 763da36e82..55eeb852bc 100644 --- a/extensions/pairing/circuit/src/pairing_extension.rs +++ b/extensions/pairing/circuit/src/pairing_extension.rs @@ -115,7 +115,7 @@ pub(crate) mod phantom { impl PhantomSubExecutor for PairingHintSubEx { fn phantom_execute( - &mut self, + &self, memory: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, diff --git a/extensions/rv32im/circuit/src/auipc/core.rs b/extensions/rv32im/circuit/src/auipc/core.rs index 7e037df397..4d0bd3c31d 100644 --- a/extensions/rv32im/circuit/src/auipc/core.rs +++ b/extensions/rv32im/circuit/src/auipc/core.rs @@ -311,7 +311,7 @@ where + for<'a> AdapterExecutorE1, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -334,7 +334,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/rv32im/circuit/src/base_alu/core.rs b/extensions/rv32im/circuit/src/base_alu/core.rs index b63ef95479..f7435eb7e5 100644 --- a/extensions/rv32im/circuit/src/base_alu/core.rs +++ b/extensions/rv32im/circuit/src/base_alu/core.rs @@ -285,7 +285,7 @@ where >, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -306,7 +306,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/rv32im/circuit/src/branch_eq/core.rs b/extensions/rv32im/circuit/src/branch_eq/core.rs index 91547cf3f1..cdc5f9b65e 100644 --- a/extensions/rv32im/circuit/src/branch_eq/core.rs +++ b/extensions/rv32im/circuit/src/branch_eq/core.rs @@ -229,7 +229,7 @@ where A: 'static + for<'a> AdapterExecutorE1, WriteData = ()>, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -257,7 +257,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/rv32im/circuit/src/branch_lt/core.rs b/extensions/rv32im/circuit/src/branch_lt/core.rs index 9a777b2d1a..d5c824df53 100644 --- a/extensions/rv32im/circuit/src/branch_lt/core.rs +++ b/extensions/rv32im/circuit/src/branch_lt/core.rs @@ -360,7 +360,7 @@ where A: 'static + for<'a> AdapterExecutorE1, WriteData = ()>, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -386,7 +386,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/rv32im/circuit/src/divrem/core.rs b/extensions/rv32im/circuit/src/divrem/core.rs index 8850681e31..bfffbe6cc3 100644 --- a/extensions/rv32im/circuit/src/divrem/core.rs +++ b/extensions/rv32im/circuit/src/divrem/core.rs @@ -544,7 +544,7 @@ where >, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -581,7 +581,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/rv32im/circuit/src/extension.rs b/extensions/rv32im/circuit/src/extension.rs index 9870f0a71b..0959a2d906 100644 --- a/extensions/rv32im/circuit/src/extension.rs +++ b/extensions/rv32im/circuit/src/extension.rs @@ -611,6 +611,8 @@ impl VmExtension for Rv32Io { /// Phantom sub-executors mod phantom { + use std::sync::{Arc, Mutex}; + use eyre::bail; use openvm_circuit::{ arch::{PhantomSubExecutor, Streams}, @@ -624,18 +626,21 @@ mod phantom { pub struct Rv32HintInputSubEx; pub struct Rv32HintRandomSubEx { - rng: OsRng, + // TODO: this should be moved to VmState in order to be reproducible. + rng: Arc>, } impl Rv32HintRandomSubEx { pub fn new() -> Self { - Self { rng: OsRng } + Self { + rng: Default::default(), + } } } pub struct Rv32PrintStrSubEx; impl PhantomSubExecutor for Rv32HintInputSubEx { fn phantom_execute( - &mut self, + &self, _: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, @@ -666,7 +671,7 @@ mod phantom { impl PhantomSubExecutor for Rv32HintRandomSubEx { fn phantom_execute( - &mut self, + &self, memory: &GuestMemory, streams: &mut Streams, _: PhantomDiscriminant, @@ -677,7 +682,10 @@ mod phantom { let len = new_read_rv32_register(memory, 1, a) as usize; streams.hint_stream.clear(); streams.hint_stream.extend( - std::iter::repeat_with(|| F::from_canonical_u8(self.rng.gen::())).take(len * 4), + std::iter::repeat_with(|| { + F::from_canonical_u8(self.rng.lock().unwrap().gen::()) + }) + .take(len * 4), ); Ok(()) } @@ -685,7 +693,7 @@ mod phantom { impl PhantomSubExecutor for Rv32PrintStrSubEx { fn phantom_execute( - &mut self, + &self, memory: &GuestMemory, _: &mut Streams, _: PhantomDiscriminant, diff --git a/extensions/rv32im/circuit/src/hintstore/mod.rs b/extensions/rv32im/circuit/src/hintstore/mod.rs index c2c465e600..38f223b95f 100644 --- a/extensions/rv32im/circuit/src/hintstore/mod.rs +++ b/extensions/rv32im/circuit/src/hintstore/mod.rs @@ -444,7 +444,7 @@ where F: PrimeField32, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -503,7 +503,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/rv32im/circuit/src/jal_lui/core.rs b/extensions/rv32im/circuit/src/jal_lui/core.rs index 836223316e..18f32e290d 100644 --- a/extensions/rv32im/circuit/src/jal_lui/core.rs +++ b/extensions/rv32im/circuit/src/jal_lui/core.rs @@ -256,7 +256,7 @@ where + for<'a> AdapterExecutorE1, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -291,7 +291,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/rv32im/circuit/src/jalr/core.rs b/extensions/rv32im/circuit/src/jalr/core.rs index 1c543718ed..0daac3d2bc 100644 --- a/extensions/rv32im/circuit/src/jalr/core.rs +++ b/extensions/rv32im/circuit/src/jalr/core.rs @@ -333,7 +333,7 @@ where >, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -364,7 +364,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/rv32im/circuit/src/less_than/core.rs b/extensions/rv32im/circuit/src/less_than/core.rs index fddad57900..7f5dfb2815 100644 --- a/extensions/rv32im/circuit/src/less_than/core.rs +++ b/extensions/rv32im/circuit/src/less_than/core.rs @@ -337,7 +337,7 @@ where >, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -364,7 +364,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/rv32im/circuit/src/load_sign_extend/core.rs b/extensions/rv32im/circuit/src/load_sign_extend/core.rs index 5142502200..3467579fde 100644 --- a/extensions/rv32im/circuit/src/load_sign_extend/core.rs +++ b/extensions/rv32im/circuit/src/load_sign_extend/core.rs @@ -316,7 +316,7 @@ where >, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -349,7 +349,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/rv32im/circuit/src/loadstore/core.rs b/extensions/rv32im/circuit/src/loadstore/core.rs index 9b9eeb59ab..5d8dccef1b 100644 --- a/extensions/rv32im/circuit/src/loadstore/core.rs +++ b/extensions/rv32im/circuit/src/loadstore/core.rs @@ -380,7 +380,7 @@ where >, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -408,7 +408,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/rv32im/circuit/src/mul/core.rs b/extensions/rv32im/circuit/src/mul/core.rs index 0aa431baa8..e31ffa3050 100644 --- a/extensions/rv32im/circuit/src/mul/core.rs +++ b/extensions/rv32im/circuit/src/mul/core.rs @@ -236,7 +236,7 @@ where >, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -264,7 +264,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/rv32im/circuit/src/mulh/core.rs b/extensions/rv32im/circuit/src/mulh/core.rs index 354db4ae5a..2df390b0a9 100644 --- a/extensions/rv32im/circuit/src/mulh/core.rs +++ b/extensions/rv32im/circuit/src/mulh/core.rs @@ -326,7 +326,7 @@ where >, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -352,7 +352,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/rv32im/circuit/src/shift/core.rs b/extensions/rv32im/circuit/src/shift/core.rs index b53131482c..b914d1be42 100644 --- a/extensions/rv32im/circuit/src/shift/core.rs +++ b/extensions/rv32im/circuit/src/shift/core.rs @@ -399,7 +399,7 @@ where >, { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -422,7 +422,7 @@ where } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, diff --git a/extensions/sha256/circuit/src/sha256_chip/mod.rs b/extensions/sha256/circuit/src/sha256_chip/mod.rs index b87069b2e2..19ddd2bd2a 100644 --- a/extensions/sha256/circuit/src/sha256_chip/mod.rs +++ b/extensions/sha256/circuit/src/sha256_chip/mod.rs @@ -75,7 +75,7 @@ impl Sha256VmStep { impl StepExecutorE1 for Sha256VmStep { fn execute_e1( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> @@ -122,7 +122,7 @@ impl StepExecutorE1 for Sha256VmStep { } fn execute_metered( - &mut self, + &self, state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, From 82f7ff99025383085c7b5c4c69db737ffd1ad5f5 Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Mon, 2 Jun 2025 20:47:07 +0200 Subject: [PATCH 46/49] fix(new-execution): return segments in `execute_metered` (#1702) - also increased the max trace cells threshold to 2bn for now --- crates/vm/src/arch/execution_mode/metered/mod.rs | 2 +- crates/vm/src/arch/vm.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/vm/src/arch/execution_mode/metered/mod.rs b/crates/vm/src/arch/execution_mode/metered/mod.rs index 94d74e3751..5c6b64651b 100644 --- a/crates/vm/src/arch/execution_mode/metered/mod.rs +++ b/crates/vm/src/arch/execution_mode/metered/mod.rs @@ -18,7 +18,7 @@ const SEGMENT_CHECK_INTERVAL: u64 = 100; // TODO(ayush): fix these values const MAX_TRACE_HEIGHT: u32 = (1 << 23) - 100; -const MAX_TRACE_CELLS: usize = 1_200_000_000; // 1.2B +const MAX_TRACE_CELLS: usize = 2_000_000_000; // 2B const MAX_INTERACTIONS: usize = BabyBear::ORDER_U32 as usize; pub struct MeteredExecutionControl<'a> { diff --git a/crates/vm/src/arch/vm.rs b/crates/vm/src/arch/vm.rs index 1fe0b56c95..36f625491f 100644 --- a/crates/vm/src/arch/vm.rs +++ b/crates/vm/src/arch/vm.rs @@ -1,4 +1,4 @@ -use std::{borrow::Borrow, collections::VecDeque, marker::PhantomData, mem, sync::Arc}; +use std::{borrow::Borrow, collections::VecDeque, marker::PhantomData, sync::Arc}; use openvm_circuit::system::program::trace::compute_exe_commit; use openvm_instructions::{exe::VmExe, program::Program}; @@ -521,7 +521,7 @@ where None => return Err(ExecutionError::DidNotTerminate), }; - todo!("record segments") + Ok(exec_state.ctx.segments) } pub fn execute_and_generate( From 69aa7a0cf7a216307b19400b3912223e6be7fd7f Mon Sep 17 00:00:00 2001 From: Xinding Wei Date: Tue, 3 Jun 2025 17:13:37 -0700 Subject: [PATCH 47/49] refactor: Move `Streams` into `VmStateMut` (#1707) Previously some chips hold `Arc>` to read from `Streams`. Move `Streams` into `VmStateMut` so it has all data about VM state. --- crates/circuits/mod-builder/src/core_chip.rs | 6 +- crates/sdk/src/prover/vm/local.rs | 2 +- crates/vm/derive/src/lib.rs | 14 ++-- crates/vm/src/arch/execution.rs | 18 +++-- crates/vm/src/arch/execution_control.rs | 12 ++-- crates/vm/src/arch/execution_mode/e1.rs | 9 +-- .../vm/src/arch/execution_mode/metered/mod.rs | 15 ++-- .../arch/execution_mode/tracegen/normal.rs | 9 +-- .../execution_mode/tracegen/segmentation.rs | 11 +-- crates/vm/src/arch/extensions.rs | 34 ++------- crates/vm/src/arch/integration_api.rs | 18 ++--- crates/vm/src/arch/interpreter.rs | 15 ++-- crates/vm/src/arch/segment.rs | 29 +++++--- crates/vm/src/arch/testing/mod.rs | 14 +++- crates/vm/src/arch/vm.rs | 72 ++++++------------- .../system/memory/offline_checker/columns.rs | 2 - crates/vm/src/system/memory/online.rs | 2 +- crates/vm/src/system/memory/paged_vec.rs | 8 +-- crates/vm/src/system/native_adapter/mod.rs | 4 +- crates/vm/src/system/phantom/mod.rs | 19 ++--- crates/vm/src/system/phantom/tests.rs | 3 - crates/vm/src/system/public_values/core.rs | 6 +- crates/vm/tests/integration_test.rs | 32 ++++----- .../algebra/circuit/src/modular_chip/is_eq.rs | 6 +- extensions/keccak256/circuit/src/lib.rs | 12 ++-- extensions/keccak256/circuit/src/trace.rs | 2 +- .../src/adapters/alu_native_adapter.rs | 4 +- .../src/adapters/branch_native_adapter.rs | 4 +- .../circuit/src/adapters/convert_adapter.rs | 4 +- .../src/adapters/loadstore_native_adapter.rs | 4 +- extensions/native/circuit/src/adapters/mod.rs | 6 +- .../src/adapters/native_vectorized_adapter.rs | 4 +- .../native/circuit/src/branch_eq/core.rs | 6 +- extensions/native/circuit/src/castf/core.rs | 6 +- extensions/native/circuit/src/extension.rs | 11 +-- .../circuit/src/field_arithmetic/core.rs | 6 +- .../circuit/src/field_extension/core.rs | 6 +- extensions/native/circuit/src/fri/mod.rs | 25 ++++--- extensions/native/circuit/src/fri/tests.rs | 21 ++---- extensions/native/circuit/src/jal/mod.rs | 6 +- .../native/circuit/src/loadstore/core.rs | 44 ++++-------- .../native/circuit/src/loadstore/mod.rs | 6 +- .../native/circuit/src/loadstore/tests.rs | 28 ++------ .../native/circuit/src/poseidon2/chip.rs | 25 +++---- .../native/circuit/src/poseidon2/mod.rs | 7 +- .../native/circuit/src/poseidon2/tests.rs | 18 ++--- extensions/rv32-adapters/src/eq_mod.rs | 4 +- extensions/rv32-adapters/src/heap.rs | 4 +- extensions/rv32-adapters/src/heap_branch.rs | 4 +- extensions/rv32-adapters/src/vec_heap.rs | 4 +- .../rv32-adapters/src/vec_heap_two_reads.rs | 4 +- extensions/rv32im/circuit/src/adapters/alu.rs | 4 +- .../rv32im/circuit/src/adapters/branch.rs | 4 +- .../rv32im/circuit/src/adapters/jalr.rs | 4 +- .../rv32im/circuit/src/adapters/loadstore.rs | 4 +- extensions/rv32im/circuit/src/adapters/mod.rs | 12 ++-- extensions/rv32im/circuit/src/adapters/mul.rs | 4 +- .../rv32im/circuit/src/adapters/rdwrite.rs | 8 +-- extensions/rv32im/circuit/src/auipc/core.rs | 6 +- .../rv32im/circuit/src/base_alu/core.rs | 6 +- .../rv32im/circuit/src/branch_eq/core.rs | 6 +- .../rv32im/circuit/src/branch_lt/core.rs | 6 +- extensions/rv32im/circuit/src/divrem/core.rs | 6 +- extensions/rv32im/circuit/src/extension.rs | 3 +- .../rv32im/circuit/src/hintstore/mod.rs | 45 +++++------- .../rv32im/circuit/src/hintstore/tests.rs | 30 ++------ extensions/rv32im/circuit/src/jal_lui/core.rs | 6 +- extensions/rv32im/circuit/src/jalr/core.rs | 6 +- .../rv32im/circuit/src/less_than/core.rs | 6 +- .../circuit/src/load_sign_extend/core.rs | 6 +- .../rv32im/circuit/src/loadstore/core.rs | 6 +- extensions/rv32im/circuit/src/mul/core.rs | 6 +- extensions/rv32im/circuit/src/mulh/core.rs | 6 +- extensions/rv32im/circuit/src/shift/core.rs | 6 +- .../sha256/circuit/src/sha256_chip/mod.rs | 10 +-- .../sha256/circuit/src/sha256_chip/trace.rs | 2 +- 76 files changed, 349 insertions(+), 494 deletions(-) diff --git a/crates/circuits/mod-builder/src/core_chip.rs b/crates/circuits/mod-builder/src/core_chip.rs index c834ab7ef6..54fcd13faa 100644 --- a/crates/circuits/mod-builder/src/core_chip.rs +++ b/crates/circuits/mod-builder/src/core_chip.rs @@ -251,7 +251,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -312,7 +312,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -328,7 +328,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/crates/sdk/src/prover/vm/local.rs b/crates/sdk/src/prover/vm/local.rs index 9177a3092e..245ddfe982 100644 --- a/crates/sdk/src/prover/vm/local.rs +++ b/crates/sdk/src/prover/vm/local.rs @@ -1,4 +1,4 @@ -use std::{marker::PhantomData, mem, sync::Arc}; +use std::{marker::PhantomData, sync::Arc}; use async_trait::async_trait; use openvm_circuit::{ diff --git a/crates/vm/derive/src/lib.rs b/crates/vm/derive/src/lib.rs index b5b4a38bcf..47cc46db7f 100644 --- a/crates/vm/derive/src/lib.rs +++ b/crates/vm/derive/src/lib.rs @@ -38,10 +38,11 @@ pub fn instruction_executor_derive(input: TokenStream) -> TokenStream { fn execute( &mut self, memory: &mut ::openvm_circuit::system::memory::MemoryController, + streams: &mut ::openvm_circuit::arch::Streams, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction, from_state: ::openvm_circuit::arch::ExecutionState, ) -> ::openvm_circuit::arch::Result<::openvm_circuit::arch::ExecutionState> { - self.0.execute(memory, instruction, from_state) + self.0.execute(memory, streams, instruction, from_state) } fn get_opcode_name(&self, opcode: usize) -> String { @@ -79,7 +80,7 @@ pub fn instruction_executor_derive(input: TokenStream) -> TokenStream { multiunzip(variants.iter().map(|(variant_name, field)| { let field_ty = &field.ty; let execute_arm = quote! { - #name::#variant_name(x) => <#field_ty as ::openvm_circuit::arch::InstructionExecutor<#first_ty_generic>>::execute(x, memory, instruction, from_state) + #name::#variant_name(x) => <#field_ty as ::openvm_circuit::arch::InstructionExecutor<#first_ty_generic>>::execute(x, memory, streams, instruction, from_state) }; let get_opcode_name_arm = quote! { #name::#variant_name(x) => <#field_ty as ::openvm_circuit::arch::InstructionExecutor<#first_ty_generic>>::get_opcode_name(x, opcode) @@ -92,6 +93,7 @@ pub fn instruction_executor_derive(input: TokenStream) -> TokenStream { fn execute( &mut self, memory: &mut ::openvm_circuit::system::memory::MemoryController<#first_ty_generic>, + streams: &mut ::openvm_circuit::arch::Streams, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction<#first_ty_generic>, from_state: ::openvm_circuit::arch::ExecutionState, ) -> ::openvm_circuit::arch::Result<::openvm_circuit::arch::ExecutionState> { @@ -144,7 +146,7 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { impl #impl_generics ::openvm_circuit::arch::InsExecutorE1 for #name #ty_generics #where_clause { fn execute_e1( &self, - state: &mut ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, Ctx>, + state: &mut ::openvm_circuit::arch::VmStateMut, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction, ) -> ::openvm_circuit::arch::Result<()> where @@ -155,7 +157,7 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { fn execute_metered( &self, - state: &mut ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, ::openvm_circuit::arch::execution_mode::metered::MeteredCtx>, + state: &mut ::openvm_circuit::arch::VmStateMut, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction, chip_index: usize, ) -> ::openvm_circuit::arch::Result<()> @@ -207,7 +209,7 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { impl #impl_generics ::openvm_circuit::arch::InsExecutorE1<#first_ty_generic> for #name #ty_generics { fn execute_e1( &self, - state: &mut ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, Ctx>, + state: &mut ::openvm_circuit::arch::VmStateMut, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction<#first_ty_generic>, ) -> ::openvm_circuit::arch::Result<()> where @@ -220,7 +222,7 @@ pub fn ins_executor_e1_executor_derive(input: TokenStream) -> TokenStream { fn execute_metered( &self, - state: &mut ::openvm_circuit::arch::VmStateMut<::openvm_circuit::system::memory::online::GuestMemory, ::openvm_circuit::arch::execution_mode::metered::MeteredCtx>, + state: &mut ::openvm_circuit::arch::VmStateMut, instruction: &::openvm_circuit::arch::instructions::instruction::Instruction<#first_ty_generic>, chip_index: usize, ) -> ::openvm_circuit::arch::Result<()> { diff --git a/crates/vm/src/arch/execution.rs b/crates/vm/src/arch/execution.rs index f51c44f431..12a8983984 100644 --- a/crates/vm/src/arch/execution.rs +++ b/crates/vm/src/arch/execution.rs @@ -83,13 +83,14 @@ pub enum ExecutionError { /// The state is generic in guest memory `MEM` and additional host state `CTX`. /// The host state is execution context specific. #[derive(derive_new::new)] -pub struct VmStateMut<'a, MEM, CTX> { +pub struct VmStateMut<'a, F, MEM, CTX> { pub pc: &'a mut u32, pub memory: &'a mut MEM, + pub streams: &'a mut Streams, pub ctx: &'a mut CTX, } -impl VmStateMut<'_, TracingMemory, CTX> { +impl VmStateMut<'_, F, TracingMemory, CTX> { // TODO: store as u32 directly #[inline(always)] pub fn ins_start(&self, from_state: &mut ExecutionState) { @@ -105,6 +106,7 @@ pub trait InstructionExecutor { fn execute( &mut self, memory: &mut MemoryController, + streams: &mut Streams, instruction: &Instruction, from_state: ExecutionState, ) -> Result>; @@ -118,7 +120,7 @@ pub trait InstructionExecutor { pub trait InsExecutorE1 { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -127,7 +129,7 @@ pub trait InsExecutorE1 { fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> @@ -141,7 +143,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -153,7 +155,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> @@ -169,10 +171,12 @@ impl> InstructionExecutor for RefCell { fn execute( &mut self, memory: &mut MemoryController, + streams: &mut Streams, instruction: &Instruction, prev_state: ExecutionState, ) -> Result> { - self.borrow_mut().execute(memory, instruction, prev_state) + self.borrow_mut() + .execute(memory, streams, instruction, prev_state) } fn get_opcode_name(&self, opcode: usize) -> String { diff --git a/crates/vm/src/arch/execution_control.rs b/crates/vm/src/arch/execution_control.rs index afd46373d8..5f3aab4b45 100644 --- a/crates/vm/src/arch/execution_control.rs +++ b/crates/vm/src/arch/execution_control.rs @@ -21,28 +21,28 @@ where /// Determines if execution should suspend fn should_suspend( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, chip_complex: &VmChipComplex, ) -> bool; /// Called before execution begins fn on_start( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ); /// Called after suspend or terminate fn on_suspend_or_terminate( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, exit_code: Option, ); fn on_suspend( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ) { self.on_suspend_or_terminate(state, chip_complex, None); @@ -50,7 +50,7 @@ where fn on_terminate( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, exit_code: u32, ) { @@ -61,7 +61,7 @@ where // TODO(ayush): change instruction to Instruction / PInstruction fn execute_instruction( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> diff --git a/crates/vm/src/arch/execution_mode/e1.rs b/crates/vm/src/arch/execution_mode/e1.rs index 1e72d22ae8..f074f9d63d 100644 --- a/crates/vm/src/arch/execution_mode/e1.rs +++ b/crates/vm/src/arch/execution_mode/e1.rs @@ -32,7 +32,7 @@ where fn should_suspend( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, _chip_complex: &VmChipComplex, ) -> bool { if let Some(clk_end) = self.clk_end { @@ -44,14 +44,14 @@ where fn on_start( &self, - _state: &mut VmSegmentState, + _state: &mut VmSegmentState, _chip_complex: &mut VmChipComplex, ) { } fn on_suspend_or_terminate( &self, - _state: &mut VmSegmentState, + _state: &mut VmSegmentState, _chip_complex: &mut VmChipComplex, _exit_code: Option, ) { @@ -60,7 +60,7 @@ where /// Execute a single instruction fn execute_instruction( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> @@ -73,6 +73,7 @@ where let mut vm_state = VmStateMut { pc: &mut state.pc, memory: state.memory.as_mut().unwrap(), + streams: &mut state.streams, ctx: &mut state.ctx, }; executor.execute_e1(&mut vm_state, instruction)?; diff --git a/crates/vm/src/arch/execution_mode/metered/mod.rs b/crates/vm/src/arch/execution_mode/metered/mod.rs index 5c6b64651b..5d2cc2306b 100644 --- a/crates/vm/src/arch/execution_mode/metered/mod.rs +++ b/crates/vm/src/arch/execution_mode/metered/mod.rs @@ -56,7 +56,7 @@ impl<'a> MeteredExecutionControl<'a> { .sum() } - fn should_segment(&self, state: &mut VmSegmentState) -> bool { + fn should_segment(&self, state: &mut VmSegmentState) -> bool { let trace_heights = state.ctx.trace_heights_if_finalized(); for (i, &height) in trace_heights.iter().enumerate() { if height > MAX_TRACE_HEIGHT { @@ -102,7 +102,7 @@ impl<'a> MeteredExecutionControl<'a> { fn reset_segment( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ) where F: PrimeField32, @@ -155,7 +155,7 @@ impl<'a> MeteredExecutionControl<'a> { fn check_segment_limits( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ) where F: PrimeField32, @@ -200,7 +200,7 @@ where fn should_suspend( &self, - _state: &mut VmSegmentState, + _state: &mut VmSegmentState, _chip_complex: &VmChipComplex, ) -> bool { false @@ -208,7 +208,7 @@ where fn on_start( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ) { self.reset_segment::(state, chip_complex); @@ -216,7 +216,7 @@ where fn on_suspend_or_terminate( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, _chip_complex: &mut VmChipComplex, _exit_code: Option, ) { @@ -245,7 +245,7 @@ where /// Execute a single instruction fn execute_instruction( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> @@ -267,6 +267,7 @@ where let mut vm_state = VmStateMut { pc: &mut state.pc, memory: state.memory.as_mut().unwrap(), + streams: &mut state.streams, ctx: &mut state.ctx, }; executor.execute_metered(&mut vm_state, instruction, offset + i)?; diff --git a/crates/vm/src/arch/execution_mode/tracegen/normal.rs b/crates/vm/src/arch/execution_mode/tracegen/normal.rs index ee5d274a6f..48894a0449 100644 --- a/crates/vm/src/arch/execution_mode/tracegen/normal.rs +++ b/crates/vm/src/arch/execution_mode/tracegen/normal.rs @@ -36,7 +36,7 @@ where fn should_suspend( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, _chip_complex: &VmChipComplex, ) -> bool { state.clk >= self.clk_end @@ -44,7 +44,7 @@ where fn on_start( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ) { chip_complex @@ -54,7 +54,7 @@ where fn on_suspend_or_terminate( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, exit_code: Option, ) { @@ -67,7 +67,7 @@ where /// Execute a single instruction fn execute_instruction( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> @@ -82,6 +82,7 @@ where let memory_controller = &mut chip_complex.base.memory_controller; let new_state = executor.execute( memory_controller, + &mut state.streams, instruction, ExecutionState::new(state.pc, timestamp), )?; diff --git a/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs b/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs index a12c0eb368..05a82ae787 100644 --- a/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs +++ b/crates/vm/src/arch/execution_mode/tracegen/segmentation.rs @@ -7,7 +7,7 @@ use crate::{ execution_control::ExecutionControl, ExecutionError, ExecutionState, InstructionExecutor, VmChipComplex, VmConfig, VmSegmentState, }, - system::memory::{MemoryImage, INITIAL_TIMESTAMP}, + system::memory::INITIAL_TIMESTAMP, }; /// Check segment every 100 instructions. @@ -40,7 +40,7 @@ where } fn should_suspend( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, chip_complex: &VmChipComplex, ) -> bool { // Avoid checking segment too often. @@ -58,7 +58,7 @@ where fn on_start( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, ) { chip_complex @@ -68,7 +68,7 @@ where fn on_suspend_or_terminate( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, chip_complex: &mut VmChipComplex, exit_code: Option, ) { @@ -81,7 +81,7 @@ where /// Execute a single instruction fn execute_instruction( &self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, instruction: &Instruction, chip_complex: &mut VmChipComplex, ) -> Result<(), ExecutionError> @@ -96,6 +96,7 @@ where let memory_controller = &mut chip_complex.base.memory_controller; let new_state = executor.execute( memory_controller, + &mut state.streams, instruction, ExecutionState::new(state.pc, timestamp), )?; diff --git a/crates/vm/src/arch/extensions.rs b/crates/vm/src/arch/extensions.rs index c48f26258a..968248409c 100644 --- a/crates/vm/src/arch/extensions.rs +++ b/crates/vm/src/arch/extensions.rs @@ -2,7 +2,7 @@ use std::{ any::{Any, TypeId}, cell::RefCell, iter::once, - sync::{Arc, Mutex}, + sync::Arc, }; use derive_more::derive::From; @@ -36,7 +36,7 @@ use serde::{Deserialize, Serialize}; use super::{ vm_poseidon2_config, ExecutionBus, GenerationError, InstructionExecutor, PhantomSubExecutor, - Streams, SystemConfig, SystemTraceHeights, + SystemConfig, SystemTraceHeights, }; #[cfg(feature = "bench-metrics")] use crate::metrics::VmMetrics; @@ -122,7 +122,6 @@ pub struct SystemPort { pub struct VmInventoryBuilder<'a, F: PrimeField32> { system_config: &'a SystemConfig, system: &'a SystemBase, - streams: &'a Arc>>, bus_idx_mgr: BusIndexManager, /// Chips that are already included in the chipset and may be used /// as dependencies. The order should be that depended-on chips are ordered @@ -134,13 +133,11 @@ impl<'a, F: PrimeField32> VmInventoryBuilder<'a, F> { pub fn new( system_config: &'a SystemConfig, system: &'a SystemBase, - streams: &'a Arc>>, bus_idx_mgr: BusIndexManager, ) -> Self { Self { system_config, system, - streams, bus_idx_mgr, chips: Vec::new(), } @@ -193,11 +190,6 @@ impl<'a, F: PrimeField32> VmInventoryBuilder<'a, F> { Ok(()) } - /// Shareable streams. Clone to get a shared mutable reference. - pub fn streams(&self) -> &Arc>> { - self.streams - } - fn add_chip(&mut self, chip: &'a E) { self.chips.push(chip); } @@ -466,7 +458,6 @@ pub struct VmChipComplex { /// Absolute maximum value a trace height can be and still be provable. max_trace_height: usize, - streams: Arc>>, bus_idx_mgr: BusIndexManager, } @@ -635,11 +626,8 @@ impl SystemComplex { ); inventory.add_periphery_chip(chip); } - let streams = Arc::new(Mutex::new(Streams::default())); let phantom_opcode = SystemOpcode::PHANTOM.global_opcode(); - let mut phantom_chip = - PhantomChip::new(execution_bus, program_bus, SystemOpcode::CLASS_OFFSET); - phantom_chip.set_streams(streams.clone()); + let phantom_chip = PhantomChip::new(execution_bus, program_bus, SystemOpcode::CLASS_OFFSET); inventory .add_executor(RefCell::new(phantom_chip), [phantom_opcode]) .unwrap(); @@ -666,7 +654,6 @@ impl SystemComplex { base, inventory, bus_idx_mgr, - streams, overridden_inventory_heights: None, max_trace_height, } @@ -685,8 +672,7 @@ impl VmChipComplex { E: AnyEnum, P: AnyEnum, { - let mut builder = - VmInventoryBuilder::new(&self.config, &self.base, &self.streams, self.bus_idx_mgr); + let mut builder = VmInventoryBuilder::new(&self.config, &self.base, self.bus_idx_mgr); // Add range checker for convenience, the other system base chips aren't included - they can // be accessed directly from builder builder.add_chip(&self.base.range_checker_chip); @@ -731,7 +717,6 @@ impl VmChipComplex { base: self.base, inventory: self.inventory.transmute(), bus_idx_mgr: self.bus_idx_mgr, - streams: self.streams, overridden_inventory_heights: self.overridden_inventory_heights, max_trace_height: self.max_trace_height, } @@ -827,17 +812,6 @@ impl VmChipComplex { self.base.memory_controller.set_initial_memory(memory); } - /// Warning: this sets the stream in all chips which have a shared mutable reference to the - /// streams. - pub(crate) fn set_streams(&mut self, streams: Streams) { - *self.streams.lock().unwrap() = streams; - } - - /// This should **only** be called after segment execution has finished. - pub fn take_streams(&mut self) -> Streams { - std::mem::take(&mut self.streams.lock().unwrap()) - } - // This is O(1). pub fn num_airs(&self) -> usize { 3 + self.memory_controller().num_airs() + self.inventory.num_airs() diff --git a/crates/vm/src/arch/integration_api.rs b/crates/vm/src/arch/integration_api.rs index 5ea8e11cc7..9b45f816dd 100644 --- a/crates/vm/src/arch/integration_api.rs +++ b/crates/vm/src/arch/integration_api.rs @@ -17,7 +17,7 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use super::{ execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, - ExecutionState, InsExecutorE1, InstructionExecutor, Result, VmStateMut, + ExecutionState, InsExecutorE1, InstructionExecutor, Result, Streams, VmStateMut, }; use crate::system::memory::{ online::{GuestMemory, TracingMemory}, @@ -162,7 +162,7 @@ pub struct AdapterAirContext> { pub trait TraceStep { fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, // TODO(ayush): combine to a single struct trace: &mut [F], @@ -263,6 +263,7 @@ where fn execute( &mut self, memory: &mut MemoryController, + streams: &mut Streams, instruction: &Instruction, from_state: ExecutionState, ) -> Result> { @@ -270,6 +271,7 @@ where let state = VmStateMut { pc: &mut pc, memory: &mut memory.memory, + streams, ctx: &mut (), }; self.step.execute( @@ -392,7 +394,7 @@ where fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -400,7 +402,7 @@ where fn write( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, ) where @@ -411,7 +413,7 @@ where pub trait StepExecutorE1 { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -419,7 +421,7 @@ pub trait StepExecutorE1 { fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()>; @@ -432,7 +434,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -443,7 +445,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> diff --git a/crates/vm/src/arch/interpreter.rs b/crates/vm/src/arch/interpreter.rs index 3268b5492b..608861a523 100644 --- a/crates/vm/src/arch/interpreter.rs +++ b/crates/vm/src/arch/interpreter.rs @@ -4,7 +4,7 @@ use openvm_stark_backend::p3_field::{Field, PrimeField32}; use crate::{ arch::{ execution_control::ExecutionControl, execution_mode::E1E2ExecutionCtx, ExecutionError, - Streams, VmChipComplex, VmConfig, VmSegmentState, + Streams, VmConfig, VmSegmentState, }, system::memory::{online::GuestMemory, AddressMap}, }; @@ -28,14 +28,12 @@ impl> InterpretedInstance { &self, ctrl: CTRL, inputs: impl Into>, - ) -> Result, ExecutionError> + ) -> Result, ExecutionError> where CTRL::Ctx: E1E2ExecutionCtx, { // Initialize the chip complex let mut chip_complex = self.vm_config.create_chip_complex().unwrap(); - let inputs = inputs.into(); - chip_complex.set_streams(inputs); // Initialize the memory let memory = if self.vm_config.system().continuation_enabled { let mem_config = self.vm_config.system().memory_config; @@ -51,13 +49,8 @@ impl> InterpretedInstance { // Initialize the context let ctx = ctrl.initialize_context(); - let mut vm_state = VmSegmentState { - clk: 0, - pc: self.exe.pc_start, - memory, - exit_code: None, - ctx, - }; + + let mut vm_state = VmSegmentState::new(0, self.exe.pc_start, memory, inputs.into(), ctx); // Start execution ctrl.on_start(&mut vm_state, &mut chip_complex); diff --git a/crates/vm/src/arch/segment.rs b/crates/vm/src/arch/segment.rs index af57ad7fc9..4aae131d86 100644 --- a/crates/vm/src/arch/segment.rs +++ b/crates/vm/src/arch/segment.rs @@ -13,31 +13,38 @@ use openvm_stark_backend::{ Chip, }; +#[cfg(feature = "bench-metrics")] +use super::InstructionExecutor; use super::{ - execution_control::ExecutionControl, ExecutionError, GenerationError, SystemConfig, + execution_control::ExecutionControl, ExecutionError, GenerationError, Streams, SystemConfig, VmChipComplex, VmComplexTraceHeights, VmConfig, }; #[cfg(feature = "bench-metrics")] use crate::metrics::VmMetrics; -use crate::{ - arch::{instructions::*, InstructionExecutor}, - system::memory::online::GuestMemory, -}; +use crate::{arch::instructions::*, system::memory::online::GuestMemory}; -pub struct VmSegmentState { +pub struct VmSegmentState { pub clk: u64, pub pc: u32, pub memory: Option, + pub streams: Streams, pub exit_code: Option, pub ctx: Ctx, } -impl VmSegmentState { - pub fn new(clk: u64, pc: u32, memory: Option, ctx: Ctx) -> Self { +impl VmSegmentState { + pub fn new( + clk: u64, + pc: u32, + memory: Option, + streams: Streams, + ctx: Ctx, + ) -> Self { Self { clk, pc, memory, + streams, ctx, exit_code: None, } @@ -105,7 +112,7 @@ where /// Stopping is triggered by should_stop() or if VM is terminated pub fn execute_from_state( &mut self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, ) -> Result<(), ExecutionError> { let mut prev_backtrace: Option = None; @@ -133,7 +140,7 @@ where // TODO(ayush): clean this up, separate to smaller functions fn execute_instruction( &mut self, - state: &mut VmSegmentState, + state: &mut VmSegmentState, prev_backtrace: &mut Option, ) -> Result<(), ExecutionError> { let pc = state.pc; @@ -208,7 +215,7 @@ where } /// Returns bool of whether to switch to next segment or not. - fn should_suspend(&mut self, state: &mut VmSegmentState) -> bool { + fn should_suspend(&mut self, state: &mut VmSegmentState) -> bool { if !self.system_config().continuation_enabled { return false; } diff --git a/crates/vm/src/arch/testing/mod.rs b/crates/vm/src/arch/testing/mod.rs index fd0c73dc1a..320580125b 100644 --- a/crates/vm/src/arch/testing/mod.rs +++ b/crates/vm/src/arch/testing/mod.rs @@ -30,7 +30,7 @@ use tracing::Level; use super::{ExecutionBridge, ExecutionBus, InstructionExecutor, SystemPort}; use crate::{ - arch::{ExecutionState, MemoryConfig}, + arch::{ExecutionState, MemoryConfig, Streams}, system::{ memory::{ interface::MemoryInterface, @@ -62,6 +62,7 @@ const RANGE_CHECKER_BUS: BusIndex = 4; pub struct VmChipTestBuilder { pub memory: MemoryTester, + pub streams: Streams, pub execution: ExecutionTester, pub program: ProgramTester, rng: StdRng, @@ -72,6 +73,7 @@ pub struct VmChipTestBuilder { impl VmChipTestBuilder { pub fn new( memory_controller: MemoryController, + streams: Streams, execution_bus: ExecutionBus, program_bus: ProgramBus, rng: StdRng, @@ -79,6 +81,7 @@ impl VmChipTestBuilder { setup_tracing_with_log_level(Level::WARN); Self { memory: MemoryTester::new(memory_controller), + streams, execution: ExecutionTester::new(execution_bus), program: ProgramTester::new(program_bus), rng, @@ -110,7 +113,12 @@ impl VmChipTestBuilder { tracing::debug!(?initial_state.timestamp); let final_state = executor - .execute(&mut self.memory.controller, instruction, initial_state) + .execute( + &mut self.memory.controller, + &mut self.streams, + instruction, + initial_state, + ) .expect("Expected the execution not to fail"); self.program.execute(instruction, &initial_state); @@ -280,6 +288,7 @@ impl VmChipTestBuilder { ); Self { memory: MemoryTester::new(memory_controller), + streams: Default::default(), execution: ExecutionTester::new(ExecutionBus::new(EXECUTION_BUS)), program: ProgramTester::new(ProgramBus::new(READ_INSTRUCTION_BUS)), rng: StdRng::seed_from_u64(0), @@ -304,6 +313,7 @@ impl Default for VmChipTestBuilder { ); Self { memory: MemoryTester::new(memory_controller), + streams: Default::default(), execution: ExecutionTester::new(ExecutionBus::new(EXECUTION_BUS)), program: ProgramTester::new(ProgramBus::new(READ_INSTRUCTION_BUS)), rng: StdRng::seed_from_u64(0), diff --git a/crates/vm/src/arch/vm.rs b/crates/vm/src/arch/vm.rs index 36f625491f..f84b3ae1df 100644 --- a/crates/vm/src/arch/vm.rs +++ b/crates/vm/src/arch/vm.rs @@ -252,7 +252,6 @@ where let chip_complex = create_and_initialize_chip_complex( &self.config, exe.program.clone(), - from_state.input, Some(from_state.memory), ) .unwrap(); @@ -273,7 +272,8 @@ where segment.set_override_trace_heights(overridden_heights.clone()); } - let mut exec_state = VmSegmentState::new(from_state.clk, from_state.pc, None, ctx); + let mut exec_state = + VmSegmentState::new(from_state.clk, from_state.pc, None, from_state.input, ctx); metrics_span("execute_time_ms", || { segment.execute_from_state(&mut exec_state) })?; @@ -295,7 +295,7 @@ where .unwrap() .pc ); - let streams = segment.chip_complex.take_streams(); + let streams = exec_state.streams; #[cfg(feature = "bench-metrics")] let metrics = segment.metrics.partial_take(); @@ -364,24 +364,17 @@ where { let mem_config = self.config.system().memory_config; let exe = exe.into(); - let memory = AddressMap::from_sparse( + let memory = Some(GuestMemory::new(AddressMap::from_sparse( mem_config.as_offset, 1 << mem_config.as_height, 1 << mem_config.pointer_max_bits, exe.init_memory.clone(), - ); - - let state = VmState::new(0, exe.pc_start, memory, input); + ))); let _span = info_span!("execute_e1_until_cycle").entered(); - let chip_complex = create_and_initialize_chip_complex( - &self.config, - exe.program.clone(), - state.input, - None, - ) - .unwrap(); + let chip_complex = + create_and_initialize_chip_complex(&self.config, exe.program.clone(), None).unwrap(); let mut segment = VmSegmentExecutor::::new( chip_complex, self.trace_height_constraints.clone(), @@ -390,15 +383,10 @@ where ); #[cfg(feature = "bench-metrics")] { - segment.metrics = state.metrics; + segment.metrics = Default::default(); } - let mut exec_state = VmSegmentState::new( - state.clk, - state.pc, - Some(GuestMemory::new(state.memory)), - (), - ); + let mut exec_state = VmSegmentState::new(0, exe.pc_start, memory, input.into(), ()); metrics_span("execute_time_ms", || { segment.execute_from_state(&mut exec_state) })?; @@ -420,7 +408,7 @@ where clk: exec_state.clk, pc: exec_state.pc, memory: exec_state.memory.unwrap().memory, - input: segment.chip_complex.take_streams(), + input: exec_state.streams, #[cfg(feature = "bench-metrics")] metrics: segment.metrics.partial_take(), }; @@ -441,23 +429,17 @@ where let mem_config = self.config.system().memory_config; let exe = exe.into(); - let memory = AddressMap::from_sparse( + let memory = Some(GuestMemory::new(AddressMap::from_sparse( mem_config.as_offset, 1 << mem_config.as_height, 1 << mem_config.pointer_max_bits, exe.init_memory.clone(), - ); - let state = VmState::new(0, exe.pc_start, memory, input); + ))); let _span = info_span!("execute_metered").entered(); - let chip_complex = create_and_initialize_chip_complex( - &self.config, - exe.program.clone(), - state.input, - None, - ) - .unwrap(); + let chip_complex = + create_and_initialize_chip_complex(&self.config, exe.program.clone(), None).unwrap(); let air_names = chip_complex.air_names(); let ctrl = MeteredExecutionControl::new(&air_names, &widths, &interactions); let mut executor = VmSegmentExecutor::::new( @@ -469,7 +451,7 @@ where #[cfg(feature = "bench-metrics")] { - executor.metrics = state.metrics; + executor.metrics = Default::default(); } let continuations_enabled = executor @@ -501,12 +483,7 @@ where .memory_dimensions(), ); - let mut exec_state = VmSegmentState::new( - state.clk, - state.pc, - Some(GuestMemory::new(state.memory)), - ctx, - ); + let mut exec_state = VmSegmentState::new(0, exe.pc_start, memory, input.into(), ctx); metrics_span("execute_time_ms", || { executor.execute_from_state(&mut exec_state) })?; @@ -554,7 +531,6 @@ where let chip_complex = create_and_initialize_chip_complex( &self.config, exe.program.clone(), - state.input, Some(state.memory), ) .unwrap(); @@ -571,7 +547,7 @@ where segment.set_override_trace_heights(overridden_heights.clone()); } - let mut exec_state = VmSegmentState::new(state.clk, state.pc, None, ()); + let mut exec_state = VmSegmentState::new(state.clk, state.pc, None, state.input, ()); metrics_span("execute_from_state", || { segment.execute_from_state(&mut exec_state) })?; @@ -632,7 +608,7 @@ where let per_segment = self.execute_and_then( exe, input, - |seg_idx, mut seg| { + |seg_idx, seg| { // Note: this will only be Some on the last segment; otherwise it is // already moved into next segment state final_memory = Some(seg.chip_complex.memory_controller().memory_image().clone()); @@ -753,13 +729,8 @@ where input: impl Into>, ) -> Result, ExecutionError> { - let chip_complex = create_and_initialize_chip_complex( - &self.config, - exe.program.clone(), - input.into(), - None, - ) - .unwrap(); + let chip_complex = + create_and_initialize_chip_complex(&self.config, exe.program.clone(), None).unwrap(); let ctrl = TracegenExecutionControlWithSegmentation::new(chip_complex.air_names()); let mut segment = VmSegmentExecutor::new( chip_complex, @@ -776,6 +747,7 @@ where 0, exe.pc_start, None, + input.into(), TracegenCtx { since_last_segment_check: 0, }, @@ -1167,7 +1139,6 @@ where pub fn create_and_initialize_chip_complex( config: &VC, program: Program, - init_streams: Streams, initial_memory: Option, ) -> Result, VmInventoryError> where @@ -1175,7 +1146,6 @@ where VC: VmConfig, { let mut chip_complex = config.create_chip_complex()?; - chip_complex.set_streams(init_streams); // Strip debug info if profiling is disabled let program = if !config.system().profiling { diff --git a/crates/vm/src/system/memory/offline_checker/columns.rs b/crates/vm/src/system/memory/offline_checker/columns.rs index be5037d3ec..34ec716c30 100644 --- a/crates/vm/src/system/memory/offline_checker/columns.rs +++ b/crates/vm/src/system/memory/offline_checker/columns.rs @@ -1,8 +1,6 @@ //! Defines auxiliary columns for memory operations: `MemoryReadAuxCols`, //! `MemoryReadWithImmediateAuxCols`, and `MemoryWriteAuxCols`. -use std::ops::DerefMut; - use openvm_circuit_primitives::is_less_than::LessThanAuxCols; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_stark_backend::p3_field::PrimeField32; diff --git a/crates/vm/src/system/memory/online.rs b/crates/vm/src/system/memory/online.rs index 2d6a292729..c334d6017e 100644 --- a/crates/vm/src/system/memory/online.rs +++ b/crates/vm/src/system/memory/online.rs @@ -7,7 +7,7 @@ use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; use super::{ - adapter::{AccessAdapterInventory, AdapterInventoryTraceCursor}, + adapter::AccessAdapterInventory, offline_checker::MemoryBus, paged_vec::{AddressMap, PAGE_SIZE}, Address, MemoryAddress, PagedVec, diff --git a/crates/vm/src/system/memory/paged_vec.rs b/crates/vm/src/system/memory/paged_vec.rs index ead78f81ee..6b5b48b14f 100644 --- a/crates/vm/src/system/memory/paged_vec.rs +++ b/crates/vm/src/system/memory/paged_vec.rs @@ -1,10 +1,4 @@ -use std::{ - alloc::{alloc, Layout}, - fmt::Debug, - marker::PhantomData, - mem::MaybeUninit, - ptr, -}; +use std::{fmt::Debug, marker::PhantomData, mem::MaybeUninit, ptr}; use itertools::{zip_eq, Itertools}; use openvm_instructions::exe::SparseMemoryImage; diff --git a/crates/vm/src/system/native_adapter/mod.rs b/crates/vm/src/system/native_adapter/mod.rs index 25729e9376..adbc6215a0 100644 --- a/crates/vm/src/system/native_adapter/mod.rs +++ b/crates/vm/src/system/native_adapter/mod.rs @@ -279,7 +279,7 @@ where #[inline(always)] fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -312,7 +312,7 @@ where #[inline(always)] fn write( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, ) where diff --git a/crates/vm/src/system/phantom/mod.rs b/crates/vm/src/system/phantom/mod.rs index a49a227baf..e184796ed2 100644 --- a/crates/vm/src/system/phantom/mod.rs +++ b/crates/vm/src/system/phantom/mod.rs @@ -1,6 +1,6 @@ use std::{ borrow::{Borrow, BorrowMut}, - sync::{Arc, Mutex, OnceLock}, + sync::Arc, }; use openvm_circuit_primitives_derive::AlignedBorrow; @@ -92,7 +92,6 @@ impl Air for PhantomAir { pub struct PhantomChip { pub air: PhantomAir, pub rows: Vec>, - streams: OnceLock>>>, phantom_executors: FxHashMap>>, } @@ -104,17 +103,10 @@ impl PhantomChip { phantom_opcode: VmOpcode::from_usize(offset + SystemOpcode::PHANTOM.local_usize()), }, rows: vec![], - streams: OnceLock::new(), phantom_executors: FxHashMap::default(), } } - pub fn set_streams(&mut self, streams: Arc>>) { - if self.streams.set(streams).is_err() { - panic!("Streams should only be set once"); - } - } - pub(crate) fn add_sub_executor + 'static>( &mut self, sub_executor: P, @@ -131,7 +123,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<(), ExecutionError> where @@ -154,13 +146,12 @@ where discriminant, } })?; - let mut streams = self.streams.get().unwrap().lock().unwrap(); // TODO(ayush): implement phantom subexecutor for new traits sub_executor .as_ref() .phantom_execute( state.memory, - &mut streams, + state.streams, discriminant, a.as_canonical_u32(), b.as_canonical_u32(), @@ -180,7 +171,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, _chip_index: usize, ) -> Result<(), ExecutionError> { @@ -194,6 +185,7 @@ impl InstructionExecutor for PhantomChip { fn execute( &mut self, memory: &mut MemoryController, + streams: &mut Streams, instruction: &Instruction, from_state: ExecutionState, ) -> Result, ExecutionError> { @@ -208,6 +200,7 @@ impl InstructionExecutor for PhantomChip { let mut state = VmStateMut { pc: &mut pc, memory: &mut memory.memory.data, + streams, ctx: &mut E1Ctx::default(), }; self.execute_e1(&mut state, instruction)?; diff --git a/crates/vm/src/system/phantom/tests.rs b/crates/vm/src/system/phantom/tests.rs index 7a0b068d36..e2aef1119e 100644 --- a/crates/vm/src/system/phantom/tests.rs +++ b/crates/vm/src/system/phantom/tests.rs @@ -1,5 +1,3 @@ -use std::sync::{Arc, Mutex}; - use openvm_instructions::{instruction::Instruction, SystemOpcode}; use openvm_stark_backend::p3_field::{FieldAlgebra, PrimeField32}; use openvm_stark_sdk::p3_baby_bear::BabyBear; @@ -16,7 +14,6 @@ fn test_nops_and_terminate() { tester.program_bus(), SystemOpcode::CLASS_OFFSET, ); - chip.set_streams(Arc::new(Mutex::new(Default::default()))); let nop = Instruction::from_isize(SystemOpcode::PHANTOM.global_opcode(), 0, 0, 0, 0, 0); let mut state: ExecutionState = ExecutionState::new(F::ZERO, F::ONE); diff --git a/crates/vm/src/system/public_values/core.rs b/crates/vm/src/system/public_values/core.rs index 472dd66120..958497e0c4 100644 --- a/crates/vm/src/system/public_values/core.rs +++ b/crates/vm/src/system/public_values/core.rs @@ -164,7 +164,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -232,7 +232,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -260,7 +260,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/crates/vm/tests/integration_test.rs b/crates/vm/tests/integration_test.rs index a726a74830..d2b911785f 100644 --- a/crates/vm/tests/integration_test.rs +++ b/crates/vm/tests/integration_test.rs @@ -719,13 +719,8 @@ fn test_hint_load_1() { let program = Program::from_instructions(&instructions); - let chip_complex = create_and_initialize_chip_complex( - &test_native_config(), - program, - vec![vec![F::ONE, F::TWO]].into(), - None, - ) - .unwrap(); + let chip_complex = + create_and_initialize_chip_complex(&test_native_config(), program, None).unwrap(); let ctrl = TracegenExecutionControlWithSegmentation::new(chip_complex.air_names()); let ctx = ExecutionControl::::initialize_context(&ctrl); let mut segment = VmSegmentExecutor::::new( @@ -735,10 +730,10 @@ fn test_hint_load_1() { ctrl, ); - let mut exec_state = VmSegmentState::new(0, 0, None, ctx); + let mut exec_state = VmSegmentState::new(0, 0, None, vec![vec![F::ONE, F::TWO]].into(), ctx); segment.execute_from_state(&mut exec_state).unwrap(); - let streams = segment.chip_complex.take_streams(); + let streams = exec_state.streams; assert!(streams.input_stream.is_empty()); assert_eq!(streams.hint_stream, VecDeque::from(vec![F::ZERO])); assert_eq!(streams.hint_space, vec![vec![F::ONE, F::TWO]]); @@ -766,13 +761,8 @@ fn test_hint_load_2() { let program = Program::from_instructions(&instructions); - let chip_complex = create_and_initialize_chip_complex( - &test_native_config(), - program, - vec![vec![F::ONE, F::TWO], vec![F::TWO, F::ONE]].into(), - None, - ) - .unwrap(); + let chip_complex = + create_and_initialize_chip_complex(&test_native_config(), program, None).unwrap(); let ctrl = TracegenExecutionControlWithSegmentation::new(chip_complex.air_names()); let ctx = ExecutionControl::::initialize_context(&ctrl); let mut segment = VmSegmentExecutor::::new( @@ -782,7 +772,13 @@ fn test_hint_load_2() { ctrl, ); - let mut exec_state = VmSegmentState::new(0, 0, None, ctx); + let mut exec_state = VmSegmentState::new( + 0, + 0, + None, + vec![vec![F::ONE, F::TWO], vec![F::TWO, F::ONE]].into(), + ctx, + ); segment.execute_from_state(&mut exec_state).unwrap(); let [read] = unsafe { @@ -794,7 +790,7 @@ fn test_hint_load_2() { .read::(4, 32) }; assert_eq!(read, F::ZERO); - let streams = segment.chip_complex.take_streams(); + let streams = exec_state.streams; assert!(streams.input_stream.is_empty()); assert_eq!(streams.hint_stream, VecDeque::from(vec![F::ONE])); assert_eq!( diff --git a/extensions/algebra/circuit/src/modular_chip/is_eq.rs b/extensions/algebra/circuit/src/modular_chip/is_eq.rs index 6e53552f5c..ecbb927a53 100644 --- a/extensions/algebra/circuit/src/modular_chip/is_eq.rs +++ b/extensions/algebra/circuit/src/modular_chip/is_eq.rs @@ -311,7 +311,7 @@ where { fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -425,7 +425,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -463,7 +463,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/keccak256/circuit/src/lib.rs b/extensions/keccak256/circuit/src/lib.rs index 6c3300ab01..4c9d1ebc70 100644 --- a/extensions/keccak256/circuit/src/lib.rs +++ b/extensions/keccak256/circuit/src/lib.rs @@ -94,7 +94,7 @@ impl KeccakVmStep { impl StepExecutorE1 for KeccakVmStep { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -125,7 +125,8 @@ impl StepExecutorE1 for KeccakVmStep { // TODO(ayush): read in a single call let mut message = Vec::with_capacity(len as usize); for offset in (0..len as usize).step_by(KECCAK_WORD_SIZE) { - let read = memory_read_from_state::<_, KECCAK_WORD_SIZE>(state, e, src + offset as u32); + let read = + memory_read_from_state::(state, e, src + offset as u32); let copy_len = std::cmp::min(KECCAK_WORD_SIZE, (len as usize) - offset); message.extend_from_slice(&read[..copy_len]); } @@ -142,7 +143,7 @@ impl StepExecutorE1 for KeccakVmStep { fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { @@ -170,7 +171,8 @@ impl StepExecutorE1 for KeccakVmStep { let mut message = Vec::with_capacity(len as usize); for offset in (0..len as usize).step_by(KECCAK_WORD_SIZE) { - let read = memory_read_from_state::<_, KECCAK_WORD_SIZE>(state, e, src + offset as u32); + let read = + memory_read_from_state::(state, e, src + offset as u32); let copy_len = std::cmp::min(KECCAK_WORD_SIZE, (len as usize) - offset); message.extend_from_slice(&read[..copy_len]); } @@ -182,7 +184,7 @@ impl StepExecutorE1 for KeccakVmStep { hasher.finalize(&mut output); for (i, word) in output.chunks_exact(KECCAK_WORD_SIZE).enumerate() { - memory_write_from_state::<_, KECCAK_WORD_SIZE>( + memory_write_from_state::( state, e, dst + (i * KECCAK_WORD_SIZE) as u32, diff --git a/extensions/keccak256/circuit/src/trace.rs b/extensions/keccak256/circuit/src/trace.rs index 52ed933375..32adea2cf1 100644 --- a/extensions/keccak256/circuit/src/trace.rs +++ b/extensions/keccak256/circuit/src/trace.rs @@ -29,7 +29,7 @@ use crate::{columns::NUM_KECCAK_VM_COLS, utils::num_keccak_f, KeccakVmStep, KECC impl TraceStep for KeccakVmStep { fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, diff --git a/extensions/native/circuit/src/adapters/alu_native_adapter.rs b/extensions/native/circuit/src/adapters/alu_native_adapter.rs index 1387f16116..871ee2ae39 100644 --- a/extensions/native/circuit/src/adapters/alu_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/alu_native_adapter.rs @@ -244,7 +244,7 @@ where #[inline(always)] fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -261,7 +261,7 @@ where #[inline(always)] fn write( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, ) where diff --git a/extensions/native/circuit/src/adapters/branch_native_adapter.rs b/extensions/native/circuit/src/adapters/branch_native_adapter.rs index d764f9ad42..62fec23b2c 100644 --- a/extensions/native/circuit/src/adapters/branch_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/branch_native_adapter.rs @@ -227,7 +227,7 @@ where #[inline(always)] fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -244,7 +244,7 @@ where #[inline(always)] fn write( &self, - _state: &mut VmStateMut, + _state: &mut VmStateMut, _instruction: &Instruction, _data: &Self::WriteData, ) { diff --git a/extensions/native/circuit/src/adapters/convert_adapter.rs b/extensions/native/circuit/src/adapters/convert_adapter.rs index fe1ee70778..9c60adc7fa 100644 --- a/extensions/native/circuit/src/adapters/convert_adapter.rs +++ b/extensions/native/circuit/src/adapters/convert_adapter.rs @@ -217,7 +217,7 @@ where #[inline(always)] fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -233,7 +233,7 @@ where #[inline(always)] fn write( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, ) where diff --git a/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs b/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs index d6eaf0a3bf..07786065dd 100644 --- a/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs +++ b/extensions/native/circuit/src/adapters/loadstore_native_adapter.rs @@ -330,7 +330,7 @@ where fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -374,7 +374,7 @@ where fn write( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, ) where diff --git a/extensions/native/circuit/src/adapters/mod.rs b/extensions/native/circuit/src/adapters/mod.rs index fcacf06000..23b7d71b8b 100644 --- a/extensions/native/circuit/src/adapters/mod.rs +++ b/extensions/native/circuit/src/adapters/mod.rs @@ -53,7 +53,7 @@ where #[inline(always)] pub fn memory_read_native_from_state( - state: &mut VmStateMut, + state: &mut VmStateMut, ptr: u32, ) -> [F; N] where @@ -69,7 +69,7 @@ where #[inline(always)] pub fn memory_read_or_imm_native_from_state( - state: &mut VmStateMut, + state: &mut VmStateMut, addr_space: u32, ptr_or_imm: F, ) -> F @@ -89,7 +89,7 @@ where #[inline(always)] pub fn memory_write_native_from_state( - state: &mut VmStateMut, + state: &mut VmStateMut, ptr: u32, data: &[F; N], ) where diff --git a/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs b/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs index bf57a3b4e6..dcccc6df30 100644 --- a/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs +++ b/extensions/native/circuit/src/adapters/native_vectorized_adapter.rs @@ -225,7 +225,7 @@ where #[inline(always)] fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -245,7 +245,7 @@ where #[inline(always)] fn write( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, ) where diff --git a/extensions/native/circuit/src/branch_eq/core.rs b/extensions/native/circuit/src/branch_eq/core.rs index 6e9dd1f8c1..44b7deec47 100644 --- a/extensions/native/circuit/src/branch_eq/core.rs +++ b/extensions/native/circuit/src/branch_eq/core.rs @@ -53,7 +53,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -111,7 +111,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -140,7 +140,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/native/circuit/src/castf/core.rs b/extensions/native/circuit/src/castf/core.rs index eb05f5419a..0a4f7120da 100644 --- a/extensions/native/circuit/src/castf/core.rs +++ b/extensions/native/circuit/src/castf/core.rs @@ -134,7 +134,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -200,7 +200,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -226,7 +226,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/native/circuit/src/extension.rs b/extensions/native/circuit/src/extension.rs index 8fde829efb..b380b2bb12 100644 --- a/extensions/native/circuit/src/extension.rs +++ b/extensions/native/circuit/src/extension.rs @@ -102,7 +102,7 @@ impl VmExtension for Native { let range_checker = &builder.system_base().range_checker_chip; - let mut load_store_chip = NativeLoadStoreChip::::new( + let load_store_chip = NativeLoadStoreChip::::new( VmAirWrapper::new( NativeLoadStoreAdapterAir::new( memory_bridge, @@ -117,13 +117,12 @@ impl VmExtension for Native { MAX_INS_CAPACITY, builder.system_base().memory_controller.helper(), ); - load_store_chip.step.set_streams(builder.streams().clone()); inventory.add_executor( load_store_chip, NativeLoadStoreOpcode::iter().map(|x| x.global_opcode()), )?; - let mut block_load_store_chip = NativeLoadStoreChip::::new( + let block_load_store_chip = NativeLoadStoreChip::::new( VmAirWrapper::new( NativeLoadStoreAdapterAir::new( memory_bridge, @@ -138,9 +137,6 @@ impl VmExtension for Native { MAX_INS_CAPACITY, builder.system_base().memory_controller.helper(), ); - block_load_store_chip - .step - .set_streams(builder.streams().clone()); inventory.add_executor( block_load_store_chip, NativeLoadStore4Opcode::iter().map(|x| x.global_opcode()), @@ -224,7 +220,7 @@ impl VmExtension for Native { ExecutionBridge::new(execution_bus, program_bus), memory_bridge, ), - FriReducedOpeningStep::new(builder.streams().clone()), + FriReducedOpeningStep::new(), MAX_INS_CAPACITY, builder.system_base().memory_controller.helper(), ); @@ -238,7 +234,6 @@ impl VmExtension for Native { builder.system_port(), Poseidon2Config::default(), VerifyBatchBus::new(builder.new_bus_idx()), - builder.streams().clone(), // TODO: this may use too much memory. MAX_INS_CAPACITY, builder.system_base().memory_controller.helper(), diff --git a/extensions/native/circuit/src/field_arithmetic/core.rs b/extensions/native/circuit/src/field_arithmetic/core.rs index 46cd317edb..b07b234c31 100644 --- a/extensions/native/circuit/src/field_arithmetic/core.rs +++ b/extensions/native/circuit/src/field_arithmetic/core.rs @@ -147,7 +147,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -209,7 +209,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -233,7 +233,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/native/circuit/src/field_extension/core.rs b/extensions/native/circuit/src/field_extension/core.rs index 3ad3ce7837..ecd9a58189 100644 --- a/extensions/native/circuit/src/field_extension/core.rs +++ b/extensions/native/circuit/src/field_extension/core.rs @@ -174,7 +174,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -237,7 +237,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -265,7 +265,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/native/circuit/src/fri/mod.rs b/extensions/native/circuit/src/fri/mod.rs index 46f217fc85..fa75962048 100644 --- a/extensions/native/circuit/src/fri/mod.rs +++ b/extensions/native/circuit/src/fri/mod.rs @@ -2,15 +2,14 @@ use core::ops::Deref; use std::{ borrow::{Borrow, BorrowMut}, mem::offset_of, - sync::{Arc, Mutex}, }; use itertools::zip_eq; use openvm_circuit::{ arch::{ execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, - ExecutionBridge, ExecutionState, NewVmChipWrapper, Result, StepExecutorE1, Streams, - TraceStep, VmStateMut, + ExecutionBridge, ExecutionState, NewVmChipWrapper, Result, StepExecutorE1, TraceStep, + VmStateMut, }, system::memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols, AUX_LEN}, @@ -543,12 +542,14 @@ fn elem_to_ext(elem: F) -> [F; EXT_DEG] { } pub struct FriReducedOpeningStep { - streams: Arc>>, + phantom: std::marker::PhantomData, } impl FriReducedOpeningStep { - pub fn new(streams: Arc>>) -> Self { - Self { streams } + pub fn new() -> Self { + Self { + phantom: std::marker::PhantomData, + } } } @@ -563,7 +564,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -616,8 +617,7 @@ where let write_a = F::ONE - is_init_read; let data = if is_init == 0 { - let mut streams = self.streams.lock().unwrap(); - let hint_steam = &mut streams.hint_space[hint_id]; + let hint_steam = &mut state.streams.hint_space[hint_id]; hint_steam.drain(0..length).collect() } else { vec![] @@ -786,7 +786,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -824,8 +824,7 @@ where let length = length.as_canonical_u32() as usize; let data = if is_init == 0 { - let mut streams = self.streams.lock().unwrap(); - let hint_steam = &mut streams.hint_space[hint_id]; + let hint_steam = &mut state.streams.hint_space[hint_id]; hint_steam.drain(0..length).collect() } else { vec![] @@ -865,7 +864,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/native/circuit/src/fri/tests.rs b/extensions/native/circuit/src/fri/tests.rs index 5779be638c..b61c45da6e 100644 --- a/extensions/native/circuit/src/fri/tests.rs +++ b/extensions/native/circuit/src/fri/tests.rs @@ -1,10 +1,5 @@ -use std::sync::{Arc, Mutex}; - use itertools::Itertools; -use openvm_circuit::arch::{ - testing::{memory::gen_pointer, VmChipTestBuilder}, - Streams, -}; +use openvm_circuit::arch::testing::{memory::gen_pointer, VmChipTestBuilder}; use openvm_instructions::{instruction::Instruction, LocalOpcode}; use openvm_native_compiler::FriOpcode::FRI_REDUCED_OPENING; use openvm_stark_backend::{ @@ -24,13 +19,10 @@ use crate::fri::OVERALL_WIDTH; const MAX_INS_CAPACITY: usize = 1024; type F = BabyBear; -fn create_test_chip( - tester: &VmChipTestBuilder, - streams: Arc>>, -) -> FriReducedOpeningChip { +fn create_test_chip(tester: &VmChipTestBuilder) -> FriReducedOpeningChip { FriReducedOpeningChip::::new( FriReducedOpeningAir::new(tester.execution_bridge(), tester.memory_bridge()), - FriReducedOpeningStep::new(streams), + FriReducedOpeningStep::new(), MAX_INS_CAPACITY, tester.memory_helper(), ) @@ -61,8 +53,7 @@ fn fri_mat_opening_air_test() { let mut tester = VmChipTestBuilder::default(); - let streams = Arc::new(Mutex::new(Streams::default())); - let mut chip = create_test_chip(&tester, streams.clone()); + let mut chip = create_test_chip(&tester); let mut rng = create_seeded_rng(); @@ -74,7 +65,7 @@ fn fri_mat_opening_air_test() { }; } - streams.lock().unwrap().hint_space = vec![vec![]]; + tester.streams.hint_space = vec![vec![]]; for _ in 0..num_ops { let alpha = gen_ext!(); @@ -126,7 +117,7 @@ fn fri_mat_opening_air_test() { ); if is_init == 0 { - streams.lock().unwrap().hint_space[0].extend_from_slice(&a); + tester.streams.hint_space[0].extend_from_slice(&a); } else { for (i, ai) in a.iter().enumerate() { tester.write(address_space, a_pointer + i, [*ai]); diff --git a/extensions/native/circuit/src/jal/mod.rs b/extensions/native/circuit/src/jal/mod.rs index bf3c9033f4..0fe359f4b2 100644 --- a/extensions/native/circuit/src/jal/mod.rs +++ b/extensions/native/circuit/src/jal/mod.rs @@ -174,7 +174,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -277,7 +277,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -330,7 +330,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/native/circuit/src/loadstore/core.rs b/extensions/native/circuit/src/loadstore/core.rs index ca3c22ff54..f0f49fd766 100644 --- a/extensions/native/circuit/src/loadstore/core.rs +++ b/extensions/native/circuit/src/loadstore/core.rs @@ -1,7 +1,6 @@ use std::{ array, borrow::{Borrow, BorrowMut}, - sync::{Arc, Mutex, OnceLock}, }; use openvm_circuit::{ @@ -9,7 +8,7 @@ use openvm_circuit::{ execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, instructions::LocalOpcode, AdapterAirContext, AdapterExecutorE1, AdapterTraceStep, ExecutionError, Result, - StepExecutorE1, Streams, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, + StepExecutorE1, TraceStep, VmAdapterInterface, VmCoreAir, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -122,33 +121,18 @@ where } #[derive(Debug)] -pub struct NativeLoadStoreCoreStep -where - F: Field, -{ +pub struct NativeLoadStoreCoreStep { adapter: A, offset: usize, - pub streams: OnceLock>>>, } -impl NativeLoadStoreCoreStep -where - F: Field, -{ +impl NativeLoadStoreCoreStep { pub fn new(adapter: A, offset: usize) -> Self { - Self { - adapter, - offset, - streams: OnceLock::new(), - } - } - pub fn set_streams(&mut self, streams: Arc>>) { - self.streams.set(streams).unwrap(); + Self { adapter, offset } } } -impl TraceStep - for NativeLoadStoreCoreStep +impl TraceStep for NativeLoadStoreCoreStep where F: PrimeField32, A: 'static @@ -169,7 +153,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -187,11 +171,10 @@ where let (pointer_read, data_read) = self.adapter.read(state.memory, instruction, adapter_row); let data = if local_opcode == NativeLoadStoreOpcode::HINT_STOREW { - let mut streams = self.streams.get().unwrap().lock().unwrap(); - if streams.hint_stream.len() < NUM_CELLS { + if state.streams.hint_stream.len() < NUM_CELLS { return Err(ExecutionError::HintOutOfBounds { pc: *state.pc }); } - array::from_fn(|_| streams.hint_stream.pop_front().unwrap()) + array::from_fn(|_| state.streams.hint_stream.pop_front().unwrap()) } else { data_read }; @@ -223,7 +206,7 @@ where } } -impl StepExecutorE1 for NativeLoadStoreCoreStep +impl StepExecutorE1 for NativeLoadStoreCoreStep where F: PrimeField32, A: 'static @@ -231,7 +214,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -245,11 +228,10 @@ where let (_, data_read) = self.adapter.read(state, instruction); let data = if local_opcode == NativeLoadStoreOpcode::HINT_STOREW { - let mut streams = self.streams.get().unwrap().lock().unwrap(); - if streams.hint_stream.len() < NUM_CELLS { + if state.streams.hint_stream.len() < NUM_CELLS { return Err(ExecutionError::HintOutOfBounds { pc: *state.pc }); } - array::from_fn(|_| streams.hint_stream.pop_front().unwrap()) + array::from_fn(|_| state.streams.hint_stream.pop_front().unwrap()) } else { data_read }; @@ -263,7 +245,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/native/circuit/src/loadstore/mod.rs b/extensions/native/circuit/src/loadstore/mod.rs index e9dadaa038..705361d31d 100644 --- a/extensions/native/circuit/src/loadstore/mod.rs +++ b/extensions/native/circuit/src/loadstore/mod.rs @@ -12,7 +12,7 @@ use super::adapters::loadstore_native_adapter::{ pub type NativeLoadStoreAir = VmAirWrapper, NativeLoadStoreCoreAir>; -pub type NativeLoadStoreStep = - NativeLoadStoreCoreStep, F, NUM_CELLS>; +pub type NativeLoadStoreStep = + NativeLoadStoreCoreStep, NUM_CELLS>; pub type NativeLoadStoreChip = - NewVmChipWrapper, NativeLoadStoreStep>; + NewVmChipWrapper, NativeLoadStoreStep>; diff --git a/extensions/native/circuit/src/loadstore/tests.rs b/extensions/native/circuit/src/loadstore/tests.rs index af906cdfb9..5aa209cf52 100644 --- a/extensions/native/circuit/src/loadstore/tests.rs +++ b/extensions/native/circuit/src/loadstore/tests.rs @@ -1,6 +1,4 @@ -use std::sync::{Arc, Mutex}; - -use openvm_circuit::arch::{testing::VmChipTestBuilder, Streams, VmAirWrapper}; +use openvm_circuit::arch::{testing::VmChipTestBuilder, VmAirWrapper}; use openvm_instructions::{instruction::Instruction, LocalOpcode}; use openvm_native_compiler::NativeLoadStoreOpcode::{self, *}; use openvm_stark_backend::p3_field::{FieldAlgebra, PrimeField32}; @@ -30,7 +28,7 @@ struct TestData { } fn create_test_chip(tester: &VmChipTestBuilder) -> NativeLoadStoreChip { - let mut chip = NativeLoadStoreChip::::new( + NativeLoadStoreChip::::new( VmAirWrapper::new( NativeLoadStoreAdapterAir::new(tester.memory_bridge(), tester.execution_bridge()), NativeLoadStoreCoreAir::new(NativeLoadStoreOpcode::CLASS_OFFSET), @@ -41,10 +39,7 @@ fn create_test_chip(tester: &VmChipTestBuilder) -> NativeLoadStoreChip ), MAX_INS_CAPACITY, tester.memory_helper(), - ); - chip.step - .set_streams(Arc::new(Mutex::new(Streams::default()))); - chip + ) } fn gen_test_data(rng: &mut StdRng, opcode: NativeLoadStoreOpcode) -> TestData { @@ -78,11 +73,7 @@ fn get_data_pointer(data: &TestData) -> F { } } -fn set_values( - tester: &mut VmChipTestBuilder, - chip: &mut NativeLoadStoreChip, - data: &TestData, -) { +fn set_values(tester: &mut VmChipTestBuilder, data: &TestData) { if data.d != F::ZERO { tester.write( data.d.as_canonical_u32() as usize, @@ -105,14 +96,7 @@ fn set_values( } if data.is_hint { for _ in 0..data.e.as_canonical_u32() { - chip.step - .streams - .get() - .unwrap() - .lock() - .unwrap() - .hint_stream - .push_back(data.data_val); + tester.streams.hint_stream.push_back(data.data_val); } } } @@ -150,7 +134,7 @@ fn set_and_execute( opcode: NativeLoadStoreOpcode, ) { let data = gen_test_data(rng, opcode); - set_values(tester, chip, &data); + set_values(tester, &data); tester.execute_with_pc( chip, diff --git a/extensions/native/circuit/src/poseidon2/chip.rs b/extensions/native/circuit/src/poseidon2/chip.rs index dc325b2b72..8f5534a2e5 100644 --- a/extensions/native/circuit/src/poseidon2/chip.rs +++ b/extensions/native/circuit/src/poseidon2/chip.rs @@ -1,12 +1,9 @@ -use std::{ - borrow::{Borrow, BorrowMut}, - sync::{Arc, Mutex}, -}; +use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, - StepExecutorE1, Streams, TraceStep, VmStateMut, + StepExecutorE1, TraceStep, VmStateMut, }, system::memory::{ online::{GuestMemory, TracingMemory}, @@ -43,17 +40,15 @@ pub struct NativePoseidon2Step { // pre-computed Poseidon2 sub cols for dummy rows. empty_poseidon2_sub_cols: Vec, pub(super) subchip: Poseidon2SubChip, - pub(super) streams: Arc>>, } impl NativePoseidon2Step { - pub fn new(poseidon2_config: Poseidon2Config, streams: Arc>>) -> Self { + pub fn new(poseidon2_config: Poseidon2Config) -> Self { let subchip = Poseidon2SubChip::new(poseidon2_config.constants); let empty_poseidon2_sub_cols = subchip.generate_trace(vec![[F::ZERO; CHUNK * 2]]).values; Self { empty_poseidon2_sub_cols, subchip, - streams, } } @@ -73,7 +68,7 @@ impl TraceStep { fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -314,9 +309,8 @@ impl TraceStep let mut root = [F::ZERO; CHUNK]; let sibling_proof: Vec<[F; CHUNK]> = { - let streams = self.streams.lock().unwrap(); let proof_idx = proof_id.as_canonical_u32() as usize; - streams.hint_space[proof_idx] + state.streams.hint_space[proof_idx] .par_chunks(CHUNK) .map(|c| c.try_into().unwrap()) .collect() @@ -725,7 +719,7 @@ impl StepExecutorE1 { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> openvm_circuit::arch::Result<()> where @@ -737,7 +731,7 @@ impl StepExecutorE1 fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> openvm_circuit::arch::Result<()> { @@ -752,7 +746,7 @@ impl NativePoseidon2Step( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> usize where @@ -862,9 +856,8 @@ impl NativePoseidon2Step = { - let streams = self.streams.lock().unwrap(); let proof_idx = proof_id.as_canonical_u32() as usize; - streams.hint_space[proof_idx] + state.streams.hint_space[proof_idx] .par_chunks(CHUNK) .map(|c| c.try_into().unwrap()) .collect() diff --git a/extensions/native/circuit/src/poseidon2/mod.rs b/extensions/native/circuit/src/poseidon2/mod.rs index 7dde941126..98163b9a39 100644 --- a/extensions/native/circuit/src/poseidon2/mod.rs +++ b/extensions/native/circuit/src/poseidon2/mod.rs @@ -1,7 +1,7 @@ -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use openvm_circuit::{ - arch::{ExecutionBridge, NewVmChipWrapper, Streams, SystemPort}, + arch::{ExecutionBridge, NewVmChipWrapper, SystemPort}, system::memory::SharedMemoryHelper, }; use openvm_native_compiler::conversion::AS; @@ -30,7 +30,6 @@ pub fn new_native_poseidon2_chip( port: SystemPort, poseidon2_config: Poseidon2Config, verify_batch_bus: VerifyBatchBus, - streams: Arc>>, max_ins_capacity: usize, mem_helper: SharedMemoryHelper, ) -> NativePoseidon2Chip { @@ -42,7 +41,7 @@ pub fn new_native_poseidon2_chip( subair: Arc::new(Poseidon2SubAir::new(poseidon2_config.constants.into())), address_space: F::from_canonical_u32(AS::Native as u32), }, - NativePoseidon2Step::new(poseidon2_config, streams), + NativePoseidon2Step::new(poseidon2_config), max_ins_capacity, mem_helper, ) diff --git a/extensions/native/circuit/src/poseidon2/tests.rs b/extensions/native/circuit/src/poseidon2/tests.rs index b81af91380..ab6b4dc986 100644 --- a/extensions/native/circuit/src/poseidon2/tests.rs +++ b/extensions/native/circuit/src/poseidon2/tests.rs @@ -1,11 +1,8 @@ -use std::{ - cmp::min, - sync::{Arc, Mutex}, -}; +use std::cmp::min; use openvm_circuit::arch::{ testing::{memory::gen_pointer, VmChipTestBuilder, VmChipTester}, - verify_single, Streams, VirtualMachine, + verify_single, VirtualMachine, }; use openvm_instructions::{instruction::Instruction, program::Program, LocalOpcode, SystemOpcode}; use openvm_native_compiler::{ @@ -155,12 +152,10 @@ fn test(cases: [Case; N]) { let address_space = AS::Native as usize; let mut tester = VmChipTestBuilder::default(); - let streams = Arc::new(Mutex::new(Streams::default())); let mut chip = new_native_poseidon2_chip::( tester.system_port(), Poseidon2Config::default(), VERIFY_BATCH_BUS, - streams.clone(), MAX_INS_CAPACITY, tester.memory_helper(), ); @@ -171,7 +166,6 @@ fn test(cases: [Case; N]) { opened_element_size, } in cases { - let mut streams = streams.lock().unwrap(); let instance = random_instance(&mut rng, row_lengths, opened_element_size, |left, right| { let concatenated = @@ -205,7 +199,7 @@ fn test(cases: [Case; N]) { tester.write_usize(address_space, dim_register, [dim_base_pointer]); tester.write_usize(address_space, opened_register, [opened_base_pointer]); tester.write_usize(address_space, opened_length_register, [opened.len()]); - tester.write_usize(address_space, proof_id, [streams.hint_space.len()]); + tester.write_usize(address_space, proof_id, [tester.streams.hint_space.len()]); tester.write_usize(address_space, index_register, [index_base_pointer]); tester.write_usize(address_space, commit_register, [commit_pointer]); @@ -223,10 +217,10 @@ fn test(cases: [Case; N]) { tester.write(address_space, row_pointer + j, [opened_value]); } } - streams + tester + .streams .hint_space .push(proof.iter().flatten().copied().collect()); - drop(streams); for (i, &bit) in sibling_is_on_right.iter().enumerate() { tester.write(address_space, index_base_pointer + i, [F::from_bool(bit)]); } @@ -386,12 +380,10 @@ fn tester_with_random_poseidon2_ops(num_ops: usize) -> VmChipTester( tester.system_port(), Poseidon2Config::default(), VERIFY_BATCH_BUS, - streams.clone(), MAX_INS_CAPACITY, tester.memory_helper(), ); diff --git a/extensions/rv32-adapters/src/eq_mod.rs b/extensions/rv32-adapters/src/eq_mod.rs index e8e2c88f47..a366308f24 100644 --- a/extensions/rv32-adapters/src/eq_mod.rs +++ b/extensions/rv32-adapters/src/eq_mod.rs @@ -397,7 +397,7 @@ impl< fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -425,7 +425,7 @@ impl< fn write( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, ) where diff --git a/extensions/rv32-adapters/src/heap.rs b/extensions/rv32-adapters/src/heap.rs index d596092e39..d4e69fd20c 100644 --- a/extensions/rv32-adapters/src/heap.rs +++ b/extensions/rv32-adapters/src/heap.rs @@ -174,7 +174,7 @@ impl( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -186,7 +186,7 @@ impl( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, ) where diff --git a/extensions/rv32-adapters/src/heap_branch.rs b/extensions/rv32-adapters/src/heap_branch.rs index 8997b78f2f..86645ee011 100644 --- a/extensions/rv32-adapters/src/heap_branch.rs +++ b/extensions/rv32-adapters/src/heap_branch.rs @@ -294,7 +294,7 @@ impl AdapterExe fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -322,7 +322,7 @@ impl AdapterExe fn write( &self, - _state: &mut VmStateMut, + _state: &mut VmStateMut, _instruction: &Instruction, _data: &Self::WriteData, ) where diff --git a/extensions/rv32-adapters/src/vec_heap.rs b/extensions/rv32-adapters/src/vec_heap.rs index 924cff2f3c..78b48bcc74 100644 --- a/extensions/rv32-adapters/src/vec_heap.rs +++ b/extensions/rv32-adapters/src/vec_heap.rs @@ -466,7 +466,7 @@ impl< fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -496,7 +496,7 @@ impl< fn write( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, ) where diff --git a/extensions/rv32-adapters/src/vec_heap_two_reads.rs b/extensions/rv32-adapters/src/vec_heap_two_reads.rs index 2929ece00b..581b897840 100644 --- a/extensions/rv32-adapters/src/vec_heap_two_reads.rs +++ b/extensions/rv32-adapters/src/vec_heap_two_reads.rs @@ -524,7 +524,7 @@ impl< fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -554,7 +554,7 @@ impl< fn write( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, ) where diff --git a/extensions/rv32im/circuit/src/adapters/alu.rs b/extensions/rv32im/circuit/src/adapters/alu.rs index 271405fbab..8b5d62935b 100644 --- a/extensions/rv32im/circuit/src/adapters/alu.rs +++ b/extensions/rv32im/circuit/src/adapters/alu.rs @@ -288,7 +288,7 @@ where #[inline(always)] fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -318,7 +318,7 @@ where #[inline(always)] fn write( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, rd: &Self::WriteData, ) where diff --git a/extensions/rv32im/circuit/src/adapters/branch.rs b/extensions/rv32im/circuit/src/adapters/branch.rs index 7af331d7c5..873c66b2bc 100644 --- a/extensions/rv32im/circuit/src/adapters/branch.rs +++ b/extensions/rv32im/circuit/src/adapters/branch.rs @@ -197,7 +197,7 @@ where #[inline(always)] fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -219,7 +219,7 @@ where #[inline(always)] fn write( &self, - _state: &mut VmStateMut, + _state: &mut VmStateMut, _instruction: &Instruction, _data: &Self::WriteData, ) where diff --git a/extensions/rv32im/circuit/src/adapters/jalr.rs b/extensions/rv32im/circuit/src/adapters/jalr.rs index 29bc4552df..15431ae4f2 100644 --- a/extensions/rv32im/circuit/src/adapters/jalr.rs +++ b/extensions/rv32im/circuit/src/adapters/jalr.rs @@ -246,7 +246,7 @@ where #[inline(always)] fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -265,7 +265,7 @@ where #[inline(always)] fn write( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, ) where diff --git a/extensions/rv32im/circuit/src/adapters/loadstore.rs b/extensions/rv32im/circuit/src/adapters/loadstore.rs index 9ca3135af2..cad86880ed 100644 --- a/extensions/rv32im/circuit/src/adapters/loadstore.rs +++ b/extensions/rv32im/circuit/src/adapters/loadstore.rs @@ -566,7 +566,7 @@ where fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -630,7 +630,7 @@ where fn write( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, data: &Self::WriteData, ) where diff --git a/extensions/rv32im/circuit/src/adapters/mod.rs b/extensions/rv32im/circuit/src/adapters/mod.rs index 3de1add4b1..907f44d777 100644 --- a/extensions/rv32im/circuit/src/adapters/mod.rs +++ b/extensions/rv32im/circuit/src/adapters/mod.rs @@ -98,8 +98,8 @@ pub fn memory_write( } #[inline(always)] -pub fn memory_read_from_state( - state: &mut VmStateMut, +pub fn memory_read_from_state( + state: &mut VmStateMut, address_space: u32, ptr: u32, ) -> [u8; N] @@ -112,8 +112,8 @@ where } #[inline(always)] -pub fn memory_write_from_state( - state: &mut VmStateMut, +pub fn memory_write_from_state( + state: &mut VmStateMut, address_space: u32, ptr: u32, data: &[u8; N], @@ -243,8 +243,8 @@ pub fn new_read_rv32_register(memory: &GuestMemory, address_space: u32, ptr: u32 // TODO(AG): if "register", why `address_space` is not hardcoded to be 1? // TODO(jpw): remove new_ #[inline(always)] -pub fn new_read_rv32_register_from_state( - state: &mut VmStateMut, +pub fn new_read_rv32_register_from_state( + state: &mut VmStateMut, address_space: u32, ptr: u32, ) -> u32 diff --git a/extensions/rv32im/circuit/src/adapters/mul.rs b/extensions/rv32im/circuit/src/adapters/mul.rs index de5460e402..8b6f15118a 100644 --- a/extensions/rv32im/circuit/src/adapters/mul.rs +++ b/extensions/rv32im/circuit/src/adapters/mul.rs @@ -230,7 +230,7 @@ where #[inline(always)] fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -251,7 +251,7 @@ where #[inline(always)] fn write( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, rd: &Self::WriteData, ) where diff --git a/extensions/rv32im/circuit/src/adapters/rdwrite.rs b/extensions/rv32im/circuit/src/adapters/rdwrite.rs index d577d32a0b..2d84451788 100644 --- a/extensions/rv32im/circuit/src/adapters/rdwrite.rs +++ b/extensions/rv32im/circuit/src/adapters/rdwrite.rs @@ -267,7 +267,7 @@ where #[inline(always)] fn read( &self, - _state: &mut VmStateMut, + _state: &mut VmStateMut, _instruction: &Instruction, ) -> Self::ReadData where @@ -278,7 +278,7 @@ where #[inline(always)] fn write( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, rd: &Self::WriteData, ) where @@ -392,7 +392,7 @@ where #[inline(always)] fn read( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Self::ReadData where @@ -404,7 +404,7 @@ where #[inline(always)] fn write( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, rd: &Self::WriteData, ) where diff --git a/extensions/rv32im/circuit/src/auipc/core.rs b/extensions/rv32im/circuit/src/auipc/core.rs index 4d0bd3c31d..07b7f4d116 100644 --- a/extensions/rv32im/circuit/src/auipc/core.rs +++ b/extensions/rv32im/circuit/src/auipc/core.rs @@ -228,7 +228,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -312,7 +312,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -335,7 +335,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/base_alu/core.rs b/extensions/rv32im/circuit/src/base_alu/core.rs index f7435eb7e5..03c60c5d1c 100644 --- a/extensions/rv32im/circuit/src/base_alu/core.rs +++ b/extensions/rv32im/circuit/src/base_alu/core.rs @@ -210,7 +210,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -286,7 +286,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -307,7 +307,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/branch_eq/core.rs b/extensions/rv32im/circuit/src/branch_eq/core.rs index cdc5f9b65e..0d6d8d24c2 100644 --- a/extensions/rv32im/circuit/src/branch_eq/core.rs +++ b/extensions/rv32im/circuit/src/branch_eq/core.rs @@ -173,7 +173,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -230,7 +230,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -258,7 +258,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/branch_lt/core.rs b/extensions/rv32im/circuit/src/branch_lt/core.rs index d5c824df53..2b052b4c4f 100644 --- a/extensions/rv32im/circuit/src/branch_lt/core.rs +++ b/extensions/rv32im/circuit/src/branch_lt/core.rs @@ -236,7 +236,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -361,7 +361,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -387,7 +387,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/divrem/core.rs b/extensions/rv32im/circuit/src/divrem/core.rs index bfffbe6cc3..b4ed2d2b93 100644 --- a/extensions/rv32im/circuit/src/divrem/core.rs +++ b/extensions/rv32im/circuit/src/divrem/core.rs @@ -411,7 +411,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -545,7 +545,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -582,7 +582,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/extension.rs b/extensions/rv32im/circuit/src/extension.rs index 0959a2d906..0c5dd8c5ec 100644 --- a/extensions/rv32im/circuit/src/extension.rs +++ b/extensions/rv32im/circuit/src/extension.rs @@ -582,7 +582,7 @@ impl VmExtension for Rv32Io { chip }; - let mut hintstore_chip = Rv32HintStoreChip::::new( + let hintstore_chip = Rv32HintStoreChip::::new( Rv32HintStoreAir::new( ExecutionBridge::new(execution_bus, program_bus), memory_bridge, @@ -598,7 +598,6 @@ impl VmExtension for Rv32Io { MAX_INS_CAPACITY, builder.system_base().memory_controller.helper(), ); - hintstore_chip.step.set_streams(builder.streams().clone()); inventory.add_executor( hintstore_chip, diff --git a/extensions/rv32im/circuit/src/hintstore/mod.rs b/extensions/rv32im/circuit/src/hintstore/mod.rs index 38f223b95f..3014262c9a 100644 --- a/extensions/rv32im/circuit/src/hintstore/mod.rs +++ b/extensions/rv32im/circuit/src/hintstore/mod.rs @@ -1,13 +1,10 @@ -use std::{ - borrow::{Borrow, BorrowMut}, - sync::{Arc, Mutex, OnceLock}, -}; +use std::borrow::{Borrow, BorrowMut}; use openvm_circuit::{ arch::{ execution_mode::{metered::MeteredCtx, E1E2ExecutionCtx}, ExecutionBridge, ExecutionError, ExecutionState, NewVmChipWrapper, Result, StepExecutorE1, - Streams, TraceStep, VmStateMut, + TraceStep, VmStateMut, }, system::memory::{ offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, @@ -37,7 +34,6 @@ use openvm_stark_backend::{ p3_matrix::Matrix, rap::{BaseAirWithPublicValues, PartitionedBaseAir}, }; -use serde::{Deserialize, Serialize}; use crate::adapters::{ decompose, memory_read, memory_read_from_state, memory_write_from_state, tracing_read, @@ -264,14 +260,13 @@ impl Air for Rv32HintStoreAir { } } -pub struct Rv32HintStoreStep { +pub struct Rv32HintStoreStep { pointer_max_bits: usize, offset: usize, - pub streams: OnceLock>>>, bitwise_lookup_chip: SharedBitwiseOperationLookupChip, } -impl Rv32HintStoreStep { +impl Rv32HintStoreStep { pub fn new( bitwise_lookup_chip: SharedBitwiseOperationLookupChip, pointer_max_bits: usize, @@ -280,17 +275,12 @@ impl Rv32HintStoreStep { Self { pointer_max_bits, offset, - streams: OnceLock::new(), bitwise_lookup_chip, } } - - pub fn set_streams(&mut self, streams: Arc>>) { - self.streams.set(streams).unwrap(); - } } -impl TraceStep for Rv32HintStoreStep +impl TraceStep for Rv32HintStoreStep where F: PrimeField32, { @@ -306,7 +296,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -361,8 +351,7 @@ where debug_assert_ne!(num_words, 0); debug_assert!(num_words <= (1 << self.pointer_max_bits)); - let mut streams = self.streams.get().unwrap().lock().unwrap(); - if streams.hint_stream.len() < RV32_REGISTER_NUM_LIMBS * num_words as usize { + if state.streams.hint_stream.len() < RV32_REGISTER_NUM_LIMBS * num_words as usize { return Err(ExecutionError::HintOutOfBounds { pc: *state.pc }); } @@ -387,7 +376,7 @@ where } let data_f: [F; RV32_REGISTER_NUM_LIMBS] = - std::array::from_fn(|_| streams.hint_stream.pop_front().unwrap()); + std::array::from_fn(|_| state.streams.hint_stream.pop_front().unwrap()); let data: [u8; RV32_REGISTER_NUM_LIMBS] = data_f.map(|byte| byte.as_canonical_u32() as u8); @@ -439,13 +428,13 @@ where } } -impl StepExecutorE1 for Rv32HintStoreStep +impl StepExecutorE1 for Rv32HintStoreStep where F: PrimeField32, { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -480,14 +469,18 @@ where debug_assert_ne!(num_words, 0); debug_assert!(num_words <= (1 << self.pointer_max_bits)); - let mut streams = self.streams.get().unwrap().lock().unwrap(); - if streams.hint_stream.len() < RV32_REGISTER_NUM_LIMBS * num_words as usize { + if state.streams.hint_stream.len() < RV32_REGISTER_NUM_LIMBS * num_words as usize { return Err(ExecutionError::HintOutOfBounds { pc: *state.pc }); } for word_index in 0..num_words { let data: [u8; RV32_REGISTER_NUM_LIMBS] = std::array::from_fn(|_| { - streams.hint_stream.pop_front().unwrap().as_canonical_u32() as u8 + state + .streams + .hint_stream + .pop_front() + .unwrap() + .as_canonical_u32() as u8 }); memory_write_from_state( state, @@ -504,7 +497,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { @@ -534,4 +527,4 @@ where } } -pub type Rv32HintStoreChip = NewVmChipWrapper>; +pub type Rv32HintStoreChip = NewVmChipWrapper; diff --git a/extensions/rv32im/circuit/src/hintstore/tests.rs b/extensions/rv32im/circuit/src/hintstore/tests.rs index c56bfe185d..f8671c9f21 100644 --- a/extensions/rv32im/circuit/src/hintstore/tests.rs +++ b/extensions/rv32im/circuit/src/hintstore/tests.rs @@ -1,12 +1,8 @@ -use std::{ - array, - borrow::BorrowMut, - sync::{Arc, Mutex}, -}; +use std::{array, borrow::BorrowMut}; use openvm_circuit::arch::{ testing::{memory::gen_pointer, VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS}, - ExecutionBridge, Streams, + ExecutionBridge, }; use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, @@ -43,7 +39,7 @@ fn create_test_chip( let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS); let bitwise_chip = SharedBitwiseOperationLookupChip::::new(bitwise_bus); - let mut chip = Rv32HintStoreChip::::new( + let chip = Rv32HintStoreChip::::new( Rv32HintStoreAir::new( ExecutionBridge::new(tester.execution_bus(), tester.program_bus()), tester.memory_bridge(), @@ -55,8 +51,6 @@ fn create_test_chip( MAX_INS_CAPACITY, tester.memory_helper(), ); - chip.step - .set_streams(Arc::new(Mutex::new(Streams::default()))); (chip, bitwise_chip) } @@ -76,14 +70,7 @@ fn set_and_execute( let read_data: [F; RV32_REGISTER_NUM_LIMBS] = array::from_fn(|_| F::from_canonical_u32(rng.gen_range(0..(1 << RV32_CELL_BITS)))); for data in read_data { - chip.step - .streams - .get() - .unwrap() - .lock() - .unwrap() - .hint_stream - .push_back(data); + tester.streams.hint_stream.push_back(data); } tester.execute( @@ -117,14 +104,7 @@ fn set_and_execute_buffer( .collect(); for i in 0..num_words { for datum in data[i as usize] { - chip.step - .streams - .get() - .unwrap() - .lock() - .unwrap() - .hint_stream - .push_back(datum); + tester.streams.hint_stream.push_back(datum); } } diff --git a/extensions/rv32im/circuit/src/jal_lui/core.rs b/extensions/rv32im/circuit/src/jal_lui/core.rs index 18f32e290d..ca5a4647a7 100644 --- a/extensions/rv32im/circuit/src/jal_lui/core.rs +++ b/extensions/rv32im/circuit/src/jal_lui/core.rs @@ -183,7 +183,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -257,7 +257,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -292,7 +292,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/jalr/core.rs b/extensions/rv32im/circuit/src/jalr/core.rs index 0daac3d2bc..03f41abe03 100644 --- a/extensions/rv32im/circuit/src/jalr/core.rs +++ b/extensions/rv32im/circuit/src/jalr/core.rs @@ -218,7 +218,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -334,7 +334,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -365,7 +365,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/less_than/core.rs b/extensions/rv32im/circuit/src/less_than/core.rs index 7f5dfb2815..382b261b6c 100644 --- a/extensions/rv32im/circuit/src/less_than/core.rs +++ b/extensions/rv32im/circuit/src/less_than/core.rs @@ -207,7 +207,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -338,7 +338,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -365,7 +365,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/load_sign_extend/core.rs b/extensions/rv32im/circuit/src/load_sign_extend/core.rs index 3467579fde..6d8e7d9b5e 100644 --- a/extensions/rv32im/circuit/src/load_sign_extend/core.rs +++ b/extensions/rv32im/circuit/src/load_sign_extend/core.rs @@ -224,7 +224,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -317,7 +317,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -350,7 +350,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/loadstore/core.rs b/extensions/rv32im/circuit/src/loadstore/core.rs index 5d8dccef1b..9559c02715 100644 --- a/extensions/rv32im/circuit/src/loadstore/core.rs +++ b/extensions/rv32im/circuit/src/loadstore/core.rs @@ -296,7 +296,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -381,7 +381,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -409,7 +409,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/mul/core.rs b/extensions/rv32im/circuit/src/mul/core.rs index e31ffa3050..338036b203 100644 --- a/extensions/rv32im/circuit/src/mul/core.rs +++ b/extensions/rv32im/circuit/src/mul/core.rs @@ -170,7 +170,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -237,7 +237,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -265,7 +265,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/mulh/core.rs b/extensions/rv32im/circuit/src/mulh/core.rs index 2df390b0a9..8babb8e556 100644 --- a/extensions/rv32im/circuit/src/mulh/core.rs +++ b/extensions/rv32im/circuit/src/mulh/core.rs @@ -244,7 +244,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -327,7 +327,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -353,7 +353,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/rv32im/circuit/src/shift/core.rs b/extensions/rv32im/circuit/src/shift/core.rs index b914d1be42..8684659382 100644 --- a/extensions/rv32im/circuit/src/shift/core.rs +++ b/extensions/rv32im/circuit/src/shift/core.rs @@ -285,7 +285,7 @@ where fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, @@ -400,7 +400,7 @@ where { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -423,7 +423,7 @@ where fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { diff --git a/extensions/sha256/circuit/src/sha256_chip/mod.rs b/extensions/sha256/circuit/src/sha256_chip/mod.rs index 19ddd2bd2a..db4998018f 100644 --- a/extensions/sha256/circuit/src/sha256_chip/mod.rs +++ b/extensions/sha256/circuit/src/sha256_chip/mod.rs @@ -76,7 +76,7 @@ impl Sha256VmStep { impl StepExecutorE1 for Sha256VmStep { fn execute_e1( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, ) -> Result<()> where @@ -107,7 +107,8 @@ impl StepExecutorE1 for Sha256VmStep { // TODO(ayush): read in a single call let mut message = Vec::with_capacity(len as usize); for offset in (0..len as usize).step_by(SHA256_READ_SIZE) { - let read = memory_read_from_state::<_, SHA256_READ_SIZE>(state, e, src + offset as u32); + let read = + memory_read_from_state::(state, e, src + offset as u32); let copy_len = std::cmp::min(SHA256_READ_SIZE, (len as usize) - offset); message.extend_from_slice(&read[..copy_len]); } @@ -123,7 +124,7 @@ impl StepExecutorE1 for Sha256VmStep { fn execute_metered( &self, - state: &mut VmStateMut, + state: &mut VmStateMut, instruction: &Instruction, chip_index: usize, ) -> Result<()> { @@ -155,7 +156,8 @@ impl StepExecutorE1 for Sha256VmStep { let mut message = Vec::with_capacity(len as usize); for offset in (0..len as usize).step_by(SHA256_READ_SIZE) { - let read = memory_read_from_state::<_, SHA256_READ_SIZE>(state, e, src + offset as u32); + let read = + memory_read_from_state::(state, e, src + offset as u32); let copy_len = std::cmp::min(SHA256_READ_SIZE, (len as usize) - offset); message.extend_from_slice(&read[..copy_len]); } diff --git a/extensions/sha256/circuit/src/sha256_chip/trace.rs b/extensions/sha256/circuit/src/sha256_chip/trace.rs index e69e748073..cec8bc5580 100644 --- a/extensions/sha256/circuit/src/sha256_chip/trace.rs +++ b/extensions/sha256/circuit/src/sha256_chip/trace.rs @@ -33,7 +33,7 @@ use crate::{ impl TraceStep for Sha256VmStep { fn execute( &mut self, - state: VmStateMut, CTX>, + state: VmStateMut, CTX>, instruction: &Instruction, trace: &mut [F], trace_offset: &mut usize, From e8f34f435607ae815e763e761f74b2fb5de74f93 Mon Sep 17 00:00:00 2001 From: Golovanov399 Date: Thu, 5 Jun 2025 06:42:08 +0300 Subject: [PATCH 48/49] feat: make merkle tree finalization parallel in new execution (#1701) This resolves INT-4125. This make merkle tree finalization (and initialization as well) parallel. How: - `HasherChip::compress_and_record` now takes `&self` and not `&mut self`, - therefore we can call `compress_and_record` in parallel, - this is achieved by using `DashMap` instead of `FxHashMap` internally in the poseidon chip. Tracegen on reth went from [12k](https://github.com/axiom-crypto/openvm-reth-benchmark/blob/gh-pages/benchmarks-dispatch/refs/heads/feat/new-execution/reth-c16fa924c32aa800e1b0e84335b30184cf953bcb-f42afabc402cb0fbb9c853f62fc3e799a5667a6b608b4b470b6d23c2a266c81a.md) on `feat/new-execution` (or [9k](https://github.com/axiom-crypto/openvm-reth-benchmark/blob/gh-pages/benchmarks-dispatch/refs/heads/main/reth-fc7481de031adf2b39d9a6d1e4851345f048e7e9-e67d81427bc9a1def7136e8e1f1a20dbdb3b1df83ef2e97f151a48afd19ea268.md) on `main`) to [8.8k](https://github.com/axiom-crypto/openvm-reth-benchmark/blob/gh-pages/benchmarks-dispatch/refs/heads/feat/new-execution-another-openvm-branch-playground/reth-1a63b068532dae0a4ee61d3ddbaf6f90b9ddc8f1-569a60bc94420471b6767e34af0cf58d17d51ea92f5cab8a631838e49f910c2d.md), also went down on microbenchmarks. I believe the current tracegen is also slow(ish) due to our unnecessarily huge trace allocations sometimes. Also Merkle tree's `from_memory` is a little more optimized now --------- Co-authored-by: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> --- Cargo.lock | 16 ++ Cargo.toml | 1 + benchmarks/prove/Cargo.toml | 12 +- crates/vm/Cargo.toml | 3 +- crates/vm/src/arch/hasher/mod.rs | 6 +- crates/vm/src/system/memory/merkle/tree.rs | 247 ++++++++++++--------- crates/vm/src/system/poseidon2/chip.rs | 18 +- crates/vm/src/system/poseidon2/mod.rs | 2 +- crates/vm/src/system/poseidon2/tests.rs | 4 +- crates/vm/src/system/poseidon2/trace.rs | 15 +- 10 files changed, 192 insertions(+), 132 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ccce87fa32..794e52342c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2205,6 +2205,21 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", + "rayon", +] + [[package]] name = "der" version = "0.6.1" @@ -4841,6 +4856,7 @@ version = "1.1.1-rc.0" dependencies = [ "backtrace", "cfg-if", + "dashmap", "derivative", "derive-new 0.6.0", "derive_more 1.0.0", diff --git a/Cargo.toml b/Cargo.toml index 87eafe6f98..d4337a4bb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -221,6 +221,7 @@ rrs-lib = "0.1.0" rand = { version = "0.8.5", default-features = false } hex = { version = "0.4.3", default-features = false } serde-big-array = "0.5.1" +dashmap = "6.1.0" # default-features = false for no_std for use in guest programs itertools = { version = "0.14.0", default-features = false } diff --git a/benchmarks/prove/Cargo.toml b/benchmarks/prove/Cargo.toml index 7809fda948..d40e793123 100644 --- a/benchmarks/prove/Cargo.toml +++ b/benchmarks/prove/Cargo.toml @@ -44,14 +44,14 @@ tracing.workspace = true [features] default = ["parallel", "jemalloc", "bench-metrics"] -bench-metrics = ["openvm-native-recursion/bench-metrics"] +bench-metrics = ["openvm-sdk/bench-metrics"] profiling = ["openvm-sdk/profiling"] aggregation = [] -parallel = ["openvm-native-recursion/parallel"] -mimalloc = ["openvm-circuit/mimalloc"] -jemalloc = ["openvm-circuit/jemalloc"] -jemalloc-prof = ["openvm-circuit/jemalloc-prof"] -nightly-features = ["openvm-circuit/nightly-features"] +parallel = ["openvm-sdk/parallel"] +mimalloc = ["openvm-sdk/mimalloc"] +jemalloc = ["openvm-sdk/jemalloc"] +jemalloc-prof = ["openvm-sdk/jemalloc-prof"] +nightly-features = ["openvm-sdk/nightly-features"] [package.metadata.cargo-shear] ignored = ["derive_more"] diff --git a/crates/vm/Cargo.toml b/crates/vm/Cargo.toml index 80e6794b48..d5bd4c57f6 100644 --- a/crates/vm/Cargo.toml +++ b/crates/vm/Cargo.toml @@ -35,6 +35,7 @@ eyre.workspace = true derivative.workspace = true static_assertions.workspace = true getset.workspace = true +dashmap.workspace = true [dev-dependencies] test-log.workspace = true @@ -47,7 +48,7 @@ openvm-rv32im-transpiler.workspace = true [features] default = ["parallel", "jemalloc"] -parallel = ["openvm-stark-backend/parallel"] +parallel = ["openvm-stark-backend/parallel", "dashmap/rayon"] test-utils = ["dep:openvm-stark-sdk"] bench-metrics = ["dep:metrics", "openvm-stark-backend/bench-metrics"] function-span = ["bench-metrics"] diff --git a/crates/vm/src/arch/hasher/mod.rs b/crates/vm/src/arch/hasher/mod.rs index df90a55e4b..e858da25f9 100644 --- a/crates/vm/src/arch/hasher/mod.rs +++ b/crates/vm/src/arch/hasher/mod.rs @@ -24,10 +24,10 @@ pub trait Hasher { leaves[0] } } -pub trait HasherChip: Hasher { +pub trait HasherChip: Hasher + Send + Sync { /// Stateful version of `hash` for recording the event in the chip. - fn compress_and_record(&mut self, left: &[F; CHUNK], right: &[F; CHUNK]) -> [F; CHUNK]; - fn hash_and_record(&mut self, values: &[F; CHUNK]) -> [F; CHUNK] { + fn compress_and_record(&self, left: &[F; CHUNK], right: &[F; CHUNK]) -> [F; CHUNK]; + fn hash_and_record(&self, values: &[F; CHUNK]) -> [F; CHUNK] { self.compress_and_record(values, &[F::ZERO; CHUNK]) } } diff --git a/crates/vm/src/system/memory/merkle/tree.rs b/crates/vm/src/system/memory/merkle/tree.rs index 4a413b6dc3..47c1709111 100644 --- a/crates/vm/src/system/memory/merkle/tree.rs +++ b/crates/vm/src/system/memory/merkle/tree.rs @@ -1,4 +1,7 @@ -use openvm_stark_backend::p3_field::PrimeField32; +use openvm_stark_backend::{ + p3_field::PrimeField32, + p3_maybe_rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}, +}; use rustc_hash::FxHashMap; use super::{memory_to_partition, FinalState, MemoryMerkleCols}; @@ -33,133 +36,157 @@ impl MerkleTree { } } + #[allow(clippy::type_complexity)] /// Shared logic for both from_memory and finalize. fn process_layers( &mut self, layer: Vec<(u64, [F; CHUNK])>, md: &MemoryDimensions, mut rows: Option<&mut Vec>>, - mut compress: CompressFn, + compress: CompressFn, ) where - CompressFn: FnMut(&[F; CHUNK], &[F; CHUNK]) -> [F; CHUNK], + CompressFn: Fn(&[F; CHUNK], &[F; CHUNK]) -> [F; CHUNK] + Send + Sync, { - let mut layer = layer - .into_iter() - .map(|(index, values)| (index, values, self.get_node(index))) + let mut new_entries = layer; + let mut layer = new_entries + .par_iter() + .map(|(index, values)| { + let old_values = self.nodes.get(index).unwrap_or(&self.zero_nodes[0]); + (*index, *values, *old_values) + }) .collect::>(); for height in 1..=self.height { - let mut i = 0; - let mut new_layer = Vec::new(); - while i < layer.len() { - let (index, values, old_values) = layer[i]; - let par_index = index >> 1; - i += 1; - - let par_old_values = self.get_node(par_index); - - // Lowest `label_section_height` bits of `par_index` are the address label, - // The remaining highest are the address space label. - let label_section_height = md.address_height.saturating_sub(height); - let parent_address_label = (par_index & ((1 << label_section_height) - 1)) as u32; - let parent_as_label = - ((par_index & !(1 << (self.height - height))) >> label_section_height) as u32; - - self.nodes.insert(index, values); - - if i < layer.len() && layer[i].0 == index ^ 1 { - // sibling found - let (_, sibling_values, sibling_old_values) = layer[i]; - i += 1; - let combined = compress(&values, &sibling_values); - - // Only record rows if requested - if let Some(rows) = rows.as_deref_mut() { - rows.push(MemoryMerkleCols { - expand_direction: F::ONE, - height_section: F::from_bool(height > md.address_height), - parent_height: F::from_canonical_usize(height), - is_root: F::from_bool(height == md.overall_height()), - parent_as_label: F::from_canonical_u32(parent_as_label), - parent_address_label: F::from_canonical_u32(parent_address_label), - parent_hash: self.get_node(par_index), - left_child_hash: old_values, - right_child_hash: sibling_old_values, - left_direction_different: F::ZERO, - right_direction_different: F::ZERO, - }); - rows.push(MemoryMerkleCols { - expand_direction: F::NEG_ONE, - height_section: F::from_bool(height > md.address_height), - parent_height: F::from_canonical_usize(height), - is_root: F::from_bool(height == md.overall_height()), - parent_as_label: F::from_canonical_u32(parent_as_label), - parent_address_label: F::from_canonical_u32(parent_address_label), - parent_hash: combined, - left_child_hash: values, - right_child_hash: sibling_values, - left_direction_different: F::ZERO, - right_direction_different: F::ZERO, - }); - // This is a hacky way to say "and we also want to record the old values" - compress(&old_values, &sibling_old_values); + let new_layer = layer + .iter() + .enumerate() + .filter_map(|(i, (index, values, old_values))| { + if i > 0 && layer[i - 1].0 ^ 1 == *index { + return None; } - self.nodes.insert(index ^ 1, sibling_values); - new_layer.push((par_index, combined, par_old_values)); - } else { - // no sibling found - let sibling_values = self.get_node(index ^ 1); - let is_left = index % 2 == 0; - let (left, right) = if is_left { - (values, sibling_values) - } else { - (sibling_values, values) - }; - let combined = compress(&left, &right); + let par_index = index >> 1; - if let Some(rows) = rows.as_deref_mut() { - rows.push(MemoryMerkleCols { - expand_direction: F::ONE, - height_section: F::from_bool(height > md.address_height), - parent_height: F::from_canonical_usize(height), - is_root: F::from_bool(height == md.overall_height()), - parent_as_label: F::from_canonical_u32(parent_as_label), - parent_address_label: F::from_canonical_u32(parent_address_label), - parent_hash: self.get_node(par_index), - left_child_hash: if is_left { old_values } else { left }, - right_child_hash: if is_left { right } else { old_values }, - left_direction_different: F::ZERO, - right_direction_different: F::ZERO, - }); - rows.push(MemoryMerkleCols { - expand_direction: F::NEG_ONE, - height_section: F::from_bool(height > md.address_height), - parent_height: F::from_canonical_usize(height), - is_root: F::from_bool(height == md.overall_height()), - parent_as_label: F::from_canonical_u32(parent_as_label), - parent_address_label: F::from_canonical_u32(parent_address_label), - parent_hash: combined, - left_child_hash: left, - right_child_hash: right, - left_direction_different: F::from_bool(!is_left), - right_direction_different: F::from_bool(is_left), - }); - // This is a hacky way to say "and we also want to record the old values" - if is_left { - compress(&old_values, &right); - } else { - compress(&left, &old_values); - } + if i + 1 < layer.len() && layer[i + 1].0 == index ^ 1 { + let (_, sibling_values, sibling_old_values) = &layer[i + 1]; + Some(( + par_index, + Some((values, old_values)), + Some((sibling_values, sibling_old_values)), + )) + } else if index & 1 == 0 { + Some((par_index, Some((values, old_values)), None)) + } else { + Some((par_index, None, Some((values, old_values)))) } + }) + .collect::>(); - new_layer.push((par_index, combined, par_old_values)); + match rows { + None => { + layer = new_layer + .into_par_iter() + .map(|(par_index, left, right)| { + let left = if let Some(left) = left { + left.0 + } else { + &self.get_node(2 * par_index) + }; + let right = if let Some(right) = right { + right.0 + } else { + &self.get_node(2 * par_index + 1) + }; + let combined = compress(left, right); + let par_old_values = self.get_node(par_index); + (par_index, combined, par_old_values) + }) + .collect(); + } + Some(ref mut rows) => { + let label_section_height = md.address_height.saturating_sub(height); + let (tmp, new_rows): (Vec<(u64, [F; CHUNK], [F; CHUNK])>, Vec<[_; 2]>) = + new_layer + .into_par_iter() + .map(|(par_index, left, right)| { + let parent_address_label = + (par_index & ((1 << label_section_height) - 1)) as u32; + let parent_as_label = ((par_index & !(1 << (self.height - height))) + >> label_section_height) + as u32; + let left_node; + let (left, old_left, changed_left) = match left { + Some((left, old_left)) => (left, old_left, true), + None => { + left_node = self.get_node(2 * par_index); + (&left_node, &left_node, false) + } + }; + let right_node; + let (right, old_right, changed_right) = match right { + Some((right, old_right)) => (right, old_right, true), + None => { + right_node = self.get_node(2 * par_index + 1); + (&right_node, &right_node, false) + } + }; + let combined = compress(left, right); + // This is a hacky way to say: + // "and we also want to record the old values" + compress(old_left, old_right); + let par_old_values = self.get_node(par_index); + ( + (par_index, combined, par_old_values), + [ + MemoryMerkleCols { + expand_direction: F::ONE, + height_section: F::from_bool( + height > md.address_height, + ), + parent_height: F::from_canonical_usize(height), + is_root: F::from_bool(height == md.overall_height()), + parent_as_label: F::from_canonical_u32(parent_as_label), + parent_address_label: F::from_canonical_u32( + parent_address_label, + ), + parent_hash: par_old_values, + left_child_hash: *old_left, + right_child_hash: *old_right, + left_direction_different: F::ZERO, + right_direction_different: F::ZERO, + }, + MemoryMerkleCols { + expand_direction: F::NEG_ONE, + height_section: F::from_bool( + height > md.address_height, + ), + parent_height: F::from_canonical_usize(height), + is_root: F::from_bool(height == md.overall_height()), + parent_as_label: F::from_canonical_u32(parent_as_label), + parent_address_label: F::from_canonical_u32( + parent_address_label, + ), + parent_hash: combined, + left_child_hash: *left, + right_child_hash: *right, + left_direction_different: F::from_bool(!changed_left), + right_direction_different: F::from_bool(!changed_right), + }, + ], + ) + }) + .unzip(); + rows.extend(new_rows.into_iter().flatten()); + layer = tmp; } } - layer = new_layer; + new_entries.extend(layer.iter().map(|(idx, values, _)| (*idx, *values))); } - if !layer.is_empty() { - assert_eq!(layer.len(), 1); - self.nodes.insert(layer[0].0, layer[0].1); + + if self.nodes.is_empty() { + // This, for example, should happen in every `from_memory` call + self.nodes = FxHashMap::from_iter(new_entries); + } else { + self.nodes.extend(new_entries); } } diff --git a/crates/vm/src/system/poseidon2/chip.rs b/crates/vm/src/system/poseidon2/chip.rs index e0059f1ce1..a4fac35cd1 100644 --- a/crates/vm/src/system/poseidon2/chip.rs +++ b/crates/vm/src/system/poseidon2/chip.rs @@ -1,14 +1,18 @@ use std::{ array, - sync::{atomic::AtomicU32, Arc}, + sync::{ + atomic::{AtomicBool, AtomicU32}, + Arc, + }, }; +use dashmap::DashMap; use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubChip}; use openvm_stark_backend::{ interaction::{BusIndex, LookupBus}, p3_field::PrimeField32, }; -use rustc_hash::FxHashMap; +use rustc_hash::FxBuildHasher; use super::{ air::Poseidon2PeripheryAir, PERIPHERY_POSEIDON2_CHUNK_SIZE, PERIPHERY_POSEIDON2_WIDTH, @@ -19,7 +23,8 @@ use crate::arch::hasher::{Hasher, HasherChip}; pub struct Poseidon2PeripheryBaseChip { pub air: Arc>, pub subchip: Poseidon2SubChip, - pub records: FxHashMap<[F; PERIPHERY_POSEIDON2_WIDTH], AtomicU32>, + pub records: DashMap<[F; PERIPHERY_POSEIDON2_WIDTH], AtomicU32, FxBuildHasher>, + pub nonempty: AtomicBool, } impl Poseidon2PeripheryBaseChip { @@ -31,7 +36,8 @@ impl Poseidon2PeripheryBaseChip HasherChip [F; PERIPHERY_POSEIDON2_CHUNK_SIZE] { @@ -73,6 +79,8 @@ impl HasherChip Hasher for Poseidon2Per impl HasherChip for Poseidon2PeripheryChip { fn compress_and_record( - &mut self, + &self, lhs: &[F; PERIPHERY_POSEIDON2_CHUNK_SIZE], rhs: &[F; PERIPHERY_POSEIDON2_CHUNK_SIZE], ) -> [F; PERIPHERY_POSEIDON2_CHUNK_SIZE] { diff --git a/crates/vm/src/system/poseidon2/tests.rs b/crates/vm/src/system/poseidon2/tests.rs index 095c8acba4..2f620847e4 100644 --- a/crates/vm/src/system/poseidon2/tests.rs +++ b/crates/vm/src/system/poseidon2/tests.rs @@ -32,7 +32,7 @@ fn poseidon2_periphery_direct_test() { ) }); - let mut chip = Poseidon2PeripheryChip::::new( + let chip = Poseidon2PeripheryChip::::new( Poseidon2Config::default(), POSEIDON2_DIRECT_BUS, 3, @@ -86,7 +86,7 @@ fn poseidon2_periphery_duplicate_hashes_test() { }); let counts: [u32; NUM_OPS] = std::array::from_fn(|_| rng.next_u32() % 20); - let mut chip = Poseidon2PeripheryChip::::new( + let chip = Poseidon2PeripheryChip::::new( Poseidon2Config::default(), POSEIDON2_DIRECT_BUS, 3, diff --git a/crates/vm/src/system/poseidon2/trace.rs b/crates/vm/src/system/poseidon2/trace.rs index 979585c830..4a929b8d06 100644 --- a/crates/vm/src/system/poseidon2/trace.rs +++ b/crates/vm/src/system/poseidon2/trace.rs @@ -28,9 +28,11 @@ where let mut inputs = Vec::with_capacity(height); let mut multiplicities = Vec::with_capacity(height); - let (actual_inputs, actual_multiplicities): (Vec<_>, Vec<_>) = self - .records - .into_par_iter() + #[cfg(feature = "parallel")] + let records_iter = self.records.into_par_iter(); + #[cfg(not(feature = "parallel"))] + let records_iter = self.records.into_iter(); + let (actual_inputs, actual_multiplicities): (Vec<_>, Vec<_>) = records_iter .map(|(input, mult)| (input, mult.load(std::sync::atomic::Ordering::Relaxed))) .unzip(); inputs.extend(actual_inputs); @@ -66,7 +68,12 @@ impl ChipUsageGetter } fn current_trace_height(&self) -> usize { - self.records.len() + if self.nonempty.load(std::sync::atomic::Ordering::Relaxed) { + // Not to call `DashMap::len` too often + self.records.len() + } else { + 0 + } } fn trace_width(&self) -> usize { From 356162e6d01f44b8b9d2b57678b410bbb5bce8ef Mon Sep 17 00:00:00 2001 From: Golovanov399 Date: Fri, 6 Jun 2025 02:30:03 +0300 Subject: [PATCH 49/49] feat: Do not build a `BTreeMap` when we don't need to (#1718) Reth 21000000 tracegen [8.5](https://github.com/axiom-crypto/openvm-reth-benchmark/blob/gh-pages/benchmarks-dispatch/refs/heads/main/reth-b0d199966a0fb8aa4291bb898eb0ff7e1e721e75-fed5988df0993a3c5d0456cee0859ea17aacab67004fa00ee1a04f807143bb1a.md) -> [7.4](https://github.com/axiom-crypto/openvm-reth-benchmark/blob/gh-pages/benchmarks-dispatch/refs/heads/main/reth-33b8740cda3bb42b88c936c1b3ffa31e174c022b-dd1b108a50ec92cb6c91ff4b8e25fa58a5cd4628f28487650c8f9774eef505d1.md) --- crates/vm/src/system/memory/merkle/mod.rs | 27 +++++++++++----------- crates/vm/src/system/memory/merkle/tree.rs | 16 ++++++------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/crates/vm/src/system/memory/merkle/mod.rs b/crates/vm/src/system/memory/merkle/mod.rs index 35563969b6..964b580e68 100644 --- a/crates/vm/src/system/memory/merkle/mod.rs +++ b/crates/vm/src/system/memory/merkle/mod.rs @@ -1,7 +1,6 @@ use openvm_stark_backend::{interaction::PermutationCheckBus, p3_field::PrimeField32}; -use rustc_hash::FxHashSet; -use super::{controller::dimensions::MemoryDimensions, Equipartition, MemoryImage}; +use super::{controller::dimensions::MemoryDimensions, MemoryImage}; mod air; mod columns; mod trace; @@ -17,7 +16,6 @@ pub(super) use trace::SerialReceiver; pub struct MemoryMerkleChip { pub air: MemoryMerkleAir, - touched_nodes: FxHashSet<(usize, u32, u32)>, final_state: Option>, // TODO(AG): how are these two different? Doesn't one just end up being copied to the other? trace_height: Option, @@ -40,15 +38,12 @@ impl MemoryMerkleChip { ) -> Self { assert!(memory_dimensions.as_height > 0); assert!(memory_dimensions.address_height > 0); - let mut touched_nodes = FxHashSet::default(); - touched_nodes.insert((memory_dimensions.overall_height(), 0, 0)); Self { air: MemoryMerkleAir { memory_dimensions, merkle_bus, compression_bus, }, - touched_nodes, final_state: None, trace_height: None, overridden_height: None, @@ -59,16 +54,20 @@ impl MemoryMerkleChip { } } -fn memory_to_partition( +fn memory_to_vec_partition( memory: &MemoryImage, -) -> Equipartition { - let mut memory_partition = Equipartition::new(); + md: &MemoryDimensions, +) -> Vec<(u64, [F; N])> { + let mut memory_partition = Vec::new(); for ((address_space, pointer), value) in memory.items() { - let label = (address_space, pointer / N as u32); - let chunk = memory_partition - .entry(label) - .or_insert_with(|| [F::default(); N]); - chunk[(pointer % N as u32) as usize] = value; + let label = md.label_to_index((address_space, pointer / N as u32)); + if memory_partition + .last() + .is_none_or(|(last_label, _)| *last_label < label) + { + memory_partition.push((label, [F::ZERO; N])); + } + memory_partition.last_mut().unwrap().1[(pointer % N as u32) as usize] = value; } memory_partition } diff --git a/crates/vm/src/system/memory/merkle/tree.rs b/crates/vm/src/system/memory/merkle/tree.rs index 47c1709111..5e6f14d900 100644 --- a/crates/vm/src/system/memory/merkle/tree.rs +++ b/crates/vm/src/system/memory/merkle/tree.rs @@ -4,10 +4,13 @@ use openvm_stark_backend::{ }; use rustc_hash::FxHashMap; -use super::{memory_to_partition, FinalState, MemoryMerkleCols}; +use super::{FinalState, MemoryMerkleCols}; use crate::{ arch::hasher::HasherChip, - system::memory::{dimensions::MemoryDimensions, AddressMap, Equipartition, PAGE_SIZE}, + system::memory::{ + dimensions::MemoryDimensions, merkle::memory_to_vec_partition, AddressMap, Equipartition, + PAGE_SIZE, + }, }; #[derive(Debug)] @@ -196,14 +199,9 @@ impl MerkleTree { hasher: &impl HasherChip, ) -> Self { let mut tree = Self::new(md.overall_height(), hasher); - let layer: Vec<_> = memory_to_partition(&initial_memory) + let layer: Vec<_> = memory_to_vec_partition(&initial_memory, md) .iter() - .map(|((addr_sp, ptr), v)| { - ( - (1 << tree.height) + md.label_to_index((*addr_sp, *ptr)), - hasher.hash(v), - ) - }) + .map(|(idx, v)| ((1 << tree.height) + idx, hasher.hash(v))) .collect(); tree.process_layers(layer, md, None, |left, right| hasher.compress(left, right)); tree