Index: src/wasm/wasm-interpreter.cc |
diff --git a/src/wasm/wasm-interpreter.cc b/src/wasm/wasm-interpreter.cc |
index 9a1df93964e07791c11e6515a68d69aafdee81b6..cbc99eb57ed8eb8220a782f51726bf6b9c54b472 100644 |
--- a/src/wasm/wasm-interpreter.cc |
+++ b/src/wasm/wasm-interpreter.cc |
@@ -2,6 +2,8 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
+#include <type_traits> |
+ |
#include "src/wasm/wasm-interpreter.h" |
#include "src/utils.h" |
@@ -77,12 +79,10 @@ namespace wasm { |
V(F64Lt, double, <) \ |
V(F64Le, double, <=) \ |
V(F64Gt, double, >) \ |
- V(F64Ge, double, >=) |
- |
-#define FOREACH_SIMPLE_BINOP_NAN(V) \ |
- V(F32Mul, float, *) \ |
- V(F64Mul, double, *) \ |
- V(F32Div, float, /) \ |
+ V(F64Ge, double, >=) \ |
+ V(F32Mul, float, *) \ |
+ V(F64Mul, double, *) \ |
+ V(F32Div, float, /) \ |
V(F64Div, double, /) |
#define FOREACH_OTHER_BINOP(V) \ |
@@ -106,10 +106,8 @@ namespace wasm { |
V(I64Rol, int64_t) \ |
V(F32Min, float) \ |
V(F32Max, float) \ |
- V(F32CopySign, float) \ |
V(F64Min, double) \ |
V(F64Max, double) \ |
- V(F64CopySign, double) \ |
V(I32AsmjsDivS, int32_t) \ |
V(I32AsmjsDivU, uint32_t) \ |
V(I32AsmjsRemS, int32_t) \ |
@@ -162,10 +160,8 @@ namespace wasm { |
V(I32AsmjsSConvertF32, float) \ |
V(I32AsmjsUConvertF32, float) \ |
V(I32AsmjsSConvertF64, double) \ |
- V(I32AsmjsUConvertF64, double) |
- |
-#define FOREACH_OTHER_UNOP_NAN(V) \ |
- V(F32Sqrt, float) \ |
+ V(I32AsmjsUConvertF64, double) \ |
+ V(F32Sqrt, float) \ |
V(F64Sqrt, double) |
static inline int32_t ExecuteI32DivS(int32_t a, int32_t b, TrapReason* trap) { |
@@ -1176,6 +1172,49 @@ class ThreadImpl { |
stack_.resize(stack_.size() - pop_count); |
} |
+ template <typename ctype, typename mtype> |
+ bool ExecuteLoad(Decoder* decoder, InterpreterCode* code, pc_t pc, int& len) { |
+ MemoryAccessOperand operand(decoder, code->at(pc), sizeof(ctype)); |
+ uint32_t index = Pop().to<uint32_t>(); |
+ size_t effective_mem_size = instance()->mem_size - sizeof(mtype); |
+ if (operand.offset > effective_mem_size || |
+ index > (effective_mem_size - operand.offset)) { |
+ DoTrap(kTrapMemOutOfBounds, pc); |
+ return false; |
+ } |
+ byte* addr = instance()->mem_start + operand.offset + index; |
+ WasmVal result(static_cast<ctype>(ReadLittleEndianValue<mtype>(addr))); |
+ |
+ Push(pc, result); |
+ len = 1 + operand.length; |
+ return true; |
+ } |
+ |
+ template <typename ctype, typename mtype> |
+ bool ExecuteStore(Decoder* decoder, InterpreterCode* code, pc_t pc, |
+ int& len) { |
+ MemoryAccessOperand operand(decoder, code->at(pc), sizeof(ctype)); |
+ WasmVal val = Pop(); |
+ |
+ uint32_t index = Pop().to<uint32_t>(); |
+ size_t effective_mem_size = instance()->mem_size - sizeof(mtype); |
+ if (operand.offset > effective_mem_size || |
+ index > (effective_mem_size - operand.offset)) { |
+ DoTrap(kTrapMemOutOfBounds, pc); |
+ return false; |
+ } |
+ byte* addr = instance()->mem_start + operand.offset + index; |
+ WriteLittleEndianValue<mtype>(addr, static_cast<mtype>(val.to<ctype>())); |
+ len = 1 + operand.length; |
+ |
+ if (std::is_same<float, ctype>::value) { |
+ possible_nondeterminism_ |= std::isnan(val.to<float>()); |
+ } else if (std::is_same<double, ctype>::value) { |
+ possible_nondeterminism_ |= std::isnan(val.to<double>()); |
+ } |
+ return true; |
+ } |
+ |
void Execute(InterpreterCode* code, pc_t pc, int max) { |
Decoder decoder(code->start, code->end); |
pc_t limit = code->end - code->start; |
@@ -1427,20 +1466,10 @@ class ThreadImpl { |
break; |
} |
-#define LOAD_CASE(name, ctype, mtype) \ |
- case kExpr##name: { \ |
- MemoryAccessOperand operand(&decoder, code->at(pc), sizeof(ctype)); \ |
- uint32_t index = Pop().to<uint32_t>(); \ |
- size_t effective_mem_size = instance()->mem_size - sizeof(mtype); \ |
- if (operand.offset > effective_mem_size || \ |
- index > (effective_mem_size - operand.offset)) { \ |
- return DoTrap(kTrapMemOutOfBounds, pc); \ |
- } \ |
- byte* addr = instance()->mem_start + operand.offset + index; \ |
- WasmVal result(static_cast<ctype>(ReadLittleEndianValue<mtype>(addr))); \ |
- Push(pc, result); \ |
- len = 1 + operand.length; \ |
- break; \ |
+#define LOAD_CASE(name, ctype, mtype) \ |
+ case kExpr##name: { \ |
+ if (!ExecuteLoad<ctype, mtype>(&decoder, code, pc, len)) return; \ |
+ break; \ |
} |
LOAD_CASE(I32LoadMem8S, int32_t, int8_t); |
@@ -1459,20 +1488,10 @@ class ThreadImpl { |
LOAD_CASE(F64LoadMem, double, double); |
#undef LOAD_CASE |
-#define STORE_CASE(name, ctype, mtype) \ |
- case kExpr##name: { \ |
- MemoryAccessOperand operand(&decoder, code->at(pc), sizeof(ctype)); \ |
- WasmVal val = Pop(); \ |
- uint32_t index = Pop().to<uint32_t>(); \ |
- size_t effective_mem_size = instance()->mem_size - sizeof(mtype); \ |
- if (operand.offset > effective_mem_size || \ |
- index > (effective_mem_size - operand.offset)) { \ |
- return DoTrap(kTrapMemOutOfBounds, pc); \ |
- } \ |
- byte* addr = instance()->mem_start + operand.offset + index; \ |
- WriteLittleEndianValue<mtype>(addr, static_cast<mtype>(val.to<ctype>())); \ |
- len = 1 + operand.length; \ |
- break; \ |
+#define STORE_CASE(name, ctype, mtype) \ |
+ case kExpr##name: { \ |
+ if (!ExecuteStore<ctype, mtype>(&decoder, code, pc, len)) return; \ |
+ break; \ |
} |
STORE_CASE(I32StoreMem8, int32_t, int8_t); |
@@ -1548,13 +1567,17 @@ class ThreadImpl { |
// specially to guarantee that the quiet bit of a NaN is preserved on |
// ia32 by the reinterpret casts. |
case kExprI32ReinterpretF32: { |
- WasmVal result(ExecuteI32ReinterpretF32(Pop())); |
+ WasmVal val = Pop(); |
+ WasmVal result(ExecuteI32ReinterpretF32(val)); |
Push(pc, result); |
+ possible_nondeterminism_ |= std::isnan(val.to<float>()); |
break; |
} |
case kExprI64ReinterpretF64: { |
- WasmVal result(ExecuteI64ReinterpretF64(Pop())); |
+ WasmVal val = Pop(); |
+ WasmVal result(ExecuteI64ReinterpretF64(val)); |
Push(pc, result); |
+ possible_nondeterminism_ |= std::isnan(val.to<double>()); |
break; |
} |
#define EXECUTE_SIMPLE_BINOP(name, ctype, op) \ |
@@ -1568,19 +1591,6 @@ class ThreadImpl { |
FOREACH_SIMPLE_BINOP(EXECUTE_SIMPLE_BINOP) |
#undef EXECUTE_SIMPLE_BINOP |
-#define EXECUTE_SIMPLE_BINOP_NAN(name, ctype, op) \ |
- case kExpr##name: { \ |
- WasmVal rval = Pop(); \ |
- WasmVal lval = Pop(); \ |
- ctype result = lval.to<ctype>() op rval.to<ctype>(); \ |
- possible_nondeterminism_ |= std::isnan(result); \ |
- WasmVal result_val(result); \ |
- Push(pc, result_val); \ |
- break; \ |
- } |
- FOREACH_SIMPLE_BINOP_NAN(EXECUTE_SIMPLE_BINOP_NAN) |
-#undef EXECUTE_SIMPLE_BINOP_NAN |
- |
#define EXECUTE_OTHER_BINOP(name, ctype) \ |
case kExpr##name: { \ |
TrapReason trap = kTrapCount; \ |
@@ -1594,6 +1604,28 @@ class ThreadImpl { |
FOREACH_OTHER_BINOP(EXECUTE_OTHER_BINOP) |
#undef EXECUTE_OTHER_BINOP |
+ case kExprF32CopySign: { |
+ // Handle kExprF32CopySign separately because it may introduce |
+ // observable non-determinism. |
+ TrapReason trap = kTrapCount; |
+ volatile float rval = Pop().to<float>(); |
+ volatile float lval = Pop().to<float>(); |
+ WasmVal result(ExecuteF32CopySign(lval, rval, &trap)); |
+ Push(pc, result); |
+ possible_nondeterminism_ |= std::isnan(rval); |
+ break; |
+ } |
+ case kExprF64CopySign: { |
+ // Handle kExprF32CopySign separately because it may introduce |
+ // observable non-determinism. |
+ TrapReason trap = kTrapCount; |
+ volatile double rval = Pop().to<double>(); |
+ volatile double lval = Pop().to<double>(); |
+ WasmVal result(ExecuteF64CopySign(lval, rval, &trap)); |
+ Push(pc, result); |
+ possible_nondeterminism_ |= std::isnan(rval); |
+ break; |
+ } |
#define EXECUTE_OTHER_UNOP(name, ctype) \ |
case kExpr##name: { \ |
TrapReason trap = kTrapCount; \ |
@@ -1606,20 +1638,6 @@ class ThreadImpl { |
FOREACH_OTHER_UNOP(EXECUTE_OTHER_UNOP) |
#undef EXECUTE_OTHER_UNOP |
-#define EXECUTE_OTHER_UNOP_NAN(name, ctype) \ |
- case kExpr##name: { \ |
- TrapReason trap = kTrapCount; \ |
- volatile ctype val = Pop().to<ctype>(); \ |
- ctype result = Execute##name(val, &trap); \ |
- possible_nondeterminism_ |= std::isnan(result); \ |
- WasmVal result_val(result); \ |
- if (trap != kTrapCount) return DoTrap(trap, pc); \ |
- Push(pc, result_val); \ |
- break; \ |
- } |
- FOREACH_OTHER_UNOP_NAN(EXECUTE_OTHER_UNOP_NAN) |
-#undef EXECUTE_OTHER_UNOP_NAN |
- |
default: |
V8_Fatal(__FILE__, __LINE__, "Unknown or unimplemented opcode #%d:%s", |
code->start[pc], OpcodeName(code->start[pc])); |