[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:
Clemens Hammacher 2017-10-23 11:10:10 +02:00 committed by Commit Bot
parent 7d533b6a67
commit 319b7868c9
3 changed files with 63 additions and 67 deletions

View File

@ -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); \

View File

@ -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 { \
DCHECK_EQ(localtype, type_); \
return value_.field; \
#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 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) \
template <> \
inline ctype WasmValue::to_unchecked() const { \
return value_.field; \
} \
template <> \
inline ctype WasmValue::to() const { \
return to_##field(); \
#define DECLARE_CAST(name, localtype, ctype, ...) \
template <> \
inline ctype WasmValue::to_unchecked() const { \
return to_##name(); \
} \
template <> \
inline ctype WasmValue::to() const { \
return to_##name##_unchecked(); \
}
FOREACH_WASMVAL_UNION_MEMBER(DECLARE_CAST)
FOREACH_WASMVAL_TYPE(DECLARE_CAST)
#undef DECLARE_CAST
} // namespace wasm

View File

@ -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,