1use std::sync::Arc;
2
3use derive_more::derive::From;
4use openvm_circuit::{
5 arch::{
6 AirInventory, AirInventoryError, ChipInventory, ChipInventoryError, ExecutionBridge,
7 ExecutorInventoryBuilder, ExecutorInventoryError, RowMajorMatrixArena, VmCircuitExtension,
8 VmExecutionExtension, VmProverExtension,
9 },
10 system::{memory::SharedMemoryHelper, SystemPort},
11};
12use openvm_circuit_derive::AnyEnum;
13use openvm_circuit_primitives::{
14 bitwise_op_lookup::{
15 BitwiseOperationLookupAir, BitwiseOperationLookupBus, BitwiseOperationLookupChip,
16 SharedBitwiseOperationLookupChip,
17 },
18 range_tuple::{
19 RangeTupleCheckerAir, RangeTupleCheckerBus, RangeTupleCheckerChip,
20 SharedRangeTupleCheckerChip,
21 },
22};
23use openvm_cpu_backend::{CpuBackend, CpuDevice};
24use openvm_instructions::{program::DEFAULT_PC_STEP, LocalOpcode, PhantomDiscriminant};
25use openvm_rv32im_transpiler::{
26 BaseAluOpcode, BranchEqualOpcode, BranchLessThanOpcode, DivRemOpcode, LessThanOpcode,
27 MulHOpcode, MulOpcode, Rv32AuipcOpcode, Rv32HintStoreOpcode, Rv32JalLuiOpcode, Rv32JalrOpcode,
28 Rv32LoadStoreOpcode, Rv32Phantom, ShiftOpcode,
29};
30use openvm_stark_backend::{p3_field::PrimeField32, StarkEngine, StarkProtocolConfig, Val};
31use serde::{Deserialize, Serialize};
32use strum::IntoEnumIterator;
33
34use crate::{adapters::*, *};
35
36cfg_if::cfg_if! {
37 if #[cfg(feature = "cuda")] {
38 mod cuda;
39 pub use cuda::{
40 Rv32ImGpuProverExt as Rv32ImGpuProverExt,
41 };
42 } else {
43 pub use self::{
44 Rv32ImCpuProverExt as Rv32ImProverExt,
45 };
46 }
47}
48
49#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
53pub struct Rv32I;
54
55#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
57pub struct Rv32Io;
58
59#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
61pub struct Rv32M {
62 #[serde(default = "default_range_tuple_checker_sizes")]
63 pub range_tuple_checker_sizes: [u32; 2],
64}
65
66impl Default for Rv32M {
67 fn default() -> Self {
68 Self {
69 range_tuple_checker_sizes: default_range_tuple_checker_sizes(),
70 }
71 }
72}
73
74fn default_range_tuple_checker_sizes() -> [u32; 2] {
75 [1 << 8, 8 * (1 << 8)]
76}
77
78#[derive(Clone, From, AnyEnum, Executor, MeteredExecutor, PreflightExecutor)]
83#[cfg_attr(
84 feature = "aot",
85 derive(
86 openvm_circuit_derive::AotExecutor,
87 openvm_circuit_derive::AotMeteredExecutor
88 )
89)]
90pub enum Rv32IExecutor {
91 BaseAlu(Rv32BaseAluExecutor),
93 LessThan(Rv32LessThanExecutor),
94 Shift(Rv32ShiftExecutor),
95 LoadStore(Rv32LoadStoreExecutor),
96 LoadSignExtend(Rv32LoadSignExtendExecutor),
97 BranchEqual(Rv32BranchEqualExecutor),
98 BranchLessThan(Rv32BranchLessThanExecutor),
99 JalLui(Rv32JalLuiExecutor),
100 Jalr(Rv32JalrExecutor),
101 Auipc(Rv32AuipcExecutor),
102}
103
104#[derive(Clone, From, AnyEnum, Executor, MeteredExecutor, PreflightExecutor)]
106#[cfg_attr(
107 feature = "aot",
108 derive(
109 openvm_circuit_derive::AotExecutor,
110 openvm_circuit_derive::AotMeteredExecutor
111 )
112)]
113pub enum Rv32MExecutor {
114 Multiplication(Rv32MultiplicationExecutor),
115 MultiplicationHigh(Rv32MulHExecutor),
116 DivRem(Rv32DivRemExecutor),
117}
118
119#[derive(Clone, Copy, From, AnyEnum, Executor, MeteredExecutor, PreflightExecutor)]
121#[cfg_attr(
122 feature = "aot",
123 derive(
124 openvm_circuit_derive::AotExecutor,
125 openvm_circuit_derive::AotMeteredExecutor
126 )
127)]
128pub enum Rv32IoExecutor {
129 HintStore(Rv32HintStoreExecutor),
130}
131
132impl<F: PrimeField32> VmExecutionExtension<F> for Rv32I {
135 type Executor = Rv32IExecutor;
136
137 fn extend_execution(
138 &self,
139 inventory: &mut ExecutorInventoryBuilder<F, Rv32IExecutor>,
140 ) -> Result<(), ExecutorInventoryError> {
141 let pointer_max_bits = inventory.pointer_max_bits();
142
143 let base_alu =
144 Rv32BaseAluExecutor::new(Rv32BaseAluAdapterExecutor, BaseAluOpcode::CLASS_OFFSET);
145 inventory.add_executor(base_alu, BaseAluOpcode::iter().map(|x| x.global_opcode()))?;
146
147 let lt = LessThanExecutor::new(Rv32BaseAluAdapterExecutor, LessThanOpcode::CLASS_OFFSET);
148 inventory.add_executor(lt, LessThanOpcode::iter().map(|x| x.global_opcode()))?;
149
150 let shift = ShiftExecutor::new(Rv32BaseAluAdapterExecutor, ShiftOpcode::CLASS_OFFSET);
151 inventory.add_executor(shift, ShiftOpcode::iter().map(|x| x.global_opcode()))?;
152
153 let load_store = LoadStoreExecutor::new(
154 Rv32LoadStoreAdapterExecutor::new(pointer_max_bits),
155 Rv32LoadStoreOpcode::CLASS_OFFSET,
156 );
157 inventory.add_executor(
158 load_store,
159 Rv32LoadStoreOpcode::iter()
160 .take(Rv32LoadStoreOpcode::STOREB as usize + 1)
161 .map(|x| x.global_opcode()),
162 )?;
163
164 let load_sign_extend =
165 LoadSignExtendExecutor::new(Rv32LoadStoreAdapterExecutor::new(pointer_max_bits));
166 inventory.add_executor(
167 load_sign_extend,
168 [Rv32LoadStoreOpcode::LOADB, Rv32LoadStoreOpcode::LOADH].map(|x| x.global_opcode()),
169 )?;
170
171 let beq = BranchEqualExecutor::new(
172 Rv32BranchAdapterExecutor,
173 BranchEqualOpcode::CLASS_OFFSET,
174 DEFAULT_PC_STEP,
175 );
176 inventory.add_executor(beq, BranchEqualOpcode::iter().map(|x| x.global_opcode()))?;
177
178 let blt = BranchLessThanExecutor::new(
179 Rv32BranchAdapterExecutor,
180 BranchLessThanOpcode::CLASS_OFFSET,
181 );
182 inventory.add_executor(blt, BranchLessThanOpcode::iter().map(|x| x.global_opcode()))?;
183
184 let jal_lui = Rv32JalLuiExecutor::new(Rv32CondRdWriteAdapterExecutor::new(
185 Rv32RdWriteAdapterExecutor,
186 ));
187 inventory.add_executor(jal_lui, Rv32JalLuiOpcode::iter().map(|x| x.global_opcode()))?;
188
189 let jalr = Rv32JalrExecutor::new(Rv32JalrAdapterExecutor);
190 inventory.add_executor(jalr, Rv32JalrOpcode::iter().map(|x| x.global_opcode()))?;
191
192 let auipc = Rv32AuipcExecutor::new(Rv32RdWriteAdapterExecutor);
193 inventory.add_executor(auipc, Rv32AuipcOpcode::iter().map(|x| x.global_opcode()))?;
194
195 inventory.add_phantom_sub_executor(
197 phantom::Rv32HintInputSubEx,
198 PhantomDiscriminant(Rv32Phantom::HintInput as u16),
199 )?;
200 inventory.add_phantom_sub_executor(
201 phantom::Rv32HintRandomSubEx,
202 PhantomDiscriminant(Rv32Phantom::HintRandom as u16),
203 )?;
204 inventory.add_phantom_sub_executor(
205 phantom::Rv32PrintStrSubEx,
206 PhantomDiscriminant(Rv32Phantom::PrintStr as u16),
207 )?;
208
209 Ok(())
210 }
211}
212
213impl<SC: StarkProtocolConfig> VmCircuitExtension<SC> for Rv32I {
214 fn extend_circuit(&self, inventory: &mut AirInventory<SC>) -> Result<(), AirInventoryError> {
215 let SystemPort {
216 execution_bus,
217 program_bus,
218 memory_bridge,
219 } = inventory.system().port();
220
221 let exec_bridge = ExecutionBridge::new(execution_bus, program_bus);
222 let range_checker = inventory.range_checker().bus;
223 let pointer_max_bits = inventory.pointer_max_bits();
224
225 let bitwise_lu = {
226 let existing_air = inventory.find_air::<BitwiseOperationLookupAir<8>>().next();
228 if let Some(air) = existing_air {
229 air.bus
230 } else {
231 let bus = BitwiseOperationLookupBus::new(inventory.new_bus_idx());
232 let air = BitwiseOperationLookupAir::<8>::new(bus);
233 inventory.add_air(air);
234 air.bus
235 }
236 };
237
238 let base_alu = Rv32BaseAluAir::new(
239 Rv32BaseAluAdapterAir::new(exec_bridge, memory_bridge, bitwise_lu),
240 BaseAluCoreAir::new(bitwise_lu, BaseAluOpcode::CLASS_OFFSET),
241 );
242 inventory.add_air(base_alu);
243
244 let lt = Rv32LessThanAir::new(
245 Rv32BaseAluAdapterAir::new(exec_bridge, memory_bridge, bitwise_lu),
246 LessThanCoreAir::new(bitwise_lu, LessThanOpcode::CLASS_OFFSET),
247 );
248 inventory.add_air(lt);
249
250 let shift = Rv32ShiftAir::new(
251 Rv32BaseAluAdapterAir::new(exec_bridge, memory_bridge, bitwise_lu),
252 ShiftCoreAir::new(bitwise_lu, range_checker, ShiftOpcode::CLASS_OFFSET),
253 );
254 inventory.add_air(shift);
255
256 let load_store = Rv32LoadStoreAir::new(
257 Rv32LoadStoreAdapterAir::new(
258 memory_bridge,
259 exec_bridge,
260 range_checker,
261 pointer_max_bits,
262 ),
263 LoadStoreCoreAir::new(Rv32LoadStoreOpcode::CLASS_OFFSET),
264 );
265 inventory.add_air(load_store);
266
267 let load_sign_extend = Rv32LoadSignExtendAir::new(
268 Rv32LoadStoreAdapterAir::new(
269 memory_bridge,
270 exec_bridge,
271 range_checker,
272 pointer_max_bits,
273 ),
274 LoadSignExtendCoreAir::new(range_checker),
275 );
276 inventory.add_air(load_sign_extend);
277
278 let beq = Rv32BranchEqualAir::new(
279 Rv32BranchAdapterAir::new(exec_bridge, memory_bridge),
280 BranchEqualCoreAir::new(BranchEqualOpcode::CLASS_OFFSET, DEFAULT_PC_STEP),
281 );
282 inventory.add_air(beq);
283
284 let blt = Rv32BranchLessThanAir::new(
285 Rv32BranchAdapterAir::new(exec_bridge, memory_bridge),
286 BranchLessThanCoreAir::new(bitwise_lu, BranchLessThanOpcode::CLASS_OFFSET),
287 );
288 inventory.add_air(blt);
289
290 let jal_lui = Rv32JalLuiAir::new(
291 Rv32CondRdWriteAdapterAir::new(Rv32RdWriteAdapterAir::new(memory_bridge, exec_bridge)),
292 Rv32JalLuiCoreAir::new(bitwise_lu),
293 );
294 inventory.add_air(jal_lui);
295
296 let jalr = Rv32JalrAir::new(
297 Rv32JalrAdapterAir::new(memory_bridge, exec_bridge),
298 Rv32JalrCoreAir::new(bitwise_lu, range_checker),
299 );
300 inventory.add_air(jalr);
301
302 let auipc = Rv32AuipcAir::new(
303 Rv32RdWriteAdapterAir::new(memory_bridge, exec_bridge),
304 Rv32AuipcCoreAir::new(bitwise_lu),
305 );
306 inventory.add_air(auipc);
307
308 Ok(())
309 }
310}
311
312pub struct Rv32ImCpuProverExt;
313impl<SC, E, RA> VmProverExtension<E, RA, Rv32I> for Rv32ImCpuProverExt
316where
317 SC: StarkProtocolConfig,
318 E: StarkEngine<SC = SC, PB = CpuBackend<SC>, PD = CpuDevice<SC>>,
319 RA: RowMajorMatrixArena<Val<SC>>,
320 Val<SC>: PrimeField32,
321 SC::EF: Ord,
322{
323 fn extend_prover(
324 &self,
325 _: &Rv32I,
326 inventory: &mut ChipInventory<SC, RA, CpuBackend<SC>>,
327 ) -> Result<(), ChipInventoryError> {
328 let range_checker = inventory.range_checker()?.clone();
329 let timestamp_max_bits = inventory.timestamp_max_bits();
330 let pointer_max_bits = inventory.airs().pointer_max_bits();
331 let mem_helper = SharedMemoryHelper::new(range_checker.clone(), timestamp_max_bits);
332
333 let bitwise_lu = {
334 let existing_chip = inventory
335 .find_chip::<SharedBitwiseOperationLookupChip<8>>()
336 .next();
337 if let Some(chip) = existing_chip {
338 chip.clone()
339 } else {
340 let air: &BitwiseOperationLookupAir<8> = inventory.next_air()?;
341 let chip = Arc::new(BitwiseOperationLookupChip::new(air.bus));
342 inventory.add_periphery_chip(chip.clone());
343 chip
344 }
345 };
346
347 inventory.next_air::<Rv32BaseAluAir>()?;
350 let base_alu = Rv32BaseAluChip::new(
351 BaseAluFiller::new(
352 Rv32BaseAluAdapterFiller::new(bitwise_lu.clone()),
353 bitwise_lu.clone(),
354 BaseAluOpcode::CLASS_OFFSET,
355 ),
356 mem_helper.clone(),
357 );
358 inventory.add_executor_chip(base_alu);
359
360 inventory.next_air::<Rv32LessThanAir>()?;
361 let lt = Rv32LessThanChip::new(
362 LessThanFiller::new(
363 Rv32BaseAluAdapterFiller::new(bitwise_lu.clone()),
364 bitwise_lu.clone(),
365 LessThanOpcode::CLASS_OFFSET,
366 ),
367 mem_helper.clone(),
368 );
369 inventory.add_executor_chip(lt);
370
371 inventory.next_air::<Rv32ShiftAir>()?;
372 let shift = Rv32ShiftChip::new(
373 ShiftFiller::new(
374 Rv32BaseAluAdapterFiller::new(bitwise_lu.clone()),
375 bitwise_lu.clone(),
376 range_checker.clone(),
377 ShiftOpcode::CLASS_OFFSET,
378 ),
379 mem_helper.clone(),
380 );
381 inventory.add_executor_chip(shift);
382
383 inventory.next_air::<Rv32LoadStoreAir>()?;
384 let load_store_chip = Rv32LoadStoreChip::new(
385 LoadStoreFiller::new(
386 Rv32LoadStoreAdapterFiller::new(pointer_max_bits, range_checker.clone()),
387 Rv32LoadStoreOpcode::CLASS_OFFSET,
388 ),
389 mem_helper.clone(),
390 );
391 inventory.add_executor_chip(load_store_chip);
392
393 inventory.next_air::<Rv32LoadSignExtendAir>()?;
394 let load_sign_extend = Rv32LoadSignExtendChip::new(
395 LoadSignExtendFiller::new(
396 Rv32LoadStoreAdapterFiller::new(pointer_max_bits, range_checker.clone()),
397 range_checker.clone(),
398 ),
399 mem_helper.clone(),
400 );
401 inventory.add_executor_chip(load_sign_extend);
402
403 inventory.next_air::<Rv32BranchEqualAir>()?;
404 let beq = Rv32BranchEqualChip::new(
405 BranchEqualFiller::new(
406 Rv32BranchAdapterFiller,
407 BranchEqualOpcode::CLASS_OFFSET,
408 DEFAULT_PC_STEP,
409 ),
410 mem_helper.clone(),
411 );
412 inventory.add_executor_chip(beq);
413
414 inventory.next_air::<Rv32BranchLessThanAir>()?;
415 let blt = Rv32BranchLessThanChip::new(
416 BranchLessThanFiller::new(
417 Rv32BranchAdapterFiller,
418 bitwise_lu.clone(),
419 BranchLessThanOpcode::CLASS_OFFSET,
420 ),
421 mem_helper.clone(),
422 );
423 inventory.add_executor_chip(blt);
424
425 inventory.next_air::<Rv32JalLuiAir>()?;
426 let jal_lui = Rv32JalLuiChip::new(
427 Rv32JalLuiFiller::new(
428 Rv32CondRdWriteAdapterFiller::new(Rv32RdWriteAdapterFiller),
429 bitwise_lu.clone(),
430 ),
431 mem_helper.clone(),
432 );
433 inventory.add_executor_chip(jal_lui);
434
435 inventory.next_air::<Rv32JalrAir>()?;
436 let jalr = Rv32JalrChip::new(
437 Rv32JalrFiller::new(
438 Rv32JalrAdapterFiller,
439 bitwise_lu.clone(),
440 range_checker.clone(),
441 ),
442 mem_helper.clone(),
443 );
444 inventory.add_executor_chip(jalr);
445
446 inventory.next_air::<Rv32AuipcAir>()?;
447 let auipc = Rv32AuipcChip::new(
448 Rv32AuipcFiller::new(Rv32RdWriteAdapterFiller, bitwise_lu.clone()),
449 mem_helper.clone(),
450 );
451 inventory.add_executor_chip(auipc);
452
453 Ok(())
454 }
455}
456
457impl<F> VmExecutionExtension<F> for Rv32M {
458 type Executor = Rv32MExecutor;
459
460 fn extend_execution(
461 &self,
462 inventory: &mut ExecutorInventoryBuilder<F, Rv32MExecutor>,
463 ) -> Result<(), ExecutorInventoryError> {
464 let mult =
465 Rv32MultiplicationExecutor::new(Rv32MultAdapterExecutor, MulOpcode::CLASS_OFFSET);
466 inventory.add_executor(mult, MulOpcode::iter().map(|x| x.global_opcode()))?;
467
468 let mul_h = Rv32MulHExecutor::new(Rv32MultAdapterExecutor, MulHOpcode::CLASS_OFFSET);
469 inventory.add_executor(mul_h, MulHOpcode::iter().map(|x| x.global_opcode()))?;
470
471 let div_rem = Rv32DivRemExecutor::new(Rv32MultAdapterExecutor, DivRemOpcode::CLASS_OFFSET);
472 inventory.add_executor(div_rem, DivRemOpcode::iter().map(|x| x.global_opcode()))?;
473
474 Ok(())
475 }
476}
477
478impl<SC: StarkProtocolConfig> VmCircuitExtension<SC> for Rv32M {
479 fn extend_circuit(&self, inventory: &mut AirInventory<SC>) -> Result<(), AirInventoryError> {
480 let SystemPort {
481 execution_bus,
482 program_bus,
483 memory_bridge,
484 } = inventory.system().port();
485 let exec_bridge = ExecutionBridge::new(execution_bus, program_bus);
486
487 let bitwise_lu = {
488 let existing_air = inventory.find_air::<BitwiseOperationLookupAir<8>>().next();
489 if let Some(air) = existing_air {
490 air.bus
491 } else {
492 let bus = BitwiseOperationLookupBus::new(inventory.new_bus_idx());
493 let air = BitwiseOperationLookupAir::<8>::new(bus);
494 inventory.add_air(air);
495 air.bus
496 }
497 };
498
499 let range_tuple_checker = {
500 let existing_air = inventory.find_air::<RangeTupleCheckerAir<2>>().find(|c| {
501 c.bus.sizes[0] >= self.range_tuple_checker_sizes[0]
502 && c.bus.sizes[1] >= self.range_tuple_checker_sizes[1]
503 });
504 if let Some(air) = existing_air {
505 air.bus
506 } else {
507 let bus = RangeTupleCheckerBus::new(
508 inventory.new_bus_idx(),
509 self.range_tuple_checker_sizes,
510 );
511 let air = RangeTupleCheckerAir { bus };
512 inventory.add_air(air);
513 air.bus
514 }
515 };
516
517 let mult = Rv32MultiplicationAir::new(
518 Rv32MultAdapterAir::new(exec_bridge, memory_bridge),
519 MultiplicationCoreAir::new(range_tuple_checker, MulOpcode::CLASS_OFFSET),
520 );
521 inventory.add_air(mult);
522
523 let mul_h = Rv32MulHAir::new(
524 Rv32MultAdapterAir::new(exec_bridge, memory_bridge),
525 MulHCoreAir::new(bitwise_lu, range_tuple_checker),
526 );
527 inventory.add_air(mul_h);
528
529 let div_rem = Rv32DivRemAir::new(
530 Rv32MultAdapterAir::new(exec_bridge, memory_bridge),
531 DivRemCoreAir::new(bitwise_lu, range_tuple_checker, DivRemOpcode::CLASS_OFFSET),
532 );
533 inventory.add_air(div_rem);
534
535 Ok(())
536 }
537}
538
539impl<SC, E, RA> VmProverExtension<E, RA, Rv32M> for Rv32ImCpuProverExt
542where
543 SC: StarkProtocolConfig,
544 E: StarkEngine<SC = SC, PB = CpuBackend<SC>, PD = CpuDevice<SC>>,
545 RA: RowMajorMatrixArena<Val<SC>>,
546 Val<SC>: PrimeField32,
547 SC::EF: Ord,
548{
549 fn extend_prover(
550 &self,
551 extension: &Rv32M,
552 inventory: &mut ChipInventory<SC, RA, CpuBackend<SC>>,
553 ) -> Result<(), ChipInventoryError> {
554 let range_checker = inventory.range_checker()?.clone();
555 let timestamp_max_bits = inventory.timestamp_max_bits();
556 let mem_helper = SharedMemoryHelper::new(range_checker.clone(), timestamp_max_bits);
557
558 let bitwise_lu = {
559 let existing_chip = inventory
560 .find_chip::<SharedBitwiseOperationLookupChip<8>>()
561 .next();
562 if let Some(chip) = existing_chip {
563 chip.clone()
564 } else {
565 let air: &BitwiseOperationLookupAir<8> = inventory.next_air()?;
566 let chip = Arc::new(BitwiseOperationLookupChip::new(air.bus));
567 inventory.add_periphery_chip(chip.clone());
568 chip
569 }
570 };
571
572 let range_tuple_checker = {
573 let existing_chip = inventory
574 .find_chip::<SharedRangeTupleCheckerChip<2>>()
575 .find(|c| {
576 c.bus().sizes[0] >= extension.range_tuple_checker_sizes[0]
577 && c.bus().sizes[1] >= extension.range_tuple_checker_sizes[1]
578 });
579 if let Some(chip) = existing_chip {
580 chip.clone()
581 } else {
582 let air: &RangeTupleCheckerAir<2> = inventory.next_air()?;
583 let chip = SharedRangeTupleCheckerChip::new(RangeTupleCheckerChip::new(air.bus));
584 inventory.add_periphery_chip(chip.clone());
585 chip
586 }
587 };
588
589 inventory.next_air::<Rv32MultiplicationAir>()?;
592 let mult = Rv32MultiplicationChip::new(
593 MultiplicationFiller::new(
594 Rv32MultAdapterFiller,
595 range_tuple_checker.clone(),
596 MulOpcode::CLASS_OFFSET,
597 ),
598 mem_helper.clone(),
599 );
600 inventory.add_executor_chip(mult);
601
602 inventory.next_air::<Rv32MulHAir>()?;
603 let mul_h = Rv32MulHChip::new(
604 MulHFiller::new(
605 Rv32MultAdapterFiller,
606 bitwise_lu.clone(),
607 range_tuple_checker.clone(),
608 ),
609 mem_helper.clone(),
610 );
611 inventory.add_executor_chip(mul_h);
612
613 inventory.next_air::<Rv32DivRemAir>()?;
614 let div_rem = Rv32DivRemChip::new(
615 DivRemFiller::new(
616 Rv32MultAdapterFiller,
617 bitwise_lu.clone(),
618 range_tuple_checker.clone(),
619 DivRemOpcode::CLASS_OFFSET,
620 ),
621 mem_helper.clone(),
622 );
623 inventory.add_executor_chip(div_rem);
624
625 Ok(())
626 }
627}
628
629impl<F> VmExecutionExtension<F> for Rv32Io {
630 type Executor = Rv32IoExecutor;
631
632 fn extend_execution(
633 &self,
634 inventory: &mut ExecutorInventoryBuilder<F, Rv32IoExecutor>,
635 ) -> Result<(), ExecutorInventoryError> {
636 let pointer_max_bits = inventory.pointer_max_bits();
637 let hint_store =
638 Rv32HintStoreExecutor::new(pointer_max_bits, Rv32HintStoreOpcode::CLASS_OFFSET);
639 inventory.add_executor(
640 hint_store,
641 Rv32HintStoreOpcode::iter().map(|x| x.global_opcode()),
642 )?;
643
644 Ok(())
645 }
646}
647
648impl<SC: StarkProtocolConfig> VmCircuitExtension<SC> for Rv32Io {
649 fn extend_circuit(&self, inventory: &mut AirInventory<SC>) -> Result<(), AirInventoryError> {
650 let SystemPort {
651 execution_bus,
652 program_bus,
653 memory_bridge,
654 } = inventory.system().port();
655
656 let exec_bridge = ExecutionBridge::new(execution_bus, program_bus);
657 let pointer_max_bits = inventory.pointer_max_bits();
658
659 let bitwise_lu = {
660 let existing_air = inventory.find_air::<BitwiseOperationLookupAir<8>>().next();
661 if let Some(air) = existing_air {
662 air.bus
663 } else {
664 let bus = BitwiseOperationLookupBus::new(inventory.new_bus_idx());
665 let air = BitwiseOperationLookupAir::<8>::new(bus);
666 inventory.add_air(air);
667 air.bus
668 }
669 };
670
671 let hint_store = Rv32HintStoreAir::new(
672 exec_bridge,
673 memory_bridge,
674 bitwise_lu,
675 Rv32HintStoreOpcode::CLASS_OFFSET,
676 pointer_max_bits,
677 );
678 inventory.add_air(hint_store);
679
680 Ok(())
681 }
682}
683
684impl<SC, E, RA> VmProverExtension<E, RA, Rv32Io> for Rv32ImCpuProverExt
687where
688 SC: StarkProtocolConfig,
689 E: StarkEngine<SC = SC, PB = CpuBackend<SC>, PD = CpuDevice<SC>>,
690 RA: RowMajorMatrixArena<Val<SC>>,
691 Val<SC>: PrimeField32,
692 SC::EF: Ord,
693{
694 fn extend_prover(
695 &self,
696 _: &Rv32Io,
697 inventory: &mut ChipInventory<SC, RA, CpuBackend<SC>>,
698 ) -> Result<(), ChipInventoryError> {
699 let range_checker = inventory.range_checker()?.clone();
700 let timestamp_max_bits = inventory.timestamp_max_bits();
701 let mem_helper = SharedMemoryHelper::new(range_checker.clone(), timestamp_max_bits);
702 let pointer_max_bits = inventory.airs().pointer_max_bits();
703
704 let bitwise_lu = {
705 let existing_chip = inventory
706 .find_chip::<SharedBitwiseOperationLookupChip<8>>()
707 .next();
708 if let Some(chip) = existing_chip {
709 chip.clone()
710 } else {
711 let air: &BitwiseOperationLookupAir<8> = inventory.next_air()?;
712 let chip = Arc::new(BitwiseOperationLookupChip::new(air.bus));
713 inventory.add_periphery_chip(chip.clone());
714 chip
715 }
716 };
717
718 inventory.next_air::<Rv32HintStoreAir>()?;
719 let hint_store = Rv32HintStoreChip::new(
720 Rv32HintStoreFiller::new(pointer_max_bits, bitwise_lu.clone()),
721 mem_helper.clone(),
722 );
723 inventory.add_executor_chip(hint_store);
724
725 Ok(())
726 }
727}
728
729mod phantom {
731 use eyre::bail;
732 use openvm_circuit::{
733 arch::{PhantomSubExecutor, Streams},
734 system::memory::online::GuestMemory,
735 };
736 use openvm_instructions::PhantomDiscriminant;
737 use openvm_stark_backend::p3_field::{Field, PrimeField32};
738 use rand::{rngs::StdRng, Rng};
739
740 use crate::adapters::{memory_read, read_rv32_register};
741
742 pub struct Rv32HintInputSubEx;
743 pub struct Rv32HintRandomSubEx;
744 pub struct Rv32PrintStrSubEx;
745
746 impl<F: Field> PhantomSubExecutor<F> for Rv32HintInputSubEx {
747 fn phantom_execute(
748 &self,
749 _: &GuestMemory,
750 streams: &mut Streams<F>,
751 _: &mut StdRng,
752 _: PhantomDiscriminant,
753 _: u32,
754 _: u32,
755 _: u16,
756 ) -> eyre::Result<()> {
757 let mut hint = match streams.input_stream.pop_front() {
758 Some(hint) => hint,
759 None => {
760 bail!("EndOfInputStream");
761 }
762 };
763 streams.hint_stream.clear();
764 streams.hint_stream.extend(
765 (hint.len() as u32)
766 .to_le_bytes()
767 .iter()
768 .map(|b| F::from_u8(*b)),
769 );
770 let capacity = hint.len().div_ceil(4) * 4;
772 hint.resize(capacity, F::ZERO);
773 streams.hint_stream.extend(hint);
774 Ok(())
775 }
776 }
777
778 impl<F: PrimeField32> PhantomSubExecutor<F> for Rv32HintRandomSubEx {
779 fn phantom_execute(
780 &self,
781 memory: &GuestMemory,
782 streams: &mut Streams<F>,
783 rng: &mut StdRng,
784 _: PhantomDiscriminant,
785 a: u32,
786 _: u32,
787 _: u16,
788 ) -> eyre::Result<()> {
789 static WARN_ONCE: std::sync::Once = std::sync::Once::new();
790 WARN_ONCE.call_once(|| {
791 eprintln!("WARNING: Using fixed-seed RNG for deterministic randomness. Consider security implications for your use case.");
792 });
793
794 let len = read_rv32_register(memory, a) as usize;
795 streams.hint_stream.clear();
796 streams
797 .hint_stream
798 .extend(std::iter::repeat_with(|| F::from_u8(rng.random::<u8>())).take(len * 4));
799 Ok(())
800 }
801 }
802
803 impl<F: PrimeField32> PhantomSubExecutor<F> for Rv32PrintStrSubEx {
804 fn phantom_execute(
805 &self,
806 memory: &GuestMemory,
807 _: &mut Streams<F>,
808 _: &mut StdRng,
809 _: PhantomDiscriminant,
810 a: u32,
811 b: u32,
812 _: u16,
813 ) -> eyre::Result<()> {
814 let rd = read_rv32_register(memory, a);
815 let rs1 = read_rv32_register(memory, b);
816 let bytes = (0..rs1)
817 .map(|i| memory_read::<1>(memory, 2, rd + i)[0])
818 .collect::<Vec<u8>>();
819 let peeked_str = String::from_utf8(bytes)?;
820 print!("{peeked_str}");
821 Ok(())
822 }
823 }
824}