| 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);
|
|
|