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