diff --git a/Cargo.lock b/Cargo.lock index f5a5535397..543789b83d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4822,9 +4822,11 @@ dependencies = [ "openvm-native-circuit", "openvm-native-compiler", "openvm-native-recursion", + "openvm-native-transpiler", "openvm-pairing-circuit", "openvm-pairing-transpiler", "openvm-rv32im-circuit", + "openvm-rv32im-guest", "openvm-rv32im-transpiler", "openvm-sha256-circuit", "openvm-sha256-transpiler", diff --git a/Cargo.toml b/Cargo.toml index 3948dfc709..a786fe28a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -140,6 +140,7 @@ openvm-native-circuit = { path = "extensions/native/circuit", default-features = openvm-native-compiler = { path = "extensions/native/compiler", default-features = false } openvm-native-compiler-derive = { path = "extensions/native/compiler/derive", default-features = false } openvm-native-recursion = { path = "extensions/native/recursion", default-features = false } +openvm-native-transpiler = { path = "extensions/native/transpiler", default-features = false } openvm-keccak256-circuit = { path = "extensions/keccak256/circuit", default-features = false } openvm-keccak256-transpiler = { path = "extensions/keccak256/transpiler", default-features = false } openvm-keccak256-guest = { path = "extensions/keccak256/guest", default-features = false } diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml index e0727b6202..3996ef8c50 100644 --- a/crates/sdk/Cargo.toml +++ b/crates/sdk/Cargo.toml @@ -25,6 +25,7 @@ openvm-pairing-transpiler = { workspace = true } openvm-native-circuit = { workspace = true } openvm-native-compiler = { workspace = true } openvm-native-recursion = { workspace = true, features = ["static-verifier"] } +openvm-native-transpiler = { workspace = true } openvm-rv32im-circuit = { workspace = true } openvm-rv32im-transpiler = { workspace = true } openvm-transpiler = { workspace = true } @@ -57,6 +58,10 @@ hex.workspace = true forge-fmt = { workspace = true, optional = true } rrs-lib = { workspace = true } +[dev-dependencies] +openvm-rv32im-guest.workspace = true + + [features] default = ["parallel", "jemalloc", "evm-verify"] evm-prove = ["openvm-native-recursion/evm-prove"] diff --git a/crates/sdk/examples/sdk_app.rs b/crates/sdk/examples/sdk_app.rs index fb78a01a3a..c911a04ecc 100644 --- a/crates/sdk/examples/sdk_app.rs +++ b/crates/sdk/examples/sdk_app.rs @@ -46,7 +46,7 @@ fn main() -> Result<(), Box> { /// use std::path::PathBuf; /// /// let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).to_path_buf(); - /// path.push("guest"); + /// path.push("guest/fib"); /// let target_path = path.to_str().unwrap(); /// ``` // ANCHOR: build diff --git a/crates/sdk/examples/sdk_evm.rs b/crates/sdk/examples/sdk_evm.rs index 7126870550..b7103c4d01 100644 --- a/crates/sdk/examples/sdk_evm.rs +++ b/crates/sdk/examples/sdk_evm.rs @@ -46,7 +46,7 @@ fn main() -> Result<(), Box> { /// use std::path::PathBuf; /// /// let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).to_path_buf(); - /// path.push("guest"); + /// path.push("guest/fib"); /// let target_path = path.to_str().unwrap(); /// ``` // ANCHOR: build diff --git a/crates/sdk/guest/Cargo.toml b/crates/sdk/guest/fib/Cargo.toml similarity index 68% rename from crates/sdk/guest/Cargo.toml rename to crates/sdk/guest/fib/Cargo.toml index 1ca1917784..8bb44b44b6 100644 --- a/crates/sdk/guest/Cargo.toml +++ b/crates/sdk/guest/fib/Cargo.toml @@ -5,4 +5,4 @@ version = "0.0.0" edition = "2021" [dependencies] -openvm = { path = "../../toolchain/openvm" } +openvm = { path = "../../../toolchain/openvm" } diff --git a/crates/sdk/guest/src/main.rs b/crates/sdk/guest/fib/src/main.rs similarity index 100% rename from crates/sdk/guest/src/main.rs rename to crates/sdk/guest/fib/src/main.rs diff --git a/crates/sdk/guest/verify_openvm_stark/.gitignore b/crates/sdk/guest/verify_openvm_stark/.gitignore new file mode 100644 index 0000000000..0e52c45d5d --- /dev/null +++ b/crates/sdk/guest/verify_openvm_stark/.gitignore @@ -0,0 +1,4 @@ +*.asm +Cargo.lock +target/ +openvm/ \ No newline at end of file diff --git a/crates/sdk/guest/verify_openvm_stark/Cargo.toml b/crates/sdk/guest/verify_openvm_stark/Cargo.toml new file mode 100644 index 0000000000..f788fc95b1 --- /dev/null +++ b/crates/sdk/guest/verify_openvm_stark/Cargo.toml @@ -0,0 +1,18 @@ +[workspace] +[package] +name = "openvm-verify-stark-program" +version = "0.0.0" +edition = "2021" + +[dependencies] +openvm = { path = "../../../toolchain/openvm", features = ["std"] } +hex-literal = { version = "0.4.1", default-features = false } +bytemuck = { version = "1.20.0", features = ["extern_crate_alloc"] } + +[features] +default = [] + +[profile.profiling] +inherits = "release" +debug = 2 +strip = false diff --git a/crates/sdk/guest/verify_openvm_stark/src/main.rs b/crates/sdk/guest/verify_openvm_stark/src/main.rs new file mode 100644 index 0000000000..bb9011f328 --- /dev/null +++ b/crates/sdk/guest/verify_openvm_stark/src/main.rs @@ -0,0 +1,24 @@ +extern crate alloc; +use alloc::vec::Vec; + +use openvm::{define_verify_openvm_stark, io::read}; + +define_verify_openvm_stark!( + verify_openvm_stark, + env!("CARGO_MANIFEST_DIR"), + "root_verifier.asm" +); + +// const APP_EXE_COMMIT: [u32; 8] = [ +// 343014587, 230645511, 1447462186, 773379336, 1182270030, 1497892484, 461820702, 353704350, +// ]; +// const APP_VM_COMMIT: [u32; 8] = [ +// 445134834, 1133596793, 530952192, 425228715, 1806903712, 1362083369, 295028151, 482389308, +// ]; + +pub fn main() { + let app_exe_commit: [u32; 8] = read(); + let app_vm_commit: [u32; 8] = read(); + let pvs: Vec = read(); + verify_openvm_stark(&app_exe_commit, &app_vm_commit, &pvs); +} diff --git a/crates/sdk/src/config/global.rs b/crates/sdk/src/config/global.rs index 532c9b8d1c..a76a31aa2b 100644 --- a/crates/sdk/src/config/global.rs +++ b/crates/sdk/src/config/global.rs @@ -24,6 +24,7 @@ use openvm_native_circuit::{ CastFExtension, CastFExtensionExecutor, CastFExtensionPeriphery, Native, NativeExecutor, NativePeriphery, }; +use openvm_native_transpiler::LongFormTranspilerExtension; use openvm_pairing_circuit::{ PairingExtension, PairingExtensionExecutor, PairingExtensionPeriphery, }; @@ -138,6 +139,9 @@ impl SdkVmConfig { if self.sha256.is_some() { transpiler = transpiler.with_extension(Sha256TranspilerExtension); } + if self.native.is_some() { + transpiler = transpiler.with_extension(LongFormTranspilerExtension); + } if self.rv32m.is_some() { transpiler = transpiler.with_extension(Rv32MTranspilerExtension); } diff --git a/crates/sdk/src/stdin.rs b/crates/sdk/src/stdin.rs index eaa7bebeef..9101e8d4de 100644 --- a/crates/sdk/src/stdin.rs +++ b/crates/sdk/src/stdin.rs @@ -1,4 +1,7 @@ -use std::collections::VecDeque; +use std::{ + collections::{HashMap, VecDeque}, + sync::Arc, +}; use openvm_circuit::arch::Streams; use openvm_stark_backend::p3_field::FieldAlgebra; @@ -9,6 +12,7 @@ use crate::F; #[derive(Clone, Default, Serialize, Deserialize)] pub struct StdIn { pub buffer: VecDeque>, + pub kv_store: HashMap, Vec>, } impl StdIn { @@ -36,6 +40,9 @@ impl StdIn { pub fn write_field(&mut self, data: &[F]) { self.buffer.push_back(data.to_vec()); } + pub fn add_key_value(&mut self, key: Vec, value: Vec) { + self.kv_store.insert(key, value); + } } impl From for Streams { @@ -44,7 +51,9 @@ impl From for Streams { while let Some(input) = std_in.read() { data.push(input); } - Streams::new(data) + let mut ret = Streams::new(data); + ret.kv_store = Arc::new(std_in.kv_store); + ret } } diff --git a/crates/sdk/tests/integration_test.rs b/crates/sdk/tests/integration_test.rs index 5d4e2fe5b6..dbd3f9786e 100644 --- a/crates/sdk/tests/integration_test.rs +++ b/crates/sdk/tests/integration_test.rs @@ -6,6 +6,7 @@ use openvm_circuit::{ arch::{ hasher::poseidon2::vm_poseidon2_hasher, ContinuationVmProof, ExecutionError, GenerationError, SingleSegmentVmExecutor, SystemConfig, VmConfig, VmExecutor, + DEFAULT_MAX_NUM_PUBLIC_VALUES, }, system::{memory::tree::public_values::UserPublicValuesProof, program::trace::VmCommittedExe}, }; @@ -27,9 +28,11 @@ use openvm_native_recursion::{ wrapper::Halo2WrapperProvingKey, RawEvmProof, }, + hints::Hintable, types::InnerConfig, vars::StarkProofVariable, }; +use openvm_rv32im_guest::hint_load_by_key_encode; use openvm_rv32im_transpiler::{ Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, }; @@ -41,7 +44,9 @@ use openvm_sdk::{ types::{EvmHalo2Verifier, EvmProof}, DefaultStaticVerifierPvHandler, Sdk, StdIn, }; -use openvm_stark_backend::{keygen::types::LinearConstraint, p3_matrix::Matrix}; +use openvm_stark_backend::{ + keygen::types::LinearConstraint, p3_field::PrimeField32, p3_matrix::Matrix, +}; use openvm_stark_sdk::{ config::{ baby_bear_poseidon2::{BabyBearPoseidon2Config, BabyBearPoseidon2Engine}, @@ -393,7 +398,7 @@ fn test_static_verifier_custom_pv_handler() { #[test] fn test_e2e_proof_generation_and_verification_with_pvs() { let mut pkg_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).to_path_buf(); - pkg_dir.push("guest"); + pkg_dir.push("guest/fib"); let vm_config = SdkVmConfig::builder() .system(SdkSystemConfig { @@ -463,7 +468,7 @@ fn test_sdk_guest_build_and_transpile() { // .with_options(vec!["--release"]); ; let mut pkg_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).to_path_buf(); - pkg_dir.push("guest"); + pkg_dir.push("guest/fib"); let one = sdk .build(guest_opts.clone(), &pkg_dir, &Default::default()) .unwrap(); @@ -484,7 +489,7 @@ fn test_inner_proof_codec_roundtrip() -> eyre::Result<()> { // generate a proof let sdk = Sdk::new(); let mut pkg_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).to_path_buf(); - pkg_dir.push("guest"); + pkg_dir.push("guest/fib"); let elf = sdk.build(Default::default(), pkg_dir, &Default::default())?; let vm_config = SdkVmConfig::builder() @@ -574,11 +579,109 @@ fn test_segmentation_retry() { .sum(); assert!(new_total_height < total_height); } - #[test] -fn test_root_verifier_asm_generate() { - let agg_stark_config = agg_stark_config_for_test(); - let agg_pk = AggStarkProvingKey::keygen(agg_stark_config); +fn test_verify_openvm_stark_e2e() -> Result<()> { + const ASM_FILENAME: &str = "root_verifier.asm"; let sdk = Sdk::new(); - sdk.generate_root_verifier_asm(&agg_pk); + let guest_opts = GuestOptions::default(); + let mut pkg_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).to_path_buf(); + pkg_dir.push("guest/fib"); + let elf = sdk.build(guest_opts.clone(), &pkg_dir, &Default::default())?; + + let vm_config = SdkVmConfig::builder() + .system(SdkSystemConfig { + config: SystemConfig::default().with_continuations(), + }) + .rv32i(Default::default()) + .rv32m(Default::default()) + .io(Default::default()) + .native(Default::default()) + .build(); + assert!(vm_config.system.config.continuation_enabled); + + let app_exe = sdk.transpile(elf, vm_config.transpiler())?; + let fri_params = FriParameters::new_for_testing(LEAF_LOG_BLOWUP); + let app_config = AppConfig::new_with_leaf_fri_params(fri_params, vm_config.clone(), fri_params); + + let app_pk = sdk.app_keygen(app_config.clone())?; + let committed_app_exe = sdk.commit_app_exe(fri_params, app_exe.clone())?; + + let commits = + AppExecutionCommit::compute(&vm_config, &committed_app_exe, &app_pk.leaf_committed_exe); + + let agg_pk = AggStarkProvingKey::keygen(AggStarkConfig { + max_num_user_public_values: DEFAULT_MAX_NUM_PUBLIC_VALUES, + leaf_fri_params: FriParameters::new_for_testing(LEAF_LOG_BLOWUP), + internal_fri_params: FriParameters::new_for_testing(INTERNAL_LOG_BLOWUP), + root_fri_params: FriParameters::new_for_testing(ROOT_LOG_BLOWUP), + profiling: false, + compiler_options: CompilerOptions { + enable_cycle_tracker: true, + ..Default::default() + }, + root_max_constraint_degree: (1 << ROOT_LOG_BLOWUP) + 1, + }); + let asm = sdk.generate_root_verifier_asm(&agg_pk); + let asm_path = format!( + "{}/guest/verify_openvm_stark/{}", + env!("CARGO_MANIFEST_DIR"), + ASM_FILENAME + ); + std::fs::write(asm_path, asm)?; + + let e2e_stark_proof = sdk.generate_e2e_stark_proof( + Arc::new(app_pk), + committed_app_exe, + agg_pk, + StdIn::default(), + )?; + + let verify_exe = { + let mut pkg_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).to_path_buf(); + pkg_dir.push("guest/verify_openvm_stark"); + let elf = sdk.build(guest_opts.clone(), &pkg_dir, &Default::default())?; + sdk.transpile(elf, vm_config.transpiler())? + }; + + let pvs = [13u32, 21, 0, 0, 0, 0, 0, 0]; + + let exe_commit_u32: Vec<_> = commits + .exe_commit + .iter() + .map(|x| x.as_canonical_u32()) + .collect(); + let vm_commit_u32: Vec<_> = commits + .leaf_vm_verifier_commit + .iter() + .map(|x| x.as_canonical_u32()) + .collect(); + let pvs_u32: Vec<_> = pvs + .iter() + .flat_map(|x| x.to_le_bytes()) + .map(|x| x as u32) + .collect(); + + let key = ASM_FILENAME + .as_bytes() + .iter() + .cloned() + .chain(exe_commit_u32.iter().flat_map(|x| x.to_le_bytes())) + .chain(vm_commit_u32.iter().flat_map(|x| x.to_le_bytes())) + .chain(pvs_u32.iter().flat_map(|x| x.to_le_bytes())) + .collect(); + let mut stdin = StdIn::default(); + let to_encode: Vec> = e2e_stark_proof.proof.write(); + let value = hint_load_by_key_encode(&to_encode); + stdin.add_key_value(key, value); + + let exe_commit_u32_8: [u32; 8] = exe_commit_u32.try_into().unwrap(); + let vm_commit_u32_8: [u32; 8] = vm_commit_u32.try_into().unwrap(); + + stdin.write(&exe_commit_u32_8); + stdin.write(&vm_commit_u32_8); + stdin.write(&pvs_u32); + + sdk.execute(verify_exe, vm_config, stdin)?; + + Ok(()) } diff --git a/crates/toolchain/openvm/src/io/mod.rs b/crates/toolchain/openvm/src/io/mod.rs index 1bde9e886f..eb00a9d3cd 100644 --- a/crates/toolchain/openvm/src/io/mod.rs +++ b/crates/toolchain/openvm/src/io/mod.rs @@ -126,6 +126,16 @@ pub fn reveal_u32(x: u32, index: usize) { println!("reveal {} at byte location {}", x, index * 4); } +/// Store u32 `x` to the native address `native_addr` as 4 field element in byte. +#[allow(unused_variables)] +#[inline(always)] +pub fn store_u32_to_native(native_addr: u32, x: u32) { + #[cfg(target_os = "zkvm")] + openvm_rv32im_guest::store_to_native!(native_addr, x); + #[cfg(not(target_os = "zkvm"))] + panic!("store_to_native_u32 cannot run on non-zkVM platforms"); +} + /// A no-alloc writer to print to stdout on host machine for debugging purposes. pub struct Writer; diff --git a/crates/toolchain/openvm/src/lib.rs b/crates/toolchain/openvm/src/lib.rs index 134d10b559..606d68da46 100644 --- a/crates/toolchain/openvm/src/lib.rs +++ b/crates/toolchain/openvm/src/lib.rs @@ -29,6 +29,8 @@ pub mod utils; #[cfg(not(target_os = "zkvm"))] pub mod host; +#[cfg(target_os = "zkvm")] +pub mod verify_stark; #[cfg(target_os = "zkvm")] core::arch::global_asm!(include_str!("memset.s")); diff --git a/crates/toolchain/openvm/src/verify_stark.rs b/crates/toolchain/openvm/src/verify_stark.rs new file mode 100644 index 0000000000..1382dcd9d5 --- /dev/null +++ b/crates/toolchain/openvm/src/verify_stark.rs @@ -0,0 +1,56 @@ +/// Define a function that verifies an OpenVM Stark proof. +/// To define this function, users need to specify the function name and an ASM file containing the +/// assembly code for the verification(this ASM file can be generated by +/// `Sdk.generate_root_verifier_asm` function). To specify the ASM file, users need to provide the +/// parent folder and filename of the ASM file. +/// To call this function: +/// 1. users need to provide `app_exe_commit`/`app_vm_commit`/`user_pvs`(user public values) as the +/// arguments. CAREFUL: `app_exe_commit`/`app_vm_commit`/`user_pvs` are in u32 and are interpreted +/// as native fields. `user_pvs` are the exact public values of that proof. Which means, if a guest +/// program calls `reveal_u32` to publish an u32(e.g. `258`) in the proof, that u32 is decomposed +/// into 4 field elements in byte. In `user_pvs`, it should be an u32 array, `[2, 1, 0, 0]`. +/// 2. Provide the corresponding stark proof in the key-value store in OpenVM streams. The key +/// should +/// be the concatenation of the filename, `app_exe_commit`, `app_vm_commit`, and `user_pvs` in +/// little-endian bytes. +#[macro_export] +macro_rules! define_verify_openvm_stark { + ($fn_name: ident, $asm_folder: expr, $asm_filename: literal) => { + pub fn $fn_name(app_exe_commit: &[u32; 8], app_vm_commit: &[u32; 8], user_pvs: &[u32]) { + // The memory location for the start of the heap. + const HEAP_START_ADDRESS: u32 = 1 << 24; + const FIELDS_PER_U32: u32 = 4; + const FILENAME: &str = $asm_filename; + // Construct the hint key + let hint_key: alloc::vec::Vec = FILENAME + .as_bytes() + .iter() + .cloned() + .chain(app_exe_commit.iter().flat_map(|x| x.to_le_bytes())) + .chain(app_vm_commit.iter().flat_map(|x| x.to_le_bytes())) + .chain(user_pvs.iter().flat_map(|x| x.to_le_bytes())) + .collect(); + openvm::io::hint_load_by_key(&hint_key); + // Store the expected public values into the beginning of the native heap. + let mut native_addr = HEAP_START_ADDRESS; + for &x in app_exe_commit { + openvm::io::store_u32_to_native(native_addr, x); + native_addr += FIELDS_PER_U32; + } + for &x in app_vm_commit { + openvm::io::store_u32_to_native(native_addr, x); + native_addr += FIELDS_PER_U32; + } + for &x in user_pvs { + openvm::io::store_u32_to_native(native_addr, x); + native_addr += FIELDS_PER_U32; + } + // Assumption: the asm file should be generated by SDK. The code block should: + // 1. Increase the heap pointer in order to avoid overwriting expected public values. + // 2. Hint a stark proof from the input stream + // 3. Verify the proof + // 4. Compare the public values with the expected ones. Panic if not equal. + unsafe { core::arch::asm!(include_str!(concat!($asm_folder, "/", $asm_filename)),) } + } + }; +} diff --git a/docs/specs/RISCV.md b/docs/specs/RISCV.md index 7948272c66..eba8186bbe 100644 --- a/docs/specs/RISCV.md +++ b/docs/specs/RISCV.md @@ -72,6 +72,10 @@ the guest must take care to validate all data and account for behavior in cases | printstr | I | 0001011 | 011 | 0x1 | Tries to convert `[rd..rd + rs1]_2` to UTF-8 string and print to host stdout. Will print error message if conversion fails. | | hintrandom | I | 0001011 | 011 | 0x2 | Resets the hint stream to `4 * rd` random bytes from `rand::rngs::OsRng` on the host. | +| RISC-V Inst | FMT | opcode[6:0] | funct3 | funct7 | RISC-V description and notes | +|--------------|-----|-------------|---------|--------|------------------------------------------------------------------------------------------------------------------------------| +| nativestorew | R | 0001011 | 111 | 0x2 | Stores the 4-byte word `rs1` at address `rd` in native address space. The address `rd` must be aligned to a 4-byte boundary. | + ## Keccak Extension | RISC-V Inst | FMT | opcode[6:0] | funct3 | funct7 | RISC-V description and notes | diff --git a/docs/specs/isa-table.md b/docs/specs/isa-table.md index 940d90ed0e..7b7f374065 100644 --- a/docs/specs/isa-table.md +++ b/docs/specs/isa-table.md @@ -31,47 +31,48 @@ In the tables below, we provide the mapping between the `LocalOpcode` and `Phant #### Instructions -| VM Extension | `LocalOpcode` | ISA Instruction | -| ------------- | ---------- | ------------- | -| RV32IM | `BaseAluOpcode::ADD` | ADD_RV32 | -| RV32IM | `BaseAluOpcode::SUB` | SUB_RV32 | -| RV32IM | `BaseAluOpcode::XOR` | XOR_RV32 | -| RV32IM | `BaseAluOpcode::OR` | OR_RV32 | -| RV32IM | `BaseAluOpcode::AND` | AND_RV32 | -| RV32IM | `ShiftOpcode::SLL` | SLL_RV32 | -| RV32IM | `ShiftOpcode::SRL` | SRL_RV32 | -| RV32IM | `ShiftOpcode::SRA` | SRA_RV32 | -| RV32IM | `LessThanOpcode::SLT` | SLT_RV32 | -| RV32IM | `LessThanOpcode::SLTU` | SLTU_RV32 | -| RV32IM | `Rv32LoadStoreOpcode::LOADB` | LOADB_RV32 | -| RV32IM | `Rv32LoadStoreOpcode::LOADH` | LOADH_RV32 | -| RV32IM | `Rv32LoadStoreOpcode::LOADW` | LOADW_RV32 | -| RV32IM | `Rv32LoadStoreOpcode::LOADBU` | LOADBU_RV32 | -| RV32IM | `Rv32LoadStoreOpcode::LOADHU` | LOADHU_RV32 | -| RV32IM | `Rv32LoadStoreOpcode::STOREB` | STOREB_RV32 | -| RV32IM | `Rv32LoadStoreOpcode::STOREH` | STOREH_RV32 | -| RV32IM | `Rv32LoadStoreOpcode::STOREW` | STOREW_RV32 | -| RV32IM | `BranchEqualOpcode::BEQ` | BEQ_RV32 | -| RV32IM | `BranchEqualOpcode::BNE` | BNE_RV32 | -| RV32IM | `BranchLessThanOpcode::BLT` | BLT_RV32 | -| RV32IM | `BranchLessThanOpcode::BGE` | BGE_RV32 | -| RV32IM | `BranchLessThanOpcode::BLTU` | BLTU_RV32 | -| RV32IM | `BranchLessThanOpcode::BGEU` | BGEU_RV32 | -| RV32IM | `Rv32JalLuiOpcode::JAL` | JAL_RV32 | -| RV32IM | `Rv32JalrOpcode::JALR` | JALR_RV32 | -| RV32IM | `Rv32JalLuiOpcode::LUI` | LUI_RV32 | -| RV32IM | `Rv32AuipcOpcode::AUIPC` | AUIPC_RV32 | -| RV32IM | `MulOpcode::MUL` | MUL_RV32 | -| RV32IM | `MulHOpcode::MULH` | MULH_RV32 | -| RV32IM | `MulHOpcode::MULHSU` | MULHSU_RV32 | -| RV32IM | `MulHOpcode::MULHU` | MULHU_RV32 | -| RV32IM | `DivRemOpcode::DIV` | DIV_RV32 | -| RV32IM | `DivRemOpcode::DIVU` | DIVU_RV32 | -| RV32IM | `DivRemOpcode::REM` | REM_RV32 | -| RV32IM | `DivRemOpcode::REMU` | REMU_RV32 | +| VM Extension | `LocalOpcode` | ISA Instruction | +| ------------- | ---------- |------------------| +| RV32IM | `BaseAluOpcode::ADD` | ADD_RV32 | +| RV32IM | `BaseAluOpcode::SUB` | SUB_RV32 | +| RV32IM | `BaseAluOpcode::XOR` | XOR_RV32 | +| RV32IM | `BaseAluOpcode::OR` | OR_RV32 | +| RV32IM | `BaseAluOpcode::AND` | AND_RV32 | +| RV32IM | `ShiftOpcode::SLL` | SLL_RV32 | +| RV32IM | `ShiftOpcode::SRL` | SRL_RV32 | +| RV32IM | `ShiftOpcode::SRA` | SRA_RV32 | +| RV32IM | `LessThanOpcode::SLT` | SLT_RV32 | +| RV32IM | `LessThanOpcode::SLTU` | SLTU_RV32 | +| RV32IM | `Rv32LoadStoreOpcode::LOADB` | LOADB_RV32 | +| RV32IM | `Rv32LoadStoreOpcode::LOADH` | LOADH_RV32 | +| RV32IM | `Rv32LoadStoreOpcode::LOADW` | LOADW_RV32 | +| RV32IM | `Rv32LoadStoreOpcode::LOADBU` | LOADBU_RV32 | +| RV32IM | `Rv32LoadStoreOpcode::LOADHU` | LOADHU_RV32 | +| RV32IM | `Rv32LoadStoreOpcode::STOREB` | STOREB_RV32 | +| RV32IM | `Rv32LoadStoreOpcode::STOREH` | STOREH_RV32 | +| RV32IM | `Rv32LoadStoreOpcode::STOREW` | STOREW_RV32 | +| RV32IM | `BranchEqualOpcode::BEQ` | BEQ_RV32 | +| RV32IM | `BranchEqualOpcode::BNE` | BNE_RV32 | +| RV32IM | `BranchLessThanOpcode::BLT` | BLT_RV32 | +| RV32IM | `BranchLessThanOpcode::BGE` | BGE_RV32 | +| RV32IM | `BranchLessThanOpcode::BLTU` | BLTU_RV32 | +| RV32IM | `BranchLessThanOpcode::BGEU` | BGEU_RV32 | +| RV32IM | `Rv32JalLuiOpcode::JAL` | JAL_RV32 | +| RV32IM | `Rv32JalrOpcode::JALR` | JALR_RV32 | +| RV32IM | `Rv32JalLuiOpcode::LUI` | LUI_RV32 | +| RV32IM | `Rv32AuipcOpcode::AUIPC` | AUIPC_RV32 | +| RV32IM | `MulOpcode::MUL` | MUL_RV32 | +| RV32IM | `MulHOpcode::MULH` | MULH_RV32 | +| RV32IM | `MulHOpcode::MULHSU` | MULHSU_RV32 | +| RV32IM | `MulHOpcode::MULHU` | MULHU_RV32 | +| RV32IM | `DivRemOpcode::DIV` | DIV_RV32 | +| RV32IM | `DivRemOpcode::DIVU` | DIVU_RV32 | +| RV32IM | `DivRemOpcode::REM` | REM_RV32 | +| RV32IM | `DivRemOpcode::REMU` | REMU_RV32 | | RV32IM | `Rv32HintStoreOpcode::HINT_STOREW` | HINT_STOREW_RV32 | | RV32IM | `Rv32HintStoreOpcode::HINT_BUFFER` | HINT_BUFFER_RV32 | -| RV32IM | Pseudo-instruction for `STOREW_RV32` | REVEAL_RV32 | +| RV32IM | Pseudo-instruction for `STOREW_RV32` | REVEAL_RV32 | +| RV32IM | Pseudo-instruction for `STOREW_RV32` | NATIVE_STOREW | | #### Phantom Sub-Instructions diff --git a/extensions/rv32im/guest/src/io.rs b/extensions/rv32im/guest/src/io.rs index ee123c0577..664b9b1117 100644 --- a/extensions/rv32im/guest/src/io.rs +++ b/extensions/rv32im/guest/src/io.rs @@ -81,6 +81,21 @@ macro_rules! reveal { }; } +/// Store rs1 to [[rd]]_4. +#[macro_export] +macro_rules! store_to_native { + ($rd:ident, $rs1:ident) => { + openvm_custom_insn::custom_insn_r!( + opcode = openvm_rv32im_guest::SYSTEM_OPCODE, + funct3 = openvm_rv32im_guest::NATIVE_STOREW_FUNCT3, + funct7 = openvm_rv32im_guest::NATIVE_STOREW_FUNCT7, + rd = In $rd, + rs1 = In $rs1, + rs2 = In $rs1, + ) + }; +} + /// Print UTF-8 string encoded as bytes to host stdout for debugging purposes. #[inline(always)] pub fn print_str_from_bytes(str_as_bytes: &[u8]) { diff --git a/extensions/rv32im/guest/src/lib.rs b/extensions/rv32im/guest/src/lib.rs index 5a6b7087e4..99f1a6f97f 100644 --- a/extensions/rv32im/guest/src/lib.rs +++ b/extensions/rv32im/guest/src/lib.rs @@ -14,6 +14,8 @@ pub const SYSTEM_OPCODE: u8 = 0x0b; pub const CSR_OPCODE: u8 = 0b1110011; pub const RV32_ALU_OPCODE: u8 = 0b0110011; pub const RV32M_FUNCT7: u8 = 0x01; +pub const NATIVE_STOREW_FUNCT3: u8 = 0b111; +pub const NATIVE_STOREW_FUNCT7: u32 = 2; pub const TERMINATE_FUNCT3: u8 = 0b000; pub const HINT_FUNCT3: u8 = 0b001; diff --git a/extensions/rv32im/transpiler/src/lib.rs b/extensions/rv32im/transpiler/src/lib.rs index 42b5f9d2e2..abd4413022 100644 --- a/extensions/rv32im/transpiler/src/lib.rs +++ b/extensions/rv32im/transpiler/src/lib.rs @@ -6,7 +6,8 @@ use openvm_instructions::{ }; use openvm_rv32im_guest::{ PhantomImm, CSRRW_FUNCT3, CSR_OPCODE, HINT_BUFFER_IMM, HINT_FUNCT3, HINT_STOREW_IMM, - PHANTOM_FUNCT3, REVEAL_FUNCT3, RV32M_FUNCT7, RV32_ALU_OPCODE, SYSTEM_OPCODE, TERMINATE_FUNCT3, + NATIVE_STOREW_FUNCT3, NATIVE_STOREW_FUNCT7, PHANTOM_FUNCT3, REVEAL_FUNCT3, RV32M_FUNCT7, + RV32_ALU_OPCODE, SYSTEM_OPCODE, TERMINATE_FUNCT3, }; use openvm_stark_backend::p3_field::PrimeField32; use openvm_transpiler::{ @@ -155,9 +156,6 @@ impl TranspilerExtension for Rv32IoTranspilerExtension { if opcode != SYSTEM_OPCODE { return None; } - if funct3 != HINT_FUNCT3 && funct3 != REVEAL_FUNCT3 { - return None; - } let instruction = match funct3 { HINT_FUNCT3 => { @@ -198,6 +196,23 @@ impl TranspilerExtension for Rv32IoTranspilerExtension { (dec_insn.imm < 0) as isize, )) } + NATIVE_STOREW_FUNCT3 => { + // NATIVE_STOREW is a pseudo-instruction for STOREW_RV32 a,b,0,1,4 + let dec_insn = RType::new(instruction_u32); + if dec_insn.funct7 != NATIVE_STOREW_FUNCT7 { + return None; + } + Some(Instruction::large_from_isize( + Rv32LoadStoreOpcode::STOREW.global_opcode(), + (RV32_REGISTER_NUM_LIMBS * dec_insn.rs1) as isize, + (RV32_REGISTER_NUM_LIMBS * dec_insn.rd) as isize, + 0, + 1, + 4, + 1, + 0, + )) + } _ => return None, };