mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-25 22:51:32 +00:00
Add caching. (#134)
* Allow a module to have a different signature registry than the process-specific * Add core ability to build compiled code caches * Remove timing printouts * Serialize/Deserialize memories to reduce copies * Work more on api * Relocate local functions relatively before external functions * Fix incorrect definition in test * merge errors caused by merge * Fix emscripten compile * Fix review comments
This commit is contained in:
46
lib/clif-backend/src/cache.rs
Normal file
46
lib/clif-backend/src/cache.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use crate::relocation::{ExternalRelocation, TrapSink};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use wasmer_runtime_core::{
|
||||
backend::sys::Memory,
|
||||
cache::{Cache, Error},
|
||||
module::ModuleInfo,
|
||||
structures::Map,
|
||||
types::{LocalFuncIndex, SigIndex},
|
||||
};
|
||||
|
||||
use serde_bench::{deserialize, serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct TrampolineCache {
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub code: Vec<u8>,
|
||||
pub offsets: HashMap<SigIndex, usize>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct BackendCache {
|
||||
pub external_relocs: Map<LocalFuncIndex, Box<[ExternalRelocation]>>,
|
||||
pub offsets: Map<LocalFuncIndex, usize>,
|
||||
pub trap_sink: TrapSink,
|
||||
pub trampolines: TrampolineCache,
|
||||
}
|
||||
|
||||
impl BackendCache {
|
||||
pub fn from_cache(cache: Cache) -> Result<(ModuleInfo, Memory, Self), Error> {
|
||||
let (info, backend_data, compiled_code) = cache.consume();
|
||||
|
||||
let backend_cache = deserialize(backend_data.as_slice())
|
||||
.map_err(|e| Error::DeserializeError(e.to_string()))?;
|
||||
|
||||
Ok((info, compiled_code, backend_cache))
|
||||
}
|
||||
|
||||
pub fn into_backend_data(self) -> Result<Vec<u8>, Error> {
|
||||
let mut buffer = Vec::new();
|
||||
|
||||
serialize(&mut buffer, &self).map_err(|e| Error::SerializeError(e.to_string()))?;
|
||||
|
||||
Ok(buffer)
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ use wasmer_runtime_core::{
|
||||
backend::{ProtectedCaller, Token},
|
||||
error::RuntimeResult,
|
||||
export::Context,
|
||||
module::{ExportIndex, ModuleInner},
|
||||
module::{ExportIndex, ModuleInfo, ModuleInner},
|
||||
types::{FuncIndex, FuncSig, LocalOrImport, SigIndex, Type, Value},
|
||||
vm::{self, ImportBacking},
|
||||
};
|
||||
@ -23,7 +23,7 @@ pub struct Caller {
|
||||
}
|
||||
|
||||
impl Caller {
|
||||
pub fn new(module: &ModuleInner, handler_data: HandlerData, trampolines: Trampolines) -> Self {
|
||||
pub fn new(module: &ModuleInfo, handler_data: HandlerData, trampolines: Trampolines) -> Self {
|
||||
let mut func_export_set = HashSet::new();
|
||||
for export_index in module.exports.values() {
|
||||
if let ExportIndex::Func(func_index) = export_index {
|
||||
@ -118,6 +118,7 @@ fn get_func_from_index(
|
||||
func_index: FuncIndex,
|
||||
) -> (*const vm::Func, Context, Arc<FuncSig>, SigIndex) {
|
||||
let sig_index = *module
|
||||
.info
|
||||
.func_assoc
|
||||
.get(func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
@ -141,7 +142,7 @@ fn get_func_from_index(
|
||||
}
|
||||
};
|
||||
|
||||
let signature = module.sig_registry.lookup_signature(sig_index);
|
||||
let signature = Arc::clone(&module.info.signatures[sig_index]);
|
||||
|
||||
(func_ptr, ctx, signature, sig_index)
|
||||
}
|
||||
|
@ -5,8 +5,7 @@
|
||||
//! unless you have memory unsafety elsewhere in your code.
|
||||
|
||||
use crate::call::sighandler::install_sighandler;
|
||||
use crate::relocation::{TrapData, TrapSink};
|
||||
use cranelift_codegen::ir::TrapCode;
|
||||
use crate::relocation::{TrapCode, TrapData, TrapSink};
|
||||
use nix::libc::{c_void, siginfo_t};
|
||||
use nix::sys::signal::{Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV};
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
@ -36,7 +35,7 @@ unsafe impl Send for HandlerData {}
|
||||
unsafe impl Sync for HandlerData {}
|
||||
|
||||
pub struct HandlerData {
|
||||
trap_data: TrapSink,
|
||||
pub trap_data: TrapSink,
|
||||
buffer_ptr: *const c_void,
|
||||
buffer_size: usize,
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ use cranelift_codegen::{
|
||||
ir::{self, InstBuilder},
|
||||
isa,
|
||||
};
|
||||
use cranelift_entity::EntityRef;
|
||||
use cranelift_wasm::{self, FuncEnvironment, ModuleEnvironment};
|
||||
use std::mem;
|
||||
use wasmer_runtime_core::{
|
||||
@ -162,7 +163,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
|
||||
offset: (local_memory_ptr_offset as i64).into(),
|
||||
global_type: ptr_type,
|
||||
}),
|
||||
self.env.module.memories[local_mem_index],
|
||||
self.env.module.info.memories[local_mem_index],
|
||||
)
|
||||
}
|
||||
LocalOrImport::Import(import_mem_index) => {
|
||||
@ -182,7 +183,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
|
||||
offset: (local_memory_ptr_offset as i64).into(),
|
||||
global_type: ptr_type,
|
||||
}),
|
||||
self.env.module.imported_memories[import_mem_index].1,
|
||||
self.env.module.info.imported_memories[import_mem_index].1,
|
||||
)
|
||||
}
|
||||
};
|
||||
@ -273,7 +274,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
|
||||
|
||||
(
|
||||
table_struct_ptr_ptr,
|
||||
self.env.module.tables[local_table_index],
|
||||
self.env.module.info.tables[local_table_index],
|
||||
)
|
||||
}
|
||||
LocalOrImport::Import(import_table_index) => {
|
||||
@ -295,7 +296,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
|
||||
|
||||
(
|
||||
table_struct_ptr_ptr,
|
||||
self.env.module.imported_tables[import_table_index].1,
|
||||
self.env.module.info.imported_tables[import_table_index].1,
|
||||
)
|
||||
}
|
||||
};
|
||||
@ -367,7 +368,8 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
|
||||
func.import_function(ir::ExtFuncData {
|
||||
name,
|
||||
signature,
|
||||
colocated: false,
|
||||
// Make this colocated so all calls between local functions are relative.
|
||||
colocated: true,
|
||||
})
|
||||
}
|
||||
|
||||
@ -428,9 +430,24 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
|
||||
|
||||
pos.ins().trapz(func_ptr, ir::TrapCode::IndirectCallToNull);
|
||||
|
||||
let sig_index = self.env.deduplicated[clif_sig_index];
|
||||
let expected_sig = {
|
||||
let sig_index_global = pos.func.create_global_value(ir::GlobalValueData::Symbol {
|
||||
// The index of the `ExternalName` is the undeduplicated, signature index.
|
||||
name: ir::ExternalName::user(
|
||||
call_names::SIG_NAMESPACE,
|
||||
clif_sig_index.index() as u32,
|
||||
),
|
||||
offset: 0.into(),
|
||||
colocated: false,
|
||||
});
|
||||
|
||||
pos.ins().symbol_value(ir::types::I64, sig_index_global)
|
||||
|
||||
// let expected_sig = pos.ins().iconst(ir::types::I32, sig_index.index() as i64);
|
||||
|
||||
// self.env.deduplicated[clif_sig_index]
|
||||
};
|
||||
|
||||
let expected_sig = pos.ins().iconst(ir::types::I32, sig_index.index() as i64);
|
||||
let not_equal_flags = pos.ins().ifcmp(found_sig, expected_sig);
|
||||
|
||||
pos.ins().trapif(
|
||||
@ -555,12 +572,12 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
|
||||
LocalOrImport::Local(local_mem_index) => (
|
||||
call_names::LOCAL_NAMESPACE,
|
||||
local_mem_index.index(),
|
||||
self.env.module.memories[local_mem_index],
|
||||
self.env.module.info.memories[local_mem_index],
|
||||
),
|
||||
LocalOrImport::Import(import_mem_index) => (
|
||||
call_names::IMPORT_NAMESPACE,
|
||||
import_mem_index.index(),
|
||||
self.env.module.imported_memories[import_mem_index].1,
|
||||
self.env.module.info.imported_memories[import_mem_index].1,
|
||||
),
|
||||
};
|
||||
|
||||
@ -618,12 +635,12 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
|
||||
LocalOrImport::Local(local_mem_index) => (
|
||||
call_names::LOCAL_NAMESPACE,
|
||||
local_mem_index.index(),
|
||||
self.env.module.memories[local_mem_index],
|
||||
self.env.module.info.memories[local_mem_index],
|
||||
),
|
||||
LocalOrImport::Import(import_mem_index) => (
|
||||
call_names::IMPORT_NAMESPACE,
|
||||
import_mem_index.index(),
|
||||
self.env.module.imported_memories[import_mem_index].1,
|
||||
self.env.module.info.imported_memories[import_mem_index].1,
|
||||
),
|
||||
};
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
#[cfg(feature = "cache")]
|
||||
mod cache;
|
||||
mod call;
|
||||
mod func_env;
|
||||
mod libcalls;
|
||||
@ -12,11 +14,23 @@ use cranelift_codegen::{
|
||||
settings::{self, Configurable},
|
||||
};
|
||||
use target_lexicon::Triple;
|
||||
#[cfg(feature = "cache")]
|
||||
use wasmer_runtime_core::{
|
||||
backend::sys::Memory,
|
||||
cache::{Cache, Error as CacheError},
|
||||
module::ModuleInfo,
|
||||
};
|
||||
use wasmer_runtime_core::{
|
||||
backend::{Compiler, Token},
|
||||
error::{CompileError, CompileResult},
|
||||
module::ModuleInner,
|
||||
};
|
||||
#[cfg(feature = "cache")]
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[cfg(feature = "cache")]
|
||||
extern crate serde;
|
||||
|
||||
use wasmparser::{self, WasmDecoder};
|
||||
|
||||
pub struct CraneliftCompiler {}
|
||||
@ -28,7 +42,7 @@ impl CraneliftCompiler {
|
||||
}
|
||||
|
||||
impl Compiler for CraneliftCompiler {
|
||||
// Compiles wasm binary to a wasmer module.
|
||||
/// Compiles wasm binary to a wasmer module.
|
||||
fn compile(&self, wasm: &[u8], _: Token) -> CompileResult<ModuleInner> {
|
||||
validate(wasm)?;
|
||||
|
||||
@ -36,10 +50,48 @@ impl Compiler for CraneliftCompiler {
|
||||
|
||||
let mut module = module::Module::empty();
|
||||
let module_env = module_env::ModuleEnv::new(&mut module, &*isa);
|
||||
|
||||
let func_bodies = module_env.translate(wasm)?;
|
||||
|
||||
module.compile(&*isa, func_bodies)
|
||||
}
|
||||
|
||||
/// Create a wasmer Module from an already-compiled cache.
|
||||
#[cfg(feature = "cache")]
|
||||
unsafe fn from_cache(&self, cache: Cache, _: Token) -> Result<ModuleInner, CacheError> {
|
||||
module::Module::from_cache(cache)
|
||||
}
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
fn compile_to_backend_cache_data(
|
||||
&self,
|
||||
wasm: &[u8],
|
||||
_: Token,
|
||||
) -> CompileResult<(Box<ModuleInfo>, Vec<u8>, Memory)> {
|
||||
validate(wasm)?;
|
||||
|
||||
let isa = get_isa();
|
||||
|
||||
let mut module = module::Module::empty();
|
||||
let module_env = module_env::ModuleEnv::new(&mut module, &*isa);
|
||||
|
||||
let func_bodies = module_env.translate(wasm)?;
|
||||
|
||||
let (info, backend_cache, compiled_code) = module
|
||||
.compile_to_backend_cache(&*isa, func_bodies)
|
||||
.map_err(|e| CompileError::InternalError {
|
||||
msg: format!("{:?}", e),
|
||||
})?;
|
||||
|
||||
let buffer =
|
||||
backend_cache
|
||||
.into_backend_data()
|
||||
.map_err(|e| CompileError::InternalError {
|
||||
msg: format!("{:?}", e),
|
||||
})?;
|
||||
|
||||
Ok((Box::new(info), buffer, compiled_code))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_isa() -> Box<isa::TargetIsa> {
|
||||
|
@ -1,4 +1,7 @@
|
||||
#[cfg(feature = "cache")]
|
||||
use crate::cache::BackendCache;
|
||||
use crate::{call::Caller, resolver::FuncResolverBuilder, trampoline::Trampolines};
|
||||
|
||||
use cranelift_codegen::{ir, isa};
|
||||
use cranelift_entity::EntityRef;
|
||||
use cranelift_wasm;
|
||||
@ -7,11 +10,15 @@ use std::{
|
||||
ops::{Deref, DerefMut},
|
||||
ptr::NonNull,
|
||||
};
|
||||
#[cfg(feature = "cache")]
|
||||
use wasmer_runtime_core::{
|
||||
backend::SigRegistry,
|
||||
backend::{FuncResolver, ProtectedCaller, Token},
|
||||
backend::sys::Memory,
|
||||
cache::{Cache, Error as CacheError},
|
||||
};
|
||||
use wasmer_runtime_core::{
|
||||
backend::{Backend, FuncResolver, ProtectedCaller, Token},
|
||||
error::{CompileResult, RuntimeResult},
|
||||
module::ModuleInner,
|
||||
module::{ModuleInfo, ModuleInner, StringTable},
|
||||
structures::{Map, TypedIndex},
|
||||
types::{
|
||||
FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, MemoryIndex, SigIndex, TableIndex, Type,
|
||||
@ -59,24 +66,30 @@ impl Module {
|
||||
func_resolver: Box::new(Placeholder),
|
||||
protected_caller: Box::new(Placeholder),
|
||||
|
||||
memories: Map::new(),
|
||||
globals: Map::new(),
|
||||
tables: Map::new(),
|
||||
info: ModuleInfo {
|
||||
memories: Map::new(),
|
||||
globals: Map::new(),
|
||||
tables: Map::new(),
|
||||
|
||||
imported_functions: Map::new(),
|
||||
imported_memories: Map::new(),
|
||||
imported_tables: Map::new(),
|
||||
imported_globals: Map::new(),
|
||||
imported_functions: Map::new(),
|
||||
imported_memories: Map::new(),
|
||||
imported_tables: Map::new(),
|
||||
imported_globals: Map::new(),
|
||||
|
||||
exports: HashMap::new(),
|
||||
exports: HashMap::new(),
|
||||
|
||||
data_initializers: Vec::new(),
|
||||
elem_initializers: Vec::new(),
|
||||
data_initializers: Vec::new(),
|
||||
elem_initializers: Vec::new(),
|
||||
|
||||
start_func: None,
|
||||
start_func: None,
|
||||
|
||||
func_assoc: Map::new(),
|
||||
sig_registry: SigRegistry,
|
||||
func_assoc: Map::new(),
|
||||
signatures: Map::new(),
|
||||
backend: Backend::Cranelift,
|
||||
|
||||
namespace_table: StringTable::new(),
|
||||
name_table: StringTable::new(),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -86,19 +99,60 @@ impl Module {
|
||||
isa: &isa::TargetIsa,
|
||||
functions: Map<LocalFuncIndex, ir::Function>,
|
||||
) -> CompileResult<ModuleInner> {
|
||||
let imported_functions_len = self.module.imported_functions.len();
|
||||
let (func_resolver_builder, handler_data) =
|
||||
FuncResolverBuilder::new(isa, functions, imported_functions_len)?;
|
||||
FuncResolverBuilder::new(isa, functions, &self.module.info)?;
|
||||
|
||||
self.module.func_resolver = Box::new(func_resolver_builder.finalize()?);
|
||||
self.module.func_resolver =
|
||||
Box::new(func_resolver_builder.finalize(&self.module.info.signatures)?);
|
||||
|
||||
let trampolines = Trampolines::new(isa, &self.module);
|
||||
let trampolines = Trampolines::new(isa, &self.module.info);
|
||||
|
||||
self.module.protected_caller =
|
||||
Box::new(Caller::new(&self.module, handler_data, trampolines));
|
||||
Box::new(Caller::new(&self.module.info, handler_data, trampolines));
|
||||
|
||||
Ok(self.module)
|
||||
}
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
pub fn compile_to_backend_cache(
|
||||
self,
|
||||
isa: &isa::TargetIsa,
|
||||
functions: Map<LocalFuncIndex, ir::Function>,
|
||||
) -> CompileResult<(ModuleInfo, BackendCache, Memory)> {
|
||||
let (func_resolver_builder, handler_data) =
|
||||
FuncResolverBuilder::new(isa, functions, &self.module.info)?;
|
||||
|
||||
let trampolines = Trampolines::new(isa, &self.module.info);
|
||||
|
||||
let trampoline_cache = trampolines.to_trampoline_cache();
|
||||
|
||||
let (backend_cache, compiled_code) =
|
||||
func_resolver_builder.to_backend_cache(trampoline_cache, handler_data);
|
||||
|
||||
Ok((self.module.info, backend_cache, compiled_code))
|
||||
}
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
pub fn from_cache(cache: Cache) -> Result<ModuleInner, CacheError> {
|
||||
let (info, compiled_code, backend_cache) = BackendCache::from_cache(cache)?;
|
||||
|
||||
let (func_resolver_builder, trampolines, handler_data) =
|
||||
FuncResolverBuilder::new_from_backend_cache(backend_cache, compiled_code, &info)?;
|
||||
|
||||
let func_resolver = Box::new(
|
||||
func_resolver_builder
|
||||
.finalize(&info.signatures)
|
||||
.map_err(|e| CacheError::Unknown(format!("{:?}", e)))?,
|
||||
);
|
||||
|
||||
let protected_caller = Box::new(Caller::new(&info, handler_data, trampolines));
|
||||
|
||||
Ok(ModuleInner {
|
||||
func_resolver,
|
||||
protected_caller,
|
||||
info,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Module {
|
||||
|
@ -3,16 +3,18 @@ use crate::{
|
||||
module::{Converter, Module},
|
||||
};
|
||||
use cranelift_codegen::{ir, isa};
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use cranelift_wasm::{self, translate_module, FuncTranslator, ModuleEnvironment};
|
||||
use hashbrown::HashMap;
|
||||
use std::sync::Arc;
|
||||
use wasmer_runtime_core::{
|
||||
error::{CompileError, CompileResult},
|
||||
module::{DataInitializer, ExportIndex, ImportName, TableInitializer},
|
||||
module::{
|
||||
DataInitializer, ExportIndex, ImportName, NameIndex, NamespaceIndex, StringTableBuilder,
|
||||
TableInitializer,
|
||||
},
|
||||
structures::{Map, TypedIndex},
|
||||
types::{
|
||||
ElementType, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, Initializer,
|
||||
LocalFuncIndex, LocalOrImport, MemoryDescriptor, SigIndex, TableDescriptor, Value,
|
||||
ElementType, GlobalDescriptor, GlobalIndex, GlobalInit, Initializer, LocalFuncIndex,
|
||||
LocalOrImport, MemoryDescriptor, SigIndex, TableDescriptor, Value,
|
||||
},
|
||||
units::Pages,
|
||||
};
|
||||
@ -23,8 +25,8 @@ pub struct ModuleEnv<'module, 'isa> {
|
||||
pub signatures: Map<SigIndex, ir::Signature>,
|
||||
globals: Map<GlobalIndex, cranelift_wasm::Global>,
|
||||
func_bodies: Map<LocalFuncIndex, ir::Function>,
|
||||
pub deduplicated: PrimaryMap<cranelift_wasm::SignatureIndex, SigIndex>,
|
||||
duplicated: HashMap<SigIndex, cranelift_wasm::SignatureIndex>,
|
||||
namespace_table_builder: StringTableBuilder<NamespaceIndex>,
|
||||
name_table_builder: StringTableBuilder<NameIndex>,
|
||||
}
|
||||
|
||||
impl<'module, 'isa> ModuleEnv<'module, 'isa> {
|
||||
@ -35,14 +37,18 @@ impl<'module, 'isa> ModuleEnv<'module, 'isa> {
|
||||
signatures: Map::new(),
|
||||
globals: Map::new(),
|
||||
func_bodies: Map::new(),
|
||||
deduplicated: PrimaryMap::new(),
|
||||
duplicated: HashMap::new(),
|
||||
namespace_table_builder: StringTableBuilder::new(),
|
||||
name_table_builder: StringTableBuilder::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn translate(mut self, wasm: &[u8]) -> CompileResult<Map<LocalFuncIndex, ir::Function>> {
|
||||
translate_module(wasm, &mut self)
|
||||
.map_err(|e| CompileError::InternalError { msg: e.to_string() })?;
|
||||
|
||||
self.module.info.namespace_table = self.namespace_table_builder.finish();
|
||||
self.module.info.name_table = self.name_table_builder.finish();
|
||||
|
||||
Ok(self.func_bodies)
|
||||
}
|
||||
}
|
||||
@ -55,12 +61,11 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
|
||||
/// Declares a function signature to the environment.
|
||||
fn declare_signature(&mut self, sig: &ir::Signature) {
|
||||
let clif_sig_index = self.signatures.push(sig.clone());
|
||||
let func_sig: FuncSig = Converter(sig).into();
|
||||
let sig_index = self.module.sig_registry.lookup_sig_index(func_sig);
|
||||
self.deduplicated.push(sig_index);
|
||||
self.duplicated
|
||||
.insert(sig_index, Converter(clif_sig_index).into());
|
||||
self.signatures.push(sig.clone());
|
||||
self.module
|
||||
.info
|
||||
.signatures
|
||||
.push(Arc::new(Converter(sig).into()));
|
||||
}
|
||||
|
||||
/// Return the signature with the given index.
|
||||
@ -75,25 +80,34 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
namespace: &'data str,
|
||||
name: &'data str,
|
||||
) {
|
||||
let sig_index = self.deduplicated[clif_sig_index];
|
||||
self.module.func_assoc.push(sig_index);
|
||||
// We convert the cranelift signature index to
|
||||
// a wasmer signature index without deduplicating
|
||||
// because we'll deduplicate later.
|
||||
let sig_index = Converter(clif_sig_index).into();
|
||||
self.module.info.func_assoc.push(sig_index);
|
||||
|
||||
let namespace_index = self.namespace_table_builder.register(namespace);
|
||||
let name_index = self.name_table_builder.register(name);
|
||||
|
||||
// Add import names to list of imported functions
|
||||
self.module.imported_functions.push(ImportName {
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
self.module.info.imported_functions.push(ImportName {
|
||||
namespace_index,
|
||||
name_index,
|
||||
});
|
||||
}
|
||||
|
||||
/// Return the number of imported funcs.
|
||||
fn get_num_func_imports(&self) -> usize {
|
||||
self.module.imported_functions.len()
|
||||
self.module.info.imported_functions.len()
|
||||
}
|
||||
|
||||
/// Declares the type (signature) of a local function in the module.
|
||||
fn declare_func_type(&mut self, clif_sig_index: cranelift_wasm::SignatureIndex) {
|
||||
let sig_index = self.deduplicated[clif_sig_index];
|
||||
self.module.func_assoc.push(sig_index);
|
||||
// We convert the cranelift signature index to
|
||||
// a wasmer signature index without deduplicating
|
||||
// because we'll deduplicate later.
|
||||
let sig_index = Converter(clif_sig_index).into();
|
||||
self.module.info.func_assoc.push(sig_index);
|
||||
}
|
||||
|
||||
/// Return the signature index for the given function index.
|
||||
@ -101,8 +115,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
&self,
|
||||
func_index: cranelift_wasm::FuncIndex,
|
||||
) -> cranelift_wasm::SignatureIndex {
|
||||
let sig_index: SigIndex = self.module.func_assoc[Converter(func_index).into()];
|
||||
self.duplicated[&sig_index]
|
||||
let sig_index: SigIndex = self.module.info.func_assoc[Converter(func_index).into()];
|
||||
Converter(sig_index).into()
|
||||
}
|
||||
|
||||
/// Declares a global to the environment.
|
||||
@ -134,7 +148,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
};
|
||||
|
||||
// Add global ir to the list of globals
|
||||
self.module.globals.push(GlobalInit { desc, init });
|
||||
self.module.info.globals.push(GlobalInit { desc, init });
|
||||
|
||||
self.globals.push(global);
|
||||
}
|
||||
@ -151,9 +165,12 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
_ => false,
|
||||
});
|
||||
|
||||
let namespace_index = self.namespace_table_builder.register(namespace);
|
||||
let name_index = self.name_table_builder.register(name);
|
||||
|
||||
let import_name = ImportName {
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
namespace_index,
|
||||
name_index,
|
||||
};
|
||||
|
||||
let desc = GlobalDescriptor {
|
||||
@ -162,7 +179,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
};
|
||||
|
||||
// Add global ir to the list of globals
|
||||
self.module.imported_globals.push((import_name, desc));
|
||||
self.module.info.imported_globals.push((import_name, desc));
|
||||
|
||||
self.globals.push(global);
|
||||
}
|
||||
@ -176,7 +193,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
fn declare_table(&mut self, table: cranelift_wasm::Table) {
|
||||
use cranelift_wasm::TableElementType;
|
||||
// Add table ir to the list of tables
|
||||
self.module.tables.push(TableDescriptor {
|
||||
self.module.info.tables.push(TableDescriptor {
|
||||
element: match table.ty {
|
||||
TableElementType::Func => ElementType::Anyfunc,
|
||||
_ => unimplemented!(),
|
||||
@ -195,9 +212,12 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
) {
|
||||
use cranelift_wasm::TableElementType;
|
||||
|
||||
let namespace_index = self.namespace_table_builder.register(namespace);
|
||||
let name_index = self.name_table_builder.register(name);
|
||||
|
||||
let import_name = ImportName {
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
namespace_index,
|
||||
name_index,
|
||||
};
|
||||
|
||||
let imported_table = TableDescriptor {
|
||||
@ -211,6 +231,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
|
||||
// Add import names to list of imported tables
|
||||
self.module
|
||||
.info
|
||||
.imported_tables
|
||||
.push((import_name, imported_table));
|
||||
}
|
||||
@ -239,7 +260,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
};
|
||||
|
||||
// Add table initializer to list of table initializers
|
||||
self.module.elem_initializers.push(TableInitializer {
|
||||
self.module.info.elem_initializers.push(TableInitializer {
|
||||
table_index: Converter(table_index).into(),
|
||||
base,
|
||||
elements: elements
|
||||
@ -251,7 +272,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
|
||||
/// Declares a memory to the environment
|
||||
fn declare_memory(&mut self, memory: cranelift_wasm::Memory) {
|
||||
self.module.memories.push(MemoryDescriptor {
|
||||
self.module.info.memories.push(MemoryDescriptor {
|
||||
minimum: Pages(memory.minimum),
|
||||
maximum: memory.maximum.map(|max| Pages(max)),
|
||||
shared: memory.shared,
|
||||
@ -265,9 +286,12 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
namespace: &'data str,
|
||||
name: &'data str,
|
||||
) {
|
||||
let namespace_index = self.namespace_table_builder.register(namespace);
|
||||
let name_index = self.name_table_builder.register(name);
|
||||
|
||||
let import_name = ImportName {
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
namespace_index,
|
||||
name_index,
|
||||
};
|
||||
|
||||
let memory = MemoryDescriptor {
|
||||
@ -277,7 +301,10 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
};
|
||||
|
||||
// Add import names to list of imported memories
|
||||
self.module.imported_memories.push((import_name, memory));
|
||||
self.module
|
||||
.info
|
||||
.imported_memories
|
||||
.push((import_name, memory));
|
||||
}
|
||||
|
||||
/// Fills a declared memory with bytes at module instantiation.
|
||||
@ -303,7 +330,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
};
|
||||
|
||||
// Add data initializer to list of data initializers
|
||||
self.module.data_initializers.push(DataInitializer {
|
||||
self.module.info.data_initializers.push(DataInitializer {
|
||||
memory_index: Converter(memory_index).into(),
|
||||
base,
|
||||
data: data.to_vec(),
|
||||
@ -312,14 +339,14 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
|
||||
/// Declares a function export to the environment.
|
||||
fn declare_func_export(&mut self, func_index: cranelift_wasm::FuncIndex, name: &'data str) {
|
||||
self.module.exports.insert(
|
||||
self.module.info.exports.insert(
|
||||
name.to_string(),
|
||||
ExportIndex::Func(Converter(func_index).into()),
|
||||
);
|
||||
}
|
||||
/// Declares a table export to the environment.
|
||||
fn declare_table_export(&mut self, table_index: cranelift_wasm::TableIndex, name: &'data str) {
|
||||
self.module.exports.insert(
|
||||
self.module.info.exports.insert(
|
||||
name.to_string(),
|
||||
ExportIndex::Table(Converter(table_index).into()),
|
||||
);
|
||||
@ -330,7 +357,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
memory_index: cranelift_wasm::MemoryIndex,
|
||||
name: &'data str,
|
||||
) {
|
||||
self.module.exports.insert(
|
||||
self.module.info.exports.insert(
|
||||
name.to_string(),
|
||||
ExportIndex::Memory(Converter(memory_index).into()),
|
||||
);
|
||||
@ -341,7 +368,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
global_index: cranelift_wasm::GlobalIndex,
|
||||
name: &'data str,
|
||||
) {
|
||||
self.module.exports.insert(
|
||||
self.module.info.exports.insert(
|
||||
name.to_string(),
|
||||
ExportIndex::Global(Converter(global_index).into()),
|
||||
);
|
||||
@ -349,7 +376,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
|
||||
/// Declares a start function.
|
||||
fn declare_start_func(&mut self, func_index: cranelift_wasm::FuncIndex) {
|
||||
self.module.start_func = Some(Converter(func_index).into());
|
||||
self.module.info.start_func = Some(Converter(func_index).into());
|
||||
}
|
||||
|
||||
/// Provides the contents of a function body.
|
||||
|
@ -3,14 +3,16 @@
|
||||
//! any other calls that this function is doing, so we can "patch" the
|
||||
//! function addrs in runtime with the functions we need.
|
||||
use cranelift_codegen::binemit;
|
||||
pub use cranelift_codegen::binemit::Reloc;
|
||||
use cranelift_codegen::ir::{self, ExternalName, LibCall, SourceLoc, TrapCode};
|
||||
use hashbrown::HashMap;
|
||||
use wasmer_runtime_core::{structures::TypedIndex, types::LocalFuncIndex};
|
||||
use cranelift_codegen::ir::{self, ExternalName, SourceLoc};
|
||||
use wasmer_runtime_core::{
|
||||
structures::TypedIndex,
|
||||
types::{FuncIndex, SigIndex},
|
||||
};
|
||||
|
||||
pub mod call_names {
|
||||
pub const LOCAL_NAMESPACE: u32 = 1;
|
||||
pub const IMPORT_NAMESPACE: u32 = 2;
|
||||
pub const SIG_NAMESPACE: u32 = 3;
|
||||
|
||||
pub const STATIC_MEM_GROW: u32 = 0;
|
||||
pub const STATIC_MEM_SIZE: u32 = 1;
|
||||
@ -20,10 +22,33 @@ pub mod call_names {
|
||||
pub const DYNAMIC_MEM_SIZE: u32 = 5;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Reloc {
|
||||
Abs8,
|
||||
X86PCRel4,
|
||||
X86CallPCRel4,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum LibCall {
|
||||
Probestack,
|
||||
CeilF32,
|
||||
CeilF64,
|
||||
FloorF32,
|
||||
FloorF64,
|
||||
TruncF32,
|
||||
TruncF64,
|
||||
NearestF32,
|
||||
NearestF64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Relocation {
|
||||
pub struct ExternalRelocation {
|
||||
/// The relocation code.
|
||||
pub reloc: binemit::Reloc,
|
||||
pub reloc: Reloc,
|
||||
/// The offset where to apply the relocation.
|
||||
pub offset: binemit::CodeOffset,
|
||||
/// The addend to add to the relocation value.
|
||||
@ -32,6 +57,16 @@ pub struct Relocation {
|
||||
pub target: RelocationType,
|
||||
}
|
||||
|
||||
pub struct LocalRelocation {
|
||||
/// The offset where to apply the relocation.
|
||||
pub offset: binemit::CodeOffset,
|
||||
/// The addend to add to the relocation value.
|
||||
pub addend: binemit::Addend,
|
||||
/// Relocation type.
|
||||
pub target: FuncIndex,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum VmCallKind {
|
||||
StaticMemoryGrow,
|
||||
@ -44,6 +79,7 @@ pub enum VmCallKind {
|
||||
DynamicMemorySize,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum VmCall {
|
||||
Local(VmCallKind),
|
||||
@ -51,18 +87,20 @@ pub enum VmCall {
|
||||
}
|
||||
|
||||
/// Specify the type of relocation
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RelocationType {
|
||||
Normal(LocalFuncIndex),
|
||||
Intrinsic(String),
|
||||
LibCall(LibCall),
|
||||
VmCall(VmCall),
|
||||
Signature(SigIndex),
|
||||
}
|
||||
|
||||
/// Implementation of a relocation sink that just saves all the information for later
|
||||
pub struct RelocSink {
|
||||
/// Relocations recorded for the function.
|
||||
pub func_relocs: Vec<Relocation>,
|
||||
pub external_relocs: Vec<ExternalRelocation>,
|
||||
pub local_relocs: Vec<LocalRelocation>,
|
||||
}
|
||||
|
||||
impl binemit::RelocSink for RelocSink {
|
||||
@ -82,22 +120,30 @@ impl binemit::RelocSink for RelocSink {
|
||||
name: &ExternalName,
|
||||
addend: binemit::Addend,
|
||||
) {
|
||||
let reloc = match reloc {
|
||||
binemit::Reloc::Abs8 => Reloc::Abs8,
|
||||
binemit::Reloc::X86PCRel4 => Reloc::X86PCRel4,
|
||||
binemit::Reloc::X86CallPCRel4 => Reloc::X86CallPCRel4,
|
||||
_ => unimplemented!("unimplented reloc type: {}", reloc),
|
||||
};
|
||||
|
||||
match *name {
|
||||
ExternalName::User {
|
||||
namespace: 0,
|
||||
index,
|
||||
} => {
|
||||
self.func_relocs.push(Relocation {
|
||||
reloc,
|
||||
assert_eq!(reloc, Reloc::X86CallPCRel4);
|
||||
self.local_relocs.push(LocalRelocation {
|
||||
offset,
|
||||
addend,
|
||||
target: RelocationType::Normal(LocalFuncIndex::new(index as usize)),
|
||||
target: FuncIndex::new(index as usize),
|
||||
});
|
||||
}
|
||||
ExternalName::User { namespace, index } => {
|
||||
use self::call_names::*;
|
||||
let target = RelocationType::VmCall(match namespace {
|
||||
LOCAL_NAMESPACE => VmCall::Local(match index {
|
||||
|
||||
let target = match namespace {
|
||||
LOCAL_NAMESPACE => RelocationType::VmCall(VmCall::Local(match index {
|
||||
STATIC_MEM_GROW => VmCallKind::StaticMemoryGrow,
|
||||
STATIC_MEM_SIZE => VmCallKind::StaticMemorySize,
|
||||
|
||||
@ -107,8 +153,8 @@ impl binemit::RelocSink for RelocSink {
|
||||
DYNAMIC_MEM_GROW => VmCallKind::DynamicMemoryGrow,
|
||||
DYNAMIC_MEM_SIZE => VmCallKind::DynamicMemorySize,
|
||||
_ => unimplemented!(),
|
||||
}),
|
||||
IMPORT_NAMESPACE => VmCall::Import(match index {
|
||||
})),
|
||||
IMPORT_NAMESPACE => RelocationType::VmCall(VmCall::Import(match index {
|
||||
STATIC_MEM_GROW => VmCallKind::StaticMemoryGrow,
|
||||
STATIC_MEM_SIZE => VmCallKind::StaticMemorySize,
|
||||
|
||||
@ -118,10 +164,11 @@ impl binemit::RelocSink for RelocSink {
|
||||
DYNAMIC_MEM_GROW => VmCallKind::DynamicMemoryGrow,
|
||||
DYNAMIC_MEM_SIZE => VmCallKind::DynamicMemorySize,
|
||||
_ => unimplemented!(),
|
||||
}),
|
||||
})),
|
||||
SIG_NAMESPACE => RelocationType::Signature(SigIndex::new(index as usize)),
|
||||
_ => unimplemented!(),
|
||||
});
|
||||
self.func_relocs.push(Relocation {
|
||||
};
|
||||
self.external_relocs.push(ExternalRelocation {
|
||||
reloc,
|
||||
offset,
|
||||
addend,
|
||||
@ -131,7 +178,7 @@ impl binemit::RelocSink for RelocSink {
|
||||
ExternalName::TestCase { length, ascii } => {
|
||||
let (slice, _) = ascii.split_at(length as usize);
|
||||
let name = String::from_utf8(slice.to_vec()).unwrap();
|
||||
self.func_relocs.push(Relocation {
|
||||
self.external_relocs.push(ExternalRelocation {
|
||||
reloc,
|
||||
offset,
|
||||
addend,
|
||||
@ -139,8 +186,20 @@ impl binemit::RelocSink for RelocSink {
|
||||
});
|
||||
}
|
||||
ExternalName::LibCall(libcall) => {
|
||||
let libcall = match libcall {
|
||||
ir::LibCall::CeilF32 => LibCall::CeilF32,
|
||||
ir::LibCall::FloorF32 => LibCall::FloorF32,
|
||||
ir::LibCall::TruncF32 => LibCall::TruncF32,
|
||||
ir::LibCall::NearestF32 => LibCall::NearestF32,
|
||||
ir::LibCall::CeilF64 => LibCall::CeilF64,
|
||||
ir::LibCall::FloorF64 => LibCall::FloorF64,
|
||||
ir::LibCall::TruncF64 => LibCall::TruncF64,
|
||||
ir::LibCall::NearestF64 => LibCall::NearestF64,
|
||||
ir::LibCall::Probestack => LibCall::Probestack,
|
||||
_ => unimplemented!("unimplemented libcall: {}", libcall),
|
||||
};
|
||||
let relocation_type = RelocationType::LibCall(libcall);
|
||||
self.func_relocs.push(Relocation {
|
||||
self.external_relocs.push(ExternalRelocation {
|
||||
reloc,
|
||||
offset,
|
||||
addend,
|
||||
@ -159,43 +218,64 @@ impl binemit::RelocSink for RelocSink {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum TrapCode {
|
||||
StackOverflow,
|
||||
HeapOutOfBounds,
|
||||
TableOutOfBounds,
|
||||
OutOfBounds,
|
||||
IndirectCallToNull,
|
||||
BadSignature,
|
||||
IntegerOverflow,
|
||||
IntegerDivisionByZero,
|
||||
BadConversionToInteger,
|
||||
Interrupt,
|
||||
User(u16),
|
||||
}
|
||||
|
||||
/// Implementation of a relocation sink that just saves all the information for later
|
||||
impl RelocSink {
|
||||
pub fn new() -> RelocSink {
|
||||
RelocSink {
|
||||
func_relocs: Vec::new(),
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
external_relocs: Vec::new(),
|
||||
local_relocs: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct TrapData {
|
||||
pub trapcode: TrapCode,
|
||||
pub srcloc: SourceLoc,
|
||||
pub srcloc: u32,
|
||||
}
|
||||
|
||||
/// Simple implementation of a TrapSink
|
||||
/// that saves the info for later.
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
pub struct TrapSink {
|
||||
trap_datas: HashMap<usize, TrapData>,
|
||||
trap_datas: Vec<(usize, TrapData)>,
|
||||
}
|
||||
|
||||
impl TrapSink {
|
||||
pub fn new() -> TrapSink {
|
||||
TrapSink {
|
||||
trap_datas: HashMap::new(),
|
||||
trap_datas: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup(&self, offset: usize) -> Option<TrapData> {
|
||||
self.trap_datas.get(&offset).cloned()
|
||||
self.trap_datas.get(offset).map(|(_, trap_data)| *trap_data)
|
||||
}
|
||||
|
||||
pub fn drain_local(&mut self, current_func_offset: usize, local: &mut LocalTrapSink) {
|
||||
local.trap_datas.drain(..).for_each(|(offset, trap_data)| {
|
||||
self.trap_datas
|
||||
.insert(current_func_offset + offset, trap_data);
|
||||
});
|
||||
self.trap_datas.extend(
|
||||
local
|
||||
.trap_datas
|
||||
.drain(..)
|
||||
.map(|(offset, trap_data)| (current_func_offset + offset, trap_data)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,8 +290,27 @@ impl LocalTrapSink {
|
||||
}
|
||||
|
||||
impl binemit::TrapSink for LocalTrapSink {
|
||||
fn trap(&mut self, offset: u32, srcloc: SourceLoc, trapcode: TrapCode) {
|
||||
self.trap_datas
|
||||
.push((offset as usize, TrapData { trapcode, srcloc }));
|
||||
fn trap(&mut self, offset: u32, srcloc: SourceLoc, trapcode: ir::TrapCode) {
|
||||
let trapcode = match trapcode {
|
||||
ir::TrapCode::StackOverflow => TrapCode::StackOverflow,
|
||||
ir::TrapCode::HeapOutOfBounds => TrapCode::HeapOutOfBounds,
|
||||
ir::TrapCode::TableOutOfBounds => TrapCode::TableOutOfBounds,
|
||||
ir::TrapCode::OutOfBounds => TrapCode::OutOfBounds,
|
||||
ir::TrapCode::IndirectCallToNull => TrapCode::IndirectCallToNull,
|
||||
ir::TrapCode::BadSignature => TrapCode::BadSignature,
|
||||
ir::TrapCode::IntegerOverflow => TrapCode::IntegerOverflow,
|
||||
ir::TrapCode::IntegerDivisionByZero => TrapCode::IntegerDivisionByZero,
|
||||
ir::TrapCode::BadConversionToInteger => TrapCode::BadConversionToInteger,
|
||||
ir::TrapCode::Interrupt => TrapCode::Interrupt,
|
||||
ir::TrapCode::User(x) => TrapCode::User(x),
|
||||
};
|
||||
|
||||
self.trap_datas.push((
|
||||
offset as usize,
|
||||
TrapData {
|
||||
trapcode,
|
||||
srcloc: srcloc.bits(),
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +1,104 @@
|
||||
use crate::call::HandlerData;
|
||||
use crate::libcalls;
|
||||
use crate::relocation::{
|
||||
LocalTrapSink, Reloc, RelocSink, Relocation, RelocationType, TrapSink, VmCall, VmCallKind,
|
||||
#[cfg(feature = "cache")]
|
||||
use crate::{
|
||||
cache::{BackendCache, TrampolineCache},
|
||||
trampoline::Trampolines,
|
||||
};
|
||||
use crate::{
|
||||
call::HandlerData,
|
||||
libcalls,
|
||||
relocation::{
|
||||
ExternalRelocation, LibCall, LocalRelocation, LocalTrapSink, Reloc, RelocSink,
|
||||
RelocationType, TrapSink, VmCall, VmCallKind,
|
||||
},
|
||||
};
|
||||
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use cranelift_codegen::{ir, isa, Context};
|
||||
use std::mem;
|
||||
use std::ptr::{write_unaligned, NonNull};
|
||||
use std::{
|
||||
mem,
|
||||
ptr::{write_unaligned, NonNull},
|
||||
sync::Arc,
|
||||
};
|
||||
#[cfg(feature = "cache")]
|
||||
use wasmer_runtime_core::cache::Error as CacheError;
|
||||
use wasmer_runtime_core::{
|
||||
self,
|
||||
backend::{
|
||||
self,
|
||||
sys::{Memory, Protect},
|
||||
SigRegistry,
|
||||
},
|
||||
error::{CompileError, CompileResult},
|
||||
structures::{Map, TypedIndex},
|
||||
types::LocalFuncIndex,
|
||||
module::ModuleInfo,
|
||||
structures::{Map, SliceMap, TypedIndex},
|
||||
types::{FuncSig, LocalFuncIndex, SigIndex},
|
||||
vm, vmcalls,
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct FuncResolverBuilder {
|
||||
resolver: FuncResolver,
|
||||
relocations: Map<LocalFuncIndex, Vec<Relocation>>,
|
||||
local_relocs: Map<LocalFuncIndex, Box<[LocalRelocation]>>,
|
||||
external_relocs: Map<LocalFuncIndex, Box<[ExternalRelocation]>>,
|
||||
import_len: usize,
|
||||
}
|
||||
|
||||
impl FuncResolverBuilder {
|
||||
#[cfg(feature = "cache")]
|
||||
pub fn new_from_backend_cache(
|
||||
backend_cache: BackendCache,
|
||||
mut code: Memory,
|
||||
info: &ModuleInfo,
|
||||
) -> Result<(Self, Trampolines, HandlerData), CacheError> {
|
||||
unsafe {
|
||||
code.protect(.., Protect::ReadWrite)
|
||||
.map_err(|e| CacheError::Unknown(e.to_string()))?;
|
||||
}
|
||||
|
||||
let handler_data =
|
||||
HandlerData::new(backend_cache.trap_sink, code.as_ptr() as _, code.size());
|
||||
|
||||
Ok((
|
||||
Self {
|
||||
resolver: FuncResolver {
|
||||
map: backend_cache.offsets,
|
||||
memory: code,
|
||||
},
|
||||
local_relocs: Map::new(),
|
||||
external_relocs: backend_cache.external_relocs,
|
||||
import_len: info.imported_functions.len(),
|
||||
},
|
||||
Trampolines::from_trampoline_cache(backend_cache.trampolines),
|
||||
handler_data,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
pub fn to_backend_cache(
|
||||
mut self,
|
||||
trampolines: TrampolineCache,
|
||||
handler_data: HandlerData,
|
||||
) -> (BackendCache, Memory) {
|
||||
self.relocate_locals();
|
||||
(
|
||||
BackendCache {
|
||||
external_relocs: self.external_relocs,
|
||||
offsets: self.resolver.map,
|
||||
trap_sink: handler_data.trap_data,
|
||||
trampolines,
|
||||
},
|
||||
self.resolver.memory,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
isa: &isa::TargetIsa,
|
||||
function_bodies: Map<LocalFuncIndex, ir::Function>,
|
||||
import_len: usize,
|
||||
info: &ModuleInfo,
|
||||
) -> CompileResult<(Self, HandlerData)> {
|
||||
let mut compiled_functions: Vec<Vec<u8>> = Vec::with_capacity(function_bodies.len());
|
||||
let mut relocations = Map::with_capacity(function_bodies.len());
|
||||
let mut local_relocs = Map::with_capacity(function_bodies.len());
|
||||
let mut external_relocs = Map::new();
|
||||
|
||||
let mut trap_sink = TrapSink::new();
|
||||
let mut local_trap_sink = LocalTrapSink::new();
|
||||
@ -58,7 +123,8 @@ impl FuncResolverBuilder {
|
||||
total_size += round_up(code_buf.len(), mem::size_of::<usize>());
|
||||
|
||||
compiled_functions.push(code_buf);
|
||||
relocations.push(reloc_sink.func_relocs);
|
||||
local_relocs.push(reloc_sink.local_relocs.into_boxed_slice());
|
||||
external_relocs.push(reloc_sink.external_relocs.into_boxed_slice());
|
||||
}
|
||||
|
||||
let mut memory = Memory::with_size(total_size)
|
||||
@ -98,43 +164,58 @@ impl FuncResolverBuilder {
|
||||
|
||||
let handler_data = HandlerData::new(trap_sink, memory.as_ptr() as _, memory.size());
|
||||
|
||||
Ok((
|
||||
Self {
|
||||
resolver: FuncResolver { map, memory },
|
||||
relocations,
|
||||
import_len,
|
||||
},
|
||||
handler_data,
|
||||
))
|
||||
let mut func_resolver_builder = Self {
|
||||
resolver: FuncResolver { map, memory },
|
||||
local_relocs,
|
||||
external_relocs,
|
||||
import_len: info.imported_functions.len(),
|
||||
};
|
||||
|
||||
func_resolver_builder.relocate_locals();
|
||||
|
||||
Ok((func_resolver_builder, handler_data))
|
||||
}
|
||||
|
||||
pub fn finalize(mut self) -> CompileResult<FuncResolver> {
|
||||
for (index, relocs) in self.relocations.iter() {
|
||||
for ref reloc in relocs {
|
||||
let target_func_address: isize = match reloc.target {
|
||||
RelocationType::Normal(local_func_index) => {
|
||||
// This will always be an internal function
|
||||
// because imported functions are not
|
||||
// called in this way.
|
||||
// Adjust from wasm-wide function index to index of locally-defined functions only.
|
||||
let local_func_index =
|
||||
LocalFuncIndex::new(local_func_index.index() - self.import_len);
|
||||
fn relocate_locals(&mut self) {
|
||||
for (index, relocs) in self.local_relocs.iter() {
|
||||
for ref reloc in relocs.iter() {
|
||||
let local_func_index = LocalFuncIndex::new(reloc.target.index() - self.import_len);
|
||||
let target_func_address =
|
||||
self.resolver.lookup(local_func_index).unwrap().as_ptr() as usize;
|
||||
|
||||
self.resolver.lookup(local_func_index).unwrap().as_ptr() as isize
|
||||
}
|
||||
// We need the address of the current function
|
||||
// because these calls are relative.
|
||||
let func_addr = self.resolver.lookup(index).unwrap().as_ptr() as usize;
|
||||
|
||||
unsafe {
|
||||
let reloc_address = func_addr + reloc.offset as usize;
|
||||
let reloc_delta = target_func_address
|
||||
.wrapping_sub(reloc_address)
|
||||
.wrapping_add(reloc.addend as usize);
|
||||
|
||||
write_unaligned(reloc_address as *mut u32, reloc_delta as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finalize(
|
||||
mut self,
|
||||
signatures: &SliceMap<SigIndex, Arc<FuncSig>>,
|
||||
) -> CompileResult<FuncResolver> {
|
||||
for (index, relocs) in self.external_relocs.iter() {
|
||||
for ref reloc in relocs.iter() {
|
||||
let target_func_address: isize = match reloc.target {
|
||||
RelocationType::LibCall(libcall) => match libcall {
|
||||
ir::LibCall::CeilF32 => libcalls::ceilf32 as isize,
|
||||
ir::LibCall::FloorF32 => libcalls::floorf32 as isize,
|
||||
ir::LibCall::TruncF32 => libcalls::truncf32 as isize,
|
||||
ir::LibCall::NearestF32 => libcalls::nearbyintf32 as isize,
|
||||
ir::LibCall::CeilF64 => libcalls::ceilf64 as isize,
|
||||
ir::LibCall::FloorF64 => libcalls::floorf64 as isize,
|
||||
ir::LibCall::TruncF64 => libcalls::truncf64 as isize,
|
||||
ir::LibCall::NearestF64 => libcalls::nearbyintf64 as isize,
|
||||
ir::LibCall::Probestack => libcalls::__rust_probestack as isize,
|
||||
_ => Err(CompileError::InternalError {
|
||||
msg: format!("unexpected libcall: {}", libcall),
|
||||
})?,
|
||||
LibCall::CeilF32 => libcalls::ceilf32 as isize,
|
||||
LibCall::FloorF32 => libcalls::floorf32 as isize,
|
||||
LibCall::TruncF32 => libcalls::truncf32 as isize,
|
||||
LibCall::NearestF32 => libcalls::nearbyintf32 as isize,
|
||||
LibCall::CeilF64 => libcalls::ceilf64 as isize,
|
||||
LibCall::FloorF64 => libcalls::floorf64 as isize,
|
||||
LibCall::TruncF64 => libcalls::truncf64 as isize,
|
||||
LibCall::NearestF64 => libcalls::nearbyintf64 as isize,
|
||||
LibCall::Probestack => libcalls::__rust_probestack as isize,
|
||||
},
|
||||
RelocationType::Intrinsic(ref name) => match name.as_str() {
|
||||
"i32print" => i32_print as isize,
|
||||
@ -181,10 +262,15 @@ impl FuncResolverBuilder {
|
||||
}
|
||||
},
|
||||
},
|
||||
RelocationType::Signature(sig_index) => {
|
||||
let sig_index =
|
||||
SigRegistry.lookup_sig_index(Arc::clone(&signatures[sig_index]));
|
||||
sig_index.index() as _
|
||||
}
|
||||
};
|
||||
|
||||
// We need the address of the current function
|
||||
// because these calls are relative.
|
||||
// because some of these calls are relative.
|
||||
let func_addr = self.resolver.lookup(index).unwrap().as_ptr();
|
||||
|
||||
// Determine relocation type and apply relocation.
|
||||
@ -200,17 +286,14 @@ impl FuncResolverBuilder {
|
||||
};
|
||||
LittleEndian::write_u64(ptr_slice, ptr_to_write);
|
||||
}
|
||||
Reloc::X86PCRel4 => unsafe {
|
||||
let reloc_address = func_addr.offset(reloc.offset as isize) as isize;
|
||||
let reloc_addend = reloc.addend as isize;
|
||||
// TODO: Handle overflow.
|
||||
let reloc_delta_i32 =
|
||||
(target_func_address - reloc_address + reloc_addend) as i32;
|
||||
write_unaligned(reloc_address as *mut i32, reloc_delta_i32);
|
||||
Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => unsafe {
|
||||
let reloc_address = (func_addr as usize) + reloc.offset as usize;
|
||||
let reloc_delta = target_func_address
|
||||
.wrapping_sub(reloc_address as isize)
|
||||
.wrapping_add(reloc.addend as isize);
|
||||
|
||||
write_unaligned(reloc_address as *mut u32, reloc_delta as u32);
|
||||
},
|
||||
_ => Err(CompileError::InternalError {
|
||||
msg: format!("unsupported reloc kind: {}", reloc.reloc),
|
||||
})?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
#[cfg(feature = "cache")]
|
||||
use crate::cache::TrampolineCache;
|
||||
use cranelift_codegen::{
|
||||
binemit::{NullTrapSink, Reloc, RelocSink},
|
||||
cursor::{Cursor, FuncCursor},
|
||||
@ -8,7 +10,7 @@ use hashbrown::HashMap;
|
||||
use std::{iter, mem};
|
||||
use wasmer_runtime_core::{
|
||||
backend::sys::{Memory, Protect},
|
||||
module::{ExportIndex, ModuleInner},
|
||||
module::{ExportIndex, ModuleInfo},
|
||||
types::{FuncSig, SigIndex, Type},
|
||||
vm,
|
||||
};
|
||||
@ -27,7 +29,45 @@ pub struct Trampolines {
|
||||
}
|
||||
|
||||
impl Trampolines {
|
||||
pub fn new(isa: &isa::TargetIsa, module: &ModuleInner) -> Self {
|
||||
#[cfg(feature = "cache")]
|
||||
pub fn from_trampoline_cache(cache: TrampolineCache) -> Self {
|
||||
// pub struct TrampolineCache {
|
||||
// #[serde(with = "serde_bytes")]
|
||||
// code: Vec<u8>,
|
||||
// offsets: HashMap<SigIndex, usize>,
|
||||
// }
|
||||
|
||||
let mut memory = Memory::with_size(cache.code.len()).unwrap();
|
||||
unsafe {
|
||||
memory.protect(.., Protect::ReadWrite).unwrap();
|
||||
|
||||
// Copy over the compiled code.
|
||||
memory.as_slice_mut()[..cache.code.len()].copy_from_slice(cache.code.as_slice());
|
||||
|
||||
memory.protect(.., Protect::ReadExec).unwrap();
|
||||
}
|
||||
|
||||
Self {
|
||||
memory,
|
||||
offsets: cache.offsets,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
pub fn to_trampoline_cache(self) -> TrampolineCache {
|
||||
let mut code = vec![0; self.memory.size()];
|
||||
|
||||
unsafe {
|
||||
code.copy_from_slice(self.memory.as_slice());
|
||||
}
|
||||
|
||||
TrampolineCache {
|
||||
code,
|
||||
offsets: self.offsets,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(isa: &isa::TargetIsa, module: &ModuleInfo) -> Self {
|
||||
let func_index_iter = module
|
||||
.exports
|
||||
.values()
|
||||
@ -43,7 +83,7 @@ impl Trampolines {
|
||||
|
||||
for exported_func_index in func_index_iter {
|
||||
let sig_index = module.func_assoc[*exported_func_index];
|
||||
let func_sig = module.sig_registry.lookup_signature(sig_index);
|
||||
let func_sig = &module.signatures[sig_index];
|
||||
|
||||
let trampoline_func = generate_func(&func_sig);
|
||||
|
||||
|
Reference in New Issue
Block a user