diff --git a/CHANGELOG.md b/CHANGELOG.md index 2545d092654..d7efb62416f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All PRs to the Wasmer repository must add to this file. Blocks of changes will separated by version increments. ## **[Unreleased]** +- [#367](https://github.com/wasmerio/wasmer/pull/367) Add caching support to the LLVM backend. - [#366](https://github.com/wasmerio/wasmer/pull/366) Remove `UserTrapper` trait to fix [#365](https://github.com/wasmerio/wasmer/issues/365). - [#348](https://github.com/wasmerio/wasmer/pull/348) Refactor internal runtime ↔️ backend abstraction. - [#355](https://github.com/wasmerio/wasmer/pull/355) Misc changes to `Cargo.toml`s for publishing diff --git a/lib/clif-backend/src/cache.rs b/lib/clif-backend/src/cache.rs index 5af7087031e..8b6cb46849a 100644 --- a/lib/clif-backend/src/cache.rs +++ b/lib/clif-backend/src/cache.rs @@ -27,18 +27,12 @@ impl CacheGenerator { } impl CacheGen for CacheGenerator { - fn generate_cache( - &self, - module: &ModuleInner, - ) -> Result<(Box, Box<[u8]>, Memory), Error> { - let info = Box::new(module.info.clone()); - + fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), Error> { // Clone the memory to a new location. This could take a long time, // depending on the throughput of your memcpy implementation. let compiled_code = (*self.memory).clone(); Ok(( - info, self.backend_cache.into_backend_data()?.into_boxed_slice(), compiled_code, )) diff --git a/lib/llvm-backend/cpp/object_loader.cpp b/lib/llvm-backend/cpp/object_loader.cpp index cef7c721406..28bea635494 100644 --- a/lib/llvm-backend/cpp/object_loader.cpp +++ b/lib/llvm-backend/cpp/object_loader.cpp @@ -175,21 +175,26 @@ WasmModule::WasmModule( callbacks_t callbacks ) : memory_manager(std::unique_ptr(new MemoryManager(callbacks))) { - object_file = llvm::cantFail(llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef( - llvm::StringRef((const char *)object_start, object_size), "object" - ))); + - SymbolLookup symbol_resolver(callbacks); - runtime_dyld = std::unique_ptr(new llvm::RuntimeDyld(*memory_manager, symbol_resolver)); + if (auto created_object_file = llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef( + llvm::StringRef((const char *)object_start, object_size), "object" + ))) { + object_file = cantFail(std::move(created_object_file)); + SymbolLookup symbol_resolver(callbacks); + runtime_dyld = std::unique_ptr(new llvm::RuntimeDyld(*memory_manager, symbol_resolver)); - runtime_dyld->setProcessAllSections(true); + runtime_dyld->setProcessAllSections(true); - runtime_dyld->loadObject(*object_file); - runtime_dyld->finalizeWithMemoryManagerLocking(); + runtime_dyld->loadObject(*object_file); + runtime_dyld->finalizeWithMemoryManagerLocking(); - if (runtime_dyld->hasError()) { - std::cout << "RuntimeDyld error: " << (std::string)runtime_dyld->getErrorString() << std::endl; - abort(); + if (runtime_dyld->hasError()) { + _init_failed = true; + return; + } + } else { + _init_failed = true; } } diff --git a/lib/llvm-backend/cpp/object_loader.hh b/lib/llvm-backend/cpp/object_loader.hh index f1b53f1561c..134396b7892 100644 --- a/lib/llvm-backend/cpp/object_loader.hh +++ b/lib/llvm-backend/cpp/object_loader.hh @@ -152,6 +152,7 @@ struct WasmModule void *get_func(llvm::StringRef name) const; + bool _init_failed = false; private: std::unique_ptr memory_manager; std::unique_ptr object_file; @@ -164,6 +165,10 @@ extern "C" { *module_out = new WasmModule(mem_ptr, mem_size, callbacks); + if ((*module_out)->_init_failed) { + return RESULT_OBJECT_LOAD_FAILURE; + } + return RESULT_OK; } diff --git a/lib/llvm-backend/src/backend.rs b/lib/llvm-backend/src/backend.rs index 0baaeacffb3..783a432ff23 100644 --- a/lib/llvm-backend/src/backend.rs +++ b/lib/llvm-backend/src/backend.rs @@ -13,12 +13,17 @@ use std::{ any::Any, ffi::{c_void, CString}, mem, + ops::Deref, ptr::{self, NonNull}, slice, str, - sync::Once, + sync::{Arc, Once}, }; use wasmer_runtime_core::{ - backend::RunnableModule, + backend::{ + sys::{Memory, Protect}, + CacheGen, RunnableModule, + }, + cache::Error as CacheError, module::ModuleInfo, structures::TypedIndex, typed_func::{Wasm, WasmTrapInfo}, @@ -203,17 +208,32 @@ fn get_callbacks() -> Callbacks { } } +pub enum Buffer { + LlvmMemory(MemoryBuffer), + Memory(Memory), +} + +impl Deref for Buffer { + type Target = [u8]; + fn deref(&self) -> &[u8] { + match self { + Buffer::LlvmMemory(mem_buffer) => mem_buffer.as_slice(), + Buffer::Memory(memory) => unsafe { memory.as_slice() }, + } + } +} + unsafe impl Send for LLVMBackend {} unsafe impl Sync for LLVMBackend {} pub struct LLVMBackend { module: *mut LLVMModule, #[allow(dead_code)] - memory_buffer: MemoryBuffer, + buffer: Arc, } impl LLVMBackend { - pub fn new(module: Module, _intrinsics: Intrinsics) -> Self { + pub fn new(module: Module, _intrinsics: Intrinsics) -> (Self, LLVMCache) { Target::initialize_x86(&InitializationConfig { asm_parser: true, asm_printer: true, @@ -262,10 +282,44 @@ impl LLVMBackend { panic!("failed to load object") } - Self { - module, - memory_buffer, + let buffer = Arc::new(Buffer::LlvmMemory(memory_buffer)); + + ( + Self { + module, + buffer: Arc::clone(&buffer), + }, + LLVMCache { buffer }, + ) + } + + pub unsafe fn from_buffer(memory: Memory) -> Result<(Self, LLVMCache), String> { + let callbacks = get_callbacks(); + let mut module: *mut LLVMModule = ptr::null_mut(); + + let slice = unsafe { memory.as_slice() }; + + let res = module_load(slice.as_ptr(), slice.len(), callbacks, &mut module); + + if res != LLVMResult::OK { + return Err("failed to load object".to_string()); } + + static SIGNAL_HANDLER_INSTALLED: Once = Once::new(); + + SIGNAL_HANDLER_INSTALLED.call_once(|| unsafe { + crate::platform::install_signal_handler(); + }); + + let buffer = Arc::new(Buffer::Memory(memory)); + + Ok(( + Self { + module, + buffer: Arc::clone(&buffer), + }, + LLVMCache { buffer }, + )) } } @@ -322,6 +376,28 @@ impl RunnableModule for LLVMBackend { } } +unsafe impl Send for LLVMCache {} +unsafe impl Sync for LLVMCache {} + +pub struct LLVMCache { + buffer: Arc, +} + +impl CacheGen for LLVMCache { + fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> { + let mut memory = Memory::with_size_protect(self.buffer.len(), Protect::ReadWrite) + .map_err(CacheError::SerializeError)?; + + let buffer = self.buffer.deref(); + + unsafe { + memory.as_slice_mut()[..buffer.len()].copy_from_slice(buffer); + } + + Ok(([].as_ref().into(), memory)) + } +} + #[cfg(feature = "disasm")] unsafe fn disass_ptr(ptr: *const u8, size: usize, inst_count: usize) { use capstone::arch::BuildsCapstone; diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs index bc43c5773fe..6ce8139c327 100644 --- a/lib/llvm-backend/src/lib.rs +++ b/lib/llvm-backend/src/lib.rs @@ -38,39 +38,28 @@ impl Compiler for LLVMCompiler { let (info, code_reader) = read_info::read_module(wasm, compiler_config).unwrap(); let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap(); - let backend = backend::LLVMBackend::new(module, intrinsics); + let (backend, cache_gen) = backend::LLVMBackend::new(module, intrinsics); - // Create placeholder values here. - let cache_gen = { - use wasmer_runtime_core::backend::{sys::Memory, CacheGen}; - use wasmer_runtime_core::cache::Error as CacheError; - use wasmer_runtime_core::module::ModuleInfo; - - struct Placeholder; + Ok(ModuleInner { + runnable_module: Box::new(backend), + cache_gen: Box::new(cache_gen), - impl CacheGen for Placeholder { - fn generate_cache( - &self, - _module: &ModuleInner, - ) -> Result<(Box, Box<[u8]>, Memory), CacheError> { - unimplemented!() - } - } + info, + }) + } - Box::new(Placeholder) - }; + unsafe fn from_cache(&self, artifact: Artifact, _: Token) -> Result { + let (info, _, memory) = artifact.consume(); + let (backend, cache_gen) = + backend::LLVMBackend::from_buffer(memory).map_err(CacheError::DeserializeError)?; Ok(ModuleInner { runnable_module: Box::new(backend), - cache_gen, + cache_gen: Box::new(cache_gen), info, }) } - - unsafe fn from_cache(&self, _artifact: Artifact, _: Token) -> Result { - unimplemented!("the llvm backend doesn't support caching yet") - } } fn validate(bytes: &[u8]) -> Result<(), CompileError> { @@ -123,7 +112,7 @@ fn test_read_module() { let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap(); - let backend = backend::LLVMBackend::new(module, intrinsics); + let (backend, _) = backend::LLVMBackend::new(module, intrinsics); let func_ptr = backend.get_func(&info, LocalFuncIndex::new(0)).unwrap(); diff --git a/lib/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs index 48c7e1c5f9a..d465cda5834 100644 --- a/lib/runtime-core/src/backend.rs +++ b/lib/runtime-core/src/backend.rs @@ -83,8 +83,5 @@ pub trait RunnableModule: Send + Sync { } pub trait CacheGen: Send + Sync { - fn generate_cache( - &self, - module: &ModuleInner, - ) -> Result<(Box, Box<[u8]>, Memory), CacheError>; + fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError>; } diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index 40cde6fbf77..1ffbc71fc8c 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -121,8 +121,12 @@ impl Module { } pub fn cache(&self) -> Result { - let (info, backend_metadata, code) = self.inner.cache_gen.generate_cache(&self.inner)?; - Ok(Artifact::from_parts(info, backend_metadata, code)) + let (backend_metadata, code) = self.inner.cache_gen.generate_cache()?; + Ok(Artifact::from_parts( + Box::new(self.inner.info.clone()), + backend_metadata, + code, + )) } pub fn info(&self) -> &ModuleInfo { diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index 6a408cd0484..a1e493f8cec 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -569,10 +569,7 @@ mod vm_ctx_tests { } } impl CacheGen for Placeholder { - fn generate_cache( - &self, - module: &ModuleInner, - ) -> Result<(Box, Box<[u8]>, Memory), CacheError> { + fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> { unimplemented!() } } diff --git a/lib/runtime/examples/call.rs b/lib/runtime/examples/call.rs index 12f14906281..a8f27b4c00e 100644 --- a/lib/runtime/examples/call.rs +++ b/lib/runtime/examples/call.rs @@ -7,8 +7,6 @@ static WAT: &'static str = r#" (type (;0;) (func (result i32))) (import "env" "do_panic" (func $do_panic (type 0))) (func $dbz (result i32) - call $do_panic - drop i32.const 42 i32.const 0 i32.div_u diff --git a/lib/singlepass-backend/src/lib.rs b/lib/singlepass-backend/src/lib.rs index c388bee496c..7ecd029e480 100644 --- a/lib/singlepass-backend/src/lib.rs +++ b/lib/singlepass-backend/src/lib.rs @@ -36,10 +36,7 @@ use wasmer_runtime_core::{ struct Placeholder; impl CacheGen for Placeholder { - fn generate_cache( - &self, - _module: &ModuleInner, - ) -> Result<(Box, Box<[u8]>, Memory), CacheError> { + fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> { Err(CacheError::Unknown( "the singlepass backend doesn't support caching yet".to_string(), ))