1use derive_more::derive::From;
2use openvm_circuit::{
3 arch::{
4 SystemConfig, SystemPort, VmExtension, VmInventory, VmInventoryBuilder, VmInventoryError,
5 },
6 system::phantom::PhantomChip,
7};
8use openvm_circuit_derive::{AnyEnum, InstructionExecutor, VmConfig};
9use openvm_circuit_primitives::{
10 bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip},
11 range_tuple::{RangeTupleCheckerBus, SharedRangeTupleCheckerChip},
12};
13use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter};
14use openvm_instructions::{program::DEFAULT_PC_STEP, LocalOpcode, PhantomDiscriminant};
15use openvm_rv32im_transpiler::{
16 BaseAluOpcode, BranchEqualOpcode, BranchLessThanOpcode, DivRemOpcode, LessThanOpcode,
17 MulHOpcode, MulOpcode, Rv32AuipcOpcode, Rv32HintStoreOpcode, Rv32JalLuiOpcode, Rv32JalrOpcode,
18 Rv32LoadStoreOpcode, Rv32Phantom, ShiftOpcode,
19};
20use openvm_stark_backend::p3_field::PrimeField32;
21use serde::{Deserialize, Serialize};
22use strum::IntoEnumIterator;
23
24use crate::{adapters::*, *};
25
26#[derive(Clone, Debug, VmConfig, derive_new::new, Serialize, Deserialize)]
28pub struct Rv32IConfig {
29 #[system]
30 pub system: SystemConfig,
31 #[extension]
32 pub base: Rv32I,
33 #[extension]
34 pub io: Rv32Io,
35}
36
37#[derive(Clone, Debug, Default, VmConfig, derive_new::new, Serialize, Deserialize)]
39pub struct Rv32ImConfig {
40 #[config]
41 pub rv32i: Rv32IConfig,
42 #[extension]
43 pub mul: Rv32M,
44}
45
46impl Default for Rv32IConfig {
47 fn default() -> Self {
48 let system = SystemConfig::default().with_continuations();
49 Self {
50 system,
51 base: Default::default(),
52 io: Default::default(),
53 }
54 }
55}
56
57impl Rv32IConfig {
58 pub fn with_public_values(public_values: usize) -> Self {
59 let system = SystemConfig::default()
60 .with_continuations()
61 .with_public_values(public_values);
62 Self {
63 system,
64 base: Default::default(),
65 io: Default::default(),
66 }
67 }
68
69 pub fn with_public_values_and_segment_len(public_values: usize, segment_len: usize) -> Self {
70 let system = SystemConfig::default()
71 .with_continuations()
72 .with_public_values(public_values)
73 .with_max_segment_len(segment_len);
74 Self {
75 system,
76 base: Default::default(),
77 io: Default::default(),
78 }
79 }
80}
81
82impl Rv32ImConfig {
83 pub fn with_public_values(public_values: usize) -> Self {
84 Self {
85 rv32i: Rv32IConfig::with_public_values(public_values),
86 mul: Default::default(),
87 }
88 }
89
90 pub fn with_public_values_and_segment_len(public_values: usize, segment_len: usize) -> Self {
91 Self {
92 rv32i: Rv32IConfig::with_public_values_and_segment_len(public_values, segment_len),
93 mul: Default::default(),
94 }
95 }
96}
97
98#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
102pub struct Rv32I;
103
104#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
106pub struct Rv32Io;
107
108#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
110pub struct Rv32M {
111 #[serde(default = "default_range_tuple_checker_sizes")]
112 pub range_tuple_checker_sizes: [u32; 2],
113}
114
115impl Default for Rv32M {
116 fn default() -> Self {
117 Self {
118 range_tuple_checker_sizes: default_range_tuple_checker_sizes(),
119 }
120 }
121}
122
123fn default_range_tuple_checker_sizes() -> [u32; 2] {
124 [1 << 8, 8 * (1 << 8)]
125}
126
127#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)]
131pub enum Rv32IExecutor<F: PrimeField32> {
132 BaseAlu(Rv32BaseAluChip<F>),
134 LessThan(Rv32LessThanChip<F>),
135 Shift(Rv32ShiftChip<F>),
136 LoadStore(Rv32LoadStoreChip<F>),
137 LoadSignExtend(Rv32LoadSignExtendChip<F>),
138 BranchEqual(Rv32BranchEqualChip<F>),
139 BranchLessThan(Rv32BranchLessThanChip<F>),
140 JalLui(Rv32JalLuiChip<F>),
141 Jalr(Rv32JalrChip<F>),
142 Auipc(Rv32AuipcChip<F>),
143}
144
145#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)]
147pub enum Rv32MExecutor<F: PrimeField32> {
148 Multiplication(Rv32MultiplicationChip<F>),
149 MultiplicationHigh(Rv32MulHChip<F>),
150 DivRem(Rv32DivRemChip<F>),
151}
152
153#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)]
155pub enum Rv32IoExecutor<F: PrimeField32> {
156 HintStore(Rv32HintStoreChip<F>),
157}
158
159#[derive(From, ChipUsageGetter, Chip, AnyEnum)]
160pub enum Rv32IPeriphery<F: PrimeField32> {
161 BitwiseOperationLookup(SharedBitwiseOperationLookupChip<8>),
162 Phantom(PhantomChip<F>),
164}
165
166#[derive(From, ChipUsageGetter, Chip, AnyEnum)]
167pub enum Rv32MPeriphery<F: PrimeField32> {
168 BitwiseOperationLookup(SharedBitwiseOperationLookupChip<8>),
169 RangeTupleChecker(SharedRangeTupleCheckerChip<2>),
171 Phantom(PhantomChip<F>),
173}
174
175#[derive(From, ChipUsageGetter, Chip, AnyEnum)]
176pub enum Rv32IoPeriphery<F: PrimeField32> {
177 BitwiseOperationLookup(SharedBitwiseOperationLookupChip<8>),
178 Phantom(PhantomChip<F>),
180}
181
182impl<F: PrimeField32> VmExtension<F> for Rv32I {
185 type Executor = Rv32IExecutor<F>;
186 type Periphery = Rv32IPeriphery<F>;
187
188 fn build(
189 &self,
190 builder: &mut VmInventoryBuilder<F>,
191 ) -> Result<VmInventory<Rv32IExecutor<F>, Rv32IPeriphery<F>>, VmInventoryError> {
192 let mut inventory = VmInventory::new();
193 let SystemPort {
194 execution_bus,
195 program_bus,
196 memory_bridge,
197 } = builder.system_port();
198
199 let range_checker = builder.system_base().range_checker_chip.clone();
200 let offline_memory = builder.system_base().offline_memory();
201 let pointer_max_bits = builder.system_config().memory_config.pointer_max_bits;
202
203 let bitwise_lu_chip = if let Some(&chip) = builder
204 .find_chip::<SharedBitwiseOperationLookupChip<8>>()
205 .first()
206 {
207 chip.clone()
208 } else {
209 let bitwise_lu_bus = BitwiseOperationLookupBus::new(builder.new_bus_idx());
210 let chip = SharedBitwiseOperationLookupChip::new(bitwise_lu_bus);
211 inventory.add_periphery_chip(chip.clone());
212 chip
213 };
214
215 let base_alu_chip = Rv32BaseAluChip::new(
216 Rv32BaseAluAdapterChip::new(
217 execution_bus,
218 program_bus,
219 memory_bridge,
220 bitwise_lu_chip.clone(),
221 ),
222 BaseAluCoreChip::new(bitwise_lu_chip.clone(), BaseAluOpcode::CLASS_OFFSET),
223 offline_memory.clone(),
224 );
225 inventory.add_executor(
226 base_alu_chip,
227 BaseAluOpcode::iter().map(|x| x.global_opcode()),
228 )?;
229
230 let lt_chip = Rv32LessThanChip::new(
231 Rv32BaseAluAdapterChip::new(
232 execution_bus,
233 program_bus,
234 memory_bridge,
235 bitwise_lu_chip.clone(),
236 ),
237 LessThanCoreChip::new(bitwise_lu_chip.clone(), LessThanOpcode::CLASS_OFFSET),
238 offline_memory.clone(),
239 );
240 inventory.add_executor(lt_chip, LessThanOpcode::iter().map(|x| x.global_opcode()))?;
241
242 let shift_chip = Rv32ShiftChip::new(
243 Rv32BaseAluAdapterChip::new(
244 execution_bus,
245 program_bus,
246 memory_bridge,
247 bitwise_lu_chip.clone(),
248 ),
249 ShiftCoreChip::new(
250 bitwise_lu_chip.clone(),
251 range_checker.clone(),
252 ShiftOpcode::CLASS_OFFSET,
253 ),
254 offline_memory.clone(),
255 );
256 inventory.add_executor(shift_chip, ShiftOpcode::iter().map(|x| x.global_opcode()))?;
257
258 let load_store_chip = Rv32LoadStoreChip::new(
259 Rv32LoadStoreAdapterChip::new(
260 execution_bus,
261 program_bus,
262 memory_bridge,
263 pointer_max_bits,
264 range_checker.clone(),
265 ),
266 LoadStoreCoreChip::new(Rv32LoadStoreOpcode::CLASS_OFFSET),
267 offline_memory.clone(),
268 );
269 inventory.add_executor(
270 load_store_chip,
271 Rv32LoadStoreOpcode::iter()
272 .take(Rv32LoadStoreOpcode::STOREB as usize + 1)
273 .map(|x| x.global_opcode()),
274 )?;
275
276 let load_sign_extend_chip = Rv32LoadSignExtendChip::new(
277 Rv32LoadStoreAdapterChip::new(
278 execution_bus,
279 program_bus,
280 memory_bridge,
281 pointer_max_bits,
282 range_checker.clone(),
283 ),
284 LoadSignExtendCoreChip::new(range_checker.clone()),
285 offline_memory.clone(),
286 );
287 inventory.add_executor(
288 load_sign_extend_chip,
289 [Rv32LoadStoreOpcode::LOADB, Rv32LoadStoreOpcode::LOADH].map(|x| x.global_opcode()),
290 )?;
291
292 let beq_chip = Rv32BranchEqualChip::new(
293 Rv32BranchAdapterChip::new(execution_bus, program_bus, memory_bridge),
294 BranchEqualCoreChip::new(BranchEqualOpcode::CLASS_OFFSET, DEFAULT_PC_STEP),
295 offline_memory.clone(),
296 );
297 inventory.add_executor(
298 beq_chip,
299 BranchEqualOpcode::iter().map(|x| x.global_opcode()),
300 )?;
301
302 let blt_chip = Rv32BranchLessThanChip::new(
303 Rv32BranchAdapterChip::new(execution_bus, program_bus, memory_bridge),
304 BranchLessThanCoreChip::new(
305 bitwise_lu_chip.clone(),
306 BranchLessThanOpcode::CLASS_OFFSET,
307 ),
308 offline_memory.clone(),
309 );
310 inventory.add_executor(
311 blt_chip,
312 BranchLessThanOpcode::iter().map(|x| x.global_opcode()),
313 )?;
314
315 let jal_lui_chip = Rv32JalLuiChip::new(
316 Rv32CondRdWriteAdapterChip::new(execution_bus, program_bus, memory_bridge),
317 Rv32JalLuiCoreChip::new(bitwise_lu_chip.clone()),
318 offline_memory.clone(),
319 );
320 inventory.add_executor(
321 jal_lui_chip,
322 Rv32JalLuiOpcode::iter().map(|x| x.global_opcode()),
323 )?;
324
325 let jalr_chip = Rv32JalrChip::new(
326 Rv32JalrAdapterChip::new(execution_bus, program_bus, memory_bridge),
327 Rv32JalrCoreChip::new(bitwise_lu_chip.clone(), range_checker.clone()),
328 offline_memory.clone(),
329 );
330 inventory.add_executor(jalr_chip, Rv32JalrOpcode::iter().map(|x| x.global_opcode()))?;
331
332 let auipc_chip = Rv32AuipcChip::new(
333 Rv32RdWriteAdapterChip::new(execution_bus, program_bus, memory_bridge),
334 Rv32AuipcCoreChip::new(bitwise_lu_chip.clone()),
335 offline_memory.clone(),
336 );
337 inventory.add_executor(
338 auipc_chip,
339 Rv32AuipcOpcode::iter().map(|x| x.global_opcode()),
340 )?;
341
342 builder.add_phantom_sub_executor(
344 phantom::Rv32HintInputSubEx,
345 PhantomDiscriminant(Rv32Phantom::HintInput as u16),
346 )?;
347 builder.add_phantom_sub_executor(
348 phantom::Rv32HintRandomSubEx::new(),
349 PhantomDiscriminant(Rv32Phantom::HintRandom as u16),
350 )?;
351 builder.add_phantom_sub_executor(
352 phantom::Rv32PrintStrSubEx,
353 PhantomDiscriminant(Rv32Phantom::PrintStr as u16),
354 )?;
355
356 Ok(inventory)
357 }
358}
359
360impl<F: PrimeField32> VmExtension<F> for Rv32M {
361 type Executor = Rv32MExecutor<F>;
362 type Periphery = Rv32MPeriphery<F>;
363
364 fn build(
365 &self,
366 builder: &mut VmInventoryBuilder<F>,
367 ) -> Result<VmInventory<Rv32MExecutor<F>, Rv32MPeriphery<F>>, VmInventoryError> {
368 let mut inventory = VmInventory::new();
369 let SystemPort {
370 execution_bus,
371 program_bus,
372 memory_bridge,
373 } = builder.system_port();
374 let offline_memory = builder.system_base().offline_memory();
375
376 let bitwise_lu_chip = if let Some(&chip) = builder
377 .find_chip::<SharedBitwiseOperationLookupChip<8>>()
378 .first()
379 {
380 chip.clone()
381 } else {
382 let bitwise_lu_bus = BitwiseOperationLookupBus::new(builder.new_bus_idx());
383 let chip = SharedBitwiseOperationLookupChip::new(bitwise_lu_bus);
384 inventory.add_periphery_chip(chip.clone());
385 chip
386 };
387
388 let range_tuple_checker = if let Some(chip) = builder
389 .find_chip::<SharedRangeTupleCheckerChip<2>>()
390 .into_iter()
391 .find(|c| {
392 c.bus().sizes[0] >= self.range_tuple_checker_sizes[0]
393 && c.bus().sizes[1] >= self.range_tuple_checker_sizes[1]
394 }) {
395 chip.clone()
396 } else {
397 let range_tuple_bus =
398 RangeTupleCheckerBus::new(builder.new_bus_idx(), self.range_tuple_checker_sizes);
399 let chip = SharedRangeTupleCheckerChip::new(range_tuple_bus);
400 inventory.add_periphery_chip(chip.clone());
401 chip
402 };
403
404 let mul_chip = Rv32MultiplicationChip::new(
405 Rv32MultAdapterChip::new(execution_bus, program_bus, memory_bridge),
406 MultiplicationCoreChip::new(range_tuple_checker.clone(), MulOpcode::CLASS_OFFSET),
407 offline_memory.clone(),
408 );
409 inventory.add_executor(mul_chip, MulOpcode::iter().map(|x| x.global_opcode()))?;
410
411 let mul_h_chip = Rv32MulHChip::new(
412 Rv32MultAdapterChip::new(execution_bus, program_bus, memory_bridge),
413 MulHCoreChip::new(bitwise_lu_chip.clone(), range_tuple_checker.clone()),
414 offline_memory.clone(),
415 );
416 inventory.add_executor(mul_h_chip, MulHOpcode::iter().map(|x| x.global_opcode()))?;
417
418 let div_rem_chip = Rv32DivRemChip::new(
419 Rv32MultAdapterChip::new(execution_bus, program_bus, memory_bridge),
420 DivRemCoreChip::new(
421 bitwise_lu_chip.clone(),
422 range_tuple_checker.clone(),
423 DivRemOpcode::CLASS_OFFSET,
424 ),
425 offline_memory.clone(),
426 );
427 inventory.add_executor(
428 div_rem_chip,
429 DivRemOpcode::iter().map(|x| x.global_opcode()),
430 )?;
431
432 Ok(inventory)
433 }
434}
435
436impl<F: PrimeField32> VmExtension<F> for Rv32Io {
437 type Executor = Rv32IoExecutor<F>;
438 type Periphery = Rv32IoPeriphery<F>;
439
440 fn build(
441 &self,
442 builder: &mut VmInventoryBuilder<F>,
443 ) -> Result<VmInventory<Self::Executor, Self::Periphery>, VmInventoryError> {
444 let mut inventory = VmInventory::new();
445 let SystemPort {
446 execution_bus,
447 program_bus,
448 memory_bridge,
449 } = builder.system_port();
450 let offline_memory = builder.system_base().offline_memory();
451
452 let bitwise_lu_chip = if let Some(&chip) = builder
453 .find_chip::<SharedBitwiseOperationLookupChip<8>>()
454 .first()
455 {
456 chip.clone()
457 } else {
458 let bitwise_lu_bus = BitwiseOperationLookupBus::new(builder.new_bus_idx());
459 let chip = SharedBitwiseOperationLookupChip::new(bitwise_lu_bus);
460 inventory.add_periphery_chip(chip.clone());
461 chip
462 };
463
464 let mut hintstore_chip = Rv32HintStoreChip::new(
465 execution_bus,
466 program_bus,
467 bitwise_lu_chip.clone(),
468 memory_bridge,
469 offline_memory.clone(),
470 builder.system_config().memory_config.pointer_max_bits,
471 Rv32HintStoreOpcode::CLASS_OFFSET,
472 );
473 hintstore_chip.set_streams(builder.streams().clone());
474
475 inventory.add_executor(
476 hintstore_chip,
477 Rv32HintStoreOpcode::iter().map(|x| x.global_opcode()),
478 )?;
479
480 Ok(inventory)
481 }
482}
483
484mod phantom {
486 use eyre::bail;
487 use openvm_circuit::{
488 arch::{PhantomSubExecutor, Streams},
489 system::memory::MemoryController,
490 };
491 use openvm_instructions::PhantomDiscriminant;
492 use openvm_stark_backend::p3_field::{Field, PrimeField32};
493 use rand::{rngs::OsRng, Rng};
494
495 use crate::adapters::unsafe_read_rv32_register;
496
497 pub struct Rv32HintInputSubEx;
498 pub struct Rv32HintRandomSubEx {
499 rng: OsRng,
500 }
501 impl Rv32HintRandomSubEx {
502 pub fn new() -> Self {
503 Self { rng: OsRng }
504 }
505 }
506 pub struct Rv32PrintStrSubEx;
507
508 impl<F: Field> PhantomSubExecutor<F> for Rv32HintInputSubEx {
509 fn phantom_execute(
510 &mut self,
511 _: &MemoryController<F>,
512 streams: &mut Streams<F>,
513 _: PhantomDiscriminant,
514 _: F,
515 _: F,
516 _: u16,
517 ) -> eyre::Result<()> {
518 let mut hint = match streams.input_stream.pop_front() {
519 Some(hint) => hint,
520 None => {
521 bail!("EndOfInputStream");
522 }
523 };
524 streams.hint_stream.clear();
525 streams.hint_stream.extend(
526 (hint.len() as u32)
527 .to_le_bytes()
528 .iter()
529 .map(|b| F::from_canonical_u8(*b)),
530 );
531 let capacity = hint.len().div_ceil(4) * 4;
533 hint.resize(capacity, F::ZERO);
534 streams.hint_stream.extend(hint);
535 Ok(())
536 }
537 }
538
539 impl<F: PrimeField32> PhantomSubExecutor<F> for Rv32HintRandomSubEx {
540 fn phantom_execute(
541 &mut self,
542 memory: &MemoryController<F>,
543 streams: &mut Streams<F>,
544 _: PhantomDiscriminant,
545 a: F,
546 _: F,
547 _: u16,
548 ) -> eyre::Result<()> {
549 let len = unsafe_read_rv32_register(memory, a) as usize;
550 streams.hint_stream.clear();
551 streams.hint_stream.extend(
552 std::iter::repeat_with(|| F::from_canonical_u8(self.rng.gen::<u8>())).take(len * 4),
553 );
554 Ok(())
555 }
556 }
557
558 impl<F: PrimeField32> PhantomSubExecutor<F> for Rv32PrintStrSubEx {
559 fn phantom_execute(
560 &mut self,
561 memory: &MemoryController<F>,
562 _: &mut Streams<F>,
563 _: PhantomDiscriminant,
564 a: F,
565 b: F,
566 _: u16,
567 ) -> eyre::Result<()> {
568 let rd = unsafe_read_rv32_register(memory, a);
569 let rs1 = unsafe_read_rv32_register(memory, b);
570 let bytes = (0..rs1)
571 .map(|i| -> eyre::Result<u8> {
572 let val = memory.unsafe_read_cell(F::TWO, F::from_canonical_u32(rd + i));
573 let byte: u8 = val.as_canonical_u32().try_into()?;
574 Ok(byte)
575 })
576 .collect::<eyre::Result<Vec<u8>>>()?;
577 let peeked_str = String::from_utf8(bytes)?;
578 print!("{peeked_str}");
579 Ok(())
580 }
581 }
582}