Add caching support to llvm backend

This commit is contained in:
Lachlan Sneff
2019-04-19 13:54:48 -07:00
parent ed651055f3
commit 443663aa3f
9 changed files with 141 additions and 56 deletions

View File

@ -27,18 +27,12 @@ impl CacheGenerator {
} }
impl CacheGen for CacheGenerator { impl CacheGen for CacheGenerator {
fn generate_cache( fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), Error> {
&self,
module: &ModuleInner,
) -> Result<(Box<ModuleInfo>, Box<[u8]>, Memory), Error> {
let info = Box::new(module.info.clone());
// Clone the memory to a new location. This could take a long time, // Clone the memory to a new location. This could take a long time,
// depending on the throughput of your memcpy implementation. // depending on the throughput of your memcpy implementation.
let compiled_code = (*self.memory).clone(); let compiled_code = (*self.memory).clone();
Ok(( Ok((
info,
self.backend_cache.into_backend_data()?.into_boxed_slice(), self.backend_cache.into_backend_data()?.into_boxed_slice(),
compiled_code, compiled_code,
)) ))

View File

@ -175,21 +175,26 @@ WasmModule::WasmModule(
callbacks_t callbacks callbacks_t callbacks
) : memory_manager(std::unique_ptr<MemoryManager>(new MemoryManager(callbacks))) ) : memory_manager(std::unique_ptr<MemoryManager>(new MemoryManager(callbacks)))
{ {
object_file = llvm::cantFail(llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef(
if (auto created_object_file = llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef(
llvm::StringRef((const char *)object_start, object_size), "object" 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<llvm::RuntimeDyld>(new llvm::RuntimeDyld(*memory_manager, symbol_resolver));
SymbolLookup symbol_resolver(callbacks); runtime_dyld->setProcessAllSections(true);
runtime_dyld = std::unique_ptr<llvm::RuntimeDyld>(new llvm::RuntimeDyld(*memory_manager, symbol_resolver));
runtime_dyld->setProcessAllSections(true); runtime_dyld->loadObject(*object_file);
runtime_dyld->finalizeWithMemoryManagerLocking();
runtime_dyld->loadObject(*object_file); if (runtime_dyld->hasError()) {
runtime_dyld->finalizeWithMemoryManagerLocking(); _init_failed = true;
return;
if (runtime_dyld->hasError()) { }
std::cout << "RuntimeDyld error: " << (std::string)runtime_dyld->getErrorString() << std::endl; } else {
abort(); _init_failed = true;
} }
} }

View File

@ -152,6 +152,7 @@ struct WasmModule
void *get_func(llvm::StringRef name) const; void *get_func(llvm::StringRef name) const;
bool _init_failed = false;
private: private:
std::unique_ptr<llvm::RuntimeDyld::MemoryManager> memory_manager; std::unique_ptr<llvm::RuntimeDyld::MemoryManager> memory_manager;
std::unique_ptr<llvm::object::ObjectFile> object_file; std::unique_ptr<llvm::object::ObjectFile> object_file;
@ -164,6 +165,10 @@ extern "C"
{ {
*module_out = new WasmModule(mem_ptr, mem_size, callbacks); *module_out = new WasmModule(mem_ptr, mem_size, callbacks);
if ((*module_out)->_init_failed) {
return RESULT_OBJECT_LOAD_FAILURE;
}
return RESULT_OK; return RESULT_OK;
} }

View File

@ -13,12 +13,17 @@ use std::{
any::Any, any::Any,
ffi::{c_void, CString}, ffi::{c_void, CString},
mem, mem,
ops::Deref,
ptr::{self, NonNull}, ptr::{self, NonNull},
slice, str, slice, str,
sync::Once, sync::{Arc, Once},
}; };
use wasmer_runtime_core::{ use wasmer_runtime_core::{
backend::RunnableModule, backend::{
sys::{Memory, Protect},
CacheGen, RunnableModule,
},
cache::Error as CacheError,
module::ModuleInfo, module::ModuleInfo,
structures::TypedIndex, structures::TypedIndex,
typed_func::{Wasm, WasmTrapInfo}, 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 Send for LLVMBackend {}
unsafe impl Sync for LLVMBackend {} unsafe impl Sync for LLVMBackend {}
pub struct LLVMBackend { pub struct LLVMBackend {
module: *mut LLVMModule, module: *mut LLVMModule,
#[allow(dead_code)] #[allow(dead_code)]
memory_buffer: MemoryBuffer, buffer: Arc<Buffer>,
} }
impl LLVMBackend { impl LLVMBackend {
pub fn new(module: Module, _intrinsics: Intrinsics) -> Self { pub fn new(module: Module, _intrinsics: Intrinsics) -> (Self, LLVMCache) {
Target::initialize_x86(&InitializationConfig { Target::initialize_x86(&InitializationConfig {
asm_parser: true, asm_parser: true,
asm_printer: true, asm_printer: true,
@ -262,10 +282,44 @@ impl LLVMBackend {
panic!("failed to load object") panic!("failed to load object")
} }
Self { let buffer = Arc::new(Buffer::LlvmMemory(memory_buffer));
module,
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<Buffer>,
}
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")] #[cfg(feature = "disasm")]
unsafe fn disass_ptr(ptr: *const u8, size: usize, inst_count: usize) { unsafe fn disass_ptr(ptr: *const u8, size: usize, inst_count: usize) {
use capstone::arch::BuildsCapstone; use capstone::arch::BuildsCapstone;

View File

@ -38,38 +38,47 @@ impl Compiler for LLVMCompiler {
let (info, code_reader) = read_info::read_module(wasm, compiler_config).unwrap(); let (info, code_reader) = read_info::read_module(wasm, compiler_config).unwrap();
let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).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. // Create placeholder values here.
let cache_gen = { // let cache_gen = {
use wasmer_runtime_core::backend::{sys::Memory, CacheGen}; // use wasmer_runtime_core::backend::{sys::Memory, CacheGen};
use wasmer_runtime_core::cache::Error as CacheError; // use wasmer_runtime_core::cache::Error as CacheError;
use wasmer_runtime_core::module::ModuleInfo; // use wasmer_runtime_core::module::ModuleInfo;
struct Placeholder; // struct Placeholder;
impl CacheGen for Placeholder { // impl CacheGen for Placeholder {
fn generate_cache( // fn generate_cache(
&self, // &self,
_module: &ModuleInner, // _module: &ModuleInner,
) -> Result<(Box<ModuleInfo>, Box<[u8]>, Memory), CacheError> { // ) -> Result<(Box<ModuleInfo>, Box<[u8]>, Memory), CacheError> {
unimplemented!() // unimplemented!()
} // }
} // }
Box::new(Placeholder) // Box::new(Placeholder)
}; // };
Ok(ModuleInner { Ok(ModuleInner {
runnable_module: Box::new(backend), runnable_module: Box::new(backend),
cache_gen, cache_gen: Box::new(cache_gen),
info, info,
}) })
} }
unsafe fn from_cache(&self, _artifact: Artifact, _: Token) -> Result<ModuleInner, CacheError> { unsafe fn from_cache(&self, artifact: Artifact, _: Token) -> Result<ModuleInner, CacheError> {
unimplemented!("the llvm backend doesn't support caching yet") 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: Box::new(cache_gen),
info,
})
} }
} }

View File

@ -83,8 +83,5 @@ pub trait RunnableModule: Send + Sync {
} }
pub trait CacheGen: Send + Sync { pub trait CacheGen: Send + Sync {
fn generate_cache( fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError>;
&self,
module: &ModuleInner,
) -> Result<(Box<ModuleInfo>, Box<[u8]>, Memory), CacheError>;
} }

View File

@ -121,8 +121,12 @@ impl Module {
} }
pub fn cache(&self) -> Result<Artifact, CacheError> { pub fn cache(&self) -> Result<Artifact, CacheError> {
let (info, backend_metadata, code) = self.inner.cache_gen.generate_cache(&self.inner)?; let (backend_metadata, code) = self.inner.cache_gen.generate_cache()?;
Ok(Artifact::from_parts(info, backend_metadata, code)) Ok(Artifact::from_parts(
Box::new(self.inner.info.clone()),
backend_metadata,
code,
))
} }
pub fn info(&self) -> &ModuleInfo { pub fn info(&self) -> &ModuleInfo {

View File

@ -7,8 +7,6 @@ static WAT: &'static str = r#"
(type (;0;) (func (result i32))) (type (;0;) (func (result i32)))
(import "env" "do_panic" (func $do_panic (type 0))) (import "env" "do_panic" (func $do_panic (type 0)))
(func $dbz (result i32) (func $dbz (result i32)
call $do_panic
drop
i32.const 42 i32.const 42
i32.const 0 i32.const 0
i32.div_u i32.div_u

View File

@ -36,10 +36,7 @@ use wasmer_runtime_core::{
struct Placeholder; struct Placeholder;
impl CacheGen for Placeholder { impl CacheGen for Placeholder {
fn generate_cache( fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> {
&self,
_module: &ModuleInner,
) -> Result<(Box<ModuleInfo>, Box<[u8]>, Memory), CacheError> {
Err(CacheError::Unknown( Err(CacheError::Unknown(
"the singlepass backend doesn't support caching yet".to_string(), "the singlepass backend doesn't support caching yet".to_string(),
)) ))