[turbofan] Move isNaN/isFinite to JSCallReducer

This CL also adds the simplified operator NumberIsNaN.

Bug: v8:7340, v8:7250
Change-Id: Ifa44cf59b30ee700f7df61f8d58782a43fd0f3c5
Reviewed-on: https://chromium-review.googlesource.com/1023391
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52726}
This commit is contained in:
Sigurd Schneider 2018-04-23 10:49:19 +02:00 committed by Commit Bot
parent 7b4286b84f
commit 9557a91ff7
16 changed files with 147 additions and 130 deletions

View File

@ -770,6 +770,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kObjectIsNaN:
result = LowerObjectIsNaN(node);
break;
case IrOpcode::kNumberIsNaN:
result = LowerNumberIsNaN(node);
break;
case IrOpcode::kObjectIsNonCallable:
result = LowerObjectIsNonCallable(node);
break;
@ -2327,6 +2330,13 @@ Node* EffectControlLinearizer::LowerObjectIsNaN(Node* node) {
return done.PhiAt(0);
}
Node* EffectControlLinearizer::LowerNumberIsNaN(Node* node) {
Node* number = node->InputAt(0);
Node* diff = __ Float64Equal(number, number);
Node* check = __ Word32Equal(diff, __ Int32Constant(0));
return check;
}
Node* EffectControlLinearizer::LowerObjectIsNonCallable(Node* node) {
Node* value = node->InputAt(0);

View File

@ -97,6 +97,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerObjectIsDetectableCallable(Node* node);
Node* LowerObjectIsMinusZero(Node* node);
Node* LowerObjectIsNaN(Node* node);
Node* LowerNumberIsNaN(Node* node);
Node* LowerObjectIsNonCallable(Node* node);
Node* LowerObjectIsNumber(Node* node);
Node* LowerObjectIsReceiver(Node* node);

View File

@ -248,34 +248,6 @@ Reduction JSBuiltinReducer::ReduceDateGetTime(Node* node) {
return NoChange();
}
// ES6 section 18.2.2 isFinite ( number )
Reduction JSBuiltinReducer::ReduceGlobalIsFinite(Node* node) {
JSCallReduction r(node);
if (r.InputsMatchOne(Type::PlainPrimitive())) {
// isFinite(a:plain-primitive) -> NumberEqual(a', a')
// where a' = NumberSubtract(ToNumber(a), ToNumber(a))
Node* input = ToNumber(r.GetJSCallInput(0));
Node* diff = graph()->NewNode(simplified()->NumberSubtract(), input, input);
Node* value = graph()->NewNode(simplified()->NumberEqual(), diff, diff);
return Replace(value);
}
return NoChange();
}
// ES6 section 18.2.3 isNaN ( number )
Reduction JSBuiltinReducer::ReduceGlobalIsNaN(Node* node) {
JSCallReduction r(node);
if (r.InputsMatchOne(Type::PlainPrimitive())) {
// isNaN(a:plain-primitive) -> BooleanNot(NumberEqual(a', a'))
// where a' = ToNumber(a)
Node* input = ToNumber(r.GetJSCallInput(0));
Node* check = graph()->NewNode(simplified()->NumberEqual(), input, input);
Node* value = graph()->NewNode(simplified()->BooleanNot(), check);
return Replace(value);
}
return NoChange();
}
// ES6 section 20.1.2.13 Number.parseInt ( string, radix )
Reduction JSBuiltinReducer::ReduceNumberParseInt(Node* node) {
JSCallReduction r(node);
@ -306,12 +278,6 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
return ReduceDateNow(node);
case kDateGetTime:
return ReduceDateGetTime(node);
case kGlobalIsFinite:
reduction = ReduceGlobalIsFinite(node);
break;
case kGlobalIsNaN:
reduction = ReduceGlobalIsNaN(node);
break;
case kNumberParseInt:
reduction = ReduceNumberParseInt(node);
break;

View File

@ -43,8 +43,6 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final
Reduction ReduceDateNow(Node* node);
Reduction ReduceDateGetTime(Node* node);
Reduction ReduceGlobalIsFinite(Node* node);
Reduction ReduceGlobalIsNaN(Node* node);
Reduction ReduceNumberParseInt(Node* node);
Node* ToNumber(Node* value);
Node* ToUint32(Node* value);

View File

@ -3483,6 +3483,10 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
return ReduceNumberIsSafeInteger(node);
case Builtins::kNumberIsNaN:
return ReduceNumberIsNaN(node);
case Builtins::kGlobalIsFinite:
return ReduceGlobalIsFinite(node);
case Builtins::kGlobalIsNaN:
return ReduceGlobalIsNaN(node);
case Builtins::kMapPrototypeGet:
return ReduceMapPrototypeGet(node);
case Builtins::kMapPrototypeHas:
@ -6589,6 +6593,56 @@ Reduction JSCallReducer::ReduceArrayBufferViewAccessor(
return NoChange();
}
// ES6 section 18.2.2 isFinite ( number )
Reduction JSCallReducer::ReduceGlobalIsFinite(Node* node) {
CallParameters const& p = CallParametersOf(node->op());
if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
return NoChange();
}
if (node->op()->ValueInputCount() < 3) {
Node* value = jsgraph()->FalseConstant();
ReplaceWithValue(node, value);
return Replace(value);
}
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* input = NodeProperties::GetValueInput(node, 2);
input = effect =
graph()->NewNode(simplified()->SpeculativeToNumber(
NumberOperationHint::kNumberOrOddball, p.feedback()),
input, effect, control);
Node* value = graph()->NewNode(simplified()->NumberIsFinite(), input);
ReplaceWithValue(node, value, effect);
return Replace(value);
}
// ES6 section 18.2.3 isNaN ( number )
Reduction JSCallReducer::ReduceGlobalIsNaN(Node* node) {
CallParameters const& p = CallParametersOf(node->op());
if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
return NoChange();
}
if (node->op()->ValueInputCount() < 3) {
Node* value = jsgraph()->TrueConstant();
ReplaceWithValue(node, value);
return Replace(value);
}
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* input = NodeProperties::GetValueInput(node, 2);
input = effect =
graph()->NewNode(simplified()->SpeculativeToNumber(
NumberOperationHint::kNumberOrOddball, p.feedback()),
input, effect, control);
Node* value = graph()->NewNode(simplified()->NumberIsNaN(), input);
ReplaceWithValue(node, value, effect);
return Replace(value);
}
Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }

View File

@ -159,6 +159,9 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
Reduction ReduceNumberIsSafeInteger(Node* node);
Reduction ReduceNumberIsNaN(Node* node);
Reduction ReduceGlobalIsFinite(Node* node);
Reduction ReduceGlobalIsNaN(Node* node);
Reduction ReduceMapPrototypeHas(Node* node);
Reduction ReduceMapPrototypeGet(Node* node);
Reduction ReduceCollectionIteration(Node* node,

View File

@ -399,6 +399,7 @@
V(ObjectIsDetectableCallable) \
V(ObjectIsMinusZero) \
V(ObjectIsNaN) \
V(NumberIsNaN) \
V(ObjectIsNonCallable) \
V(ObjectIsNumber) \
V(ObjectIsReceiver) \

View File

@ -2769,7 +2769,9 @@ class RepresentationSelector {
return;
}
case IrOpcode::kNumberIsFinite: {
UNREACHABLE();
VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kBit);
return;
}
case IrOpcode::kObjectIsSafeInteger: {
Type* const input_type = GetUpperBound(node->InputAt(0));
@ -2876,19 +2878,18 @@ class RepresentationSelector {
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());
NodeProperties::ChangeOp(node, simplified()->NumberIsNaN());
}
} else {
VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
}
return;
}
case IrOpcode::kNumberIsNaN: {
VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kBit);
return;
}
case IrOpcode::kObjectIsNonCallable: {
VisitObjectIs(node, Type::NonCallable(), lowering);
return;

View File

@ -701,6 +701,7 @@ bool operator==(CheckMinusZeroParameters const& lhs,
V(ObjectIsDetectableCallable, Operator::kNoProperties, 1, 0) \
V(ObjectIsMinusZero, Operator::kNoProperties, 1, 0) \
V(ObjectIsNaN, Operator::kNoProperties, 1, 0) \
V(NumberIsNaN, Operator::kNoProperties, 1, 0) \
V(ObjectIsNonCallable, Operator::kNoProperties, 1, 0) \
V(ObjectIsNumber, Operator::kNoProperties, 1, 0) \
V(ObjectIsReceiver, Operator::kNoProperties, 1, 0) \

View File

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

View File

@ -303,6 +303,7 @@ class Typer::Visitor : public Reducer {
static Type* ObjectIsDetectableCallable(Type*, Typer*);
static Type* ObjectIsMinusZero(Type*, Typer*);
static Type* ObjectIsNaN(Type*, Typer*);
static Type* NumberIsNaN(Type*, Typer*);
static Type* ObjectIsNonCallable(Type*, Typer*);
static Type* ObjectIsNumber(Type*, Typer*);
static Type* ObjectIsReceiver(Type*, Typer*);
@ -617,6 +618,12 @@ Type* Typer::Visitor::ObjectIsNaN(Type* type, Typer* t) {
return Type::Boolean();
}
Type* Typer::Visitor::NumberIsNaN(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_;
@ -2183,7 +2190,7 @@ Type* Typer::Visitor::TypeNumberIsFloat64Hole(Node* node) {
return Type::Boolean();
}
Type* Typer::Visitor::TypeNumberIsFinite(Node* node) { UNREACHABLE(); }
Type* Typer::Visitor::TypeNumberIsFinite(Node* node) { return Type::Boolean(); }
Type* Typer::Visitor::TypeObjectIsFiniteNumber(Node* node) {
return Type::Boolean();
@ -2205,6 +2212,10 @@ Type* Typer::Visitor::TypeObjectIsNaN(Node* node) {
return TypeUnaryOp(node, ObjectIsNaN);
}
Type* Typer::Visitor::TypeNumberIsNaN(Node* node) {
return TypeUnaryOp(node, NumberIsNaN);
}
Type* Typer::Visitor::TypeObjectIsNonCallable(Node* node) {
return TypeUnaryOp(node, ObjectIsNonCallable);
}

View File

@ -1178,6 +1178,10 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::Boolean());
break;
case IrOpcode::kNumberIsNaN:
CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::Boolean());
break;
case IrOpcode::kObjectIsFiniteNumber:
CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::Boolean());

View File

@ -110,91 +110,6 @@ Type* const kNumberTypes[] = {
} // namespace
// -----------------------------------------------------------------------------
// isFinite
TEST_F(JSBuiltinReducerTest, GlobalIsFiniteWithNumber) {
Node* function = GlobalFunction("isFinite");
Node* effect = graph()->start();
Node* control = graph()->start();
Node* context = UndefinedConstant();
Node* frame_state = graph()->start();
TRACED_FOREACH(Type*, t0, kNumberTypes) {
Node* p0 = Parameter(t0, 0);
Node* call =
graph()->NewNode(javascript()->Call(3), function, UndefinedConstant(),
p0, context, frame_state, effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberEqual(IsNumberSubtract(p0, p0),
IsNumberSubtract(p0, p0)));
}
}
TEST_F(JSBuiltinReducerTest, GlobalIsFiniteWithPlainPrimitive) {
Node* function = GlobalFunction("isFinite");
Node* effect = graph()->start();
Node* control = graph()->start();
Node* context = UndefinedConstant();
Node* frame_state = graph()->start();
Node* p0 = Parameter(Type::PlainPrimitive(), 0);
Node* call =
graph()->NewNode(javascript()->Call(3), function, UndefinedConstant(), p0,
context, frame_state, effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsNumberEqual(IsNumberSubtract(IsPlainPrimitiveToNumber(p0),
IsPlainPrimitiveToNumber(p0)),
IsNumberSubtract(IsPlainPrimitiveToNumber(p0),
IsPlainPrimitiveToNumber(p0))));
}
// -----------------------------------------------------------------------------
// isNaN
TEST_F(JSBuiltinReducerTest, GlobalIsNaNWithNumber) {
Node* function = GlobalFunction("isNaN");
Node* effect = graph()->start();
Node* control = graph()->start();
Node* context = UndefinedConstant();
Node* frame_state = graph()->start();
TRACED_FOREACH(Type*, t0, kNumberTypes) {
Node* p0 = Parameter(t0, 0);
Node* call =
graph()->NewNode(javascript()->Call(3), function, UndefinedConstant(),
p0, context, frame_state, effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsBooleanNot(IsNumberEqual(p0, p0)));
}
}
TEST_F(JSBuiltinReducerTest, GlobalIsNaNWithPlainPrimitive) {
Node* function = GlobalFunction("isNaN");
Node* effect = graph()->start();
Node* control = graph()->start();
Node* context = UndefinedConstant();
Node* frame_state = graph()->start();
Node* p0 = Parameter(Type::PlainPrimitive(), 0);
Node* call =
graph()->NewNode(javascript()->Call(3), function, UndefinedConstant(), p0,
context, frame_state, effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsBooleanNot(IsNumberEqual(IsPlainPrimitiveToNumber(p0),
IsPlainPrimitiveToNumber(p0))));
}
// -----------------------------------------------------------------------------
// Number.parseInt

View File

@ -54,6 +54,15 @@ class JSCallReducerTest : public TypedGraphTest {
i::FLAG_lazy_handler_deserialization = old_flag_lazy_handler_;
}
Node* GlobalFunction(const char* name) {
Handle<JSFunction> f = Handle<JSFunction>::cast(
Object::GetProperty(
isolate()->global_object(),
isolate()->factory()->NewStringFromAsciiChecked(name))
.ToHandleChecked());
return HeapConstant(f);
}
Node* MathFunction(const std::string& name) {
Handle<Object> m =
JSObject::GetProperty(
@ -535,6 +544,44 @@ TEST_F(JSCallReducerTest, NumberIsSafeIntegerWithIntegral32) {
EXPECT_THAT(r.replacement(), IsObjectIsSafeInteger(p0));
}
// -----------------------------------------------------------------------------
// isFinite
TEST_F(JSCallReducerTest, GlobalIsFiniteWithNumber) {
Node* function = GlobalFunction("isFinite");
Node* effect = graph()->start();
Node* control = graph()->start();
Node* context = UndefinedConstant();
Node* frame_state = graph()->start();
Node* p0 = Parameter(Type::Any(), 0);
Node* call = graph()->NewNode(Call(3), function, UndefinedConstant(), p0,
context, frame_state, effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberIsFinite(IsSpeculativeToNumber(p0)));
}
// -----------------------------------------------------------------------------
// isNaN
TEST_F(JSCallReducerTest, GlobalIsNaN) {
Node* function = GlobalFunction("isNaN");
Node* effect = graph()->start();
Node* control = graph()->start();
Node* context = UndefinedConstant();
Node* frame_state = graph()->start();
Node* p0 = Parameter(Type::Any(), 0);
Node* call = graph()->NewNode(Call(3), function, UndefinedConstant(), p0,
context, frame_state, effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberIsNaN(IsSpeculativeToNumber(p0)));
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -2186,9 +2186,11 @@ IS_UNOP_MATCHER(NumberToInt32)
IS_UNOP_MATCHER(NumberToUint32)
IS_UNOP_MATCHER(PlainPrimitiveToNumber)
IS_UNOP_MATCHER(ObjectIsFiniteNumber)
IS_UNOP_MATCHER(NumberIsFinite)
IS_UNOP_MATCHER(ObjectIsInteger)
IS_UNOP_MATCHER(ObjectIsSafeInteger)
IS_UNOP_MATCHER(ObjectIsNaN)
IS_UNOP_MATCHER(NumberIsNaN)
IS_UNOP_MATCHER(ObjectIsReceiver)
IS_UNOP_MATCHER(ObjectIsSmi)
IS_UNOP_MATCHER(ObjectIsUndetectable)

View File

@ -312,9 +312,11 @@ Matcher<Node*> IsStoreElement(const Matcher<ElementAccess>& access_matcher,
const Matcher<Node*>& control_matcher);
Matcher<Node*> IsObjectIsFiniteNumber(const Matcher<Node*>& value_matcher);
Matcher<Node*> IsNumberIsFinite(const Matcher<Node*>& value_matcher);
Matcher<Node*> IsObjectIsInteger(const Matcher<Node*>& value_matcher);
Matcher<Node*> IsObjectIsSafeInteger(const Matcher<Node*>& value_matcher);
Matcher<Node*> IsObjectIsNaN(const Matcher<Node*>& value_matcher);
Matcher<Node*> IsNumberIsNaN(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);