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:
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user