[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:
parent
7631b923ca
commit
de52562d8e
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 )
|
||||
|
@ -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
|
||||
|
@ -333,6 +333,7 @@
|
||||
V(StoreElement) \
|
||||
V(StoreTypedElement) \
|
||||
V(ObjectIsDetectableCallable) \
|
||||
V(ObjectIsNaN) \
|
||||
V(ObjectIsNonCallable) \
|
||||
V(ObjectIsNumber) \
|
||||
V(ObjectIsReceiver) \
|
||||
|
@ -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;
|
||||
|
@ -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) \
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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:
|
||||
|
55
test/mjsunit/number-isnan-opt.js
Normal file
55
test/mjsunit/number-isnan-opt.js
Normal 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());
|
||||
})();
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user