| Index: src/compiler/js-create-lowering.cc
|
| diff --git a/src/compiler/js-create-lowering.cc b/src/compiler/js-create-lowering.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..efce670fbfb67c5acad2808f4c65f58945d1b5d9
|
| --- /dev/null
|
| +++ b/src/compiler/js-create-lowering.cc
|
| @@ -0,0 +1,766 @@
|
| +// 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/code-factory.h"
|
| +#include "src/compilation-dependencies.h"
|
| +#include "src/compiler/access-builder.h"
|
| +#include "src/compiler/common-operator.h"
|
| +#include "src/compiler/js-graph.h"
|
| +#include "src/compiler/js-operator.h"
|
| +#include "src/compiler/linkage.h"
|
| +#include "src/compiler/node.h"
|
| +#include "src/compiler/node-properties.h"
|
| +#include "src/compiler/simplified-operator.h"
|
| +#include "src/compiler/state-values-utils.h"
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +namespace compiler {
|
| +
|
| +namespace {
|
| +
|
| +// A helper class to construct inline allocations on the simplified operator
|
| +// level. This keeps track of the effect chain for initial stores on a newly
|
| +// allocated object and also provides helpers for commonly allocated objects.
|
| +class AllocationBuilder final {
|
| + public:
|
| + AllocationBuilder(JSGraph* jsgraph, Node* effect, Node* control)
|
| + : jsgraph_(jsgraph),
|
| + allocation_(nullptr),
|
| + effect_(effect),
|
| + control_(control) {}
|
| +
|
| + // Primitive allocation of static size.
|
| + void Allocate(int size, PretenureFlag pretenure = NOT_TENURED) {
|
| + effect_ = graph()->NewNode(common()->BeginRegion(), effect_);
|
| + allocation_ =
|
| + graph()->NewNode(simplified()->Allocate(pretenure),
|
| + jsgraph()->Constant(size), effect_, control_);
|
| + effect_ = allocation_;
|
| + }
|
| +
|
| + // Primitive store into a field.
|
| + void Store(const FieldAccess& access, Node* value) {
|
| + effect_ = graph()->NewNode(simplified()->StoreField(access), allocation_,
|
| + value, effect_, control_);
|
| + }
|
| +
|
| + // Primitive store into an element.
|
| + void Store(ElementAccess const& access, Node* index, Node* value) {
|
| + effect_ = graph()->NewNode(simplified()->StoreElement(access), allocation_,
|
| + index, value, effect_, control_);
|
| + }
|
| +
|
| + // Compound allocation of a FixedArray.
|
| + void AllocateArray(int length, Handle<Map> map,
|
| + PretenureFlag pretenure = NOT_TENURED) {
|
| + DCHECK(map->instance_type() == FIXED_ARRAY_TYPE ||
|
| + map->instance_type() == FIXED_DOUBLE_ARRAY_TYPE);
|
| + int size = (map->instance_type() == FIXED_ARRAY_TYPE)
|
| + ? FixedArray::SizeFor(length)
|
| + : FixedDoubleArray::SizeFor(length);
|
| + Allocate(size, pretenure);
|
| + Store(AccessBuilder::ForMap(), map);
|
| + Store(AccessBuilder::ForFixedArrayLength(), jsgraph()->Constant(length));
|
| + }
|
| +
|
| + // Compound store of a constant into a field.
|
| + void Store(const FieldAccess& access, Handle<Object> value) {
|
| + Store(access, jsgraph()->Constant(value));
|
| + }
|
| +
|
| + void FinishAndChange(Node* node) {
|
| + NodeProperties::SetType(allocation_, NodeProperties::GetType(node));
|
| + node->ReplaceInput(0, allocation_);
|
| + node->ReplaceInput(1, effect_);
|
| + node->TrimInputCount(2);
|
| + NodeProperties::ChangeOp(node, common()->FinishRegion());
|
| + }
|
| +
|
| + Node* Finish() {
|
| + return graph()->NewNode(common()->FinishRegion(), allocation_, effect_);
|
| + }
|
| +
|
| + protected:
|
| + JSGraph* jsgraph() { return jsgraph_; }
|
| + Graph* graph() { return jsgraph_->graph(); }
|
| + CommonOperatorBuilder* common() { return jsgraph_->common(); }
|
| + SimplifiedOperatorBuilder* simplified() { return jsgraph_->simplified(); }
|
| +
|
| + private:
|
| + JSGraph* const jsgraph_;
|
| + Node* allocation_;
|
| + Node* effect_;
|
| + Node* control_;
|
| +};
|
| +
|
| +// Retrieves the frame state holding actual argument values.
|
| +Node* GetArgumentsFrameState(Node* frame_state) {
|
| + Node* const outer_state = NodeProperties::GetFrameStateInput(frame_state, 0);
|
| + FrameStateInfo outer_state_info = OpParameter<FrameStateInfo>(outer_state);
|
| + return outer_state_info.type() == FrameStateType::kArgumentsAdaptor
|
| + ? outer_state
|
| + : frame_state;
|
| +}
|
| +
|
| +// Maximum instance size for which allocations will be inlined.
|
| +const int kMaxInlineInstanceSize = 64 * kPointerSize;
|
| +
|
| +// Checks whether allocation using the given constructor can be inlined.
|
| +bool IsAllocationInlineable(Handle<JSFunction> constructor) {
|
| + // TODO(bmeurer): Further relax restrictions on inlining, i.e.
|
| + // instance type and maybe instance size (inobject properties
|
| + // are limited anyways by the runtime).
|
| + return constructor->has_initial_map() &&
|
| + constructor->initial_map()->instance_type() == JS_OBJECT_TYPE &&
|
| + constructor->initial_map()->instance_size() < kMaxInlineInstanceSize;
|
| +}
|
| +
|
| +// 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) {
|
| + switch (node->opcode()) {
|
| + case IrOpcode::kJSCreate:
|
| + return ReduceJSCreate(node);
|
| + case IrOpcode::kJSCreateArguments:
|
| + return ReduceJSCreateArguments(node);
|
| + case IrOpcode::kJSCreateArray:
|
| + return ReduceJSCreateArray(node);
|
| + case IrOpcode::kJSCreateIterResultObject:
|
| + return ReduceJSCreateIterResultObject(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);
|
| + default:
|
| + break;
|
| + }
|
| + return NoChange();
|
| +}
|
| +
|
| +Reduction JSCreateLowering::ReduceJSCreate(Node* node) {
|
| + DCHECK_EQ(IrOpcode::kJSCreate, node->opcode());
|
| + Node* const target = NodeProperties::GetValueInput(node, 0);
|
| + Type* const target_type = NodeProperties::GetType(target);
|
| + Node* const new_target = NodeProperties::GetValueInput(node, 1);
|
| + Node* const effect = NodeProperties::GetEffectInput(node);
|
| + // TODO(turbofan): Add support for NewTarget passed to JSCreate.
|
| + if (target != new_target) return NoChange();
|
| + // Extract constructor function.
|
| + if (target_type->IsConstant() &&
|
| + target_type->AsConstant()->Value()->IsJSFunction()) {
|
| + Handle<JSFunction> constructor =
|
| + Handle<JSFunction>::cast(target_type->AsConstant()->Value());
|
| + DCHECK(constructor->IsConstructor());
|
| + // Force completion of inobject slack tracking before
|
| + // generating code to finalize the instance size.
|
| + constructor->CompleteInobjectSlackTrackingIfActive();
|
| +
|
| + // TODO(bmeurer): We fall back to the runtime in case we cannot inline
|
| + // the allocation here, which is sort of expensive. We should think about
|
| + // a soft fallback to some NewObjectCodeStub.
|
| + if (IsAllocationInlineable(constructor)) {
|
| + // Compute instance size from initial map of {constructor}.
|
| + Handle<Map> initial_map(constructor->initial_map(), isolate());
|
| + int const instance_size = initial_map->instance_size();
|
| +
|
| + // Add a dependency on the {initial_map} to make sure that this code is
|
| + // deoptimized whenever the {initial_map} of the {constructor} changes.
|
| + dependencies()->AssumeInitialMapCantChange(initial_map);
|
| +
|
| + // Emit code to allocate the JSObject instance for the {constructor}.
|
| + AllocationBuilder a(jsgraph(), effect, graph()->start());
|
| + a.Allocate(instance_size);
|
| + a.Store(AccessBuilder::ForMap(), initial_map);
|
| + a.Store(AccessBuilder::ForJSObjectProperties(),
|
| + jsgraph()->EmptyFixedArrayConstant());
|
| + a.Store(AccessBuilder::ForJSObjectElements(),
|
| + jsgraph()->EmptyFixedArrayConstant());
|
| + for (int i = 0; i < initial_map->GetInObjectProperties(); ++i) {
|
| + a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
|
| + jsgraph()->UndefinedConstant());
|
| + }
|
| + a.FinishAndChange(node);
|
| + return Changed(node);
|
| + }
|
| + }
|
| + return NoChange();
|
| +}
|
| +
|
| +Reduction JSCreateLowering::ReduceJSCreateArguments(Node* node) {
|
| + DCHECK_EQ(IrOpcode::kJSCreateArguments, node->opcode());
|
| + CreateArgumentsType type = CreateArgumentsTypeOf(node->op());
|
| + Node* const frame_state = NodeProperties::GetFrameStateInput(node, 0);
|
| + Node* const outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
|
| + FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state);
|
| +
|
| + // 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) {
|
| + if (type != CreateArgumentsType::kRestParameter) {
|
| + // TODO(bmeurer): Cleanup this mess at some point.
|
| + int parameter_count = state_info.parameter_count() - 1;
|
| + int parameter_offset = parameter_count * kPointerSize;
|
| + int offset = StandardFrameConstants::kCallerSPOffset + parameter_offset;
|
| + Node* parameter_pointer = graph()->NewNode(
|
| + machine()->IntAdd(), graph()->NewNode(machine()->LoadFramePointer()),
|
| + jsgraph()->IntPtrConstant(offset));
|
| + Handle<SharedFunctionInfo> shared;
|
| + if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
|
| + bool unmapped = type == CreateArgumentsType::kUnmappedArguments;
|
| + Callable callable = CodeFactory::ArgumentsAccess(
|
| + isolate(), unmapped, shared->has_duplicate_parameters());
|
| + CallDescriptor* desc = Linkage::GetStubCallDescriptor(
|
| + isolate(), graph()->zone(), callable.descriptor(), 0,
|
| + CallDescriptor::kNeedsFrameState);
|
| + const Operator* new_op = common()->Call(desc);
|
| + Node* stub_code = jsgraph()->HeapConstant(callable.code());
|
| + node->InsertInput(graph()->zone(), 0, stub_code);
|
| + node->InsertInput(graph()->zone(), 2,
|
| + jsgraph()->Constant(parameter_count));
|
| + node->InsertInput(graph()->zone(), 3, parameter_pointer);
|
| + NodeProperties::ChangeOp(node, new_op);
|
| + return Changed(node);
|
| + } else {
|
| + Callable callable = CodeFactory::FastNewRestParameter(isolate());
|
| + CallDescriptor* desc = Linkage::GetStubCallDescriptor(
|
| + isolate(), graph()->zone(), callable.descriptor(), 0,
|
| + CallDescriptor::kNeedsFrameState);
|
| + const Operator* new_op = common()->Call(desc);
|
| + Node* stub_code = jsgraph()->HeapConstant(callable.code());
|
| + node->InsertInput(graph()->zone(), 0, stub_code);
|
| + NodeProperties::ChangeOp(node, new_op);
|
| + return Changed(node);
|
| + }
|
| + } else if (outer_state->opcode() == IrOpcode::kFrameState) {
|
| + // Use inline allocation for all mapped arguments objects within inlined
|
| + // (i.e. non-outermost) frames, independent of the object size.
|
| + if (type == CreateArgumentsType::kMappedArguments) {
|
| + Handle<SharedFunctionInfo> shared;
|
| + if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
|
| + Node* const callee = NodeProperties::GetValueInput(node, 0);
|
| + Node* const control = NodeProperties::GetControlInput(node);
|
| + Node* const context = NodeProperties::GetContextInput(node);
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| + // TODO(mstarzinger): 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);
|
| + FrameStateInfo args_state_info = OpParameter<FrameStateInfo>(args_state);
|
| + // 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 from the current native context.
|
| + Node* const load_native_context = effect = graph()->NewNode(
|
| + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true),
|
| + context, context, effect);
|
| + Node* const load_arguments_map = effect = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForContextSlot(
|
| + has_aliased_arguments ? Context::FAST_ALIASED_ARGUMENTS_MAP_INDEX
|
| + : Context::SLOPPY_ARGUMENTS_MAP_INDEX)),
|
| + load_native_context, effect, control);
|
| + // Actually allocate and initialize the arguments object.
|
| + AllocationBuilder a(jsgraph(), effect, control);
|
| + Node* properties = jsgraph()->EmptyFixedArrayConstant();
|
| + int length = args_state_info.parameter_count() - 1; // Minus receiver.
|
| + STATIC_ASSERT(Heap::kSloppyArgumentsObjectSize == 5 * kPointerSize);
|
| + a.Allocate(Heap::kSloppyArgumentsObjectSize);
|
| + a.Store(AccessBuilder::ForMap(), load_arguments_map);
|
| + a.Store(AccessBuilder::ForJSObjectProperties(), properties);
|
| + 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);
|
| + } else if (type == CreateArgumentsType::kUnmappedArguments) {
|
| + // Use inline allocation for all unmapped arguments objects within inlined
|
| + // (i.e. non-outermost) frames, independent of the object size.
|
| + Node* const control = NodeProperties::GetControlInput(node);
|
| + Node* const context = NodeProperties::GetContextInput(node);
|
| + 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);
|
| + FrameStateInfo args_state_info = OpParameter<FrameStateInfo>(args_state);
|
| + // 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 from the current native context.
|
| + Node* const load_native_context = effect = graph()->NewNode(
|
| + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true),
|
| + context, context, effect);
|
| + Node* const load_arguments_map = effect = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForContextSlot(
|
| + Context::STRICT_ARGUMENTS_MAP_INDEX)),
|
| + load_native_context, effect, control);
|
| + // Actually allocate and initialize the arguments object.
|
| + AllocationBuilder a(jsgraph(), effect, control);
|
| + Node* properties = jsgraph()->EmptyFixedArrayConstant();
|
| + int length = args_state_info.parameter_count() - 1; // Minus receiver.
|
| + STATIC_ASSERT(Heap::kStrictArgumentsObjectSize == 4 * kPointerSize);
|
| + a.Allocate(Heap::kStrictArgumentsObjectSize);
|
| + a.Store(AccessBuilder::ForMap(), load_arguments_map);
|
| + a.Store(AccessBuilder::ForJSObjectProperties(), properties);
|
| + a.Store(AccessBuilder::ForJSObjectElements(), elements);
|
| + a.Store(AccessBuilder::ForArgumentsLength(), jsgraph()->Constant(length));
|
| + RelaxControls(node);
|
| + a.FinishAndChange(node);
|
| + return Changed(node);
|
| + } else if (type == CreateArgumentsType::kRestParameter) {
|
| + Handle<SharedFunctionInfo> shared;
|
| + if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
|
| + 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* const control = NodeProperties::GetControlInput(node);
|
| + Node* const context = NodeProperties::GetContextInput(node);
|
| + 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);
|
| + FrameStateInfo args_state_info = OpParameter<FrameStateInfo>(args_state);
|
| + // 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 from the current native context.
|
| + Node* const load_native_context = effect = graph()->NewNode(
|
| + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true),
|
| + context, context, effect);
|
| + Node* const load_jsarray_map = effect = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForContextSlot(
|
| + Context::JS_ARRAY_FAST_ELEMENTS_MAP_INDEX)),
|
| + load_native_context, effect, control);
|
| + // Actually allocate and initialize the jsarray.
|
| + AllocationBuilder a(jsgraph(), effect, control);
|
| + Node* properties = jsgraph()->EmptyFixedArrayConstant();
|
| +
|
| + // -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::kSize == 4 * kPointerSize);
|
| + a.Allocate(JSArray::kSize);
|
| + a.Store(AccessBuilder::ForMap(), load_jsarray_map);
|
| + a.Store(AccessBuilder::ForJSObjectProperties(), properties);
|
| + a.Store(AccessBuilder::ForJSObjectElements(), elements);
|
| + a.Store(AccessBuilder::ForJSArrayLength(FAST_ELEMENTS),
|
| + jsgraph()->Constant(length));
|
| + RelaxControls(node);
|
| + a.FinishAndChange(node);
|
| + return Changed(node);
|
| + }
|
| + }
|
| +
|
| + return NoChange();
|
| +}
|
| +
|
| +Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length,
|
| + int capacity,
|
| + Handle<AllocationSite> site) {
|
| + DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode());
|
| + Node* context = NodeProperties::GetContextInput(node);
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| + Node* control = NodeProperties::GetControlInput(node);
|
| +
|
| + // Extract transition and tenuring feedback from the {site} and add
|
| + // appropriate code dependencies on the {site} if deoptimization is
|
| + // enabled.
|
| + PretenureFlag pretenure = site->GetPretenureMode();
|
| + ElementsKind elements_kind = site->GetElementsKind();
|
| + DCHECK(IsFastElementsKind(elements_kind));
|
| + dependencies()->AssumeTenuringDecision(site);
|
| + dependencies()->AssumeTransitionStable(site);
|
| +
|
| + // Retrieve the initial map for the array from the appropriate native context.
|
| + Node* native_context = effect = graph()->NewNode(
|
| + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true),
|
| + context, context, effect);
|
| + Node* js_array_map = effect = graph()->NewNode(
|
| + javascript()->LoadContext(0, Context::ArrayMapIndex(elements_kind), true),
|
| + native_context, native_context, effect);
|
| +
|
| + // Setup elements and properties.
|
| + Node* elements;
|
| + if (capacity == 0) {
|
| + elements = jsgraph()->EmptyFixedArrayConstant();
|
| + } else {
|
| + elements = effect =
|
| + AllocateElements(effect, control, elements_kind, capacity, pretenure);
|
| + }
|
| + Node* properties = jsgraph()->EmptyFixedArrayConstant();
|
| +
|
| + // Perform the allocation of the actual JSArray object.
|
| + AllocationBuilder a(jsgraph(), effect, control);
|
| + a.Allocate(JSArray::kSize, pretenure);
|
| + a.Store(AccessBuilder::ForMap(), js_array_map);
|
| + a.Store(AccessBuilder::ForJSObjectProperties(), properties);
|
| + a.Store(AccessBuilder::ForJSObjectElements(), elements);
|
| + a.Store(AccessBuilder::ForJSArrayLength(elements_kind), length);
|
| + 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());
|
| + Node* target = NodeProperties::GetValueInput(node, 0);
|
| + Node* new_target = NodeProperties::GetValueInput(node, 1);
|
| +
|
| + // TODO(bmeurer): Optimize the subclassing case.
|
| + if (target != new_target) return NoChange();
|
| +
|
| + // Check if we have a feedback {site} on the {node}.
|
| + Handle<AllocationSite> site = p.site();
|
| + if (p.site().is_null()) return NoChange();
|
| +
|
| + // Attempt to inline calls to the Array constructor for the relevant cases
|
| + // where either no arguments are provided, or exactly one unsigned number
|
| + // argument is given.
|
| + if (site->CanInlineCall()) {
|
| + if (p.arity() == 0) {
|
| + Node* length = jsgraph()->ZeroConstant();
|
| + int capacity = JSArray::kPreallocatedArrayElements;
|
| + return ReduceNewArray(node, length, capacity, site);
|
| + } else if (p.arity() == 1) {
|
| + Node* length = NodeProperties::GetValueInput(node, 2);
|
| + Type* length_type = NodeProperties::GetType(length);
|
| + if (length_type->Is(Type::SignedSmall()) &&
|
| + length_type->Min() >= 0 &&
|
| + length_type->Max() <= kElementLoopUnrollLimit) {
|
| + int capacity = static_cast<int>(length_type->Max());
|
| + return ReduceNewArray(node, length, capacity, site);
|
| + }
|
| + }
|
| + }
|
| +
|
| + return NoChange();
|
| +}
|
| +
|
| +Reduction JSCreateLowering::ReduceJSCreateIterResultObject(Node* node) {
|
| + DCHECK_EQ(IrOpcode::kJSCreateIterResultObject, node->opcode());
|
| + Node* value = NodeProperties::GetValueInput(node, 0);
|
| + Node* done = NodeProperties::GetValueInput(node, 1);
|
| + Node* context = NodeProperties::GetContextInput(node);
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| +
|
| + // Load the JSIteratorResult map for the {context}.
|
| + Node* native_context = effect = graph()->NewNode(
|
| + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true),
|
| + context, context, effect);
|
| + Node* iterator_result_map = effect = graph()->NewNode(
|
| + javascript()->LoadContext(0, Context::ITERATOR_RESULT_MAP_INDEX, true),
|
| + native_context, native_context, effect);
|
| +
|
| + // 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::ForJSObjectProperties(),
|
| + jsgraph()->EmptyFixedArrayConstant());
|
| + a.Store(AccessBuilder::ForJSObjectElements(),
|
| + jsgraph()->EmptyFixedArrayConstant());
|
| + a.Store(AccessBuilder::ForJSIteratorResultValue(), value);
|
| + a.Store(AccessBuilder::ForJSIteratorResultDone(), done);
|
| + STATIC_ASSERT(JSIteratorResult::kSize == 5 * kPointerSize);
|
| + a.FinishAndChange(node);
|
| + return Changed(node);
|
| +}
|
| +
|
| +Reduction JSCreateLowering::ReduceJSCreateFunctionContext(Node* node) {
|
| + DCHECK_EQ(IrOpcode::kJSCreateFunctionContext, node->opcode());
|
| + int slot_count = OpParameter<int>(node->op());
|
| + Node* const closure = NodeProperties::GetValueInput(node, 0);
|
| +
|
| + // 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);
|
| + Node* extension = jsgraph()->TheHoleConstant();
|
| + Node* native_context = effect = graph()->NewNode(
|
| + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true),
|
| + context, context, effect);
|
| + AllocationBuilder a(jsgraph(), effect, control);
|
| + STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered.
|
| + int context_length = slot_count + Context::MIN_CONTEXT_SLOTS;
|
| + a.AllocateArray(context_length, factory()->function_context_map());
|
| + a.Store(AccessBuilder::ForContextSlot(Context::CLOSURE_INDEX), closure);
|
| + a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context);
|
| + a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), extension);
|
| + a.Store(AccessBuilder::ForContextSlot(Context::NATIVE_CONTEXT_INDEX),
|
| + native_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());
|
| + Node* object = NodeProperties::GetValueInput(node, 0);
|
| + Node* closure = NodeProperties::GetValueInput(node, 1);
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| + Node* control = NodeProperties::GetControlInput(node);
|
| + Node* context = NodeProperties::GetContextInput(node);
|
| + Node* native_context = effect = graph()->NewNode(
|
| + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true),
|
| + context, context, effect);
|
| + AllocationBuilder a(jsgraph(), effect, control);
|
| + STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered.
|
| + a.AllocateArray(Context::MIN_CONTEXT_SLOTS, factory()->with_context_map());
|
| + a.Store(AccessBuilder::ForContextSlot(Context::CLOSURE_INDEX), closure);
|
| + a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context);
|
| + a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), object);
|
| + a.Store(AccessBuilder::ForContextSlot(Context::NATIVE_CONTEXT_INDEX),
|
| + native_context);
|
| + RelaxControls(node);
|
| + a.FinishAndChange(node);
|
| + return Changed(node);
|
| +}
|
| +
|
| +Reduction JSCreateLowering::ReduceJSCreateCatchContext(Node* node) {
|
| + DCHECK_EQ(IrOpcode::kJSCreateCatchContext, node->opcode());
|
| + Handle<String> name = OpParameter<Handle<String>>(node);
|
| + Node* exception = NodeProperties::GetValueInput(node, 0);
|
| + Node* closure = NodeProperties::GetValueInput(node, 1);
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| + Node* control = NodeProperties::GetControlInput(node);
|
| + Node* context = NodeProperties::GetContextInput(node);
|
| + Node* native_context = effect = graph()->NewNode(
|
| + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true),
|
| + context, context, effect);
|
| + AllocationBuilder a(jsgraph(), effect, control);
|
| + STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered.
|
| + a.AllocateArray(Context::MIN_CONTEXT_SLOTS + 1,
|
| + factory()->catch_context_map());
|
| + a.Store(AccessBuilder::ForContextSlot(Context::CLOSURE_INDEX), closure);
|
| + a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context);
|
| + a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), name);
|
| + a.Store(AccessBuilder::ForContextSlot(Context::NATIVE_CONTEXT_INDEX),
|
| + native_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());
|
| + Handle<ScopeInfo> scope_info = OpParameter<Handle<ScopeInfo>>(node);
|
| + int const context_length = scope_info->ContextLength();
|
| + Node* const closure = NodeProperties::GetValueInput(node, 0);
|
| +
|
| + // 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);
|
| + Node* extension = jsgraph()->Constant(scope_info);
|
| + Node* native_context = effect = graph()->NewNode(
|
| + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true),
|
| + context, context, effect);
|
| + AllocationBuilder a(jsgraph(), effect, control);
|
| + STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered.
|
| + a.AllocateArray(context_length, factory()->block_context_map());
|
| + a.Store(AccessBuilder::ForContextSlot(Context::CLOSURE_INDEX), closure);
|
| + a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context);
|
| + a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), extension);
|
| + a.Store(AccessBuilder::ForContextSlot(Context::NATIVE_CONTEXT_INDEX),
|
| + native_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();
|
| +}
|
| +
|
| +// 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 = OpParameter<FrameStateInfo>(frame_state);
|
| + 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, factory()->fixed_array_map());
|
| + for (int i = 0; i < argument_count; ++i, ++parameters_it) {
|
| + a.Store(AccessBuilder::ForFixedArraySlot(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 = OpParameter<FrameStateInfo>(frame_state);
|
| + 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, factory()->fixed_array_map());
|
| + for (int i = 0; i < num_elements; ++i, ++parameters_it) {
|
| + a.Store(AccessBuilder::ForFixedArraySlot(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,
|
| + Handle<SharedFunctionInfo> shared, bool* has_aliased_arguments) {
|
| + FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state);
|
| + 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 paratemers_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, factory()->fixed_array_map());
|
| + for (int i = 0; i < mapped_count; ++i, ++paratemers_it) {
|
| + aa.Store(AccessBuilder::ForFixedArraySlot(i), jsgraph()->TheHoleConstant());
|
| + }
|
| + for (int i = mapped_count; i < argument_count; ++i, ++paratemers_it) {
|
| + aa.Store(AccessBuilder::ForFixedArraySlot(i), (*paratemers_it).node);
|
| + }
|
| + Node* arguments = aa.Finish();
|
| +
|
| + // Actually allocate the backing store.
|
| + AllocationBuilder a(jsgraph(), arguments, control);
|
| + a.AllocateArray(mapped_count + 2, factory()->sloppy_arguments_elements_map());
|
| + a.Store(AccessBuilder::ForFixedArraySlot(0), context);
|
| + a.Store(AccessBuilder::ForFixedArraySlot(1), arguments);
|
| + for (int i = 0; i < mapped_count; ++i) {
|
| + int idx = Context::MIN_CONTEXT_SLOTS + parameter_count - 1 - i;
|
| + a.Store(AccessBuilder::ForFixedArraySlot(i + 2), jsgraph()->Constant(idx));
|
| + }
|
| + return a.Finish();
|
| +}
|
| +
|
| +Node* JSCreateLowering::AllocateElements(Node* effect, Node* control,
|
| + ElementsKind elements_kind,
|
| + int capacity,
|
| + PretenureFlag pretenure) {
|
| + DCHECK_LE(1, capacity);
|
| + DCHECK_LE(capacity, JSArray::kInitialMaxFastElementArray);
|
| +
|
| + Handle<Map> elements_map = IsFastDoubleElementsKind(elements_kind)
|
| + ? factory()->fixed_double_array_map()
|
| + : factory()->fixed_array_map();
|
| + ElementAccess access = IsFastDoubleElementsKind(elements_kind)
|
| + ? AccessBuilder::ForFixedDoubleArrayElement()
|
| + : AccessBuilder::ForFixedArrayElement();
|
| + Node* value =
|
| + IsFastDoubleElementsKind(elements_kind)
|
| + ? jsgraph()->Float64Constant(bit_cast<double>(kHoleNanInt64))
|
| + : jsgraph()->TheHoleConstant();
|
| +
|
| + // Actually allocate the backing store.
|
| + AllocationBuilder a(jsgraph(), effect, control);
|
| + a.AllocateArray(capacity, elements_map, pretenure);
|
| + for (int i = 0; i < capacity; ++i) {
|
| + Node* index = jsgraph()->Constant(i);
|
| + a.Store(access, index, value);
|
| + }
|
| + return a.Finish();
|
| +}
|
| +
|
| +Factory* JSCreateLowering::factory() const { return isolate()->factory(); }
|
| +
|
| +Graph* JSCreateLowering::graph() const { return jsgraph()->graph(); }
|
| +
|
| +Isolate* JSCreateLowering::isolate() const { return jsgraph()->isolate(); }
|
| +
|
| +JSOperatorBuilder* JSCreateLowering::javascript() const {
|
| + return jsgraph()->javascript();
|
| +}
|
| +
|
| +CommonOperatorBuilder* JSCreateLowering::common() const {
|
| + return jsgraph()->common();
|
| +}
|
| +
|
| +SimplifiedOperatorBuilder* JSCreateLowering::simplified() const {
|
| + return jsgraph()->simplified();
|
| +}
|
| +
|
| +MachineOperatorBuilder* JSCreateLowering::machine() const {
|
| + return jsgraph()->machine();
|
| +}
|
| +
|
| +} // namespace compiler
|
| +} // namespace internal
|
| +} // namespace v8
|
|
|