use std::sync::Arc;
use derive_more::derive::From;
use openvm_circuit::{
arch::{
SystemConfig, SystemExecutor, SystemPeriphery, SystemPort, VmChipComplex, VmConfig,
VmExtension, VmInventory, VmInventoryBuilder, VmInventoryError,
},
system::phantom::PhantomChip,
};
use openvm_circuit_derive::{AnyEnum, InstructionExecutor, VmConfig};
use openvm_circuit_primitives::{
bitwise_op_lookup::{BitwiseOperationLookupBus, BitwiseOperationLookupChip},
range_tuple::{RangeTupleCheckerBus, RangeTupleCheckerChip},
};
use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter};
use openvm_instructions::{program::DEFAULT_PC_STEP, PhantomDiscriminant, UsizeOpcode, VmOpcode};
use openvm_rv32im_transpiler::{
BaseAluOpcode, BranchEqualOpcode, BranchLessThanOpcode, DivRemOpcode, LessThanOpcode,
MulHOpcode, MulOpcode, Rv32AuipcOpcode, Rv32HintStoreOpcode, Rv32JalLuiOpcode, Rv32JalrOpcode,
Rv32LoadStoreOpcode, Rv32Phantom, ShiftOpcode,
};
use openvm_stark_backend::p3_field::PrimeField32;
use serde::{Deserialize, Serialize};
use strum::IntoEnumIterator;
use crate::{adapters::*, *};
#[derive(Clone, Debug, VmConfig, derive_new::new, Serialize, Deserialize)]
pub struct Rv32IConfig {
#[system]
pub system: SystemConfig,
#[extension]
pub base: Rv32I,
#[extension]
pub io: Rv32Io,
}
#[derive(Clone, Debug, VmConfig, derive_new::new, Serialize, Deserialize)]
pub struct Rv32ImConfig {
#[system]
pub system: SystemConfig,
#[extension]
pub base: Rv32I,
#[extension]
pub mul: Rv32M,
#[extension]
pub io: Rv32Io,
}
impl Default for Rv32IConfig {
fn default() -> Self {
let system = SystemConfig::default().with_continuations();
Self {
system,
base: Default::default(),
io: Default::default(),
}
}
}
impl Default for Rv32ImConfig {
fn default() -> Self {
let inner = Rv32IConfig::default();
Self {
system: inner.system,
base: inner.base,
mul: Default::default(),
io: Default::default(),
}
}
}
impl Rv32IConfig {
pub fn with_public_values(public_values: usize) -> Self {
let system = SystemConfig::default()
.with_continuations()
.with_public_values(public_values);
Self {
system,
base: Default::default(),
io: Default::default(),
}
}
pub fn with_public_values_and_segment_len(public_values: usize, segment_len: usize) -> Self {
let system = SystemConfig::default()
.with_continuations()
.with_public_values(public_values)
.with_max_segment_len(segment_len);
Self {
system,
base: Default::default(),
io: Default::default(),
}
}
}
impl Rv32ImConfig {
pub fn with_public_values(public_values: usize) -> Self {
let inner = Rv32IConfig::with_public_values(public_values);
Self {
system: inner.system,
base: inner.base,
mul: Default::default(),
io: Default::default(),
}
}
pub fn with_public_values_and_segment_len(public_values: usize, segment_len: usize) -> Self {
let inner = Rv32IConfig::with_public_values_and_segment_len(public_values, segment_len);
Self {
system: inner.system,
base: inner.base,
mul: Default::default(),
io: Default::default(),
}
}
}
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
pub struct Rv32I;
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
pub struct Rv32Io;
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct Rv32M {
#[serde(default = "default_range_tuple_checker_sizes")]
pub range_tuple_checker_sizes: [u32; 2],
}
impl Default for Rv32M {
fn default() -> Self {
Self {
range_tuple_checker_sizes: default_range_tuple_checker_sizes(),
}
}
}
fn default_range_tuple_checker_sizes() -> [u32; 2] {
[1 << 8, 8 * (1 << 8)]
}
#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)]
pub enum Rv32IExecutor<F: PrimeField32> {
BaseAlu(Rv32BaseAluChip<F>),
LessThan(Rv32LessThanChip<F>),
Shift(Rv32ShiftChip<F>),
LoadStore(Rv32LoadStoreChip<F>),
LoadSignExtend(Rv32LoadSignExtendChip<F>),
BranchEqual(Rv32BranchEqualChip<F>),
BranchLessThan(Rv32BranchLessThanChip<F>),
JalLui(Rv32JalLuiChip<F>),
Jalr(Rv32JalrChip<F>),
Auipc(Rv32AuipcChip<F>),
}
#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)]
pub enum Rv32MExecutor<F: PrimeField32> {
Multiplication(Rv32MultiplicationChip<F>),
MultiplicationHigh(Rv32MulHChip<F>),
DivRem(Rv32DivRemChip<F>),
}
#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)]
pub enum Rv32IoExecutor<F: PrimeField32> {
HintStore(Rv32HintStoreChip<F>),
}
#[derive(From, ChipUsageGetter, Chip, AnyEnum)]
pub enum Rv32IPeriphery<F: PrimeField32> {
BitwiseOperationLookup(Arc<BitwiseOperationLookupChip<8>>),
Phantom(PhantomChip<F>),
}
#[derive(From, ChipUsageGetter, Chip, AnyEnum)]
pub enum Rv32MPeriphery<F: PrimeField32> {
BitwiseOperationLookup(Arc<BitwiseOperationLookupChip<8>>),
RangeTupleChecker(Arc<RangeTupleCheckerChip<2>>),
Phantom(PhantomChip<F>),
}
#[derive(From, ChipUsageGetter, Chip, AnyEnum)]
pub enum Rv32IoPeriphery<F: PrimeField32> {
BitwiseOperationLookup(Arc<BitwiseOperationLookupChip<8>>),
Phantom(PhantomChip<F>),
}
impl<F: PrimeField32> VmExtension<F> for Rv32I {
type Executor = Rv32IExecutor<F>;
type Periphery = Rv32IPeriphery<F>;
fn build(
&self,
builder: &mut VmInventoryBuilder<F>,
) -> Result<VmInventory<Rv32IExecutor<F>, Rv32IPeriphery<F>>, VmInventoryError> {
let mut inventory = VmInventory::new();
let SystemPort {
execution_bus,
program_bus,
memory_controller,
} = builder.system_port();
let range_checker = builder.system_base().range_checker_chip.clone();
let bitwise_lu_chip = if let Some(chip) = builder
.find_chip::<Arc<BitwiseOperationLookupChip<8>>>()
.first()
{
Arc::clone(chip)
} else {
let bitwise_lu_bus = BitwiseOperationLookupBus::new(builder.new_bus_idx());
let chip = Arc::new(BitwiseOperationLookupChip::new(bitwise_lu_bus));
inventory.add_periphery_chip(chip.clone());
chip
};
let base_alu_chip = Rv32BaseAluChip::new(
Rv32BaseAluAdapterChip::new(execution_bus, program_bus, memory_controller.clone()),
BaseAluCoreChip::new(bitwise_lu_chip.clone(), BaseAluOpcode::default_offset()),
memory_controller.clone(),
);
inventory.add_executor(
base_alu_chip,
BaseAluOpcode::iter().map(VmOpcode::with_default_offset),
)?;
let lt_chip = Rv32LessThanChip::new(
Rv32BaseAluAdapterChip::new(execution_bus, program_bus, memory_controller.clone()),
LessThanCoreChip::new(bitwise_lu_chip.clone(), LessThanOpcode::default_offset()),
memory_controller.clone(),
);
inventory.add_executor(
lt_chip,
LessThanOpcode::iter().map(VmOpcode::with_default_offset),
)?;
let shift_chip = Rv32ShiftChip::new(
Rv32BaseAluAdapterChip::new(execution_bus, program_bus, memory_controller.clone()),
ShiftCoreChip::new(
bitwise_lu_chip.clone(),
range_checker.clone(),
ShiftOpcode::default_offset(),
),
memory_controller.clone(),
);
inventory.add_executor(
shift_chip,
ShiftOpcode::iter().map(VmOpcode::with_default_offset),
)?;
let load_store_chip = Rv32LoadStoreChip::new(
Rv32LoadStoreAdapterChip::new(
execution_bus,
program_bus,
memory_controller.clone(),
range_checker.clone(),
Rv32LoadStoreOpcode::default_offset(),
),
LoadStoreCoreChip::new(Rv32LoadStoreOpcode::default_offset()),
memory_controller.clone(),
);
inventory.add_executor(
load_store_chip,
Rv32LoadStoreOpcode::iter()
.take(Rv32LoadStoreOpcode::STOREB as usize + 1)
.map(VmOpcode::with_default_offset),
)?;
let load_sign_extend_chip = Rv32LoadSignExtendChip::new(
Rv32LoadStoreAdapterChip::new(
execution_bus,
program_bus,
memory_controller.clone(),
range_checker.clone(),
Rv32LoadStoreOpcode::default_offset(),
),
LoadSignExtendCoreChip::new(
range_checker.clone(),
Rv32LoadStoreOpcode::default_offset(),
),
memory_controller.clone(),
);
inventory.add_executor(
load_sign_extend_chip,
[Rv32LoadStoreOpcode::LOADB, Rv32LoadStoreOpcode::LOADH]
.map(VmOpcode::with_default_offset),
)?;
let beq_chip = Rv32BranchEqualChip::new(
Rv32BranchAdapterChip::new(execution_bus, program_bus, memory_controller.clone()),
BranchEqualCoreChip::new(BranchEqualOpcode::default_offset(), DEFAULT_PC_STEP),
memory_controller.clone(),
);
inventory.add_executor(
beq_chip,
BranchEqualOpcode::iter().map(VmOpcode::with_default_offset),
)?;
let blt_chip = Rv32BranchLessThanChip::new(
Rv32BranchAdapterChip::new(execution_bus, program_bus, memory_controller.clone()),
BranchLessThanCoreChip::new(
bitwise_lu_chip.clone(),
BranchLessThanOpcode::default_offset(),
),
memory_controller.clone(),
);
inventory.add_executor(
blt_chip,
BranchLessThanOpcode::iter().map(VmOpcode::with_default_offset),
)?;
let jal_lui_chip = Rv32JalLuiChip::new(
Rv32CondRdWriteAdapterChip::new(execution_bus, program_bus, memory_controller.clone()),
Rv32JalLuiCoreChip::new(bitwise_lu_chip.clone(), Rv32JalLuiOpcode::default_offset()),
memory_controller.clone(),
);
inventory.add_executor(
jal_lui_chip,
Rv32JalLuiOpcode::iter().map(VmOpcode::with_default_offset),
)?;
let jalr_chip = Rv32JalrChip::new(
Rv32JalrAdapterChip::new(execution_bus, program_bus, memory_controller.clone()),
Rv32JalrCoreChip::new(
bitwise_lu_chip.clone(),
range_checker.clone(),
Rv32JalrOpcode::default_offset(),
),
memory_controller.clone(),
);
inventory.add_executor(
jalr_chip,
Rv32JalrOpcode::iter().map(VmOpcode::with_default_offset),
)?;
let auipc_chip = Rv32AuipcChip::new(
Rv32RdWriteAdapterChip::new(execution_bus, program_bus, memory_controller.clone()),
Rv32AuipcCoreChip::new(bitwise_lu_chip.clone(), Rv32AuipcOpcode::default_offset()),
memory_controller.clone(),
);
inventory.add_executor(
auipc_chip,
Rv32AuipcOpcode::iter().map(VmOpcode::with_default_offset),
)?;
builder.add_phantom_sub_executor(
phantom::Rv32HintInputSubEx,
PhantomDiscriminant(Rv32Phantom::HintInput as u16),
)?;
builder.add_phantom_sub_executor(
phantom::Rv32PrintStrSubEx,
PhantomDiscriminant(Rv32Phantom::PrintStr as u16),
)?;
Ok(inventory)
}
}
impl<F: PrimeField32> VmExtension<F> for Rv32M {
type Executor = Rv32MExecutor<F>;
type Periphery = Rv32MPeriphery<F>;
fn build(
&self,
builder: &mut VmInventoryBuilder<F>,
) -> Result<VmInventory<Rv32MExecutor<F>, Rv32MPeriphery<F>>, VmInventoryError> {
let mut inventory = VmInventory::new();
let SystemPort {
execution_bus,
program_bus,
memory_controller,
} = builder.system_port();
let bitwise_lu_chip = if let Some(chip) = builder
.find_chip::<Arc<BitwiseOperationLookupChip<8>>>()
.first()
{
Arc::clone(chip)
} else {
let bitwise_lu_bus = BitwiseOperationLookupBus::new(builder.new_bus_idx());
let chip = Arc::new(BitwiseOperationLookupChip::new(bitwise_lu_bus));
inventory.add_periphery_chip(chip.clone());
chip
};
let range_tuple_checker = if let Some(chip) = builder
.find_chip::<Arc<RangeTupleCheckerChip<2>>>()
.into_iter()
.find(|c| {
c.bus().sizes[0] >= self.range_tuple_checker_sizes[0]
&& c.bus().sizes[1] >= self.range_tuple_checker_sizes[1]
}) {
chip.clone()
} else {
let range_tuple_bus =
RangeTupleCheckerBus::new(builder.new_bus_idx(), self.range_tuple_checker_sizes);
let chip = Arc::new(RangeTupleCheckerChip::new(range_tuple_bus));
inventory.add_periphery_chip(chip.clone());
chip
};
let mul_chip = Rv32MultiplicationChip::new(
Rv32MultAdapterChip::new(execution_bus, program_bus, memory_controller.clone()),
MultiplicationCoreChip::new(range_tuple_checker.clone(), MulOpcode::default_offset()),
memory_controller.clone(),
);
inventory.add_executor(
mul_chip,
MulOpcode::iter().map(VmOpcode::with_default_offset),
)?;
let mul_h_chip = Rv32MulHChip::new(
Rv32MultAdapterChip::new(execution_bus, program_bus, memory_controller.clone()),
MulHCoreChip::new(
bitwise_lu_chip.clone(),
range_tuple_checker.clone(),
MulHOpcode::default_offset(),
),
memory_controller.clone(),
);
inventory.add_executor(
mul_h_chip,
MulHOpcode::iter().map(VmOpcode::with_default_offset),
)?;
let div_rem_chip = Rv32DivRemChip::new(
Rv32MultAdapterChip::new(execution_bus, program_bus, memory_controller.clone()),
DivRemCoreChip::new(
bitwise_lu_chip.clone(),
range_tuple_checker.clone(),
DivRemOpcode::default_offset(),
),
memory_controller.clone(),
);
inventory.add_executor(
div_rem_chip,
DivRemOpcode::iter().map(VmOpcode::with_default_offset),
)?;
Ok(inventory)
}
}
impl<F: PrimeField32> VmExtension<F> for Rv32Io {
type Executor = Rv32IoExecutor<F>;
type Periphery = Rv32IoPeriphery<F>;
fn build(
&self,
builder: &mut VmInventoryBuilder<F>,
) -> Result<VmInventory<Self::Executor, Self::Periphery>, VmInventoryError> {
let mut inventory = VmInventory::new();
let SystemPort {
execution_bus,
program_bus,
memory_controller,
} = builder.system_port();
let range_checker = builder.system_base().range_checker_chip.clone();
let bitwise_lu_chip = if let Some(chip) = builder
.find_chip::<Arc<BitwiseOperationLookupChip<8>>>()
.first()
{
Arc::clone(chip)
} else {
let bitwise_lu_bus = BitwiseOperationLookupBus::new(builder.new_bus_idx());
let chip = Arc::new(BitwiseOperationLookupChip::new(bitwise_lu_bus));
inventory.add_periphery_chip(chip.clone());
chip
};
let mut hintstore_chip = Rv32HintStoreChip::new(
Rv32HintStoreAdapterChip::new(
execution_bus,
program_bus,
memory_controller.clone(),
range_checker.clone(),
),
Rv32HintStoreCoreChip::new(
bitwise_lu_chip.clone(),
Rv32HintStoreOpcode::default_offset(),
),
memory_controller.clone(),
);
hintstore_chip.core.set_streams(builder.streams().clone());
inventory.add_executor(
hintstore_chip,
Rv32HintStoreOpcode::iter().map(VmOpcode::with_default_offset),
)?;
Ok(inventory)
}
}
mod phantom {
use eyre::bail;
use openvm_circuit::{
arch::{PhantomSubExecutor, Streams},
system::memory::MemoryController,
};
use openvm_instructions::PhantomDiscriminant;
use openvm_stark_backend::p3_field::{Field, PrimeField32};
use crate::adapters::unsafe_read_rv32_register;
pub struct Rv32HintInputSubEx;
pub struct Rv32PrintStrSubEx;
impl<F: Field> PhantomSubExecutor<F> for Rv32HintInputSubEx {
fn phantom_execute(
&mut self,
_: &MemoryController<F>,
streams: &mut Streams<F>,
_: PhantomDiscriminant,
_: F,
_: F,
_: u16,
) -> eyre::Result<()> {
let mut hint = match streams.input_stream.pop_front() {
Some(hint) => hint,
None => {
bail!("EndOfInputStream");
}
};
streams.hint_stream.clear();
streams.hint_stream.extend(
(hint.len() as u32)
.to_le_bytes()
.iter()
.map(|b| F::from_canonical_u8(*b)),
);
let capacity = hint.len().div_ceil(4) * 4;
hint.resize(capacity, F::ZERO);
streams.hint_stream.extend(hint);
Ok(())
}
}
impl<F: PrimeField32> PhantomSubExecutor<F> for Rv32PrintStrSubEx {
fn phantom_execute(
&mut self,
memory: &MemoryController<F>,
_: &mut Streams<F>,
_: PhantomDiscriminant,
a: F,
b: F,
_: u16,
) -> eyre::Result<()> {
let rd = unsafe_read_rv32_register(memory, a);
let rs1 = unsafe_read_rv32_register(memory, b);
let bytes = (0..rs1)
.map(|i| -> eyre::Result<u8> {
let val = memory.unsafe_read_cell(F::TWO, F::from_canonical_u32(rd + i));
let byte: u8 = val.as_canonical_u32().try_into()?;
Ok(byte)
})
.collect::<eyre::Result<Vec<u8>>>()?;
let peeked_str = String::from_utf8(bytes)?;
print!("{peeked_str}");
Ok(())
}
}
}