diff --git a/crates/compile-utils/src/lib.rs b/crates/compile-utils/src/lib.rs index 19bf736..2d9e720 100644 --- a/crates/compile-utils/src/lib.rs +++ b/crates/compile-utils/src/lib.rs @@ -3,5 +3,8 @@ mod rust; pub use { error::CommonError, - rust::{CargoBuildCmd, cargo_metadata, rustc_path, rustup_add_components, rustup_add_rust_src}, + rust::{ + CargoBuildCmd, RustTarget, cargo_metadata, rustc_path, rustup_add_components, + rustup_add_rust_src, + }, }; diff --git a/crates/compile-utils/src/rust.rs b/crates/compile-utils/src/rust.rs index e2c4483..3f49df4 100644 --- a/crates/compile-utils/src/rust.rs +++ b/crates/compile-utils/src/rust.rs @@ -10,6 +10,36 @@ use tempfile::tempdir; const CARGO_ENCODED_RUSTFLAGS_SEPARATOR: &str = "\x1f"; +/// Target specification for cargo build. +#[derive(Debug, Clone, Copy)] +pub enum RustTarget { + /// Built-in target name (e.g., "riscv64im-unknown-none-elf"). + Name(&'static str), + /// Custom target specification JSON content. + SpecJson { + /// Target name (e.g., "riscv64ima-unknown-none-elf"). + name: &'static str, + /// Raw JSON content of the target specification. + json: &'static str, + }, +} + +impl RustTarget { + /// Returns the target name. + pub const fn name(&self) -> &'static str { + match self { + Self::Name(name) => name, + Self::SpecJson { name, .. } => name, + } + } +} + +impl From<&'static str> for RustTarget { + fn from(name: &'static str) -> Self { + Self::Name(name) + } +} + /// A builder for configuring `cargo build` invocation. #[derive(Clone)] pub struct CargoBuildCmd { @@ -73,12 +103,12 @@ impl CargoBuildCmd { self } - /// Takes the path to the manifest directory and the target triple, then + /// Takes the path to the manifest directory and the target, then /// runs configured `cargo build` and returns built ELF. pub fn exec( &self, manifest_dir: impl AsRef, - target: impl AsRef, + target: impl Into, ) -> Result, CommonError> { let metadata = cargo_metadata(manifest_dir.as_ref())?; let package = metadata.root_package().unwrap(); @@ -103,6 +133,18 @@ impl CargoBuildCmd { })?; } + let target = target.into(); + let target_arg = match target { + RustTarget::Name(name) => name.to_string(), + RustTarget::SpecJson { name, json } => { + let json_name = format!("{name}.json"); + let json_path = tempdir.path().join(&json_name); + fs::write(&json_path, json.as_bytes()) + .map_err(|err| CommonError::write_file(json_name, &json_path, err))?; + json_path.to_string_lossy().to_string() + } + }; + let encoded_rustflags = iter::empty() .chain(self.rustflags.iter().cloned()) .chain( @@ -120,7 +162,7 @@ impl CargoBuildCmd { .chain(["build".into()]) .chain(self.build_options.iter().cloned()) .chain(["--profile".into(), self.profile.clone()]) - .chain(["--target".into(), target.as_ref().into()]) + .chain(["--target".into(), target_arg]) .chain(["--manifest-path".into(), package.manifest_path.to_string()]); let mut cmd = Command::new("cargo"); @@ -136,7 +178,7 @@ impl CargoBuildCmd { let elf_path = metadata .target_directory - .join(target.as_ref()) + .join(target.name()) .join(&self.profile) .join(&package.name); let elf = diff --git a/crates/dockerized/compiler/src/main.rs b/crates/dockerized/compiler/src/main.rs index 17d8502..0988174 100644 --- a/crates/dockerized/compiler/src/main.rs +++ b/crates/dockerized/compiler/src/main.rs @@ -228,11 +228,11 @@ fn compile(guest_dir: PathBuf, compiler_kind: CompilerKind) -> CompilationResult use ere_sp1::compiler::*; match compiler_kind { CompilerKind::Rust => { - let program = RustRv32ima.compile(&guest_dir)?; + let program = RustRv64ima.compile(&guest_dir)?; (Some(program.elf().to_vec()), None, program) } CompilerKind::RustCustomized => { - let program = RustRv32imaCustomized.compile(&guest_dir)?; + let program = RustRv64imaCustomized.compile(&guest_dir)?; (Some(program.elf().to_vec()), None, program) } _ => bail!(unsupported_compiler_kind_err( diff --git a/crates/zkvm/sp1/src/compiler.rs b/crates/zkvm/sp1/src/compiler.rs index 888b55b..58a8660 100644 --- a/crates/zkvm/sp1/src/compiler.rs +++ b/crates/zkvm/sp1/src/compiler.rs @@ -1,7 +1,7 @@ mod error; -mod rust_rv32ima; -mod rust_rv32ima_customized; +mod rust_rv64ima; +mod rust_rv64ima_customized; pub use error::Error; -pub use rust_rv32ima::RustRv32ima; -pub use rust_rv32ima_customized::RustRv32imaCustomized; +pub use rust_rv64ima::RustRv64ima; +pub use rust_rv64ima_customized::RustRv64imaCustomized; diff --git a/crates/zkvm/sp1/src/compiler/rust_rv32ima.rs b/crates/zkvm/sp1/src/compiler/rust_rv64ima.rs similarity index 65% rename from crates/zkvm/sp1/src/compiler/rust_rv32ima.rs rename to crates/zkvm/sp1/src/compiler/rust_rv64ima.rs index 3dabc9e..7bc104c 100644 --- a/crates/zkvm/sp1/src/compiler/rust_rv32ima.rs +++ b/crates/zkvm/sp1/src/compiler/rust_rv64ima.rs @@ -1,9 +1,22 @@ use crate::{compiler::Error, program::SP1Program}; -use ere_compile_utils::CargoBuildCmd; +use ere_compile_utils::{CargoBuildCmd, RustTarget}; use ere_zkvm_interface::compiler::Compiler; use std::{env, path::Path}; -const TARGET_TRIPLE: &str = "riscv32ima-unknown-none-elf"; +/// Target spec modified from `riscv64im-unknown-none-elf` with patch `atomic-cas = true`. +/// +/// To reproduce: +/// +/// ```bash +/// rustc +nightly -Z unstable-options --print target-spec-json --target riscv64im-unknown-none-elf \ +/// | jq '.["atomic-cas"] = true' \ +/// > ./crates/zkvm/sp1/src/compiler/rust_rv64ima/riscv64ima-unknown-none-elf.json +/// ``` +const TARGET: RustTarget = RustTarget::SpecJson { + name: "riscv64ima-unknown-none-elf", + json: include_str!("./rust_rv64ima/riscv64ima-unknown-none-elf.json"), +}; + /// According to https://github.com/succinctlabs/sp1/blob/v6.0.0/crates/build/src/command/utils.rs#L49. const RUSTFLAGS: &[&str] = &[ "-C", @@ -24,12 +37,14 @@ const RUSTFLAGS: &[&str] = &[ const CARGO_BUILD_OPTIONS: &[&str] = &[ // For bare metal we have to build core and alloc "-Zbuild-std=core,alloc", + // For using json target spec + "-Zjson-target-spec", ]; -/// Compiler for Rust guest program to RV32IMA architecture. -pub struct RustRv32ima; +/// Compiler for Rust guest program to RV64IMA architecture. +pub struct RustRv64ima; -impl Compiler for RustRv32ima { +impl Compiler for RustRv64ima { type Error = Error; type Program = SP1Program; @@ -40,14 +55,14 @@ impl Compiler for RustRv32ima { .toolchain(toolchain) .build_options(CARGO_BUILD_OPTIONS) .rustflags(RUSTFLAGS) - .exec(guest_directory, TARGET_TRIPLE)?; + .exec(guest_directory, TARGET)?; Ok(SP1Program { elf }) } } #[cfg(test)] mod tests { - use crate::{compiler::RustRv32ima, zkvm::EreSP1}; + use crate::{compiler::RustRv64ima, zkvm::EreSP1}; use ere_test_utils::host::testing_guest_directory; use ere_zkvm_interface::{ Input, @@ -58,14 +73,14 @@ mod tests { #[test] fn test_compile() { let guest_directory = testing_guest_directory("sp1", "stock_nightly_no_std"); - let program = RustRv32ima.compile(&guest_directory).unwrap(); + let program = RustRv64ima.compile(&guest_directory).unwrap(); assert!(!program.elf().is_empty(), "ELF bytes should not be empty."); } #[test] fn test_execute() { let guest_directory = testing_guest_directory("sp1", "stock_nightly_no_std"); - let program = RustRv32ima.compile(&guest_directory).unwrap(); + let program = RustRv64ima.compile(&guest_directory).unwrap(); let zkvm = EreSP1::new(program, ProverResource::Cpu).unwrap(); zkvm.execute(&Input::new()).unwrap(); diff --git a/crates/zkvm/sp1/src/compiler/rust_rv64ima/riscv64ima-unknown-none-elf.json b/crates/zkvm/sp1/src/compiler/rust_rv64ima/riscv64ima-unknown-none-elf.json new file mode 100644 index 0000000..9f105d4 --- /dev/null +++ b/crates/zkvm/sp1/src/compiler/rust_rv64ima/riscv64ima-unknown-none-elf.json @@ -0,0 +1,25 @@ +{ + "arch": "riscv64", + "atomic-cas": true, + "code-model": "medium", + "cpu": "generic-rv64", + "crt-objects-fallback": "false", + "data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", + "eh-frame-header": false, + "emit-debug-gdb-scripts": false, + "features": "+m,+forced-atomics", + "linker": "rust-lld", + "linker-flavor": "gnu-lld", + "llvm-abiname": "lp64", + "llvm-target": "riscv64", + "max-atomic-width": 64, + "metadata": { + "description": "Bare RISC-V (RV64IM ISA)", + "host_tools": false, + "std": false, + "tier": 3 + }, + "panic-strategy": "abort", + "relocation-model": "static", + "target-pointer-width": 64 +} diff --git a/crates/zkvm/sp1/src/compiler/rust_rv32ima_customized.rs b/crates/zkvm/sp1/src/compiler/rust_rv64ima_customized.rs similarity index 89% rename from crates/zkvm/sp1/src/compiler/rust_rv32ima_customized.rs rename to crates/zkvm/sp1/src/compiler/rust_rv64ima_customized.rs index dec4889..cfca81e 100644 --- a/crates/zkvm/sp1/src/compiler/rust_rv32ima_customized.rs +++ b/crates/zkvm/sp1/src/compiler/rust_rv64ima_customized.rs @@ -5,11 +5,11 @@ use std::{fs, path::Path, process::Command}; use tempfile::tempdir; use tracing::info; -/// Compiler for Rust guest program to RV32IMA architecture, using customized +/// Compiler for Rust guest program to RV64IMA architecture, using customized /// Rust toolchain of Succinct. -pub struct RustRv32imaCustomized; +pub struct RustRv64imaCustomized; -impl Compiler for RustRv32imaCustomized { +impl Compiler for RustRv64imaCustomized { type Error = Error; type Program = SP1Program; @@ -56,14 +56,14 @@ impl Compiler for RustRv32imaCustomized { #[cfg(test)] mod tests { - use crate::compiler::RustRv32imaCustomized; + use crate::compiler::RustRv64imaCustomized; use ere_test_utils::host::testing_guest_directory; use ere_zkvm_interface::compiler::Compiler; #[test] fn test_compile() { let guest_directory = testing_guest_directory("sp1", "basic"); - let program = RustRv32imaCustomized.compile(&guest_directory).unwrap(); + let program = RustRv64imaCustomized.compile(&guest_directory).unwrap(); assert!(!program.elf().is_empty(), "ELF bytes should not be empty."); } } diff --git a/crates/zkvm/sp1/src/zkvm.rs b/crates/zkvm/sp1/src/zkvm.rs index 077cf38..92788c7 100644 --- a/crates/zkvm/sp1/src/zkvm.rs +++ b/crates/zkvm/sp1/src/zkvm.rs @@ -131,7 +131,7 @@ fn input_to_stdin(input: &Input) -> Result { #[cfg(test)] mod tests { - use crate::{compiler::RustRv32imaCustomized, program::SP1Program, zkvm::EreSP1}; + use crate::{compiler::RustRv64imaCustomized, program::SP1Program, zkvm::EreSP1}; use ere_test_utils::{ host::{TestCase, run_zkvm_execute, run_zkvm_prove, testing_guest_directory}, io::serde::bincode::BincodeLegacy, @@ -148,7 +148,7 @@ mod tests { static PROGRAM: OnceLock = OnceLock::new(); PROGRAM .get_or_init(|| { - RustRv32imaCustomized + RustRv64imaCustomized .compile(&testing_guest_directory("sp1", "basic")) .unwrap() })