Commit 5b5dcf08 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[turbofan] Support multiple maps in Array.prototype.push

Bug: v8:7127, v8:7204, v8:7205
Change-Id: I05d6bc2e20e29eaa683ad3aa94af24a4309bcdc7
Reviewed-on: https://chromium-review.googlesource.com/847484Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50341}
parent 8de3a3bc
......@@ -3388,106 +3388,104 @@ Reduction JSCallReducer::ReduceArrayPrototypePush(Node* node) {
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Try to determine the {receiver} map(s).
ZoneHandleSet<Map> receiver_maps;
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
if (receiver_maps.size() != 1) return NoChange();
DCHECK_NE(NodeProperties::kNoReceiverMaps, result);
// TODO(turbofan): Relax this to deal with multiple {receiver} maps.
Handle<Map> receiver_map = receiver_maps[0];
if (CanInlineArrayResizeOperation(receiver_map)) {
// Collect the value inputs to push.
std::vector<Node*> values(num_values);
for (int i = 0; i < num_values; ++i) {
values[i] = NodeProperties::GetValueInput(node, 2 + i);
}
if (result == NodeProperties::kNoReceiverMaps) return NoChange();
DCHECK_NE(0, receiver_maps.size());
// Install code dependencies on the {receiver} prototype maps and the
// global array protector cell.
dependencies()->AssumePropertyCell(factory()->no_elements_protector());
dependencies()->AssumePrototypeMapsStable(receiver_map);
const ElementsKind kind = receiver_maps[0]->elements_kind();
// If the {receiver_maps} information is not reliable, we need
// to check that the {receiver} still has one of these maps.
if (result == NodeProperties::kUnreliableReceiverMaps) {
if (receiver_map->is_stable()) {
dependencies()->AssumeMapStable(receiver_map);
} else {
effect = graph()->NewNode(
simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps,
p.feedback()),
receiver, effect, control);
}
}
for (Handle<Map> receiver_map : receiver_maps) {
if (!CanInlineArrayResizeOperation(receiver_map)) return NoChange();
if (receiver_map->elements_kind() != kind) return NoChange();
}
for (auto& value : values) {
if (IsSmiElementsKind(receiver_map->elements_kind())) {
value = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()),
value, effect, control);
} else if (IsDoubleElementsKind(receiver_map->elements_kind())) {
value = effect = graph()->NewNode(
simplified()->CheckNumber(p.feedback()), value, effect, control);
// Make sure we do not store signaling NaNs into double arrays.
value = graph()->NewNode(simplified()->NumberSilenceNaN(), value);
}
// Install code dependencies on the {receiver} prototype maps and the
// global array protector cell.
dependencies()->AssumePropertyCell(factory()->no_elements_protector());
for (size_t i = 0; i < receiver_maps.size(); ++i) {
dependencies()->AssumePrototypeMapsStable(receiver_maps[i]);
}
// If the {receiver_maps} information is not reliable, we need
// to check that the {receiver} still has one of these maps.
if (result == NodeProperties::kUnreliableReceiverMaps) {
effect =
graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
receiver_maps, p.feedback()),
receiver, effect, control);
}
// Collect the value inputs to push.
std::vector<Node*> values(num_values);
for (int i = 0; i < num_values; ++i) {
values[i] = NodeProperties::GetValueInput(node, 2 + i);
}
for (auto& value : values) {
if (IsSmiElementsKind(kind)) {
value = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()),
value, effect, control);
} else if (IsDoubleElementsKind(kind)) {
value = effect = graph()->NewNode(simplified()->CheckNumber(p.feedback()),
value, effect, control);
// Make sure we do not store signaling NaNs into double arrays.
value = graph()->NewNode(simplified()->NumberSilenceNaN(), value);
}
}
// Load the "length" property of the {receiver}.
Node* length = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())),
receiver, effect, control);
Node* value = length;
// Load the "length" property of the {receiver}.
Node* length = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
effect, control);
Node* value = length;
// Check if we have any {values} to push.
if (num_values > 0) {
// Compute the resulting "length" of the {receiver}.
Node* new_length = value = graph()->NewNode(
simplified()->NumberAdd(), length, jsgraph()->Constant(num_values));
// Check if we have any {values} to push.
if (num_values > 0) {
// Compute the resulting "length" of the {receiver}.
Node* new_length = value = graph()->NewNode(
simplified()->NumberAdd(), length, jsgraph()->Constant(num_values));
// Load the elements backing store of the {receiver}.
Node* elements = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
receiver, effect, control);
Node* elements_length = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
elements, effect, control);
GrowFastElementsMode mode =
IsDoubleElementsKind(receiver_map->elements_kind())
? GrowFastElementsMode::kDoubleElements
: GrowFastElementsMode::kSmiOrObjectElements;
elements = effect = graph()->NewNode(
simplified()->MaybeGrowFastElements(mode, p.feedback()), receiver,
elements,
graph()->NewNode(simplified()->NumberAdd(), length,
jsgraph()->Constant(num_values - 1)),
elements_length, effect, control);
// Update the JSArray::length field. Since this is observable,
// there must be no other check after this.
// Load the elements backing store of the {receiver}.
Node* elements = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
effect, control);
Node* elements_length = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), elements,
effect, control);
GrowFastElementsMode mode =
IsDoubleElementsKind(kind) ? GrowFastElementsMode::kDoubleElements
: GrowFastElementsMode::kSmiOrObjectElements;
elements = effect = graph()->NewNode(
simplified()->MaybeGrowFastElements(mode, p.feedback()), receiver,
elements,
graph()->NewNode(simplified()->NumberAdd(), length,
jsgraph()->Constant(num_values - 1)),
elements_length, effect, control);
// Update the JSArray::length field. Since this is observable,
// there must be no other check after this.
effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)),
receiver, new_length, effect, control);
// Append the {values} to the {elements}.
for (int i = 0; i < num_values; ++i) {
Node* value = values[i];
Node* index = graph()->NewNode(simplified()->NumberAdd(), length,
jsgraph()->Constant(i));
effect = graph()->NewNode(
simplified()->StoreField(
AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())),
receiver, new_length, effect, control);
// Append the {values} to the {elements}.
for (int i = 0; i < num_values; ++i) {
Node* value = values[i];
Node* index = graph()->NewNode(simplified()->NumberAdd(), length,
jsgraph()->Constant(i));
effect = graph()->NewNode(
simplified()->StoreElement(AccessBuilder::ForFixedArrayElement(
receiver_map->elements_kind())),
elements, index, value, effect, control);
}
simplified()->StoreElement(AccessBuilder::ForFixedArrayElement(kind)),
elements, index, value, effect, control);
}
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
return NoChange();
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
// ES6 section 22.1.3.17 Array.prototype.pop ( )
......
// Copyright 2018 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 --opt
(function singleUnreliableReceiverMap() {
function f(a, g) {
a.push(2, g());
}
f([1], () => 3);
f([1], () => 3);
%OptimizeFunctionOnNextCall(f);
f([1], () => 3);
assertOptimized(f);
})();
(function singleUnreliableReceiverMapDeopt() {
function f(a, g) {
a.push(2, g());
}
f([1], () => 3);
f([1], () => 3);
%OptimizeFunctionOnNextCall(f);
f([1], () => true);
%OptimizeFunctionOnNextCall(f);
f([1], () => true);
assertOptimized(f);
})();
(function multipleUnreliableReceiverMaps(){
function f(a, g) {
a.push(2, g());
}
let b = [1]
b.x = 3;
f([1], () => 3);
f(b, () => 3);
f([1], () => 3);
f(b, () => 3);
%OptimizeFunctionOnNextCall(f);
f([1], () => 3);
assertOptimized(f);
})();
(function multipleUnreliableReceiverMapsDeopt(){
function f(a, g) {
a.push(2, g());
}
let b = [1]
b.x = 3;
f([1], () => 3);
f(b, () => 3);
f([1], () => 3);
f(b, () => 3);
%OptimizeFunctionOnNextCall(f);
f([0.1], () => 3);
%OptimizeFunctionOnNextCall(f);
f([0.1], () => 3);
assertOptimized(f);
})();
(function multipleReliableReceiverMaps(){
function f(a) {
a.push(2);
}
let b = [1]
b.x = 3;
f([1]);
f(b);
f([1]);
f(b);
%OptimizeFunctionOnNextCall(f);
f([1]);
assertOptimized(f);
})();
(function multipleReliableReceiverMapsDeopt(){
function f(a) {
a.push(2);
}
let b = [1]
b.x = 3;
f([1]);
f(b);
f([1]);
f(b);
%OptimizeFunctionOnNextCall(f);
f([0.1]);
%OptimizeFunctionOnNextCall(f);
f([0.1]);
assertOptimized(f);
})();
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