Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve DX around statically linked bytecodes, add example #37

Merged
merged 2 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

90 changes: 83 additions & 7 deletions crates/revmc-context/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -154,6 +188,20 @@ pub type RawEvmCompilerFn = unsafe extern "C" fn(
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EvmCompilerFn(RawEvmCompilerFn);

impl From<RawEvmCompilerFn> for EvmCompilerFn {
#[inline]
fn from(f: RawEvmCompilerFn) -> Self {
Self::new(f)
}
}

impl From<EvmCompilerFn> for RawEvmCompilerFn {
#[inline]
fn from(f: EvmCompilerFn) -> Self {
f.into_inner()
}
}

impl EvmCompilerFn {
/// Wraps the function.
#[inline]
Expand Down Expand Up @@ -693,6 +741,14 @@ fn option_as_mut_ptr<T>(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::*;
Expand All @@ -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)]
Expand All @@ -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<revm_interpreter::LoadAccountResult> {
unimplemented!()
}
fn block_hash(&mut self, number: U256) -> Option<revm_primitives::B256> {
Expand All @@ -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)> {
Expand Down Expand Up @@ -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);

Expand Down
2 changes: 2 additions & 0 deletions crates/revmc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;

Expand Down
1 change: 1 addition & 0 deletions examples/compiler/build.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
fn main() {
// Emit the configuration to run compiled bytecodes.
revmc_build::emit();
}
4 changes: 2 additions & 2 deletions examples/compiler/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ use std::path::PathBuf;

#[derive(Parser)]
struct Cli {
#[arg(long)]
#[arg(long, required_unless_present = "code_path")]
code: Option<String>,
#[arg(long, conflicts_with = "code", required_unless_present = "code")]
#[arg(long, conflicts_with = "code")]
code_path: Option<PathBuf>,
}

Expand Down
2 changes: 2 additions & 0 deletions examples/runner/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
26 changes: 25 additions & 1 deletion examples/runner/build.rs
Original file line number Diff line number Diff line change
@@ -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(())
}
22 changes: 18 additions & 4 deletions examples/runner/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,26 @@

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 _;

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())
Expand All @@ -28,8 +39,11 @@ impl ExternalContext {
}

fn get_function(&self, bytecode_hash: B256) -> Option<EvmCompilerFn> {
// 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
}
}
Expand Down