[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:
parent
4d46b510ca
commit
07e9921f5a
2
BUILD.gn
2
BUILD.gn
@ -795,6 +795,8 @@ source_set("v8_base") {
|
|||||||
"src/compiler/js-context-relaxation.h",
|
"src/compiler/js-context-relaxation.h",
|
||||||
"src/compiler/js-context-specialization.cc",
|
"src/compiler/js-context-specialization.cc",
|
||||||
"src/compiler/js-context-specialization.h",
|
"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.cc",
|
||||||
"src/compiler/js-frame-specialization.h",
|
"src/compiler/js-frame-specialization.h",
|
||||||
"src/compiler/js-generic-lowering.cc",
|
"src/compiler/js-generic-lowering.cc",
|
||||||
|
766
src/compiler/js-create-lowering.cc
Normal file
766
src/compiler/js-create-lowering.cc
Normal 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
|
80
src/compiler/js-create-lowering.h
Normal file
80
src/compiler/js-create-lowering.h
Normal 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_
|
@ -11,7 +11,6 @@
|
|||||||
#include "src/compiler/node-matchers.h"
|
#include "src/compiler/node-matchers.h"
|
||||||
#include "src/compiler/node-properties.h"
|
#include "src/compiler/node-properties.h"
|
||||||
#include "src/compiler/operator-properties.h"
|
#include "src/compiler/operator-properties.h"
|
||||||
#include "src/compiler/state-values-utils.h"
|
|
||||||
#include "src/type-cache.h"
|
#include "src/type-cache.h"
|
||||||
#include "src/types.h"
|
#include "src/types.h"
|
||||||
|
|
||||||
@ -19,86 +18,6 @@ namespace v8 {
|
|||||||
namespace internal {
|
namespace internal {
|
||||||
namespace compiler {
|
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
|
// 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
|
// JSOperator. This class manages the rewriting of context, control, and effect
|
||||||
// dependencies during lowering of a binop and contains numerous helper
|
// 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) {
|
Reduction JSTypedLowering::ReduceJSCallConstruct(Node* node) {
|
||||||
DCHECK_EQ(IrOpcode::kJSCallConstruct, node->opcode());
|
DCHECK_EQ(IrOpcode::kJSCallConstruct, node->opcode());
|
||||||
CallConstructParameters const& p = CallConstructParametersOf(node->op());
|
CallConstructParameters const& p = CallConstructParametersOf(node->op());
|
||||||
@ -2313,22 +1733,6 @@ Reduction JSTypedLowering::Reduce(Node* node) {
|
|||||||
return ReduceJSStoreContext(node);
|
return ReduceJSStoreContext(node);
|
||||||
case IrOpcode::kJSConvertReceiver:
|
case IrOpcode::kJSConvertReceiver:
|
||||||
return ReduceJSConvertReceiver(node);
|
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:
|
case IrOpcode::kJSCallConstruct:
|
||||||
return ReduceJSCallConstruct(node);
|
return ReduceJSCallConstruct(node);
|
||||||
case IrOpcode::kJSCallFunction:
|
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(); }
|
Factory* JSTypedLowering::factory() const { return jsgraph()->factory(); }
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,14 +68,6 @@ class JSTypedLowering final : public AdvancedReducer {
|
|||||||
Reduction ReduceJSToString(Node* node);
|
Reduction ReduceJSToString(Node* node);
|
||||||
Reduction ReduceJSToObject(Node* node);
|
Reduction ReduceJSToObject(Node* node);
|
||||||
Reduction ReduceJSConvertReceiver(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 ReduceJSCallConstruct(Node* node);
|
||||||
Reduction ReduceJSCallFunction(Node* node);
|
Reduction ReduceJSCallFunction(Node* node);
|
||||||
Reduction ReduceJSForInDone(Node* node);
|
Reduction ReduceJSForInDone(Node* node);
|
||||||
@ -86,19 +78,8 @@ class JSTypedLowering final : public AdvancedReducer {
|
|||||||
Reduction ReduceInt32Binop(Node* node, const Operator* intOp);
|
Reduction ReduceInt32Binop(Node* node, const Operator* intOp);
|
||||||
Reduction ReduceUI32Shift(Node* node, Signedness left_signedness,
|
Reduction ReduceUI32Shift(Node* node, Signedness left_signedness,
|
||||||
const Operator* shift_op);
|
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* 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;
|
Factory* factory() const;
|
||||||
Graph* graph() const;
|
Graph* graph() const;
|
||||||
@ -111,10 +92,6 @@ class JSTypedLowering final : public AdvancedReducer {
|
|||||||
CompilationDependencies* dependencies() const;
|
CompilationDependencies* dependencies() const;
|
||||||
Flags flags() const { return flags_; }
|
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_;
|
CompilationDependencies* dependencies_;
|
||||||
Flags flags_;
|
Flags flags_;
|
||||||
JSGraph* jsgraph_;
|
JSGraph* jsgraph_;
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include "src/compiler/js-call-reducer.h"
|
#include "src/compiler/js-call-reducer.h"
|
||||||
#include "src/compiler/js-context-relaxation.h"
|
#include "src/compiler/js-context-relaxation.h"
|
||||||
#include "src/compiler/js-context-specialization.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-frame-specialization.h"
|
||||||
#include "src/compiler/js-generic-lowering.h"
|
#include "src/compiler/js-generic-lowering.h"
|
||||||
#include "src/compiler/js-global-object-specialization.h"
|
#include "src/compiler/js-global-object-specialization.h"
|
||||||
@ -612,6 +613,8 @@ struct TypedLoweringPhase {
|
|||||||
data->common());
|
data->common());
|
||||||
LoadElimination load_elimination(&graph_reducer);
|
LoadElimination load_elimination(&graph_reducer);
|
||||||
JSBuiltinReducer builtin_reducer(&graph_reducer, data->jsgraph());
|
JSBuiltinReducer builtin_reducer(&graph_reducer, data->jsgraph());
|
||||||
|
JSCreateLowering create_lowering(
|
||||||
|
&graph_reducer, data->info()->dependencies(), data->jsgraph());
|
||||||
JSTypedLowering::Flags typed_lowering_flags = JSTypedLowering::kNoFlags;
|
JSTypedLowering::Flags typed_lowering_flags = JSTypedLowering::kNoFlags;
|
||||||
if (data->info()->is_deoptimization_enabled()) {
|
if (data->info()->is_deoptimization_enabled()) {
|
||||||
typed_lowering_flags |= JSTypedLowering::kDeoptimizationEnabled;
|
typed_lowering_flags |= JSTypedLowering::kDeoptimizationEnabled;
|
||||||
@ -631,6 +634,9 @@ struct TypedLoweringPhase {
|
|||||||
data->common(), data->machine());
|
data->common(), data->machine());
|
||||||
AddReducer(data, &graph_reducer, &dead_code_elimination);
|
AddReducer(data, &graph_reducer, &dead_code_elimination);
|
||||||
AddReducer(data, &graph_reducer, &builtin_reducer);
|
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, &typed_lowering);
|
||||||
AddReducer(data, &graph_reducer, &intrinsic_lowering);
|
AddReducer(data, &graph_reducer, &intrinsic_lowering);
|
||||||
AddReducer(data, &graph_reducer, &load_elimination);
|
AddReducer(data, &graph_reducer, &load_elimination);
|
||||||
|
@ -112,10 +112,6 @@ class TypeCache final {
|
|||||||
Type* const kStringLengthType =
|
Type* const kStringLengthType =
|
||||||
CreateNative(CreateRange(0.0, String::kMaxLength), Type::TaggedSigned());
|
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) \
|
#define TYPED_ARRAY(TypeName, type_name, TYPE_NAME, ctype, size) \
|
||||||
Type* const k##TypeName##Array = CreateArray(k##TypeName);
|
Type* const k##TypeName##Array = CreateArray(k##TypeName);
|
||||||
TYPED_ARRAYS(TYPED_ARRAY)
|
TYPED_ARRAYS(TYPED_ARRAY)
|
||||||
|
237
test/unittests/compiler/js-create-lowering-unittest.cc
Normal file
237
test/unittests/compiler/js-create-lowering-unittest.cc
Normal 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
|
@ -92,18 +92,6 @@ class JSTypedLoweringTest : public TypedGraphTest {
|
|||||||
return reducer.Reduce(node);
|
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> NewArrayBuffer(void* bytes, size_t byte_length) {
|
||||||
Handle<JSArrayBuffer> buffer = factory()->NewJSArrayBuffer();
|
Handle<JSArrayBuffer> buffer = factory()->NewJSArrayBuffer();
|
||||||
JSArrayBuffer::Setup(buffer, isolate(), true, bytes, byte_length);
|
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
|
// JSInstanceOf
|
||||||
// Test that instanceOf is reduced if and only if the right-hand side is a
|
// Test that instanceOf is reduced if and only if the right-hand side is a
|
||||||
|
@ -64,6 +64,7 @@
|
|||||||
'compiler/interpreter-assembler-unittest.h',
|
'compiler/interpreter-assembler-unittest.h',
|
||||||
'compiler/js-builtin-reducer-unittest.cc',
|
'compiler/js-builtin-reducer-unittest.cc',
|
||||||
'compiler/js-context-relaxation-unittest.cc',
|
'compiler/js-context-relaxation-unittest.cc',
|
||||||
|
'compiler/js-create-lowering-unittest.cc',
|
||||||
'compiler/js-intrinsic-lowering-unittest.cc',
|
'compiler/js-intrinsic-lowering-unittest.cc',
|
||||||
'compiler/js-operator-unittest.cc',
|
'compiler/js-operator-unittest.cc',
|
||||||
'compiler/js-typed-lowering-unittest.cc',
|
'compiler/js-typed-lowering-unittest.cc',
|
||||||
|
@ -593,6 +593,8 @@
|
|||||||
'../../src/compiler/js-context-relaxation.h',
|
'../../src/compiler/js-context-relaxation.h',
|
||||||
'../../src/compiler/js-context-specialization.cc',
|
'../../src/compiler/js-context-specialization.cc',
|
||||||
'../../src/compiler/js-context-specialization.h',
|
'../../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.cc',
|
||||||
'../../src/compiler/js-frame-specialization.h',
|
'../../src/compiler/js-frame-specialization.h',
|
||||||
'../../src/compiler/js-generic-lowering.cc',
|
'../../src/compiler/js-generic-lowering.cc',
|
||||||
|
Loading…
Reference in New Issue
Block a user