[compiler] Don't try to inline allocate large arguments arrays

... otherwise we'd abort at runtime.

Bug: chromium:1178076
Change-Id: Ic7b4a3b27379ec0d42419e2695ab487904eabd72
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2695395
Reviewed-by: Michael Stanton <mvstanton@chromium.org>
Commit-Queue: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72744}
This commit is contained in:
Georg Neis 2021-02-15 14:08:50 +01:00 committed by Commit Bot
parent 053d1e0dce
commit eb2906ae73
4 changed files with 134 additions and 76 deletions

View File

@ -27,11 +27,21 @@ void AllocationBuilder::AllocateContext(int variadic_part_length, MapRef map) {
jsgraph()->Constant(variadic_part_length));
}
// static
bool AllocationBuilder::CanAllocateArray(int length, MapRef map,
AllocationType allocation) {
DCHECK(map.instance_type() == FIXED_ARRAY_TYPE ||
map.instance_type() == FIXED_DOUBLE_ARRAY_TYPE);
int const size = (map.instance_type() == FIXED_ARRAY_TYPE)
? FixedArray::SizeFor(length)
: FixedDoubleArray::SizeFor(length);
return size <= Heap::MaxRegularHeapObjectSize(allocation);
}
// Compound allocation of a FixedArray.
void AllocationBuilder::AllocateArray(int length, MapRef map,
AllocationType allocation) {
DCHECK(map.instance_type() == FIXED_ARRAY_TYPE ||
map.instance_type() == FIXED_DOUBLE_ARRAY_TYPE);
DCHECK(CanAllocateArray(length, map, allocation));
int size = (map.instance_type() == FIXED_ARRAY_TYPE)
? FixedArray::SizeFor(length)
: FixedDoubleArray::SizeFor(length);
@ -40,8 +50,16 @@ void AllocationBuilder::AllocateArray(int length, MapRef map,
Store(AccessBuilder::ForFixedArrayLength(), jsgraph()->Constant(length));
}
// static
bool AllocationBuilder::CanAllocateSloppyArgumentElements(
int length, MapRef map, AllocationType allocation) {
int const size = SloppyArgumentsElements::SizeFor(length);
return size <= Heap::MaxRegularHeapObjectSize(allocation);
}
void AllocationBuilder::AllocateSloppyArgumentElements(
int length, MapRef map, AllocationType allocation) {
DCHECK(CanAllocateSloppyArgumentElements(length, map, allocation));
int size = SloppyArgumentsElements::SizeFor(length);
Allocate(size, allocation, Type::OtherInternal());
Store(AccessBuilder::ForMap(), map);

View File

@ -52,10 +52,16 @@ class AllocationBuilder final {
inline void AllocateContext(int variadic_part_length, MapRef map);
// Compound allocation of a FixedArray.
inline static bool CanAllocateArray(
int length, MapRef map,
AllocationType allocation = AllocationType::kYoung);
inline void AllocateArray(int length, MapRef map,
AllocationType allocation = AllocationType::kYoung);
// Compound allocation of a SloppyArgumentsElements
static inline bool CanAllocateSloppyArgumentElements(
int length, MapRef map,
AllocationType allocation = AllocationType::kYoung);
inline void AllocateSloppyArgumentElements(
int length, MapRef map,
AllocationType allocation = AllocationType::kYoung);

View File

@ -168,9 +168,11 @@ Reduction JSCreateLowering::ReduceJSCreateArguments(Node* node) {
graph()->NewNode(simplified()->ArgumentsLength());
// Allocate the elements backing store.
bool has_aliased_arguments = false;
Node* const elements = effect =
AllocateAliasedArguments(effect, control, context, arguments_length,
shared, &has_aliased_arguments);
Node* const elements = effect = TryAllocateAliasedArguments(
effect, control, context, arguments_length, shared,
&has_aliased_arguments);
if (elements == nullptr) return NoChange();
// Load the arguments object map.
Node* const arguments_map = jsgraph()->Constant(
has_aliased_arguments
@ -269,15 +271,11 @@ Reduction JSCreateLowering::ReduceJSCreateArguments(Node* node) {
}
FrameStateInfo args_state_info = args_state.frame_state_info();
int length = args_state_info.parameter_count() - 1; // Minus receiver.
// Check that the array allocated for arguments is not "large".
{
const int alloc_size = FixedArray::SizeFor(length);
if (alloc_size > kMaxRegularHeapObjectSize) return NoChange();
}
// Prepare element backing store to be used by arguments object.
bool has_aliased_arguments = false;
Node* const elements = AllocateAliasedArguments(
Node* const elements = TryAllocateAliasedArguments(
effect, control, args_state, context, shared, &has_aliased_arguments);
if (elements == nullptr) return NoChange();
effect = elements->op()->EffectOutputCount() > 0 ? elements : effect;
// Load the arguments object map.
Node* const arguments_map = jsgraph()->Constant(
@ -313,13 +311,9 @@ Reduction JSCreateLowering::ReduceJSCreateArguments(Node* node) {
}
FrameStateInfo args_state_info = args_state.frame_state_info();
int length = args_state_info.parameter_count() - 1; // Minus receiver.
// Check that the array allocated for arguments is not "large".
{
const int alloc_size = FixedArray::SizeFor(length);
if (alloc_size > kMaxRegularHeapObjectSize) return NoChange();
}
// Prepare element backing store to be used by arguments object.
Node* const elements = AllocateArguments(effect, control, args_state);
Node* const elements = TryAllocateArguments(effect, control, args_state);
if (elements == nullptr) return NoChange();
effect = elements->op()->EffectOutputCount() > 0 ? elements : effect;
// Load the arguments object map.
Node* const arguments_map =
@ -355,7 +349,8 @@ Reduction JSCreateLowering::ReduceJSCreateArguments(Node* node) {
FrameStateInfo args_state_info = args_state.frame_state_info();
// Prepare element backing store to be used by the rest array.
Node* const elements =
AllocateRestArguments(effect, control, args_state, start_index);
TryAllocateRestArguments(effect, control, args_state, start_index);
if (elements == nullptr) return NoChange();
effect = elements->op()->EffectOutputCount() > 0 ? elements : effect;
// Load the JSArray object map.
Node* const jsarray_map =
@ -407,11 +402,15 @@ Reduction JSCreateLowering::ReduceJSCreateGeneratorObject(Node* node) {
SharedFunctionInfoRef shared = js_function.shared();
DCHECK(shared.HasBytecodeArray());
int parameter_count_no_receiver = shared.internal_formal_parameter_count();
int size = parameter_count_no_receiver +
shared.GetBytecodeArray().register_count();
int length = parameter_count_no_receiver +
shared.GetBytecodeArray().register_count();
MapRef fixed_array_map(broker(), factory()->fixed_array_map());
AllocationBuilder ab(jsgraph(), effect, control);
ab.AllocateArray(size, MapRef(broker(), factory()->fixed_array_map()));
for (int i = 0; i < size; ++i) {
if (!ab.CanAllocateArray(length, fixed_array_map)) {
return NoChange();
}
ab.AllocateArray(length, fixed_array_map);
for (int i = 0; i < length; ++i) {
ab.Store(AccessBuilder::ForFixedArraySlot(i),
jsgraph()->UndefinedConstant());
}
@ -776,9 +775,12 @@ Reduction JSCreateLowering::ReduceJSCreateAsyncFunctionObject(Node* node) {
Node* control = NodeProperties::GetControlInput(node);
// Create the register file.
MapRef fixed_array_map(broker(), factory()->fixed_array_map());
AllocationBuilder ab(jsgraph(), effect, control);
ab.AllocateArray(register_count,
MapRef(broker(), factory()->fixed_array_map()));
if (!ab.CanAllocateArray(register_count, fixed_array_map)) {
return NoChange();
}
ab.AllocateArray(register_count, fixed_array_map);
for (int i = 0; i < register_count; ++i) {
ab.Store(AccessBuilder::ForFixedArraySlot(i),
jsgraph()->UndefinedConstant());
@ -888,13 +890,17 @@ Reduction JSCreateLowering::ReduceJSCreateBoundFunction(Node* node) {
// Create the [[BoundArguments]] for the result.
Node* bound_arguments = jsgraph()->EmptyFixedArrayConstant();
if (arity > 0) {
AllocationBuilder a(jsgraph(), effect, control);
a.AllocateArray(arity, MapRef(broker(), factory()->fixed_array_map()));
for (int i = 0; i < arity; ++i) {
a.Store(AccessBuilder::ForFixedArraySlot(i),
NodeProperties::GetValueInput(node, 2 + i));
MapRef fixed_array_map(broker(), factory()->fixed_array_map());
AllocationBuilder ab(jsgraph(), effect, control);
if (!ab.CanAllocateArray(arity, fixed_array_map)) {
return NoChange();
}
bound_arguments = effect = a.Finish();
ab.AllocateArray(arity, fixed_array_map);
for (int i = 0; i < arity; ++i) {
ab.Store(AccessBuilder::ForFixedArraySlot(i),
NodeProperties::GetValueInput(node, 2 + i));
}
bound_arguments = effect = ab.Finish();
}
// Create the JSBoundFunction result.
@ -1412,8 +1418,8 @@ Reduction JSCreateLowering::ReduceJSCreateObject(Node* node) {
// Helper that allocates a FixedArray holding argument values recorded in the
// given {frame_state}. Serves as backing store for JSCreateArguments nodes.
Node* JSCreateLowering::AllocateArguments(Node* effect, Node* control,
FrameState frame_state) {
Node* JSCreateLowering::TryAllocateArguments(Node* effect, Node* control,
FrameState frame_state) {
FrameStateInfo state_info = frame_state.frame_state_info();
int argument_count = state_info.parameter_count() - 1; // Minus receiver.
if (argument_count == 0) return jsgraph()->EmptyFixedArrayConstant();
@ -1424,22 +1430,25 @@ Node* JSCreateLowering::AllocateArguments(Node* effect, Node* control,
auto parameters_it = parameters_access.begin_without_receiver();
// Actually allocate the backing store.
AllocationBuilder a(jsgraph(), effect, control);
a.AllocateArray(argument_count,
MapRef(broker(), factory()->fixed_array_map()));
MapRef fixed_array_map(broker(), factory()->fixed_array_map());
AllocationBuilder ab(jsgraph(), effect, control);
if (!ab.CanAllocateArray(argument_count, fixed_array_map)) {
return nullptr;
}
ab.AllocateArray(argument_count, fixed_array_map);
for (int i = 0; i < argument_count; ++i, ++parameters_it) {
DCHECK_NOT_NULL(parameters_it.node());
a.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i),
parameters_it.node());
ab.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i),
parameters_it.node());
}
return a.Finish();
return ab.Finish();
}
// Helper that allocates a FixedArray holding argument values recorded in the
// given {frame_state}. Serves as backing store for JSCreateArguments nodes.
Node* JSCreateLowering::AllocateRestArguments(Node* effect, Node* control,
FrameState frame_state,
int start_index) {
Node* JSCreateLowering::TryAllocateRestArguments(Node* effect, Node* control,
FrameState frame_state,
int start_index) {
FrameStateInfo state_info = frame_state.frame_state_info();
int argument_count = state_info.parameter_count() - 1; // Minus receiver.
int num_elements = std::max(0, argument_count - start_index);
@ -1452,20 +1461,24 @@ Node* JSCreateLowering::AllocateRestArguments(Node* effect, Node* control,
parameters_access.begin_without_receiver_and_skip(start_index);
// Actually allocate the backing store.
AllocationBuilder a(jsgraph(), effect, control);
a.AllocateArray(num_elements, MapRef(broker(), factory()->fixed_array_map()));
MapRef fixed_array_map(broker(), factory()->fixed_array_map());
AllocationBuilder ab(jsgraph(), effect, control);
if (!ab.CanAllocateArray(num_elements, fixed_array_map)) {
return nullptr;
}
ab.AllocateArray(num_elements, fixed_array_map);
for (int i = 0; i < num_elements; ++i, ++parameters_it) {
DCHECK_NOT_NULL(parameters_it.node());
a.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i),
parameters_it.node());
ab.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i),
parameters_it.node());
}
return a.Finish();
return ab.Finish();
}
// Helper that allocates a FixedArray serving as a parameter map for values
// recorded in the given {frame_state}. Some elements map to slots within the
// given {context}. Serves as backing store for JSCreateArguments nodes.
Node* JSCreateLowering::AllocateAliasedArguments(
Node* JSCreateLowering::TryAllocateAliasedArguments(
Node* effect, Node* control, FrameState frame_state, Node* context,
const SharedFunctionInfoRef& shared, bool* has_aliased_arguments) {
FrameStateInfo state_info = frame_state.frame_state_info();
@ -1476,13 +1489,25 @@ Node* JSCreateLowering::AllocateAliasedArguments(
// any way, we can just return an unmapped backing store instead.
int parameter_count = shared.internal_formal_parameter_count();
if (parameter_count == 0) {
return AllocateArguments(effect, control, frame_state);
return TryAllocateArguments(effect, control, frame_state);
}
// Calculate number of argument values being aliased/mapped.
int mapped_count = std::min(argument_count, parameter_count);
*has_aliased_arguments = true;
MapRef sloppy_arguments_elements_map(
broker(), factory()->sloppy_arguments_elements_map());
if (AllocationBuilder::CanAllocateSloppyArgumentElements(
mapped_count, sloppy_arguments_elements_map)) {
return nullptr;
}
MapRef fixed_array_map(broker(), factory()->fixed_array_map());
if (!AllocationBuilder::CanAllocateArray(argument_count, fixed_array_map)) {
return nullptr;
}
// Prepare an iterator over argument values recorded in the frame state.
Node* const parameters = frame_state.parameters();
StateValuesAccess parameters_access(parameters);
@ -1492,25 +1517,23 @@ Node* JSCreateLowering::AllocateAliasedArguments(
// The unmapped argument values recorded in the frame state are stored yet
// another indirection away and then linked into the parameter map below,
// whereas mapped argument values are replaced with a hole instead.
AllocationBuilder aa(jsgraph(), effect, control);
aa.AllocateArray(argument_count,
AllocationBuilder ab(jsgraph(), effect, control);
ab.AllocateArray(argument_count,
MapRef(broker(), factory()->fixed_array_map()));
for (int i = 0; i < mapped_count; ++i) {
aa.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i),
ab.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i),
jsgraph()->TheHoleConstant());
}
for (int i = mapped_count; i < argument_count; ++i, ++parameters_it) {
DCHECK_NOT_NULL(parameters_it.node());
aa.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i),
ab.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i),
parameters_it.node());
}
Node* arguments = aa.Finish();
Node* arguments = ab.Finish();
// Actually allocate the backing store.
AllocationBuilder a(jsgraph(), arguments, control);
a.AllocateSloppyArgumentElements(
mapped_count,
MapRef(broker(), factory()->sloppy_arguments_elements_map()));
a.AllocateSloppyArgumentElements(mapped_count, sloppy_arguments_elements_map);
a.Store(AccessBuilder::ForSloppyArgumentsElementsContext(), context);
a.Store(AccessBuilder::ForSloppyArgumentsElementsArguments(), arguments);
for (int i = 0; i < mapped_count; ++i) {
@ -1525,7 +1548,7 @@ Node* JSCreateLowering::AllocateAliasedArguments(
// unknown at compile-time, the true {arguments_length} and {arguments_frame}
// values can only be determined dynamically at run-time and are provided.
// Serves as backing store for JSCreateArguments nodes.
Node* JSCreateLowering::AllocateAliasedArguments(
Node* JSCreateLowering::TryAllocateAliasedArguments(
Node* effect, Node* control, Node* context, Node* arguments_length,
const SharedFunctionInfoRef& shared, bool* has_aliased_arguments) {
// If there is no aliasing, the arguments object elements are not
@ -1538,11 +1561,18 @@ Node* JSCreateLowering::AllocateAliasedArguments(
arguments_length, effect);
}
int mapped_count = parameter_count;
MapRef sloppy_arguments_elements_map(
broker(), factory()->sloppy_arguments_elements_map());
if (AllocationBuilder::CanAllocateSloppyArgumentElements(
mapped_count, sloppy_arguments_elements_map)) {
return nullptr;
}
// From here on we are going to allocate a mapped (aka. aliased) elements
// backing store. We do not statically know how many arguments exist, but
// dynamically selecting the hole for some of the "mapped" elements allows
// using a static shape for the parameter map.
int mapped_count = parameter_count;
*has_aliased_arguments = true;
// The unmapped argument values are stored yet another indirection away and
@ -1555,9 +1585,7 @@ Node* JSCreateLowering::AllocateAliasedArguments(
// Actually allocate the backing store.
AllocationBuilder a(jsgraph(), effect, control);
a.AllocateSloppyArgumentElements(
mapped_count,
MapRef(broker(), factory()->sloppy_arguments_elements_map()));
a.AllocateSloppyArgumentElements(mapped_count, sloppy_arguments_elements_map);
a.Store(AccessBuilder::ForSloppyArgumentsElementsContext(), context);
a.Store(AccessBuilder::ForSloppyArgumentsElementsArguments(), arguments);
for (int i = 0; i < mapped_count; ++i) {
@ -1758,16 +1786,18 @@ Node* JSCreateLowering::AllocateFastLiteralElements(Node* effect, Node* control,
}
// Allocate the backing store array and store the elements.
AllocationBuilder builder(jsgraph(), effect, control);
builder.AllocateArray(elements_length, elements_map, allocation);
MapRef fixed_array_map(broker(), factory()->fixed_array_map());
AllocationBuilder ab(jsgraph(), effect, control);
CHECK(ab.CanAllocateArray(elements_length, elements_map, allocation));
ab.AllocateArray(elements_length, elements_map, allocation);
ElementAccess const access =
(elements_map.instance_type() == FIXED_DOUBLE_ARRAY_TYPE)
? AccessBuilder::ForFixedDoubleArrayElement()
: AccessBuilder::ForFixedArrayElement();
for (int i = 0; i < elements_length; ++i) {
builder.Store(access, jsgraph()->Constant(i), elements_values[i]);
ab.Store(access, jsgraph()->Constant(i), elements_values[i]);
}
return builder.Finish();
return ab.Finish();
}
Node* JSCreateLowering::AllocateLiteralRegExp(

View File

@ -83,17 +83,21 @@ class V8_EXPORT_PRIVATE JSCreateLowering final
const SlackTrackingPrediction& slack_tracking_prediction);
Reduction ReduceJSCreateObject(Node* node);
Node* AllocateArguments(Node* effect, Node* control, FrameState frame_state);
Node* AllocateRestArguments(Node* effect, Node* control,
FrameState frame_state, int start_index);
Node* AllocateAliasedArguments(Node* effect, Node* control,
FrameState frame_state, Node* context,
const SharedFunctionInfoRef& shared,
bool* has_aliased_arguments);
Node* AllocateAliasedArguments(Node* effect, Node* control, Node* context,
Node* arguments_length,
const SharedFunctionInfoRef& shared,
bool* has_aliased_arguments);
// The following functions all return nullptr iff there are too many arguments
// for inline allocation.
Node* TryAllocateArguments(Node* effect, Node* control,
FrameState frame_state);
Node* TryAllocateRestArguments(Node* effect, Node* control,
FrameState frame_state, int start_index);
Node* TryAllocateAliasedArguments(Node* effect, Node* control,
FrameState frame_state, Node* context,
const SharedFunctionInfoRef& shared,
bool* has_aliased_arguments);
Node* TryAllocateAliasedArguments(Node* effect, Node* control, Node* context,
Node* arguments_length,
const SharedFunctionInfoRef& shared,
bool* has_aliased_arguments);
Node* AllocateElements(Node* effect, Node* control,
ElementsKind elements_kind, int capacity,
AllocationType allocation);