Commit 50637197 authored by Mike Stanton's avatar Mike Stanton Committed by Commit Bot

[TurboFan] Handle double holey arrays in several array builtins.

Array.prototype.{forEach, filter, map, every} get this support
with the help of a new opcode NumberIsFloat64Hole.

Bug: v8:1956
Change-Id: Ic6a785590cec66bae4c1462c19d6843c0aa5473b
Reviewed-on: https://chromium-review.googlesource.com/847435Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarDaniel Clifford <danno@chromium.org>
Commit-Queue: Michael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50358}
parent c38cb367
......@@ -895,6 +895,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kStringLessThanOrEqual:
result = LowerStringLessThanOrEqual(node);
break;
case IrOpcode::kNumberIsFloat64Hole:
result = LowerNumberIsFloat64Hole(node);
break;
case IrOpcode::kCheckFloat64Hole:
result = LowerCheckFloat64Hole(node, frame_state);
break;
......@@ -2172,6 +2175,13 @@ Node* EffectControlLinearizer::LowerObjectIsDetectableCallable(Node* node) {
return done.PhiAt(0);
}
Node* EffectControlLinearizer::LowerNumberIsFloat64Hole(Node* node) {
Node* value = node->InputAt(0);
Node* check = __ Word32Equal(__ Float64ExtractHighWord32(value),
__ Int32Constant(kHoleNanUpper32));
return check;
}
Node* EffectControlLinearizer::LowerObjectIsMinusZero(Node* node) {
Node* value = node->InputAt(0);
Node* zero = __ Int32Constant(0);
......
......@@ -104,6 +104,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerObjectIsString(Node* node);
Node* LowerObjectIsSymbol(Node* node);
Node* LowerObjectIsUndetectable(Node* node);
Node* LowerNumberIsFloat64Hole(Node* node);
Node* LowerArgumentsFrame(Node* node);
Node* LowerArgumentsLength(Node* node);
Node* LowerNewDoubleElements(Node* node);
......
......@@ -840,23 +840,25 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
}
if (receiver_maps.size() == 0) return NoChange();
ElementsKind kind = IsDoubleElementsKind(receiver_maps[0]->elements_kind())
? PACKED_DOUBLE_ELEMENTS
: PACKED_ELEMENTS;
// By ensuring that {kind} is object or double, we can be polymorphic
// on different elements kinds.
ElementsKind kind = receiver_maps[0]->elements_kind();
if (IsSmiElementsKind(kind)) {
kind = FastSmiToObjectElementsKind(kind);
}
for (Handle<Map> receiver_map : receiver_maps) {
ElementsKind next_kind = receiver_map->elements_kind();
if (!CanInlineArrayIteratingBuiltin(receiver_map)) {
return NoChange();
}
if (!IsFastElementsKind(next_kind) ||
(IsDoubleElementsKind(next_kind) && IsHoleyElementsKind(next_kind))) {
if (!IsFastElementsKind(next_kind)) {
return NoChange();
}
if (IsDoubleElementsKind(kind) != IsDoubleElementsKind(next_kind)) {
return NoChange();
}
if (IsHoleyElementsKind(next_kind)) {
kind = HOLEY_ELEMENTS;
kind = GetHoleyElementsKind(kind);
}
}
......@@ -867,8 +869,8 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
Node* k = jsgraph()->ZeroConstant();
Node* original_length = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayLength(PACKED_ELEMENTS)),
receiver, effect, control);
simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
effect, control);
std::vector<Node*> checkpoint_params(
{receiver, fncallback, this_arg, k, original_length});
......@@ -927,8 +929,13 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
if (IsHoleyElementsKind(kind)) {
// Holey elements kind require a hole check and skipping of the element in
// the case of a hole.
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), element,
jsgraph()->TheHoleConstant());
Node* check;
if (IsDoubleElementsKind(kind)) {
check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
} else {
check = graph()->NewNode(simplified()->ReferenceEqual(), element,
jsgraph()->TheHoleConstant());
}
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
hole_true = graph()->NewNode(common()->IfTrue(), branch);
......@@ -1479,11 +1486,6 @@ Reduction JSCallReducer::ReduceArrayMap(Handle<JSFunction> function,
const ElementsKind kind = receiver_maps[0]->elements_kind();
// TODO(pwong): Handle holey double elements kinds.
if (IsDoubleElementsKind(kind) && IsHoleyElementsKind(kind)) {
return NoChange();
}
for (Handle<Map> receiver_map : receiver_maps) {
if (!CanInlineArrayIteratingBuiltin(receiver_map)) return NoChange();
// We can handle different maps, as long as their elements kind are the
......@@ -1575,8 +1577,13 @@ Reduction JSCallReducer::ReduceArrayMap(Handle<JSFunction> function,
if (IsHoleyElementsKind(kind)) {
// Holey elements kind require a hole check and skipping of the element in
// the case of a hole.
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), element,
jsgraph()->TheHoleConstant());
Node* check;
if (IsDoubleElementsKind(kind)) {
check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
} else {
check = graph()->NewNode(simplified()->ReferenceEqual(), element,
jsgraph()->TheHoleConstant());
}
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
hole_true = graph()->NewNode(common()->IfTrue(), branch);
......@@ -1682,11 +1689,6 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
// The output array is packed (filter doesn't visit holes).
const ElementsKind packed_kind = GetPackedElementsKind(kind);
// TODO(pwong): Handle holey double elements kinds.
if (IsDoubleElementsKind(kind) && IsHoleyElementsKind(kind)) {
return NoChange();
}
for (Handle<Map> receiver_map : receiver_maps) {
if (!CanInlineArrayIteratingBuiltin(receiver_map)) {
return NoChange();
......@@ -1802,8 +1804,13 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
if (IsHoleyElementsKind(kind)) {
// Holey elements kind require a hole check and skipping of the element in
// the case of a hole.
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), element,
jsgraph()->TheHoleConstant());
Node* check;
if (IsDoubleElementsKind(kind)) {
check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
} else {
check = graph()->NewNode(simplified()->ReferenceEqual(), element,
jsgraph()->TheHoleConstant());
}
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
hole_true = graph()->NewNode(common()->IfTrue(), branch);
......@@ -2268,11 +2275,6 @@ Reduction JSCallReducer::ReduceArrayEvery(Handle<JSFunction> function,
const ElementsKind kind = receiver_maps[0]->elements_kind();
// TODO(pwong): Handle holey double elements kinds.
if (IsDoubleElementsKind(kind) && IsHoleyElementsKind(kind)) {
return NoChange();
}
for (Handle<Map> receiver_map : receiver_maps) {
if (!CanInlineArrayIteratingBuiltin(receiver_map)) return NoChange();
// We can handle different maps, as long as their elements kind are the
......@@ -2359,8 +2361,13 @@ Reduction JSCallReducer::ReduceArrayEvery(Handle<JSFunction> function,
if (IsHoleyElementsKind(kind)) {
// Holey elements kind require a hole check and skipping of the element in
// the case of a hole.
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), element,
jsgraph()->TheHoleConstant());
Node* check;
if (IsDoubleElementsKind(kind)) {
check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
} else {
check = graph()->NewNode(simplified()->ReferenceEqual(), element,
jsgraph()->TheHoleConstant());
}
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
hole_true = graph()->NewNode(common()->IfTrue(), branch);
......
......@@ -372,6 +372,7 @@
V(TransitionAndStoreNumberElement) \
V(TransitionAndStoreNonNumberElement) \
V(ToBoolean) \
V(NumberIsFloat64Hole) \
V(ObjectIsArrayBufferView) \
V(ObjectIsBigInt) \
V(ObjectIsCallable) \
......
......@@ -2551,6 +2551,11 @@ class RepresentationSelector {
}
return;
}
case IrOpcode::kNumberIsFloat64Hole: {
VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kBit);
return;
}
case IrOpcode::kTransitionAndStoreElement: {
Type* value_type = TypeOf(node->InputAt(2));
......
......@@ -696,6 +696,7 @@ bool operator==(CheckMinusZeroParameters const& lhs,
V(ObjectIsString, Operator::kNoProperties, 1, 0) \
V(ObjectIsSymbol, Operator::kNoProperties, 1, 0) \
V(ObjectIsUndetectable, Operator::kNoProperties, 1, 0) \
V(NumberIsFloat64Hole, Operator::kNoProperties, 1, 0) \
V(ConvertTaggedHoleToUndefined, Operator::kNoProperties, 1, 0) \
V(SameValue, Operator::kCommutative, 2, 0) \
V(ReferenceEqual, Operator::kCommutative, 2, 0) \
......
......@@ -597,6 +597,8 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* ObjectIsSymbol();
const Operator* ObjectIsUndetectable();
const Operator* NumberIsFloat64Hole();
const Operator* ArgumentsFrame();
const Operator* ArgumentsLength(int formal_parameter_count,
bool is_rest_length);
......
......@@ -2133,6 +2133,10 @@ Type* Typer::Visitor::TypeObjectIsMinusZero(Node* node) {
return TypeUnaryOp(node, ObjectIsMinusZero);
}
Type* Typer::Visitor::TypeNumberIsFloat64Hole(Node* node) {
return Type::Boolean();
}
Type* Typer::Visitor::TypeObjectIsNaN(Node* node) {
return TypeUnaryOp(node, ObjectIsNaN);
}
......
......@@ -1098,6 +1098,10 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::Boolean());
break;
case IrOpcode::kNumberIsFloat64Hole:
CheckValueInputIs(node, 0, Type::NumberOrHole());
CheckTypeIs(node, Type::Boolean());
break;
case IrOpcode::kFindOrderedHashMapEntry:
CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::SignedSmall());
......
......@@ -343,3 +343,34 @@ var c = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25];
assertTrue(re.exec(e.stack) !== null);
}
})();
// Verify holes are skipped.
(() => {
const a = [1, 2, , 3, 4];
function withHoles() {
const callback_values = [];
a.forEach(v => {
callback_values.push(v);
});
return callback_values;
}
withHoles();
withHoles();
%OptimizeFunctionOnNextCall(withHoles);
assertArrayEquals([1, 2, 3, 4], withHoles());
})();
(() => {
const a = [1.5, 2.5, , 3.5, 4.5];
function withHoles() {
const callback_values = [];
a.forEach(v => {
callback_values.push(v);
});
return callback_values;
}
withHoles();
withHoles();
%OptimizeFunctionOnNextCall(withHoles);
assertArrayEquals([1.5, 2.5, 3.5, 4.5], withHoles());
})();
......@@ -503,7 +503,6 @@ var c = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25];
assertArrayEquals([1.5, 2.5, 3.5, 4.5], callback_values);
})();
// Messing with the Array species constructor causes deoptimization.
(function() {
var result = 0;
......
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