From 536f9813dc18753fd900dba07334e4ba0a064faa Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Thu, 1 Aug 2019 12:12:53 -0700 Subject: [PATCH] Implement InternalEvent::Breakpoint in the llvm backend. Enable now-working metering unit tests when run with the llvm backend. --- lib/llvm-backend/cpp/object_loader.hh | 32 ++++++++++++++++++++++++--- lib/llvm-backend/src/backend.rs | 4 +++- lib/llvm-backend/src/code.rs | 24 +++++++++++++++++++- lib/llvm-backend/src/intrinsics.rs | 7 +++++- lib/middleware-common/src/metering.rs | 2 +- 5 files changed, 62 insertions(+), 7 deletions(-) diff --git a/lib/llvm-backend/cpp/object_loader.hh b/lib/llvm-backend/cpp/object_loader.hh index 63630a479..2952835de 100644 --- a/lib/llvm-backend/cpp/object_loader.hh +++ b/lib/llvm-backend/cpp/object_loader.hh @@ -1,9 +1,10 @@ #include #include -#include +#include #include #include -#include + +#include typedef enum { @@ -77,6 +78,19 @@ struct UserException : UncatchableException box_any_t error_data; }; +struct BreakpointException : UncatchableException +{ + public: + BreakpointException(uintptr_t callback) : callback(callback) {} + + virtual std::string description() const noexcept override + { + return "breakpoint exception"; + } + + uintptr_t callback; +}; + struct WasmTrap : UncatchableException { public: @@ -166,6 +180,8 @@ struct WasmModule extern "C" { + void callback_trampoline(void *, void *); + result_t module_load(const uint8_t *mem_ptr, size_t mem_size, callbacks_t callbacks, WasmModule **module_out) { *module_out = new WasmModule(mem_ptr, mem_size, callbacks); @@ -192,6 +208,12 @@ extern "C" throw UserException(data, vtable); } + // Throw a pointer that's assumed to be codegen::BreakpointHandler on the + // rust side. + [[noreturn]] void throw_breakpoint(uintptr_t callback) { + throw BreakpointException(callback); + } + bool invoke_trampoline( trampoline_t trampoline, void *ctx, @@ -217,6 +239,10 @@ extern "C" *user_error = e.error_data; return false; } + catch (const BreakpointException &e) { + callback_trampoline(user_error, (void *)e.callback); + return false; + } catch (const WasmException &e) { *trap_out = WasmTrap::Type::Unknown; @@ -233,4 +259,4 @@ extern "C" { return module->get_func(llvm::StringRef(name)); } -} \ No newline at end of file +} diff --git a/lib/llvm-backend/src/backend.rs b/lib/llvm-backend/src/backend.rs index ff8c281df..94b86e34d 100644 --- a/lib/llvm-backend/src/backend.rs +++ b/lib/llvm-backend/src/backend.rs @@ -39,7 +39,8 @@ extern "C" { fn module_delete(module: *mut LLVMModule); fn get_func_symbol(module: *mut LLVMModule, name: *const c_char) -> *const vm::Func; - fn throw_trap(ty: i32); + fn throw_trap(ty: i32) -> !; + fn throw_breakpoint(ty: i64) -> !; /// This should be the same as spliting up the fat pointer into two arguments, /// but this is cleaner, I think? @@ -103,6 +104,7 @@ fn get_callbacks() -> Callbacks { fn_name!("vm.memory.size.static.local") => vmcalls::local_static_memory_size as _, fn_name!("vm.exception.trap") => throw_trap as _, + fn_name!("vm.breakpoint") => throw_breakpoint as _, _ => ptr::null(), } diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index 07dbbacc4..1e9a62182 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -496,6 +496,21 @@ pub struct CodegenError { pub message: String, } +// This is only called by C++ code, the 'pub' + '#[no_mangle]' combination +// prevents unused function elimination. +#[no_mangle] +pub unsafe extern "C" fn callback_trampoline( + b: *mut Option>, + callback: *mut BreakpointHandler, +) { + let callback = Box::from_raw(callback); + let result: Result<(), Box> = callback(BreakpointInfo { fault: None }); + match result { + Ok(()) => *b = None, + Err(e) => *b = Some(e), + } +} + pub struct LLVMModuleCodeGenerator { context: Option, builder: Option, @@ -612,7 +627,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { InternalEvent::FunctionBegin(_) | InternalEvent::FunctionEnd => { return Ok(()); } - InternalEvent::Breakpoint(_callback) => { + InternalEvent::Breakpoint(callback) => { + let raw = Box::into_raw(Box::new(callback)) as u64; + let callback = intrinsics.i64_ty.const_int(raw, false); + builder.build_call( + intrinsics.throw_breakpoint, + &[callback.as_basic_value_enum()], + "", + ); return Ok(()); } InternalEvent::GetInternal(idx) => { diff --git a/lib/llvm-backend/src/intrinsics.rs b/lib/llvm-backend/src/intrinsics.rs index 0d4e05087..461c54d5d 100644 --- a/lib/llvm-backend/src/intrinsics.rs +++ b/lib/llvm-backend/src/intrinsics.rs @@ -147,6 +147,7 @@ pub struct Intrinsics { pub memory_size_shared_import: FunctionValue, pub throw_trap: FunctionValue, + pub throw_breakpoint: FunctionValue, pub ctx_ptr_ty: PointerType, } @@ -309,7 +310,6 @@ impl Intrinsics { i32_ty.fn_type(&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic], false); let ret_i1_take_i1_i1 = i1_ty.fn_type(&[i1_ty_basic, i1_ty_basic], false); - Self { ctlz_i32: module.add_function("llvm.ctlz.i32", ret_i32_take_i32_i1, None), ctlz_i64: module.add_function("llvm.ctlz.i64", ret_i64_take_i64_i1, None), @@ -525,6 +525,11 @@ impl Intrinsics { void_ty.fn_type(&[i32_ty_basic], false), None, ), + throw_breakpoint: module.add_function( + "vm.breakpoint", + void_ty.fn_type(&[i64_ty_basic], false), + None, + ), ctx_ptr_ty, } } diff --git a/lib/middleware-common/src/metering.rs b/lib/middleware-common/src/metering.rs index 75e2f6ef0..08806f593 100644 --- a/lib/middleware-common/src/metering.rs +++ b/lib/middleware-common/src/metering.rs @@ -129,7 +129,7 @@ pub fn set_points_used_ctx(ctx: &mut Ctx, value: u64) { ctx.set_internal(&INTERNAL_FIELD, value); } -#[cfg(all(test, feature = "singlepass"))] +#[cfg(all(test, any(feature = "singlepass", feature = "llvm")))] mod tests { use super::*; use wabt::wat2wasm;