[turbofan] Introduce dedicated ObjectIsNaN operator.

We can compile a !== a and Number.isNaN(a) to ObjectIsNaN. The former is
commonly used to check for NaN, i.e. in case of equals in AngularJS.

R=jarin@chromium.org
BUG=v8:5267

Review-Url: https://codereview.chromium.org/2722483003
Cr-Commit-Position: refs/heads/master@{#43572}
This commit is contained in:
bmeurer 2017-03-03 01:08:14 -08:00 committed by Commit bot
parent 7631b923ca
commit de52562d8e
14 changed files with 138 additions and 13 deletions

View File

@ -720,6 +720,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kObjectIsDetectableCallable:
result = LowerObjectIsDetectableCallable(node);
break;
case IrOpcode::kObjectIsNaN:
result = LowerObjectIsNaN(node);
break;
case IrOpcode::kObjectIsNonCallable:
result = LowerObjectIsNonCallable(node);
break;
@ -1717,6 +1720,29 @@ Node* EffectControlLinearizer::LowerObjectIsDetectableCallable(Node* node) {
return done.PhiAt(0);
}
Node* EffectControlLinearizer::LowerObjectIsNaN(Node* node) {
Node* value = node->InputAt(0);
Node* zero = __ Int32Constant(0);
auto done = __ MakeLabel<3>(MachineRepresentation::kBit);
// Check if {value} is a Smi.
__ GotoIf(ObjectIsSmi(value), &done, zero);
// Check if {value} is a HeapNumber.
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
__ GotoUnless(__ WordEqual(value_map, __ HeapNumberMapConstant()), &done,
zero);
// Check if {value} contains a NaN.
Node* value_value = __ LoadField(AccessBuilder::ForHeapNumberValue(), value);
__ Goto(&done,
__ Word32Equal(__ Float64Equal(value_value, value_value), zero));
__ Bind(&done);
return done.PhiAt(0);
}
Node* EffectControlLinearizer::LowerObjectIsNonCallable(Node* node) {
Node* value = node->InputAt(0);

View File

@ -81,6 +81,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerTruncateTaggedToWord32(Node* node);
Node* LowerCheckedTruncateTaggedToWord32(Node* node, Node* frame_state);
Node* LowerObjectIsDetectableCallable(Node* node);
Node* LowerObjectIsNaN(Node* node);
Node* LowerObjectIsNonCallable(Node* node);
Node* LowerObjectIsNumber(Node* node);
Node* LowerObjectIsReceiver(Node* node);

View File

@ -1513,14 +1513,10 @@ Reduction JSBuiltinReducer::ReduceNumberIsInteger(Node* node) {
// ES6 section 20.1.2.4 Number.isNaN ( number )
Reduction JSBuiltinReducer::ReduceNumberIsNaN(Node* node) {
JSCallReduction r(node);
if (r.InputsMatchOne(Type::Number())) {
// Number.isNaN(a:number) -> BooleanNot(NumberEqual(a, a))
Node* input = r.GetJSCallInput(0);
Node* check = graph()->NewNode(simplified()->NumberEqual(), input, input);
Node* value = graph()->NewNode(simplified()->BooleanNot(), check);
return Replace(value);
}
return NoChange();
// Number.isNaN(a:number) -> ObjectIsNaN(a)
Node* input = r.GetJSCallInput(0);
Node* value = graph()->NewNode(simplified()->ObjectIsNaN(), input);
return Replace(value);
}
// ES6 section 20.1.2.5 Number.isSafeInteger ( number )

View File

@ -993,11 +993,12 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node, bool invert) {
JSBinopReduction r(this, node);
if (r.left() == r.right()) {
// x === x is always true if x != NaN
if (!r.left_type()->Maybe(Type::NaN())) {
Node* replacement = jsgraph()->BooleanConstant(!invert);
ReplaceWithValue(node, replacement);
return Replace(replacement);
Node* replacement = graph()->NewNode(simplified()->ObjectIsNaN(), r.left());
if (!invert) {
replacement = graph()->NewNode(simplified()->BooleanNot(), replacement);
}
ReplaceWithValue(node, replacement);
return Replace(replacement);
}
if (r.OneInputCannotBe(Type::NumberOrString())) {
// For values with canonical representation (i.e. neither String, nor

View File

@ -333,6 +333,7 @@
V(StoreElement) \
V(StoreTypedElement) \
V(ObjectIsDetectableCallable) \
V(ObjectIsNaN) \
V(ObjectIsNonCallable) \
V(ObjectIsNumber) \
V(ObjectIsReceiver) \

View File

@ -2545,6 +2545,35 @@ class RepresentationSelector {
VisitObjectIs(node, Type::DetectableCallable(), lowering);
return;
}
case IrOpcode::kObjectIsNaN: {
Type* const input_type = GetUpperBound(node->InputAt(0));
if (input_type->Is(Type::NaN())) {
VisitUnop(node, UseInfo::None(), MachineRepresentation::kBit);
if (lower()) {
DeferReplacement(node, lowering->jsgraph()->Int32Constant(1));
}
} else if (!input_type->Maybe(Type::NaN())) {
VisitUnop(node, UseInfo::Any(), MachineRepresentation::kBit);
if (lower()) {
DeferReplacement(node, lowering->jsgraph()->Int32Constant(0));
}
} else if (input_type->Is(Type::Number())) {
VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kBit);
if (lower()) {
// ObjectIsNaN(x:kRepFloat64) => Word32Equal(Float64Equal(x,x),#0)
Node* const input = node->InputAt(0);
node->ReplaceInput(
0, jsgraph_->graph()->NewNode(
lowering->machine()->Float64Equal(), input, input));
node->AppendInput(jsgraph_->zone(), jsgraph_->Int32Constant(0));
NodeProperties::ChangeOp(node, lowering->machine()->Word32Equal());
}
} else {
VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
}
return;
}
case IrOpcode::kObjectIsNonCallable: {
VisitObjectIs(node, Type::NonCallable(), lowering);
return;

View File

@ -475,6 +475,7 @@ UnicodeEncoding UnicodeEncodingOf(const Operator* op) {
V(TruncateTaggedToWord32, Operator::kNoProperties, 1, 0) \
V(TruncateTaggedToFloat64, Operator::kNoProperties, 1, 0) \
V(ObjectIsDetectableCallable, Operator::kNoProperties, 1, 0) \
V(ObjectIsNaN, Operator::kNoProperties, 1, 0) \
V(ObjectIsNonCallable, Operator::kNoProperties, 1, 0) \
V(ObjectIsNumber, Operator::kNoProperties, 1, 0) \
V(ObjectIsReceiver, Operator::kNoProperties, 1, 0) \

View File

@ -413,6 +413,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* ConvertTaggedHoleToUndefined();
const Operator* ObjectIsDetectableCallable();
const Operator* ObjectIsNaN();
const Operator* ObjectIsNonCallable();
const Operator* ObjectIsNumber();
const Operator* ObjectIsReceiver();

View File

@ -285,6 +285,7 @@ class Typer::Visitor : public Reducer {
#undef DECLARE_METHOD
static Type* ObjectIsDetectableCallable(Type*, Typer*);
static Type* ObjectIsNaN(Type*, Typer*);
static Type* ObjectIsNonCallable(Type*, Typer*);
static Type* ObjectIsNumber(Type*, Typer*);
static Type* ObjectIsReceiver(Type*, Typer*);
@ -512,6 +513,12 @@ Type* Typer::Visitor::ObjectIsDetectableCallable(Type* type, Typer* t) {
return Type::Boolean();
}
Type* Typer::Visitor::ObjectIsNaN(Type* type, Typer* t) {
if (type->Is(Type::NaN())) return t->singleton_true_;
if (!type->Maybe(Type::NaN())) return t->singleton_false_;
return Type::Boolean();
}
Type* Typer::Visitor::ObjectIsNonCallable(Type* type, Typer* t) {
if (type->Is(Type::NonCallable())) return t->singleton_true_;
if (!type->Maybe(Type::NonCallable())) return t->singleton_false_;
@ -1926,6 +1933,10 @@ Type* Typer::Visitor::TypeObjectIsDetectableCallable(Node* node) {
return TypeUnaryOp(node, ObjectIsDetectableCallable);
}
Type* Typer::Visitor::TypeObjectIsNaN(Node* node) {
return TypeUnaryOp(node, ObjectIsNaN);
}
Type* Typer::Visitor::TypeObjectIsNonCallable(Node* node) {
return TypeUnaryOp(node, ObjectIsNonCallable);
}

View File

@ -954,6 +954,7 @@ void Verifier::Visitor::Check(Node* node) {
break;
case IrOpcode::kObjectIsDetectableCallable:
case IrOpcode::kObjectIsNaN:
case IrOpcode::kObjectIsNonCallable:
case IrOpcode::kObjectIsNumber:
case IrOpcode::kObjectIsReceiver:

View File

@ -0,0 +1,55 @@
// 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: --allow-natives-syntax
(function() {
function foo(x) { return Number.isNaN(x); }
assertTrue(foo(+undefined));
assertFalse(foo(undefined));
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(+undefined));
assertFalse(foo(undefined));
})();
(function() {
function foo(x) { return Number.isNaN(+x); }
assertTrue(foo(+undefined));
assertFalse(foo(0));
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(+undefined));
assertFalse(foo(0));
})();
(function() {
function foo(x) { return Number.isNaN(x|0); }
assertFalse(foo(+undefined));
assertFalse(foo(0));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(+undefined));
assertFalse(foo(0));
})();
(function() {
function foo(x) { return Number.isNaN("" + x); }
assertFalse(foo(undefined));
assertFalse(foo(0));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(undefined));
assertFalse(foo(0));
})();
(function() {
function foo(x) { return Number.isNaN(0/0); }
assertTrue(foo());
assertTrue(foo());
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo());
assertTrue(foo());
})();

View File

@ -1475,7 +1475,7 @@ TEST_F(JSBuiltinReducerTest, NumberIsNaNWithNumber) {
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsBooleanNot(IsNumberEqual(p0, p0)));
EXPECT_THAT(r.replacement(), IsObjectIsNaN(p0));
}
}

View File

@ -2348,6 +2348,7 @@ IS_UNOP_MATCHER(NumberToBoolean)
IS_UNOP_MATCHER(NumberToInt32)
IS_UNOP_MATCHER(NumberToUint32)
IS_UNOP_MATCHER(PlainPrimitiveToNumber)
IS_UNOP_MATCHER(ObjectIsNaN)
IS_UNOP_MATCHER(ObjectIsReceiver)
IS_UNOP_MATCHER(ObjectIsSmi)
IS_UNOP_MATCHER(ObjectIsUndetectable)

View File

@ -308,6 +308,7 @@ Matcher<Node*> IsStoreElement(const Matcher<ElementAccess>& access_matcher,
const Matcher<Node*>& value_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher);
Matcher<Node*> IsObjectIsNaN(const Matcher<Node*>& value_matcher);
Matcher<Node*> IsObjectIsReceiver(const Matcher<Node*>& value_matcher);
Matcher<Node*> IsObjectIsSmi(const Matcher<Node*>& value_matcher);
Matcher<Node*> IsObjectIsUndetectable(const Matcher<Node*>& value_matcher);