[wasm] [interpreter] Box floats for certain operations
There are wasm operations which operate on floats or double, but they need to preserve the exact bit pattern. Thus they cannot be stored and passed as float or double, since that might flip the signaling NaN bit. This CL extends WasmValue to store floats and doubles as bit pattern, and adds accessors to extract them as Float32 or Float64. The interpreter is changed to execute certain operations (i32.abs, i32.neg, i64.abs, i64.neg, f32.reinterpret/i32, f64.reinterpret/i64) on boxed floats. R=titzer@chromium.org Bug: v8:6954 Change-Id: I0251d1a67b6caf593194d4eb292a325cdd3f20cf Reviewed-on: https://chromium-review.googlesource.com/730716 Commit-Queue: Clemens Hammacher <clemensh@chromium.org> Reviewed-by: Ben Titzer <titzer@chromium.org> Cr-Commit-Position: refs/heads/master@{#48819}
This commit is contained in:
parent
7d533b6a67
commit
319b7868c9
@ -7,6 +7,7 @@
|
||||
#include "src/wasm/wasm-interpreter.h"
|
||||
|
||||
#include "src/assembler-inl.h"
|
||||
#include "src/boxed-float.h"
|
||||
#include "src/compiler/wasm-compiler.h"
|
||||
#include "src/conversions.h"
|
||||
#include "src/identity-map.h"
|
||||
@ -133,14 +134,14 @@ namespace wasm {
|
||||
V(I64Ctz, uint64_t) \
|
||||
V(I64Popcnt, uint64_t) \
|
||||
V(I64Eqz, uint64_t) \
|
||||
V(F32Abs, float) \
|
||||
V(F32Neg, float) \
|
||||
V(F32Abs, Float32) \
|
||||
V(F32Neg, Float32) \
|
||||
V(F32Ceil, float) \
|
||||
V(F32Floor, float) \
|
||||
V(F32Trunc, float) \
|
||||
V(F32NearestInt, float) \
|
||||
V(F64Abs, double) \
|
||||
V(F64Neg, double) \
|
||||
V(F64Abs, Float64) \
|
||||
V(F64Neg, Float64) \
|
||||
V(F64Ceil, double) \
|
||||
V(F64Floor, double) \
|
||||
V(F64Trunc, double) \
|
||||
@ -412,12 +413,12 @@ inline int32_t ExecuteI64Eqz(uint64_t val, TrapReason* trap) {
|
||||
return val == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
inline float ExecuteF32Abs(float a, TrapReason* trap) {
|
||||
return bit_cast<float>(bit_cast<uint32_t>(a) & 0x7fffffff);
|
||||
inline Float32 ExecuteF32Abs(Float32 a, TrapReason* trap) {
|
||||
return Float32::FromBits(a.get_bits() & 0x7fffffff);
|
||||
}
|
||||
|
||||
inline float ExecuteF32Neg(float a, TrapReason* trap) {
|
||||
return bit_cast<float>(bit_cast<uint32_t>(a) ^ 0x80000000);
|
||||
inline Float32 ExecuteF32Neg(Float32 a, TrapReason* trap) {
|
||||
return Float32::FromBits(a.get_bits() ^ 0x80000000);
|
||||
}
|
||||
|
||||
inline float ExecuteF32Ceil(float a, TrapReason* trap) { return ceilf(a); }
|
||||
@ -435,12 +436,12 @@ inline float ExecuteF32Sqrt(float a, TrapReason* trap) {
|
||||
return result;
|
||||
}
|
||||
|
||||
inline double ExecuteF64Abs(double a, TrapReason* trap) {
|
||||
return bit_cast<double>(bit_cast<uint64_t>(a) & 0x7fffffffffffffff);
|
||||
inline Float64 ExecuteF64Abs(Float64 a, TrapReason* trap) {
|
||||
return Float64::FromBits(a.get_bits() & 0x7fffffffffffffff);
|
||||
}
|
||||
|
||||
inline double ExecuteF64Neg(double a, TrapReason* trap) {
|
||||
return bit_cast<double>(bit_cast<uint64_t>(a) ^ 0x8000000000000000);
|
||||
inline Float64 ExecuteF64Neg(Float64 a, TrapReason* trap) {
|
||||
return Float64::FromBits(a.get_bits() ^ 0x8000000000000000);
|
||||
}
|
||||
|
||||
inline double ExecuteF64Ceil(double a, TrapReason* trap) { return ceil(a); }
|
||||
@ -578,8 +579,8 @@ inline float ExecuteF32ConvertF64(double a, TrapReason* trap) {
|
||||
return static_cast<float>(a);
|
||||
}
|
||||
|
||||
inline float ExecuteF32ReinterpretI32(int32_t a, TrapReason* trap) {
|
||||
return bit_cast<float>(a);
|
||||
inline Float32 ExecuteF32ReinterpretI32(int32_t a, TrapReason* trap) {
|
||||
return Float32::FromBits(a);
|
||||
}
|
||||
|
||||
inline double ExecuteF64SConvertI32(int32_t a, TrapReason* trap) {
|
||||
@ -606,16 +607,16 @@ inline double ExecuteF64ConvertF32(float a, TrapReason* trap) {
|
||||
return static_cast<double>(a);
|
||||
}
|
||||
|
||||
inline double ExecuteF64ReinterpretI64(int64_t a, TrapReason* trap) {
|
||||
return bit_cast<double>(a);
|
||||
inline Float64 ExecuteF64ReinterpretI64(int64_t a, TrapReason* trap) {
|
||||
return Float64::FromBits(a);
|
||||
}
|
||||
|
||||
inline int32_t ExecuteI32ReinterpretF32(WasmValue a) {
|
||||
return a.to_unchecked<int32_t>();
|
||||
return a.to_f32_boxed().get_bits();
|
||||
}
|
||||
|
||||
inline int64_t ExecuteI64ReinterpretF64(WasmValue a) {
|
||||
return a.to_unchecked<int64_t>();
|
||||
return a.to_f64_boxed().get_bits();
|
||||
}
|
||||
|
||||
enum InternalOpcode {
|
||||
@ -2000,7 +2001,7 @@ class ThreadImpl {
|
||||
#define EXECUTE_OTHER_UNOP(name, ctype) \
|
||||
case kExpr##name: { \
|
||||
TrapReason trap = kTrapCount; \
|
||||
volatile ctype val = Pop().to<ctype>(); \
|
||||
ctype val = Pop().to<ctype>(); \
|
||||
WasmValue result(Execute##name(val, &trap)); \
|
||||
if (trap != kTrapCount) return DoTrap(trap, pc); \
|
||||
Push(result); \
|
||||
|
@ -5,6 +5,7 @@
|
||||
#ifndef V8_WASM_VALUE_H_
|
||||
#define V8_WASM_VALUE_H_
|
||||
|
||||
#include "src/boxed-float.h"
|
||||
#include "src/wasm/wasm-opcodes.h"
|
||||
#include "src/zone/zone-containers.h"
|
||||
|
||||
@ -12,71 +13,71 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
// Macro for defining WasmValue union members.
|
||||
#define FOREACH_WASMVAL_UNION_MEMBER(V) \
|
||||
V(i32, kWasmI32, int32_t) \
|
||||
V(u32, kWasmI32, uint32_t) \
|
||||
V(i64, kWasmI64, int64_t) \
|
||||
V(u64, kWasmI64, uint64_t) \
|
||||
V(f32, kWasmF32, float) \
|
||||
V(f64, kWasmF64, double)
|
||||
// Macro for defining WasmValue methods for different types.
|
||||
// Elements:
|
||||
// - name (for to_<name>() method)
|
||||
// - wasm type
|
||||
// - c type
|
||||
// - how to get bit pattern from value {v} of type {c type}
|
||||
// - how to get value of type {c type} from bit pattern {p}
|
||||
#define FOREACH_WASMVAL_TYPE(V) \
|
||||
V(i32, kWasmI32, int32_t, static_cast<uint32_t>(v), static_cast<int32_t>(p)) \
|
||||
V(u32, kWasmI32, uint32_t, v, static_cast<uint32_t>(p)) \
|
||||
V(i64, kWasmI64, int64_t, static_cast<uint64_t>(v), static_cast<int64_t>(p)) \
|
||||
V(u64, kWasmI64, uint64_t, v, p) \
|
||||
V(f32, kWasmF32, float, bit_cast<uint32_t>(v), \
|
||||
bit_cast<float>(static_cast<uint32_t>(p))) \
|
||||
V(f32_boxed, kWasmF32, Float32, v.get_bits(), \
|
||||
Float32::FromBits(static_cast<uint32_t>(p))) \
|
||||
V(f64, kWasmF64, double, bit_cast<uint64_t>(v), bit_cast<double>(p)) \
|
||||
V(f64_boxed, kWasmF64, Float64, v.get_bits(), Float64::FromBits(p))
|
||||
|
||||
// A wasm value with type information.
|
||||
class WasmValue {
|
||||
public:
|
||||
WasmValue() : type_(kWasmStmt) {}
|
||||
|
||||
#define DEFINE_TYPE_SPECIFIC_METHODS(field, localtype, ctype) \
|
||||
explicit WasmValue(ctype v) : type_(localtype) { value_.field = v; } \
|
||||
ctype to_##field() const { \
|
||||
#define DEFINE_TYPE_SPECIFIC_METHODS(name, localtype, ctype, v_to_p, p_to_v) \
|
||||
explicit WasmValue(ctype v) : type_(localtype), bit_pattern_(v_to_p) {} \
|
||||
ctype to_##name() const { \
|
||||
DCHECK_EQ(localtype, type_); \
|
||||
return value_.field; \
|
||||
return to_##name##_unchecked(); \
|
||||
} \
|
||||
ctype to_##name##_unchecked() const { \
|
||||
auto p = bit_pattern_; \
|
||||
return p_to_v; \
|
||||
}
|
||||
FOREACH_WASMVAL_UNION_MEMBER(DEFINE_TYPE_SPECIFIC_METHODS)
|
||||
FOREACH_WASMVAL_TYPE(DEFINE_TYPE_SPECIFIC_METHODS)
|
||||
#undef DEFINE_TYPE_SPECIFIC_METHODS
|
||||
|
||||
ValueType type() const { return type_; }
|
||||
|
||||
// Checks equality of type and bit pattern (also for float and double values).
|
||||
bool operator==(const WasmValue& other) const {
|
||||
if (type_ != other.type_) return false;
|
||||
#define CHECK_VALUE_EQ(field, localtype, ctype) \
|
||||
if (type_ == localtype) { \
|
||||
return value_.field == other.value_.field; \
|
||||
}
|
||||
FOREACH_WASMVAL_UNION_MEMBER(CHECK_VALUE_EQ)
|
||||
#undef CHECK_VALUE_EQ
|
||||
UNREACHABLE();
|
||||
return type_ == other.type_ && bit_pattern_ == other.bit_pattern_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T to() const {
|
||||
static_assert(sizeof(T) == -1, "Do only use this method with valid types");
|
||||
}
|
||||
inline T to() const;
|
||||
|
||||
template <typename T>
|
||||
inline T to_unchecked() const {
|
||||
static_assert(sizeof(T) == -1, "Do only use this method with valid types");
|
||||
}
|
||||
inline T to_unchecked() const;
|
||||
|
||||
private:
|
||||
ValueType type_;
|
||||
union {
|
||||
#define DECLARE_FIELD(field, localtype, ctype) ctype field;
|
||||
FOREACH_WASMVAL_UNION_MEMBER(DECLARE_FIELD)
|
||||
#undef DECLARE_FIELD
|
||||
} value_;
|
||||
uint64_t bit_pattern_;
|
||||
};
|
||||
|
||||
#define DECLARE_CAST(field, localtype, ctype) \
|
||||
#define DECLARE_CAST(name, localtype, ctype, ...) \
|
||||
template <> \
|
||||
inline ctype WasmValue::to_unchecked() const { \
|
||||
return value_.field; \
|
||||
return to_##name(); \
|
||||
} \
|
||||
template <> \
|
||||
inline ctype WasmValue::to() const { \
|
||||
return to_##field(); \
|
||||
return to_##name##_unchecked(); \
|
||||
}
|
||||
FOREACH_WASMVAL_UNION_MEMBER(DECLARE_CAST)
|
||||
FOREACH_WASMVAL_TYPE(DECLARE_CAST)
|
||||
#undef DECLARE_CAST
|
||||
|
||||
} // namespace wasm
|
||||
|
@ -1034,9 +1034,6 @@ WASM_EXEC_TEST(BrTable_loop_target) {
|
||||
|
||||
WASM_EXEC_TEST(F32ReinterpretI32) {
|
||||
WasmRunner<int32_t> r(execution_mode);
|
||||
// TODO(clemensh): Reenable this test after fixing crbug.com/v8/6954.
|
||||
if (execution_mode == kExecuteInterpreted) return;
|
||||
|
||||
int32_t* memory = r.builder().AddMemoryElems<int32_t>(8);
|
||||
|
||||
BUILD(r, WASM_I32_REINTERPRET_F32(
|
||||
@ -1051,9 +1048,6 @@ WASM_EXEC_TEST(F32ReinterpretI32) {
|
||||
|
||||
WASM_EXEC_TEST(I32ReinterpretF32) {
|
||||
WasmRunner<int32_t, int32_t> r(execution_mode);
|
||||
// TODO(clemensh): Reenable this test after fixing crbug.com/v8/6954.
|
||||
if (execution_mode == kExecuteInterpreted) return;
|
||||
|
||||
int32_t* memory = r.builder().AddMemoryElems<int32_t>(8);
|
||||
|
||||
BUILD(r, WASM_STORE_MEM(MachineType::Float32(), WASM_ZERO,
|
||||
|
Loading…
Reference in New Issue
Block a user