deps: cherry-pick 56f6a76 from upstream V8
Original commit message:
[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: Sigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57382}
PR-URL: https://github.com/nodejs/node/pull/25269
Refs: 56f6a763c2
Fixes: https://github.com/nodejs/node/issues/25268
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: Ujjwal Sharma <usharma1998@gmail.com>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
e54d11e2f6
commit
ddbb7d7777
@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
# Reset this number to 0 on major V8 upgrades.
|
# Reset this number to 0 on major V8 upgrades.
|
||||||
# Increment by one for each non-official patch applied to deps/v8.
|
# Increment by one for each non-official patch applied to deps/v8.
|
||||||
'v8_embedder_string': '-node.8',
|
'v8_embedder_string': '-node.9',
|
||||||
|
|
||||||
##### V8 defaults for Node.js #####
|
##### V8 defaults for Node.js #####
|
||||||
|
|
||||||
|
@ -797,6 +797,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;
|
||||||
@ -2543,6 +2546,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);
|
||||||
@ -2559,15 +2570,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);
|
||||||
|
if (machine()->Is64()) {
|
||||||
|
Node* value64 = __ BitcastFloat64ToInt64(value_value);
|
||||||
|
__ Goto(&done, __ Word64Equal(value64, __ Int64Constant(kMinusZeroBits)));
|
||||||
|
} else {
|
||||||
|
Node* value_lo = __ Float64ExtractLowWord32(value_value);
|
||||||
|
__ GotoIfNot(__ Word32Equal(value_lo, __ Int32Constant(kMinusZeroLoBits)),
|
||||||
|
&done, zero);
|
||||||
|
Node* value_hi = __ Float64ExtractHighWord32(value_value);
|
||||||
__ Goto(&done,
|
__ Goto(&done,
|
||||||
__ Float64Equal(
|
__ Word32Equal(value_hi, __ Int32Constant(kMinusZeroHiBits)));
|
||||||
__ Float64Div(__ Float64Constant(1.0), value_value),
|
}
|
||||||
__ Float64Constant(-std::numeric_limits<double>::infinity())));
|
|
||||||
|
|
||||||
__ 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);
|
||||||
|
@ -106,6 +106,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);
|
||||||
|
1
deps/v8/src/compiler/opcodes.h
vendored
1
deps/v8/src/compiler/opcodes.h
vendored
@ -417,6 +417,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) \
|
||||||
|
12
deps/v8/src/compiler/simplified-lowering.cc
vendored
12
deps/v8/src/compiler/simplified-lowering.cc
vendored
@ -3008,17 +3008,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);
|
||||||
|
1
deps/v8/src/compiler/simplified-operator.cc
vendored
1
deps/v8/src/compiler/simplified-operator.cc
vendored
@ -745,6 +745,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) \
|
||||||
|
1
deps/v8/src/compiler/simplified-operator.h
vendored
1
deps/v8/src/compiler/simplified-operator.h
vendored
@ -719,6 +719,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();
|
||||||
|
11
deps/v8/src/compiler/typer.cc
vendored
11
deps/v8/src/compiler/typer.cc
vendored
@ -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*);
|
||||||
@ -597,6 +598,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_;
|
||||||
@ -2104,6 +2111,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();
|
||||||
}
|
}
|
||||||
|
1
deps/v8/src/compiler/verifier.cc
vendored
1
deps/v8/src/compiler/verifier.cc
vendored
@ -1188,6 +1188,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());
|
||||||
|
39
deps/v8/test/mjsunit/regress/regress-crbug-903043.js
vendored
Normal file
39
deps/v8/test/mjsunit/regress/regress-crbug-903043.js
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// 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)));
|
||||||
|
})();
|
Loading…
x
Reference in New Issue
Block a user