Commit e3905047 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Initial support for inline allocations of arrays.

Add support for using inline allocations for arrays in lowering of
JSCreateArray when target equals new.target.  Currently we are only
concerend with the straight-forward Array() and Array(length) cases,
but at some point TurboFan should also be able to support the more
complex initializing cases.

R=mvstanton@chromium.org
BUG=v8:4470
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#32191}
parent d705e1ab
......@@ -275,12 +275,20 @@ FieldAccess AccessBuilder::ForSharedFunctionInfoTypeFeedbackVector() {
// static
ElementAccess AccessBuilder::ForFixedArrayElement() {
ElementAccess access = {kTaggedBase, FixedArray::kHeaderSize, Type::Any(),
ElementAccess access = {kTaggedBase, FixedArray::kHeaderSize, Type::Tagged(),
kMachAnyTagged};
return access;
}
// static
ElementAccess AccessBuilder::ForFixedDoubleArrayElement() {
ElementAccess access = {kTaggedBase, FixedDoubleArray::kHeaderSize,
TypeCache::Get().kFloat64, kMachFloat64};
return access;
}
// static
ElementAccess AccessBuilder::ForTypedArrayElement(ExternalArrayType type,
bool is_external) {
......
......@@ -108,6 +108,9 @@ class AccessBuilder final : public AllStatic {
// Provides access to FixedArray elements.
static ElementAccess ForFixedArrayElement();
// Provides access to FixedDoubleArray elements.
static ElementAccess ForFixedDoubleArrayElement();
// Provides access to Fixed{type}TypedArray and External{type}Array elements.
static ElementAccess ForTypedArrayElement(ExternalArrayType type,
bool is_external);
......
......@@ -12,31 +12,14 @@
#include "src/compiler/node-properties.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/state-values-utils.h"
#include "src/type-cache.h"
#include "src/types.h"
namespace v8 {
namespace internal {
namespace compiler {
// TODO(turbofan): js-typed-lowering improvements possible
// - immediately put in type bounds for all new nodes
// - relax effects from generic but not-side-effecting operations
JSTypedLowering::JSTypedLowering(Editor* editor,
CompilationDependencies* dependencies,
Flags flags, JSGraph* jsgraph, Zone* zone)
: AdvancedReducer(editor),
dependencies_(dependencies),
flags_(flags),
jsgraph_(jsgraph) {
for (size_t k = 0; k < arraysize(shifted_int32_ranges_); ++k) {
double min = kMinInt / (1 << k);
double max = kMaxInt / (1 << k);
shifted_int32_ranges_[k] = Type::Range(min, max, graph()->zone());
}
}
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
......@@ -50,10 +33,11 @@ class AllocationBuilder final {
control_(control) {}
// Primitive allocation of static size.
void Allocate(int size) {
void Allocate(int size, PretenureFlag pretenure = NOT_TENURED) {
effect_ = graph()->NewNode(common()->BeginRegion(), effect_);
allocation_ = graph()->NewNode(
simplified()->Allocate(), jsgraph()->Constant(size), effect_, control_);
allocation_ =
graph()->NewNode(simplified()->Allocate(pretenure),
jsgraph()->Constant(size), effect_, control_);
effect_ = allocation_;
}
......@@ -63,9 +47,21 @@ class AllocationBuilder final {
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) {
Allocate(FixedArray::SizeFor(length));
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));
}
......@@ -100,6 +96,8 @@ class AllocationBuilder final {
Node* control_;
};
} // namespace
// A helper class to simplify the process of reducing a single binop node with a
// JSOperator. This class manages the rewriting of context, control, and effect
......@@ -417,6 +415,27 @@ class JSBinopReduction final {
};
// TODO(turbofan): js-typed-lowering improvements possible
// - immediately put in type bounds for all new nodes
// - relax effects from generic but not-side-effecting operations
JSTypedLowering::JSTypedLowering(Editor* editor,
CompilationDependencies* dependencies,
Flags flags, JSGraph* jsgraph, Zone* zone)
: AdvancedReducer(editor),
dependencies_(dependencies),
flags_(flags),
jsgraph_(jsgraph),
type_cache_(TypeCache::Get()) {
for (size_t k = 0; k < arraysize(shifted_int32_ranges_); ++k) {
double min = kMinInt / (1 << k);
double max = kMaxInt / (1 << k);
shifted_int32_ranges_[k] = Type::Range(min, max, graph()->zone());
}
}
Reduction JSTypedLowering::ReduceJSAdd(Node* node) {
JSBinopReduction r(this, node);
if (r.BothInputsAre(Type::Number())) {
......@@ -1561,11 +1580,81 @@ Reduction JSTypedLowering::ReduceJSCreateArguments(Node* node) {
}
Reduction JSTypedLowering::ReduceNewArray(Node* node, Node* length,
int capacity,
Handle<AllocationSite> site) {
DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode());
Node* target = NodeProperties::GetValueInput(node, 0);
Type* target_type = NodeProperties::GetType(target);
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();
if (flags() & kDeoptimizationEnabled) {
dependencies()->AssumeTenuringDecision(site);
dependencies()->AssumeTransitionStable(site);
}
// Retrieve the initial map for the array from the appropriate native context.
Node* js_array_map;
if (target_type->IsConstant()) {
Handle<JSFunction> target_function =
Handle<JSFunction>::cast(target_type->AsConstant()->Value());
Handle<FixedArray> js_array_maps(
FixedArray::cast(target_function->native_context()->js_array_maps()),
isolate());
js_array_map = jsgraph()->Constant(
handle(js_array_maps->get(elements_kind), isolate()));
} else {
Node* global_object = effect = graph()->NewNode(
javascript()->LoadContext(0, Context::GLOBAL_OBJECT_INDEX, true),
context, context, effect);
Node* native_context = effect =
graph()->NewNode(simplified()->LoadField(
AccessBuilder::ForJSGlobalObjectNativeContext()),
global_object, effect, control);
Node* js_array_maps = effect = graph()->NewNode(
javascript()->LoadContext(0, Context::JS_ARRAY_MAPS_INDEX, true),
native_context, native_context, effect);
js_array_map = effect =
graph()->NewNode(simplified()->LoadField(
AccessBuilder::ForFixedArraySlot(elements_kind)),
js_array_maps, effect, control);
}
// 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* const target = NodeProperties::GetValueInput(node, 0);
Node* const new_target = NodeProperties::GetValueInput(node, 1);
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();
......@@ -1573,16 +1662,34 @@ Reduction JSTypedLowering::ReduceJSCreateArray(Node* node) {
// Check if we have a feedback {site} on the {node}.
Handle<AllocationSite> site = p.site();
if (p.site().is_null()) return NoChange();
ElementsKind const elements_kind = site->GetElementsKind();
AllocationSiteOverrideMode override_mode =
(AllocationSite::GetMode(elements_kind) == TRACK_ALLOCATION_SITE)
? DISABLE_ALLOCATION_SITES
: DONT_OVERRIDE;
// 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);
}
}
}
// Reduce {node} to the appropriate ArrayConstructorStub backend.
// Note that these stubs "behave" like JSFunctions, which means they
// expect a receiver on the stack, which they remove. We just push
// undefined for the receiver.
ElementsKind elements_kind = site->GetElementsKind();
AllocationSiteOverrideMode override_mode =
(AllocationSite::GetMode(elements_kind) == TRACK_ALLOCATION_SITE)
? DISABLE_ALLOCATION_SITES
: DONT_OVERRIDE;
if (p.arity() == 0) {
ArrayNoArgumentConstructorStub stub(isolate(), elements_kind,
override_mode);
......@@ -1590,7 +1697,7 @@ Reduction JSTypedLowering::ReduceJSCreateArray(Node* node) {
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 1,
CallDescriptor::kNeedsFrameState);
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
node->InsertInput(graph()->zone(), 2, jsgraph()->UndefinedConstant());
node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site));
node->InsertInput(graph()->zone(), 3, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(node, common()->Call(desc));
return Changed(node);
......@@ -1615,7 +1722,7 @@ Reduction JSTypedLowering::ReduceJSCreateArray(Node* node) {
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(),
arity + 1, CallDescriptor::kNeedsFrameState);
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
node->InsertInput(graph()->zone(), 2, jsgraph()->UndefinedConstant());
node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site));
node->InsertInput(graph()->zone(), 3, jsgraph()->Int32Constant(arity));
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(node, common()->Call(desc));
......@@ -2441,6 +2548,34 @@ Node* JSTypedLowering::AllocateAliasedArguments(
}
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()->Int32Constant(i);
a.Store(access, index, value);
}
return a.Finish();
}
Factory* JSTypedLowering::factory() const { return jsgraph()->factory(); }
......
......@@ -15,6 +15,7 @@ namespace internal {
// Forward declarations.
class CompilationDependencies;
class Factory;
class TypeCache;
namespace compiler {
......@@ -86,12 +87,17 @@ class JSTypedLowering final : public AdvancedReducer {
Reduction ReduceInt32Binop(Node* node, const Operator* intOp);
Reduction ReduceUI32Shift(Node* node, Signedness left_signedness,
const Operator* shift_op);
Reduction ReduceNewArray(Node* node, Node* length, int capacity,
Handle<AllocationSite> site);
Node* Word32Shl(Node* const lhs, int32_t const rhs);
Node* AllocateArguments(Node* effect, Node* control, Node* frame_state);
Node* 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;
......@@ -112,6 +118,7 @@ class JSTypedLowering final : public AdvancedReducer {
Flags flags_;
JSGraph* jsgraph_;
Type* shifted_int32_ranges_[4];
TypeCache const& type_cache_;
};
DEFINE_OPERATORS_FOR_FLAGS(JSTypedLowering::Flags)
......
......@@ -94,6 +94,10 @@ class TypeCache final {
Type* const kStringLengthType =
CreateNative(CreateRange(0.0, String::kMaxLength), Type::TaggedSigned());
// When initializing arrays, we'll unfold the loop if the number of
// elements is known to be of this type.
Type* const kElementLoopUnrollType = CreateRange(0.0, 16.0);
#define TYPED_ARRAY(TypeName, type_name, TYPE_NAME, ctype, size) \
Type* const k##TypeName##Array = CreateArray(k##TypeName);
TYPED_ARRAYS(TYPED_ARRAY)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment