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:
@@ -21,6 +21,7 @@ thiserror = "1.0.4"
|
||||
more-asserts = "0.2.1"
|
||||
cfg-if = "0.1.9"
|
||||
backtrace = "0.3.42"
|
||||
lazy_static = "1.3.0"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = { version = "0.3.7", features = ["winbase", "memoryapi", "errhandlingapi"] }
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
use crate::export::Export;
|
||||
use crate::imports::Imports;
|
||||
use crate::jit_int::GdbJitImageRegistration;
|
||||
use crate::memory::{DefaultMemoryCreator, RuntimeLinearMemory, RuntimeMemoryCreator};
|
||||
use crate::table::Table;
|
||||
use crate::traphandlers::Trap;
|
||||
@@ -21,7 +20,6 @@ use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::{mem, ptr, slice};
|
||||
use thiserror::Error;
|
||||
@@ -40,6 +38,9 @@ pub(crate) struct Instance {
|
||||
/// The `Module` this `Instance` was instantiated from.
|
||||
module: Arc<Module>,
|
||||
|
||||
/// The module's JIT code (if exists).
|
||||
code: Arc<dyn Any>,
|
||||
|
||||
/// Offsets in the `vmctx` region.
|
||||
offsets: VMOffsets,
|
||||
|
||||
@@ -67,9 +68,6 @@ pub(crate) struct Instance {
|
||||
/// Hosts can store arbitrary per-instance information here.
|
||||
host_state: Box<dyn Any>,
|
||||
|
||||
/// Optional image of JIT'ed code for debugger registration.
|
||||
dbg_jit_registration: Option<Rc<GdbJitImageRegistration>>,
|
||||
|
||||
/// Externally allocated data indicating how this instance will be
|
||||
/// interrupted.
|
||||
pub(crate) interrupts: Arc<VMInterrupts>,
|
||||
@@ -96,14 +94,10 @@ impl Instance {
|
||||
unsafe { *self.signature_ids_ptr().add(index) }
|
||||
}
|
||||
|
||||
pub(crate) fn module(&self) -> &Arc<Module> {
|
||||
pub(crate) fn module(&self) -> &Module {
|
||||
&self.module
|
||||
}
|
||||
|
||||
pub(crate) fn module_ref(&self) -> &Module {
|
||||
&*self.module
|
||||
}
|
||||
|
||||
/// Return a pointer to the `VMSharedSignatureIndex`s.
|
||||
fn signature_ids_ptr(&self) -> *mut VMSharedSignatureIndex {
|
||||
unsafe { self.vmctx_plus_offset(self.offsets.vmctx_signature_ids_begin()) }
|
||||
@@ -782,12 +776,12 @@ impl InstanceHandle {
|
||||
/// safety.
|
||||
pub unsafe fn new(
|
||||
module: Arc<Module>,
|
||||
code: Arc<dyn Any>,
|
||||
finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
||||
imports: Imports,
|
||||
mem_creator: Option<&dyn RuntimeMemoryCreator>,
|
||||
vmshared_signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
dbg_jit_registration: Option<Rc<GdbJitImageRegistration>>,
|
||||
host_state: Box<dyn Any>,
|
||||
interrupts: Arc<VMInterrupts>,
|
||||
) -> Result<Self, InstantiationError> {
|
||||
@@ -815,6 +809,7 @@ impl InstanceHandle {
|
||||
let handle = {
|
||||
let instance = Instance {
|
||||
module,
|
||||
code,
|
||||
offsets,
|
||||
memories,
|
||||
tables,
|
||||
@@ -822,7 +817,6 @@ impl InstanceHandle {
|
||||
passive_data,
|
||||
finished_functions,
|
||||
trampolines,
|
||||
dbg_jit_registration,
|
||||
host_state,
|
||||
interrupts,
|
||||
vmctx: VMContext {},
|
||||
@@ -941,14 +935,9 @@ impl InstanceHandle {
|
||||
self.instance().vmctx_ptr()
|
||||
}
|
||||
|
||||
/// Return a reference-counting pointer to a module.
|
||||
pub fn module(&self) -> &Arc<Module> {
|
||||
self.instance().module()
|
||||
}
|
||||
|
||||
/// Return a reference to a module.
|
||||
pub fn module_ref(&self) -> &Module {
|
||||
self.instance().module_ref()
|
||||
pub fn module(&self) -> &Module {
|
||||
self.instance().module()
|
||||
}
|
||||
|
||||
/// Lookup an export with the given name.
|
||||
@@ -1065,8 +1054,7 @@ impl InstanceHandle {
|
||||
}
|
||||
|
||||
fn check_table_init_bounds(instance: &Instance) -> Result<(), InstantiationError> {
|
||||
let module = Arc::clone(&instance.module);
|
||||
for init in &module.table_elements {
|
||||
for init in &instance.module().table_elements {
|
||||
let start = get_table_init_start(init, instance);
|
||||
let table = instance.get_table(init.table_index);
|
||||
|
||||
@@ -1170,8 +1158,7 @@ fn get_table_init_start(init: &TableElements, instance: &Instance) -> usize {
|
||||
|
||||
/// Initialize the table memory from the provided initializers.
|
||||
fn initialize_tables(instance: &Instance) -> Result<(), InstantiationError> {
|
||||
let module = Arc::clone(&instance.module);
|
||||
for init in &module.table_elements {
|
||||
for init in &instance.module().table_elements {
|
||||
let start = get_table_init_start(init, instance);
|
||||
let table = instance.get_table(init.table_index);
|
||||
|
||||
@@ -1284,7 +1271,7 @@ fn create_globals(module: &Module) -> BoxedSlice<DefinedGlobalIndex, VMGlobalDef
|
||||
}
|
||||
|
||||
fn initialize_globals(instance: &Instance) {
|
||||
let module = Arc::clone(&instance.module);
|
||||
let module = instance.module();
|
||||
let num_imports = module.local.num_imported_globals;
|
||||
for (index, global) in module.local.globals.iter().skip(num_imports) {
|
||||
let def_index = module.local.defined_global_index(index).unwrap();
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
//! the __jit_debug_register_code() and __jit_debug_descriptor to register
|
||||
//! or unregister generated object images with debuggers.
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use std::pin::Pin;
|
||||
use std::ptr;
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[repr(C)]
|
||||
struct JITCodeEntry {
|
||||
@@ -43,19 +46,41 @@ extern "C" fn __jit_debug_register_code() {
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
// The process controls access to the __jit_debug_descriptor by itself --
|
||||
// the GDB/LLDB accesses this structure and its data at the process startup
|
||||
// and when paused in __jit_debug_register_code.
|
||||
//
|
||||
// The GDB_REGISTRATION lock is needed for GdbJitImageRegistration to protect
|
||||
// access to the __jit_debug_descriptor within this process.
|
||||
pub static ref GDB_REGISTRATION: Mutex<()> = Mutex::new(Default::default());
|
||||
}
|
||||
|
||||
/// Registeration for JIT image
|
||||
pub struct GdbJitImageRegistration {
|
||||
entry: *mut JITCodeEntry,
|
||||
file: Vec<u8>,
|
||||
entry: Pin<Box<JITCodeEntry>>,
|
||||
file: Pin<Box<[u8]>>,
|
||||
}
|
||||
|
||||
impl GdbJitImageRegistration {
|
||||
/// Registers JIT image using __jit_debug_register_code
|
||||
pub fn register(file: Vec<u8>) -> Self {
|
||||
Self {
|
||||
entry: unsafe { register_gdb_jit_image(&file) },
|
||||
file,
|
||||
let file = Pin::new(file.into_boxed_slice());
|
||||
|
||||
// Create a code entry for the file, which gives the start and size
|
||||
// of the symbol file.
|
||||
let mut entry = Pin::new(Box::new(JITCodeEntry {
|
||||
next_entry: ptr::null_mut(),
|
||||
prev_entry: ptr::null_mut(),
|
||||
symfile_addr: file.as_ptr(),
|
||||
symfile_size: file.len() as u64,
|
||||
}));
|
||||
|
||||
unsafe {
|
||||
register_gdb_jit_image(&mut *entry);
|
||||
}
|
||||
|
||||
Self { entry, file }
|
||||
}
|
||||
|
||||
/// JIT image used in registration
|
||||
@@ -67,20 +92,19 @@ impl GdbJitImageRegistration {
|
||||
impl Drop for GdbJitImageRegistration {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
unregister_gdb_jit_image(self.entry);
|
||||
unregister_gdb_jit_image(&mut *self.entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn register_gdb_jit_image(file: &[u8]) -> *mut JITCodeEntry {
|
||||
// Create a code entry for the file, which gives the start and size of the symbol file.
|
||||
let entry = Box::into_raw(Box::new(JITCodeEntry {
|
||||
next_entry: __jit_debug_descriptor.first_entry,
|
||||
prev_entry: ptr::null_mut(),
|
||||
symfile_addr: file.as_ptr(),
|
||||
symfile_size: file.len() as u64,
|
||||
}));
|
||||
unsafe impl Send for GdbJitImageRegistration {}
|
||||
unsafe impl Sync for GdbJitImageRegistration {}
|
||||
|
||||
unsafe fn register_gdb_jit_image(entry: *mut JITCodeEntry) {
|
||||
let _lock = GDB_REGISTRATION.lock().unwrap();
|
||||
|
||||
// Add it to the linked list in the JIT descriptor.
|
||||
(*entry).next_entry = __jit_debug_descriptor.first_entry;
|
||||
if !__jit_debug_descriptor.first_entry.is_null() {
|
||||
(*__jit_debug_descriptor.first_entry).prev_entry = entry;
|
||||
}
|
||||
@@ -93,10 +117,11 @@ unsafe fn register_gdb_jit_image(file: &[u8]) -> *mut JITCodeEntry {
|
||||
|
||||
__jit_debug_descriptor.action_flag = JIT_NOACTION;
|
||||
__jit_debug_descriptor.relevant_entry = ptr::null_mut();
|
||||
entry
|
||||
}
|
||||
|
||||
unsafe fn unregister_gdb_jit_image(entry: *mut JITCodeEntry) {
|
||||
let _lock = GDB_REGISTRATION.lock().unwrap();
|
||||
|
||||
// Remove the code entry corresponding to the code from the linked list.
|
||||
if !(*entry).prev_entry.is_null() {
|
||||
(*(*entry).prev_entry).next_entry = (*entry).next_entry;
|
||||
@@ -114,5 +139,4 @@ unsafe fn unregister_gdb_jit_image(entry: *mut JITCodeEntry) {
|
||||
|
||||
__jit_debug_descriptor.action_flag = JIT_NOACTION;
|
||||
__jit_debug_descriptor.relevant_entry = ptr::null_mut();
|
||||
let _box = Box::from_raw(entry);
|
||||
}
|
||||
|
||||
@@ -3,27 +3,16 @@
|
||||
|
||||
use crate::vmcontext::VMSharedSignatureIndex;
|
||||
use more_asserts::assert_lt;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{hash_map, HashMap};
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::RwLock;
|
||||
use wasmtime_environ::{ir, wasm::WasmFuncType};
|
||||
|
||||
/// 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 {
|
||||
// This structure is stored in a `Compiler` and is intended to be shared
|
||||
// across many instances. Ideally instances can themselves be sent across
|
||||
// threads, and ideally we can compile across many threads. As a result we
|
||||
// use interior mutability here with a lock to avoid having callers to
|
||||
// externally synchronize calls to compilation.
|
||||
inner: RwLock<Inner>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Inner {
|
||||
pub struct SignatureRegistry {
|
||||
wasm2index: HashMap<WasmFuncType, VMSharedSignatureIndex>,
|
||||
|
||||
// Maps the index to the original Wasm signature.
|
||||
@@ -34,35 +23,31 @@ struct Inner {
|
||||
}
|
||||
|
||||
impl SignatureRegistry {
|
||||
/// Create a new `SignatureRegistry`.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a signature and return its unique index.
|
||||
pub fn register(&self, wasm: WasmFuncType, native: ir::Signature) -> VMSharedSignatureIndex {
|
||||
let Inner {
|
||||
wasm2index,
|
||||
index2wasm,
|
||||
index2native,
|
||||
} = &mut *self.inner.write().unwrap();
|
||||
let len = wasm2index.len();
|
||||
pub fn register(
|
||||
&mut self,
|
||||
wasm: WasmFuncType,
|
||||
native: ir::Signature,
|
||||
) -> VMSharedSignatureIndex {
|
||||
let len = self.wasm2index.len();
|
||||
|
||||
*wasm2index.entry(wasm.clone()).or_insert_with(|| {
|
||||
// Keep `signature_hash` len under 2**32 -- VMSharedSignatureIndex::new(std::u32::MAX)
|
||||
// is reserved for VMSharedSignatureIndex::default().
|
||||
assert_lt!(
|
||||
len,
|
||||
std::u32::MAX as usize,
|
||||
"Invariant check: signature_hash.len() < std::u32::MAX"
|
||||
);
|
||||
let index = VMSharedSignatureIndex::new(u32::try_from(len).unwrap());
|
||||
index2wasm.insert(index, wasm);
|
||||
index2native.insert(index, native);
|
||||
index
|
||||
})
|
||||
match self.wasm2index.entry(wasm.clone()) {
|
||||
hash_map::Entry::Occupied(entry) => *entry.get(),
|
||||
hash_map::Entry::Vacant(entry) => {
|
||||
// Keep `signature_hash` len under 2**32 -- VMSharedSignatureIndex::new(std::u32::MAX)
|
||||
// is reserved for VMSharedSignatureIndex::default().
|
||||
assert_lt!(
|
||||
len,
|
||||
std::u32::MAX as usize,
|
||||
"Invariant check: signature_hash.len() < std::u32::MAX"
|
||||
);
|
||||
let index = VMSharedSignatureIndex::new(u32::try_from(len).unwrap());
|
||||
entry.insert(index);
|
||||
self.index2wasm.insert(index, wasm);
|
||||
self.index2native.insert(index, native);
|
||||
index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Looks up a shared native signature within this registry.
|
||||
@@ -70,7 +55,7 @@ impl SignatureRegistry {
|
||||
/// Note that for this operation to be semantically correct the `idx` must
|
||||
/// have previously come from a call to `register` of this same object.
|
||||
pub fn lookup_native(&self, idx: VMSharedSignatureIndex) -> Option<ir::Signature> {
|
||||
self.inner.read().unwrap().index2native.get(&idx).cloned()
|
||||
self.index2native.get(&idx).cloned()
|
||||
}
|
||||
|
||||
/// Looks up a shared Wasm signature within this registry.
|
||||
@@ -78,6 +63,6 @@ impl SignatureRegistry {
|
||||
/// Note that for this operation to be semantically correct the `idx` must
|
||||
/// have previously come from a call to `register` of this same object.
|
||||
pub fn lookup_wasm(&self, idx: VMSharedSignatureIndex) -> Option<WasmFuncType> {
|
||||
self.inner.read().unwrap().index2wasm.get(&idx).cloned()
|
||||
self.index2wasm.get(&idx).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user