Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SharedMemory & Atomics #3153

Merged
merged 28 commits into from
Nov 29, 2022
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a5f641b
Implemented shared memory for Wasmer in preparation for multithreading
john-sharratt Aug 17, 2022
34cba1c
Added spec threads tests, but disabling all for now
ptitSeb Aug 29, 2022
013d634
Enabled threads and already working tests
ptitSeb Aug 30, 2022
2b7bf2f
Added some missing emitter on x86_64 singlepass (for #3161)
ptitSeb Aug 31, 2022
9bbcc8a
Added helper functions for WaitNotify opcodes (for #3155)
ptitSeb Sep 6, 2022
bfc8d94
Added Wait/Notify opcode handling to Sinbglepass, and enable x86_64 t…
ptitSeb Sep 6, 2022
9472576
Map cranelift HeapMisaligned to wasmer UnalignedAtomic trap code (for…
ptitSeb Sep 6, 2022
bb69903
Fixed Notify helper funciton and opcode (for #3155 and #3158)
ptitSeb Sep 6, 2022
516da67
Added Wait/Notify opcodes for cranelift compiler (for #3156)
ptitSeb Sep 6, 2022
04a03fc
Fixed clippy warning on using Self
ptitSeb Sep 7, 2022
6ed8e48
Added Wait / Notify opcode to LLVM compiler (for #3157)
ptitSeb Sep 7, 2022
74721a8
Fixed linter
ptitSeb Sep 7, 2022
12ce309
Fixed Alignment check for Atomic access in LLVM compiler (for #3163)
ptitSeb Sep 7, 2022
8a0bd4b
Added some aarch64 atomic access emitter (not finished)
ptitSeb Sep 9, 2022
e4523f7
Fixed issue #3167 and added the relevant test
ptitSeb Oct 10, 2022
519f29b
Added exists function to imports
syrusakbary Oct 10, 2022
b86f129
Moved import_shared_memory to wasi specific, and use this function on…
ptitSeb Oct 10, 2022
3144ef2
Enable threads/imports test, but disabling multiple table individual …
ptitSeb Oct 12, 2022
f0c00e5
Merge branch 'master' into feat_sharedmemory
ptitSeb Nov 22, 2022
ffd6d6b
Merge branch 'master' into feat_sharedmemory
ptitSeb Nov 24, 2022
fce0ac9
Update lib/vm/src/instance/mod.rs
ptitSeb Nov 24, 2022
dba6a6a
Update lib/vm/src/instance/mod.rs
ptitSeb Nov 24, 2022
23029d8
Refactore the Wait/Notify HashMap for more clarity
ptitSeb Nov 25, 2022
4329e39
Added Trap when more the 2^32 waiters are in queue
ptitSeb Nov 25, 2022
f26e9ba
Added a Trap for unaligned waiter access
ptitSeb Nov 25, 2022
68b8995
Refactor atomic_notify with a new funciton to avoid duplication
ptitSeb Nov 25, 2022
32137b4
Merge branch 'master' into feat_sharedmemory
ptitSeb Nov 25, 2022
30a7d61
Merge branch 'master' into feat_sharedmemory
ptitSeb Nov 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ fn main() -> anyhow::Result<()> {
wast_processor,
)?;
test_directory_module(spectests, "tests/wast/spec/proposals/simd", wast_processor)?;
test_directory_module(
spectests,
"tests/wast/spec/proposals/threads",
wast_processor,
)?;
// test_directory_module(spectests, "tests/wast/spec/proposals/bulk-memory-operations", wast_processor)?;
Ok(())
})?;
Expand Down
6 changes: 5 additions & 1 deletion lib/api/src/js/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,11 @@ impl Imports {
/// Resolve and return a vector of imports in the order they are defined in the `module`'s source code.
///
/// This means the returned `Vec<Extern>` might be a subset of the imports contained in `self`.
pub fn imports_for_module(&self, module: &Module) -> Result<Vec<Extern>, LinkError> {
pub fn imports_for_module(
&self,
module: &Module,
_store: &mut impl AsStoreMut,
) -> Result<Vec<Extern>, LinkError> {
let mut ret = vec![];
for import in module.imports() {
if let Some(imp) = self
Expand Down
1 change: 0 additions & 1 deletion lib/api/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ pub use crate::js::value::Value as Val;

pub mod vm {
//! The `vm` module re-exports wasmer-vm types.

pub use crate::js::export::VMMemory;
}

Expand Down
18 changes: 14 additions & 4 deletions lib/api/src/sys/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,26 @@ impl Imports {
/// import_object.get_export("module", "name");
/// ```
pub fn get_export(&self, module: &str, name: &str) -> Option<Extern> {
if self
.map
.contains_key(&(module.to_string(), name.to_string()))
{
if self.exists(module, name) {
let ext = &self.map[&(module.to_string(), name.to_string())];
return Some(ext.clone());
}
None
}

/// Returns if an export exist for a given module and name.
///
/// # Usage
/// ```no_run
/// # use wasmer::Imports;
/// let mut import_object = Imports::new();
/// import_object.exists("module", "name");
/// ```
pub fn exists(&self, module: &str, name: &str) -> bool {
self.map
.contains_key(&(module.to_string(), name.to_string()))
}

/// Returns true if the Imports contains namespace with the provided name.
pub fn contains_namespace(&self, name: &str) -> bool {
self.map.keys().any(|(k, _)| (k == name))
Expand Down
4 changes: 2 additions & 2 deletions lib/api/src/sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ pub mod vm {
//! The `vm` module re-exports wasmer-vm types.

pub use wasmer_vm::{
MemoryError, MemoryStyle, TableStyle, VMExtern, VMMemory, VMMemoryDefinition, VMTable,
VMTableDefinition,
MemoryError, MemoryStyle, TableStyle, VMExtern, VMMemory, VMMemoryDefinition,
VMOwnedMemory, VMSharedMemory, VMTable, VMTableDefinition,
};
}

Expand Down
7 changes: 4 additions & 3 deletions lib/cli/src/commands/run/wasi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use std::collections::BTreeSet;
use std::path::PathBuf;
use wasmer::{AsStoreMut, FunctionEnv, Instance, Module, RuntimeError, Value};
use wasmer_wasi::{
get_wasi_versions, import_object_for_all_wasi_versions, is_wasix_module, WasiEnv, WasiError,
WasiState, WasiVersion,
get_wasi_versions, import_object_for_all_wasi_versions, is_wasix_module,
wasi_import_shared_memory, WasiEnv, WasiError, WasiState, WasiVersion,
};

use clap::Parser;
Expand Down Expand Up @@ -104,7 +104,8 @@ impl Wasi {
is_wasix_module(module),
std::sync::atomic::Ordering::Release,
);
let import_object = import_object_for_all_wasi_versions(store, &wasi_env.env);
let mut import_object = import_object_for_all_wasi_versions(store, &wasi_env.env);
wasi_import_shared_memory(&mut import_object, module, store);
let instance = Instance::new(store, module, &import_object)?;
let memory = instance.exports.get_memory("memory")?;
wasi_env.data_mut(store).set_memory(memory.clone());
Expand Down
2 changes: 1 addition & 1 deletion lib/compiler-cranelift/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ fn translate_ir_trapcode(trap: ir::TrapCode) -> TrapCode {
match trap {
ir::TrapCode::StackOverflow => TrapCode::StackOverflow,
ir::TrapCode::HeapOutOfBounds => TrapCode::HeapAccessOutOfBounds,
ir::TrapCode::HeapMisaligned => TrapCode::HeapMisaligned,
ir::TrapCode::HeapMisaligned => TrapCode::UnalignedAtomic,
ir::TrapCode::TableOutOfBounds => TrapCode::TableAccessOutOfBounds,
ir::TrapCode::IndirectCallToNull => TrapCode::IndirectCallToNull,
ir::TrapCode::BadSignature => TrapCode::BadSignature,
Expand Down
189 changes: 174 additions & 15 deletions lib/compiler-cranelift/src/func_environ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ pub struct FuncEnvironment<'module_environment> {
/// The external function signature for implementing wasm's `table.fill`.
table_fill_sig: Option<ir::SigRef>,

/// The external function signature for implementing wasm's `memory32.atomic.wait32`.
memory32_atomic_wait32_sig: Option<ir::SigRef>,

/// The external function signature for implementing wasm's `memory32.atomic.wait64`.
memory32_atomic_wait64_sig: Option<ir::SigRef>,

/// The external function signature for implementing wasm's `memory32.atomic.notify`.
memory32_atomic_notify_sig: Option<ir::SigRef>,

/// Offsets to struct fields accessed by JIT code.
offsets: VMOffsets,

Expand Down Expand Up @@ -143,6 +152,9 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
data_drop_sig: None,
func_ref_sig: None,
table_fill_sig: None,
memory32_atomic_wait32_sig: None,
memory32_atomic_wait64_sig: None,
memory32_atomic_notify_sig: None,
offsets: VMOffsets::new(target_config.pointer_bytes(), module),
memory_styles,
table_styles,
Expand Down Expand Up @@ -684,6 +696,139 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
(sig, VMBuiltinFunctionIndex::get_data_drop_index())
}

fn get_memory32_atomic_wait32_sig(&mut self, func: &mut Function) -> ir::SigRef {
let sig = self.memory32_atomic_wait32_sig.unwrap_or_else(|| {
func.import_signature(Signature {
params: vec![
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
// Memory Index
AbiParam::new(I32),
// Dst
AbiParam::new(I32),
// Val
AbiParam::new(I32),
// Timeout
AbiParam::new(I64),
],
returns: vec![AbiParam::new(I32)],
call_conv: self.target_config.default_call_conv,
})
});
self.memory32_atomic_wait32_sig = Some(sig);
sig
}

/// Return the memory.atomic.wait32 function signature to call for the given index,
/// along with the translated index value to pass to it
/// and its index in `VMBuiltinFunctionsArray`.
fn get_memory_atomic_wait32_func(
&mut self,
func: &mut Function,
index: MemoryIndex,
) -> (ir::SigRef, usize, VMBuiltinFunctionIndex) {
if self.module.is_imported_memory(index) {
(
self.get_memory32_atomic_wait32_sig(func),
index.index(),
VMBuiltinFunctionIndex::get_imported_memory_atomic_wait32_index(),
)
} else {
(
self.get_memory32_atomic_wait32_sig(func),
self.module.local_memory_index(index).unwrap().index(),
VMBuiltinFunctionIndex::get_memory_atomic_wait32_index(),
)
}
}

fn get_memory32_atomic_wait64_sig(&mut self, func: &mut Function) -> ir::SigRef {
let sig = self.memory32_atomic_wait64_sig.unwrap_or_else(|| {
func.import_signature(Signature {
params: vec![
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
// Memory Index
AbiParam::new(I32),
// Dst
AbiParam::new(I32),
// Val
AbiParam::new(I64),
// Timeout
AbiParam::new(I64),
],
returns: vec![AbiParam::new(I32)],
call_conv: self.target_config.default_call_conv,
})
});
self.memory32_atomic_wait64_sig = Some(sig);
sig
}

/// Return the memory.atomic.wait64 function signature to call for the given index,
/// along with the translated index value to pass to it
/// and its index in `VMBuiltinFunctionsArray`.
fn get_memory_atomic_wait64_func(
&mut self,
func: &mut Function,
index: MemoryIndex,
) -> (ir::SigRef, usize, VMBuiltinFunctionIndex) {
if self.module.is_imported_memory(index) {
(
self.get_memory32_atomic_wait64_sig(func),
index.index(),
VMBuiltinFunctionIndex::get_imported_memory_atomic_wait64_index(),
)
} else {
(
self.get_memory32_atomic_wait64_sig(func),
self.module.local_memory_index(index).unwrap().index(),
VMBuiltinFunctionIndex::get_memory_atomic_wait64_index(),
)
}
}

fn get_memory32_atomic_notify_sig(&mut self, func: &mut Function) -> ir::SigRef {
let sig = self.memory32_atomic_notify_sig.unwrap_or_else(|| {
func.import_signature(Signature {
params: vec![
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
// Memory Index
AbiParam::new(I32),
// Dst
AbiParam::new(I32),
// Count
AbiParam::new(I32),
],
returns: vec![AbiParam::new(I32)],
call_conv: self.target_config.default_call_conv,
})
});
self.memory32_atomic_notify_sig = Some(sig);
sig
}

/// Return the memory.atomic.notify function signature to call for the given index,
/// along with the translated index value to pass to it
/// and its index in `VMBuiltinFunctionsArray`.
fn get_memory_atomic_notify_func(
&mut self,
func: &mut Function,
index: MemoryIndex,
) -> (ir::SigRef, usize, VMBuiltinFunctionIndex) {
if self.module.is_imported_memory(index) {
(
self.get_memory32_atomic_notify_sig(func),
index.index(),
VMBuiltinFunctionIndex::get_imported_memory_atomic_notify_index(),
)
} else {
(
self.get_memory32_atomic_notify_sig(func),
self.module.local_memory_index(index).unwrap().index(),
VMBuiltinFunctionIndex::get_memory_atomic_notify_index(),
)
}
}

/// Translates load of builtin function and returns a pair of values `vmctx`
/// and address of the loaded function.
fn translate_load_builtin_function_address(
Expand Down Expand Up @@ -1389,29 +1534,43 @@ impl<'module_environment> BaseFuncEnvironment for FuncEnvironment<'module_enviro

fn translate_atomic_wait(
&mut self,
_pos: FuncCursor,
_index: MemoryIndex,
mut pos: FuncCursor,
index: MemoryIndex,
_heap: ir::Heap,
_addr: ir::Value,
_expected: ir::Value,
_timeout: ir::Value,
addr: ir::Value,
expected: ir::Value,
timeout: ir::Value,
) -> WasmResult<ir::Value> {
Err(WasmError::Unsupported(
"wasm atomics (fn translate_atomic_wait)".to_string(),
))
let (func_sig, index_arg, func_idx) = if pos.func.dfg.value_type(expected) == I64 {
self.get_memory_atomic_wait64_func(pos.func, index)
} else {
self.get_memory_atomic_wait32_func(pos.func, index)
};
let memory_index = pos.ins().iconst(I32, index_arg as i64);
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
let call_inst = pos.ins().call_indirect(
func_sig,
func_addr,
&[vmctx, memory_index, addr, expected, timeout],
);
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
}

fn translate_atomic_notify(
&mut self,
_pos: FuncCursor,
_index: MemoryIndex,
mut pos: FuncCursor,
index: MemoryIndex,
_heap: ir::Heap,
_addr: ir::Value,
_count: ir::Value,
addr: ir::Value,
count: ir::Value,
) -> WasmResult<ir::Value> {
Err(WasmError::Unsupported(
"wasm atomics (fn translate_atomic_notify)".to_string(),
))
let (func_sig, index_arg, func_idx) = self.get_memory_atomic_notify_func(pos.func, index);
let memory_index = pos.ins().iconst(I32, index_arg as i64);
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
let call_inst =
pos.ins()
.call_indirect(func_sig, func_addr, &[vmctx, memory_index, addr, count]);
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
}

fn get_global_type(&self, global_index: GlobalIndex) -> Option<WasmerType> {
Expand Down
Loading