[wasm] Add more saturating float to int conversions
Adds I32UConvertF32, I32SConvertF64, and I32UConvertF64 instructions. Refactors code to use templates where appropriate, and to use previously committed template function is_inbounds() when appropriate in tests. Bug: v8:7226 Change-Id: I2701e5fd0b21cefa1f285677f20616cfde29ab0d Reviewed-on: https://chromium-review.googlesource.com/862609 Commit-Queue: Karl Schimpf <kschimpf@chromium.org> Reviewed-by: Ben Titzer <titzer@chromium.org> Cr-Commit-Position: refs/heads/master@{#50632}
This commit is contained in:
parent
25b2116770
commit
1607614a91
@ -564,9 +564,15 @@ Node* WasmGraphBuilder::Unop(wasm::WasmOpcode opcode, Node* input,
|
||||
op = m->Float64Sqrt();
|
||||
break;
|
||||
case wasm::kExprI32SConvertF64:
|
||||
return BuildI32SConvertF64(input, position);
|
||||
return BuildI32SConvertF64(input, position, NumericImplementation::kTrap);
|
||||
case wasm::kExprI32SConvertSatF64:
|
||||
return BuildI32SConvertF64(input, position,
|
||||
NumericImplementation::kSaturate);
|
||||
case wasm::kExprI32UConvertF64:
|
||||
return BuildI32UConvertF64(input, position);
|
||||
return BuildI32UConvertF64(input, position, NumericImplementation::kTrap);
|
||||
case wasm::kExprI32UConvertSatF64:
|
||||
return BuildI32UConvertF64(input, position,
|
||||
NumericImplementation::kSaturate);
|
||||
case wasm::kExprI32AsmjsSConvertF64:
|
||||
return BuildI32AsmjsSConvertF64(input);
|
||||
case wasm::kExprI32AsmjsUConvertF64:
|
||||
@ -592,7 +598,10 @@ Node* WasmGraphBuilder::Unop(wasm::WasmOpcode opcode, Node* input,
|
||||
return BuildI32SConvertF32(input, position,
|
||||
NumericImplementation::kSaturate);
|
||||
case wasm::kExprI32UConvertF32:
|
||||
return BuildI32UConvertF32(input, position);
|
||||
return BuildI32UConvertF32(input, position, NumericImplementation::kTrap);
|
||||
case wasm::kExprI32UConvertSatF32:
|
||||
return BuildI32UConvertF32(input, position,
|
||||
NumericImplementation::kSaturate);
|
||||
case wasm::kExprI32AsmjsSConvertF32:
|
||||
return BuildI32AsmjsSConvertF32(input);
|
||||
case wasm::kExprI32AsmjsUConvertF32:
|
||||
@ -1364,93 +1373,167 @@ Node* WasmGraphBuilder::BuildF64CopySign(Node* left, Node* right) {
|
||||
#endif
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::BuildI32SConvertF32(Node* input,
|
||||
wasm::WasmCodePosition position,
|
||||
NumericImplementation impl) {
|
||||
MachineOperatorBuilder* m = jsgraph()->machine();
|
||||
// Truncation of the input value is needed for the overflow check later.
|
||||
Node* trunc = Unop(wasm::kExprF32Trunc, input);
|
||||
Node* result = graph()->NewNode(m->TruncateFloat32ToInt32(), trunc);
|
||||
// Helper classes for float to int conversions.
|
||||
struct WasmGraphBuilder::IntConvertOps {
|
||||
MachineRepresentation word_rep() const {
|
||||
return MachineRepresentation::kWord32;
|
||||
}
|
||||
Node* zero() const { return builder_->Int32Constant(0); }
|
||||
virtual Node* min() const = 0;
|
||||
virtual Node* max() const = 0;
|
||||
virtual ~IntConvertOps() = default;
|
||||
|
||||
// Convert the result back to f64. If we end up at a different value than the
|
||||
// truncated input value, then there has been an overflow and we trap.
|
||||
Node* check = Unop(wasm::kExprF32SConvertI32, result);
|
||||
Node* overflow = Binop(wasm::kExprF32Ne, trunc, check);
|
||||
protected:
|
||||
explicit IntConvertOps(WasmGraphBuilder* builder) : builder_(builder) {}
|
||||
WasmGraphBuilder* builder_;
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(IntConvertOps);
|
||||
};
|
||||
|
||||
struct I32SConvertOps final : public WasmGraphBuilder::IntConvertOps {
|
||||
explicit I32SConvertOps(WasmGraphBuilder* builder)
|
||||
: WasmGraphBuilder::IntConvertOps(builder) {}
|
||||
~I32SConvertOps() = default;
|
||||
Node* min() const {
|
||||
return builder_->Int32Constant(std::numeric_limits<int32_t>::min());
|
||||
}
|
||||
Node* max() const {
|
||||
return builder_->Int32Constant(std::numeric_limits<int32_t>::max());
|
||||
}
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(I32SConvertOps);
|
||||
};
|
||||
|
||||
struct I32UConvertOps final : public WasmGraphBuilder::IntConvertOps {
|
||||
explicit I32UConvertOps(WasmGraphBuilder* builder)
|
||||
: WasmGraphBuilder::IntConvertOps(builder) {}
|
||||
~I32UConvertOps() = default;
|
||||
Node* min() const {
|
||||
return builder_->Int32Constant(std::numeric_limits<uint32_t>::min());
|
||||
}
|
||||
Node* max() const {
|
||||
return builder_->Int32Constant(std::numeric_limits<uint32_t>::max());
|
||||
}
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(I32UConvertOps);
|
||||
};
|
||||
|
||||
struct WasmGraphBuilder::FloatConvertOps {
|
||||
virtual Node* zero() const = 0;
|
||||
virtual wasm::WasmOpcode trunc_op() const = 0;
|
||||
virtual wasm::WasmOpcode ne_op() const = 0;
|
||||
virtual wasm::WasmOpcode lt_op() const = 0;
|
||||
virtual ~FloatConvertOps() = default;
|
||||
|
||||
protected:
|
||||
explicit FloatConvertOps(WasmGraphBuilder* builder) : builder_(builder) {}
|
||||
WasmGraphBuilder* builder_;
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(FloatConvertOps);
|
||||
};
|
||||
|
||||
struct F32ConvertOps final : public WasmGraphBuilder::FloatConvertOps {
|
||||
explicit F32ConvertOps(WasmGraphBuilder* builder)
|
||||
: WasmGraphBuilder::FloatConvertOps(builder) {}
|
||||
~F32ConvertOps() = default;
|
||||
Node* zero() const { return builder_->Float32Constant(0.0); }
|
||||
wasm::WasmOpcode trunc_op() const { return wasm::kExprF32Trunc; }
|
||||
wasm::WasmOpcode ne_op() const { return wasm::kExprF32Ne; }
|
||||
wasm::WasmOpcode lt_op() const { return wasm::kExprF32Lt; }
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(F32ConvertOps);
|
||||
};
|
||||
|
||||
struct F64ConvertOps final : public WasmGraphBuilder::FloatConvertOps {
|
||||
explicit F64ConvertOps(WasmGraphBuilder* builder)
|
||||
: WasmGraphBuilder::FloatConvertOps(builder) {}
|
||||
~F64ConvertOps() = default;
|
||||
Node* zero() const { return builder_->Float64Constant(0.0); }
|
||||
wasm::WasmOpcode trunc_op() const { return wasm::kExprF64Trunc; }
|
||||
wasm::WasmOpcode ne_op() const { return wasm::kExprF64Ne; }
|
||||
wasm::WasmOpcode lt_op() const { return wasm::kExprF64Lt; }
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(F64ConvertOps);
|
||||
};
|
||||
|
||||
Node* WasmGraphBuilder::BuildConvertCheck(Node* test, Node* result, Node* input,
|
||||
wasm::WasmCodePosition position,
|
||||
NumericImplementation impl,
|
||||
const IntConvertOps* int_ops,
|
||||
const FloatConvertOps* float_ops) {
|
||||
switch (impl) {
|
||||
case NumericImplementation::kTrap:
|
||||
TrapIfTrue(wasm::kTrapFloatUnrepresentable, overflow, position);
|
||||
TrapIfTrue(wasm::kTrapFloatUnrepresentable, test, position);
|
||||
return result;
|
||||
case NumericImplementation::kSaturate: {
|
||||
Diamond tl_d(graph(), jsgraph()->common(), overflow, BranchHint::kFalse);
|
||||
Diamond tl_d(graph(), jsgraph()->common(), test, BranchHint::kFalse);
|
||||
tl_d.Chain(*control_);
|
||||
Diamond nan_d(graph(), jsgraph()->common(),
|
||||
Binop(wasm::kExprF32Ne, input, input), // Checks if NaN.
|
||||
Binop(float_ops->ne_op(), input, input), // Checks if NaN.
|
||||
BranchHint::kFalse);
|
||||
nan_d.Nest(tl_d, true);
|
||||
Diamond sat_d(
|
||||
graph(), jsgraph()->common(),
|
||||
graph()->NewNode(m->Float32LessThan(), input, Float32Constant(0.0)),
|
||||
BranchHint::kNone);
|
||||
Diamond sat_d(graph(), jsgraph()->common(),
|
||||
Binop(float_ops->lt_op(), input, float_ops->zero()),
|
||||
BranchHint::kNone);
|
||||
sat_d.Nest(nan_d, false);
|
||||
Node* sat_val =
|
||||
sat_d.Phi(MachineRepresentation::kWord32,
|
||||
Int32Constant(std::numeric_limits<int32_t>::min()),
|
||||
Int32Constant(std::numeric_limits<int32_t>::max()));
|
||||
Node* nan_val =
|
||||
nan_d.Phi(MachineRepresentation::kWord32, Int32Constant(0), sat_val);
|
||||
return tl_d.Phi(MachineRepresentation::kWord32, nan_val, result);
|
||||
sat_d.Phi(int_ops->word_rep(), int_ops->min(), int_ops->max());
|
||||
Node* nan_val = nan_d.Phi(int_ops->word_rep(), int_ops->zero(), sat_val);
|
||||
return tl_d.Phi(int_ops->word_rep(), nan_val, result);
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::BuildI32SConvertF64(Node* input,
|
||||
wasm::WasmCodePosition position) {
|
||||
MachineOperatorBuilder* m = jsgraph()->machine();
|
||||
Node* WasmGraphBuilder::BuildI32ConvertOp(
|
||||
Node* input, wasm::WasmCodePosition position, NumericImplementation impl,
|
||||
const Operator* op, wasm::WasmOpcode check_op, const IntConvertOps* int_ops,
|
||||
const FloatConvertOps* float_ops) {
|
||||
// Truncation of the input value is needed for the overflow check later.
|
||||
Node* trunc = Unop(wasm::kExprF64Trunc, input);
|
||||
Node* result = graph()->NewNode(m->ChangeFloat64ToInt32(), trunc);
|
||||
Node* trunc = Unop(float_ops->trunc_op(), input);
|
||||
Node* result = graph()->NewNode(op, trunc);
|
||||
|
||||
// Convert the result back to f64. If we end up at a different value than the
|
||||
// truncated input value, then there has been an overflow and we trap.
|
||||
Node* check = Unop(wasm::kExprF64SConvertI32, result);
|
||||
Node* overflow = Binop(wasm::kExprF64Ne, trunc, check);
|
||||
TrapIfTrue(wasm::kTrapFloatUnrepresentable, overflow, position);
|
||||
// truncated input value, then there has been an overflow and we
|
||||
// trap/saturate.
|
||||
Node* check = Unop(check_op, result);
|
||||
Node* overflow = Binop(float_ops->ne_op(), trunc, check);
|
||||
return BuildConvertCheck(overflow, result, input, position, impl, int_ops,
|
||||
float_ops);
|
||||
}
|
||||
|
||||
return result;
|
||||
Node* WasmGraphBuilder::BuildI32SConvertF32(Node* input,
|
||||
wasm::WasmCodePosition position,
|
||||
NumericImplementation impl) {
|
||||
I32SConvertOps int_ops(this);
|
||||
F32ConvertOps float_ops(this);
|
||||
return BuildI32ConvertOp(input, position, impl,
|
||||
jsgraph()->machine()->TruncateFloat32ToInt32(),
|
||||
wasm::kExprF32SConvertI32, &int_ops, &float_ops);
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::BuildI32SConvertF64(Node* input,
|
||||
wasm::WasmCodePosition position,
|
||||
NumericImplementation impl) {
|
||||
I32SConvertOps int_ops(this);
|
||||
F64ConvertOps float_ops(this);
|
||||
return BuildI32ConvertOp(input, position, impl,
|
||||
jsgraph()->machine()->ChangeFloat64ToInt32(),
|
||||
wasm::kExprF64SConvertI32, &int_ops, &float_ops);
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::BuildI32UConvertF32(Node* input,
|
||||
wasm::WasmCodePosition position) {
|
||||
MachineOperatorBuilder* m = jsgraph()->machine();
|
||||
// Truncation of the input value is needed for the overflow check later.
|
||||
Node* trunc = Unop(wasm::kExprF32Trunc, input);
|
||||
Node* result = graph()->NewNode(m->TruncateFloat32ToUint32(), trunc);
|
||||
|
||||
// Convert the result back to f32. If we end up at a different value than the
|
||||
// truncated input value, then there has been an overflow and we trap.
|
||||
Node* check = Unop(wasm::kExprF32UConvertI32, result);
|
||||
Node* overflow = Binop(wasm::kExprF32Ne, trunc, check);
|
||||
TrapIfTrue(wasm::kTrapFloatUnrepresentable, overflow, position);
|
||||
|
||||
return result;
|
||||
wasm::WasmCodePosition position,
|
||||
NumericImplementation impl) {
|
||||
I32UConvertOps int_ops(this);
|
||||
F32ConvertOps float_ops(this);
|
||||
return BuildI32ConvertOp(input, position, impl,
|
||||
jsgraph()->machine()->TruncateFloat32ToUint32(),
|
||||
wasm::kExprF32UConvertI32, &int_ops, &float_ops);
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::BuildI32UConvertF64(Node* input,
|
||||
wasm::WasmCodePosition position) {
|
||||
MachineOperatorBuilder* m = jsgraph()->machine();
|
||||
// Truncation of the input value is needed for the overflow check later.
|
||||
Node* trunc = Unop(wasm::kExprF64Trunc, input);
|
||||
Node* result = graph()->NewNode(m->TruncateFloat64ToUint32(), trunc);
|
||||
|
||||
// Convert the result back to f64. If we end up at a different value than the
|
||||
// truncated input value, then there has been an overflow and we trap.
|
||||
Node* check = Unop(wasm::kExprF64UConvertI32, result);
|
||||
Node* overflow = Binop(wasm::kExprF64Ne, trunc, check);
|
||||
TrapIfTrue(wasm::kTrapFloatUnrepresentable, overflow, position);
|
||||
|
||||
return result;
|
||||
wasm::WasmCodePosition position,
|
||||
NumericImplementation impl) {
|
||||
I32UConvertOps int_ops(this);
|
||||
F64ConvertOps float_ops(this);
|
||||
return BuildI32ConvertOp(input, position, impl,
|
||||
jsgraph()->machine()->TruncateFloat64ToUint32(),
|
||||
wasm::kExprF64UConvertI32, &int_ops, &float_ops);
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::BuildI32AsmjsSConvertF32(Node* input) {
|
||||
|
@ -252,6 +252,8 @@ typedef ZoneVector<Node*> NodeVector;
|
||||
class WasmGraphBuilder {
|
||||
public:
|
||||
enum EnforceBoundsCheck : bool { kNeedsBoundsCheck, kCanOmitBoundsCheck };
|
||||
struct IntConvertOps;
|
||||
struct FloatConvertOps;
|
||||
|
||||
WasmGraphBuilder(ModuleEnv* env, Zone* zone, JSGraph* graph,
|
||||
Handle<Code> centry_stub, wasm::FunctionSig* sig,
|
||||
@ -509,11 +511,25 @@ class WasmGraphBuilder {
|
||||
|
||||
Node* BuildF32CopySign(Node* left, Node* right);
|
||||
Node* BuildF64CopySign(Node* left, Node* right);
|
||||
|
||||
Node* BuildI32ConvertOp(Node* input, wasm::WasmCodePosition position,
|
||||
NumericImplementation impl, const Operator* op,
|
||||
wasm::WasmOpcode check_op,
|
||||
const IntConvertOps* int_ops,
|
||||
const FloatConvertOps* float_ops);
|
||||
Node* BuildConvertCheck(Node* test, Node* result, Node* input,
|
||||
wasm::WasmCodePosition position,
|
||||
NumericImplementation impl,
|
||||
const IntConvertOps* int_ops,
|
||||
const FloatConvertOps* float_ops);
|
||||
Node* BuildI32SConvertF32(Node* input, wasm::WasmCodePosition position,
|
||||
NumericImplementation impl);
|
||||
Node* BuildI32SConvertF64(Node* input, wasm::WasmCodePosition position);
|
||||
Node* BuildI32UConvertF32(Node* input, wasm::WasmCodePosition position);
|
||||
Node* BuildI32UConvertF64(Node* input, wasm::WasmCodePosition position);
|
||||
Node* BuildI32SConvertF64(Node* input, wasm::WasmCodePosition position,
|
||||
NumericImplementation impl);
|
||||
Node* BuildI32UConvertF32(Node* input, wasm::WasmCodePosition position,
|
||||
NumericImplementation impl);
|
||||
Node* BuildI32UConvertF64(Node* input, wasm::WasmCodePosition position,
|
||||
NumericImplementation impl);
|
||||
Node* BuildI32Ctz(Node* input);
|
||||
Node* BuildI32Popcnt(Node* input);
|
||||
Node* BuildI64Ctz(Node* input);
|
||||
|
@ -126,6 +126,12 @@ namespace wasm {
|
||||
V(F32CopySign, Float32) \
|
||||
V(F64CopySign, Float64)
|
||||
|
||||
#define FOREACH_I32CONV_FLOATOP(V) \
|
||||
V(I32SConvertF32, int32_t, float) \
|
||||
V(I32SConvertF64, int32_t, double) \
|
||||
V(I32UConvertF32, uint32_t, float) \
|
||||
V(I32UConvertF64, uint32_t, double)
|
||||
|
||||
#define FOREACH_OTHER_UNOP(V) \
|
||||
V(I32Clz, uint32_t) \
|
||||
V(I32Ctz, uint32_t) \
|
||||
@ -147,10 +153,6 @@ namespace wasm {
|
||||
V(F64Floor, double) \
|
||||
V(F64Trunc, double) \
|
||||
V(F64NearestInt, double) \
|
||||
V(I32SConvertF32, float) \
|
||||
V(I32SConvertF64, double) \
|
||||
V(I32UConvertF32, float) \
|
||||
V(I32UConvertF64, double) \
|
||||
V(I32ConvertI64, int64_t) \
|
||||
V(I64SConvertF32, float) \
|
||||
V(I64SConvertF64, double) \
|
||||
@ -441,47 +443,26 @@ inline double ExecuteF64NearestInt(double a, TrapReason* trap) {
|
||||
|
||||
inline double ExecuteF64Sqrt(double a, TrapReason* trap) { return sqrt(a); }
|
||||
|
||||
int32_t ExecuteI32SConvertF32(float a, TrapReason* trap) {
|
||||
if (is_inbounds<int32_t>(a)) {
|
||||
return static_cast<int32_t>(a);
|
||||
template <typename int_type, typename float_type>
|
||||
int_type ExecuteConvert(float_type a, TrapReason* trap) {
|
||||
if (is_inbounds<int_type>(a)) {
|
||||
return static_cast<int_type>(a);
|
||||
}
|
||||
*trap = kTrapFloatUnrepresentable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t ExecuteI32SConvertSatF32(float a) {
|
||||
template <typename int_type, typename float_type>
|
||||
int_type ExecuteConvertSaturate(float_type a) {
|
||||
TrapReason base_trap = kTrapCount;
|
||||
int32_t val = ExecuteI32SConvertF32(a, &base_trap);
|
||||
int32_t val = ExecuteConvert<int_type>(a, &base_trap);
|
||||
if (base_trap == kTrapCount) {
|
||||
return val;
|
||||
}
|
||||
return std::isnan(a) ? 0
|
||||
: (a < 0.0 ? std::numeric_limits<int32_t>::min()
|
||||
: std::numeric_limits<int32_t>::max());
|
||||
}
|
||||
|
||||
int32_t ExecuteI32SConvertF64(double a, TrapReason* trap) {
|
||||
if (is_inbounds<int32_t>(a)) {
|
||||
return static_cast<int32_t>(a);
|
||||
}
|
||||
*trap = kTrapFloatUnrepresentable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t ExecuteI32UConvertF32(float a, TrapReason* trap) {
|
||||
if (is_inbounds<uint32_t>(a)) {
|
||||
return static_cast<uint32_t>(a);
|
||||
}
|
||||
*trap = kTrapFloatUnrepresentable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t ExecuteI32UConvertF64(double a, TrapReason* trap) {
|
||||
if (is_inbounds<uint32_t>(a)) {
|
||||
return static_cast<uint32_t>(a);
|
||||
}
|
||||
*trap = kTrapFloatUnrepresentable;
|
||||
return 0;
|
||||
: (a < static_cast<float_type>(0.0)
|
||||
? std::numeric_limits<int_type>::min()
|
||||
: std::numeric_limits<int_type>::max());
|
||||
}
|
||||
|
||||
inline uint32_t ExecuteI32ConvertI64(int64_t a, TrapReason* trap) {
|
||||
@ -1550,12 +1531,18 @@ class ThreadImpl {
|
||||
bool ExecuteNumericOp(WasmOpcode opcode, Decoder* decoder,
|
||||
InterpreterCode* code, pc_t pc, int& len) {
|
||||
switch (opcode) {
|
||||
case kExprI32SConvertSatF32: {
|
||||
float val = Pop().to<float>();
|
||||
auto result = ExecuteI32SConvertSatF32(val);
|
||||
Push(WasmValue(result));
|
||||
case kExprI32SConvertSatF32:
|
||||
Push(WasmValue(ExecuteConvertSaturate<int32_t>(Pop().to<float>())));
|
||||
return true;
|
||||
case kExprI32UConvertSatF32:
|
||||
Push(WasmValue(ExecuteConvertSaturate<uint32_t>(Pop().to<float>())));
|
||||
return true;
|
||||
case kExprI32SConvertSatF64:
|
||||
Push(WasmValue(ExecuteConvertSaturate<int32_t>(Pop().to<double>())));
|
||||
return true;
|
||||
case kExprI32UConvertSatF64:
|
||||
Push(WasmValue(ExecuteConvertSaturate<uint32_t>(Pop().to<double>())));
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
V8_Fatal(__FILE__, __LINE__, "Unknown or unimplemented opcode #%d:%s",
|
||||
code->start[pc], OpcodeName(code->start[pc]));
|
||||
@ -2143,19 +2130,27 @@ class ThreadImpl {
|
||||
FOREACH_OTHER_BINOP(EXECUTE_OTHER_BINOP)
|
||||
#undef EXECUTE_OTHER_BINOP
|
||||
|
||||
#define EXECUTE_OTHER_UNOP(name, ctype) \
|
||||
#define EXECUTE_UNOP(name, ctype, exec_fn) \
|
||||
case kExpr##name: { \
|
||||
TrapReason trap = kTrapCount; \
|
||||
ctype val = Pop().to<ctype>(); \
|
||||
auto result = Execute##name(val, &trap); \
|
||||
auto result = exec_fn(val, &trap); \
|
||||
possible_nondeterminism_ |= has_nondeterminism(result); \
|
||||
if (trap != kTrapCount) return DoTrap(trap, pc); \
|
||||
Push(WasmValue(result)); \
|
||||
break; \
|
||||
}
|
||||
|
||||
#define EXECUTE_OTHER_UNOP(name, ctype) EXECUTE_UNOP(name, ctype, Execute##name)
|
||||
FOREACH_OTHER_UNOP(EXECUTE_OTHER_UNOP)
|
||||
#undef EXECUTE_OTHER_UNOP
|
||||
|
||||
#define EXECUTE_I32CONV_FLOATOP(name, out_type, in_type) \
|
||||
EXECUTE_UNOP(name, in_type, ExecuteConvert<out_type>)
|
||||
FOREACH_I32CONV_FLOATOP(EXECUTE_I32CONV_FLOATOP)
|
||||
#undef EXECUTE_I32CONV_FLOATOP
|
||||
#undef EXECUTE_UNOP
|
||||
|
||||
default:
|
||||
V8_Fatal(__FILE__, __LINE__, "Unknown or unimplemented opcode #%d:%s",
|
||||
code->start[pc], OpcodeName(code->start[pc]));
|
||||
@ -2960,6 +2955,7 @@ WasmInterpreter::HeapObjectsScope::~HeapObjectsScope() {
|
||||
#undef WASM_CTYPES
|
||||
#undef FOREACH_SIMPLE_BINOP
|
||||
#undef FOREACH_OTHER_BINOP
|
||||
#undef FOREACH_I32CONV_FLOATOP
|
||||
#undef FOREACH_OTHER_UNOP
|
||||
|
||||
} // namespace wasm
|
||||
|
@ -49,6 +49,9 @@ namespace wasm {
|
||||
#define CASE_CONVERT_OP(name, RES, SRC, src_suffix, str) \
|
||||
CASE_##RES##_OP(U##name##SRC, str "_u/" src_suffix) \
|
||||
CASE_##RES##_OP(S##name##SRC, str "_s/" src_suffix)
|
||||
#define CASE_CONVERT_SAT_OP(name, RES, SRC, src_suffix, str) \
|
||||
CASE_##RES##_OP(U##name##Sat##SRC, str "_u:sat/" src_suffix) \
|
||||
CASE_##RES##_OP(S##name##Sat##SRC, str "_s:sat/" src_suffix)
|
||||
#define CASE_L32_OP(name, str) \
|
||||
CASE_SIGN_OP(I32, name##8, str "8") \
|
||||
CASE_SIGN_OP(I32, name##16, str "16") \
|
||||
@ -98,9 +101,9 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
|
||||
CASE_I32_OP(ConvertI64, "wrap/i64")
|
||||
CASE_CONVERT_OP(Convert, INT, F32, "f32", "trunc")
|
||||
CASE_CONVERT_OP(Convert, INT, F64, "f64", "trunc")
|
||||
// TODO(kschimpf): Simplify after filling in other saturating
|
||||
// operations.
|
||||
CASE_I32_OP(SConvertSatF32, "trunc_s:sat/f32")
|
||||
// TODO(kschimpf): Add I64 versions of saturating conversions.
|
||||
CASE_CONVERT_SAT_OP(Convert, I32, F32, "f32", "trunc")
|
||||
CASE_CONVERT_SAT_OP(Convert, I32, F64, "f64", "trunc")
|
||||
|
||||
CASE_CONVERT_OP(Convert, I64, I32, "i32", "extend")
|
||||
CASE_CONVERT_OP(Convert, F32, I32, "i32", "convert")
|
||||
@ -279,6 +282,7 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
|
||||
#undef CASE_UNSIGNED_OP
|
||||
#undef CASE_ALL_SIGN_OP
|
||||
#undef CASE_CONVERT_OP
|
||||
#undef CASE_CONVERT_SAT_OP
|
||||
#undef CASE_L32_OP
|
||||
#undef CASE_U32_OP
|
||||
|
||||
|
@ -399,8 +399,12 @@ using WasmName = Vector<const char>;
|
||||
V(S128LoadMem, 0xfd80, s_i) \
|
||||
V(S128StoreMem, 0xfd81, v_is)
|
||||
|
||||
#define FOREACH_NUMERIC_OPCODE(V) V(I32SConvertSatF32, 0xfc00, i_f)
|
||||
// TODO(kschimpf): Add remaining numeric opcodes.
|
||||
#define FOREACH_NUMERIC_OPCODE(V) \
|
||||
V(I32SConvertSatF32, 0xfc00, i_f) \
|
||||
V(I32UConvertSatF32, 0xfc01, i_f) \
|
||||
V(I32SConvertSatF64, 0xfc02, i_d) \
|
||||
V(I32UConvertSatF64, 0xfc03, i_d)
|
||||
// TODO(kschimpf): Add remaining i64 numeric opcodes.
|
||||
|
||||
#define FOREACH_ATOMIC_OPCODE(V) \
|
||||
V(I32AtomicLoad, 0xfe10, i_i) \
|
||||
|
@ -2854,17 +2854,8 @@ WASM_EXEC_TEST(I32SConvertF32) {
|
||||
WasmRunner<int32_t, float> r(execution_mode);
|
||||
BUILD(r, WASM_I32_SCONVERT_F32(WASM_GET_LOCAL(0)));
|
||||
|
||||
constexpr float kLowerBound =
|
||||
static_cast<float>(std::numeric_limits<int32_t>::min());
|
||||
constexpr float kUpperBound =
|
||||
static_cast<float>(std::numeric_limits<int32_t>::max());
|
||||
assert(static_cast<int64_t>(kUpperBound) >
|
||||
static_cast<int64_t>(std::numeric_limits<int32_t>::max()));
|
||||
assert(static_cast<int32_t>(kLowerBound) ==
|
||||
std::numeric_limits<int32_t>::min());
|
||||
|
||||
FOR_FLOAT32_INPUTS(i) {
|
||||
if (*i < kUpperBound && *i >= kLowerBound) {
|
||||
if (is_inbounds<int32_t>(*i)) {
|
||||
CHECK_EQ(static_cast<int32_t>(*i), r.Call(*i));
|
||||
} else {
|
||||
CHECK_TRAP32(r.Call(*i));
|
||||
@ -2877,25 +2868,15 @@ WASM_EXEC_TEST(I32SConvertSatF32) {
|
||||
WasmRunner<int32_t, float> r(execution_mode);
|
||||
BUILD(r, WASM_I32_SCONVERT_SAT_F32(WASM_GET_LOCAL(0)));
|
||||
|
||||
constexpr float kLowerBound = std::numeric_limits<int32_t>::min();
|
||||
constexpr float kUpperBound = std::numeric_limits<int32_t>::max();
|
||||
|
||||
FOR_FLOAT32_INPUTS(i) {
|
||||
static_assert(static_cast<int64_t>(kUpperBound) >
|
||||
static_cast<int64_t>(std::numeric_limits<int32_t>::max()),
|
||||
"kUpperBound invalidates the following bounds check.");
|
||||
static_assert(static_cast<int32_t>(kLowerBound) ==
|
||||
std::numeric_limits<int32_t>::min(),
|
||||
"kLowerBounds invalidates the following bounds check.");
|
||||
if (*i < kUpperBound && *i >= kLowerBound) {
|
||||
CHECK_EQ(static_cast<int32_t>(*i), r.Call(*i));
|
||||
} else if (std::isnan(*i)) {
|
||||
CHECK_EQ(0, r.Call(*i));
|
||||
} else if (*i < 0.0) {
|
||||
CHECK_EQ(std::numeric_limits<int32_t>::min(), r.Call(*i));
|
||||
} else {
|
||||
CHECK_EQ(std::numeric_limits<int32_t>::max(), r.Call(*i));
|
||||
}
|
||||
int32_t expected =
|
||||
is_inbounds<int32_t>(*i)
|
||||
? static_cast<int32_t>(*i)
|
||||
: std::isnan(*i) ? 0
|
||||
: *i < 0.0 ? std::numeric_limits<int32_t>::min()
|
||||
: std::numeric_limits<int32_t>::max();
|
||||
int32_t found = r.Call(*i);
|
||||
CHECK_EQ(expected, found);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2903,14 +2884,8 @@ WASM_EXEC_TEST(I32SConvertF64) {
|
||||
WasmRunner<int32_t, double> r(execution_mode);
|
||||
BUILD(r, WASM_I32_SCONVERT_F64(WASM_GET_LOCAL(0)));
|
||||
|
||||
// The upper bound is (INT32_MAX + 1), which is the lowest double-
|
||||
// representable number above INT32_MAX which cannot be represented as int32.
|
||||
double upper_bound = 2147483648.0;
|
||||
// The lower bound is (INT32_MIN - 1), which is the greatest double-
|
||||
// representable number below INT32_MIN which cannot be represented as int32.
|
||||
double lower_bound = -2147483649.0;
|
||||
FOR_FLOAT64_INPUTS(i) {
|
||||
if (*i<upper_bound&& * i> lower_bound) {
|
||||
if (is_inbounds<int32_t>(*i)) {
|
||||
CHECK_EQ(static_cast<int32_t>(*i), r.Call(*i));
|
||||
} else {
|
||||
CHECK_TRAP32(r.Call(*i));
|
||||
@ -2918,16 +2893,27 @@ WASM_EXEC_TEST(I32SConvertF64) {
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I32SConvertSatF64) {
|
||||
EXPERIMENTAL_FLAG_SCOPE(sat_f2i_conversions);
|
||||
WasmRunner<int32_t, double> r(execution_mode);
|
||||
BUILD(r, WASM_I32_SCONVERT_SAT_F64(WASM_GET_LOCAL(0)));
|
||||
FOR_FLOAT64_INPUTS(i) {
|
||||
int32_t expected =
|
||||
is_inbounds<int32_t>(*i)
|
||||
? static_cast<int32_t>(*i)
|
||||
: std::isnan(*i) ? 0
|
||||
: *i < 0.0 ? std::numeric_limits<int32_t>::min()
|
||||
: std::numeric_limits<int32_t>::max();
|
||||
int32_t found = r.Call(*i);
|
||||
CHECK_EQ(expected, found);
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I32UConvertF32) {
|
||||
WasmRunner<uint32_t, float> r(execution_mode);
|
||||
BUILD(r, WASM_I32_UCONVERT_F32(WASM_GET_LOCAL(0)));
|
||||
// The upper bound is (UINT32_MAX + 1), which is the lowest
|
||||
// float-representable number above UINT32_MAX which cannot be represented as
|
||||
// uint32.
|
||||
double upper_bound = 4294967296.0f;
|
||||
double lower_bound = -1.0f;
|
||||
FOR_FLOAT32_INPUTS(i) {
|
||||
if (*i<upper_bound&& * i> lower_bound) {
|
||||
if (is_inbounds<uint32_t>(*i)) {
|
||||
CHECK_EQ(static_cast<uint32_t>(*i), r.Call(*i));
|
||||
} else {
|
||||
CHECK_TRAP32(r.Call(*i));
|
||||
@ -2935,16 +2921,27 @@ WASM_EXEC_TEST(I32UConvertF32) {
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I32UConvertSatF32) {
|
||||
EXPERIMENTAL_FLAG_SCOPE(sat_f2i_conversions);
|
||||
WasmRunner<uint32_t, float> r(execution_mode);
|
||||
BUILD(r, WASM_I32_UCONVERT_SAT_F32(WASM_GET_LOCAL(0)));
|
||||
FOR_FLOAT32_INPUTS(i) {
|
||||
int32_t expected =
|
||||
is_inbounds<uint32_t>(*i)
|
||||
? static_cast<uint32_t>(*i)
|
||||
: std::isnan(*i) ? 0
|
||||
: *i < 0.0 ? std::numeric_limits<uint32_t>::min()
|
||||
: std::numeric_limits<uint32_t>::max();
|
||||
int32_t found = r.Call(*i);
|
||||
CHECK_EQ(expected, found);
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I32UConvertF64) {
|
||||
WasmRunner<uint32_t, double> r(execution_mode);
|
||||
BUILD(r, WASM_I32_UCONVERT_F64(WASM_GET_LOCAL(0)));
|
||||
// The upper bound is (UINT32_MAX + 1), which is the lowest
|
||||
// double-representable number above UINT32_MAX which cannot be represented as
|
||||
// uint32.
|
||||
double upper_bound = 4294967296.0;
|
||||
double lower_bound = -1.0;
|
||||
FOR_FLOAT64_INPUTS(i) {
|
||||
if (*i<upper_bound&& * i> lower_bound) {
|
||||
if (is_inbounds<uint32_t>(*i)) {
|
||||
CHECK_EQ(static_cast<uint32_t>(*i), r.Call(*i));
|
||||
} else {
|
||||
CHECK_TRAP32(r.Call(*i));
|
||||
@ -2952,6 +2949,22 @@ WASM_EXEC_TEST(I32UConvertF64) {
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I32UConvertSatF64) {
|
||||
EXPERIMENTAL_FLAG_SCOPE(sat_f2i_conversions);
|
||||
WasmRunner<uint32_t, double> r(execution_mode);
|
||||
BUILD(r, WASM_I32_UCONVERT_SAT_F64(WASM_GET_LOCAL(0)));
|
||||
FOR_FLOAT64_INPUTS(i) {
|
||||
int32_t expected =
|
||||
is_inbounds<uint32_t>(*i)
|
||||
? static_cast<uint32_t>(*i)
|
||||
: std::isnan(*i) ? 0
|
||||
: *i < 0.0 ? std::numeric_limits<uint32_t>::min()
|
||||
: std::numeric_limits<uint32_t>::max();
|
||||
int32_t found = r.Call(*i);
|
||||
CHECK_EQ(expected, found);
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(F64CopySign) {
|
||||
WasmRunner<double, double, double> r(execution_mode);
|
||||
BUILD(r, WASM_F64_COPYSIGN(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
|
||||
|
@ -561,6 +561,9 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
|
||||
//------------------------------------------------------------------------------
|
||||
#define WASM_NUMERIC_OP(op) kNumericPrefix, static_cast<byte>(op)
|
||||
#define WASM_I32_SCONVERT_SAT_F32(x) x, WASM_NUMERIC_OP(kExprI32SConvertSatF32)
|
||||
#define WASM_I32_UCONVERT_SAT_F32(x) x, WASM_NUMERIC_OP(kExprI32UConvertSatF32)
|
||||
#define WASM_I32_SCONVERT_SAT_F64(x) x, WASM_NUMERIC_OP(kExprI32SConvertSatF64)
|
||||
#define WASM_I32_UCONVERT_SAT_F64(x) x, WASM_NUMERIC_OP(kExprI32UConvertSatF64)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Memory Operations.
|
||||
|
Loading…
Reference in New Issue
Block a user