blob: 8d79036e7598c8eb7a5d740d5b4716b0134af19a [file] [log] [blame]
// Copyright 2016 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.
#include "src/compiler/js-create-lowering.h"
#include "src/codegen/code-factory.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/allocation-builder-inl.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/compilation-dependencies.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/node.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/simplified-operator.h"
#include "src/compiler/state-values-utils.h"
#include "src/execution/protectors.h"
#include "src/objects/arguments.h"
#include "src/objects/hash-table-inl.h"
#include "src/objects/heap-number.h"
#include "src/objects/js-collection-iterator.h"
#include "src/objects/js-generator.h"
#include "src/objects/js-promise.h"
#include "src/objects/js-regexp-inl.h"
#include "src/objects/objects-inl.h"
#include "src/objects/template-objects.h"
#include "torque-generated/exported-class-definitions-tq.h"
namespace v8 {
namespace internal {
namespace compiler {
namespace {
// Retrieves the frame state holding actual argument values.
Node* GetArgumentsFrameState(Node* frame_state) {
Node* const outer_state = NodeProperties::GetFrameStateInput(frame_state);
FrameStateInfo outer_state_info = FrameStateInfoOf(outer_state->op());
return outer_state_info.type() == FrameStateType::kArgumentsAdaptor
? outer_state
: frame_state;
}
// When initializing arrays, we'll unfold the loop if the number of
// elements is known to be of this type.
const int kElementLoopUnrollLimit = 16;
// Limits up to which context allocations are inlined.
const int kFunctionContextAllocationLimit = 16;
const int kBlockContextAllocationLimit = 16;
} // namespace
Reduction JSCreateLowering::Reduce(Node* node) {
DisallowHeapAccess disallow_heap_access;
switch (node->opcode()) {
case IrOpcode::kJSCreate:
return ReduceJSCreate(node);
case IrOpcode::kJSCreateArguments:
return ReduceJSCreateArguments(node);
case IrOpcode::kJSCreateArray:
return ReduceJSCreateArray(node);
case IrOpcode::kJSCreateArrayIterator:
return ReduceJSCreateArrayIterator(node);
case IrOpcode::kJSCreateAsyncFunctionObject:
return ReduceJSCreateAsyncFunctionObject(node);
case IrOpcode::kJSCreateBoundFunction:
return ReduceJSCreateBoundFunction(node);
case IrOpcode::kJSCreateClosure:
return ReduceJSCreateClosure(node);
case IrOpcode::kJSCreateCollectionIterator:
return ReduceJSCreateCollectionIterator(node);
case IrOpcode::kJSCreateIterResultObject:
return ReduceJSCreateIterResultObject(node);
case IrOpcode::kJSCreateStringIterator:
return ReduceJSCreateStringIterator(node);
case IrOpcode::kJSCreateKeyValueArray:
return ReduceJSCreateKeyValueArray(node);
case IrOpcode::kJSCreatePromise:
return ReduceJSCreatePromise(node);
case IrOpcode::kJSCreateLiteralArray:
case IrOpcode::kJSCreateLiteralObject:
return ReduceJSCreateLiteralArrayOrObject(node);
case IrOpcode::kJSCreateLiteralRegExp:
return ReduceJSCreateLiteralRegExp(node);
case IrOpcode::kJSGetTemplateObject:
return ReduceJSGetTemplateObject(node);
case IrOpcode::kJSCreateEmptyLiteralArray:
return ReduceJSCreateEmptyLiteralArray(node);
case IrOpcode::kJSCreateEmptyLiteralObject:
return ReduceJSCreateEmptyLiteralObject(node);
case IrOpcode::kJSCreateFunctionContext:
return ReduceJSCreateFunctionContext(node);
case IrOpcode::kJSCreateWithContext:
return ReduceJSCreateWithContext(node);
case IrOpcode::kJSCreateCatchContext:
return ReduceJSCreateCatchContext(node);
case IrOpcode::kJSCreateBlockContext:
return ReduceJSCreateBlockContext(node);
case IrOpcode::kJSCreateGeneratorObject:
return ReduceJSCreateGeneratorObject(node);
case IrOpcode::kJSCreateObject:
return ReduceJSCreateObject(node);
default:
break;
}
return NoChange();
}
Reduction JSCreateLowering::ReduceJSCreate(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreate, node->opcode());
Node* const new_target = NodeProperties::GetValueInput(node, 1);
Node* const effect = NodeProperties::GetEffectInput(node);
Node* const control = NodeProperties::GetControlInput(node);
base::Optional<MapRef> initial_map =
NodeProperties::GetJSCreateMap(broker(), node);
if (!initial_map.has_value()) return NoChange();
JSFunctionRef original_constructor =
HeapObjectMatcher(new_target).Ref(broker()).AsJSFunction();
SlackTrackingPrediction slack_tracking_prediction =
dependencies()->DependOnInitialMapInstanceSizePrediction(
original_constructor);
// Emit code to allocate the JSObject instance for the
// {original_constructor}.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(slack_tracking_prediction.instance_size());
a.Store(AccessBuilder::ForMap(), *initial_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
for (int i = 0; i < slack_tracking_prediction.inobject_property_count();
++i) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(*initial_map, i),
jsgraph()->UndefinedConstant());
}
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateArguments(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateArguments, node->opcode());
CreateArgumentsType type = CreateArgumentsTypeOf(node->op());
Node* const frame_state = NodeProperties::GetFrameStateInput(node);
Node* const outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
Node* const control = graph()->start();
FrameStateInfo state_info = FrameStateInfoOf(frame_state->op());
SharedFunctionInfoRef shared(broker(),
state_info.shared_info().ToHandleChecked());
// Use the ArgumentsAccessStub for materializing both mapped and unmapped
// arguments object, but only for non-inlined (i.e. outermost) frames.
if (outer_state->opcode() != IrOpcode::kFrameState) {
switch (type) {
case CreateArgumentsType::kMappedArguments: {
// TODO(turbofan): Duplicate parameters are not handled yet.
if (shared.has_duplicate_parameters()) return NoChange();
Node* const callee = NodeProperties::GetValueInput(node, 0);
Node* const context = NodeProperties::GetContextInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* const arguments_frame =
graph()->NewNode(simplified()->ArgumentsFrame());
Node* const arguments_length =
graph()->NewNode(simplified()->ArgumentsLength(
shared.internal_formal_parameter_count()),
arguments_frame);
// Allocate the elements backing store.
bool has_aliased_arguments = false;
Node* const elements = effect = AllocateAliasedArguments(
effect, control, context, arguments_frame, arguments_length, shared,
&has_aliased_arguments);
// Load the arguments object map.
Node* const arguments_map = jsgraph()->Constant(
has_aliased_arguments
? native_context().fast_aliased_arguments_map()
: native_context().sloppy_arguments_map());
// Actually allocate and initialize the arguments object.
AllocationBuilder a(jsgraph(), effect, control);
STATIC_ASSERT(JSSloppyArgumentsObject::kSize == 5 * kTaggedSize);
a.Allocate(JSSloppyArgumentsObject::kSize);
a.Store(AccessBuilder::ForMap(), arguments_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(), elements);
a.Store(AccessBuilder::ForArgumentsLength(), arguments_length);
a.Store(AccessBuilder::ForArgumentsCallee(), callee);
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
case CreateArgumentsType::kUnmappedArguments: {
Node* effect = NodeProperties::GetEffectInput(node);
Node* const arguments_frame =
graph()->NewNode(simplified()->ArgumentsFrame());
Node* const arguments_length =
graph()->NewNode(simplified()->ArgumentsLength(
shared.internal_formal_parameter_count()),
arguments_frame);
// Allocate the elements backing store.
Node* const elements = effect =
graph()->NewNode(simplified()->NewArgumentsElements(
CreateArgumentsType::kUnmappedArguments,
shared.internal_formal_parameter_count()),
arguments_frame, arguments_length, effect);
// Load the arguments object map.
Node* const arguments_map =
jsgraph()->Constant(native_context().strict_arguments_map());
// Actually allocate and initialize the arguments object.
AllocationBuilder a(jsgraph(), effect, control);
STATIC_ASSERT(JSStrictArgumentsObject::kSize == 4 * kTaggedSize);
a.Allocate(JSStrictArgumentsObject::kSize);
a.Store(AccessBuilder::ForMap(), arguments_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(), elements);
a.Store(AccessBuilder::ForArgumentsLength(), arguments_length);
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
case CreateArgumentsType::kRestParameter: {
Node* effect = NodeProperties::GetEffectInput(node);
Node* const arguments_frame =
graph()->NewNode(simplified()->ArgumentsFrame());
Node* const arguments_length =
graph()->NewNode(simplified()->ArgumentsLength(
shared.internal_formal_parameter_count()),
arguments_frame);
Node* const rest_length = graph()->NewNode(
simplified()->RestLength(shared.internal_formal_parameter_count()),
arguments_frame);
// Allocate the elements backing store.
Node* const elements = effect =
graph()->NewNode(simplified()->NewArgumentsElements(
CreateArgumentsType::kRestParameter,
shared.internal_formal_parameter_count()),
arguments_frame, arguments_length, effect);
// Load the JSArray object map.
Node* const jsarray_map = jsgraph()->Constant(
native_context().js_array_packed_elements_map());
// Actually allocate and initialize the jsarray.
AllocationBuilder a(jsgraph(), effect, control);
STATIC_ASSERT(JSArray::kHeaderSize == 4 * kTaggedSize);
a.Allocate(JSArray::kHeaderSize);
a.Store(AccessBuilder::ForMap(), jsarray_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(), elements);
a.Store(AccessBuilder::ForJSArrayLength(PACKED_ELEMENTS), rest_length);
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
}
UNREACHABLE();
}
// Use inline allocation for all mapped arguments objects within inlined
// (i.e. non-outermost) frames, independent of the object size.
DCHECK_EQ(outer_state->opcode(), IrOpcode::kFrameState);
switch (type) {
case CreateArgumentsType::kMappedArguments: {
Node* const callee = NodeProperties::GetValueInput(node, 0);
Node* const context = NodeProperties::GetContextInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
// TODO(turbofan): Duplicate parameters are not handled yet.
if (shared.has_duplicate_parameters()) return NoChange();
// Choose the correct frame state and frame state info depending on
// whether there conceptually is an arguments adaptor frame in the call
// chain.
Node* const args_state = GetArgumentsFrameState(frame_state);
if (args_state->InputAt(kFrameStateParametersInput)->opcode() ==
IrOpcode::kDeadValue) {
// This protects against an incompletely propagated DeadValue node.
// If the FrameState has a DeadValue input, then this node will be
// pruned anyway.
return NoChange();
}
FrameStateInfo args_state_info = FrameStateInfoOf(args_state->op());
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(
effect, control, args_state, context, shared, &has_aliased_arguments);
effect = elements->op()->EffectOutputCount() > 0 ? elements : effect;
// Load the arguments object map.
Node* const arguments_map = jsgraph()->Constant(
has_aliased_arguments ? native_context().fast_aliased_arguments_map()
: native_context().sloppy_arguments_map());
// Actually allocate and initialize the arguments object.
AllocationBuilder a(jsgraph(), effect, control);
STATIC_ASSERT(JSSloppyArgumentsObject::kSize == 5 * kTaggedSize);
a.Allocate(JSSloppyArgumentsObject::kSize);
a.Store(AccessBuilder::ForMap(), arguments_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(), elements);
a.Store(AccessBuilder::ForArgumentsLength(), jsgraph()->Constant(length));
a.Store(AccessBuilder::ForArgumentsCallee(), callee);
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
case CreateArgumentsType::kUnmappedArguments: {
// Use inline allocation for all unmapped arguments objects within inlined
// (i.e. non-outermost) frames, independent of the object size.
Node* effect = NodeProperties::GetEffectInput(node);
// Choose the correct frame state and frame state info depending on
// whether there conceptually is an arguments adaptor frame in the call
// chain.
Node* const args_state = GetArgumentsFrameState(frame_state);
if (args_state->InputAt(kFrameStateParametersInput)->opcode() ==
IrOpcode::kDeadValue) {
// This protects against an incompletely propagated DeadValue node.
// If the FrameState has a DeadValue input, then this node will be
// pruned anyway.
return NoChange();
}
FrameStateInfo args_state_info = FrameStateInfoOf(args_state->op());
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);
effect = elements->op()->EffectOutputCount() > 0 ? elements : effect;
// Load the arguments object map.
Node* const arguments_map =
jsgraph()->Constant(native_context().strict_arguments_map());
// Actually allocate and initialize the arguments object.
AllocationBuilder a(jsgraph(), effect, control);
STATIC_ASSERT(JSStrictArgumentsObject::kSize == 4 * kTaggedSize);
a.Allocate(JSStrictArgumentsObject::kSize);
a.Store(AccessBuilder::ForMap(), arguments_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(), elements);
a.Store(AccessBuilder::ForArgumentsLength(), jsgraph()->Constant(length));
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
case CreateArgumentsType::kRestParameter: {
int start_index = shared.internal_formal_parameter_count();
// Use inline allocation for all unmapped arguments objects within inlined
// (i.e. non-outermost) frames, independent of the object size.
Node* effect = NodeProperties::GetEffectInput(node);
// Choose the correct frame state and frame state info depending on
// whether there conceptually is an arguments adaptor frame in the call
// chain.
Node* const args_state = GetArgumentsFrameState(frame_state);
if (args_state->InputAt(kFrameStateParametersInput)->opcode() ==
IrOpcode::kDeadValue) {
// This protects against an incompletely propagated DeadValue node.
// If the FrameState has a DeadValue input, then this node will be
// pruned anyway.
return NoChange();
}
FrameStateInfo args_state_info = FrameStateInfoOf(args_state->op());
// Prepare element backing store to be used by the rest array.
Node* const elements =
AllocateRestArguments(effect, control, args_state, start_index);
effect = elements->op()->EffectOutputCount() > 0 ? elements : effect;
// Load the JSArray object map.
Node* const jsarray_map =
jsgraph()->Constant(native_context().js_array_packed_elements_map());
// Actually allocate and initialize the jsarray.
AllocationBuilder a(jsgraph(), effect, control);
// -1 to minus receiver
int argument_count = args_state_info.parameter_count() - 1;
int length = std::max(0, argument_count - start_index);
STATIC_ASSERT(JSArray::kHeaderSize == 4 * kTaggedSize);
a.Allocate(JSArray::kHeaderSize);
a.Store(AccessBuilder::ForMap(), jsarray_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(), elements);
a.Store(AccessBuilder::ForJSArrayLength(PACKED_ELEMENTS),
jsgraph()->Constant(length));
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
}
UNREACHABLE();
}
Reduction JSCreateLowering::ReduceJSCreateGeneratorObject(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateGeneratorObject, node->opcode());
Node* const closure = NodeProperties::GetValueInput(node, 0);
Node* const receiver = NodeProperties::GetValueInput(node, 1);
Node* const context = NodeProperties::GetContextInput(node);
Type const closure_type = NodeProperties::GetType(closure);
Node* effect = NodeProperties::GetEffectInput(node);
Node* const control = NodeProperties::GetControlInput(node);
if (closure_type.IsHeapConstant()) {
DCHECK(closure_type.AsHeapConstant()->Ref().IsJSFunction());
JSFunctionRef js_function =
closure_type.AsHeapConstant()->Ref().AsJSFunction();
if (!js_function.has_initial_map()) return NoChange();
SlackTrackingPrediction slack_tracking_prediction =
dependencies()->DependOnInitialMapInstanceSizePrediction(js_function);
MapRef initial_map = js_function.initial_map();
DCHECK(initial_map.instance_type() == JS_GENERATOR_OBJECT_TYPE ||
initial_map.instance_type() == JS_ASYNC_GENERATOR_OBJECT_TYPE);
// Allocate a register file.
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();
AllocationBuilder ab(jsgraph(), effect, control);
ab.AllocateArray(size, MapRef(broker(), factory()->fixed_array_map()));
for (int i = 0; i < size; ++i) {
ab.Store(AccessBuilder::ForFixedArraySlot(i),
jsgraph()->UndefinedConstant());
}
Node* parameters_and_registers = effect = ab.Finish();
// Emit code to allocate the JS[Async]GeneratorObject instance.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(slack_tracking_prediction.instance_size());
Node* undefined = jsgraph()->UndefinedConstant();
a.Store(AccessBuilder::ForMap(), initial_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSGeneratorObjectContext(), context);
a.Store(AccessBuilder::ForJSGeneratorObjectFunction(), closure);
a.Store(AccessBuilder::ForJSGeneratorObjectReceiver(), receiver);
a.Store(AccessBuilder::ForJSGeneratorObjectInputOrDebugPos(), undefined);
a.Store(AccessBuilder::ForJSGeneratorObjectResumeMode(),
jsgraph()->Constant(JSGeneratorObject::kNext));
a.Store(AccessBuilder::ForJSGeneratorObjectContinuation(),
jsgraph()->Constant(JSGeneratorObject::kGeneratorExecuting));
a.Store(AccessBuilder::ForJSGeneratorObjectParametersAndRegisters(),
parameters_and_registers);
if (initial_map.instance_type() == JS_ASYNC_GENERATOR_OBJECT_TYPE) {
a.Store(AccessBuilder::ForJSAsyncGeneratorObjectQueue(), undefined);
a.Store(AccessBuilder::ForJSAsyncGeneratorObjectIsAwaiting(),
jsgraph()->ZeroConstant());
}
// Handle in-object properties, too.
for (int i = 0; i < slack_tracking_prediction.inobject_property_count();
++i) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
undefined);
}
a.FinishAndChange(node);
return Changed(node);
}
return NoChange();
}
// Constructs an array with a variable {length} when no upper bound
// is known for the capacity.
Reduction JSCreateLowering::ReduceNewArray(
Node* node, Node* length, MapRef initial_map, ElementsKind elements_kind,
AllocationType allocation,
const SlackTrackingPrediction& slack_tracking_prediction) {
DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode());
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Constructing an Array via new Array(N) where N is an unsigned
// integer, always creates a holey backing store.
ASSIGN_RETURN_NO_CHANGE_IF_DATA_MISSING(
initial_map,
initial_map.AsElementsKind(GetHoleyElementsKind(elements_kind)));
// Because CheckBounds performs implicit conversion from string to number, an
// additional CheckNumber is required to behave correctly for calls with a
// single string argument.
length = effect = graph()->NewNode(
simplified()->CheckNumber(FeedbackSource{}), length, effect, control);
// Check that the {limit} is an unsigned integer in the valid range.
// This has to be kept in sync with src/runtime/runtime-array.cc,
// where this limit is protected.
length = effect = graph()->NewNode(
simplified()->CheckBounds(FeedbackSource()), length,
jsgraph()->Constant(JSArray::kInitialMaxFastElementArray), effect,
control);
// Construct elements and properties for the resulting JSArray.
Node* elements = effect =
graph()->NewNode(IsDoubleElementsKind(initial_map.elements_kind())
? simplified()->NewDoubleElements(allocation)
: simplified()->NewSmiOrObjectElements(allocation),
length, effect, control);
// Perform the allocation of the actual JSArray object.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(slack_tracking_prediction.instance_size(), allocation);
a.Store(AccessBuilder::ForMap(), initial_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(), elements);
a.Store(AccessBuilder::ForJSArrayLength(initial_map.elements_kind()), length);
for (int i = 0; i < slack_tracking_prediction.inobject_property_count();
++i) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
jsgraph()->UndefinedConstant());
}
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
// Constructs an array with a variable {length} when an actual
// upper bound is known for the {capacity}.
Reduction JSCreateLowering::ReduceNewArray(
Node* node, Node* length, int capacity, MapRef initial_map,
ElementsKind elements_kind, AllocationType allocation,
const SlackTrackingPrediction& slack_tracking_prediction) {
DCHECK(node->opcode() == IrOpcode::kJSCreateArray ||
node->opcode() == IrOpcode::kJSCreateEmptyLiteralArray);
DCHECK(NodeProperties::GetType(length).Is(Type::Number()));
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Determine the appropriate elements kind.
if (NodeProperties::GetType(length).Max() > 0.0) {
elements_kind = GetHoleyElementsKind(elements_kind);
}
ASSIGN_RETURN_NO_CHANGE_IF_DATA_MISSING(
initial_map, initial_map.AsElementsKind(elements_kind));
DCHECK(IsFastElementsKind(elements_kind));
// Setup elements and properties.
Node* elements;
if (capacity == 0) {
elements = jsgraph()->EmptyFixedArrayConstant();
} else {
elements = effect =
AllocateElements(effect, control, elements_kind, capacity, allocation);
}
// Perform the allocation of the actual JSArray object.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(slack_tracking_prediction.instance_size(), allocation);
a.Store(AccessBuilder::ForMap(), initial_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(), elements);
a.Store(AccessBuilder::ForJSArrayLength(elements_kind), length);
for (int i = 0; i < slack_tracking_prediction.inobject_property_count();
++i) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
jsgraph()->UndefinedConstant());
}
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceNewArray(
Node* node, std::vector<Node*> values, MapRef initial_map,
ElementsKind elements_kind, AllocationType allocation,
const SlackTrackingPrediction& slack_tracking_prediction) {
DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode());
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Determine the appropriate elements kind.
DCHECK(IsFastElementsKind(elements_kind));
ASSIGN_RETURN_NO_CHANGE_IF_DATA_MISSING(
initial_map, initial_map.AsElementsKind(elements_kind));
// Check {values} based on the {elements_kind}. These checks are guarded
// by the {elements_kind} feedback on the {site}, so it's safe to just
// deoptimize in this case.
if (IsSmiElementsKind(elements_kind)) {
for (auto& value : values) {
if (!NodeProperties::GetType(value).Is(Type::SignedSmall())) {
value = effect = graph()->NewNode(
simplified()->CheckSmi(FeedbackSource()), value, effect, control);
}
}
} else if (IsDoubleElementsKind(elements_kind)) {
for (auto& value : values) {
if (!NodeProperties::GetType(value).Is(Type::Number())) {
value = effect =
graph()->NewNode(simplified()->CheckNumber(FeedbackSource()), value,
effect, control);
}
// Make sure we do not store signaling NaNs into double arrays.
value = graph()->NewNode(simplified()->NumberSilenceNaN(), value);
}
}
// Setup elements, properties and length.
Node* elements = effect =
AllocateElements(effect, control, elements_kind, values, allocation);
Node* length = jsgraph()->Constant(static_cast<int>(values.size()));
// Perform the allocation of the actual JSArray object.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(slack_tracking_prediction.instance_size(), allocation);
a.Store(AccessBuilder::ForMap(), initial_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(), elements);
a.Store(AccessBuilder::ForJSArrayLength(elements_kind), length);
for (int i = 0; i < slack_tracking_prediction.inobject_property_count();
++i) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
jsgraph()->UndefinedConstant());
}
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode());
CreateArrayParameters const& p = CreateArrayParametersOf(node->op());
int const arity = static_cast<int>(p.arity());
base::Optional<AllocationSiteRef> site_ref;
{
Handle<AllocationSite> site;
if (p.site().ToHandle(&site)) {
site_ref = AllocationSiteRef(broker(), site);
}
}
AllocationType allocation = AllocationType::kYoung;
base::Optional<MapRef> initial_map =
NodeProperties::GetJSCreateMap(broker(), node);
if (!initial_map.has_value()) return NoChange();
Node* new_target = NodeProperties::GetValueInput(node, 1);
JSFunctionRef original_constructor =
HeapObjectMatcher(new_target).Ref(broker()).AsJSFunction();
SlackTrackingPrediction slack_tracking_prediction =
dependencies()->DependOnInitialMapInstanceSizePrediction(
original_constructor);
// Tells whether we are protected by either the {site} or a
// protector cell to do certain speculative optimizations.
bool can_inline_call = false;
// Check if we have a feedback {site} on the {node}.
ElementsKind elements_kind = initial_map->elements_kind();
if (site_ref) {
elements_kind = site_ref->GetElementsKind();
can_inline_call = site_ref->CanInlineCall();
allocation = dependencies()->DependOnPretenureMode(*site_ref);
dependencies()->DependOnElementsKind(*site_ref);
} else {
PropertyCellRef array_constructor_protector(
broker(), factory()->array_constructor_protector());
can_inline_call = array_constructor_protector.value().AsSmi() ==
Protectors::kProtectorValid;
}
if (arity == 0) {
Node* length = jsgraph()->ZeroConstant();
int capacity = JSArray::kPreallocatedArrayElements;
return ReduceNewArray(node, length, capacity, *initial_map, elements_kind,
allocation, slack_tracking_prediction);
} else if (arity == 1) {
Node* length = NodeProperties::GetValueInput(node, 2);
Type length_type = NodeProperties::GetType(length);
if (!length_type.Maybe(Type::Number())) {
// Handle the single argument case, where we know that the value
// cannot be a valid Array length.
elements_kind = GetMoreGeneralElementsKind(
elements_kind, IsHoleyElementsKind(elements_kind) ? HOLEY_ELEMENTS
: PACKED_ELEMENTS);
return ReduceNewArray(node, std::vector<Node*>{length}, *initial_map,
elements_kind, allocation,
slack_tracking_prediction);
}
if (length_type.Is(Type::SignedSmall()) && length_type.Min() >= 0 &&
length_type.Max() <= kElementLoopUnrollLimit &&
length_type.Min() == length_type.Max()) {
int capacity = static_cast<int>(length_type.Max());
// Replace length with a constant in order to protect against a potential
// typer bug leading to length > capacity.
length = jsgraph()->Constant(capacity);
return ReduceNewArray(node, length, capacity, *initial_map, elements_kind,
allocation, slack_tracking_prediction);
}
if (length_type.Maybe(Type::UnsignedSmall()) && can_inline_call) {
return ReduceNewArray(node, length, *initial_map, elements_kind,
allocation, slack_tracking_prediction);
}
} else if (arity <= JSArray::kInitialMaxFastElementArray) {
// Gather the values to store into the newly created array.
bool values_all_smis = true, values_all_numbers = true,
values_any_nonnumber = false;
std::vector<Node*> values;
values.reserve(p.arity());
for (int i = 0; i < arity; ++i) {
Node* value = NodeProperties::GetValueInput(node, 2 + i);
Type value_type = NodeProperties::GetType(value);
if (!value_type.Is(Type::SignedSmall())) {
values_all_smis = false;
}
if (!value_type.Is(Type::Number())) {
values_all_numbers = false;
}
if (!value_type.Maybe(Type::Number())) {
values_any_nonnumber = true;
}
values.push_back(value);
}
// Try to figure out the ideal elements kind statically.
if (values_all_smis) {
// Smis can be stored with any elements kind.
} else if (values_all_numbers) {
elements_kind = GetMoreGeneralElementsKind(
elements_kind, IsHoleyElementsKind(elements_kind)
? HOLEY_DOUBLE_ELEMENTS
: PACKED_DOUBLE_ELEMENTS);
} else if (values_any_nonnumber) {
elements_kind = GetMoreGeneralElementsKind(
elements_kind, IsHoleyElementsKind(elements_kind) ? HOLEY_ELEMENTS
: PACKED_ELEMENTS);
} else if (!can_inline_call) {
// We have some crazy combination of types for the {values} where
// there's no clear decision on the elements kind statically. And
// we don't have a protection against deoptimization loops for the
// checks that are introduced in the call to ReduceNewArray, so
// we cannot inline this invocation of the Array constructor here.
return NoChange();
}
return ReduceNewArray(node, values, *initial_map, elements_kind, allocation,
slack_tracking_prediction);
}
return NoChange();
}
Reduction JSCreateLowering::ReduceJSCreateArrayIterator(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateArrayIterator, node->opcode());
CreateArrayIteratorParameters const& p =
CreateArrayIteratorParametersOf(node->op());
Node* iterated_object = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Create the JSArrayIterator result.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(JSArrayIterator::kHeaderSize, AllocationType::kYoung,
Type::OtherObject());
a.Store(AccessBuilder::ForMap(),
native_context().initial_array_iterator_map());
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSArrayIteratorIteratedObject(), iterated_object);
a.Store(AccessBuilder::ForJSArrayIteratorNextIndex(),
jsgraph()->ZeroConstant());
a.Store(AccessBuilder::ForJSArrayIteratorKind(),
jsgraph()->Constant(static_cast<int>(p.kind())));
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateAsyncFunctionObject(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateAsyncFunctionObject, node->opcode());
int const register_count = RegisterCountOf(node->op());
Node* closure = NodeProperties::GetValueInput(node, 0);
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* promise = NodeProperties::GetValueInput(node, 2);
Node* context = NodeProperties::GetContextInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Create the register file.
AllocationBuilder ab(jsgraph(), effect, control);
ab.AllocateArray(register_count,
MapRef(broker(), factory()->fixed_array_map()));
for (int i = 0; i < register_count; ++i) {
ab.Store(AccessBuilder::ForFixedArraySlot(i),
jsgraph()->UndefinedConstant());
}
Node* parameters_and_registers = effect = ab.Finish();
// Create the JSAsyncFunctionObject result.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(JSAsyncFunctionObject::kHeaderSize);
a.Store(AccessBuilder::ForMap(),
native_context().async_function_object_map());
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSGeneratorObjectContext(), context);
a.Store(AccessBuilder::ForJSGeneratorObjectFunction(), closure);
a.Store(AccessBuilder::ForJSGeneratorObjectReceiver(), receiver);
a.Store(AccessBuilder::ForJSGeneratorObjectInputOrDebugPos(),
jsgraph()->UndefinedConstant());
a.Store(AccessBuilder::ForJSGeneratorObjectResumeMode(),
jsgraph()->Constant(JSGeneratorObject::kNext));
a.Store(AccessBuilder::ForJSGeneratorObjectContinuation(),
jsgraph()->Constant(JSGeneratorObject::kGeneratorExecuting));
a.Store(AccessBuilder::ForJSGeneratorObjectParametersAndRegisters(),
parameters_and_registers);
a.Store(AccessBuilder::ForJSAsyncFunctionObjectPromise(), promise);
a.FinishAndChange(node);
return Changed(node);
}
namespace {
MapRef MapForCollectionIterationKind(const NativeContextRef& native_context,
CollectionKind collection_kind,
IterationKind iteration_kind) {
switch (collection_kind) {
case CollectionKind::kSet:
switch (iteration_kind) {
case IterationKind::kKeys:
UNREACHABLE();
case IterationKind::kValues:
return native_context.set_value_iterator_map();
case IterationKind::kEntries:
return native_context.set_key_value_iterator_map();
}
break;
case CollectionKind::kMap:
switch (iteration_kind) {
case IterationKind::kKeys:
return native_context.map_key_iterator_map();
case IterationKind::kValues:
return native_context.map_value_iterator_map();
case IterationKind::kEntries:
return native_context.map_key_value_iterator_map();
}
break;
}
UNREACHABLE();
}
} // namespace
Reduction JSCreateLowering::ReduceJSCreateCollectionIterator(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateCollectionIterator, node->opcode());
CreateCollectionIteratorParameters const& p =
CreateCollectionIteratorParametersOf(node->op());
Node* iterated_object = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Load the OrderedHashTable from the {receiver}.
Node* table = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSCollectionTable()),
iterated_object, effect, control);
// Create the JSCollectionIterator result.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(JSCollectionIterator::kHeaderSize, AllocationType::kYoung,
Type::OtherObject());
a.Store(AccessBuilder::ForMap(),
MapForCollectionIterationKind(native_context(), p.collection_kind(),
p.iteration_kind()));
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSCollectionIteratorTable(), table);
a.Store(AccessBuilder::ForJSCollectionIteratorIndex(),
jsgraph()->ZeroConstant());
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateBoundFunction(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateBoundFunction, node->opcode());
CreateBoundFunctionParameters const& p =
CreateBoundFunctionParametersOf(node->op());
int const arity = static_cast<int>(p.arity());
MapRef const map(broker(), p.map());
Node* bound_target_function = NodeProperties::GetValueInput(node, 0);
Node* bound_this = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(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));
}
bound_arguments = effect = a.Finish();
}
// Create the JSBoundFunction result.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(JSBoundFunction::kHeaderSize, AllocationType::kYoung,
Type::BoundFunction());
a.Store(AccessBuilder::ForMap(), map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSBoundFunctionBoundTargetFunction(),
bound_target_function);
a.Store(AccessBuilder::ForJSBoundFunctionBoundThis(), bound_this);
a.Store(AccessBuilder::ForJSBoundFunctionBoundArguments(), bound_arguments);
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateClosure(Node* node) {
JSCreateClosureNode n(node);
CreateClosureParameters const& p = n.Parameters();
SharedFunctionInfoRef shared(broker(), p.shared_info());
FeedbackCellRef feedback_cell = n.GetFeedbackCellRefChecked(broker());
HeapObjectRef code(broker(), p.code());
Effect effect = n.effect();
Control control = n.control();
Node* context = n.context();
// Use inline allocation of closures only for instantiation sites that have
// seen more than one instantiation, this simplifies the generated code and
// also serves as a heuristic of which allocation sites benefit from it.
if (!feedback_cell.map().equals(
MapRef(broker(), factory()->many_closures_cell_map()))) {
return NoChange();
}
MapRef function_map =
native_context().GetFunctionMapFromIndex(shared.function_map_index());
DCHECK(!function_map.IsInobjectSlackTrackingInProgress());
DCHECK(!function_map.is_dictionary_map());
// TODO(turbofan): We should use the pretenure flag from {p} here,
// but currently the heuristic in the parser works against us, as
// it marks closures like
//
// args[l] = function(...) { ... }
//
// for old-space allocation, which doesn't always make sense. For
// example in case of the bluebird-parallel benchmark, where this
// is a core part of the *promisify* logic (see crbug.com/810132).
AllocationType allocation = AllocationType::kYoung;
// Emit code to allocate the JSFunction instance.
STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kTaggedSize);
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(function_map.instance_size(), allocation, Type::Function());
a.Store(AccessBuilder::ForMap(), function_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSFunctionSharedFunctionInfo(), shared);
a.Store(AccessBuilder::ForJSFunctionContext(), context);
a.Store(AccessBuilder::ForJSFunctionFeedbackCell(), feedback_cell);
a.Store(AccessBuilder::ForJSFunctionCode(), code);
STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kTaggedSize);
if (function_map.has_prototype_slot()) {
a.Store(AccessBuilder::ForJSFunctionPrototypeOrInitialMap(),
jsgraph()->TheHoleConstant());
STATIC_ASSERT(JSFunction::kSizeWithPrototype == 8 * kTaggedSize);
}
for (int i = 0; i < function_map.GetInObjectProperties(); i++) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(function_map, i),
jsgraph()->UndefinedConstant());
}
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateIterResultObject(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateIterResultObject, node->opcode());
Node* value = NodeProperties::GetValueInput(node, 0);
Node* done = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* iterator_result_map =
jsgraph()->Constant(native_context().iterator_result_map());
// Emit code to allocate the JSIteratorResult instance.
AllocationBuilder a(jsgraph(), effect, graph()->start());
a.Allocate(JSIteratorResult::kSize);
a.Store(AccessBuilder::ForMap(), iterator_result_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSIteratorResultValue(), value);
a.Store(AccessBuilder::ForJSIteratorResultDone(), done);
STATIC_ASSERT(JSIteratorResult::kSize == 5 * kTaggedSize);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateStringIterator(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateStringIterator, node->opcode());
Node* string = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
Node* map =
jsgraph()->Constant(native_context().initial_string_iterator_map());
// Allocate new iterator and attach the iterator to this string.
AllocationBuilder a(jsgraph(), effect, graph()->start());
a.Allocate(JSStringIterator::kHeaderSize, AllocationType::kYoung,
Type::OtherObject());
a.Store(AccessBuilder::ForMap(), map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSStringIteratorString(), string);
a.Store(AccessBuilder::ForJSStringIteratorIndex(), jsgraph()->SmiConstant(0));
STATIC_ASSERT(JSIteratorResult::kSize == 5 * kTaggedSize);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateKeyValueArray(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateKeyValueArray, node->opcode());
Node* key = NodeProperties::GetValueInput(node, 0);
Node* value = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* array_map =
jsgraph()->Constant(native_context().js_array_packed_elements_map());
Node* length = jsgraph()->Constant(2);
AllocationBuilder aa(jsgraph(), effect, graph()->start());
aa.AllocateArray(2, MapRef(broker(), factory()->fixed_array_map()));
aa.Store(AccessBuilder::ForFixedArrayElement(PACKED_ELEMENTS),
jsgraph()->ZeroConstant(), key);
aa.Store(AccessBuilder::ForFixedArrayElement(PACKED_ELEMENTS),
jsgraph()->OneConstant(), value);
Node* elements = aa.Finish();
AllocationBuilder a(jsgraph(), elements, graph()->start());
a.Allocate(JSArray::kHeaderSize);
a.Store(AccessBuilder::ForMap(), array_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(), elements);
a.Store(AccessBuilder::ForJSArrayLength(PACKED_ELEMENTS), length);
STATIC_ASSERT(JSArray::kHeaderSize == 4 * kTaggedSize);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreatePromise(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreatePromise, node->opcode());
Node* effect = NodeProperties::GetEffectInput(node);
MapRef promise_map = native_context().promise_function().initial_map();
AllocationBuilder a(jsgraph(), effect, graph()->start());
a.Allocate(promise_map.instance_size());
a.Store(AccessBuilder::ForMap(), promise_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectOffset(JSPromise::kReactionsOrResultOffset),
jsgraph()->ZeroConstant());
STATIC_ASSERT(v8::Promise::kPending == 0);
a.Store(AccessBuilder::ForJSObjectOffset(JSPromise::kFlagsOffset),
jsgraph()->ZeroConstant());
STATIC_ASSERT(JSPromise::kHeaderSize == 5 * kTaggedSize);
for (int offset = JSPromise::kHeaderSize;
offset < JSPromise::kSizeWithEmbedderFields; offset += kTaggedSize) {
a.Store(AccessBuilder::ForJSObjectOffset(offset),
jsgraph()->ZeroConstant());
}
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateLiteralArrayOrObject(Node* node) {
DCHECK(node->opcode() == IrOpcode::kJSCreateLiteralArray ||
node->opcode() == IrOpcode::kJSCreateLiteralObject);
JSCreateLiteralOpNode n(node);
CreateLiteralParameters const& p = n.Parameters();
Effect effect = n.effect();
Control control = n.control();
ProcessedFeedback const& feedback =
broker()->GetFeedbackForArrayOrObjectLiteral(p.feedback());
if (!feedback.IsInsufficient()) {
AllocationSiteRef site = feedback.AsLiteral().value();
if (site.IsFastLiteral()) {
AllocationType allocation = AllocationType::kYoung;
if (FLAG_allocation_site_pretenuring) {
allocation = dependencies()->DependOnPretenureMode(site);
}
dependencies()->DependOnElementsKinds(site);
JSObjectRef boilerplate = site.boilerplate().value();
Node* value = effect =
AllocateFastLiteral(effect, control, boilerplate, allocation);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
}
return NoChange();
}
Reduction JSCreateLowering::ReduceJSCreateEmptyLiteralArray(Node* node) {
JSCreateEmptyLiteralArrayNode n(node);
FeedbackParameter const& p = n.Parameters();
ProcessedFeedback const& feedback =
broker()->GetFeedbackForArrayOrObjectLiteral(p.feedback());
if (!feedback.IsInsufficient()) {
AllocationSiteRef site = feedback.AsLiteral().value();
DCHECK(!site.PointsToLiteral());
MapRef initial_map =
native_context().GetInitialJSArrayMap(site.GetElementsKind());
AllocationType const allocation =
dependencies()->DependOnPretenureMode(site);
dependencies()->DependOnElementsKind(site);
Node* length = jsgraph()->ZeroConstant();
DCHECK(!initial_map.IsInobjectSlackTrackingInProgress());
SlackTrackingPrediction slack_tracking_prediction(
initial_map, initial_map.instance_size());
return ReduceNewArray(node, length, 0, initial_map,
initial_map.elements_kind(), allocation,
slack_tracking_prediction);
}
return NoChange();
}
Reduction JSCreateLowering::ReduceJSCreateEmptyLiteralObject(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateEmptyLiteralObject, node->opcode());
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Retrieve the initial map for the object.
MapRef map = native_context().object_function().initial_map();
DCHECK(!map.is_dictionary_map());
DCHECK(!map.IsInobjectSlackTrackingInProgress());
Node* js_object_map = jsgraph()->Constant(map);
// Setup elements and properties.
Node* elements = jsgraph()->EmptyFixedArrayConstant();
// Perform the allocation of the actual JSArray object.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(map.instance_size());
a.Store(AccessBuilder::ForMap(), js_object_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(), elements);
for (int i = 0; i < map.GetInObjectProperties(); i++) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(map, i),
jsgraph()->UndefinedConstant());
}
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateLiteralRegExp(Node* node) {
JSCreateLiteralRegExpNode n(node);
CreateLiteralParameters const& p = n.Parameters();
Effect effect = n.effect();
Control control = n.control();
ProcessedFeedback const& feedback =
broker()->GetFeedbackForRegExpLiteral(p.feedback());
if (!feedback.IsInsufficient()) {
JSRegExpRef literal = feedback.AsRegExpLiteral().value();
Node* value = effect = AllocateLiteralRegExp(effect, control, literal);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
return NoChange();
}
Reduction JSCreateLowering::ReduceJSGetTemplateObject(Node* node) {
JSGetTemplateObjectNode n(node);
GetTemplateObjectParameters const& parameters = n.Parameters();
SharedFunctionInfoRef shared(broker(), parameters.shared());
JSArrayRef template_object = shared.GetTemplateObject(
TemplateObjectDescriptionRef(broker(), parameters.description()),
parameters.feedback());
Node* value = jsgraph()->Constant(template_object);
ReplaceWithValue(node, value);
return Replace(value);
}
Reduction JSCreateLowering::ReduceJSCreateFunctionContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateFunctionContext, node->opcode());
const CreateFunctionContextParameters& parameters =
CreateFunctionContextParametersOf(node->op());
ScopeInfoRef scope_info(broker(), parameters.scope_info());
int slot_count = parameters.slot_count();
ScopeType scope_type = parameters.scope_type();
// Use inline allocation for function contexts up to a size limit.
if (slot_count < kFunctionContextAllocationLimit) {
// JSCreateFunctionContext[slot_count < limit]](fun)
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* context = NodeProperties::GetContextInput(node);
AllocationBuilder a(jsgraph(), effect, control);
STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 2); // Ensure fully covered.
int context_length = slot_count + Context::MIN_CONTEXT_SLOTS;
switch (scope_type) {
case EVAL_SCOPE:
a.AllocateContext(context_length, native_context().eval_context_map());
break;
case FUNCTION_SCOPE:
a.AllocateContext(context_length,
native_context().function_context_map());
break;
default:
UNREACHABLE();
}
a.Store(AccessBuilder::ForContextSlot(Context::SCOPE_INFO_INDEX),
scope_info);
a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context);
for (int i = Context::MIN_CONTEXT_SLOTS; i < context_length; ++i) {
a.Store(AccessBuilder::ForContextSlot(i), jsgraph()->UndefinedConstant());
}
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
return NoChange();
}
Reduction JSCreateLowering::ReduceJSCreateWithContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateWithContext, node->opcode());
ScopeInfoRef scope_info(broker(), ScopeInfoOf(node->op()));
Node* extension = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* context = NodeProperties::GetContextInput(node);
AllocationBuilder a(jsgraph(), effect, control);
STATIC_ASSERT(Context::MIN_CONTEXT_EXTENDED_SLOTS ==
3); // Ensure fully covered.
a.AllocateContext(Context::MIN_CONTEXT_EXTENDED_SLOTS,
native_context().with_context_map());
a.Store(AccessBuilder::ForContextSlot(Context::SCOPE_INFO_INDEX), scope_info);
a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context);
a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), extension);
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateCatchContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateCatchContext, node->opcode());
ScopeInfoRef scope_info(broker(), ScopeInfoOf(node->op()));
Node* exception = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* context = NodeProperties::GetContextInput(node);
AllocationBuilder a(jsgraph(), effect, control);
STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 2); // Ensure fully covered.
a.AllocateContext(Context::MIN_CONTEXT_SLOTS + 1,
native_context().catch_context_map());
a.Store(AccessBuilder::ForContextSlot(Context::SCOPE_INFO_INDEX), scope_info);
a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context);
a.Store(AccessBuilder::ForContextSlot(Context::THROWN_OBJECT_INDEX),
exception);
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateBlockContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateBlockContext, node->opcode());
ScopeInfoRef scope_info(broker(), ScopeInfoOf(node->op()));
int const context_length = scope_info.ContextLength();
// Use inline allocation for block contexts up to a size limit.
if (context_length < kBlockContextAllocationLimit) {
// JSCreateBlockContext[scope[length < limit]](fun)
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* context = NodeProperties::GetContextInput(node);
AllocationBuilder a(jsgraph(), effect, control);
STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 2); // Ensure fully covered.
a.AllocateContext(context_length, native_context().block_context_map());
a.Store(AccessBuilder::ForContextSlot(Context::SCOPE_INFO_INDEX),
scope_info);
a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context);
for (int i = Context::MIN_CONTEXT_SLOTS; i < context_length; ++i) {
a.Store(AccessBuilder::ForContextSlot(i), jsgraph()->UndefinedConstant());
}
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
return NoChange();
}
namespace {
base::Optional<MapRef> GetObjectCreateMap(JSHeapBroker* broker,
HeapObjectRef prototype) {
MapRef standard_map =
broker->target_native_context().object_function().initial_map();
if (prototype.equals(standard_map.prototype())) {
return standard_map;
}
if (prototype.map().oddball_type() == OddballType::kNull) {
return broker->target_native_context()
.slow_object_with_null_prototype_map();
}
if (prototype.IsJSObject()) {
return prototype.AsJSObject().GetObjectCreateMap();
}
return base::Optional<MapRef>();
}
} // namespace
Reduction JSCreateLowering::ReduceJSCreateObject(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateObject, node->opcode());
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* prototype = NodeProperties::GetValueInput(node, 0);
Type prototype_type = NodeProperties::GetType(prototype);
if (!prototype_type.IsHeapConstant()) return NoChange();
HeapObjectRef prototype_const = prototype_type.AsHeapConstant()->Ref();
auto maybe_instance_map = GetObjectCreateMap(broker(), prototype_const);
if (!maybe_instance_map) return NoChange();
MapRef instance_map = maybe_instance_map.value();
Node* properties = jsgraph()->EmptyFixedArrayConstant();
if (instance_map.is_dictionary_map()) {
DCHECK_EQ(prototype_const.map().oddball_type(), OddballType::kNull);
// Allocate an empty NameDictionary as backing store for the properties.
MapRef map(broker(), factory()->name_dictionary_map());
int capacity =
NameDictionary::ComputeCapacity(NameDictionary::kInitialCapacity);
DCHECK(base::bits::IsPowerOfTwo(capacity));
int length = NameDictionary::EntryToIndex(InternalIndex(capacity));
int size = NameDictionary::SizeFor(length);
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(size, AllocationType::kYoung, Type::Any());
a.Store(AccessBuilder::ForMap(), map);
// Initialize FixedArray fields.
a.Store(AccessBuilder::ForFixedArrayLength(),
jsgraph()->SmiConstant(length));
// Initialize HashTable fields.
a.Store(AccessBuilder::ForHashTableBaseNumberOfElements(),
jsgraph()->SmiConstant(0));
a.Store(AccessBuilder::ForHashTableBaseNumberOfDeletedElement(),
jsgraph()->SmiConstant(0));
a.Store(AccessBuilder::ForHashTableBaseCapacity(),
jsgraph()->SmiConstant(capacity));
// Initialize Dictionary fields.
a.Store(AccessBuilder::ForDictionaryNextEnumerationIndex(),
jsgraph()->SmiConstant(PropertyDetails::kInitialIndex));
a.Store(AccessBuilder::ForDictionaryObjectHashIndex(),
jsgraph()->SmiConstant(PropertyArray::kNoHashSentinel));
// Initialize the Properties fields.
Node* undefined = jsgraph()->UndefinedConstant();
STATIC_ASSERT(NameDictionary::kElementsStartIndex ==
NameDictionary::kObjectHashIndex + 1);
for (int index = NameDictionary::kElementsStartIndex; index < length;
index++) {
a.Store(AccessBuilder::ForFixedArraySlot(index, kNoWriteBarrier),
undefined);
}
properties = effect = a.Finish();
}
int const instance_size = instance_map.instance_size();
if (instance_size > kMaxRegularHeapObjectSize) return NoChange();
CHECK(!instance_map.IsInobjectSlackTrackingInProgress());
// Emit code to allocate the JSObject instance for the given
// {instance_map}.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(instance_size, AllocationType::kYoung, Type::Any());
a.Store(AccessBuilder::ForMap(), instance_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
// Initialize Object fields.
Node* undefined = jsgraph()->UndefinedConstant();
for (int offset = JSObject::kHeaderSize; offset < instance_size;
offset += kTaggedSize) {
a.Store(AccessBuilder::ForJSObjectOffset(offset, kNoWriteBarrier),
undefined);
}
Node* value = effect = a.Finish();
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
// 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,
Node* frame_state) {
FrameStateInfo state_info = FrameStateInfoOf(frame_state->op());
int argument_count = state_info.parameter_count() - 1; // Minus receiver.
if (argument_count == 0) return jsgraph()->EmptyFixedArrayConstant();
// Prepare an iterator over argument values recorded in the frame state.
Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
StateValuesAccess parameters_access(parameters);
auto parameters_it = ++parameters_access.begin();
// Actually allocate the backing store.
AllocationBuilder a(jsgraph(), effect, control);
a.AllocateArray(argument_count,
MapRef(broker(), factory()->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());
}
return a.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,
Node* frame_state,
int start_index) {
FrameStateInfo state_info = FrameStateInfoOf(frame_state->op());
int argument_count = state_info.parameter_count() - 1; // Minus receiver.
int num_elements = std::max(0, argument_count - start_index);
if (num_elements == 0) return jsgraph()->EmptyFixedArrayConstant();
// Prepare an iterator over argument values recorded in the frame state.
Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
StateValuesAccess parameters_access(parameters);
auto parameters_it = ++parameters_access.begin();
// Skip unused arguments.
for (int i = 0; i < start_index; i++) {
++parameters_it;
}
// Actually allocate the backing store.
AllocationBuilder a(jsgraph(), effect, control);
a.AllocateArray(num_elements, MapRef(broker(), factory()->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());
}
return a.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* effect, Node* control, Node* frame_state, Node* context,
const SharedFunctionInfoRef& shared, bool* has_aliased_arguments) {
FrameStateInfo state_info = FrameStateInfoOf(frame_state->op());
int argument_count = state_info.parameter_count() - 1; // Minus receiver.
if (argument_count == 0) return jsgraph()->EmptyFixedArrayConstant();
// If there is no aliasing, the arguments object elements are not special in
// 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);
}
// Calculate number of argument values being aliased/mapped.
int mapped_count = Min(argument_count, parameter_count);
*has_aliased_arguments = true;
// Prepare an iterator over argument values recorded in the frame state.
Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
StateValuesAccess parameters_access(parameters);
auto parameters_it = ++parameters_access.begin();
// 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,
MapRef(broker(), factory()->fixed_array_map()));
for (int i = 0; i < mapped_count; ++i, ++parameters_it) {
aa.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),
parameters_it.node());
}
Node* arguments = aa.Finish();
// Actually allocate the backing store.
AllocationBuilder a(jsgraph(), arguments, control);
a.AllocateSloppyArgumentElements(
mapped_count,
MapRef(broker(), factory()->sloppy_arguments_elements_map()));
a.Store(AccessBuilder::ForSloppyArgumentsElementsContext(), context);
a.Store(AccessBuilder::ForSloppyArgumentsElementsArguments(), arguments);
for (int i = 0; i < mapped_count; ++i) {
int idx = shared.context_header_size() + parameter_count - 1 - i;
a.Store(AccessBuilder::ForSloppyArgumentsElementsMappedEntry(),
jsgraph()->Constant(i), jsgraph()->Constant(idx));
}
return a.Finish();
}
// Helper that allocates a FixedArray serving as a parameter map for values
// 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* effect, Node* control, Node* context, Node* arguments_frame,
Node* arguments_length, const SharedFunctionInfoRef& shared,
bool* has_aliased_arguments) {
// If there is no aliasing, the arguments object elements are not
// special in any way, we can just return an unmapped backing store.
int parameter_count = shared.internal_formal_parameter_count();
if (parameter_count == 0) {
return graph()->NewNode(
simplified()->NewArgumentsElements(
CreateArgumentsType::kUnmappedArguments, parameter_count),
arguments_frame, arguments_length, effect);
}
// 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
// then linked into the parameter map below, whereas mapped argument values
// (i.e. the first {mapped_count} elements) are replaced with a hole instead.
Node* arguments = effect =
graph()->NewNode(simplified()->NewArgumentsElements(
CreateArgumentsType::kMappedArguments, mapped_count),
arguments_frame, arguments_length, effect);
// Actually allocate the backing store.
AllocationBuilder a(jsgraph(), effect, control);
a.AllocateSloppyArgumentElements(
mapped_count,
MapRef(broker(), factory()->sloppy_arguments_elements_map()));
a.Store(AccessBuilder::ForSloppyArgumentsElementsContext(), context);
a.Store(AccessBuilder::ForSloppyArgumentsElementsArguments(), arguments);
for (int i = 0; i < mapped_count; ++i) {
int idx = shared.context_header_size() + parameter_count - 1 - i;
Node* value = graph()->NewNode(
common()->Select(MachineRepresentation::kTagged),
graph()->NewNode(simplified()->NumberLessThan(), jsgraph()->Constant(i),
arguments_length),
jsgraph()->Constant(idx), jsgraph()->TheHoleConstant());
a.Store(AccessBuilder::ForSloppyArgumentsElementsMappedEntry(),
jsgraph()->Constant(i), value);
}
return a.Finish();
}
Node* JSCreateLowering::AllocateElements(Node* effect, Node* control,
ElementsKind elements_kind,
int capacity,
AllocationType allocation) {
DCHECK_LE(1, capacity);
DCHECK_LE(capacity, JSArray::kInitialMaxFastElementArray);
Handle<Map> elements_map = IsDoubleElementsKind(elements_kind)
? factory()->fixed_double_array_map()
: factory()->fixed_array_map();
ElementAccess access = IsDoubleElementsKind(elements_kind)
? AccessBuilder::ForFixedDoubleArrayElement()
: AccessBuilder::ForFixedArrayElement();
Node* value = jsgraph()->TheHoleConstant();
// Actually allocate the backing store.
AllocationBuilder a(jsgraph(), effect, control);
a.AllocateArray(capacity, MapRef(broker(), elements_map), allocation);
for (int i = 0; i < capacity; ++i) {
Node* index = jsgraph()->Constant(i);
a.Store(access, index, value);
}
return a.Finish();
}
Node* JSCreateLowering::AllocateElements(Node* effect, Node* control,
ElementsKind elements_kind,
std::vector<Node*> const& values,
AllocationType allocation) {
int const capacity = static_cast<int>(values.size());
DCHECK_LE(1, capacity);
DCHECK_LE(capacity, JSArray::kInitialMaxFastElementArray);
Handle<Map> elements_map = IsDoubleElementsKind(elements_kind)
? factory()->fixed_double_array_map()
: factory()->fixed_array_map();
ElementAccess access = IsDoubleElementsKind(elements_kind)
? AccessBuilder::ForFixedDoubleArrayElement()
: AccessBuilder::ForFixedArrayElement();
// Actually allocate the backing store.
AllocationBuilder a(jsgraph(), effect, control);
a.AllocateArray(capacity, MapRef(broker(), elements_map), allocation);
for (int i = 0; i < capacity; ++i) {
Node* index = jsgraph()->Constant(i);
a.Store(access, index, values[i]);
}
return a.Finish();
}
Node* JSCreateLowering::AllocateFastLiteral(Node* effect, Node* control,
JSObjectRef boilerplate,
AllocationType allocation) {
// Compute the in-object properties to store first (might have effects).
MapRef boilerplate_map = boilerplate.map();
ZoneVector<std::pair<FieldAccess, Node*>> inobject_fields(zone());
inobject_fields.reserve(boilerplate_map.GetInObjectProperties());
int const boilerplate_nof = boilerplate_map.NumberOfOwnDescriptors();
for (InternalIndex i : InternalIndex::Range(boilerplate_nof)) {
PropertyDetails const property_details =
boilerplate_map.GetPropertyDetails(i);
if (property_details.location() != kField) continue;
DCHECK_EQ(kData, property_details.kind());
NameRef property_name = boilerplate_map.GetPropertyKey(i);
FieldIndex index = boilerplate_map.GetFieldIndexFor(i);
ConstFieldInfo const_field_info(boilerplate_map.object());
FieldAccess access = {kTaggedBase,
index.offset(),
property_name.object(),
MaybeHandle<Map>(),
Type::Any(),
MachineType::AnyTagged(),
kFullWriteBarrier,
LoadSensitivity::kUnsafe,
const_field_info};
Node* value;
if (boilerplate_map.IsUnboxedDoubleField(i)) {
access.machine_type = MachineType::Float64();
access.type = Type::Number();
uint64_t value_bits = boilerplate.RawFastDoublePropertyAsBitsAt(index);
if (value_bits == kHoleNanInt64) {
// This special case is analogous to is_uninitialized being true in the
// non-unboxed-double case below. The store of the hole NaN value here
// will always be followed by another store that actually initializes
// the field. The hole NaN should therefore be unobservable.
// Load elimination expects there to be at most one const store to any
// given field, so we always mark the unobservable ones as mutable.
access.const_field_info = ConstFieldInfo::None();
}
value = jsgraph()->Constant(bit_cast<double>(value_bits));
} else {
ObjectRef boilerplate_value = boilerplate.RawFastPropertyAt(index);
bool is_uninitialized =
boilerplate_value.IsHeapObject() &&
boilerplate_value.AsHeapObject().map().oddball_type() ==
OddballType::kUninitialized;
if (is_uninitialized) {
access.const_field_info = ConstFieldInfo::None();
}
if (boilerplate_value.IsJSObject()) {
JSObjectRef boilerplate_object = boilerplate_value.AsJSObject();
value = effect = AllocateFastLiteral(effect, control,
boilerplate_object, allocation);
} else if (property_details.representation().IsDouble()) {
double number = boilerplate_value.AsHeapNumber().value();
// Allocate a mutable HeapNumber box and store the value into it.
AllocationBuilder builder(jsgraph(), effect, control);
builder.Allocate(HeapNumber::kSize, allocation);
builder.Store(AccessBuilder::ForMap(),
MapRef(broker(), factory()->heap_number_map()));
builder.Store(AccessBuilder::ForHeapNumberValue(),
jsgraph()->Constant(number));
value = effect = builder.Finish();
} else if (property_details.representation().IsSmi()) {
// Ensure that value is stored as smi.
value = is_uninitialized
? jsgraph()->ZeroConstant()
: jsgraph()->Constant(boilerplate_value.AsSmi());
} else {
value = jsgraph()->Constant(boilerplate_value);
}
}
inobject_fields.push_back(std::make_pair(access, value));
}
// Fill slack at the end of the boilerplate object with filler maps.
int const boilerplate_length = boilerplate_map.GetInObjectProperties();
for (int index = static_cast<int>(inobject_fields.size());
index < boilerplate_length; ++index) {
FieldAccess access =
AccessBuilder::ForJSObjectInObjectProperty(boilerplate_map, index);
Node* value = jsgraph()->HeapConstant(factory()->one_pointer_filler_map());
inobject_fields.push_back(std::make_pair(access, value));
}
// Setup the elements backing store.
Node* elements =
AllocateFastLiteralElements(effect, control, boilerplate, allocation);
if (elements->op()->EffectOutputCount() > 0) effect = elements;
// Actually allocate and initialize the object.
AllocationBuilder builder(jsgraph(), effect, control);
builder.Allocate(boilerplate_map.instance_size(), allocation,
Type::For(boilerplate_map));
builder.Store(AccessBuilder::ForMap(), boilerplate_map);
builder.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(),
jsgraph()->EmptyFixedArrayConstant());
builder.Store(AccessBuilder::ForJSObjectElements(), elements);
if (boilerplate.IsJSArray()) {
JSArrayRef boilerplate_array = boilerplate.AsJSArray();
builder.Store(
AccessBuilder::ForJSArrayLength(boilerplate_array.GetElementsKind()),
boilerplate_array.length());
}
for (auto const& inobject_field : inobject_fields) {
builder.Store(inobject_field.first, inobject_field.second);
}
return builder.Finish();
}
Node* JSCreateLowering::AllocateFastLiteralElements(Node* effect, Node* control,
JSObjectRef boilerplate,
AllocationType allocation) {
FixedArrayBaseRef boilerplate_elements = boilerplate.elements();
// Empty or copy-on-write elements just store a constant.
int const elements_length = boilerplate_elements.length();
MapRef elements_map = boilerplate_elements.map();
if (boilerplate_elements.length() == 0 || elements_map.IsFixedCowArrayMap()) {
if (allocation == AllocationType::kOld) {
boilerplate.EnsureElementsTenured();
boilerplate_elements = boilerplate.elements();
}
return jsgraph()->HeapConstant(boilerplate_elements.object());
}
// Compute the elements to store first (might have effects).
ZoneVector<Node*> elements_values(elements_length, zone());
if (elements_map.instance_type() == FIXED_DOUBLE_ARRAY_TYPE) {
FixedDoubleArrayRef elements = boilerplate_elements.AsFixedDoubleArray();
for (int i = 0; i < elements_length; ++i) {
Float64 value = elements.get(i);
if (value.is_hole_nan()) {
elements_values[i] = jsgraph()->TheHoleConstant();
} else {
elements_values[i] = jsgraph()->Constant(value.get_scalar());
}
}
} else {
FixedArrayRef elements = boilerplate_elements.AsFixedArray();
for (int i = 0; i < elements_length; ++i) {
ObjectRef element_value = elements.get(i);
if (element_value.IsJSObject()) {
elements_values[i] = effect = AllocateFastLiteral(
effect, control, element_value.AsJSObject(), allocation);
} else {
elements_values[i] = jsgraph()->Constant(element_value);
}
}
}
// Allocate the backing store array and store the elements.
AllocationBuilder builder(jsgraph(), effect, control);
builder.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]);
}
return builder.Finish();
}
Node* JSCreateLowering::AllocateLiteralRegExp(Node* effect, Node* control,
JSRegExpRef boilerplate) {
MapRef boilerplate_map = boilerplate.map();
// Sanity check that JSRegExp object layout hasn't changed.
STATIC_ASSERT(static_cast<int>(JSRegExp::kDataOffset) ==
static_cast<int>(JSObject::kHeaderSize));
STATIC_ASSERT(JSRegExp::kSourceOffset == JSRegExp::kDataOffset + kTaggedSize);
STATIC_ASSERT(JSRegExp::kFlagsOffset ==
JSRegExp::kSourceOffset + kTaggedSize);
STATIC_ASSERT(JSRegExp::kHeaderSize == JSRegExp::kFlagsOffset + kTaggedSize);
STATIC_ASSERT(JSRegExp::kLastIndexOffset == JSRegExp::kHeaderSize);
STATIC_ASSERT(JSRegExp::kInObjectFieldCount == 1); // LastIndex.
const AllocationType allocation = AllocationType::kYoung;
const int size =
JSRegExp::kHeaderSize + JSRegExp::kInObjectFieldCount * kTaggedSize;
AllocationBuilder builder(jsgraph(), effect, control);
builder.Allocate(size, allocation, Type::For(boilerplate_map));
builder.Store(AccessBuilder::ForMap(), boilerplate_map);
builder.Store(AccessBuilder::ForJSObjectPropertiesOrHash(),
boilerplate.raw_properties_or_hash());
builder.Store(AccessBuilder::ForJSObjectElements(), boilerplate.elements());
builder.Store(AccessBuilder::ForJSRegExpData(), boilerplate.data());
builder.Store(AccessBuilder::ForJSRegExpSource(), boilerplate.source());
builder.Store(AccessBuilder::ForJSRegExpFlags(), boilerplate.flags());
builder.Store(AccessBuilder::ForJSRegExpLastIndex(),
boilerplate.last_index());
return builder.Finish();
}
Factory* JSCreateLowering::factory() const {
return jsgraph()->isolate()->factory();
}
Graph* JSCreateLowering::graph() const { return jsgraph()->graph(); }
CommonOperatorBuilder* JSCreateLowering::common() const {
return jsgraph()->common();
}
SimplifiedOperatorBuilder* JSCreateLowering::simplified() const {
return jsgraph()->simplified();
}
NativeContextRef JSCreateLowering::native_context() const {
return broker()->target_native_context();
}
} // namespace compiler
} // namespace internal
} // namespace v8