Commit 56f6a763 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Fix -0 check for subnormals.

Previously we'd check `x` for -0 by testing `(1.0 / x) == -Infinity`,
but this will yield the wrong results when `x` is a subnormal, i.e.
really close to 0.

In CSA we already perform bit checks to test for -0, so teach TurboFan
to do the same for comparisons to -0 (via `Object.is`). We introduce a
new NumberIsMinusZero simplified operator to handle the case where
SimplifiedLowering already knows that the input is a number.

Bug: chromium:903043, v8:6882
Change-Id: I0cb7c568029b461a92fc183104d5f359b4bfe7f4
Reviewed-on: https://chromium-review.googlesource.com/c/1328802
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarSigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57382}
parent 88fe4e54
...@@ -809,6 +809,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, ...@@ -809,6 +809,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kObjectIsMinusZero: case IrOpcode::kObjectIsMinusZero:
result = LowerObjectIsMinusZero(node); result = LowerObjectIsMinusZero(node);
break; break;
case IrOpcode::kNumberIsMinusZero:
result = LowerNumberIsMinusZero(node);
break;
case IrOpcode::kObjectIsNaN: case IrOpcode::kObjectIsNaN:
result = LowerObjectIsNaN(node); result = LowerObjectIsNaN(node);
break; break;
...@@ -2660,6 +2663,14 @@ Node* EffectControlLinearizer::LowerObjectIsSafeInteger(Node* node) { ...@@ -2660,6 +2663,14 @@ Node* EffectControlLinearizer::LowerObjectIsSafeInteger(Node* node) {
return done.PhiAt(0); return done.PhiAt(0);
} }
namespace {
const int64_t kMinusZeroBits = bit_cast<int64_t>(-0.0);
const int32_t kMinusZeroLoBits = static_cast<int32_t>(kMinusZeroBits);
const int32_t kMinusZeroHiBits = static_cast<int32_t>(kMinusZeroBits >> 32);
} // namespace
Node* EffectControlLinearizer::LowerObjectIsMinusZero(Node* node) { Node* EffectControlLinearizer::LowerObjectIsMinusZero(Node* node) {
Node* value = node->InputAt(0); Node* value = node->InputAt(0);
Node* zero = __ Int32Constant(0); Node* zero = __ Int32Constant(0);
...@@ -2676,15 +2687,43 @@ Node* EffectControlLinearizer::LowerObjectIsMinusZero(Node* node) { ...@@ -2676,15 +2687,43 @@ Node* EffectControlLinearizer::LowerObjectIsMinusZero(Node* node) {
// Check if {value} contains -0. // Check if {value} contains -0.
Node* value_value = __ LoadField(AccessBuilder::ForHeapNumberValue(), value); Node* value_value = __ LoadField(AccessBuilder::ForHeapNumberValue(), value);
__ Goto(&done, if (machine()->Is64()) {
__ Float64Equal( Node* value64 = __ BitcastFloat64ToInt64(value_value);
__ Float64Div(__ Float64Constant(1.0), value_value), __ Goto(&done, __ Word64Equal(value64, __ Int64Constant(kMinusZeroBits)));
__ Float64Constant(-std::numeric_limits<double>::infinity()))); } else {
Node* value_lo = __ Float64ExtractLowWord32(value_value);
__ GotoIfNot(__ Word32Equal(value_lo, __ Int32Constant(kMinusZeroLoBits)),
&done, zero);
Node* value_hi = __ Float64ExtractHighWord32(value_value);
__ Goto(&done,
__ Word32Equal(value_hi, __ Int32Constant(kMinusZeroHiBits)));
}
__ Bind(&done); __ Bind(&done);
return done.PhiAt(0); return done.PhiAt(0);
} }
Node* EffectControlLinearizer::LowerNumberIsMinusZero(Node* node) {
Node* value = node->InputAt(0);
if (machine()->Is64()) {
Node* value64 = __ BitcastFloat64ToInt64(value);
return __ Word64Equal(value64, __ Int64Constant(kMinusZeroBits));
} else {
auto done = __ MakeLabel(MachineRepresentation::kBit);
Node* value_lo = __ Float64ExtractLowWord32(value);
__ GotoIfNot(__ Word32Equal(value_lo, __ Int32Constant(kMinusZeroLoBits)),
&done, __ Int32Constant(0));
Node* value_hi = __ Float64ExtractHighWord32(value);
__ Goto(&done,
__ Word32Equal(value_hi, __ Int32Constant(kMinusZeroHiBits)));
__ Bind(&done);
return done.PhiAt(0);
}
}
Node* EffectControlLinearizer::LowerObjectIsNaN(Node* node) { Node* EffectControlLinearizer::LowerObjectIsNaN(Node* node) {
Node* value = node->InputAt(0); Node* value = node->InputAt(0);
Node* zero = __ Int32Constant(0); Node* zero = __ Int32Constant(0);
......
...@@ -110,6 +110,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { ...@@ -110,6 +110,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerObjectIsConstructor(Node* node); Node* LowerObjectIsConstructor(Node* node);
Node* LowerObjectIsDetectableCallable(Node* node); Node* LowerObjectIsDetectableCallable(Node* node);
Node* LowerObjectIsMinusZero(Node* node); Node* LowerObjectIsMinusZero(Node* node);
Node* LowerNumberIsMinusZero(Node* node);
Node* LowerObjectIsNaN(Node* node); Node* LowerObjectIsNaN(Node* node);
Node* LowerNumberIsNaN(Node* node); Node* LowerNumberIsNaN(Node* node);
Node* LowerObjectIsNonCallable(Node* node); Node* LowerObjectIsNonCallable(Node* node);
......
...@@ -426,6 +426,7 @@ ...@@ -426,6 +426,7 @@
V(ObjectIsConstructor) \ V(ObjectIsConstructor) \
V(ObjectIsDetectableCallable) \ V(ObjectIsDetectableCallable) \
V(ObjectIsMinusZero) \ V(ObjectIsMinusZero) \
V(NumberIsMinusZero) \
V(ObjectIsNaN) \ V(ObjectIsNaN) \
V(NumberIsNaN) \ V(NumberIsNaN) \
V(ObjectIsNonCallable) \ V(ObjectIsNonCallable) \
......
...@@ -3050,17 +3050,7 @@ class RepresentationSelector { ...@@ -3050,17 +3050,7 @@ class RepresentationSelector {
VisitUnop(node, UseInfo::TruncatingFloat64(), VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kBit); MachineRepresentation::kBit);
if (lower()) { if (lower()) {
// ObjectIsMinusZero(x:kRepFloat64) NodeProperties::ChangeOp(node, simplified()->NumberIsMinusZero());
// => Float64Equal(Float64Div(1.0,x),-Infinity)
Node* const input = node->InputAt(0);
node->ReplaceInput(
0, jsgraph_->graph()->NewNode(
lowering->machine()->Float64Div(),
lowering->jsgraph()->Float64Constant(1.0), input));
node->AppendInput(jsgraph_->zone(),
jsgraph_->Float64Constant(
-std::numeric_limits<double>::infinity()));
NodeProperties::ChangeOp(node, lowering->machine()->Float64Equal());
} }
} else { } else {
VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit); VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
......
...@@ -747,6 +747,7 @@ bool operator==(CheckMinusZeroParameters const& lhs, ...@@ -747,6 +747,7 @@ bool operator==(CheckMinusZeroParameters const& lhs,
V(ObjectIsConstructor, Operator::kNoProperties, 1, 0) \ V(ObjectIsConstructor, Operator::kNoProperties, 1, 0) \
V(ObjectIsDetectableCallable, Operator::kNoProperties, 1, 0) \ V(ObjectIsDetectableCallable, Operator::kNoProperties, 1, 0) \
V(ObjectIsMinusZero, Operator::kNoProperties, 1, 0) \ V(ObjectIsMinusZero, Operator::kNoProperties, 1, 0) \
V(NumberIsMinusZero, Operator::kNoProperties, 1, 0) \
V(ObjectIsNaN, Operator::kNoProperties, 1, 0) \ V(ObjectIsNaN, Operator::kNoProperties, 1, 0) \
V(NumberIsNaN, Operator::kNoProperties, 1, 0) \ V(NumberIsNaN, Operator::kNoProperties, 1, 0) \
V(ObjectIsNonCallable, Operator::kNoProperties, 1, 0) \ V(ObjectIsNonCallable, Operator::kNoProperties, 1, 0) \
......
...@@ -726,6 +726,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -726,6 +726,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* ObjectIsConstructor(); const Operator* ObjectIsConstructor();
const Operator* ObjectIsDetectableCallable(); const Operator* ObjectIsDetectableCallable();
const Operator* ObjectIsMinusZero(); const Operator* ObjectIsMinusZero();
const Operator* NumberIsMinusZero();
const Operator* ObjectIsNaN(); const Operator* ObjectIsNaN();
const Operator* NumberIsNaN(); const Operator* NumberIsNaN();
const Operator* ObjectIsNonCallable(); const Operator* ObjectIsNonCallable();
......
...@@ -290,6 +290,7 @@ class Typer::Visitor : public Reducer { ...@@ -290,6 +290,7 @@ class Typer::Visitor : public Reducer {
static Type ObjectIsConstructor(Type, Typer*); static Type ObjectIsConstructor(Type, Typer*);
static Type ObjectIsDetectableCallable(Type, Typer*); static Type ObjectIsDetectableCallable(Type, Typer*);
static Type ObjectIsMinusZero(Type, Typer*); static Type ObjectIsMinusZero(Type, Typer*);
static Type NumberIsMinusZero(Type, Typer*);
static Type ObjectIsNaN(Type, Typer*); static Type ObjectIsNaN(Type, Typer*);
static Type NumberIsNaN(Type, Typer*); static Type NumberIsNaN(Type, Typer*);
static Type ObjectIsNonCallable(Type, Typer*); static Type ObjectIsNonCallable(Type, Typer*);
...@@ -599,6 +600,12 @@ Type Typer::Visitor::ObjectIsMinusZero(Type type, Typer* t) { ...@@ -599,6 +600,12 @@ Type Typer::Visitor::ObjectIsMinusZero(Type type, Typer* t) {
return Type::Boolean(); return Type::Boolean();
} }
Type Typer::Visitor::NumberIsMinusZero(Type type, Typer* t) {
if (type.Is(Type::MinusZero())) return t->singleton_true_;
if (!type.Maybe(Type::MinusZero())) return t->singleton_false_;
return Type::Boolean();
}
Type Typer::Visitor::ObjectIsNaN(Type type, Typer* t) { Type Typer::Visitor::ObjectIsNaN(Type type, Typer* t) {
if (type.Is(Type::NaN())) return t->singleton_true_; if (type.Is(Type::NaN())) return t->singleton_true_;
if (!type.Maybe(Type::NaN())) return t->singleton_false_; if (!type.Maybe(Type::NaN())) return t->singleton_false_;
...@@ -2142,6 +2149,10 @@ Type Typer::Visitor::TypeObjectIsMinusZero(Node* node) { ...@@ -2142,6 +2149,10 @@ Type Typer::Visitor::TypeObjectIsMinusZero(Node* node) {
return TypeUnaryOp(node, ObjectIsMinusZero); return TypeUnaryOp(node, ObjectIsMinusZero);
} }
Type Typer::Visitor::TypeNumberIsMinusZero(Node* node) {
return TypeUnaryOp(node, NumberIsMinusZero);
}
Type Typer::Visitor::TypeNumberIsFloat64Hole(Node* node) { Type Typer::Visitor::TypeNumberIsFloat64Hole(Node* node) {
return Type::Boolean(); return Type::Boolean();
} }
......
...@@ -1209,6 +1209,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) { ...@@ -1209,6 +1209,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckValueInputIs(node, 0, Type::Number()); CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::Boolean()); CheckTypeIs(node, Type::Boolean());
break; break;
case IrOpcode::kNumberIsMinusZero:
case IrOpcode::kNumberIsNaN: case IrOpcode::kNumberIsNaN:
CheckValueInputIs(node, 0, Type::Number()); CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::Boolean()); CheckTypeIs(node, Type::Boolean());
......
// Copyright 2018 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: --allow-natives-syntax
(function() {
function foo() {
const x = 1e-1;
return Object.is(-0, x * (-1e-308));
}
assertFalse(foo());
assertFalse(foo());
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo());
})();
(function() {
function foo(x) {
return Object.is(-0, x * (-1e-308));
}
assertFalse(foo(1e-1));
assertFalse(foo(1e-1));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(1e-1));
})();
(function() {
function foo(x) {
return Object.is(-0, x);
}
assertFalse(foo(1e-1 * (-1e-308)));
assertFalse(foo(1e-1 * (-1e-308)));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(1e-1 * (-1e-308)));
})();
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