1use std::sync::Arc;
2
3use derive_more::derive::From;
4use openvm_circuit_derive::{AnyEnum, Executor, MeteredExecutor, PreflightExecutor};
5#[cfg(feature = "aot")]
6use openvm_circuit_derive::{AotExecutor, AotMeteredExecutor};
7use openvm_circuit_primitives::var_range::{
8 SharedVariableRangeCheckerChip, VariableRangeCheckerAir, VariableRangeCheckerBus,
9 VariableRangeCheckerChip,
10};
11use openvm_instructions::{
12 LocalOpcode, PhantomDiscriminant, PublishOpcode, SysPhantom, SystemOpcode,
13};
14use openvm_stark_backend::{
15 config::{StarkGenericConfig, Val},
16 engine::StarkEngine,
17 interaction::{LookupBus, PermutationCheckBus},
18 p3_field::{Field, PrimeField32},
19 prover::{
20 cpu::{CpuBackend, CpuDevice},
21 hal::{MatrixDimensions, ProverBackend},
22 types::{AirProvingContext, CommittedTraceData},
23 },
24 AirRef, Chip,
25};
26use rustc_hash::FxHashMap;
27
28use self::{connector::VmConnectorAir, program::ProgramAir, public_values::PublicValuesAir};
29use crate::{
30 arch::{
31 vm_poseidon2_config, AirInventory, AirInventoryError, BusIndexManager, ChipInventory,
32 ChipInventoryError, DenseRecordArena, ExecutionBridge, ExecutionBus, ExecutionState,
33 ExecutorInventory, ExecutorInventoryError, MatrixRecordArena, PhantomSubExecutor,
34 RowMajorMatrixArena, SystemConfig, VmAirWrapper, VmBuilder, VmChipComplex, VmChipWrapper,
35 VmCircuitConfig, VmExecutionConfig, VmField, CONNECTOR_AIR_ID, PROGRAM_AIR_ID,
36 PUBLIC_VALUES_AIR_ID,
37 },
38 system::{
39 connector::VmConnectorChip,
40 memory::{
41 interface::{MemoryInterface, MemoryInterfaceAirs},
42 offline_checker::{MemoryBridge, MemoryBus},
43 online::GuestMemory,
44 MemoryAirInventory, MemoryController, TimestampedEquipartition, CHUNK,
45 },
46 native_adapter::{NativeAdapterAir, NativeAdapterExecutor},
47 phantom::{
48 CycleEndPhantomExecutor, CycleStartPhantomExecutor, NopPhantomExecutor, PhantomAir,
49 PhantomChip, PhantomExecutor, PhantomFiller,
50 },
51 poseidon2::{
52 air::Poseidon2PeripheryAir, new_poseidon2_periphery_air, Poseidon2PeripheryChip,
53 },
54 program::{ProgramBus, ProgramChip},
55 public_values::{
56 PublicValuesChip, PublicValuesCoreAir, PublicValuesExecutor, PublicValuesFiller,
57 },
58 },
59};
60
61pub mod connector;
62#[cfg(feature = "cuda")]
63pub mod cuda;
64pub mod memory;
65pub mod native_adapter;
67pub mod phantom;
68pub mod poseidon2;
69pub mod program;
70pub mod public_values;
71
72const POSEIDON2_INSERTION_IDX: usize = 1;
74pub(crate) const PV_EXECUTOR_IDX: usize = 0;
76
77pub trait SystemChipComplex<RA, PB: ProverBackend> {
86 fn load_program(&mut self, cached_program_trace: CommittedTraceData<PB>);
88
89 fn transport_init_memory_to_device(&mut self, memory: &GuestMemory);
92
93 fn generate_proving_ctx(
96 &mut self,
97 system_records: SystemRecords<PB::Val>,
98 record_arenas: Vec<RA>,
99 ) -> Vec<AirProvingContext<PB>>;
100
101 fn memory_top_tree(&self) -> Option<&[[PB::Val; CHUNK]]>;
110
111 #[cfg(feature = "metrics")]
121 fn finalize_trace_heights(&self, _heights: &mut [usize]) {}
122}
123
124pub trait SystemWithFixedTraceHeights {
126 fn override_trace_heights(&mut self, heights: &[u32]);
129}
130
131pub struct SystemRecords<F> {
132 pub from_state: ExecutionState<u32>,
133 pub to_state: ExecutionState<u32>,
134 pub exit_code: Option<u32>,
135 pub filtered_exec_frequencies: Vec<u32>,
138 pub access_adapter_records: DenseRecordArena,
141 pub touched_memory: TouchedMemory<F>,
143 pub public_values: Vec<F>,
146}
147
148pub enum TouchedMemory<F> {
149 Persistent(TimestampedEquipartition<F, CHUNK>),
150 Volatile(TimestampedEquipartition<F, 1>),
151}
152
153#[derive(Clone, AnyEnum, Executor, MeteredExecutor, PreflightExecutor, From)]
154#[cfg_attr(feature = "aot", derive(AotExecutor, AotMeteredExecutor))]
155pub enum SystemExecutor<F: Field> {
156 PublicValues(PublicValuesExecutor<F>),
157 Phantom(PhantomExecutor<F>),
158}
159
160#[derive(Clone, Copy)]
162pub struct SystemPort {
163 pub execution_bus: ExecutionBus,
164 pub program_bus: ProgramBus,
165 pub memory_bridge: MemoryBridge,
166}
167
168#[derive(Clone)]
169pub struct SystemAirInventory<SC: StarkGenericConfig> {
170 pub program: ProgramAir,
171 pub connector: VmConnectorAir,
172 pub memory: MemoryAirInventory<SC>,
173 pub public_values: Option<PublicValuesAir>,
176}
177
178impl<SC: StarkGenericConfig> SystemAirInventory<SC> {
179 pub fn new(
180 config: &SystemConfig,
181 port: SystemPort,
182 merkle_compression_buses: Option<(PermutationCheckBus, PermutationCheckBus)>,
183 ) -> Self {
184 let SystemPort {
185 execution_bus,
186 program_bus,
187 memory_bridge,
188 } = port;
189 let range_bus = memory_bridge.range_bus();
190 let program = ProgramAir::new(program_bus);
191 let connector = VmConnectorAir::new(
192 execution_bus,
193 program_bus,
194 range_bus,
195 config.memory_config.timestamp_max_bits,
196 );
197 assert_eq!(
198 config.continuation_enabled,
199 merkle_compression_buses.is_some()
200 );
201
202 let memory = MemoryAirInventory::new(
203 memory_bridge,
204 &config.memory_config,
205 range_bus,
206 merkle_compression_buses,
207 );
208
209 let public_values = if config.has_public_values_chip() {
210 let air = VmAirWrapper::new(
211 NativeAdapterAir::new(
212 ExecutionBridge::new(execution_bus, program_bus),
213 memory_bridge,
214 ),
215 PublicValuesCoreAir::new(
216 config.num_public_values,
217 config.max_constraint_degree as u32 - 1,
218 ),
219 );
220 Some(air)
221 } else {
222 None
223 };
224
225 Self {
226 program,
227 connector,
228 memory,
229 public_values,
230 }
231 }
232
233 pub fn port(&self) -> SystemPort {
234 SystemPort {
235 memory_bridge: self.memory.bridge,
236 program_bus: self.program.bus,
237 execution_bus: self.connector.execution_bus,
238 }
239 }
240
241 pub fn into_airs(self) -> Vec<AirRef<SC>> {
242 let mut airs: Vec<AirRef<SC>> = Vec::new();
243 airs.push(Arc::new(self.program));
244 airs.push(Arc::new(self.connector));
245 if let Some(public_values) = self.public_values {
246 airs.push(Arc::new(public_values));
247 }
248 airs.extend(self.memory.into_airs());
249 airs
250 }
251}
252
253impl<F: PrimeField32> VmExecutionConfig<F> for SystemConfig {
254 type Executor = SystemExecutor<F>;
255
256 fn create_executors(
260 &self,
261 ) -> Result<ExecutorInventory<Self::Executor>, ExecutorInventoryError> {
262 let mut inventory = ExecutorInventory::new(self.clone());
263 if self.has_public_values_chip() {
265 assert_eq!(inventory.executors().len(), PV_EXECUTOR_IDX);
266
267 let public_values = PublicValuesExecutor::new(NativeAdapterExecutor::default());
268 inventory.add_executor(public_values, [PublishOpcode::PUBLISH.global_opcode()])?;
269 }
270 let phantom_opcode = SystemOpcode::PHANTOM.global_opcode();
271 let mut phantom_executors: FxHashMap<PhantomDiscriminant, Arc<dyn PhantomSubExecutor<F>>> =
272 FxHashMap::default();
273 phantom_executors.insert(
275 PhantomDiscriminant(SysPhantom::DebugPanic as u16),
276 Arc::new(NopPhantomExecutor),
277 );
278 phantom_executors.insert(
279 PhantomDiscriminant(SysPhantom::Nop as u16),
280 Arc::new(NopPhantomExecutor),
281 );
282 phantom_executors.insert(
283 PhantomDiscriminant(SysPhantom::CtStart as u16),
284 Arc::new(CycleStartPhantomExecutor),
285 );
286 phantom_executors.insert(
287 PhantomDiscriminant(SysPhantom::CtEnd as u16),
288 Arc::new(CycleEndPhantomExecutor),
289 );
290 let phantom = PhantomExecutor::new(phantom_executors, phantom_opcode);
291 inventory.add_executor(phantom, [phantom_opcode])?;
292
293 Ok(inventory)
294 }
295}
296
297impl<SC> VmCircuitConfig<SC> for SystemConfig
298where
299 SC: StarkGenericConfig,
300 Val<SC>: VmField,
301{
302 fn create_airs(&self) -> Result<AirInventory<SC>, AirInventoryError> {
305 let mut bus_idx_mgr = BusIndexManager::new();
306 let execution_bus = ExecutionBus::new(bus_idx_mgr.new_bus_idx());
307 let memory_bus = MemoryBus::new(bus_idx_mgr.new_bus_idx());
308 let program_bus = ProgramBus::new(bus_idx_mgr.new_bus_idx());
309 let range_bus =
310 VariableRangeCheckerBus::new(bus_idx_mgr.new_bus_idx(), self.memory_config.decomp);
311
312 let merkle_compression_buses = if self.continuation_enabled {
313 let merkle_bus = PermutationCheckBus::new(bus_idx_mgr.new_bus_idx());
314 let compression_bus = PermutationCheckBus::new(bus_idx_mgr.new_bus_idx());
315 Some((merkle_bus, compression_bus))
316 } else {
317 None
318 };
319 let memory_bridge =
320 MemoryBridge::new(memory_bus, self.memory_config.timestamp_max_bits, range_bus);
321 let system_port = SystemPort {
322 execution_bus,
323 program_bus,
324 memory_bridge,
325 };
326 let system = SystemAirInventory::new(self, system_port, merkle_compression_buses);
327
328 let mut inventory = AirInventory::new(self.clone(), system, bus_idx_mgr);
329
330 let range_checker = VariableRangeCheckerAir::new(range_bus);
331 inventory.add_air(range_checker);
333
334 if self.continuation_enabled {
335 assert_eq!(inventory.ext_airs().len(), POSEIDON2_INSERTION_IDX);
336 let (_, compression_bus) = merkle_compression_buses.unwrap();
340 let direct_bus_idx = compression_bus.index;
341 let air = new_poseidon2_periphery_air(
342 vm_poseidon2_config(),
343 LookupBus::new(direct_bus_idx),
344 self.max_constraint_degree,
345 );
346 inventory.add_air_ref(air);
347 }
348 let execution_bridge = ExecutionBridge::new(execution_bus, program_bus);
349 let phantom = PhantomAir {
350 execution_bridge,
351 phantom_opcode: SystemOpcode::PHANTOM.global_opcode(),
352 };
353 inventory.add_air(phantom);
354
355 Ok(inventory)
356 }
357}
358
359pub struct SystemChipInventory<SC: StarkGenericConfig>
364where
365 Val<SC>: VmField,
366{
367 pub program_chip: ProgramChip<SC>,
368 pub connector_chip: VmConnectorChip<Val<SC>>,
369 pub memory_controller: MemoryController<Val<SC>>,
371 pub public_values_chip: Option<PublicValuesChip<Val<SC>>>,
372}
373
374impl<SC: StarkGenericConfig> SystemChipInventory<SC>
377where
378 Val<SC>: VmField,
379{
380 pub fn new(
381 config: &SystemConfig,
382 mem_inventory: &MemoryAirInventory<SC>,
383 range_checker: SharedVariableRangeCheckerChip,
384 hasher_chip: Option<Arc<Poseidon2PeripheryChip<Val<SC>>>>,
385 ) -> Self {
386 let program_chip = ProgramChip::unloaded();
389 let connector_chip = VmConnectorChip::<Val<SC>>::new(
390 range_checker.clone(),
391 config.memory_config.timestamp_max_bits,
392 );
393 let memory_bus = mem_inventory.bridge.memory_bus();
394 let memory_controller = match &mem_inventory.interface {
395 MemoryInterfaceAirs::Persistent {
396 boundary: _,
397 merkle,
398 } => {
399 assert!(config.continuation_enabled);
400 MemoryController::<Val<SC>>::with_persistent_memory(
401 memory_bus,
402 config.memory_config.clone(),
403 range_checker.clone(),
404 merkle.merkle_bus,
405 merkle.compression_bus,
406 hasher_chip.unwrap(),
407 )
408 }
409 MemoryInterfaceAirs::Volatile { boundary: _ } => {
410 assert!(!config.continuation_enabled);
411 MemoryController::with_volatile_memory(
412 memory_bus,
413 config.memory_config.clone(),
414 range_checker.clone(),
415 )
416 }
417 };
418
419 let public_values_chip = config.has_public_values_chip().then(|| {
420 VmChipWrapper::new(
421 PublicValuesFiller::new(
422 NativeAdapterExecutor::default(),
423 config.num_public_values,
424 (config.max_constraint_degree as u32)
425 .checked_sub(1)
426 .unwrap(),
427 ),
428 memory_controller.helper(),
429 )
430 });
431
432 Self {
433 program_chip,
434 connector_chip,
435 memory_controller,
436 public_values_chip,
437 }
438 }
439}
440
441impl<RA, SC> SystemChipComplex<RA, CpuBackend<SC>> for SystemChipInventory<SC>
442where
443 RA: RowMajorMatrixArena<Val<SC>>,
444 SC: StarkGenericConfig,
445 Val<SC>: VmField,
446{
447 fn load_program(&mut self, cached_program_trace: CommittedTraceData<CpuBackend<SC>>) {
448 let _ = self.program_chip.cached.replace(cached_program_trace);
449 }
450
451 fn transport_init_memory_to_device(&mut self, memory: &GuestMemory) {
452 self.memory_controller
453 .set_initial_memory(memory.memory.clone());
454 }
455
456 fn generate_proving_ctx(
457 &mut self,
458 system_records: SystemRecords<Val<SC>>,
459 mut record_arenas: Vec<RA>,
460 ) -> Vec<AirProvingContext<CpuBackend<SC>>> {
461 let SystemRecords {
462 from_state,
463 to_state,
464 exit_code,
465 filtered_exec_frequencies,
466 access_adapter_records,
467 touched_memory,
468 public_values,
469 } = system_records;
470
471 if let Some(chip) = &mut self.public_values_chip {
472 chip.inner.set_public_values(public_values);
473 }
474 self.program_chip.filtered_exec_frequencies = filtered_exec_frequencies;
475 let program_ctx = self.program_chip.generate_proving_ctx(());
476 self.connector_chip.begin(from_state);
477 self.connector_chip.end(to_state, exit_code);
478 let connector_ctx = self.connector_chip.generate_proving_ctx(());
479
480 let pv_ctx = self.public_values_chip.as_ref().map(|chip| {
481 let arena = record_arenas.remove(PUBLIC_VALUES_AIR_ID);
482 chip.generate_proving_ctx(arena)
483 });
484
485 let memory_ctxs = self
486 .memory_controller
487 .generate_proving_ctx(access_adapter_records, touched_memory);
488
489 [program_ctx, connector_ctx]
490 .into_iter()
491 .chain(pv_ctx)
492 .chain(memory_ctxs)
493 .collect()
494 }
495
496 fn memory_top_tree(&self) -> Option<&[[Val<SC>; CHUNK]]> {
497 match &self.memory_controller.interface_chip {
498 MemoryInterface::Persistent { merkle_chip, .. } => {
499 let top_tree = &merkle_chip.top_tree;
500 (!top_tree.is_empty()).then_some(top_tree.as_slice())
501 }
502 MemoryInterface::Volatile { .. } => None,
503 }
504 }
505
506 #[cfg(feature = "metrics")]
507 fn finalize_trace_heights(&self, heights: &mut [usize]) {
508 use openvm_stark_backend::ChipUsageGetter;
509
510 use crate::system::memory::interface::MemoryInterface;
511
512 let boundary_idx = PUBLIC_VALUES_AIR_ID + usize::from(self.public_values_chip.is_some());
513 let mut access_adapter_offset = boundary_idx + 1;
514 match &self.memory_controller.interface_chip {
515 MemoryInterface::Volatile { boundary_chip } => {
516 let boundary_height = boundary_chip
517 .final_memory
518 .as_ref()
519 .map(|m| m.len())
520 .unwrap_or(0);
521 heights[boundary_idx] = boundary_height;
522 }
523 MemoryInterface::Persistent {
524 boundary_chip,
525 merkle_chip,
526 ..
527 } => {
528 let boundary_height = 2 * boundary_chip.touched_labels.len();
529 heights[boundary_idx] = boundary_height;
530 heights[boundary_idx + 1] = merkle_chip.current_height;
531 access_adapter_offset += 1;
532
533 let poseidon_chip = self.memory_controller.hasher_chip.as_ref().unwrap();
536 let poseidon_height = poseidon_chip.current_trace_height();
537 let poseidon_idx = heights.len() - 1 - POSEIDON2_INSERTION_IDX;
540 heights[poseidon_idx] = poseidon_height;
541 }
542 }
543 let access_heights = &self
544 .memory_controller
545 .access_adapter_inventory
546 .trace_heights;
547 heights[access_adapter_offset..access_adapter_offset + access_heights.len()]
548 .copy_from_slice(access_heights);
549 }
550}
551
552#[derive(Clone)]
553pub struct SystemCpuBuilder;
554
555impl<SC, E> VmBuilder<E> for SystemCpuBuilder
556where
557 SC: StarkGenericConfig,
558 E: StarkEngine<SC = SC, PB = CpuBackend<SC>, PD = CpuDevice<SC>>,
559 Val<SC>: VmField,
560{
561 type VmConfig = SystemConfig;
562 type RecordArena = MatrixRecordArena<Val<SC>>;
563 type SystemChipInventory = SystemChipInventory<SC>;
564
565 fn create_chip_complex(
566 &self,
567 config: &SystemConfig,
568 airs: AirInventory<SC>,
569 ) -> Result<
570 VmChipComplex<SC, MatrixRecordArena<Val<SC>>, CpuBackend<SC>, SystemChipInventory<SC>>,
571 ChipInventoryError,
572 > {
573 let range_bus = airs.range_checker().bus;
574 let range_checker = Arc::new(VariableRangeCheckerChip::new(range_bus));
575
576 let mut inventory = ChipInventory::new(airs);
577 if config.has_public_values_chip() {
579 assert_eq!(
580 inventory.executor_idx_to_insertion_idx.len(),
581 PV_EXECUTOR_IDX
582 );
583 let insertion_idx = inventory
587 .airs()
588 .num_airs()
589 .checked_sub(1 + PUBLIC_VALUES_AIR_ID)
590 .unwrap();
591 inventory.executor_idx_to_insertion_idx.push(insertion_idx);
592 }
593 inventory.next_air::<VariableRangeCheckerAir>()?;
594 inventory.add_periphery_chip(range_checker.clone());
595
596 let hasher_chip = if config.continuation_enabled {
597 assert_eq!(inventory.chips().len(), POSEIDON2_INSERTION_IDX);
598 let direct_bus = if config.max_constraint_degree >= 7 {
600 inventory
601 .next_air::<Poseidon2PeripheryAir<Val<SC>, 0>>()?
602 .bus
603 } else {
604 inventory
605 .next_air::<Poseidon2PeripheryAir<Val<SC>, 1>>()?
606 .bus
607 };
608 let chip = Arc::new(Poseidon2PeripheryChip::new(
609 vm_poseidon2_config(),
610 direct_bus.index,
611 config.max_constraint_degree,
612 ));
613 inventory.add_periphery_chip(chip.clone());
614 Some(chip)
615 } else {
616 None
617 };
618 let system = SystemChipInventory::new(
619 config,
620 &inventory.airs().system().memory,
621 range_checker,
622 hasher_chip,
623 );
624
625 let phantom_chip = PhantomChip::new(PhantomFiller, system.memory_controller.helper());
626 inventory.add_executor_chip(phantom_chip);
627
628 Ok(VmChipComplex { system, inventory })
629 }
630}
631
632impl<SC: StarkGenericConfig> SystemWithFixedTraceHeights for SystemChipInventory<SC>
633where
634 Val<SC>: VmField,
635{
636 fn override_trace_heights(&mut self, heights: &[u32]) {
639 assert_eq!(
640 heights[PROGRAM_AIR_ID] as usize,
641 self.program_chip
642 .cached
643 .as_ref()
644 .expect("program not loaded")
645 .trace
646 .height()
647 );
648 assert_eq!(heights[CONNECTOR_AIR_ID], 2);
649 let mut memory_start_idx = PUBLIC_VALUES_AIR_ID;
650 if self.public_values_chip.is_some() {
651 memory_start_idx += 1;
652 }
653 self.memory_controller
654 .set_override_trace_heights(&heights[memory_start_idx..]);
655 }
656}