Commit 516c25b4 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[turbofan] Move Number.isFinite to JSCallReducer

This also introduces two new simplified operators,
NumberIsFinite and ObjectIsFiniteNumber; the latter
handles all values, and the former is a fast-path
of the fast-path that is inserted by typed optimization
if we know the input has Type::Number.

Bug: v8:7340, v8:7250
Change-Id: I1b4812c01bf470bbff40fb3da6e11da543a22cd2
Reviewed-on: https://chromium-review.googlesource.com/951244
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51980}
parent e27deb72
......@@ -866,6 +866,12 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kNumberIsFloat64Hole:
result = LowerNumberIsFloat64Hole(node);
break;
case IrOpcode::kNumberIsFinite:
result = LowerNumberIsFinite(node);
break;
case IrOpcode::kObjectIsFiniteNumber:
result = LowerObjectIsFiniteNumber(node);
break;
case IrOpcode::kCheckFloat64Hole:
result = LowerCheckFloat64Hole(node, frame_state);
break;
......@@ -2149,6 +2155,38 @@ Node* EffectControlLinearizer::LowerNumberIsFloat64Hole(Node* node) {
return check;
}
Node* EffectControlLinearizer::LowerNumberIsFinite(Node* node) {
Node* number = node->InputAt(0);
Node* diff = __ Float64Sub(number, number);
Node* check = __ Float64Equal(diff, diff);
return check;
}
Node* EffectControlLinearizer::LowerObjectIsFiniteNumber(Node* node) {
Node* object = node->InputAt(0);
Node* zero = __ Int32Constant(0);
Node* one = __ Int32Constant(1);
auto done = __ MakeLabel(MachineRepresentation::kBit);
// Check if {value} is a Smi.
__ GotoIf(ObjectIsSmi(object), &done, one);
// Check if {value} is a HeapNumber.
Node* value_map = __ LoadField(AccessBuilder::ForMap(), object);
__ GotoIfNot(__ WordEqual(value_map, __ HeapNumberMapConstant()), &done,
zero);
// Value is a HeapNumber.
Node* value = __ LoadField(AccessBuilder::ForHeapNumberValue(), object);
Node* diff = __ Float64Sub(value, value);
Node* check = __ Float64Equal(diff, diff);
__ Goto(&done, check);
__ Bind(&done);
return done.PhiAt(0);
}
Node* EffectControlLinearizer::LowerObjectIsMinusZero(Node* node) {
Node* value = node->InputAt(0);
Node* zero = __ Int32Constant(0);
......
......@@ -105,6 +105,8 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerObjectIsSymbol(Node* node);
Node* LowerObjectIsUndetectable(Node* node);
Node* LowerNumberIsFloat64Hole(Node* node);
Node* LowerNumberIsFinite(Node* node);
Node* LowerObjectIsFiniteNumber(Node* node);
Node* LowerArgumentsFrame(Node* node);
Node* LowerArgumentsLength(Node* node);
Node* LowerNewDoubleElements(Node* node);
......
......@@ -754,20 +754,6 @@ Reduction JSBuiltinReducer::ReduceMapHas(Node* node) {
return Replace(value);
}
// ES6 section 20.1.2.2 Number.isFinite ( number )
Reduction JSBuiltinReducer::ReduceNumberIsFinite(Node* node) {
JSCallReduction r(node);
if (r.InputsMatchOne(Type::Number())) {
// Number.isFinite(a:number) -> NumberEqual(a', a')
// where a' = NumberSubtract(a, a)
Node* input = 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 20.1.2.3 Number.isInteger ( number )
Reduction JSBuiltinReducer::ReduceNumberIsInteger(Node* node) {
JSCallReduction r(node);
......@@ -996,9 +982,6 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
return ReduceCollectionIteratorNext(
node, OrderedHashMap::kEntrySize, factory()->empty_ordered_hash_map(),
FIRST_MAP_ITERATOR_TYPE, LAST_MAP_ITERATOR_TYPE);
case kNumberIsFinite:
reduction = ReduceNumberIsFinite(node);
break;
case kNumberIsInteger:
reduction = ReduceNumberIsInteger(node);
break;
......
......@@ -57,7 +57,6 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final
Reduction ReduceGlobalIsNaN(Node* node);
Reduction ReduceMapHas(Node* node);
Reduction ReduceMapGet(Node* node);
Reduction ReduceNumberIsFinite(Node* node);
Reduction ReduceNumberIsInteger(Node* node);
Reduction ReduceNumberIsNaN(Node* node);
Reduction ReduceNumberIsSafeInteger(Node* node);
......
......@@ -3424,6 +3424,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
case Builtins::kMathMin:
return ReduceMathMinMax(node, simplified()->NumberMin(),
jsgraph()->Constant(V8_INFINITY));
case Builtins::kNumberIsFinite:
return ReduceNumberIsFinite(node);
case Builtins::kReturnReceiver:
return ReduceReturnReceiver(node);
case Builtins::kStringPrototypeIndexOf:
......@@ -5877,6 +5879,19 @@ Reduction JSCallReducer::ReduceTypedArrayConstructor(
return Replace(result);
}
// ES #sec-number.isfinite
Reduction JSCallReducer::ReduceNumberIsFinite(Node* node) {
if (node->op()->ValueInputCount() < 3) {
Node* value = jsgraph()->FalseConstant();
ReplaceWithValue(node, value);
return Replace(value);
}
Node* input = NodeProperties::GetValueInput(node, 2);
Node* value = graph()->NewNode(simplified()->ObjectIsFiniteNumber(), input);
ReplaceWithValue(node, value);
return Replace(value);
}
Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }
......
......@@ -150,6 +150,8 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
Reduction ReduceMathClz32(Node* node);
Reduction ReduceMathMinMax(Node* node, const Operator* op, Node* empty_value);
Reduction ReduceNumberIsFinite(Node* node);
// Returns the updated {to} node, and updates control and effect along the
// way.
Node* DoFilterPostCallbackWork(ElementsKind kind, Node** control,
......
......@@ -384,6 +384,8 @@
V(TransitionAndStoreNonNumberElement) \
V(ToBoolean) \
V(NumberIsFloat64Hole) \
V(NumberIsFinite) \
V(ObjectIsFiniteNumber) \
V(ObjectIsArrayBufferView) \
V(ObjectIsBigInt) \
V(ObjectIsCallable) \
......
......@@ -2744,6 +2744,33 @@ class RepresentationSelector {
VisitObjectIs(node, Type::DetectableCallable(), lowering);
return;
}
case IrOpcode::kObjectIsFiniteNumber: {
Type* const input_type = GetUpperBound(node->InputAt(0));
if (input_type->Is(type_cache_.kInteger)) {
VisitUnop(node, UseInfo::None(), MachineRepresentation::kBit);
if (lower()) {
DeferReplacement(node, lowering->jsgraph()->Int32Constant(1));
}
} else if (!input_type->Maybe(Type::Number())) {
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()) {
NodeProperties::ChangeOp(node,
lowering->simplified()->NumberIsFinite());
}
} else {
VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
}
return;
}
case IrOpcode::kNumberIsFinite: {
UNREACHABLE();
}
case IrOpcode::kObjectIsMinusZero: {
Type* const input_type = GetUpperBound(node->InputAt(0));
if (input_type->Is(Type::MinusZero())) {
......
......@@ -709,6 +709,8 @@ bool operator==(CheckMinusZeroParameters const& lhs,
V(ObjectIsSymbol, Operator::kNoProperties, 1, 0) \
V(ObjectIsUndetectable, Operator::kNoProperties, 1, 0) \
V(NumberIsFloat64Hole, Operator::kNoProperties, 1, 0) \
V(NumberIsFinite, Operator::kNoProperties, 1, 0) \
V(ObjectIsFiniteNumber, Operator::kNoProperties, 1, 0) \
V(ConvertTaggedHoleToUndefined, Operator::kNoProperties, 1, 0) \
V(SameValue, Operator::kCommutative, 2, 0) \
V(ReferenceEqual, Operator::kCommutative, 2, 0) \
......
......@@ -622,6 +622,8 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* ObjectIsUndetectable();
const Operator* NumberIsFloat64Hole();
const Operator* NumberIsFinite();
const Operator* ObjectIsFiniteNumber();
const Operator* ArgumentsFrame();
const Operator* ArgumentsLength(int formal_parameter_count,
......
......@@ -2176,6 +2176,12 @@ Type* Typer::Visitor::TypeNumberIsFloat64Hole(Node* node) {
return Type::Boolean();
}
Type* Typer::Visitor::TypeNumberIsFinite(Node* node) { UNREACHABLE(); }
Type* Typer::Visitor::TypeObjectIsFiniteNumber(Node* node) {
return Type::Boolean();
}
Type* Typer::Visitor::TypeObjectIsNaN(Node* node) {
return TypeUnaryOp(node, ObjectIsNaN);
}
......
......@@ -1166,6 +1166,14 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckValueInputIs(node, 0, Type::NumberOrHole());
CheckTypeIs(node, Type::Boolean());
break;
case IrOpcode::kNumberIsFinite:
CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::Boolean());
break;
case IrOpcode::kObjectIsFiniteNumber:
CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::Boolean());
break;
case IrOpcode::kFindOrderedHashMapEntry:
CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::SignedSmall());
......
......@@ -12,6 +12,9 @@ function test(f) {
assertTrue(f(Number.MIN_SAFE_INTEGER - 13));
assertTrue(f(Number.MAX_SAFE_INTEGER));
assertTrue(f(Number.MAX_SAFE_INTEGER + 23));
assertTrue(f(0));
assertTrue(f(-1));
assertTrue(f(123456));
assertFalse(f(Number.NaN));
assertFalse(f(Number.POSITIVE_INFINITY));
assertFalse(f(Number.NEGATIVE_INFINITY));
......@@ -27,3 +30,33 @@ test(f);
test(f);
%OptimizeFunctionOnNextCall(f);
test(f);
function test2(f) {
assertFalse(f({}));
assertFalse(f("abc"));
assertTrue(f(0));
assertTrue(f(Number.MIN_VALUE));
assertTrue(f(Number.MAX_VALUE));
assertTrue(f(Number.MIN_SAFE_INTEGER));
assertTrue(f(Number.MIN_SAFE_INTEGER - 13));
assertTrue(f(Number.MAX_SAFE_INTEGER));
assertTrue(f(Number.MAX_SAFE_INTEGER + 23));
assertTrue(f(0));
assertTrue(f(-1));
assertTrue(f(123456));
assertFalse(f(Number.NaN));
assertFalse(f(Number.POSITIVE_INFINITY));
assertFalse(f(Number.NEGATIVE_INFINITY));
assertFalse(f(1 / 0));
assertFalse(f(-1 / 0));
}
function f2(x) {
return Number.isFinite(x);
}
test2(f2);
test2(f2);
%OptimizeFunctionOnNextCall(f2);
test2(f2);
......@@ -195,28 +195,6 @@ TEST_F(JSBuiltinReducerTest, GlobalIsNaNWithPlainPrimitive) {
IsPlainPrimitiveToNumber(p0))));
}
// -----------------------------------------------------------------------------
// Number.isFinite
TEST_F(JSBuiltinReducerTest, NumberIsFiniteWithNumber) {
Node* function = NumberFunction("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)));
}
}
// -----------------------------------------------------------------------------
// Number.isInteger
......
......@@ -80,6 +80,19 @@ class JSCallReducerTest : public TypedGraphTest {
return HeapConstant(f);
}
Node* NumberFunction(const char* name) {
Handle<Object> m =
JSObject::GetProperty(
isolate()->global_object(),
isolate()->factory()->NewStringFromAsciiChecked("Number"))
.ToHandleChecked();
Handle<JSFunction> f = Handle<JSFunction>::cast(
Object::GetProperty(
m, isolate()->factory()->NewStringFromAsciiChecked(name))
.ToHandleChecked());
return HeapConstant(f);
}
std::string op_name_for(const char* fnc) {
std::string string_fnc(fnc);
char initial = std::toupper(fnc[0]);
......@@ -439,6 +452,25 @@ TEST_F(JSCallReducerTest, StringFromCharCodeWithPlainPrimitive) {
EXPECT_THAT(r.replacement(), IsStringFromCharCode(IsSpeculativeToNumber(p0)));
}
// -----------------------------------------------------------------------------
// Number.isFinite
TEST_F(JSCallReducerTest, NumberIsFinite) {
Node* function = NumberFunction("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(), IsObjectIsFiniteNumber(p0));
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -2180,6 +2180,7 @@ IS_UNOP_MATCHER(NumberToBoolean)
IS_UNOP_MATCHER(NumberToInt32)
IS_UNOP_MATCHER(NumberToUint32)
IS_UNOP_MATCHER(PlainPrimitiveToNumber)
IS_UNOP_MATCHER(ObjectIsFiniteNumber)
IS_UNOP_MATCHER(ObjectIsNaN)
IS_UNOP_MATCHER(ObjectIsReceiver)
IS_UNOP_MATCHER(ObjectIsSmi)
......
......@@ -310,6 +310,8 @@ 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*> IsObjectIsFiniteNumber(const Matcher<Node*>& value_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);
......
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