Skip to content

Commit

Permalink
chore: adjustments
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniPopes committed Apr 22, 2024
1 parent 38b7252 commit ebbe816
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 74 deletions.
4 changes: 2 additions & 2 deletions benches/benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ fn run_bench(c: &mut Criterion, bench: &Bench) {
];
let jit_ids = jit_matrix.map(|(name, (gas, stack))| {
compiler.gas_metering(gas);
unsafe { compiler.stack_length_checks(stack) };
(name, compiler.translate(Some(name), bytecode, SPEC_ID).expect(name))
unsafe { compiler.stack_bound_checks(stack) };
(name, compiler.translate(None, bytecode, SPEC_ID).expect(name))
});
for &(name, fn_id) in &jit_ids {
let jit = compiler.jit_function(fn_id).expect(name);
Expand Down
2 changes: 1 addition & 1 deletion benches/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ fn main() -> Result<()> {
compiler.set_dump_to(Some(PathBuf::from("tmp/revm-jit")));
compiler.set_module_name(&cli.bench_name);
compiler.gas_metering(!cli.no_gas);
unsafe { compiler.stack_length_checks(!cli.no_len_checks) };
unsafe { compiler.stack_bound_checks(!cli.no_len_checks) };
compiler.frame_pointers(true);
compiler.debug_assertions(cli.debug_assertions);

Expand Down
159 changes: 112 additions & 47 deletions crates/revm-jit-llvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ extern crate revm_jit_backend;
use inkwell::{
attributes::{Attribute, AttributeLoc},
basic_block::BasicBlock,
context::Context,
execution_engine::ExecutionEngine,
module::Module,
passes::PassBuilderOptions,
support::error_handling::install_fatal_error_handler,
targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine},
targets::{
CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, TargetTriple,
},
types::{BasicType, BasicTypeEnum, FunctionType, IntType, PointerType, StringRadix, VoidType},
values::{BasicValue, BasicValueEnum, FunctionValue, PointerValue},
AddressSpace, IntPredicate, OptimizationLevel,
Expand All @@ -25,20 +26,32 @@ use revm_jit_backend::{
eyre, Backend, BackendTypes, Builder, Error, IntCC, Result, TypeMethods, U256,
};
use rustc_hash::FxHashMap;
use std::{path::Path, sync::Once};
use std::{
path::Path,
sync::{Once, OnceLock},
};

pub use inkwell;
pub use inkwell::{self, context::Context};

pub mod orc;

/// Executes the given closure with a thread-local LLVM context.
#[inline]
pub fn with_llvm_context<R>(f: impl FnOnce(&Context) -> R) -> R {
thread_local! {
static TLS_LLVM_CONTEXT: Context = Context::create();
}
TLS_LLVM_CONTEXT.with(f)
}

/// The LLVM-based EVM bytecode compiler backend.
#[derive(Debug)]
#[must_use]
pub struct EvmLlvmBackend<'ctx> {
cx: &'ctx Context,
bcx: inkwell::builder::Builder<'ctx>,
module: Module<'ctx>,
exec_engine: ExecutionEngine<'ctx>,
exec_engine: Option<ExecutionEngine<'ctx>>,
machine: TargetMachine,

ty_void: VoidType<'ctx>,
Expand Down Expand Up @@ -74,45 +87,17 @@ impl<'ctx> EvmLlvmBackend<'ctx> {
aot: bool,
opt_level: revm_jit_backend::OptimizationLevel,
) -> Result<Self> {
let mut init_result = Ok(());
static INIT: Once = Once::new();
INIT.call_once(|| {
// TODO: This also reports "PLEASE submit a bug report to..." when the segfault is
// outside of LLVM.
// enable_llvm_pretty_stack_trace();

extern "C" fn report_fatal_error(msg: *const std::ffi::c_char) {
let msg = unsafe { std::ffi::CStr::from_ptr(msg) };
error!(msg = %msg.to_string_lossy(), "LLVM fatal error");
}

unsafe {
install_fatal_error_handler(report_fatal_error);
}

let config = InitializationConfig {
asm_parser: false,
asm_printer: true,
base: true,
disassembler: true,
info: true,
machine_code: true,
};
init_result = Target::initialize_native(&config).map_err(Error::msg);
});
init_result?;
init()?;

let opt_level = convert_opt_level(opt_level);

let triple = TargetMachine::get_default_triple();
let cpu = TargetMachine::get_host_cpu_name();
let features = TargetMachine::get_host_cpu_features();
let target = Target::from_triple(&triple).map_err(error_msg)?;
let target_defaults = TargetDefaults::get();
let target = Target::from_triple(&target_defaults.triple).map_err(error_msg)?;
let machine = target
.create_target_machine(
&triple,
&cpu.to_string_lossy(),
&features.to_string_lossy(),
&target_defaults.triple,
&target_defaults.cpu,
&target_defaults.features,
opt_level,
if aot { RelocMode::DynamicNoPic } else { RelocMode::PIC },
if aot { CodeModel::Default } else { CodeModel::JITDefault },
Expand All @@ -121,7 +106,20 @@ impl<'ctx> EvmLlvmBackend<'ctx> {

let module = create_module(cx, &machine)?;

let exec_engine = module.create_jit_execution_engine(opt_level).map_err(error_msg)?;
let exec_engine = if aot {
None
} else {
if !target.has_jit() {
return Err(eyre::eyre!("target {:?} does not support JIT", target.get_name()));
}
if !target.has_target_machine() {
return Err(eyre::eyre!(
"target {:?} does not have target machine",
target.get_name()
));
}
Some(module.create_jit_execution_engine(opt_level).map_err(error_msg)?)
};

let bcx = cx.create_builder();

Expand Down Expand Up @@ -161,6 +159,11 @@ impl<'ctx> EvmLlvmBackend<'ctx> {
self.cx
}

fn exec_engine(&self) -> &ExecutionEngine<'ctx> {
assert!(!self.aot, "requested JIT execution engine on AOT");
self.exec_engine.as_ref().expect("missing JIT execution engine")
}

fn fn_type(
&self,
ret: Option<BasicTypeEnum<'ctx>>,
Expand Down Expand Up @@ -309,26 +312,54 @@ impl<'ctx> Backend for EvmLlvmBackend<'ctx> {

fn jit_function(&mut self, id: Self::FuncId) -> Result<usize> {
let name = self.id_to_name(id);
self.exec_engine.get_function_address(name).map_err(Into::into)
self.exec_engine().get_function_address(name).map_err(Into::into)
}

unsafe fn free_function(&mut self, id: Self::FuncId) -> Result<()> {
let name = self.id_to_name(id);
let function = self.exec_engine.get_function_value(name)?;
self.exec_engine.free_fn_machine_code(function);
let function = self.exec_engine().get_function_value(name)?;
self.exec_engine().free_fn_machine_code(function);
self.function_names.clear();
Ok(())
}

unsafe fn free_all_functions(&mut self) -> Result<()> {
self.exec_engine.remove_module(&self.module).map_err(|e| Error::msg(e.to_string()))?;
if let Some(exec_engine) = &self.exec_engine {
exec_engine.remove_module(&self.module).map_err(|e| Error::msg(e.to_string()))?;
}
self.module = create_module(self.cx, &self.machine)?;
self.exec_engine =
self.module.create_jit_execution_engine(self.opt_level).map_err(error_msg)?;
if self.exec_engine.is_some() {
self.exec_engine =
Some(self.module.create_jit_execution_engine(self.opt_level).map_err(error_msg)?);
}
Ok(())
}
}

/// Cached target information.
struct TargetDefaults {
triple: TargetTriple,
cpu: String,
features: String,
// target: Target,
}

// SAFETY: No mutability is exposed and `TargetTriple` is an owned string.
unsafe impl std::marker::Send for TargetDefaults {}
unsafe impl std::marker::Sync for TargetDefaults {}

impl TargetDefaults {
fn get() -> &'static Self {
static TARGET_DEFAULTS: OnceLock<TargetDefaults> = OnceLock::new();
TARGET_DEFAULTS.get_or_init(|| {
let triple = TargetMachine::get_default_triple();
let cpu = TargetMachine::get_host_cpu_name().to_string_lossy().into_owned();
let features = TargetMachine::get_host_cpu_features().to_string_lossy().into_owned();
TargetDefaults { triple, cpu, features }
})
}
}

/// The LLVM-based EVM bytecode compiler function builder.
#[derive(Debug)]
#[must_use]
Expand Down Expand Up @@ -872,7 +903,9 @@ impl<'a, 'ctx> Builder for EvmLlvmBuilder<'a, 'ctx> {
) -> Self::Function {
let func_ty = self.fn_type(ret, params);
let function = self.module.add_function(name, func_ty, Some(convert_linkage(linkage)));
self.exec_engine.add_global_mapping(&function, address);
if let Some(exec_engine) = &self.exec_engine {
exec_engine.add_global_mapping(&function, address);
}
function
}

Expand All @@ -888,6 +921,38 @@ impl<'a, 'ctx> Builder for EvmLlvmBuilder<'a, 'ctx> {
}
}

fn init() -> Result<()> {
let mut init_result = Ok(());
static INIT: Once = Once::new();
INIT.call_once(|| init_result = init_());
init_result
}

fn init_() -> Result<()> {
// TODO: This also reports "PLEASE submit a bug report to..." when the segfault is
// outside of LLVM.
// enable_llvm_pretty_stack_trace();

extern "C" fn report_fatal_error(msg: *const std::ffi::c_char) {
let msg = unsafe { std::ffi::CStr::from_ptr(msg) };
error!(msg = %msg.to_string_lossy(), "LLVM fatal error");
}

unsafe {
install_fatal_error_handler(report_fatal_error);
}

let config = InitializationConfig {
asm_parser: false,
asm_printer: true,
base: true,
disassembler: true,
info: true,
machine_code: true,
};
Target::initialize_native(&config).map_err(Error::msg)
}

fn create_module<'ctx>(cx: &'ctx Context, machine: &TargetMachine) -> Result<Module<'ctx>> {
let module_name = "evm";
let module = cx.create_module(module_name);
Expand Down
27 changes: 19 additions & 8 deletions crates/revm-jit/src/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ impl<B: Backend> EvmCompiler<B> {
self.config.inspect_stack_length = yes;
}

/// Sets whether to enable stack checks.
/// Sets whether to enable stack bound checks.
///
/// Defaults to `true`.
///
Expand All @@ -193,8 +193,8 @@ impl<B: Backend> EvmCompiler<B> {
///
/// [`StackUnderflow`]: crate::interpreter::InstructionResult::StackUnderflow
/// [`StackOverflow`]: crate::interpreter::InstructionResult::StackOverflow
pub unsafe fn stack_length_checks(&mut self, yes: bool) {
self.config.stack_length_checks = yes;
pub unsafe fn stack_bound_checks(&mut self, yes: bool) {
self.config.stack_bound_checks = yes;
}

/// Sets whether to track gas costs.
Expand All @@ -213,6 +213,9 @@ impl<B: Backend> EvmCompiler<B> {
}

/// Translates the given EVM bytecode into an internal function.
///
/// NOTE: `name` must be unique for each function, as it is used as the name of the final
/// symbol. Use `None` for a default unique name.
pub fn translate(
&mut self,
name: Option<&str>,
Expand All @@ -225,6 +228,8 @@ impl<B: Backend> EvmCompiler<B> {
}

/// (JIT) Compiles the given EVM bytecode into a JIT function.
///
/// See [`translate`](Self::translate) for more information.
pub fn jit(
&mut self,
name: Option<&str>,
Expand Down Expand Up @@ -292,8 +297,14 @@ impl<B: Backend> EvmCompiler<B> {
name: Option<&str>,
bytecode: &Bytecode<'_>,
) -> Result<B::FuncId> {
let name = name.unwrap_or("evm_bytecode");
let mname = self.mangle_name(name, bytecode.spec_id);
let storage;
let name = match name {
Some(name) => name,
None => {
storage = self.default_name();
&storage
}
};

if let Some(dump_dir) = &self.dump_dir() {
trace_time!("dump bytecode", || Self::dump_bytecode(dump_dir, bytecode))?;
Expand All @@ -302,7 +313,7 @@ impl<B: Backend> EvmCompiler<B> {
let (bcx, id) = trace_time!("make builder", || Self::make_builder(
&mut self.backend,
&self.config,
&mname,
&name,
))?;
trace_time!("translate", || FunctionCx::translate(
bcx,
Expand Down Expand Up @@ -464,8 +475,8 @@ impl<B: Backend> EvmCompiler<B> {
Ok(())
}

fn mangle_name(&mut self, base: &str, spec_id: SpecId) -> String {
let name = format!("{base}_{spec_id:?}_{}", self.function_counter);
fn default_name(&mut self) -> String {
let name = format!("__evm_compiler_{}", self.function_counter);
self.function_counter += 1;
name
}
Expand Down
6 changes: 3 additions & 3 deletions crates/revm-jit/src/compiler/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub(super) struct FcxConfig {

pub(super) local_stack: bool,
pub(super) inspect_stack_length: bool,
pub(super) stack_length_checks: bool,
pub(super) stack_bound_checks: bool,
pub(super) gas_metering: bool,
}

Expand All @@ -33,7 +33,7 @@ impl Default for FcxConfig {
frame_pointers: cfg!(debug_assertions),
local_stack: false,
inspect_stack_length: false,
stack_length_checks: true,
stack_bound_checks: true,
gas_metering: true,
}
}
Expand Down Expand Up @@ -526,7 +526,7 @@ impl<'a, B: Backend> FunctionCx<'a, B> {
}
self.len_before = self.stack_len.load(&mut self.bcx, "stack_len");

if self.config.stack_length_checks {
if self.config.stack_bound_checks {
let underflow = |this: &mut Self| {
this.bcx.icmp_imm(IntCC::UnsignedLessThan, this.len_before, inp as i64)
};
Expand Down
Loading

0 comments on commit ebbe816

Please sign in to comment.