1use alu_native_adapter::{AluNativeAdapterAir, AluNativeAdapterExecutor};
2use branch_native_adapter::{BranchNativeAdapterAir, BranchNativeAdapterExecutor};
3use convert_adapter::{ConvertAdapterAir, ConvertAdapterExecutor};
4use derive_more::derive::From;
5use loadstore_native_adapter::{NativeLoadStoreAdapterAir, NativeLoadStoreAdapterExecutor};
6use native_vectorized_adapter::{NativeVectorizedAdapterAir, NativeVectorizedAdapterExecutor};
7use openvm_circuit::{
8 arch::{
9 AirInventory, AirInventoryError, ChipInventory, ChipInventoryError, ExecutionBridge,
10 ExecutorInventoryBuilder, ExecutorInventoryError, RowMajorMatrixArena, VmCircuitExtension,
11 VmExecutionExtension, VmProverExtension,
12 },
13 system::{memory::SharedMemoryHelper, SystemPort},
14};
15use openvm_circuit_derive::{AnyEnum, Executor, MeteredExecutor, PreflightExecutor};
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::BranchEqualCoreAir;
24use openvm_stark_backend::{
25 config::{StarkGenericConfig, Val},
26 p3_field::{Field, PrimeField32},
27 prover::cpu::{CpuBackend, CpuDevice},
28};
29use openvm_stark_sdk::engine::StarkEngine;
30use serde::{Deserialize, Serialize};
31use strum::IntoEnumIterator;
32
33use crate::{
34 adapters::*,
35 branch_eq::{
36 NativeBranchEqAir, NativeBranchEqChip, NativeBranchEqExecutor, NativeBranchEqualFiller,
37 },
38 castf::{CastFAir, CastFChip, CastFCoreAir, CastFCoreFiller, CastFExecutor},
39 field_arithmetic::{
40 FieldArithmeticAir, FieldArithmeticChip, FieldArithmeticCoreAir, FieldArithmeticCoreFiller,
41 FieldArithmeticExecutor,
42 },
43 field_extension::{
44 FieldExtensionAir, FieldExtensionChip, FieldExtensionCoreAir, FieldExtensionCoreFiller,
45 FieldExtensionExecutor,
46 },
47 fri::{
48 FriReducedOpeningAir, FriReducedOpeningChip, FriReducedOpeningExecutor,
49 FriReducedOpeningFiller,
50 },
51 jal_rangecheck::{
52 JalRangeCheckAir, JalRangeCheckExecutor, JalRangeCheckFiller, NativeJalRangeCheckChip,
53 },
54 loadstore::{
55 NativeLoadStoreAir, NativeLoadStoreChip, NativeLoadStoreCoreAir, NativeLoadStoreCoreFiller,
56 NativeLoadStoreExecutor,
57 },
58 phantom::*,
59 poseidon2::{
60 air::{NativePoseidon2Air, VerifyBatchBus},
61 chip::{NativePoseidon2Executor, NativePoseidon2Filler},
62 NativePoseidon2Chip,
63 },
64};
65
66cfg_if::cfg_if! {
67 if #[cfg(feature = "cuda")] {
68 mod cuda;
69 pub use self::cuda::*;
70 pub use self::cuda::{
71 NativeGpuProverExt as NativeProverExt,
72 };
73 pub type NativeBuilder = crate::NativeGpuBuilder;
74 } else {
75 pub use self::{
76 NativeCpuProverExt as NativeProverExt,
77 };
78 pub type NativeBuilder = crate::NativeCpuBuilder;
79 }
80}
81
82#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
85pub struct Native;
86
87#[derive(Clone, From, AnyEnum, Executor, MeteredExecutor, PreflightExecutor)]
88pub enum NativeExecutor<F: Field> {
89 LoadStore(NativeLoadStoreExecutor<1>),
90 BlockLoadStore(NativeLoadStoreExecutor<BLOCK_LOAD_STORE_SIZE>),
91 BranchEqual(NativeBranchEqExecutor),
92 Jal(JalRangeCheckExecutor),
93 FieldArithmetic(FieldArithmeticExecutor),
94 FieldExtension(FieldExtensionExecutor),
95 FriReducedOpening(FriReducedOpeningExecutor),
96 VerifyBatch(NativePoseidon2Executor<F, 1>),
97}
98
99impl<F: PrimeField32> VmExecutionExtension<F> for Native {
100 type Executor = NativeExecutor<F>;
101
102 fn extend_execution(
103 &self,
104 inventory: &mut ExecutorInventoryBuilder<F, NativeExecutor<F>>,
105 ) -> Result<(), ExecutorInventoryError> {
106 let load_store = NativeLoadStoreExecutor::<1>::new(
107 NativeLoadStoreAdapterExecutor::new(NativeLoadStoreOpcode::CLASS_OFFSET),
108 NativeLoadStoreOpcode::CLASS_OFFSET,
109 );
110 inventory.add_executor(
111 load_store,
112 NativeLoadStoreOpcode::iter().map(|x| x.global_opcode()),
113 )?;
114
115 let block_load_store = NativeLoadStoreExecutor::<BLOCK_LOAD_STORE_SIZE>::new(
116 NativeLoadStoreAdapterExecutor::new(NativeLoadStore4Opcode::CLASS_OFFSET),
117 NativeLoadStore4Opcode::CLASS_OFFSET,
118 );
119 inventory.add_executor(
120 block_load_store,
121 NativeLoadStore4Opcode::iter().map(|x| x.global_opcode()),
122 )?;
123
124 let branch_equal = NativeBranchEqExecutor::new(
125 BranchNativeAdapterExecutor::new(),
126 NativeBranchEqualOpcode::CLASS_OFFSET,
127 DEFAULT_PC_STEP,
128 );
129 inventory.add_executor(
130 branch_equal,
131 NativeBranchEqualOpcode::iter().map(|x| x.global_opcode()),
132 )?;
133
134 let jal_rangecheck = JalRangeCheckExecutor;
135 inventory.add_executor(
136 jal_rangecheck,
137 [
138 NativeJalOpcode::JAL.global_opcode(),
139 NativeRangeCheckOpcode::RANGE_CHECK.global_opcode(),
140 ],
141 )?;
142
143 let field_arithmetic = FieldArithmeticExecutor::new(AluNativeAdapterExecutor::new());
144 inventory.add_executor(
145 field_arithmetic,
146 FieldArithmeticOpcode::iter().map(|x| x.global_opcode()),
147 )?;
148
149 let field_extension = FieldExtensionExecutor::new(NativeVectorizedAdapterExecutor::new());
150 inventory.add_executor(
151 field_extension,
152 FieldExtensionOpcode::iter().map(|x| x.global_opcode()),
153 )?;
154
155 let fri_reduced_opening = FriReducedOpeningExecutor::new();
156 inventory.add_executor(
157 fri_reduced_opening,
158 FriOpcode::iter().map(|x| x.global_opcode()),
159 )?;
160
161 let verify_batch = NativePoseidon2Executor::<F, 1>::new(Poseidon2Config::default());
162 inventory.add_executor(
163 verify_batch,
164 [
165 VerifyBatchOpcode::VERIFY_BATCH.global_opcode(),
166 Poseidon2Opcode::PERM_POS2.global_opcode(),
167 Poseidon2Opcode::COMP_POS2.global_opcode(),
168 ],
169 )?;
170
171 inventory.add_phantom_sub_executor(
172 NativeHintInputSubEx,
173 PhantomDiscriminant(NativePhantom::HintInput as u16),
174 )?;
175
176 inventory.add_phantom_sub_executor(
177 NativeHintSliceSubEx::<1>,
178 PhantomDiscriminant(NativePhantom::HintFelt as u16),
179 )?;
180
181 inventory.add_phantom_sub_executor(
182 NativeHintBitsSubEx,
183 PhantomDiscriminant(NativePhantom::HintBits as u16),
184 )?;
185
186 inventory.add_phantom_sub_executor(
187 NativePrintSubEx,
188 PhantomDiscriminant(NativePhantom::Print as u16),
189 )?;
190
191 inventory.add_phantom_sub_executor(
192 NativeHintLoadSubEx,
193 PhantomDiscriminant(NativePhantom::HintLoad as u16),
194 )?;
195
196 Ok(())
197 }
198}
199
200impl<SC: StarkGenericConfig> VmCircuitExtension<SC> for Native
201where
202 Val<SC>: PrimeField32,
203{
204 fn extend_circuit(&self, inventory: &mut AirInventory<SC>) -> Result<(), AirInventoryError> {
205 let SystemPort {
206 execution_bus,
207 program_bus,
208 memory_bridge,
209 } = inventory.system().port();
210 let exec_bridge = ExecutionBridge::new(execution_bus, program_bus);
211 let range_checker = inventory.range_checker().bus;
212
213 let load_store = NativeLoadStoreAir::<1>::new(
214 NativeLoadStoreAdapterAir::new(memory_bridge, exec_bridge),
215 NativeLoadStoreCoreAir::new(NativeLoadStoreOpcode::CLASS_OFFSET),
216 );
217 inventory.add_air(load_store);
218
219 let block_load_store = NativeLoadStoreAir::<BLOCK_LOAD_STORE_SIZE>::new(
220 NativeLoadStoreAdapterAir::new(memory_bridge, exec_bridge),
221 NativeLoadStoreCoreAir::new(NativeLoadStore4Opcode::CLASS_OFFSET),
222 );
223 inventory.add_air(block_load_store);
224
225 let branch_equal = NativeBranchEqAir::new(
226 BranchNativeAdapterAir::new(exec_bridge, memory_bridge),
227 BranchEqualCoreAir::new(NativeBranchEqualOpcode::CLASS_OFFSET, DEFAULT_PC_STEP),
228 );
229 inventory.add_air(branch_equal);
230
231 let jal_rangecheck = JalRangeCheckAir::new(
232 ExecutionBridge::new(execution_bus, program_bus),
233 memory_bridge,
234 range_checker,
235 );
236 inventory.add_air(jal_rangecheck);
237
238 let field_arithmetic = FieldArithmeticAir::new(
239 AluNativeAdapterAir::new(exec_bridge, memory_bridge),
240 FieldArithmeticCoreAir::new(),
241 );
242 inventory.add_air(field_arithmetic);
243
244 let field_extension = FieldExtensionAir::new(
245 NativeVectorizedAdapterAir::new(exec_bridge, memory_bridge),
246 FieldExtensionCoreAir::new(),
247 );
248 inventory.add_air(field_extension);
249
250 let fri_reduced_opening = FriReducedOpeningAir::new(
251 ExecutionBridge::new(execution_bus, program_bus),
252 memory_bridge,
253 );
254 inventory.add_air(fri_reduced_opening);
255
256 let verify_batch = NativePoseidon2Air::<_, 1>::new(
257 exec_bridge,
258 memory_bridge,
259 VerifyBatchBus::new(inventory.new_bus_idx()),
260 Poseidon2Config::default(),
261 );
262 inventory.add_air(verify_batch);
263
264 Ok(())
265 }
266}
267
268pub struct NativeCpuProverExt;
269impl<E, SC, RA> VmProverExtension<E, RA, Native> for NativeCpuProverExt
272where
273 SC: StarkGenericConfig,
274 E: StarkEngine<SC = SC, PB = CpuBackend<SC>, PD = CpuDevice<SC>>,
275 RA: RowMajorMatrixArena<Val<SC>>,
276 Val<SC>: PrimeField32,
277{
278 fn extend_prover(
279 &self,
280 _: &Native,
281 inventory: &mut ChipInventory<SC, RA, CpuBackend<SC>>,
282 ) -> Result<(), ChipInventoryError> {
283 let range_checker = inventory.range_checker()?.clone();
284 let timestamp_max_bits = inventory.timestamp_max_bits();
285 let mem_helper = SharedMemoryHelper::new(range_checker.clone(), timestamp_max_bits);
286
287 inventory.next_air::<NativeLoadStoreAir<1>>()?;
290 let load_store = NativeLoadStoreChip::<_, 1>::new(
291 NativeLoadStoreCoreFiller::new(NativeLoadStoreAdapterFiller),
292 mem_helper.clone(),
293 );
294 inventory.add_executor_chip(load_store);
295
296 inventory.next_air::<NativeLoadStoreAir<BLOCK_LOAD_STORE_SIZE>>()?;
297 let block_load_store = NativeLoadStoreChip::<_, BLOCK_LOAD_STORE_SIZE>::new(
298 NativeLoadStoreCoreFiller::new(NativeLoadStoreAdapterFiller),
299 mem_helper.clone(),
300 );
301 inventory.add_executor_chip(block_load_store);
302
303 inventory.next_air::<NativeBranchEqAir>()?;
304 let branch_eq = NativeBranchEqChip::new(
305 NativeBranchEqualFiller::new(BranchNativeAdapterFiller),
306 mem_helper.clone(),
307 );
308
309 inventory.add_executor_chip(branch_eq);
310
311 inventory.next_air::<JalRangeCheckAir>()?;
312 let jal_rangecheck = NativeJalRangeCheckChip::new(
313 JalRangeCheckFiller::new(range_checker.clone()),
314 mem_helper.clone(),
315 );
316 inventory.add_executor_chip(jal_rangecheck);
317
318 inventory.next_air::<FieldArithmeticAir>()?;
319 let field_arithmetic = FieldArithmeticChip::new(
320 FieldArithmeticCoreFiller::new(AluNativeAdapterFiller),
321 mem_helper.clone(),
322 );
323 inventory.add_executor_chip(field_arithmetic);
324
325 inventory.next_air::<FieldExtensionAir>()?;
326 let field_extension = FieldExtensionChip::new(
327 FieldExtensionCoreFiller::new(NativeVectorizedAdapterFiller),
328 mem_helper.clone(),
329 );
330 inventory.add_executor_chip(field_extension);
331
332 inventory.next_air::<FriReducedOpeningAir>()?;
333 let fri_reduced_opening =
334 FriReducedOpeningChip::new(FriReducedOpeningFiller::new(), mem_helper.clone());
335 inventory.add_executor_chip(fri_reduced_opening);
336
337 inventory.next_air::<NativePoseidon2Air<Val<SC>, 1>>()?;
338 let poseidon2 = NativePoseidon2Chip::<_, 1>::new(
339 NativePoseidon2Filler::new(Poseidon2Config::default()),
340 mem_helper.clone(),
341 );
342 inventory.add_executor_chip(poseidon2);
343
344 Ok(())
345 }
346}
347
348pub(crate) mod phantom {
349 use eyre::bail;
350 use openvm_circuit::{
351 arch::{PhantomSubExecutor, Streams},
352 system::memory::online::GuestMemory,
353 };
354 use openvm_instructions::PhantomDiscriminant;
355 use openvm_stark_backend::p3_field::{Field, PrimeField32};
356 use rand::rngs::StdRng;
357
358 pub struct NativeHintInputSubEx;
359 pub struct NativeHintSliceSubEx<const N: usize>;
360 pub struct NativePrintSubEx;
361 pub struct NativeHintBitsSubEx;
362 pub struct NativeHintLoadSubEx;
363
364 impl<F: Field> PhantomSubExecutor<F> for NativeHintInputSubEx {
365 fn phantom_execute(
366 &self,
367 _: &GuestMemory,
368 streams: &mut Streams<F>,
369 _: &mut StdRng,
370 _: PhantomDiscriminant,
371 _: u32,
372 _: u32,
373 _: u16,
374 ) -> eyre::Result<()> {
375 let hint = match streams.input_stream.pop_front() {
376 Some(hint) => hint,
377 None => {
378 bail!("EndOfInputStream");
379 }
380 };
381 assert!(streams.hint_stream.is_empty());
382 streams
383 .hint_stream
384 .push_back(F::from_canonical_usize(hint.len()));
385 streams.hint_stream.extend(hint);
386 Ok(())
387 }
388 }
389
390 impl<F: Field, const N: usize> PhantomSubExecutor<F> for NativeHintSliceSubEx<N> {
391 fn phantom_execute(
392 &self,
393 _: &GuestMemory,
394 streams: &mut Streams<F>,
395 _: &mut StdRng,
396 _: PhantomDiscriminant,
397 _: u32,
398 _: u32,
399 _: u16,
400 ) -> eyre::Result<()> {
401 let hint = match streams.input_stream.pop_front() {
402 Some(hint) => hint,
403 None => {
404 bail!("EndOfInputStream");
405 }
406 };
407 assert!(streams.hint_stream.is_empty());
408 assert_eq!(hint.len(), N);
409 streams.hint_stream = hint.into();
410 Ok(())
411 }
412 }
413
414 impl<F: PrimeField32> PhantomSubExecutor<F> for NativePrintSubEx {
415 fn phantom_execute(
416 &self,
417 memory: &GuestMemory,
418 _: &mut Streams<F>,
419 _: &mut StdRng,
420 _: PhantomDiscriminant,
421 a: u32,
422 _: u32,
423 c_upper: u16,
424 ) -> eyre::Result<()> {
425 assert!(
427 (c_upper as usize) < memory.memory.config.len(),
428 "c_upper out of bounds"
429 );
430 let [value] = unsafe { memory.read::<F, 1>(c_upper as u32, a) };
434 println!("{value}");
435 Ok(())
436 }
437 }
438
439 impl<F: PrimeField32> PhantomSubExecutor<F> for NativeHintBitsSubEx {
440 fn phantom_execute(
441 &self,
442 memory: &GuestMemory,
443 streams: &mut Streams<F>,
444 _: &mut StdRng,
445 _: PhantomDiscriminant,
446 a: u32,
447 len: u32,
448 c_upper: u16,
449 ) -> eyre::Result<()> {
450 assert!(
452 (c_upper as usize) < memory.memory.config.len(),
453 "c_upper out of bounds"
454 );
455 let [val] = unsafe { memory.read::<F, 1>(c_upper as u32, a) };
459 let mut val = val.as_canonical_u32();
460
461 assert!(streams.hint_stream.is_empty());
462 for _ in 0..len {
463 streams
464 .hint_stream
465 .push_back(F::from_canonical_u32(val & 1));
466 val >>= 1;
467 }
468 Ok(())
469 }
470 }
471
472 impl<F: PrimeField32> PhantomSubExecutor<F> for NativeHintLoadSubEx {
473 fn phantom_execute(
474 &self,
475 _: &GuestMemory,
476 streams: &mut Streams<F>,
477 _: &mut StdRng,
478 _: PhantomDiscriminant,
479 _: u32,
480 _: u32,
481 _: u16,
482 ) -> eyre::Result<()> {
483 let payload = match streams.input_stream.pop_front() {
484 Some(hint) => hint,
485 None => {
486 bail!("EndOfInputStream");
487 }
488 };
489 let id = streams.hint_space.len();
490 streams.hint_space.push(payload);
491 assert!(streams.hint_stream.is_empty());
493 streams.hint_stream.push_back(F::from_canonical_usize(id));
494 Ok(())
495 }
496 }
497}
498
499#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
500pub struct CastFExtension;
501
502#[derive(Clone, From, AnyEnum, Executor, MeteredExecutor, PreflightExecutor)]
503pub enum CastFExtensionExecutor {
504 CastF(CastFExecutor),
505}
506
507impl<F: PrimeField32> VmExecutionExtension<F> for CastFExtension {
508 type Executor = CastFExtensionExecutor;
509
510 fn extend_execution(
511 &self,
512 inventory: &mut ExecutorInventoryBuilder<F, CastFExtensionExecutor>,
513 ) -> Result<(), ExecutorInventoryError> {
514 let castf = CastFExecutor::new(ConvertAdapterExecutor::new());
515 inventory.add_executor(castf, [CastfOpcode::CASTF.global_opcode()])?;
516 Ok(())
517 }
518}
519
520impl<SC: StarkGenericConfig> VmCircuitExtension<SC> for CastFExtension {
521 fn extend_circuit(&self, inventory: &mut AirInventory<SC>) -> Result<(), AirInventoryError> {
522 let SystemPort {
523 execution_bus,
524 program_bus,
525 memory_bridge,
526 } = inventory.system().port();
527 let exec_bridge = ExecutionBridge::new(execution_bus, program_bus);
528 let range_checker = inventory.range_checker().bus;
529
530 let castf = CastFAir::new(
531 ConvertAdapterAir::new(exec_bridge, memory_bridge),
532 CastFCoreAir::new(range_checker),
533 );
534 inventory.add_air(castf);
535 Ok(())
536 }
537}
538
539impl<E, SC, RA> VmProverExtension<E, RA, CastFExtension> for NativeCpuProverExt
540where
541 SC: StarkGenericConfig,
542 E: StarkEngine<SC = SC, PB = CpuBackend<SC>, PD = CpuDevice<SC>>,
543 RA: RowMajorMatrixArena<Val<SC>>,
544 Val<SC>: PrimeField32,
545{
546 fn extend_prover(
547 &self,
548 _: &CastFExtension,
549 inventory: &mut ChipInventory<SC, RA, CpuBackend<SC>>,
550 ) -> Result<(), ChipInventoryError> {
551 let range_checker = inventory.range_checker()?.clone();
552 let timestamp_max_bits = inventory.timestamp_max_bits();
553 let mem_helper = SharedMemoryHelper::new(range_checker.clone(), timestamp_max_bits);
554
555 inventory.next_air::<CastFAir>()?;
556 let castf = CastFChip::new(
557 CastFCoreFiller::new(ConvertAdapterFiller::new(), range_checker),
558 mem_helper.clone(),
559 );
560 inventory.add_executor_chip(castf);
561
562 Ok(())
563 }
564}
565
566pub const NATIVE_MAX_TRACE_HEIGHTS: &[u32] = &[
570 4194304, 4, 128, 2097152, 8388608, 4194304, 262144, 2097152, 16777216, 2097152, 8388608,
571 262144, 2097152, 1048576, 4194304, 65536, 262144,
572];