Index: src/crankshaft/hydrogen.cc |
diff --git a/src/crankshaft/hydrogen.cc b/src/crankshaft/hydrogen.cc |
index a39a8e5617cd7ea91cf82dc3dbf325b5cda04313..c9fcc8c1001dddb36e28a2b210d150d4b579aaef 100644 |
--- a/src/crankshaft/hydrogen.cc |
+++ b/src/crankshaft/hydrogen.cc |
@@ -2880,52 +2880,6 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( |
} |
-HValue* HGraphBuilder::BuildAllocateArrayFromLength( |
- JSArrayBuilder* array_builder, |
- HValue* length_argument) { |
- if (length_argument->IsConstant() && |
- HConstant::cast(length_argument)->HasSmiValue()) { |
- int array_length = HConstant::cast(length_argument)->Integer32Value(); |
- if (array_length == 0) { |
- return array_builder->AllocateEmptyArray(); |
- } else { |
- return array_builder->AllocateArray(length_argument, |
- length_argument); |
- } |
- } |
- |
- HValue* constant_zero = graph()->GetConstant0(); |
- HConstant* max_alloc_length = |
- Add<HConstant>(JSArray::kInitialMaxFastElementArray); |
- HInstruction* checked_length = Add<HBoundsCheck>(length_argument, |
- max_alloc_length); |
- IfBuilder if_builder(this); |
- if_builder.If<HCompareNumericAndBranch>(checked_length, constant_zero, |
- Token::EQ); |
- if_builder.Then(); |
- const int initial_capacity = JSArray::kPreallocatedArrayElements; |
- HConstant* initial_capacity_node = Add<HConstant>(initial_capacity); |
- Push(initial_capacity_node); // capacity |
- Push(constant_zero); // length |
- if_builder.Else(); |
- if (!(top_info()->IsStub()) && |
- IsFastPackedElementsKind(array_builder->kind())) { |
- // We'll come back later with better (holey) feedback. |
- if_builder.Deopt( |
- Deoptimizer::kHoleyArrayDespitePackedElements_kindFeedback); |
- } else { |
- Push(checked_length); // capacity |
- Push(checked_length); // length |
- } |
- if_builder.End(); |
- |
- // Figure out total size |
- HValue* length = Pop(); |
- HValue* capacity = Pop(); |
- return array_builder->AllocateArray(capacity, length); |
-} |
- |
- |
HValue* HGraphBuilder::BuildCalculateElementsSize(ElementsKind kind, |
HValue* capacity) { |
int elements_size = IsFastDoubleElementsKind(kind) |
@@ -3016,15 +2970,13 @@ void HGraphBuilder::BuildJSArrayHeader(HValue* array, |
HValue* length_field) { |
Add<HStoreNamedField>(array, HObjectAccess::ForMap(), array_map); |
- HConstant* empty_fixed_array = |
- Add<HConstant>(isolate()->factory()->empty_fixed_array()); |
+ HValue* empty_fixed_array = Add<HLoadRoot>(Heap::kEmptyFixedArrayRootIndex); |
Add<HStoreNamedField>( |
array, HObjectAccess::ForPropertiesPointer(), empty_fixed_array); |
- Add<HStoreNamedField>( |
- array, HObjectAccess::ForElementsPointer(), |
- elements != NULL ? elements : empty_fixed_array); |
+ Add<HStoreNamedField>(array, HObjectAccess::ForElementsPointer(), |
+ elements != nullptr ? elements : empty_fixed_array); |
Add<HStoreNamedField>( |
array, HObjectAccess::ForArrayLength(elements_kind), length_field); |
@@ -3535,140 +3487,6 @@ HValue* HGraphBuilder::BuildArrayBufferViewFieldAccessor(HValue* object, |
return Pop(); |
} |
- |
-HGraphBuilder::JSArrayBuilder::JSArrayBuilder(HGraphBuilder* builder, |
- ElementsKind kind, |
- HValue* allocation_site_payload, |
- HValue* constructor_function, |
- AllocationSiteOverrideMode override_mode) : |
- builder_(builder), |
- kind_(kind), |
- allocation_site_payload_(allocation_site_payload), |
- constructor_function_(constructor_function) { |
- DCHECK(!allocation_site_payload->IsConstant() || |
- HConstant::cast(allocation_site_payload)->handle( |
- builder_->isolate())->IsAllocationSite()); |
- mode_ = override_mode == DISABLE_ALLOCATION_SITES |
- ? DONT_TRACK_ALLOCATION_SITE |
- : AllocationSite::GetMode(kind); |
-} |
- |
- |
-HGraphBuilder::JSArrayBuilder::JSArrayBuilder(HGraphBuilder* builder, |
- ElementsKind kind, |
- HValue* constructor_function) : |
- builder_(builder), |
- kind_(kind), |
- mode_(DONT_TRACK_ALLOCATION_SITE), |
- allocation_site_payload_(NULL), |
- constructor_function_(constructor_function) { |
-} |
- |
- |
-HValue* HGraphBuilder::JSArrayBuilder::EmitMapCode() { |
- if (!builder()->top_info()->IsStub()) { |
- // A constant map is fine. |
- Handle<Map> map(builder()->isolate()->get_initial_js_array_map(kind_), |
- builder()->isolate()); |
- return builder()->Add<HConstant>(map); |
- } |
- |
- if (constructor_function_ != NULL && kind_ == GetInitialFastElementsKind()) { |
- // No need for a context lookup if the kind_ matches the initial |
- // map, because we can just load the map in that case. |
- HObjectAccess access = HObjectAccess::ForPrototypeOrInitialMap(); |
- return builder()->Add<HLoadNamedField>(constructor_function_, nullptr, |
- access); |
- } |
- |
- // TODO(mvstanton): we should always have a constructor function if we |
- // are creating a stub. |
- HInstruction* native_context = constructor_function_ != NULL |
- ? builder()->BuildGetNativeContext(constructor_function_) |
- : builder()->BuildGetNativeContext(); |
- |
- HObjectAccess access = |
- HObjectAccess::ForContextSlot(Context::ArrayMapIndex(kind_)); |
- return builder()->Add<HLoadNamedField>(native_context, nullptr, access); |
-} |
- |
- |
-HValue* HGraphBuilder::JSArrayBuilder::EmitInternalMapCode() { |
- // Find the map near the constructor function |
- HObjectAccess access = HObjectAccess::ForPrototypeOrInitialMap(); |
- return builder()->Add<HLoadNamedField>(constructor_function_, nullptr, |
- access); |
-} |
- |
- |
-HAllocate* HGraphBuilder::JSArrayBuilder::AllocateEmptyArray() { |
- HConstant* capacity = builder()->Add<HConstant>(initial_capacity()); |
- return AllocateArray(capacity, |
- builder()->graph()->GetConstant0()); |
-} |
- |
- |
-HAllocate* HGraphBuilder::JSArrayBuilder::AllocateArray( |
- HValue* capacity, |
- HValue* length_field, |
- FillMode fill_mode) { |
- // These HForceRepresentations are because we store these as fields in the |
- // objects we construct, and an int32-to-smi HChange could deopt. Accept |
- // the deopt possibility now, before allocation occurs. |
- capacity = |
- builder()->AddUncasted<HForceRepresentation>(capacity, |
- Representation::Smi()); |
- length_field = |
- builder()->AddUncasted<HForceRepresentation>(length_field, |
- Representation::Smi()); |
- |
- // Generate size calculation code here in order to make it dominate |
- // the JSArray allocation. |
- HValue* elements_size = |
- builder()->BuildCalculateElementsSize(kind_, capacity); |
- |
- // Bail out for large objects. |
- HValue* max_regular_heap_object_size = |
- builder()->Add<HConstant>(Page::kMaxRegularHeapObjectSize); |
- builder()->Add<HBoundsCheck>(elements_size, max_regular_heap_object_size); |
- |
- // Allocate (dealing with failure appropriately) |
- HAllocate* array_object = builder()->AllocateJSArrayObject(mode_); |
- |
- // Fill in the fields: map, properties, length |
- HValue* map; |
- if (allocation_site_payload_ == NULL) { |
- map = EmitInternalMapCode(); |
- } else { |
- map = EmitMapCode(); |
- } |
- |
- builder()->BuildJSArrayHeader(array_object, |
- map, |
- NULL, // set elements to empty fixed array |
- mode_, |
- kind_, |
- allocation_site_payload_, |
- length_field); |
- |
- // Allocate and initialize the elements |
- elements_location_ = builder()->BuildAllocateElements(kind_, elements_size); |
- |
- builder()->BuildInitializeElementsHeader(elements_location_, kind_, capacity); |
- |
- // Set the elements |
- builder()->Add<HStoreNamedField>( |
- array_object, HObjectAccess::ForElementsPointer(), elements_location_); |
- |
- if (fill_mode == FILL_WITH_HOLE) { |
- builder()->BuildFillElementsWithHole(elements_location_, kind_, |
- graph()->GetConstant0(), capacity); |
- } |
- |
- return array_object; |
-} |
- |
- |
HValue* HGraphBuilder::AddLoadJSBuiltin(int context_index) { |
HValue* native_context = BuildGetNativeContext(); |
HObjectAccess function_access = HObjectAccess::ForContextSlot(context_index); |
@@ -9704,26 +9522,6 @@ HValue* HOptimizedGraphBuilder::ImplicitReceiverFor(HValue* function, |
} |
-void HOptimizedGraphBuilder::BuildArrayCall(Expression* expression, |
- int arguments_count, |
- HValue* function, |
- Handle<AllocationSite> site) { |
- Add<HCheckValue>(function, array_function()); |
- |
- if (IsCallArrayInlineable(arguments_count, site)) { |
- BuildInlinedCallArray(expression, arguments_count, site); |
- return; |
- } |
- |
- HInstruction* call = PreProcessCall(New<HCallNewArray>( |
- function, arguments_count + 1, site->GetElementsKind(), site)); |
- if (expression->IsCall()) { |
- Drop(1); |
- } |
- ast_context()->ReturnInstruction(call, expression->id()); |
-} |
- |
- |
HValue* HOptimizedGraphBuilder::BuildArrayIndexOf(HValue* receiver, |
HValue* search_element, |
ElementsKind kind, |
@@ -9870,8 +9668,8 @@ HValue* HOptimizedGraphBuilder::BuildArrayIndexOf(HValue* receiver, |
return Pop(); |
} |
- |
-bool HOptimizedGraphBuilder::TryHandleArrayCall(Call* expr, HValue* function) { |
+template <class T> |
+bool HOptimizedGraphBuilder::TryHandleArrayCall(T* expr, HValue* function) { |
if (!array_function().is_identical_to(expr->target())) { |
return false; |
} |
@@ -9879,24 +9677,16 @@ bool HOptimizedGraphBuilder::TryHandleArrayCall(Call* expr, HValue* function) { |
Handle<AllocationSite> site = expr->allocation_site(); |
if (site.is_null()) return false; |
- BuildArrayCall(expr, |
- expr->arguments()->length(), |
- function, |
- site); |
- return true; |
-} |
- |
+ Add<HCheckValue>(function, array_function()); |
-bool HOptimizedGraphBuilder::TryHandleArrayCallNew(CallNew* expr, |
- HValue* function) { |
- if (!array_function().is_identical_to(expr->target())) { |
- return false; |
- } |
+ int arguments_count = expr->arguments()->length(); |
+ if (TryInlineArrayCall(expr, arguments_count, site)) return true; |
- Handle<AllocationSite> site = expr->allocation_site(); |
- if (site.is_null()) return false; |
+ HInstruction* call = PreProcessCall(New<HCallNewArray>( |
+ function, arguments_count + 1, site->GetElementsKind(), site)); |
+ if (expr->IsCall()) Drop(1); |
+ ast_context()->ReturnInstruction(call, expr->id()); |
- BuildArrayCall(expr, expr->arguments()->length(), function, site); |
return true; |
} |
@@ -10082,50 +9872,108 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) { |
return ast_context()->ReturnInstruction(call, expr->id()); |
} |
+bool HOptimizedGraphBuilder::TryInlineArrayCall(Expression* expression, |
+ int argument_count, |
+ Handle<AllocationSite> site) { |
+ Handle<JSFunction> caller = current_info()->closure(); |
+ Handle<JSFunction> target = array_function(); |
-void HOptimizedGraphBuilder::BuildInlinedCallArray( |
- Expression* expression, |
- int argument_count, |
- Handle<AllocationSite> site) { |
- DCHECK(!site.is_null()); |
- DCHECK(argument_count >= 0 && argument_count <= 1); |
- NoObservableSideEffectsScope no_effects(this); |
+ if (!site->CanInlineCall()) { |
+ TraceInline(target, caller, "AllocationSite requested no inlining."); |
+ return false; |
+ } |
- // We should at least have the constructor on the expression stack. |
- HValue* constructor = environment()->ExpressionStackAt(argument_count); |
+ if (argument_count > 1) { |
+ TraceInline(target, caller, "Too many arguments to inline."); |
+ return false; |
+ } |
- // Register on the site for deoptimization if the transition feedback changes. |
- top_info()->dependencies()->AssumeTransitionStable(site); |
- ElementsKind kind = site->GetElementsKind(); |
- HInstruction* site_instruction = Add<HConstant>(site); |
+ int array_length = 0; |
+ // Do not inline if the constant length argument is not a smi or outside the |
+ // valid range for unrolled loop initialization. |
+ if (argument_count == 1) { |
+ HValue* argument = Top(); |
+ if (!argument->IsConstant()) { |
+ TraceInline(target, caller, |
+ "Dont inline [new] Array(n) where n isn't constant."); |
+ return false; |
+ } |
- // In the single constant argument case, we may have to adjust elements kind |
- // to avoid creating a packed non-empty array. |
- if (argument_count == 1 && !IsHoleyElementsKind(kind)) { |
- HValue* argument = environment()->Top(); |
- if (argument->IsConstant()) { |
- HConstant* constant_argument = HConstant::cast(argument); |
- DCHECK(constant_argument->HasSmiValue()); |
- int constant_array_size = constant_argument->Integer32Value(); |
- if (constant_array_size != 0) { |
- kind = GetHoleyElementsKind(kind); |
- } |
+ HConstant* constant_argument = HConstant::cast(argument); |
+ if (!constant_argument->HasSmiValue()) { |
+ TraceInline(target, caller, |
+ "Constant length outside of valid inlining range."); |
+ return false; |
+ } |
+ array_length = constant_argument->Integer32Value(); |
+ if (array_length < 0 || array_length > kElementLoopUnrollThreshold) { |
+ TraceInline(target, caller, |
+ "Constant length outside of valid inlining range."); |
+ return false; |
} |
} |
+ TraceInline(target, caller, NULL); |
+ |
+ NoObservableSideEffectsScope no_effects(this); |
+ |
+ // Register on the site for deoptimization if the transition feedback changes. |
+ top_info()->dependencies()->AssumeTransitionStable(site); |
+ |
// Build the array. |
- JSArrayBuilder array_builder(this, |
- kind, |
- site_instruction, |
- constructor, |
- DISABLE_ALLOCATION_SITES); |
- HValue* new_object = argument_count == 0 |
- ? array_builder.AllocateEmptyArray() |
- : BuildAllocateArrayFromLength(&array_builder, Top()); |
+ ElementsKind kind = site->GetElementsKind(); |
+ HValue* capacity; |
+ HValue* length; |
+ if (array_length == 0) { |
+ STATIC_ASSERT(0 < JSArray::kPreallocatedArrayElements); |
+ const int initial_capacity = JSArray::kPreallocatedArrayElements; |
+ capacity = Add<HConstant>(initial_capacity); |
+ length = graph()->GetConstant0(); |
+ } else { |
+ length = Top(); |
+ capacity = length; |
+ kind = GetHoleyElementsKind(kind); |
+ } |
+ |
+ // These HForceRepresentations are because we store these as fields in the |
+ // objects we construct, and an int32-to-smi HChange could deopt. Accept |
+ // the deopt possibility now, before allocation occurs. |
+ length = AddUncasted<HForceRepresentation>(length, Representation::Smi()); |
+ capacity = AddUncasted<HForceRepresentation>(capacity, Representation::Smi()); |
+ |
+ // Generate size calculation code here in order to make it dominate |
+ // the JSArray allocation. |
+ HValue* elements_size = BuildCalculateElementsSize(kind, capacity); |
+ |
+ // Bail out for large objects. |
+ HValue* max_size = Add<HConstant>(Page::kMaxRegularHeapObjectSize); |
+ Add<HBoundsCheck>(elements_size, max_size); |
+ |
+ // Allocate (dealing with failure appropriately). |
+ AllocationSiteMode mode = DONT_TRACK_ALLOCATION_SITE; |
+ HAllocate* new_object = AllocateJSArrayObject(mode); |
+ |
+ // Fill in the fields: map, properties, length. |
+ Handle<Map> map_constant(isolate()->get_initial_js_array_map(kind)); |
+ HValue* map = Add<HConstant>(map_constant); |
+ |
+ BuildJSArrayHeader(new_object, map, |
+ nullptr, // set elements to empty fixed array |
+ mode, kind, nullptr, length); |
+ |
+ // Allocate and initialize the elements. |
+ HAllocate* elements = BuildAllocateElements(kind, elements_size); |
+ BuildInitializeElementsHeader(elements, kind, capacity); |
+ BuildFillElementsWithHole(elements, kind, graph()->GetConstant0(), capacity); |
+ |
+ // Set the elements. |
+ Add<HStoreNamedField>(new_object, HObjectAccess::ForElementsPointer(), |
+ elements); |
int args_to_drop = argument_count + (expression->IsCall() ? 2 : 1); |
Drop(args_to_drop); |
ast_context()->ReturnValue(new_object); |
+ return true; |
} |
@@ -10138,53 +9986,6 @@ static bool IsAllocationInlineable(Handle<JSFunction> constructor) { |
HAllocate::kMaxInlineSize; |
} |
- |
-bool HOptimizedGraphBuilder::IsCallArrayInlineable( |
- int argument_count, |
- Handle<AllocationSite> site) { |
- Handle<JSFunction> caller = current_info()->closure(); |
- Handle<JSFunction> target = array_function(); |
- // We should have the function plus array arguments on the environment stack. |
- DCHECK(environment()->length() >= (argument_count + 1)); |
- DCHECK(!site.is_null()); |
- |
- bool inline_ok = false; |
- if (site->CanInlineCall()) { |
- // We also want to avoid inlining in certain 1 argument scenarios. |
- if (argument_count == 1) { |
- HValue* argument = Top(); |
- if (argument->IsConstant()) { |
- // Do not inline if the constant length argument is not a smi or |
- // outside the valid range for unrolled loop initialization. |
- HConstant* constant_argument = HConstant::cast(argument); |
- if (constant_argument->HasSmiValue()) { |
- int value = constant_argument->Integer32Value(); |
- inline_ok = value >= 0 && value <= kElementLoopUnrollThreshold; |
- if (!inline_ok) { |
- TraceInline(target, caller, |
- "Constant length outside of valid inlining range."); |
- } |
- } |
- } else { |
- TraceInline(target, caller, |
- "Dont inline [new] Array(n) where n isn't constant."); |
- } |
- } else if (argument_count == 0) { |
- inline_ok = true; |
- } else { |
- TraceInline(target, caller, "Too many arguments to inline."); |
- } |
- } else { |
- TraceInline(target, caller, "AllocationSite requested no inlining."); |
- } |
- |
- if (inline_ok) { |
- TraceInline(target, caller, NULL); |
- } |
- return inline_ok; |
-} |
- |
- |
void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) { |
DCHECK(!HasStackOverflow()); |
DCHECK(current_block() != NULL); |
@@ -10278,7 +10079,7 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) { |
} else { |
// The constructor function is both an operand to the instruction and an |
// argument to the construct call. |
- if (TryHandleArrayCallNew(expr, function)) return; |
+ if (TryHandleArrayCall(expr, function)) return; |
} |
HValue* arity = Add<HConstant>(argument_count - 1); |