From f94ad61ba5b5ed00738ff45b102db850c3765786 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 23 Aug 2024 13:37:39 +0000 Subject: [PATCH] Rebase + changes to enable wait notify execution. --- crates/interpreter/Cargo.lock | 10 ++ crates/interpreter/Cargo.toml | 4 +- crates/interpreter/src/exec.rs | 159 ++++++++++++++++++++++++------- crates/interpreter/src/module.rs | 7 +- crates/interpreter/src/parser.rs | 3 +- crates/interpreter/tests/spec.rs | 153 ++++++++++++++++++----------- crates/runner-host/Cargo.lock | 1 + crates/scheduler/Cargo.lock | 1 + third_party/WebAssembly/threads | 2 +- 9 files changed, 249 insertions(+), 91 deletions(-) diff --git a/crates/interpreter/Cargo.lock b/crates/interpreter/Cargo.lock index 7247e9104..cdf4c65ce 100644 --- a/crates/interpreter/Cargo.lock +++ b/crates/interpreter/Cargo.lock @@ -88,6 +88,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "portable-atomic", +] + [[package]] name = "syn" version = "2.0.66" @@ -121,6 +130,7 @@ dependencies = [ "num_enum", "paste", "portable-atomic", + "spin", "wast", ] diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml index d1984d3a6..fabe9d451 100644 --- a/crates/interpreter/Cargo.toml +++ b/crates/interpreter/Cargo.toml @@ -16,13 +16,15 @@ libm = { version = "0.2.8", default-features = false, optional = true } lru = { version = "0.12.3", default-features = false, optional = true } num_enum = { version = "0.7.2", default-features = false } paste = { version = "1.0.15", default-features = false } -portable-atomic = { version = "1.6.0", default-features = false } +portable-atomic = { version = "1.6.0" }# , default-features = false } +spin = { version = "0.9.8", default-features = false, features = ["spin_mutex", "portable_atomic"]} [dev-dependencies] lazy_static = "1.4.0" wast = "214.0.0" [features] +# default = ["threads", "debug"] # Enable debugging features (only works for targets with std). debug = [] # Use safe operations when time-of-use and time-of-check differ. diff --git a/crates/interpreter/src/exec.rs b/crates/interpreter/src/exec.rs index 01f0b078d..925a287c6 100644 --- a/crates/interpreter/src/exec.rs +++ b/crates/interpreter/src/exec.rs @@ -17,7 +17,7 @@ use alloc::vec; use alloc::vec::Vec; #[cfg(feature = "threads")] -use portable_atomic::{AtomicU16, AtomicU32, AtomicU64, AtomicU8, Ordering}; +use portable_atomic::{AtomicBool, AtomicU16, AtomicU32, AtomicU64, AtomicU8, Ordering}; use crate::error::*; use crate::module::*; @@ -61,6 +61,7 @@ pub struct Store<'m> { // functions in `funcs` is stored to limit normal linking to that part. func_default: Option<(&'m str, usize)>, threads: Vec>, + lock: spin::Mutex<()>, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -102,6 +103,7 @@ impl<'m> Default for Store<'m> { funcs: vec![], func_default: None, threads: vec![], + lock: spin::Mutex::new(()), } } } @@ -120,42 +122,45 @@ impl<'m> Store<'m> { pub fn instantiate( &mut self, module: Module<'m>, memory: &'m mut [u8], ) -> Result { + let lock = self.lock.lock(); let inst_id = self.insts.len(); self.insts.push(Instance::default()); - self.last_inst().module = module; - for import in self.last_inst().module.imports() { - let type_ = import.type_(&self.last_inst().module); + drop(lock); + self.last_inst(inst_id).module = module; + for import in self.last_inst(inst_id).module.imports() { + let type_ = import.type_(&self.last_inst(inst_id).module); let id = self.resolve(&import, type_)?; match import.desc { - ImportDesc::Func(_) => self.last_inst().funcs.ext.push(id), - ImportDesc::Table(_) => self.last_inst().tables.ext.push(id), - ImportDesc::Mem(_) => self.last_inst().mems.ext.push(id), - ImportDesc::Global(_) => self.last_inst().globals.ext.push(id), + ImportDesc::Func(_) => self.last_inst(inst_id).funcs.ext.push(id), + ImportDesc::Table(_) => self.last_inst(inst_id).tables.ext.push(id), + ImportDesc::Mem(_) => self.last_inst(inst_id).mems.ext.push(id), + ImportDesc::Global(_) => self.last_inst(inst_id).globals.ext.push(id), } } - if let Some(mut parser) = self.last_inst().module.section(SectionId::Table) { + if let Some(mut parser) = self.last_inst(inst_id).module.section(SectionId::Table) { for _ in 0 .. parser.parse_vec().into_ok() { - (self.last_inst().tables.int).push(Table::new(parser.parse_tabletype().into_ok())); + (self.last_inst(inst_id).tables.int) + .push(Table::new(parser.parse_tabletype().into_ok())); } } - if let Some(mut parser) = self.last_inst().module.section(SectionId::Memory) { + if let Some(mut parser) = self.last_inst(inst_id).module.section(SectionId::Memory) { match parser.parse_vec().into_ok() { 0 => (), 1 => { let limits = parser.parse_memtype().into_ok(); - self.last_inst().mems.int.init(memory, limits)?; + self.last_inst(inst_id).mems.int.init(memory, limits)?; } _ => unimplemented!(), } } - if let Some(mut parser) = self.last_inst().module.section(SectionId::Global) { + if let Some(mut parser) = self.last_inst(inst_id).module.section(SectionId::Global) { for _ in 0 .. parser.parse_vec().into_ok() { parser.parse_globaltype().into_ok(); let value = Thread::const_expr(self, inst_id, &mut parser); - self.last_inst().globals.int.push(Global::new(value)); + self.last_inst(inst_id).globals.int.push(Global::new(value)); } } - if let Some(mut parser) = self.last_inst().module.section(SectionId::Element) { + if let Some(mut parser) = self.last_inst(inst_id).module.section(SectionId::Element) { for _ in 0 .. parser.parse_vec().into_ok() { // TODO: This is inefficient because we only need init for active segments. let mut elem = ComputeElem::new(self, inst_id); @@ -171,10 +176,10 @@ impl<'m> Store<'m> { } ElemMode::Declarative => true, }; - self.last_inst().elems.push(drop); + self.last_inst(inst_id).elems.push(drop); } } - if let Some(mut parser) = self.last_inst().module.section(SectionId::Data) { + if let Some(mut parser) = self.last_inst(inst_id).module.section(SectionId::Data) { for _ in 0 .. parser.parse_vec().into_ok() { let mut data = ComputeData::new(self, inst_id); parser.parse_data(&mut data).into_ok(); @@ -188,10 +193,10 @@ impl<'m> Store<'m> { true } }; - self.last_inst().datas.push(drop); + self.last_inst(inst_id).datas.push(drop); } } - if let Some(mut parser) = self.last_inst().module.section(SectionId::Start) { + if let Some(mut parser) = self.last_inst(inst_id).module.section(SectionId::Start) { let x = parser.parse_funcidx().into_ok(); let ptr = self.func_ptr(inst_id, x); let inst_id = ptr.instance().unwrap_wasm(); @@ -213,6 +218,8 @@ impl<'m> Store<'m> { pub fn invoke<'a>( &'a mut self, inst: InstId, name: &str, args: Vec, ) -> Result, Error> { + #[cfg(feature = "debug")] + eprintln!("DBK invoke fn {:?}, instid: {:?}", name, inst); let inst_id = self.inst_id(inst)?; let inst = &self.insts[inst_id]; let ptr = match inst.module.export(name).ok_or_else(not_found)? { @@ -500,8 +507,8 @@ struct Continuation<'m> { } impl<'m> Store<'m> { - fn last_inst(&mut self) -> &mut Instance<'m> { - self.insts.last_mut().unwrap() + fn last_inst(&mut self, inst_id: usize) -> &mut Instance<'m> { + &mut self.insts[inst_id] } fn inst_id(&self, inst: InstId) -> Result { @@ -556,6 +563,8 @@ impl<'m> Store<'m> { } fn resolve(&mut self, import: &Import<'m>, imp_type_: ExternType<'m>) -> Result { + #[cfg(feature = "debug")] + eprintln!("DBK import {:?}", import); let host_name = HostName { module: import.module, name: import.name }; let mut found = None; let funcs_len = match self.func_default { @@ -822,6 +831,8 @@ impl<'m> Thread<'m> { } LocalGet(x) => { let v = self.frame().locals[x as usize]; + #[cfg(feature = "debug")] + eprintln!("DBK local read: {:?}, expected: {:?}", x, v); self.push_value(v); } LocalSet(x) => { @@ -979,12 +990,18 @@ impl<'m> Thread<'m> { AtomicFence => (), #[cfg(feature = "threads")] AtomicLoad(n, m) => { - self.atomic_load(store.mem(inst_id, 0), NumType::i(n), n.into(), m)? + match self.atomic_load(store.mem(inst_id, 0), NumType::i(n), n.into(), m) { + Ok(_) => Ok(()), + Err(e) => Err(e), + }? } #[cfg(feature = "threads")] AtomicLoad_(b, m) => { - self.atomic_load(store.mem(inst_id, 0), NumType::i(b.into()), b.into(), m)? + match self.atomic_load(store.mem(inst_id, 0), NumType::i(b.into()), b.into(), m) { + Ok(_) => Ok(()), + Err(e) => Err(e), + }? } #[cfg(feature = "threads")] @@ -1148,9 +1165,9 @@ impl<'m> Thread<'m> { } } - fn mem_slice<'a>( - &mut self, mem: &'a mut Memory<'m>, m: MemArg, i: u32, len: usize, align: bool, - ) -> Option<&'a mut [u8]> { + fn mem_slice_addr<'a>( + &mut self, mem: &'a Memory<'m>, m: MemArg, i: u32, len: usize, align: bool, + ) -> Option { let ea = i.checked_add(m.offset)?; if ea.checked_add(len as u32)? > mem.len() { memory_too_small(ea as usize, len, mem); @@ -1159,6 +1176,13 @@ impl<'m> Thread<'m> { if align && ea % len as u32 != 0 { return None; } + Some(ea) + } + + fn mem_slice<'a>( + &mut self, mem: &'a mut Memory<'m>, m: MemArg, i: u32, len: usize, align: bool, + ) -> Option<&'a mut [u8]> { + let ea = self.mem_slice_addr(mem, m, i, len, align)?; Some(&mut mem.data[ea as usize ..][.. len]) } @@ -1194,6 +1218,8 @@ impl<'m> Thread<'m> { (NumType::F64, 64, _) => convert!(F64, u64, u64), _ => unreachable!(), }; + #[cfg(feature = "debug")] + eprintln!("DBK load: {:?}, val: {:?}", i, c); self.push_value(c); Ok(()) } @@ -1431,11 +1457,13 @@ impl<'m> Thread<'m> { let i = self.pop_value().unwrap_i32(); // Trap if memory access is OOB. - let _mem = match self.mem_slice(mem, m, i, 4, true) { + let _mem = match self.mem_slice_addr(mem, m, i, 4, true) { None => return Err(trap()), Some(x) => x, }; - self.push_value(count); + + let notified = mem.share_data.notify(_mem, count.unwrap_i32()); + self.push_value(Val::I32(notified)); Ok(()) } @@ -1448,23 +1476,36 @@ impl<'m> Thread<'m> { } let _timeout = self.pop_value().unwrap_i64(); let expected = self.pop_value(); - self.atomic_load(mem, t, n, m)?; + let ea = self.atomic_load(mem, t, n, m)?; let read = self.pop_value(); + #[cfg(feature = "debug")] + eprintln!("DBK wait value read: {:?}, expected: {:?}", read, expected); if read != expected { self.push_value(Val::I32(1)); + #[cfg(feature = "debug")] + eprintln!("DBK wait value read: {:?} != expected: {:?}", read, expected); + return Ok(()); } + let waiting_thread = mem.share_data.wait(ea); + while !waiting_thread.notify.load(portable_atomic::Ordering::SeqCst) {} + self.push_value(Val::I32(0)); Ok(()) } #[cfg(feature = "threads")] fn atomic_load( - &mut self, mem: &mut Memory<'m>, t: NumType, n: usize, m: MemArg, - ) -> Result<(), Error> { + &mut self, mem_param: &mut Memory<'m>, t: NumType, n: usize, m: MemArg, + ) -> Result { let i = self.pop_value().unwrap_i32(); - let mem = match self.mem_slice(mem, m, i, n / 8, true) { + let ea = match self.mem_slice_addr(mem_param, m, i, n / 8, true) { + None => return Err(trap()), + Some(x) => x, + }; + let mem = match self.mem_slice(mem_param, m, i, n / 8, true) { None => return Err(trap()), Some(x) => x, }; + macro_rules! convert { ($T:ident, $t:ident, $s:ident) => {{ let ptr = mem.as_mut_ptr() as *mut $s; @@ -1483,7 +1524,7 @@ impl<'m> Thread<'m> { _ => unreachable!(), }; self.push_value(c); - Ok(()) + Ok(ea) } #[cfg(feature = "threads")] @@ -1651,6 +1692,56 @@ impl Table { } } +#[cfg(feature = "threads")] +#[derive(Debug, Default)] +#[allow(dead_code)] +struct WaitingThread { + address: u32, + thread_id: u32, + pub notify: AtomicBool, +} + +#[cfg(feature = "threads")] +#[derive(Debug, Default)] +struct ShareData { + queue: Vec, + lock: spin::Mutex<()>, + max_thread_id: u32, +} + +#[cfg(feature = "threads")] +impl ShareData { + fn wait(&mut self, address: u32) -> &WaitingThread { + let _lock = self.lock.lock(); + self.queue.push(WaitingThread { + address, + thread_id: self.max_thread_id, + notify: false.into(), + }); + self.max_thread_id += 1; + return self.queue.last().unwrap(); + } + + fn notify(&mut self, address: u32, number: u32) -> u32 { + let _lock = self.lock.lock(); + let mut notified = 0; + for thread in self.queue.iter_mut() { + #[cfg(feature = "debug")] + eprintln!("DBK notify {:?}", thread); + if notified >= number { + break; + } + if thread.address == address && !thread.notify.load(portable_atomic::Ordering::SeqCst) { + thread.notify.store(true, portable_atomic::Ordering::SeqCst); + notified += 1 + } + #[cfg(feature = "debug")] + eprintln!("DBK notify {:?}", thread); + } + notified + } +} + #[derive(Debug, Default)] struct Memory<'m> { // May be shorter than the maximum length for the module, but not larger. @@ -1665,6 +1756,8 @@ struct Memory<'m> { // https://github.com/google/wasefire/pull/513#discussion_r1652977484 #[cfg(feature = "threads")] share: bool, + #[cfg(feature = "threads")] + share_data: ShareData, } impl<'m> Memory<'m> { diff --git a/crates/interpreter/src/module.rs b/crates/interpreter/src/module.rs index b4d5dee6a..045413a47 100644 --- a/crates/interpreter/src/module.rs +++ b/crates/interpreter/src/module.rs @@ -41,6 +41,9 @@ enum CacheKey { Skip { ptr: *const u8, depth: LabelIdx }, } +unsafe impl Send for CacheKey {} +unsafe impl Sync for CacheKey {} + union CacheValue { skip: usize, // delta } @@ -106,7 +109,7 @@ impl<'m> Module<'m> { pub type Parser<'m> = parser::Parser<'m, Use>; impl<'m> Module<'m> { - pub(crate) fn section(&self, expected_id: SectionId) -> Option> { + pub fn section(&self, expected_id: SectionId) -> Option> { let mut parser = unsafe { Parser::new(self.binary) }; loop { if parser.is_empty() { @@ -171,6 +174,8 @@ impl<'m> Module<'m> { } pub(crate) fn export(&self, expected_name: &str) -> Option { + #[cfg(feature = "debug")] + eprintln!("DBK export {}", expected_name); let mut parser = self.section(SectionId::Export).unwrap(); for _ in 0 .. parser.parse_vec().into_ok() { let actual_name = parser.parse_name().into_ok(); diff --git a/crates/interpreter/src/parser.rs b/crates/interpreter/src/parser.rs index 464566e15..929a9460c 100644 --- a/crates/interpreter/src/parser.rs +++ b/crates/interpreter/src/parser.rs @@ -702,7 +702,8 @@ impl<'m, M: Mode> Parser<'m, M> { /// Maximum number of locals (must be less than 2^32). // NOTE: This should be configurable. -const MAX_LOCALS: usize = 100; +// NOTE: Spec test skip_stack_guard_page needs lots of locals. +const MAX_LOCALS: usize = 10000; fn check_eq(x: T, y: T) -> MResult<(), M> { M::check(|| x == y) diff --git a/crates/interpreter/tests/spec.rs b/crates/interpreter/tests/spec.rs index 5886cbf29..8b9e46091 100644 --- a/crates/interpreter/tests/spec.rs +++ b/crates/interpreter/tests/spec.rs @@ -13,9 +13,13 @@ // limitations under the License. #![feature(int_roundings)] +#![feature(get_mut_unchecked)] #![allow(unused_crate_dependencies)] +use std::borrow::BorrowMut; use std::collections::HashMap; +use std::sync::Arc; +use std::{thread, vec}; use lazy_static::lazy_static; use wasefire_interpreter::*; @@ -24,44 +28,79 @@ use wast::lexer::Lexer; use wast::token::Id; use wast::{parser, QuoteWat, Wast, WastArg, WastDirective, WastExecute, WastInvoke, WastRet, Wat}; -fn test(repo: &str, name: &str) { - let path = format!("../../third_party/WebAssembly/{repo}/test/core/{name}.wast"); - let content = std::fs::read_to_string(path).unwrap(); - let mut lexer = Lexer::new(&content); - lexer.allow_confusing_unicode(true); - let buffer = parser::ParseBuffer::new_with_lexer(lexer).unwrap(); - let wast: Wast = parser::parse(&buffer).unwrap(); +fn parse_directives(name: &str, directives: Vec, root_env: Option>) { let layout = std::alloc::Layout::from_size_align(pool_size(name), MEMORY_ALIGN).unwrap(); - let pool = unsafe { std::slice::from_raw_parts_mut(std::alloc::alloc(layout), layout.size()) }; - let mut env = Env::new(pool); - env.instantiate("spectest", &SPECTEST); - env.register_name("spectest", None); - assert!(env.inst.is_ok()); - for directive in wast.directives { + let pool = + unsafe { std::slice::from_raw_parts_mut(std::alloc::alloc_zeroed(layout), layout.size()) }; + let mut env_arc = Arc::new(Env::new(pool)); + let first_call = root_env.is_some(); + if root_env.is_some() { + env_arc = root_env.unwrap(); + } + let env_arc_cpy = env_arc.clone(); + let env = unsafe { Arc::>::get_mut_unchecked(env_arc.borrow_mut()) }; + let mut threads = Vec::new(); + let mut register_directives = Vec::new(); + if !first_call { + let inst_id = env.instantiate("spectest", &SPECTEST); + env.register_name("spectest", None, Some(inst_id)); + } + let mut inst = None; + for directive in directives { eprintln!("{name}:{}", directive.span().offset()); match directive { WastDirective::Wat(QuoteWat::Wat(Wat::Module(mut m))) => { - env.instantiate(name, &m.encode().unwrap()); - if !matches!(env.inst, Err(Error::Unsupported(_))) { - env.register_id(m.id, env.inst.unwrap()); - } + inst = Some(env.instantiate(name, &m.encode().unwrap())); + env.register_id(m.id, inst.unwrap()); + } + WastDirective::Wat(mut wat) => { + inst = Some(env.instantiate(name, &wat.encode().unwrap())); } - WastDirective::Wat(mut wat) => env.instantiate(name, &wat.encode().unwrap()), WastDirective::AssertMalformed { module, .. } => assert_malformed(module), WastDirective::AssertInvalid { module, .. } => assert_invalid(module), WastDirective::AssertReturn { exec, results, .. } => { - assert_return(&mut env, exec, results) + assert_return(env, exec, results, inst) + } + WastDirective::AssertTrap { exec, .. } => assert_trap(env, exec, inst), + WastDirective::Invoke(invoke) => assert_invoke(env, invoke, inst), + WastDirective::AssertExhaustion { call, .. } => assert_exhaustion(env, call, inst), + WastDirective::Register { name, module, span } => { + env.register_name(name, module, inst); + register_directives.push(WastDirective::Register { name, module, span }); + } + WastDirective::AssertUnlinkable { module, .. } => assert_unlinkable(env, module), + WastDirective::Thread(thread) => { + println!("DBK wast-thread {:?}", thread.name); + threads.push(thread); + } + WastDirective::Wait { span, thread } => { + if !first_call { + thread::scope(|s| { + for t in threads.drain(0 ..) { + let env_cpy = env_arc_cpy.clone(); + s.spawn(|| { + parse_directives(name, t.directives, Some(env_cpy)); + }); + } + }); + } + println!("DBK wast-wait {:?}, {:?}", thread, span) } - WastDirective::AssertTrap { exec, .. } => assert_trap(&mut env, exec), - WastDirective::Invoke(invoke) => assert_invoke(&mut env, invoke), - WastDirective::AssertExhaustion { call, .. } => assert_exhaustion(&mut env, call), - WastDirective::Register { name, module, .. } => env.register_name(name, module), - WastDirective::AssertUnlinkable { module, .. } => assert_unlinkable(&mut env, module), _ => unimplemented!("{:?}", directive), } } } +fn test(repo: &str, name: &str) { + let path = format!("../../third_party/WebAssembly/{repo}/test/core/{name}.wast"); + let content = std::fs::read_to_string(path).unwrap(); + let mut lexer = Lexer::new(&content); + lexer.allow_confusing_unicode(true); + let buffer = parser::ParseBuffer::new_with_lexer(lexer).unwrap(); + let wast: Wast = parser::parse(&buffer).unwrap(); + parse_directives(name, wast.directives, None); +} + fn pool_size(name: &str) -> usize { match name { "address" => 0x200000, @@ -100,16 +139,17 @@ fn mem_size(name: &str) -> usize { struct Env<'m> { pool: &'m mut [u8], store: Store<'m>, - inst: Result, map: HashMap, InstId>, + lock: spin::Mutex<()>, } impl<'m> Env<'m> { fn new(pool: &'m mut [u8]) -> Self { - Env { pool, store: Store::default(), inst: Err(Error::Invalid), map: HashMap::new() } + Env { pool, store: Store::default(), map: HashMap::new(), lock: spin::Mutex::new(()) } } fn alloc(&mut self, size: usize) -> &'m mut [u8] { + let _lock = self.lock.lock(); if self.pool.len() < size { panic!("pool is too small"); } @@ -120,14 +160,6 @@ impl<'m> Env<'m> { &mut result[.. size] } - fn set_inst(&mut self, inst: Result) { - match inst { - Ok(_) | Err(Error::Unsupported(_)) => (), - Err(e) => panic!("{e:?}"), - } - self.inst = inst; - } - fn maybe_instantiate(&mut self, name: &str, wasm: &[u8]) -> Result { let module = self.alloc(wasm.len()); module.copy_from_slice(wasm); @@ -139,9 +171,10 @@ impl<'m> Env<'m> { self.store.instantiate(module, memory) } - fn instantiate(&mut self, name: &str, wasm: &[u8]) { + fn instantiate(&mut self, name: &str, wasm: &[u8]) -> InstId { let inst = self.maybe_instantiate(name, wasm); - self.set_inst(inst); + println!("DBK (spec.rs): instantiate inst_id={:?} for module {:?}", inst, name); + inst.unwrap() } fn invoke(&mut self, inst_id: InstId, name: &str, args: Vec) -> Result, Error> { @@ -151,8 +184,10 @@ impl<'m> Env<'m> { }) } - fn register_name(&mut self, name: &'m str, module: Option>) { - let inst_id = self.inst.unwrap(); + fn register_name(&mut self, name: &'m str, module: Option>, inst_id: Option) { + println!("DBK (spec.rs): register_name name={:?} for module {:?}", name, module); + + let inst_id = self.inst_id(module, inst_id).unwrap(); self.register_id(module, inst_id); self.store.set_name(inst_id, name).unwrap(); } @@ -163,10 +198,10 @@ impl<'m> Env<'m> { } } - fn inst_id(&self, id: Option) -> Result { + fn inst_id(&self, id: Option, default: Option) -> Result { match id { Some(x) => Ok(self.map[&x]), - None => self.inst, + None => Ok(default.unwrap()), } } } @@ -256,8 +291,8 @@ fn spectest() -> Vec { wasm } -fn assert_return(env: &mut Env, exec: WastExecute, expected: Vec) { - let actual = wast_execute(env, exec).unwrap(); +fn assert_return(env: &mut Env, exec: WastExecute, expected: Vec, inst: Option) { + let actual = wast_execute(env, exec, inst).unwrap(); assert_eq!(actual.len(), expected.len()); for (actual, expected) in actual.into_iter().zip(expected.into_iter()) { use wast::core::HeapType; @@ -294,12 +329,12 @@ fn assert_return(env: &mut Env, exec: WastExecute, expected: Vec) { } } -fn assert_trap(env: &mut Env, exec: WastExecute) { - assert_eq!(wast_execute(env, exec), Err(Error::Trap)); +fn assert_trap(env: &mut Env, exec: WastExecute, inst: Option) { + assert_eq!(wast_execute(env, exec, inst), Err(Error::Trap)); } -fn assert_invoke(env: &mut Env, invoke: WastInvoke) { - assert_eq!(wast_invoke(env, invoke), Ok(Vec::new())); +fn assert_invoke(env: &mut Env, invoke: WastInvoke, inst: Option) { + assert_eq!(wast_invoke(env, invoke, inst), Ok(Vec::new())); } fn assert_malformed(mut wat: QuoteWat) { @@ -319,8 +354,8 @@ fn assert_invalid(mut wat: QuoteWat) { } } -fn assert_exhaustion(env: &mut Env, call: WastInvoke) { - let result = wast_invoke(env, call); +fn assert_exhaustion(env: &mut Env, call: WastInvoke, inst: Option) { + let result = wast_invoke(env, call, inst); if !matches!(result, Err(Error::Unsupported(_))) { assert_eq!(result, Err(Error::Trap)); } @@ -333,21 +368,23 @@ fn assert_unlinkable(env: &mut Env, mut wat: Wat) { } } -fn wast_execute(env: &mut Env, exec: WastExecute) -> Result, Error> { +fn wast_execute(env: &mut Env, exec: WastExecute, inst: Option) -> Result, Error> { match exec { - WastExecute::Invoke(invoke) => wast_invoke(env, invoke), + WastExecute::Invoke(invoke) => wast_invoke(env, invoke, inst), WastExecute::Wat(mut wat) => { env.maybe_instantiate("", &wat.encode().unwrap()).map(|_| Vec::new()) } WastExecute::Get { module, global, .. } => { - let inst_id = env.inst_id(module)?; + let inst_id = env.inst_id(module, inst)?; env.store.get_global(inst_id, global).map(|x| vec![x]) } } } -fn wast_invoke(env: &mut Env, invoke: WastInvoke) -> Result, Error> { - let inst_id = env.inst_id(invoke.module)?; +fn wast_invoke(env: &mut Env, invoke: WastInvoke, inst: Option) -> Result, Error> { + // let inst_id = inst.unwrap_or(env.inst_id(invoke.module)?); + let inst_id = env.inst_id(invoke.module, inst)?; + println!("DBK (spec.rs): got inst_id = {:?} for module {:?}", inst_id, invoke.module); let args = wast_args(invoke.args); env.invoke(inst_id, invoke.name, args) } @@ -498,5 +535,13 @@ test!(utf8_custom_section_id, "utf8-custom-section-id"); test!(utf8_import_field, "utf8-import-field"); test!(utf8_import_module, "utf8-import-module"); test!(utf8_invalid_encoding, "utf8-invalid-encoding"); - test!("threads", atomic); +test!("threads", wait_notify, "threads/wait_notify"); + +test!("threads", lb_atomic, "threads/LB_atomic"); +test!("threads", lb, "threads/LB"); +test!("threads", mp_atomic, "threads/MP_atomic"); +test!("threads", mp, "threads/MP"); +test!("threads", mp_wait, "threads/MP_wait"); +test!("threads", sb_atomic, "threads/SB_atomic"); +test!("threads", sb, "threads/SB"); diff --git a/crates/runner-host/Cargo.lock b/crates/runner-host/Cargo.lock index 1ad01e054..5140257ad 100644 --- a/crates/runner-host/Cargo.lock +++ b/crates/runner-host/Cargo.lock @@ -1829,6 +1829,7 @@ dependencies = [ "num_enum", "paste", "portable-atomic", + "spin", ] [[package]] diff --git a/crates/scheduler/Cargo.lock b/crates/scheduler/Cargo.lock index a7e2f797d..0c52fb736 100644 --- a/crates/scheduler/Cargo.lock +++ b/crates/scheduler/Cargo.lock @@ -786,6 +786,7 @@ dependencies = [ "num_enum", "paste", "portable-atomic", + "spin", ] [[package]] diff --git a/third_party/WebAssembly/threads b/third_party/WebAssembly/threads index 3635ca51a..318176eb6 160000 --- a/third_party/WebAssembly/threads +++ b/third_party/WebAssembly/threads @@ -1 +1 @@ -Subproject commit 3635ca51a17e57e106988846c5b0e0cc48ac04fc +Subproject commit 318176eb610d78e3b16c7ec996e00f25fb852a4c