1use air::VerifyBatchBus;
2use alu_native_adapter::AluNativeAdapterChip;
3use branch_native_adapter::BranchNativeAdapterChip;
4use derive_more::derive::From;
5use loadstore_native_adapter::NativeLoadStoreAdapterChip;
6use native_vectorized_adapter::NativeVectorizedAdapterChip;
7use openvm_circuit::{
8 arch::{
9 ExecutionBridge, MemoryConfig, SystemConfig, SystemPort, VmExtension, VmInventory,
10 VmInventoryBuilder, VmInventoryError,
11 },
12 system::phantom::PhantomChip,
13};
14use openvm_circuit_derive::{AnyEnum, InstructionExecutor, VmConfig};
15use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter};
16use openvm_instructions::{program::DEFAULT_PC_STEP, LocalOpcode, PhantomDiscriminant};
17use openvm_native_compiler::{
18 CastfOpcode, FieldArithmeticOpcode, FieldExtensionOpcode, FriOpcode, NativeBranchEqualOpcode,
19 NativeJalOpcode, NativeLoadStore4Opcode, NativeLoadStoreOpcode, NativePhantom,
20 NativeRangeCheckOpcode, Poseidon2Opcode, VerifyBatchOpcode, BLOCK_LOAD_STORE_SIZE,
21};
22use openvm_poseidon2_air::Poseidon2Config;
23use openvm_rv32im_circuit::{
24 BranchEqualCoreChip, Rv32I, Rv32IExecutor, Rv32IPeriphery, Rv32Io, Rv32IoExecutor,
25 Rv32IoPeriphery, Rv32M, Rv32MExecutor, Rv32MPeriphery,
26};
27use openvm_stark_backend::p3_field::PrimeField32;
28use serde::{Deserialize, Serialize};
29use strum::IntoEnumIterator;
30
31use crate::{
32 adapters::{convert_adapter::ConvertAdapterChip, *},
33 chip::NativePoseidon2Chip,
34 phantom::*,
35 *,
36};
37
38#[derive(Clone, Debug, Serialize, Deserialize, VmConfig, derive_new::new)]
39pub struct NativeConfig {
40 #[system]
41 pub system: SystemConfig,
42 #[extension]
43 pub native: Native,
44}
45
46impl NativeConfig {
47 pub fn aggregation(num_public_values: usize, max_constraint_degree: usize) -> Self {
48 Self {
49 system: SystemConfig::new(
50 max_constraint_degree,
51 MemoryConfig {
52 max_access_adapter_n: 8,
53 ..Default::default()
54 },
55 num_public_values,
56 )
57 .with_max_segment_len((1 << 24) - 100),
58 native: Default::default(),
59 }
60 }
61}
62
63#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
64pub struct Native;
65
66#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)]
67pub enum NativeExecutor<F: PrimeField32> {
68 LoadStore(NativeLoadStoreChip<F, 1>),
69 BlockLoadStore(NativeLoadStoreChip<F, 4>),
70 BranchEqual(NativeBranchEqChip<F>),
71 Jal(JalRangeCheckChip<F>),
72 FieldArithmetic(FieldArithmeticChip<F>),
73 FieldExtension(FieldExtensionChip<F>),
74 FriReducedOpening(FriReducedOpeningChip<F>),
75 VerifyBatch(NativePoseidon2Chip<F, 1>),
76}
77
78#[derive(From, ChipUsageGetter, Chip, AnyEnum)]
79pub enum NativePeriphery<F: PrimeField32> {
80 Phantom(PhantomChip<F>),
81}
82
83impl<F: PrimeField32> VmExtension<F> for Native {
84 type Executor = NativeExecutor<F>;
85 type Periphery = NativePeriphery<F>;
86
87 fn build(
88 &self,
89 builder: &mut VmInventoryBuilder<F>,
90 ) -> Result<VmInventory<NativeExecutor<F>, NativePeriphery<F>>, VmInventoryError> {
91 let mut inventory = VmInventory::new();
92 let SystemPort {
93 execution_bus,
94 program_bus,
95 memory_bridge,
96 } = builder.system_port();
97 let offline_memory = builder.system_base().offline_memory();
98
99 let mut load_store_chip = NativeLoadStoreChip::<F, 1>::new(
100 NativeLoadStoreAdapterChip::new(
101 execution_bus,
102 program_bus,
103 memory_bridge,
104 NativeLoadStoreOpcode::CLASS_OFFSET,
105 ),
106 NativeLoadStoreCoreChip::new(NativeLoadStoreOpcode::CLASS_OFFSET),
107 offline_memory.clone(),
108 );
109 load_store_chip.core.set_streams(builder.streams().clone());
110
111 inventory.add_executor(
112 load_store_chip,
113 NativeLoadStoreOpcode::iter().map(|x| x.global_opcode()),
114 )?;
115
116 let mut block_load_store_chip = NativeLoadStoreChip::<F, BLOCK_LOAD_STORE_SIZE>::new(
117 NativeLoadStoreAdapterChip::new(
118 execution_bus,
119 program_bus,
120 memory_bridge,
121 NativeLoadStore4Opcode::CLASS_OFFSET,
122 ),
123 NativeLoadStoreCoreChip::new(NativeLoadStore4Opcode::CLASS_OFFSET),
124 offline_memory.clone(),
125 );
126 block_load_store_chip
127 .core
128 .set_streams(builder.streams().clone());
129
130 inventory.add_executor(
131 block_load_store_chip,
132 NativeLoadStore4Opcode::iter().map(|x| x.global_opcode()),
133 )?;
134
135 let branch_equal_chip = NativeBranchEqChip::new(
136 BranchNativeAdapterChip::<_>::new(execution_bus, program_bus, memory_bridge),
137 BranchEqualCoreChip::new(NativeBranchEqualOpcode::CLASS_OFFSET, DEFAULT_PC_STEP),
138 offline_memory.clone(),
139 );
140 inventory.add_executor(
141 branch_equal_chip,
142 NativeBranchEqualOpcode::iter().map(|x| x.global_opcode()),
143 )?;
144
145 let jal_chip = JalRangeCheckChip::new(
146 ExecutionBridge::new(execution_bus, program_bus),
147 offline_memory.clone(),
148 builder.system_base().range_checker_chip.clone(),
149 );
150 inventory.add_executor(
151 jal_chip,
152 [
153 NativeJalOpcode::JAL.global_opcode(),
154 NativeRangeCheckOpcode::RANGE_CHECK.global_opcode(),
155 ],
156 )?;
157
158 let field_arithmetic_chip = FieldArithmeticChip::new(
159 AluNativeAdapterChip::<F>::new(execution_bus, program_bus, memory_bridge),
160 FieldArithmeticCoreChip::new(),
161 offline_memory.clone(),
162 );
163 inventory.add_executor(
164 field_arithmetic_chip,
165 FieldArithmeticOpcode::iter().map(|x| x.global_opcode()),
166 )?;
167
168 let field_extension_chip = FieldExtensionChip::new(
169 NativeVectorizedAdapterChip::new(execution_bus, program_bus, memory_bridge),
170 FieldExtensionCoreChip::new(),
171 offline_memory.clone(),
172 );
173 inventory.add_executor(
174 field_extension_chip,
175 FieldExtensionOpcode::iter().map(|x| x.global_opcode()),
176 )?;
177
178 let fri_reduced_opening_chip = FriReducedOpeningChip::new(
179 execution_bus,
180 program_bus,
181 memory_bridge,
182 offline_memory.clone(),
183 builder.streams().clone(),
184 );
185 inventory.add_executor(
186 fri_reduced_opening_chip,
187 FriOpcode::iter().map(|x| x.global_opcode()),
188 )?;
189
190 let poseidon2_chip = NativePoseidon2Chip::new(
191 builder.system_port(),
192 offline_memory.clone(),
193 Poseidon2Config::default(),
194 VerifyBatchBus::new(builder.new_bus_idx()),
195 builder.streams().clone(),
196 );
197 inventory.add_executor(
198 poseidon2_chip,
199 [
200 VerifyBatchOpcode::VERIFY_BATCH.global_opcode(),
201 Poseidon2Opcode::PERM_POS2.global_opcode(),
202 Poseidon2Opcode::COMP_POS2.global_opcode(),
203 ],
204 )?;
205
206 builder.add_phantom_sub_executor(
207 NativeHintInputSubEx,
208 PhantomDiscriminant(NativePhantom::HintInput as u16),
209 )?;
210
211 builder.add_phantom_sub_executor(
212 NativeHintSliceSubEx::<1>,
213 PhantomDiscriminant(NativePhantom::HintFelt as u16),
214 )?;
215
216 builder.add_phantom_sub_executor(
217 NativeHintBitsSubEx,
218 PhantomDiscriminant(NativePhantom::HintBits as u16),
219 )?;
220
221 builder.add_phantom_sub_executor(
222 NativePrintSubEx,
223 PhantomDiscriminant(NativePhantom::Print as u16),
224 )?;
225
226 builder.add_phantom_sub_executor(
227 NativeHintLoadSubEx,
228 PhantomDiscriminant(NativePhantom::HintLoad as u16),
229 )?;
230
231 Ok(inventory)
232 }
233}
234
235pub(crate) mod phantom {
236 use eyre::bail;
237 use openvm_circuit::{
238 arch::{PhantomSubExecutor, Streams},
239 system::memory::MemoryController,
240 };
241 use openvm_instructions::PhantomDiscriminant;
242 use openvm_stark_backend::p3_field::{Field, PrimeField32};
243
244 pub struct NativeHintInputSubEx;
245 pub struct NativeHintSliceSubEx<const N: usize>;
246 pub struct NativePrintSubEx;
247 pub struct NativeHintBitsSubEx;
248 pub struct NativeHintLoadSubEx;
249
250 impl<F: Field> PhantomSubExecutor<F> for NativeHintInputSubEx {
251 fn phantom_execute(
252 &mut self,
253 _: &MemoryController<F>,
254 streams: &mut Streams<F>,
255 _: PhantomDiscriminant,
256 _: F,
257 _: F,
258 _: u16,
259 ) -> eyre::Result<()> {
260 let hint = match streams.input_stream.pop_front() {
261 Some(hint) => hint,
262 None => {
263 bail!("EndOfInputStream");
264 }
265 };
266 assert!(streams.hint_stream.is_empty());
267 streams
268 .hint_stream
269 .push_back(F::from_canonical_usize(hint.len()));
270 streams.hint_stream.extend(hint);
271 Ok(())
272 }
273 }
274
275 impl<F: Field, const N: usize> PhantomSubExecutor<F> for NativeHintSliceSubEx<N> {
276 fn phantom_execute(
277 &mut self,
278 _: &MemoryController<F>,
279 streams: &mut Streams<F>,
280 _: PhantomDiscriminant,
281 _: F,
282 _: F,
283 _: u16,
284 ) -> eyre::Result<()> {
285 let hint = match streams.input_stream.pop_front() {
286 Some(hint) => hint,
287 None => {
288 bail!("EndOfInputStream");
289 }
290 };
291 assert!(streams.hint_stream.is_empty());
292 assert_eq!(hint.len(), N);
293 streams.hint_stream = hint.into();
294 Ok(())
295 }
296 }
297
298 impl<F: PrimeField32> PhantomSubExecutor<F> for NativePrintSubEx {
299 fn phantom_execute(
300 &mut self,
301 memory: &MemoryController<F>,
302 _: &mut Streams<F>,
303 _: PhantomDiscriminant,
304 a: F,
305 _: F,
306 c_upper: u16,
307 ) -> eyre::Result<()> {
308 let addr_space = F::from_canonical_u16(c_upper);
309 let value = memory.unsafe_read_cell(addr_space, a);
310 println!("{}", value);
311 Ok(())
312 }
313 }
314
315 impl<F: PrimeField32> PhantomSubExecutor<F> for NativeHintBitsSubEx {
316 fn phantom_execute(
317 &mut self,
318 memory: &MemoryController<F>,
319 streams: &mut Streams<F>,
320 _: PhantomDiscriminant,
321 a: F,
322 b: F,
323 c_upper: u16,
324 ) -> eyre::Result<()> {
325 let addr_space = F::from_canonical_u16(c_upper);
326 let val = memory.unsafe_read_cell(addr_space, a);
327 let mut val = val.as_canonical_u32();
328
329 let len = b.as_canonical_u32();
330 assert!(streams.hint_stream.is_empty());
331 for _ in 0..len {
332 streams
333 .hint_stream
334 .push_back(F::from_canonical_u32(val & 1));
335 val >>= 1;
336 }
337 Ok(())
338 }
339 }
340
341 impl<F: PrimeField32> PhantomSubExecutor<F> for NativeHintLoadSubEx {
342 fn phantom_execute(
343 &mut self,
344 _: &MemoryController<F>,
345 streams: &mut Streams<F>,
346 _: PhantomDiscriminant,
347 _: F,
348 _: F,
349 _: u16,
350 ) -> eyre::Result<()> {
351 let payload = match streams.input_stream.pop_front() {
352 Some(hint) => hint,
353 None => {
354 bail!("EndOfInputStream");
355 }
356 };
357 let id = streams.hint_space.len();
358 streams.hint_space.push(payload);
359 assert!(streams.hint_stream.is_empty());
361 streams.hint_stream.push_back(F::from_canonical_usize(id));
362 Ok(())
363 }
364 }
365}
366
367#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
368pub struct CastFExtension;
369
370#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)]
371pub enum CastFExtensionExecutor<F: PrimeField32> {
372 CastF(CastFChip<F>),
373}
374
375#[derive(From, ChipUsageGetter, Chip, AnyEnum)]
376pub enum CastFExtensionPeriphery<F: PrimeField32> {
377 Placeholder(CastFChip<F>),
378}
379
380impl<F: PrimeField32> VmExtension<F> for CastFExtension {
381 type Executor = CastFExtensionExecutor<F>;
382 type Periphery = CastFExtensionPeriphery<F>;
383
384 fn build(
385 &self,
386 builder: &mut VmInventoryBuilder<F>,
387 ) -> Result<VmInventory<Self::Executor, Self::Periphery>, VmInventoryError> {
388 let mut inventory = VmInventory::new();
389 let SystemPort {
390 execution_bus,
391 program_bus,
392 memory_bridge,
393 } = builder.system_port();
394 let offline_memory = builder.system_base().offline_memory();
395 let range_checker = builder.system_base().range_checker_chip.clone();
396
397 let castf_chip = CastFChip::new(
398 ConvertAdapterChip::new(execution_bus, program_bus, memory_bridge),
399 CastFCoreChip::new(range_checker.clone()),
400 offline_memory.clone(),
401 );
402 inventory.add_executor(castf_chip, [CastfOpcode::CASTF.global_opcode()])?;
403
404 Ok(inventory)
405 }
406}
407
408#[derive(Clone, Debug, VmConfig, derive_new::new, Serialize, Deserialize)]
409pub struct Rv32WithKernelsConfig {
410 #[system]
411 pub system: SystemConfig,
412 #[extension]
413 pub rv32i: Rv32I,
414 #[extension]
415 pub rv32m: Rv32M,
416 #[extension]
417 pub io: Rv32Io,
418 #[extension]
419 pub native: Native,
420 #[extension]
421 pub castf: CastFExtension,
422}
423
424impl Default for Rv32WithKernelsConfig {
425 fn default() -> Self {
426 Self {
427 system: SystemConfig::default().with_continuations(),
428 rv32i: Rv32I,
429 rv32m: Rv32M::default(),
430 io: Rv32Io,
431 native: Native,
432 castf: CastFExtension,
433 }
434 }
435}