Disconnects Store state fields from Compiler (#1761)

*  Moves CodeMemory, VMInterrupts and SignatureRegistry from Compiler
*  CompiledModule holds CodeMemory and GdbJitImageRegistration
*  Store keeps track of its JIT code
*  Makes "jit_int.rs" stuff Send+Sync
*  Adds the threads example.
This commit is contained in:
Yury Delendik
2020-06-02 13:44:39 -05:00
committed by GitHub
parent b41330393d
commit 15c68f2cc1
61 changed files with 982 additions and 663 deletions

View File

@@ -22,10 +22,10 @@ impl CodeMemoryEntry {
Ok(Self { mmap, registry })
}
fn contains(&self, addr: usize) -> bool {
fn range(&self) -> (usize, usize) {
let start = self.mmap.as_ptr() as usize;
let end = start + self.mmap.len();
start <= addr && addr < end
(start, end)
}
}
@@ -243,11 +243,10 @@ impl CodeMemory {
Ok(())
}
/// Returns whether any published segment of this code memory contains
/// `addr`.
pub fn published_contains(&self, addr: usize) -> bool {
/// Returns all published segment ranges.
pub fn published_ranges<'a>(&'a self) -> impl Iterator<Item = (usize, usize)> + 'a {
self.entries[..self.published]
.iter()
.any(|entry| entry.contains(addr))
.map(|entry| entry.range())
}
}

View File

@@ -9,20 +9,16 @@ use cranelift_codegen::Context;
use cranelift_codegen::{binemit, ir};
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use std::collections::HashMap;
use std::sync::Arc;
use wasmtime_debug::{emit_debugsections_image, DebugInfoData};
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa};
use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex, MemoryIndex};
use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex, MemoryIndex, SignatureIndex};
use wasmtime_environ::{
CacheConfig, CompileError, CompiledFunction, Compiler as _C, ModuleAddressMap,
ModuleMemoryOffset, ModuleTranslation, ModuleVmctxInfo, Relocation, RelocationTarget,
Relocations, Traps, Tunables, VMOffsets,
};
use wasmtime_runtime::{
InstantiationError, SignatureRegistry, VMFunctionBody, VMInterrupts, VMSharedSignatureIndex,
VMTrampoline,
};
use wasmtime_runtime::{InstantiationError, VMFunctionBody, VMTrampoline};
/// Select which kind of compilation to use.
#[derive(Copy, Clone, Debug)]
@@ -48,12 +44,9 @@ pub enum CompilationStrategy {
/// TODO: Consider using cranelift-module.
pub struct Compiler {
isa: Box<dyn TargetIsa>,
code_memory: CodeMemory,
signatures: SignatureRegistry,
strategy: CompilationStrategy,
cache_config: CacheConfig,
tunables: Tunables,
interrupts: Arc<VMInterrupts>,
}
impl Compiler {
@@ -66,22 +59,25 @@ impl Compiler {
) -> Self {
Self {
isa,
code_memory: CodeMemory::new(),
signatures: SignatureRegistry::new(),
strategy,
cache_config,
tunables,
interrupts: Arc::new(VMInterrupts::default()),
}
}
}
fn _assert_compiler_send_sync() {
fn _assert<T: Send + Sync>() {}
_assert::<Compiler>();
}
#[allow(missing_docs)]
pub struct Compilation {
pub code_memory: CodeMemory,
pub finished_functions: PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
pub relocations: Relocations,
pub trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
pub trampoline_relocations: HashMap<VMSharedSignatureIndex, Vec<Relocation>>,
pub trampolines: PrimaryMap<SignatureIndex, VMTrampoline>,
pub trampoline_relocations: HashMap<SignatureIndex, Vec<Relocation>>,
pub jt_offsets: PrimaryMap<DefinedFuncIndex, ir::JumpTableOffsets>,
pub dbg_image: Option<Vec<u8>>,
pub traps: Traps,
@@ -89,6 +85,11 @@ pub struct Compilation {
}
impl Compiler {
/// Return the isa.
pub fn isa(&self) -> &dyn TargetIsa {
self.isa.as_ref()
}
/// Return the target's frontend configuration settings.
pub fn frontend_config(&self) -> TargetFrontendConfig {
self.isa.frontend_config()
@@ -99,17 +100,14 @@ impl Compiler {
&self.tunables
}
/// Return the handle by which to interrupt instances
pub fn interrupts(&self) -> &Arc<VMInterrupts> {
&self.interrupts
}
/// Compile the given function bodies.
pub(crate) fn compile<'data>(
&mut self,
&self,
translation: &ModuleTranslation,
debug_data: Option<DebugInfoData>,
) -> Result<Compilation, SetupError> {
let mut code_memory = CodeMemory::new();
let (compilation, relocations, address_transform, value_ranges, stack_slots, traps) =
match self.strategy {
// For now, interpret `Auto` as `Cranelift` since that's the most stable
@@ -135,7 +133,7 @@ impl Compiler {
// Allocate all of the compiled functions into executable memory,
// copying over their contents.
let finished_functions =
allocate_functions(&mut self.code_memory, &compilation).map_err(|message| {
allocate_functions(&mut code_memory, &compilation).map_err(|message| {
SetupError::Instantiate(InstantiationError::Resource(format!(
"failed to allocate memory for functions: {}",
message
@@ -147,23 +145,17 @@ impl Compiler {
// guarantees that all functions (including indirect ones through
// tables) have a trampoline when invoked through the wasmtime API.
let mut cx = FunctionBuilderContext::new();
let mut trampolines = HashMap::new();
let mut trampolines = PrimaryMap::new();
let mut trampoline_relocations = HashMap::new();
for (wasm_func_ty, native_sig) in translation.module.local.signatures.values() {
let index = self
.signatures
.register(wasm_func_ty.clone(), native_sig.clone());
if trampolines.contains_key(&index) {
continue;
}
for (index, (_, native_sig)) in translation.module.local.signatures.iter() {
let (trampoline, relocations) = make_trampoline(
&*self.isa,
&mut self.code_memory,
&mut code_memory,
&mut cx,
native_sig,
std::mem::size_of::<u128>(),
)?;
trampolines.insert(index, trampoline);
trampolines.push(trampoline);
// Typically trampolines do not have relocations, so if one does
// show up be sure to log it in case anyone's listening and there's
@@ -217,6 +209,7 @@ impl Compiler {
let jt_offsets = compilation.get_jt_offsets();
Ok(Compilation {
code_memory,
finished_functions,
relocations,
trampolines,
@@ -227,22 +220,6 @@ impl Compiler {
address_transform,
})
}
/// Make memory containing compiled code executable.
pub(crate) fn publish_compiled_code(&mut self) {
self.code_memory.publish(self.isa.as_ref());
}
/// Shared signature registry.
pub fn signatures(&self) -> &SignatureRegistry {
&self.signatures
}
/// Returns whether or not the given address falls within the JIT code
/// managed by the compiler
pub fn is_in_jit_code(&self, addr: usize) -> bool {
self.code_memory.published_contains(addr)
}
}
/// Create a trampoline for invoking a function.

View File

@@ -3,6 +3,7 @@
//! `CompiledModule` to allow compiling and instantiating to be done as separate
//! steps.
use crate::code_memory::CodeMemory;
use crate::compiler::Compiler;
use crate::imports::resolve_imports;
use crate::link::link_module;
@@ -10,7 +11,6 @@ use crate::resolver::Resolver;
use std::any::Any;
use std::collections::HashMap;
use std::io::Write;
use std::rc::Rc;
use std::sync::Arc;
use thiserror::Error;
use wasmtime_debug::read_debuginfo;
@@ -24,7 +24,7 @@ use wasmtime_profiling::ProfilingAgent;
use wasmtime_runtime::VMInterrupts;
use wasmtime_runtime::{
GdbJitImageRegistration, InstanceHandle, InstantiationError, RuntimeMemoryCreator,
SignatureRegistry, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline,
SignatureRegistry, VMFunctionBody, VMTrampoline,
};
/// An error condition while setting up a wasm instance, be it validation,
@@ -49,23 +49,33 @@ pub enum SetupError {
DebugInfo(#[from] anyhow::Error),
}
/// This is similar to `CompiledModule`, but references the data initializers
/// from the wasm buffer rather than holding its own copy.
struct RawCompiledModule<'data> {
module: Module,
finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>,
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
data_initializers: Box<[DataInitializer<'data>]>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
struct FinishedFunctions(BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>);
unsafe impl Send for FinishedFunctions {}
unsafe impl Sync for FinishedFunctions {}
/// Container for data needed for an Instance function to exist.
pub struct ModuleCode {
code_memory: CodeMemory,
#[allow(dead_code)]
dbg_jit_registration: Option<GdbJitImageRegistration>,
}
/// A compiled wasm module, ready to be instantiated.
pub struct CompiledModule {
module: Arc<Module>,
code: Arc<ModuleCode>,
finished_functions: FinishedFunctions,
trampolines: PrimaryMap<SignatureIndex, VMTrampoline>,
data_initializers: Box<[OwnedDataInitializer]>,
traps: Traps,
address_transform: ModuleAddressMap,
}
impl<'data> RawCompiledModule<'data> {
/// Create a new `RawCompiledModule` by compiling the wasm module in `data` and instatiating it.
fn new(
compiler: &mut Compiler,
impl CompiledModule {
/// Compile a data buffer into a `CompiledModule`, which may then be instantiated.
pub fn new<'data>(
compiler: &Compiler,
data: &'data [u8],
profiler: &dyn ProfilingAgent,
) -> Result<Self, SetupError> {
@@ -83,26 +93,24 @@ impl<'data> RawCompiledModule<'data> {
let compilation = compiler.compile(&translation, debug_data)?;
link_module(&translation.module, &compilation);
let module = translation.module;
// Compute indices into the shared signature table.
let signatures = {
let signature_registry = compiler.signatures();
translation
.module
.local
.signatures
.values()
.map(|(wasm, native)| signature_registry.register(wasm.clone(), native.clone()))
.collect::<PrimaryMap<_, _>>()
};
link_module(&module, &compilation);
// Make all code compiled thus far executable.
compiler.publish_compiled_code();
let mut code_memory = compilation.code_memory;
code_memory.publish(compiler.isa());
let data_initializers = translation
.data_initializers
.into_iter()
.map(OwnedDataInitializer::new)
.collect::<Vec<_>>()
.into_boxed_slice();
// Initialize profiler and load the wasm module
profiler.module_load(
&translation.module,
&module,
&compilation.finished_functions,
compilation.dbg_image.as_deref(),
);
@@ -116,82 +124,22 @@ impl<'data> RawCompiledModule<'data> {
None
};
let finished_functions =
FinishedFunctions(compilation.finished_functions.into_boxed_slice());
Ok(Self {
module: translation.module,
finished_functions: compilation.finished_functions.into_boxed_slice(),
module: Arc::new(module),
code: Arc::new(ModuleCode {
code_memory,
dbg_jit_registration,
}),
finished_functions,
trampolines: compilation.trampolines,
data_initializers: translation.data_initializers.into_boxed_slice(),
signatures: signatures.into_boxed_slice(),
dbg_jit_registration,
data_initializers,
traps: compilation.traps,
address_transform: compilation.address_transform,
})
}
}
/// A compiled wasm module, ready to be instantiated.
pub struct CompiledModule {
module: Arc<Module>,
finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>,
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
data_initializers: Box<[OwnedDataInitializer]>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
dbg_jit_registration: Option<Rc<GdbJitImageRegistration>>,
traps: Traps,
address_transform: ModuleAddressMap,
interrupts: Arc<VMInterrupts>,
}
impl CompiledModule {
/// Compile a data buffer into a `CompiledModule`, which may then be instantiated.
pub fn new<'data>(
compiler: &mut Compiler,
data: &'data [u8],
profiler: &dyn ProfilingAgent,
) -> Result<Self, SetupError> {
let raw = RawCompiledModule::<'data>::new(compiler, data, profiler)?;
Ok(Self::from_parts(
raw.module,
raw.finished_functions,
raw.trampolines,
raw.data_initializers
.iter()
.map(OwnedDataInitializer::new)
.collect::<Vec<_>>()
.into_boxed_slice(),
raw.signatures.clone(),
raw.dbg_jit_registration,
raw.traps,
raw.address_transform,
compiler.interrupts().clone(),
))
}
/// Construct a `CompiledModule` from component parts.
pub fn from_parts(
module: Module,
finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>,
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
data_initializers: Box<[OwnedDataInitializer]>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
dbg_jit_registration: Option<GdbJitImageRegistration>,
traps: Traps,
address_transform: ModuleAddressMap,
interrupts: Arc<VMInterrupts>,
) -> Self {
Self {
module: Arc::new(module),
finished_functions,
trampolines,
data_initializers,
signatures,
dbg_jit_registration: dbg_jit_registration.map(Rc::new),
traps,
address_transform,
interrupts,
}
}
/// Crate an `Instance` from this `CompiledModule`.
///
@@ -205,21 +153,41 @@ impl CompiledModule {
pub unsafe fn instantiate(
&self,
resolver: &mut dyn Resolver,
sig_registry: &SignatureRegistry,
signature_registry: &mut SignatureRegistry,
mem_creator: Option<&dyn RuntimeMemoryCreator>,
interrupts: Arc<VMInterrupts>,
host_state: Box<dyn Any>,
) -> Result<InstanceHandle, InstantiationError> {
let imports = resolve_imports(&self.module, &sig_registry, resolver)?;
// Compute indices into the shared signature table.
let signatures = {
self.module
.local
.signatures
.values()
.map(|(wasm_sig, native)| {
signature_registry.register(wasm_sig.clone(), native.clone())
})
.collect::<PrimaryMap<_, _>>()
};
let mut trampolines = HashMap::new();
for (i, trampoline) in self.trampolines.iter() {
trampolines.insert(signatures[i], trampoline.clone());
}
let finished_functions = self.finished_functions.0.clone();
let imports = resolve_imports(&self.module, signature_registry, resolver)?;
InstanceHandle::new(
Arc::clone(&self.module),
self.finished_functions.clone(),
self.trampolines.clone(),
self.module.clone(),
self.code.clone(),
finished_functions,
trampolines,
imports,
mem_creator,
self.signatures.clone(),
self.dbg_jit_registration.as_ref().map(|r| Rc::clone(&r)),
signatures.into_boxed_slice(),
host_state,
self.interrupts.clone(),
interrupts,
)
}
@@ -239,19 +207,14 @@ impl CompiledModule {
&self.module
}
/// Return a reference-counting pointer to a module.
pub fn module_mut(&mut self) -> &mut Arc<Module> {
&mut self.module
}
/// Return a reference to a module.
pub fn module_ref(&self) -> &Module {
&self.module
/// Return a reference to a mutable module (if possible).
pub fn module_mut(&mut self) -> Option<&mut Module> {
Arc::get_mut(&mut self.module)
}
/// Returns the map of all finished JIT functions compiled for this module
pub fn finished_functions(&self) -> &BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]> {
&self.finished_functions
&self.finished_functions.0
}
/// Returns the a map for all traps in this module.
@@ -263,6 +226,16 @@ impl CompiledModule {
pub fn address_transform(&self) -> &ModuleAddressMap {
&self.address_transform
}
/// Returns all ranges convered by JIT code.
pub fn jit_code_ranges<'a>(&'a self) -> impl Iterator<Item = (usize, usize)> + 'a {
self.code.code_memory.published_ranges()
}
/// Returns module's JIT code.
pub fn code(&self) -> &Arc<ModuleCode> {
&self.code
}
}
/// Similar to `DataInitializer`, but owns its own copy of the data rather
@@ -276,7 +249,7 @@ pub struct OwnedDataInitializer {
}
impl OwnedDataInitializer {
fn new(borrowed: &DataInitializer<'_>) -> Self {
fn new(borrowed: DataInitializer<'_>) -> Self {
Self {
location: borrowed.location.clone(),
data: borrowed.data.to_vec().into_boxed_slice(),

View File

@@ -22,7 +22,7 @@ pub fn link_module(module: &Module, compilation: &Compilation) {
for (i, function_relocs) in compilation.trampoline_relocations.iter() {
for r in function_relocs.iter() {
println!("tramopline relocation");
let body = compilation.trampolines[&i] as *const VMFunctionBody;
let body = compilation.trampolines[*i] as *const VMFunctionBody;
apply_reloc(module, compilation, body, r);
}
}