From 087b5b4dff1ad670df2dd1c902e4df7a9896fa40 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 3 Jan 2019 08:31:03 -0800 Subject: [PATCH] Implement cross-instance indirect calls. --- lib/jit/src/compiler.rs | 8 +++++++- lib/jit/src/instantiate.rs | 24 ++++++++++++++++++++-- lib/runtime/src/instance.rs | 24 ++++++++-------------- lib/runtime/src/lib.rs | 3 ++- lib/runtime/src/sig_registry.rs | 35 +++++++++------------------------ lib/runtime/src/vmcontext.rs | 1 + lib/wast/src/spectest.rs | 2 ++ 7 files changed, 51 insertions(+), 46 deletions(-) diff --git a/lib/jit/src/compiler.rs b/lib/jit/src/compiler.rs index 3ec3642c16..b781271263 100644 --- a/lib/jit/src/compiler.rs +++ b/lib/jit/src/compiler.rs @@ -16,7 +16,7 @@ use std::vec::Vec; use target_tunables::target_tunables; use wasmtime_environ::cranelift; use wasmtime_environ::{Compilation, CompileError, Module, Relocations, Tunables}; -use wasmtime_runtime::{InstantiationError, VMFunctionBody}; +use wasmtime_runtime::{InstantiationError, SignatureRegistry, VMFunctionBody}; /// A WebAssembly code JIT compiler. /// @@ -31,6 +31,7 @@ pub struct Compiler { code_memory: CodeMemory, trampoline_park: HashMap<*const VMFunctionBody, *const VMFunctionBody>, + signatures: SignatureRegistry, /// The `FunctionBuilderContext`, shared between trampline function compilations. fn_builder_ctx: FunctionBuilderContext, @@ -43,6 +44,7 @@ impl Compiler { isa, code_memory: CodeMemory::new(), trampoline_park: HashMap::new(), + signatures: SignatureRegistry::new(), fn_builder_ctx: FunctionBuilderContext::new(), } } @@ -115,6 +117,10 @@ impl Compiler { pub(crate) fn publish_compiled_code(&mut self) { self.code_memory.publish(); } + + pub(crate) fn signatures(&mut self) -> &mut SignatureRegistry { + &mut self.signatures + } } /// Create a trampoline for invoking a function. diff --git a/lib/jit/src/instantiate.rs b/lib/jit/src/instantiate.rs index c7772531ca..14a94c106d 100644 --- a/lib/jit/src/instantiate.rs +++ b/lib/jit/src/instantiate.rs @@ -5,7 +5,7 @@ use compiler::Compiler; use cranelift_entity::{BoxedSlice, PrimaryMap}; -use cranelift_wasm::DefinedFuncIndex; +use cranelift_wasm::{DefinedFuncIndex, SignatureIndex}; use link::link_module; use resolver::Resolver; use std::boxed::Box; @@ -15,7 +15,9 @@ use std::vec::Vec; use wasmtime_environ::{ CompileError, DataInitializer, DataInitializerLocation, Module, ModuleEnvironment, }; -use wasmtime_runtime::{Imports, Instance, InstantiationError, VMFunctionBody}; +use wasmtime_runtime::{ + Imports, Instance, InstantiationError, VMFunctionBody, VMSharedSignatureIndex, +}; /// An error condition while setting up a wasm instance, be it validation, /// compilation, or instantiation. @@ -42,6 +44,7 @@ struct RawCompiledModule<'data> { finished_functions: BoxedSlice, imports: Imports, data_initializers: Box<[DataInitializer<'data>]>, + signatures: BoxedSlice, } impl<'data> RawCompiledModule<'data> { @@ -79,6 +82,16 @@ impl<'data> RawCompiledModule<'data> { .collect::>() .into_boxed_slice(); + // Compute indices into the shared signature table. + let signatures = { + let signature_registry = compiler.signatures(); + let mut signatures = PrimaryMap::new(); + for sig in translation.module.signatures.values() { + signatures.push(signature_registry.register(sig)); + } + signatures + }; + // Make all code compiled thus far executable. compiler.publish_compiled_code(); @@ -87,6 +100,7 @@ impl<'data> RawCompiledModule<'data> { finished_functions, imports, data_initializers: translation.data_initializers.into_boxed_slice(), + signatures: signatures.into_boxed_slice(), }) } } @@ -97,6 +111,7 @@ pub struct CompiledModule { finished_functions: BoxedSlice, imports: Imports, data_initializers: Box<[OwnedDataInitializer]>, + signatures: BoxedSlice, } impl CompiledModule { @@ -118,6 +133,7 @@ impl CompiledModule { .map(OwnedDataInitializer::new) .collect::>() .into_boxed_slice(), + signatures: raw.signatures.clone(), }) } @@ -127,12 +143,14 @@ impl CompiledModule { finished_functions: BoxedSlice, imports: Imports, data_initializers: Box<[OwnedDataInitializer]>, + signatures: BoxedSlice, ) -> Self { Self { module: Rc::new(module), finished_functions, imports, data_initializers, + signatures, } } @@ -155,6 +173,7 @@ impl CompiledModule { self.finished_functions.clone(), self.imports.clone(), &data_initializers, + self.signatures.clone(), ) } } @@ -194,6 +213,7 @@ pub fn instantiate( raw.finished_functions, raw.imports, &*raw.data_initializers, + raw.signatures, ) .map_err(SetupError::Instantiate) } diff --git a/lib/runtime/src/instance.rs b/lib/runtime/src/instance.rs index 564c41ac10..23c0b921b8 100644 --- a/lib/runtime/src/instance.rs +++ b/lib/runtime/src/instance.rs @@ -5,11 +5,11 @@ use cranelift_entity::EntityRef; use cranelift_entity::{BoxedSlice, PrimaryMap}; use cranelift_wasm::{ DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, GlobalInit, + SignatureIndex, }; use export::Export; use imports::Imports; use memory::LinearMemory; -use sig_registry::SignatureRegistry; use signalhandlers::{wasmtime_init_eager, wasmtime_init_finish}; use std::rc::Rc; use std::slice; @@ -18,7 +18,7 @@ use table::Table; use traphandlers::wasmtime_call; use vmcontext::{ VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMGlobalDefinition, VMMemoryDefinition, - VMTableDefinition, + VMSharedSignatureIndex, VMTableDefinition, }; use wasmtime_environ::{DataInitializer, Module}; @@ -38,8 +38,7 @@ pub struct Instance { tables: BoxedSlice, /// Function Signature IDs. - /// FIXME: This should be shared across instances rather than per-Instance. - sig_registry: SignatureRegistry, + vmshared_signatures: BoxedSlice, /// Resolved imports. vmctx_imports: Imports, @@ -67,8 +66,8 @@ impl Instance { finished_functions: BoxedSlice, mut vmctx_imports: Imports, data_initializers: &[DataInitializer], + mut vmshared_signatures: BoxedSlice, ) -> Result, InstantiationError> { - let mut sig_registry = create_and_initialize_signatures(&module); let mut tables = create_tables(&module); let mut memories = create_memories(&module)?; @@ -102,13 +101,14 @@ impl Instance { let vmctx_tables_ptr = vmctx_tables.values_mut().into_slice().as_mut_ptr(); let vmctx_memories_ptr = vmctx_memories.values_mut().into_slice().as_mut_ptr(); let vmctx_globals_ptr = vmctx_globals.values_mut().into_slice().as_mut_ptr(); - let vmctx_shared_signatures_ptr = sig_registry.vmshared_signatures(); + let vmctx_shared_signatures_ptr = + vmshared_signatures.values_mut().into_slice().as_mut_ptr(); let mut result = Box::new(Self { module, memories, tables, - sig_registry, + vmshared_signatures, vmctx_imports, finished_functions, vmctx_tables, @@ -399,14 +399,6 @@ fn check_memory_init_bounds( Ok(()) } -fn create_and_initialize_signatures(module: &Module) -> SignatureRegistry { - let mut sig_registry = SignatureRegistry::new(); - for (sig_index, sig) in module.signatures.iter() { - sig_registry.register(sig_index, sig); - } - sig_registry -} - /// Allocate memory for just the tables of the current module. fn create_tables(module: &Module) -> BoxedSlice { let num_imports = module.imported_tables.len(); @@ -453,7 +445,7 @@ fn initialize_tables(instance: &mut Instance) -> Result<(), InstantiationError> let imported_func = &instance.vmctx_imports.functions[*func_idx]; (imported_func.body, imported_func.vmctx) }; - let type_index = instance.sig_registry.lookup(callee_sig); + let type_index = instance.vmshared_signatures[callee_sig]; subslice[i] = VMCallerCheckedAnyfunc { func_ptr: callee_ptr, type_index, diff --git a/lib/runtime/src/lib.rs b/lib/runtime/src/lib.rs index f8a67aec2e..da0689bbe9 100644 --- a/lib/runtime/src/lib.rs +++ b/lib/runtime/src/lib.rs @@ -62,11 +62,12 @@ pub use export::Export; pub use imports::Imports; pub use instance::{Instance, InstantiationError, LinkError}; pub use mmap::Mmap; +pub use sig_registry::SignatureRegistry; pub use signalhandlers::{wasmtime_init_eager, wasmtime_init_finish}; pub use traphandlers::{wasmtime_call, wasmtime_call_trampoline}; pub use vmcontext::{ VMContext, VMFunctionBody, VMFunctionImport, VMGlobalDefinition, VMGlobalImport, - VMMemoryDefinition, VMMemoryImport, VMTableDefinition, VMTableImport, + VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, }; #[cfg(not(feature = "std"))] diff --git a/lib/runtime/src/sig_registry.rs b/lib/runtime/src/sig_registry.rs index ba1febda30..0d3f7468b4 100644 --- a/lib/runtime/src/sig_registry.rs +++ b/lib/runtime/src/sig_registry.rs @@ -3,53 +3,36 @@ use cast; use cranelift_codegen::ir; -use cranelift_entity::PrimaryMap; -use cranelift_wasm::SignatureIndex; use std::collections::{hash_map, HashMap}; use vmcontext::VMSharedSignatureIndex; +/// WebAssembly requires that the caller and callee signatures in an indirect +/// call must match. To implement this efficiently, keep a registry of all +/// signatures, shared by all instances, so that call sites can just do an +/// index comparison. #[derive(Debug)] pub struct SignatureRegistry { signature_hash: HashMap, - shared_signatures: PrimaryMap, } impl SignatureRegistry { + /// Create a new `SignatureRegistry`. pub fn new() -> Self { Self { signature_hash: HashMap::new(), - shared_signatures: PrimaryMap::new(), } } - pub fn vmshared_signatures(&mut self) -> *mut VMSharedSignatureIndex { - self.shared_signatures - .values_mut() - .into_slice() - .as_mut_ptr() - } - - /// Register the given signature. - pub fn register(&mut self, sig_index: SignatureIndex, sig: &ir::Signature) { - // TODO: Refactor this interface so that we're not passing in redundant - // information. - debug_assert_eq!(sig_index.index(), self.shared_signatures.len()); - use cranelift_entity::EntityRef; - + /// Register a signature and return its unique index. + pub fn register(&mut self, sig: &ir::Signature) -> VMSharedSignatureIndex { let len = self.signature_hash.len(); - let sig_id = match self.signature_hash.entry(sig.clone()) { + match self.signature_hash.entry(sig.clone()) { hash_map::Entry::Occupied(entry) => *entry.get(), hash_map::Entry::Vacant(entry) => { let sig_id = VMSharedSignatureIndex::new(cast::u32(len).unwrap()); entry.insert(sig_id); sig_id } - }; - self.shared_signatures.push(sig_id); - } - - /// Return the identifying runtime index for the given signature. - pub fn lookup(&mut self, sig_index: SignatureIndex) -> VMSharedSignatureIndex { - self.shared_signatures[sig_index] + } } } diff --git a/lib/runtime/src/vmcontext.rs b/lib/runtime/src/vmcontext.rs index e34eee1fa2..c78baf9786 100644 --- a/lib/runtime/src/vmcontext.rs +++ b/lib/runtime/src/vmcontext.rs @@ -403,6 +403,7 @@ mod test_vmshared_signature_index { } impl VMSharedSignatureIndex { + /// Create a new `VMSharedSignatureIndex`. pub fn new(value: u32) -> Self { VMSharedSignatureIndex(value) } diff --git a/lib/wast/src/spectest.rs b/lib/wast/src/spectest.rs index 19afc595af..6929c37b87 100644 --- a/lib/wast/src/spectest.rs +++ b/lib/wast/src/spectest.rs @@ -216,12 +216,14 @@ pub fn instantiate_spectest() -> Result, InstantiationError> { let imports = Imports::none(); let data_initializers = Vec::new(); + let signatures = PrimaryMap::new(); CompiledModule::from_parts( module, finished_functions.into_boxed_slice(), imports, data_initializers.into_boxed_slice(), + signatures.into_boxed_slice(), ) .instantiate() }