Index: src/compiler/js-typed-lowering.cc |
diff --git a/src/compiler/js-typed-lowering.cc b/src/compiler/js-typed-lowering.cc |
index 79e40aa3b667267ae1efa76399b2a9fa96550165..9c684b31cceed4308f2efc5e46448eaae74c7d27 100644 |
--- a/src/compiler/js-typed-lowering.cc |
+++ b/src/compiler/js-typed-lowering.cc |
@@ -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(); } |