[turbofan] Move large array allocation bailout earlier

The CanAllocateArray used to be executed during JSCreateLowering,
leading to bailouts when large arrays are passed as arguments to
an async function or a bound function. This meant that
JSCreateAsyncFunctionObject or JSCreateBoundFunction will reach
JSGenericLowering, where they are not lowered. This CL moves
the checks earlier in the pipeline during JSNativeContextSpecialization
and JSCallReducer respectively, so that those operators are not
created at all in such cases and we bail out to the runtime instead.

Bug: v8:11564
Change-Id: I232ce7d9378730ae0cc8690e52fde840a484e069
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2807609
Commit-Queue: Maya Lekova <mslekova@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73928}
This commit is contained in:
Maya Lekova 2021-04-12 20:15:46 +02:00 committed by Commit Bot
parent 524f41db67
commit 930f26549f
4 changed files with 50 additions and 6 deletions

View File

@ -15,6 +15,7 @@
#include "src/codegen/tnode.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/access-info.h"
#include "src/compiler/allocation-builder-inl.h"
#include "src/compiler/allocation-builder.h"
#include "src/compiler/compilation-dependencies.h"
#include "src/compiler/feedback-source.h"
@ -2677,6 +2678,15 @@ Reduction JSCallReducer::ReduceFunctionPrototypeBind(Node* node) {
static constexpr int kBoundThis = 1;
static constexpr int kReceiverContextEffectAndControl = 4;
int const arity = n.ArgumentCount();
if (arity > 0) {
MapRef fixed_array_map(broker(), factory()->fixed_array_map());
AllocationBuilder ab(jsgraph(), effect, control);
if (!ab.CanAllocateArray(arity, fixed_array_map)) {
return NoChange();
}
}
int const arity_with_bound_this = std::max(arity, kBoundThis);
int const input_count =
arity_with_bound_this + kReceiverContextEffectAndControl;

View File

@ -777,9 +777,7 @@ Reduction JSCreateLowering::ReduceJSCreateAsyncFunctionObject(Node* node) {
// Create the register file.
MapRef fixed_array_map(broker(), factory()->fixed_array_map());
AllocationBuilder ab(jsgraph(), effect, control);
if (!ab.CanAllocateArray(register_count, fixed_array_map)) {
return NoChange();
}
CHECK(ab.CanAllocateArray(register_count, fixed_array_map));
ab.AllocateArray(register_count, fixed_array_map);
for (int i = 0; i < register_count; ++i) {
ab.Store(AccessBuilder::ForFixedArraySlot(i),
@ -892,9 +890,7 @@ Reduction JSCreateLowering::ReduceJSCreateBoundFunction(Node* node) {
if (arity > 0) {
MapRef fixed_array_map(broker(), factory()->fixed_array_map());
AllocationBuilder ab(jsgraph(), effect, control);
if (!ab.CanAllocateArray(arity, fixed_array_map)) {
return NoChange();
}
CHECK(ab.CanAllocateArray(arity, fixed_array_map));
ab.AllocateArray(arity, fixed_array_map);
for (int i = 0; i < arity; ++i) {
ab.Store(AccessBuilder::ForFixedArraySlot(i),

View File

@ -10,6 +10,7 @@
#include "src/codegen/string-constants.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/access-info.h"
#include "src/compiler/allocation-builder-inl.h"
#include "src/compiler/allocation-builder.h"
#include "src/compiler/compilation-dependencies.h"
#include "src/compiler/js-graph.h"
@ -234,6 +235,11 @@ Reduction JSNativeContextSpecialization::ReduceJSAsyncFunctionEnter(
DCHECK(shared.is_compiled());
int register_count = shared.internal_formal_parameter_count() +
shared.GetBytecodeArray().register_count();
MapRef fixed_array_map(broker(), factory()->fixed_array_map());
AllocationBuilder ab(jsgraph(), effect, control);
if (!ab.CanAllocateArray(register_count, fixed_array_map)) {
return NoChange();
}
Node* value = effect =
graph()->NewNode(javascript()->CreateAsyncFunctionObject(register_count),
closure, receiver, promise, context, effect, control);

View File

@ -0,0 +1,32 @@
// Copyright 2021 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 --max-optimized-bytecode-size=300000
const args = new Array(35000).fill('arg');
// Regression test for ReduceJSCreateAsyncFunctionObject.
function outer_async() {
async function g(replace_me) {}
%PrepareFunctionForOptimization(g);
%OptimizeFunctionOnNextCall(g);
new Promise(g);
}
const outer_async_many_args = outer_async.toLocaleString().replace('replace_me', args);
eval(outer_async_many_args);
outer_async();
// Regression test for ReduceJSCreateBoundFunction.
function outer_bind(arg) {
function b() { return 42; };
return b.bind(null, replace_me);
}
const outer_bind_many_args = outer_bind.toLocaleString().replace('replace_me', args);
eval(outer_bind_many_args);
%PrepareFunctionForOptimization(outer_bind);
outer_bind();
%OptimizeFunctionOnNextCall(outer_bind);
outer_bind();