| Index: src/hydrogen.cc
|
| diff --git a/src/hydrogen.cc b/src/hydrogen.cc
|
| index 33586f347ef85a5911c5315a963c23e3f2c55188..b47ec8010d25d10a76a1b203fa4bde3e5f39ba01 100644
|
| --- a/src/hydrogen.cc
|
| +++ b/src/hydrogen.cc
|
| @@ -1909,6 +1909,48 @@ 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();
|
| + HValue* new_object = array_length == 0
|
| + ? array_builder->AllocateEmptyArray()
|
| + : array_builder->AllocateArray(length_argument, length_argument);
|
| + return new_object;
|
| + }
|
| +
|
| + HValue* constant_zero = graph()->GetConstant0();
|
| + HConstant* max_alloc_length =
|
| + Add<HConstant>(JSObject::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("Holey array despite packed elements_kind feedback");
|
| + }
|
| + 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::BuildAllocateElements(ElementsKind kind,
|
| HValue* capacity) {
|
| int elements_size;
|
| @@ -2097,19 +2139,18 @@ void HGraphBuilder::BuildFillElementsWithHole(HValue* elements,
|
| : Add<HConstant>(nan_double);
|
|
|
| // Special loop unfolding case
|
| - static const int kLoopUnfoldLimit = 4;
|
| - bool unfold_loop = false;
|
| - int initial_capacity = JSArray::kPreallocatedArrayElements;
|
| - if (from->ActualValue()->IsConstant() && to->ActualValue()->IsConstant() &&
|
| - initial_capacity <= kLoopUnfoldLimit) {
|
| + static const int kLoopUnfoldLimit = 8;
|
| + STATIC_ASSERT(JSArray::kPreallocatedArrayElements <= kLoopUnfoldLimit);
|
| + int initial_capacity = -1;
|
| + if (from->ActualValue()->IsConstant() && to->ActualValue()->IsConstant()) {
|
| HConstant* constant_from = HConstant::cast(from->ActualValue());
|
| HConstant* constant_to = HConstant::cast(to->ActualValue());
|
|
|
| if (constant_from->HasInteger32Value() &&
|
| constant_from->Integer32Value() == 0 &&
|
| constant_to->HasInteger32Value() &&
|
| - constant_to->Integer32Value() == initial_capacity) {
|
| - unfold_loop = true;
|
| + constant_to->Integer32Value() <= kLoopUnfoldLimit) {
|
| + initial_capacity = constant_to->Integer32Value();
|
| }
|
| }
|
|
|
| @@ -2119,7 +2160,7 @@ void HGraphBuilder::BuildFillElementsWithHole(HValue* elements,
|
| elements_kind = FAST_HOLEY_ELEMENTS;
|
| }
|
|
|
| - if (unfold_loop) {
|
| + if (initial_capacity >= 0) {
|
| for (int i = 0; i < initial_capacity; i++) {
|
| HInstruction* key = Add<HConstant>(i);
|
| Add<HStoreKeyed>(elements, key, hole, elements_kind);
|
| @@ -2378,6 +2419,13 @@ HGraphBuilder::JSArrayBuilder::JSArrayBuilder(HGraphBuilder* builder,
|
|
|
|
|
| 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 (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.
|
| @@ -2420,12 +2468,14 @@ HValue* HGraphBuilder::JSArrayBuilder::EstablishAllocationSize(
|
|
|
| HInstruction* elements_size_value =
|
| builder()->Add<HConstant>(elements_size());
|
| - HInstruction* mul = builder()->Add<HMul>(length_node, elements_size_value);
|
| - mul->ClearFlag(HValue::kCanOverflow);
|
| -
|
| + HInstruction* mul = HMul::NewImul(builder()->zone(), builder()->context(),
|
| + length_node, elements_size_value);
|
| + builder()->AddInstruction(mul);
|
| HInstruction* base = builder()->Add<HConstant>(base_size);
|
| - HInstruction* total_size = builder()->Add<HAdd>(base, mul);
|
| + HInstruction* total_size = HAdd::New(builder()->zone(), builder()->context(),
|
| + base, mul);
|
| total_size->ClearFlag(HValue::kCanOverflow);
|
| + builder()->AddInstruction(total_size);
|
| return total_size;
|
| }
|
|
|
| @@ -2449,23 +2499,22 @@ HValue* HGraphBuilder::JSArrayBuilder::AllocateEmptyArray() {
|
| HConstant* capacity = builder()->Add<HConstant>(initial_capacity());
|
| return AllocateArray(size_in_bytes,
|
| capacity,
|
| - builder()->graph()->GetConstant0(),
|
| - true);
|
| + builder()->graph()->GetConstant0());
|
| }
|
|
|
|
|
| HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* capacity,
|
| HValue* length_field,
|
| - bool fill_with_hole) {
|
| + FillMode fill_mode) {
|
| HValue* size_in_bytes = EstablishAllocationSize(capacity);
|
| - return AllocateArray(size_in_bytes, capacity, length_field, fill_with_hole);
|
| + return AllocateArray(size_in_bytes, capacity, length_field, fill_mode);
|
| }
|
|
|
|
|
| HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* size_in_bytes,
|
| HValue* capacity,
|
| HValue* length_field,
|
| - bool fill_with_hole) {
|
| + 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.
|
| @@ -2499,7 +2548,7 @@ HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* size_in_bytes,
|
| // Initialize the elements
|
| builder()->BuildInitializeElementsHeader(elements_location_, kind_, capacity);
|
|
|
| - if (fill_with_hole) {
|
| + if (fill_mode == FILL_WITH_HOLE) {
|
| builder()->BuildFillElementsWithHole(elements_location_, kind_,
|
| graph()->GetConstant0(), capacity);
|
| }
|
| @@ -7535,6 +7584,71 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
|
| }
|
|
|
|
|
| +void HOptimizedGraphBuilder::BuildInlinedCallNewArray(CallNew* expr) {
|
| + NoObservableSideEffectsScope no_effects(this);
|
| +
|
| + int argument_count = expr->arguments()->length();
|
| + // We should at least have the constructor on the expression stack.
|
| + HValue* constructor = environment()->ExpressionStackAt(argument_count);
|
| +
|
| + ElementsKind kind = expr->elements_kind();
|
| + Handle<Cell> cell = expr->allocation_info_cell();
|
| + AllocationSite* site = AllocationSite::cast(cell->value());
|
| +
|
| + // Register on the site for deoptimization if the cell value changes.
|
| + site->AddDependentCompilationInfo(AllocationSite::TRANSITIONS, top_info());
|
| + HInstruction* cell_instruction = Add<HConstant>(cell);
|
| +
|
| + // 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);
|
| + ASSERT(constant_argument->HasSmiValue());
|
| + int constant_array_size = constant_argument->Integer32Value();
|
| + if (constant_array_size != 0) {
|
| + kind = GetHoleyElementsKind(kind);
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Build the array.
|
| + JSArrayBuilder array_builder(this,
|
| + kind,
|
| + cell_instruction,
|
| + constructor,
|
| + DISABLE_ALLOCATION_SITES);
|
| + HValue* new_object;
|
| + if (argument_count == 0) {
|
| + new_object = array_builder.AllocateEmptyArray();
|
| + } else if (argument_count == 1) {
|
| + HValue* argument = environment()->Top();
|
| + new_object = BuildAllocateArrayFromLength(&array_builder, argument);
|
| + } else {
|
| + HValue* length = Add<HConstant>(argument_count);
|
| + // Smi arrays need to initialize array elements with the hole because
|
| + // bailout could occur if the arguments don't fit in a smi.
|
| + //
|
| + // TODO(mvstanton): If all the arguments are constants in smi range, then
|
| + // we could set fill_with_hole to false and save a few instructions.
|
| + JSArrayBuilder::FillMode fill_mode = IsFastSmiElementsKind(kind)
|
| + ? JSArrayBuilder::FILL_WITH_HOLE
|
| + : JSArrayBuilder::DONT_FILL_WITH_HOLE;
|
| + new_object = array_builder.AllocateArray(length, length, fill_mode);
|
| + HValue* elements = array_builder.GetElementsLocation();
|
| + for (int i = 0; i < argument_count; i++) {
|
| + HValue* value = environment()->ExpressionStackAt(argument_count - i - 1);
|
| + HValue* constant_i = Add<HConstant>(i);
|
| + Add<HStoreKeyed>(elements, constant_i, value, kind);
|
| + }
|
| + }
|
| +
|
| + Drop(argument_count + 1); // drop constructor and args.
|
| + ast_context()->ReturnValue(new_object);
|
| +}
|
| +
|
| +
|
| // Checks whether allocation using the given constructor can be inlined.
|
| static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
|
| return constructor->has_initial_map() &&
|
| @@ -7544,6 +7658,50 @@ static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
|
| }
|
|
|
|
|
| +bool HOptimizedGraphBuilder::IsCallNewArrayInlineable(CallNew* expr) {
|
| + bool inline_ok = false;
|
| + Handle<JSFunction> caller = current_info()->closure();
|
| + Handle<JSFunction> target(isolate()->global_context()->array_function(),
|
| + isolate());
|
| + int argument_count = expr->arguments()->length();
|
| + // We should have the function plus array arguments on the environment stack.
|
| + ASSERT(environment()->length() >= (argument_count + 1));
|
| + Handle<Cell> cell = expr->allocation_info_cell();
|
| + AllocationSite* site = AllocationSite::cast(cell->value());
|
| + 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 a fast array.
|
| + HConstant* constant_argument = HConstant::cast(argument);
|
| + if (constant_argument->HasSmiValue()) {
|
| + int value = constant_argument->Integer32Value();
|
| + inline_ok = value >= 0 &&
|
| + value < JSObject::kInitialMaxFastElementArray;
|
| + if (!inline_ok) {
|
| + TraceInline(target, caller,
|
| + "Length outside of valid array range");
|
| + }
|
| + }
|
| + } else {
|
| + inline_ok = true;
|
| + }
|
| + } else {
|
| + inline_ok = true;
|
| + }
|
| + } else {
|
| + TraceInline(target, caller, "AllocationSite requested no inlining.");
|
| + }
|
| +
|
| + if (inline_ok) {
|
| + TraceInline(target, caller, NULL);
|
| + }
|
| + return inline_ok;
|
| +}
|
| +
|
| +
|
| void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
|
| ASSERT(!HasStackOverflow());
|
| ASSERT(current_block() != NULL);
|
| @@ -7552,14 +7710,15 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
|
| int argument_count = expr->arguments()->length() + 1; // Plus constructor.
|
| Factory* factory = isolate()->factory();
|
|
|
| + // The constructor function is on the stack in the unoptimized code
|
| + // during evaluation of the arguments.
|
| + CHECK_ALIVE(VisitForValue(expr->expression()));
|
| + HValue* function = Top();
|
| + CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
| +
|
| if (FLAG_inline_construct &&
|
| expr->IsMonomorphic() &&
|
| IsAllocationInlineable(expr->target())) {
|
| - // The constructor function is on the stack in the unoptimized code
|
| - // during evaluation of the arguments.
|
| - CHECK_ALIVE(VisitForValue(expr->expression()));
|
| - HValue* function = Top();
|
| - CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
| Handle<JSFunction> constructor = expr->target();
|
| HValue* check = Add<HCheckValue>(function, constructor);
|
|
|
| @@ -7646,19 +7805,24 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
|
| // argument to the construct call.
|
| Handle<JSFunction> array_function(
|
| isolate()->global_context()->array_function(), isolate());
|
| - CHECK_ALIVE(VisitArgument(expr->expression()));
|
| - HValue* constructor = HPushArgument::cast(Top())->argument();
|
| - CHECK_ALIVE(VisitArgumentList(expr->arguments()));
|
| + bool use_call_new_array = expr->target().is_identical_to(array_function);
|
| + Handle<Cell> cell = expr->allocation_info_cell();
|
| + if (use_call_new_array && IsCallNewArrayInlineable(expr)) {
|
| + // Verify we are still calling the array function for our native context.
|
| + Add<HCheckValue>(function, array_function);
|
| + BuildInlinedCallNewArray(expr);
|
| + return;
|
| + }
|
| +
|
| HBinaryCall* call;
|
| - if (expr->target().is_identical_to(array_function)) {
|
| - Handle<Cell> cell = expr->allocation_info_cell();
|
| - Add<HCheckValue>(constructor, array_function);
|
| - call = New<HCallNewArray>(constructor, argument_count,
|
| - cell, expr->elements_kind());
|
| + if (use_call_new_array) {
|
| + Add<HCheckValue>(function, array_function);
|
| + call = New<HCallNewArray>(function, argument_count, cell,
|
| + expr->elements_kind());
|
| } else {
|
| - call = New<HCallNew>(constructor, argument_count);
|
| + call = New<HCallNew>(function, argument_count);
|
| }
|
| - Drop(argument_count);
|
| + PreProcessCall(call);
|
| return ast_context()->ReturnInstruction(call, expr->id());
|
| }
|
| }
|
|
|