From df1af2e69ca1cee61030a8458251106b2fd23340 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Tue, 24 Sep 2019 15:44:17 -0700 Subject: [PATCH] Finish atomic operations for singlepass, excluding wait and notify. --- lib/singlepass-backend/src/codegen_x64.rs | 588 +++++++++++++++++++++- lib/spectests/tests/excludes.txt | 46 +- 2 files changed, 615 insertions(+), 19 deletions(-) diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index 9b4412d66..9322531f8 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -1514,24 +1514,23 @@ impl X64FunctionCode { a.emit_add(Size::S64, Location::GPR(tmp_base), Location::GPR(tmp_bound)); a.emit_mov(Size::S32, addr, Location::GPR(tmp_addr)); - if memarg.offset != 0 && value_size != 0 { - // This branch is used for emitting "faster" code for the special case of (offset + value_size) not exceeding u32 range. - match (memarg.offset as u32).checked_add(value_size as u32) { - Some(x) => { - a.emit_add(Size::S64, Location::Imm32(x), Location::GPR(tmp_addr)); - } - None => { - a.emit_add( - Size::S64, - Location::Imm32(memarg.offset as u32), - Location::GPR(tmp_addr), - ); - a.emit_add( - Size::S64, - Location::Imm32(value_size as u32), - Location::GPR(tmp_addr), - ); - } + // This branch is used for emitting "faster" code for the special case of (offset + value_size) not exceeding u32 range. + match (memarg.offset as u32).checked_add(value_size as u32) { + Some(0) => {} + Some(x) => { + a.emit_add(Size::S64, Location::Imm32(x), Location::GPR(tmp_addr)); + } + None => { + a.emit_add( + Size::S64, + Location::Imm32(memarg.offset as u32), + Location::GPR(tmp_addr), + ); + a.emit_add( + Size::S64, + Location::Imm32(value_size as u32), + Location::GPR(tmp_addr), + ); } } @@ -6552,6 +6551,559 @@ impl FunctionCodeGenerator for X64FunctionCode { }, ); } + Operator::I32AtomicRmwXchg { ref memarg } => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + a.emit_mov(Size::S32, loc, Location::GPR(value)); + Self::emit_memory_op( + module_info, + &self.config, + a, + &mut self.machine, + target, + memarg, + true, + 4, + |a, _m, addr| { + a.emit_xchg(Size::S32, Location::GPR(value), Location::Memory(addr, 0)) + }, + ); + a.emit_mov(Size::S32, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I64AtomicRmwXchg { ref memarg } => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + a.emit_mov(Size::S64, loc, Location::GPR(value)); + Self::emit_memory_op( + module_info, + &self.config, + a, + &mut self.machine, + target, + memarg, + true, + 8, + |a, _m, addr| { + a.emit_xchg(Size::S64, Location::GPR(value), Location::Memory(addr, 0)) + }, + ); + a.emit_mov(Size::S64, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I32AtomicRmw8UXchg { ref memarg } => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + a.emit_movzx(Size::S8, loc, Size::S32, Location::GPR(value)); + Self::emit_memory_op( + module_info, + &self.config, + a, + &mut self.machine, + target, + memarg, + true, + 1, + |a, _m, addr| { + a.emit_xchg(Size::S8, Location::GPR(value), Location::Memory(addr, 0)) + }, + ); + a.emit_mov(Size::S32, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I32AtomicRmw16UXchg { ref memarg } => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + a.emit_movzx(Size::S16, loc, Size::S32, Location::GPR(value)); + Self::emit_memory_op( + module_info, + &self.config, + a, + &mut self.machine, + target, + memarg, + true, + 2, + |a, _m, addr| { + a.emit_xchg(Size::S16, Location::GPR(value), Location::Memory(addr, 0)) + }, + ); + a.emit_mov(Size::S32, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I64AtomicRmw8UXchg { ref memarg } => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + a.emit_movzx(Size::S8, loc, Size::S64, Location::GPR(value)); + Self::emit_memory_op( + module_info, + &self.config, + a, + &mut self.machine, + target, + memarg, + true, + 1, + |a, _m, addr| { + a.emit_xchg(Size::S8, Location::GPR(value), Location::Memory(addr, 0)) + }, + ); + a.emit_mov(Size::S64, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I64AtomicRmw16UXchg { ref memarg } => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + a.emit_movzx(Size::S16, loc, Size::S64, Location::GPR(value)); + Self::emit_memory_op( + module_info, + &self.config, + a, + &mut self.machine, + target, + memarg, + true, + 2, + |a, _m, addr| { + a.emit_xchg(Size::S16, Location::GPR(value), Location::Memory(addr, 0)) + }, + ); + a.emit_mov(Size::S64, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I64AtomicRmw32UXchg { ref memarg } => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + a.emit_mov(Size::S32, loc, Location::GPR(value)); + Self::emit_memory_op( + module_info, + &self.config, + a, + &mut self.machine, + target, + memarg, + true, + 4, + |a, _m, addr| { + a.emit_xchg(Size::S32, Location::GPR(value), Location::Memory(addr, 0)) + }, + ); + a.emit_mov(Size::S64, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I32AtomicRmwCmpxchg { ref memarg } => { + let new = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let cmp = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + let compare = self.machine.reserve_temp_gpr(GPR::RAX); + let value = if cmp == Location::GPR(GPR::R14) { + if new == Location::GPR(GPR::R13) { + GPR::R12 + } else { + GPR::R13 + } + } else { + GPR::R14 + }; + a.emit_push(Size::S64, Location::GPR(value)); + a.emit_mov(Size::S32, cmp, Location::GPR(compare)); + a.emit_mov(Size::S32, new, Location::GPR(value)); + + Self::emit_memory_op( + module_info, + &self.config, + a, + &mut self.machine, + target, + memarg, + true, + 4, + |a, _m, addr| { + a.emit_lock_cmpxchg( + Size::S32, + Location::GPR(value), + Location::Memory(addr, 0), + ); + a.emit_mov(Size::S32, Location::GPR(compare), ret); + }, + ); + a.emit_pop(Size::S64, Location::GPR(value)); + self.machine.release_temp_gpr(compare); + } + Operator::I64AtomicRmwCmpxchg { ref memarg } => { + let new = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let cmp = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + let compare = self.machine.reserve_temp_gpr(GPR::RAX); + let value = if cmp == Location::GPR(GPR::R14) { + if new == Location::GPR(GPR::R13) { + GPR::R12 + } else { + GPR::R13 + } + } else { + GPR::R14 + }; + a.emit_push(Size::S64, Location::GPR(value)); + a.emit_mov(Size::S64, cmp, Location::GPR(compare)); + a.emit_mov(Size::S64, new, Location::GPR(value)); + + Self::emit_memory_op( + module_info, + &self.config, + a, + &mut self.machine, + target, + memarg, + true, + 8, + |a, _m, addr| { + a.emit_lock_cmpxchg( + Size::S64, + Location::GPR(value), + Location::Memory(addr, 0), + ); + a.emit_mov(Size::S64, Location::GPR(compare), ret); + }, + ); + a.emit_pop(Size::S64, Location::GPR(value)); + self.machine.release_temp_gpr(compare); + } + Operator::I32AtomicRmw8UCmpxchg { ref memarg } => { + let new = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let cmp = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + let compare = self.machine.reserve_temp_gpr(GPR::RAX); + let value = if cmp == Location::GPR(GPR::R14) { + if new == Location::GPR(GPR::R13) { + GPR::R12 + } else { + GPR::R13 + } + } else { + GPR::R14 + }; + a.emit_push(Size::S64, Location::GPR(value)); + a.emit_mov(Size::S32, cmp, Location::GPR(compare)); + a.emit_mov(Size::S32, new, Location::GPR(value)); + + Self::emit_memory_op( + module_info, + &self.config, + a, + &mut self.machine, + target, + memarg, + true, + 1, + |a, _m, addr| { + a.emit_lock_cmpxchg( + Size::S8, + Location::GPR(value), + Location::Memory(addr, 0), + ); + a.emit_movzx(Size::S8, Location::GPR(compare), Size::S32, ret); + }, + ); + a.emit_pop(Size::S64, Location::GPR(value)); + self.machine.release_temp_gpr(compare); + } + Operator::I32AtomicRmw16UCmpxchg { ref memarg } => { + let new = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let cmp = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + let compare = self.machine.reserve_temp_gpr(GPR::RAX); + let value = if cmp == Location::GPR(GPR::R14) { + if new == Location::GPR(GPR::R13) { + GPR::R12 + } else { + GPR::R13 + } + } else { + GPR::R14 + }; + a.emit_push(Size::S64, Location::GPR(value)); + a.emit_mov(Size::S32, cmp, Location::GPR(compare)); + a.emit_mov(Size::S32, new, Location::GPR(value)); + + Self::emit_memory_op( + module_info, + &self.config, + a, + &mut self.machine, + target, + memarg, + true, + 1, + |a, _m, addr| { + a.emit_lock_cmpxchg( + Size::S16, + Location::GPR(value), + Location::Memory(addr, 0), + ); + a.emit_movzx(Size::S16, Location::GPR(compare), Size::S32, ret); + }, + ); + a.emit_pop(Size::S64, Location::GPR(value)); + self.machine.release_temp_gpr(compare); + } + Operator::I64AtomicRmw8UCmpxchg { ref memarg } => { + let new = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let cmp = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + let compare = self.machine.reserve_temp_gpr(GPR::RAX); + let value = if cmp == Location::GPR(GPR::R14) { + if new == Location::GPR(GPR::R13) { + GPR::R12 + } else { + GPR::R13 + } + } else { + GPR::R14 + }; + a.emit_push(Size::S64, Location::GPR(value)); + a.emit_mov(Size::S64, cmp, Location::GPR(compare)); + a.emit_mov(Size::S64, new, Location::GPR(value)); + + Self::emit_memory_op( + module_info, + &self.config, + a, + &mut self.machine, + target, + memarg, + true, + 1, + |a, _m, addr| { + a.emit_lock_cmpxchg( + Size::S8, + Location::GPR(value), + Location::Memory(addr, 0), + ); + a.emit_movzx(Size::S8, Location::GPR(compare), Size::S64, ret); + }, + ); + a.emit_pop(Size::S64, Location::GPR(value)); + self.machine.release_temp_gpr(compare); + } + Operator::I64AtomicRmw16UCmpxchg { ref memarg } => { + let new = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let cmp = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + let compare = self.machine.reserve_temp_gpr(GPR::RAX); + let value = if cmp == Location::GPR(GPR::R14) { + if new == Location::GPR(GPR::R13) { + GPR::R12 + } else { + GPR::R13 + } + } else { + GPR::R14 + }; + a.emit_push(Size::S64, Location::GPR(value)); + a.emit_mov(Size::S64, cmp, Location::GPR(compare)); + a.emit_mov(Size::S64, new, Location::GPR(value)); + + Self::emit_memory_op( + module_info, + &self.config, + a, + &mut self.machine, + target, + memarg, + true, + 1, + |a, _m, addr| { + a.emit_lock_cmpxchg( + Size::S16, + Location::GPR(value), + Location::Memory(addr, 0), + ); + a.emit_movzx(Size::S16, Location::GPR(compare), Size::S64, ret); + }, + ); + a.emit_pop(Size::S64, Location::GPR(value)); + self.machine.release_temp_gpr(compare); + } + Operator::I64AtomicRmw32UCmpxchg { ref memarg } => { + let new = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let cmp = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + let compare = self.machine.reserve_temp_gpr(GPR::RAX); + let value = if cmp == Location::GPR(GPR::R14) { + if new == Location::GPR(GPR::R13) { + GPR::R12 + } else { + GPR::R13 + } + } else { + GPR::R14 + }; + a.emit_push(Size::S64, Location::GPR(value)); + a.emit_mov(Size::S64, cmp, Location::GPR(compare)); + a.emit_mov(Size::S64, new, Location::GPR(value)); + + Self::emit_memory_op( + module_info, + &self.config, + a, + &mut self.machine, + target, + memarg, + true, + 1, + |a, _m, addr| { + a.emit_lock_cmpxchg( + Size::S32, + Location::GPR(value), + Location::Memory(addr, 0), + ); + a.emit_mov(Size::S32, Location::GPR(compare), ret); + }, + ); + a.emit_pop(Size::S64, Location::GPR(value)); + self.machine.release_temp_gpr(compare); + } _ => { return Err(CodegenError { message: format!("not yet implemented: {:?}", op), diff --git a/lib/spectests/tests/excludes.txt b/lib/spectests/tests/excludes.txt index 19a1c51a8..56c6a2d56 100644 --- a/lib/spectests/tests/excludes.txt +++ b/lib/spectests/tests/excludes.txt @@ -867,7 +867,6 @@ llvm:skip:simd_binaryen.wast:*:unix # Module - caught panic Any # Singlepass -singlepass:skip:atomic.wast:* # Threads not implemented singlepass:skip:simd.wast:* # SIMD not implemented singlepass:skip:simd_binaryen.wast:* # SIMD not implemented @@ -904,6 +903,51 @@ singlepass:fail:address.wast:586 # AssertTrap - expected trap, got Runtime:Error singlepass:fail:address.wast:588 # AssertTrap - expected trap, got [] singlepass:fail:address.wast:589 # AssertTrap - expected trap, got [] singlepass:fail:align.wast:864 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:380 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:381 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:382 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:383 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:384 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:385 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:386 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:387 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:388 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:389 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:390 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:391 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:392 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:393 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:394 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:395 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:396 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:397 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:398 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:399 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:400 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:401 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:402 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:403 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:404 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:405 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:406 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:407 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:408 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:409 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:410 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:411 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:412 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:413 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:414 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:415 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:416 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:417 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:418 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:419 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:420 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:421 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:422 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:423 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:atomic.wast:424 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:call.wast:289 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:call_indirect.wast:469 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:call_indirect.wast:470 # AssertTrap - expected trap, got Runtime:Error unknown error