[turbofan] Introduce JSCreateLowering for optimizing JSCreate nodes.

This moves the JSCreate related functionality from JSTypedLowering into
a dedicated JSCreateLowering reducer. This is in preparation of landing
the support for optimized literals in TurboFan, which would blow up
JSTypedLowering quite seriously otherwise.

R=mstarzinger@chromium.org

Review URL: https://codereview.chromium.org/1678833002

Cr-Commit-Position: refs/heads/master@{#33813}
This commit is contained in:
bmeurer 2016-02-08 04:33:44 -08:00 committed by Commit bot
parent 4d46b510ca
commit 07e9921f5a
11 changed files with 1094 additions and 958 deletions

View File

@ -795,6 +795,8 @@ source_set("v8_base") {
"src/compiler/js-context-relaxation.h",
"src/compiler/js-context-specialization.cc",
"src/compiler/js-context-specialization.h",
"src/compiler/js-create-lowering.cc",
"src/compiler/js-create-lowering.h",
"src/compiler/js-frame-specialization.cc",
"src/compiler/js-frame-specialization.h",
"src/compiler/js-generic-lowering.cc",

View File

@ -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

View File

@ -0,0 +1,80 @@
// 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.
#ifndef V8_COMPILER_JS_CREATE_LOWERING_H_
#define V8_COMPILER_JS_CREATE_LOWERING_H_
#include "src/compiler/graph-reducer.h"
namespace v8 {
namespace internal {
// Forward declarations.
class CompilationDependencies;
class Factory;
namespace compiler {
// Forward declarations.
class CommonOperatorBuilder;
class JSGraph;
class JSOperatorBuilder;
class MachineOperatorBuilder;
class SimplifiedOperatorBuilder;
// Lowers JSCreate-level operators to fast (inline) allocations.
class JSCreateLowering final : public AdvancedReducer {
public:
JSCreateLowering(Editor* editor, CompilationDependencies* dependencies,
JSGraph* jsgraph)
: AdvancedReducer(editor),
dependencies_(dependencies),
jsgraph_(jsgraph) {}
~JSCreateLowering() final {}
Reduction Reduce(Node* node) final;
private:
Reduction ReduceJSCreate(Node* node);
Reduction ReduceJSCreateArguments(Node* node);
Reduction ReduceJSCreateArray(Node* node);
Reduction ReduceJSCreateIterResultObject(Node* node);
Reduction ReduceJSCreateFunctionContext(Node* node);
Reduction ReduceJSCreateWithContext(Node* node);
Reduction ReduceJSCreateCatchContext(Node* node);
Reduction ReduceJSCreateBlockContext(Node* node);
Reduction ReduceNewArray(Node* node, Node* length, int capacity,
Handle<AllocationSite> site);
Node* AllocateArguments(Node* effect, Node* control, Node* frame_state);
Node* AllocateRestArguments(Node* effect, Node* control, Node* frame_state,
int start_index);
Node* AllocateAliasedArguments(Node* effect, Node* control, Node* frame_state,
Node* context, Handle<SharedFunctionInfo>,
bool* has_aliased_arguments);
Node* AllocateElements(Node* effect, Node* control,
ElementsKind elements_kind, int capacity,
PretenureFlag pretenure);
Factory* factory() const;
Graph* graph() const;
JSGraph* jsgraph() const { return jsgraph_; }
Isolate* isolate() const;
JSOperatorBuilder* javascript() const;
CommonOperatorBuilder* common() const;
SimplifiedOperatorBuilder* simplified() const;
MachineOperatorBuilder* machine() const;
CompilationDependencies* dependencies() const { return dependencies_; }
CompilationDependencies* const dependencies_;
JSGraph* const jsgraph_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_JS_CREATE_LOWERING_H_

View File

@ -11,7 +11,6 @@
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/state-values-utils.h"
#include "src/type-cache.h"
#include "src/types.h"
@ -19,86 +18,6 @@ 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_;
};
} // namespace
// A helper class to simplify the process of reducing a single binop node with a
// JSOperator. This class manages the rewriting of context, control, and effect
// dependencies during lowering of a binop and contains numerous helper
@ -1432,505 +1351,6 @@ Reduction JSTypedLowering::ReduceJSConvertReceiver(Node* node) {
}
namespace {
// 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;
}
} // namespace
Reduction JSTypedLowering::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();
}
namespace {
// Retrieves the frame state holding actual argument values.
Node* GetArgumentsFrameState(Node* frame_state) {
Node* const outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
FrameStateInfo outer_state_info = OpParameter<FrameStateInfo>(outer_state);
return outer_state_info.type() == FrameStateType::kArgumentsAdaptor
? outer_state
: frame_state;
}
} // namespace
Reduction JSTypedLowering::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.
Isolate* isolate = jsgraph()->isolate();
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 JSTypedLowering::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));
if (flags() & kDeoptimizationEnabled) {
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 JSTypedLowering::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_cache_.kElementLoopUnrollType)) {
int capacity = static_cast<int>(length_type->Max());
return ReduceNewArray(node, length, capacity, site);
}
}
}
return NoChange();
}
Reduction JSTypedLowering::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 JSTypedLowering::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 JSTypedLowering::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 JSTypedLowering::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 JSTypedLowering::ReduceJSCreateBlockContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateBlockContext, node->opcode());
Handle<ScopeInfo> scope_info = OpParameter<Handle<ScopeInfo>>(node);
int 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();
}
Reduction JSTypedLowering::ReduceJSCallConstruct(Node* node) {
DCHECK_EQ(IrOpcode::kJSCallConstruct, node->opcode());
CallConstructParameters const& p = CallConstructParametersOf(node->op());
@ -2313,22 +1733,6 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSStoreContext(node);
case IrOpcode::kJSConvertReceiver:
return ReduceJSConvertReceiver(node);
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);
case IrOpcode::kJSCallConstruct:
return ReduceJSCallConstruct(node);
case IrOpcode::kJSCallFunction:
@ -2355,139 +1759,6 @@ Node* JSTypedLowering::Word32Shl(Node* const lhs, int32_t const rhs) {
}
// Helper that allocates a FixedArray holding argument values recorded in the
// given {frame_state}. Serves as backing store for JSCreateArguments nodes.
Node* JSTypedLowering::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* JSTypedLowering::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* JSTypedLowering::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* JSTypedLowering::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* JSTypedLowering::factory() const { return jsgraph()->factory(); }

View File

@ -68,14 +68,6 @@ class JSTypedLowering final : public AdvancedReducer {
Reduction ReduceJSToString(Node* node);
Reduction ReduceJSToObject(Node* node);
Reduction ReduceJSConvertReceiver(Node* node);
Reduction ReduceJSCreate(Node* node);
Reduction ReduceJSCreateArguments(Node* node);
Reduction ReduceJSCreateArray(Node* node);
Reduction ReduceJSCreateIterResultObject(Node* node);
Reduction ReduceJSCreateFunctionContext(Node* node);
Reduction ReduceJSCreateWithContext(Node* node);
Reduction ReduceJSCreateCatchContext(Node* node);
Reduction ReduceJSCreateBlockContext(Node* node);
Reduction ReduceJSCallConstruct(Node* node);
Reduction ReduceJSCallFunction(Node* node);
Reduction ReduceJSForInDone(Node* node);
@ -86,19 +78,8 @@ class JSTypedLowering final : public AdvancedReducer {
Reduction ReduceInt32Binop(Node* node, const Operator* intOp);
Reduction ReduceUI32Shift(Node* node, Signedness left_signedness,
const Operator* shift_op);
Reduction ReduceNewArray(Node* node, Node* length, int capacity,
Handle<AllocationSite> site);
Node* Word32Shl(Node* const lhs, int32_t const rhs);
Node* AllocateArguments(Node* effect, Node* control, Node* frame_state);
Node* AllocateRestArguments(Node* effect, Node* control, Node* frame_state,
int start_index);
Node* AllocateAliasedArguments(Node* effect, Node* control, Node* frame_state,
Node* context, Handle<SharedFunctionInfo>,
bool* has_aliased_arguments);
Node* AllocateElements(Node* effect, Node* control,
ElementsKind elements_kind, int capacity,
PretenureFlag pretenure);
Factory* factory() const;
Graph* graph() const;
@ -111,10 +92,6 @@ class JSTypedLowering final : public AdvancedReducer {
CompilationDependencies* dependencies() const;
Flags flags() const { return flags_; }
// Limits up to which context allocations are inlined.
static const int kFunctionContextAllocationLimit = 16;
static const int kBlockContextAllocationLimit = 16;
CompilationDependencies* dependencies_;
Flags flags_;
JSGraph* jsgraph_;

View File

@ -32,6 +32,7 @@
#include "src/compiler/js-call-reducer.h"
#include "src/compiler/js-context-relaxation.h"
#include "src/compiler/js-context-specialization.h"
#include "src/compiler/js-create-lowering.h"
#include "src/compiler/js-frame-specialization.h"
#include "src/compiler/js-generic-lowering.h"
#include "src/compiler/js-global-object-specialization.h"
@ -612,6 +613,8 @@ struct TypedLoweringPhase {
data->common());
LoadElimination load_elimination(&graph_reducer);
JSBuiltinReducer builtin_reducer(&graph_reducer, data->jsgraph());
JSCreateLowering create_lowering(
&graph_reducer, data->info()->dependencies(), data->jsgraph());
JSTypedLowering::Flags typed_lowering_flags = JSTypedLowering::kNoFlags;
if (data->info()->is_deoptimization_enabled()) {
typed_lowering_flags |= JSTypedLowering::kDeoptimizationEnabled;
@ -631,6 +634,9 @@ struct TypedLoweringPhase {
data->common(), data->machine());
AddReducer(data, &graph_reducer, &dead_code_elimination);
AddReducer(data, &graph_reducer, &builtin_reducer);
if (data->info()->is_deoptimization_enabled()) {
AddReducer(data, &graph_reducer, &create_lowering);
}
AddReducer(data, &graph_reducer, &typed_lowering);
AddReducer(data, &graph_reducer, &intrinsic_lowering);
AddReducer(data, &graph_reducer, &load_elimination);

View File

@ -112,10 +112,6 @@ class TypeCache final {
Type* const kStringLengthType =
CreateNative(CreateRange(0.0, String::kMaxLength), Type::TaggedSigned());
// When initializing arrays, we'll unfold the loop if the number of
// elements is known to be of this type.
Type* const kElementLoopUnrollType = CreateRange(0.0, 16.0);
#define TYPED_ARRAY(TypeName, type_name, TYPE_NAME, ctype, size) \
Type* const k##TypeName##Array = CreateArray(k##TypeName);
TYPED_ARRAYS(TYPED_ARRAY)

View File

@ -0,0 +1,237 @@
// 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/compiler/access-builder.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/operator-properties.h"
#include "src/isolate-inl.h"
#include "test/unittests/compiler/compiler-test-utils.h"
#include "test/unittests/compiler/graph-unittest.h"
#include "test/unittests/compiler/node-test-utils.h"
#include "testing/gmock-support.h"
using testing::_;
using testing::BitEq;
using testing::IsNaN;
namespace v8 {
namespace internal {
namespace compiler {
class JSCreateLoweringTest : public TypedGraphTest {
public:
JSCreateLoweringTest()
: TypedGraphTest(3), javascript_(zone()), deps_(isolate(), zone()) {}
~JSCreateLoweringTest() override {}
protected:
Reduction Reduce(Node* node) {
MachineOperatorBuilder machine(zone());
SimplifiedOperatorBuilder simplified(zone());
JSGraph jsgraph(isolate(), graph(), common(), javascript(), &simplified,
&machine);
// TODO(titzer): mock the GraphReducer here for better unit testing.
GraphReducer graph_reducer(zone(), graph());
JSCreateLowering reducer(&graph_reducer, &deps_, &jsgraph);
return reducer.Reduce(node);
}
Node* FrameState(Handle<SharedFunctionInfo> shared, Node* outer_frame_state) {
Node* state_values = graph()->NewNode(common()->StateValues(0));
return graph()->NewNode(
common()->FrameState(BailoutId::None(),
OutputFrameStateCombine::Ignore(),
common()->CreateFrameStateFunctionInfo(
FrameStateType::kJavaScriptFunction, 1, 0,
shared, CALL_MAINTAINS_NATIVE_CONTEXT)),
state_values, state_values, state_values, NumberConstant(0),
UndefinedConstant(), outer_frame_state);
}
JSOperatorBuilder* javascript() { return &javascript_; }
private:
JSOperatorBuilder javascript_;
CompilationDependencies deps_;
};
TEST_F(JSCreateLoweringTest, JSCreate) {
Handle<JSFunction> function = isolate()->object_function();
Node* const target = Parameter(Type::Constant(function, graph()->zone()));
Node* const context = Parameter(Type::Any());
Node* const effect = graph()->start();
Reduction r = Reduce(graph()->NewNode(javascript()->Create(), target, target,
context, EmptyFrameState(), effect));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsFinishRegion(
IsAllocate(IsNumberConstant(function->initial_map()->instance_size()),
IsBeginRegion(effect), _),
_));
}
// -----------------------------------------------------------------------------
// JSCreateArguments
TEST_F(JSCreateLoweringTest, JSCreateArgumentsViaStub) {
Node* const closure = Parameter(Type::Any());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Handle<SharedFunctionInfo> shared(isolate()->object_function()->shared());
Node* const frame_state = FrameState(shared, graph()->start());
Reduction r = Reduce(graph()->NewNode(
javascript()->CreateArguments(CreateArgumentsType::kMappedArguments),
closure, context, frame_state, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsCall(_,
IsHeapConstant(
CodeFactory::ArgumentsAccess(isolate(), false, false).code()),
closure, IsNumberConstant(0), _, effect, control));
}
TEST_F(JSCreateLoweringTest, JSCreateArgumentsRestParameterViaStub) {
Node* const closure = Parameter(Type::Any());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Handle<SharedFunctionInfo> shared(isolate()->object_function()->shared());
Node* const frame_state = FrameState(shared, graph()->start());
Reduction r = Reduce(graph()->NewNode(
javascript()->CreateArguments(CreateArgumentsType::kRestParameter),
closure, context, frame_state, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsCall(_, IsHeapConstant(
CodeFactory::FastNewRestParameter(isolate()).code()),
closure, context, frame_state, effect, control));
}
TEST_F(JSCreateLoweringTest, JSCreateArgumentsInlinedMapped) {
Node* const closure = Parameter(Type::Any());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Handle<SharedFunctionInfo> shared(isolate()->object_function()->shared());
Node* const frame_state_outer = FrameState(shared, graph()->start());
Node* const frame_state_inner = FrameState(shared, frame_state_outer);
Reduction r = Reduce(graph()->NewNode(
javascript()->CreateArguments(CreateArgumentsType::kMappedArguments),
closure, context, frame_state_inner, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsFinishRegion(
IsAllocate(IsNumberConstant(Heap::kSloppyArgumentsObjectSize),
_, control),
_));
}
TEST_F(JSCreateLoweringTest, JSCreateArgumentsInlinedUnmapped) {
Node* const closure = Parameter(Type::Any());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Handle<SharedFunctionInfo> shared(isolate()->object_function()->shared());
Node* const frame_state_outer = FrameState(shared, graph()->start());
Node* const frame_state_inner = FrameState(shared, frame_state_outer);
Reduction r = Reduce(graph()->NewNode(
javascript()->CreateArguments(CreateArgumentsType::kUnmappedArguments),
closure, context, frame_state_inner, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsFinishRegion(
IsAllocate(IsNumberConstant(Heap::kStrictArgumentsObjectSize),
_, control),
_));
}
TEST_F(JSCreateLoweringTest, JSCreateArgumentsInlinedRestArray) {
Node* const closure = Parameter(Type::Any());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Handle<SharedFunctionInfo> shared(isolate()->object_function()->shared());
Node* const frame_state_outer = FrameState(shared, graph()->start());
Node* const frame_state_inner = FrameState(shared, frame_state_outer);
Reduction r = Reduce(graph()->NewNode(
javascript()->CreateArguments(CreateArgumentsType::kRestParameter),
closure, context, frame_state_inner, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsFinishRegion(
IsAllocate(IsNumberConstant(JSArray::kSize), _, control), _));
}
// -----------------------------------------------------------------------------
// JSCreateFunctionContext
TEST_F(JSCreateLoweringTest, JSCreateFunctionContextViaInlinedAllocation) {
Node* const closure = Parameter(Type::Any());
Node* const context = Parameter(Type::Any());
Node* const effect = graph()->start();
Node* const control = graph()->start();
Reduction const r =
Reduce(graph()->NewNode(javascript()->CreateFunctionContext(8), closure,
context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsFinishRegion(IsAllocate(IsNumberConstant(Context::SizeFor(
8 + Context::MIN_CONTEXT_SLOTS)),
IsBeginRegion(_), control),
_));
}
// -----------------------------------------------------------------------------
// JSCreateWithContext
TEST_F(JSCreateLoweringTest, JSCreateWithContext) {
Node* const object = Parameter(Type::Receiver());
Node* const closure = Parameter(Type::Function());
Node* const context = Parameter(Type::Any());
Node* const effect = graph()->start();
Node* const control = graph()->start();
Reduction r =
Reduce(graph()->NewNode(javascript()->CreateWithContext(), object,
closure, context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsFinishRegion(IsAllocate(IsNumberConstant(Context::SizeFor(
Context::MIN_CONTEXT_SLOTS)),
IsBeginRegion(_), control),
_));
}
// -----------------------------------------------------------------------------
// JSCreateCatchContext
TEST_F(JSCreateLoweringTest, JSCreateCatchContext) {
Handle<String> name = factory()->length_string();
Node* const exception = Parameter(Type::Receiver());
Node* const closure = Parameter(Type::Function());
Node* const context = Parameter(Type::Any());
Node* const effect = graph()->start();
Node* const control = graph()->start();
Reduction r =
Reduce(graph()->NewNode(javascript()->CreateCatchContext(name), exception,
closure, context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsFinishRegion(IsAllocate(IsNumberConstant(Context::SizeFor(
Context::MIN_CONTEXT_SLOTS + 1)),
IsBeginRegion(_), control),
_));
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -92,18 +92,6 @@ class JSTypedLoweringTest : public TypedGraphTest {
return reducer.Reduce(node);
}
Node* FrameState(Handle<SharedFunctionInfo> shared, Node* outer_frame_state) {
Node* state_values = graph()->NewNode(common()->StateValues(0));
return graph()->NewNode(
common()->FrameState(BailoutId::None(),
OutputFrameStateCombine::Ignore(),
common()->CreateFrameStateFunctionInfo(
FrameStateType::kJavaScriptFunction, 1, 0,
shared, CALL_MAINTAINS_NATIVE_CONTEXT)),
state_values, state_values, state_values, NumberConstant(0),
UndefinedConstant(), outer_frame_state);
}
Handle<JSArrayBuffer> NewArrayBuffer(void* bytes, size_t byte_length) {
Handle<JSArrayBuffer> buffer = factory()->NewJSArrayBuffer();
JSArrayBuffer::Setup(buffer, isolate(), true, bytes, byte_length);
@ -907,196 +895,6 @@ TEST_F(JSTypedLoweringTest, JSAddWithString) {
}
// -----------------------------------------------------------------------------
// JSCreate
TEST_F(JSTypedLoweringTest, JSCreate) {
Handle<JSFunction> function = isolate()->object_function();
Node* const target = Parameter(Type::Constant(function, graph()->zone()));
Node* const context = Parameter(Type::Any());
Node* const effect = graph()->start();
Reduction r = Reduce(graph()->NewNode(javascript()->Create(), target, target,
context, EmptyFrameState(), effect));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsFinishRegion(
IsAllocate(IsNumberConstant(function->initial_map()->instance_size()),
IsBeginRegion(effect), _),
_));
}
// -----------------------------------------------------------------------------
// JSCreateArguments
TEST_F(JSTypedLoweringTest, JSCreateArgumentsViaStub) {
Node* const closure = Parameter(Type::Any());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Handle<SharedFunctionInfo> shared(isolate()->object_function()->shared());
Node* const frame_state = FrameState(shared, graph()->start());
Reduction r = Reduce(
graph()->NewNode(javascript()->CreateArguments(
CreateArgumentsType::kMappedArguments),
closure, context, frame_state, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsCall(_, IsHeapConstant(CodeFactory::ArgumentsAccess(
isolate(), false, false)
.code()),
closure, IsNumberConstant(0), _, effect, control));
}
TEST_F(JSTypedLoweringTest, JSCreateArgumentsRestParameterViaStub) {
Node* const closure = Parameter(Type::Any());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Handle<SharedFunctionInfo> shared(isolate()->object_function()->shared());
Node* const frame_state = FrameState(shared, graph()->start());
Reduction r = Reduce(graph()->NewNode(
javascript()->CreateArguments(CreateArgumentsType::kRestParameter),
closure, context, frame_state, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsCall(_, IsHeapConstant(
CodeFactory::FastNewRestParameter(isolate()).code()),
closure, context, frame_state, effect, control));
}
TEST_F(JSTypedLoweringTest, JSCreateArgumentsInlinedMapped) {
Node* const closure = Parameter(Type::Any());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Handle<SharedFunctionInfo> shared(isolate()->object_function()->shared());
Node* const frame_state_outer = FrameState(shared, graph()->start());
Node* const frame_state_inner = FrameState(shared, frame_state_outer);
Reduction r = Reduce(
graph()->NewNode(javascript()->CreateArguments(
CreateArgumentsType::kMappedArguments),
closure, context, frame_state_inner, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsFinishRegion(
IsAllocate(IsNumberConstant(Heap::kSloppyArgumentsObjectSize),
_, control),
_));
}
TEST_F(JSTypedLoweringTest, JSCreateArgumentsInlinedUnmapped) {
Node* const closure = Parameter(Type::Any());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Handle<SharedFunctionInfo> shared(isolate()->object_function()->shared());
Node* const frame_state_outer = FrameState(shared, graph()->start());
Node* const frame_state_inner = FrameState(shared, frame_state_outer);
Reduction r = Reduce(
graph()->NewNode(javascript()->CreateArguments(
CreateArgumentsType::kUnmappedArguments),
closure, context, frame_state_inner, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsFinishRegion(
IsAllocate(IsNumberConstant(Heap::kStrictArgumentsObjectSize),
_, control),
_));
}
TEST_F(JSTypedLoweringTest, JSCreateArgumentsInlinedRestArray) {
Node* const closure = Parameter(Type::Any());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Handle<SharedFunctionInfo> shared(isolate()->object_function()->shared());
Node* const frame_state_outer = FrameState(shared, graph()->start());
Node* const frame_state_inner = FrameState(shared, frame_state_outer);
Reduction r = Reduce(graph()->NewNode(
javascript()->CreateArguments(CreateArgumentsType::kRestParameter),
closure, context, frame_state_inner, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsFinishRegion(
IsAllocate(IsNumberConstant(JSArray::kSize), _, control), _));
}
// -----------------------------------------------------------------------------
// JSCreateFunctionContext
TEST_F(JSTypedLoweringTest, JSCreateFunctionContextViaInlinedAllocation) {
Node* const closure = Parameter(Type::Any());
Node* const context = Parameter(Type::Any());
Node* const effect = graph()->start();
Node* const control = graph()->start();
Reduction const r =
Reduce(graph()->NewNode(javascript()->CreateFunctionContext(8), closure,
context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsFinishRegion(IsAllocate(IsNumberConstant(Context::SizeFor(
8 + Context::MIN_CONTEXT_SLOTS)),
IsBeginRegion(_), control),
_));
}
// -----------------------------------------------------------------------------
// JSCreateWithContext
TEST_F(JSTypedLoweringTest, JSCreateWithContext) {
Node* const object = Parameter(Type::Receiver());
Node* const closure = Parameter(Type::Function());
Node* const context = Parameter(Type::Any());
Node* const effect = graph()->start();
Node* const control = graph()->start();
Reduction r =
Reduce(graph()->NewNode(javascript()->CreateWithContext(), object,
closure, context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsFinishRegion(IsAllocate(IsNumberConstant(Context::SizeFor(
Context::MIN_CONTEXT_SLOTS)),
IsBeginRegion(_), control),
_));
}
// -----------------------------------------------------------------------------
// JSCreateCatchContext
TEST_F(JSTypedLoweringTest, JSCreateCatchContext) {
Handle<String> name = factory()->length_string();
Node* const exception = Parameter(Type::Receiver());
Node* const closure = Parameter(Type::Function());
Node* const context = Parameter(Type::Any());
Node* const effect = graph()->start();
Node* const control = graph()->start();
Reduction r =
Reduce(graph()->NewNode(javascript()->CreateCatchContext(name), exception,
closure, context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsFinishRegion(IsAllocate(IsNumberConstant(Context::SizeFor(
Context::MIN_CONTEXT_SLOTS + 1)),
IsBeginRegion(_), control),
_));
}
// -----------------------------------------------------------------------------
// JSInstanceOf
// Test that instanceOf is reduced if and only if the right-hand side is a

View File

@ -64,6 +64,7 @@
'compiler/interpreter-assembler-unittest.h',
'compiler/js-builtin-reducer-unittest.cc',
'compiler/js-context-relaxation-unittest.cc',
'compiler/js-create-lowering-unittest.cc',
'compiler/js-intrinsic-lowering-unittest.cc',
'compiler/js-operator-unittest.cc',
'compiler/js-typed-lowering-unittest.cc',

View File

@ -593,6 +593,8 @@
'../../src/compiler/js-context-relaxation.h',
'../../src/compiler/js-context-specialization.cc',
'../../src/compiler/js-context-specialization.h',
'../../src/compiler/js-create-lowering.cc',
'../../src/compiler/js-create-lowering.h',
'../../src/compiler/js-frame-specialization.cc',
'../../src/compiler/js-frame-specialization.h',
'../../src/compiler/js-generic-lowering.cc',