Commit 55aed782 authored by ahaas's avatar ahaas Committed by Commit bot

[wasm] Fix constant folding with signalling NaN.

According to the WebAssembly spec no arithmetic operation should ever
return a signalling NaN. With the constant folding in V8, however, it
was possible that some arithmetic operations were elided, and if the
input of the arithmetic operation was a signalling NaN, then also the
result was the same signalling NaN. This CL removes some constant
folding optimizations and adjusts others so that even with constant
folding the result of an arithmetic operation is never a signalling NaN.

R=titzer@chromium.org, rossberg@chromium.org, bmeurer@chromium.org

Review-Url: https://codereview.chromium.org/2647353007
Cr-Commit-Position: refs/heads/master@{#42694}
parent 01d051ff
......@@ -316,14 +316,14 @@ Reduction MachineOperatorReducer::Reduce(Node* node) {
}
case IrOpcode::kFloat32Sub: {
Float32BinopMatcher m(node);
if (m.right().Is(0) && (copysign(1.0, m.right().Value()) > 0)) {
return Replace(m.left().node()); // x - 0 => x
}
// TODO(ahaas): We could do x - 0.0 = x if we knew that x is not an sNaN.
if (m.right().IsNaN()) { // x - NaN => NaN
return Replace(m.right().node());
// Do some calculation to make a signalling NaN quiet.
return ReplaceFloat32(m.right().Value() - m.right().Value());
}
if (m.left().IsNaN()) { // NaN - x => NaN
return Replace(m.left().node());
// Do some calculation to make a signalling NaN quiet.
return ReplaceFloat32(m.left().Value() - m.left().Value());
}
if (m.IsFoldable()) { // L - R => (L - R)
return ReplaceFloat32(m.left().Value() - m.right().Value());
......@@ -350,7 +350,8 @@ Reduction MachineOperatorReducer::Reduce(Node* node) {
case IrOpcode::kFloat64Add: {
Float64BinopMatcher m(node);
if (m.right().IsNaN()) { // x + NaN => NaN
return Replace(m.right().node());
// Do some calculation to make a signalling NaN quiet.
return ReplaceFloat64(m.right().Value() - m.right().Value());
}
if (m.IsFoldable()) { // K + K => K
return ReplaceFloat64(m.left().Value() + m.right().Value());
......@@ -359,14 +360,14 @@ Reduction MachineOperatorReducer::Reduce(Node* node) {
}
case IrOpcode::kFloat64Sub: {
Float64BinopMatcher m(node);
if (m.right().Is(0) && (Double(m.right().Value()).Sign() > 0)) {
return Replace(m.left().node()); // x - 0 => x
}
// TODO(ahaas): We could do x - 0.0 = x if we knew that x is not an sNaN.
if (m.right().IsNaN()) { // x - NaN => NaN
return Replace(m.right().node());
// Do some calculation to make a signalling NaN quiet.
return ReplaceFloat64(m.right().Value() - m.right().Value());
}
if (m.left().IsNaN()) { // NaN - x => NaN
return Replace(m.left().node());
// Do some calculation to make a signalling NaN quiet.
return ReplaceFloat64(m.left().Value() - m.left().Value());
}
if (m.IsFoldable()) { // L - R => (L - R)
return ReplaceFloat64(m.left().Value() - m.right().Value());
......@@ -392,15 +393,16 @@ Reduction MachineOperatorReducer::Reduce(Node* node) {
}
case IrOpcode::kFloat64Mul: {
Float64BinopMatcher m(node);
// TODO(ahaas): We could do x * 1.0 = x if we knew that x is not an sNaN.
if (m.right().Is(-1)) { // x * -1.0 => -0.0 - x
node->ReplaceInput(0, Float64Constant(-0.0));
node->ReplaceInput(1, m.left().node());
NodeProperties::ChangeOp(node, machine()->Float64Sub());
return Changed(node);
}
if (m.right().Is(1)) return Replace(m.left().node()); // x * 1.0 => x
if (m.right().IsNaN()) { // x * NaN => NaN
return Replace(m.right().node());
// Do some calculation to make a signalling NaN quiet.
return ReplaceFloat64(m.right().Value() - m.right().Value());
}
if (m.IsFoldable()) { // K * K => K
return ReplaceFloat64(m.left().Value() * m.right().Value());
......@@ -414,12 +416,14 @@ Reduction MachineOperatorReducer::Reduce(Node* node) {
}
case IrOpcode::kFloat64Div: {
Float64BinopMatcher m(node);
if (m.right().Is(1)) return Replace(m.left().node()); // x / 1.0 => x
// TODO(ahaas): We could do x / 1.0 = x if we knew that x is not an sNaN.
if (m.right().IsNaN()) { // x / NaN => NaN
return Replace(m.right().node());
// Do some calculation to make a signalling NaN quiet.
return ReplaceFloat64(m.right().Value() - m.right().Value());
}
if (m.left().IsNaN()) { // NaN / x => NaN
return Replace(m.left().node());
// Do some calculation to make a signalling NaN quiet.
return ReplaceFloat64(m.left().Value() - m.left().Value());
}
if (m.IsFoldable()) { // K / K => K
return ReplaceFloat64(m.left().Value() / m.right().Value());
......
......@@ -850,12 +850,12 @@ TEST(ReduceFloat32Sub) {
}
Node* x = R.Parameter();
Node* zero = R.Constant<float>(0.0);
Node* nan = R.Constant<float>(std::numeric_limits<float>::quiet_NaN());
R.CheckBinop(x, x, zero); // x - 0 => x
R.CheckBinop(nan, nan, x); // nan - x => nan
R.CheckBinop(nan, x, nan); // x - nan => nan
// nan - x => nan
R.CheckFoldBinop(std::numeric_limits<float>::quiet_NaN(), nan, x);
// x - nan => nan
R.CheckFoldBinop(std::numeric_limits<float>::quiet_NaN(), x, nan);
}
TEST(ReduceFloat64Sub) {
......@@ -870,12 +870,12 @@ TEST(ReduceFloat64Sub) {
}
Node* x = R.Parameter();
Node* zero = R.Constant<double>(0.0);
Node* nan = R.Constant<double>(std::numeric_limits<double>::quiet_NaN());
R.CheckBinop(x, x, zero); // x - 0 => x
R.CheckBinop(nan, nan, x); // nan - x => nan
R.CheckBinop(nan, x, nan); // x - nan => nan
// nan - x => nan
R.CheckFoldBinop(std::numeric_limits<double>::quiet_NaN(), nan, x);
// x - nan => nan
R.CheckFoldBinop(std::numeric_limits<double>::quiet_NaN(), x, nan);
}
// TODO(titzer): test MachineOperatorReducer for Word64And
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
(function() {
print("F32: sNaN - 0 = qNaN");
var builder = new WasmModuleBuilder();
builder.addFunction("F32Sub0", kSig_i_i)
.addBody([
kExprGetLocal, 0,
kExprF32ReinterpretI32,
kExprF32Const, 0x00, 0x00, 0x00, 0x00, // 0.0
kExprF32Sub,
kExprI32ReinterpretF32,
])
.exportFunc();
var module = builder.instantiate();
// F32Sub0(signalling_NaN)
assertEquals(0x7fe00000, module.exports.F32Sub0(0x7fa00000));
})();
(function() {
print("F32: sNaN - X = qNaN");
var builder = new WasmModuleBuilder();
builder.addFunction("F32NaNSubX", kSig_i_i)
.addBody([
kExprF32Const, 0x00, 0x00, 0xa0, 0x7f,
kExprF32Const, 0x12, 0x34, 0x56, 0x78,
kExprF32Sub,
kExprI32ReinterpretF32,
])
.exportFunc();
var module = builder.instantiate();
assertEquals(0x7fe00000, module.exports.F32NaNSubX());
})();
(function() {
print("F32: X - sNaN = qNaN");
var builder = new WasmModuleBuilder();
builder.addFunction("F32XSubNaN", kSig_i_i)
.addBody([
kExprF32Const, 0x12, 0x34, 0x56, 0x78,
kExprF32Const, 0x00, 0x00, 0xa0, 0x7f,
kExprF32Sub,
kExprI32ReinterpretF32,
])
.exportFunc();
var module = builder.instantiate();
assertEquals(0x7fe00000, module.exports.F32XSubNaN());
})();
(function() {
print("F64: X + sNaN = qNaN");
var builder = new WasmModuleBuilder();
builder.addFunction("F32XAddNaN", kSig_i_i)
.addBody([
kExprF64Const, 0xde, 0xbc, 0x0a, 0x89, 0x67, 0x45, 0x23, 0x01,
kExprF64Const, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x7f,
kExprF64Add,
kExprI64ReinterpretF64,
kExprI64Const, 32,
kExprI64ShrU,
kExprI32ConvertI64,
])
.exportFunc();
var module = builder.instantiate();
assertEquals(0x7ffa0000, module.exports.F32XAddNaN());
})();
(function() {
print("F64: sNaN - 0 = qNaN");
var builder = new WasmModuleBuilder();
builder.addFunction("F64Sub0", kSig_i_i)
.addBody([
kExprI64Const, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xf9, 0xff, 0x00,
kExprF64ReinterpretI64,
kExprF64Const, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0.0
kExprF64Sub,
kExprI64ReinterpretF64,
kExprI64Const, 32,
kExprI64ShrU,
kExprI32ConvertI64,
])
.exportFunc();
var module = builder.instantiate();
assertEquals(0x7ffa0000, module.exports.F64Sub0());
})();
(function() {
print("F64: sNaN - X = qNaN");
var builder = new WasmModuleBuilder();
builder.addFunction("F64NaNSubX", kSig_i_i)
.addBody([
kExprF64Const, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x7f,
kExprF64Const, 0xde, 0xbc, 0x0a, 0x89, 0x67, 0x45, 0x23, 0x01,
kExprF64Sub,
kExprI64ReinterpretF64,
kExprI64Const, 32,
kExprI64ShrU,
kExprI32ConvertI64,
])
.exportFunc();
var module = builder.instantiate();
assertEquals(0x7ffa0000, module.exports.F64NaNSubX());
})();
(function() {
print("F64: X - sNaN = qNaN");
var builder = new WasmModuleBuilder();
builder.addFunction("F64XSubNaN", kSig_i_i)
.addBody([
kExprF64Const, 0xde, 0xbc, 0x0a, 0x89, 0x67, 0x45, 0x23, 0x01,
kExprF64Const, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x7f,
kExprF64Sub,
kExprI64ReinterpretF64,
kExprI64Const, 32,
kExprI64ShrU,
kExprI32ConvertI64,
])
.exportFunc();
var module = builder.instantiate();
assertEquals(0x7ffa0000, module.exports.F64XSubNaN());
})();
(function() {
print("F64: sNaN * 1 = qNaN");
var builder = new WasmModuleBuilder();
builder.addFunction("F64Mul1", kSig_i_i)
.addBody([
kExprI64Const, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xf9, 0xff, 0x00,
kExprF64ReinterpretI64,
kExprF64Const, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f,
kExprF64Mul,
kExprI64ReinterpretF64,
kExprI64Const, 32,
kExprI64ShrU,
kExprI32ConvertI64,
])
.exportFunc();
var module = builder.instantiate();
assertEquals(0x7ffa0000, module.exports.F64Mul1());
})();
(function() {
print("F64: X * sNaN = qNaN");
var builder = new WasmModuleBuilder();
builder.addFunction("F64XMulNaN", kSig_i_i)
.addBody([
kExprF64Const, 0xde, 0xbc, 0x0a, 0x89, 0x67, 0x45, 0x23, 0x01,
kExprF64Const, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x7f,
kExprF64Mul,
kExprI64ReinterpretF64,
kExprI64Const, 32,
kExprI64ShrU,
kExprI32ConvertI64,
])
.exportFunc();
var module = builder.instantiate();
assertEquals(0x7ffa0000, module.exports.F64XMulNaN());
})();
(function() {
print("F64: sNaN / 1 = qNaN");
var builder = new WasmModuleBuilder();
builder.addFunction("F64Div1", kSig_i_i)
.addBody([
kExprI64Const, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xf9, 0xff, 0x00,
kExprF64ReinterpretI64,
kExprF64Const, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f,
kExprF64Div,
kExprI64ReinterpretF64,
kExprI64Const, 32,
kExprI64ShrU,
kExprI32ConvertI64,
])
.exportFunc();
var module = builder.instantiate();
assertEquals(0x7ffa0000, module.exports.F64Div1());
})();
(function() {
print("F64: X / sNaN = qNaN");
var builder = new WasmModuleBuilder();
builder.addFunction("F64XDivNaN", kSig_i_i)
.addBody([
kExprF64Const, 0xde, 0xbc, 0x0a, 0x89, 0x67, 0x45, 0x23, 0x01,
kExprF64Const, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x7f,
kExprF64Div,
kExprI64ReinterpretF64,
kExprI64Const, 32,
kExprI64ShrU,
kExprI32ConvertI64,
])
.exportFunc();
var module = builder.instantiate();
assertEquals(0x7ffa0000, module.exports.F64XDivNaN());
})();
(function() {
print("F64: sNaN / X = qNaN");
var builder = new WasmModuleBuilder();
builder.addFunction("F64NaNDivX", kSig_i_i)
.addBody([
kExprF64Const, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x7f,
kExprF64Const, 0xde, 0xbc, 0x0a, 0x89, 0x67, 0x45, 0x23, 0x01,
kExprF64Div,
kExprI64ReinterpretF64,
kExprI64Const, 32,
kExprI64ShrU,
kExprI32ConvertI64,
])
.exportFunc();
var module = builder.instantiate();
assertEquals(0x7ffa0000, module.exports.F64NaNDivX());
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment