diff --git a/Cargo.lock b/Cargo.lock index 23a111a..49bdef5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2169,7 +2169,9 @@ dependencies = [ name = "revmc-examples-runner" version = "0.1.0" dependencies = [ + "cc", "revm", + "revmc", "revmc-build", "revmc-builtins", "revmc-context", diff --git a/crates/revmc-context/src/lib.rs b/crates/revmc-context/src/lib.rs index 901c8a8..44a3f77 100644 --- a/crates/revmc-context/src/lib.rs +++ b/crates/revmc-context/src/lib.rs @@ -136,6 +136,40 @@ impl dyn HostExt { } } +/// Declare [`RawEvmCompilerFn`] functions in an `extern "C"` block. +/// +/// # Examples +/// +/// ```no_run +/// use revmc_context::{extern_revmc, EvmCompilerFn}; +/// +/// extern_revmc! { +/// /// A simple function that returns `Continue`. +/// pub fn test_fn; +/// } +/// +/// let test_fn = EvmCompilerFn::new(test_fn); +/// ``` +#[macro_export] +macro_rules! extern_revmc { + ($( $(#[$attr:meta])* $vis:vis fn $name:ident; )+) => { + #[allow(improper_ctypes)] + extern "C" { + $( + $(#[$attr])* + $vis fn $name( + gas: *mut $crate::private::revm_interpreter::Gas, + stack: *mut $crate::EvmStack, + stack_len: *mut usize, + env: *const $crate::private::revm_primitives::Env, + contract: *const $crate::private::revm_interpreter::Contract, + ecx: *mut $crate::EvmContext<'_>, + ) -> $crate::private::revm_interpreter::InstructionResult; + )+ + } + }; +} + /// The raw function signature of a bytecode function. /// /// Prefer using [`EvmCompilerFn`] instead of this type. See [`EvmCompilerFn::call`] for more @@ -154,6 +188,20 @@ pub type RawEvmCompilerFn = unsafe extern "C" fn( #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct EvmCompilerFn(RawEvmCompilerFn); +impl From for EvmCompilerFn { + #[inline] + fn from(f: RawEvmCompilerFn) -> Self { + Self::new(f) + } +} + +impl From for RawEvmCompilerFn { + #[inline] + fn from(f: EvmCompilerFn) -> Self { + f.into_inner() + } +} + impl EvmCompilerFn { /// Wraps the function. #[inline] @@ -693,6 +741,14 @@ fn option_as_mut_ptr(opt: Option<&mut T>) -> *mut T { } } +// Macro re-exports. +// Not public API. +#[doc(hidden)] +pub mod private { + pub use revm_interpreter; + pub use revm_primitives; +} + #[cfg(test)] mod tests { use super::*; @@ -705,19 +761,30 @@ mod tests { assert_eq!(usize::try_from(&mut word), Ok(0)); } - #[cfg(not(feature = "host-ext-any"))] - extern "C" fn test_fn( + extern_revmc! { + #[link_name = "__test_fn"] + fn test_fn; + } + + #[no_mangle] + extern "C" fn __test_fn( _gas: *mut Gas, _stack: *mut EvmStack, _stack_len: *mut usize, - _env: *mut Env, + _env: *const Env, _contract: *const Contract, _ecx: *mut EvmContext<'_>, ) -> InstructionResult { InstructionResult::Continue } - #[cfg(not(feature = "host-ext-any"))] + #[test] + fn extern_macro() { + let _f1 = EvmCompilerFn::new(test_fn); + let _f2 = EvmCompilerFn::new(__test_fn); + assert_eq!(test_fn as usize, __test_fn as usize); + } + #[test] fn borrowing_host() { #[allow(unused)] @@ -730,7 +797,10 @@ mod tests { fn env_mut(&mut self) -> &mut Env { self.0 } - fn load_account(&mut self, address: Address) -> Option<(bool, bool)> { + fn load_account( + &mut self, + address: Address, + ) -> Option { unimplemented!() } fn block_hash(&mut self, number: U256) -> Option { @@ -739,7 +809,7 @@ mod tests { fn balance(&mut self, address: Address) -> Option<(U256, bool)> { unimplemented!() } - fn code(&mut self, address: Address) -> Option<(revm_primitives::Bytecode, bool)> { + fn code(&mut self, address: Address) -> Option<(revm_primitives::Bytes, bool)> { unimplemented!() } fn code_hash(&mut self, address: Address) -> Option<(revm_primitives::B256, bool)> { @@ -774,8 +844,14 @@ mod tests { } } + #[allow(unused_mut)] let mut env = Env::default(); - let mut host = BHost(&mut env); + #[cfg(not(feature = "host-ext-any"))] + let env = &mut env; + #[cfg(feature = "host-ext-any")] + let env = Box::leak(Box::new(env)); + + let mut host = BHost(env); let f = EvmCompilerFn::new(test_fn); let mut interpreter = Interpreter::new(Contract::default(), u64::MAX, false); diff --git a/crates/revmc/src/lib.rs b/crates/revmc/src/lib.rs index 5ad82ad..4965fdd 100644 --- a/crates/revmc/src/lib.rs +++ b/crates/revmc/src/lib.rs @@ -23,8 +23,10 @@ pub use linker::Linker; #[cfg(test)] mod tests; +#[allow(ambiguous_glob_reexports)] #[doc(inline)] pub use revmc_backend::*; +#[allow(ambiguous_glob_reexports)] #[doc(inline)] pub use revmc_context::*; diff --git a/examples/compiler/build.rs b/examples/compiler/build.rs index 135f937..b4532d5 100644 --- a/examples/compiler/build.rs +++ b/examples/compiler/build.rs @@ -1,3 +1,4 @@ fn main() { + // Emit the configuration to run compiled bytecodes. revmc_build::emit(); } diff --git a/examples/compiler/src/main.rs b/examples/compiler/src/main.rs index b46c49b..263e569 100644 --- a/examples/compiler/src/main.rs +++ b/examples/compiler/src/main.rs @@ -14,9 +14,9 @@ use std::path::PathBuf; #[derive(Parser)] struct Cli { - #[arg(long)] + #[arg(long, required_unless_present = "code_path")] code: Option, - #[arg(long, conflicts_with = "code", required_unless_present = "code")] + #[arg(long, conflicts_with = "code")] code_path: Option, } diff --git a/examples/runner/Cargo.toml b/examples/runner/Cargo.toml index 62059fa..16b50eb 100644 --- a/examples/runner/Cargo.toml +++ b/examples/runner/Cargo.toml @@ -13,7 +13,9 @@ repository.workspace = true exclude.workspace = true [build-dependencies] +revmc = { workspace = true, features = ["llvm-prefer-dynamic"] } revmc-build.workspace = true +cc = "1.0" [dependencies] revmc-builtins = { workspace = true, default-features = false } diff --git a/examples/runner/build.rs b/examples/runner/build.rs index 135f937..2892cc1 100644 --- a/examples/runner/build.rs +++ b/examples/runner/build.rs @@ -1,3 +1,27 @@ -fn main() { +use revmc::{new_llvm_backend, primitives::SpecId, EvmCompiler, OptimizationLevel, Result}; +use std::path::PathBuf; + +fn main() -> Result<()> { + // Emit the configuration to run compiled bytecodes. + // This not used if we are only using statically linked bytecodes. revmc_build::emit(); + + // Compile and statically link a bytecode. + let name = "fibonacci"; + let bytecode = revmc::primitives::hex!( + "5f355f60015b8215601a578181019150909160019003916005565b9150505f5260205ff3" + ); + println!("cargo:rustc-env=FIB_HASH={}", revmc::primitives::keccak256(bytecode)); + + let out_dir = PathBuf::from(std::env::var("OUT_DIR")?); + let context = revmc::llvm::inkwell::context::Context::create(); + let backend = new_llvm_backend(&context, true, OptimizationLevel::Aggressive)?; + let mut compiler = EvmCompiler::new(backend); + compiler.translate(Some(name), &bytecode, SpecId::CANCUN)?; + let object = out_dir.join(name).with_extension("o"); + compiler.write_object_to_file(&object)?; + + cc::Build::new().object(&object).static_flag(true).compile(name); + + Ok(()) } diff --git a/examples/runner/src/lib.rs b/examples/runner/src/lib.rs index 79fe920..b42bb3e 100644 --- a/examples/runner/src/lib.rs +++ b/examples/runner/src/lib.rs @@ -4,7 +4,7 @@ extern crate alloc; -// This dependency is needed to export the necessary symbols used by the compiled bytecodes, +// This dependency is needed to define the necessary symbols used by the compiled bytecodes, // but we don't use it directly, so silence the unused crate depedency warning. use revmc_builtins as _; @@ -12,7 +12,18 @@ use alloc::sync::Arc; use revm::{handler::register::EvmHandler, primitives::B256, Database}; use revmc_context::EvmCompilerFn; -pub fn get_evm<'a, DB: Database + 'static>(db: DB) -> revm::Evm<'a, ExternalContext, DB> { +// The bytecode we statically linked. +const FIB_HASH: B256 = + match revm::primitives::hex::const_decode_to_array(env!("FIB_HASH").as_bytes()) { + Ok(hash) => B256::new(hash), + Err(_err) => panic!(), + }; +revmc_context::extern_revmc! { + fn fibonacci; +} + +/// Build a [`revm::Evm`] with a custom handler that can call compiled functions. +pub fn build_evm<'a, DB: Database + 'static>(db: DB) -> revm::Evm<'a, ExternalContext, DB> { revm::Evm::builder() .with_db(db) .with_external_context(ExternalContext::new()) @@ -28,8 +39,11 @@ impl ExternalContext { } fn get_function(&self, bytecode_hash: B256) -> Option { - // Some way to get the function, either linked statically or dynamically. - let _ = bytecode_hash; + // Can use any mapping between bytecode hash and function. + if bytecode_hash == FIB_HASH { + return Some(EvmCompilerFn::new(fibonacci)); + } + None } }