| Index: src/hydrogen.cc
|
| diff --git a/src/hydrogen.cc b/src/hydrogen.cc
|
| index 2e5e1740117035a27f13030f97a7c4ab28783678..c8aca403e26d5a675475880faafd9b3957e9ec47 100644
|
| --- a/src/hydrogen.cc
|
| +++ b/src/hydrogen.cc
|
| @@ -607,7 +607,8 @@ void HGraph::Verify(bool do_full_verify) const {
|
| block->predecessors()->first()->last_environment()->ast_id();
|
| for (int k = 0; k < block->predecessors()->length(); k++) {
|
| HBasicBlock* predecessor = block->predecessors()->at(k);
|
| - ASSERT(predecessor->end()->IsGoto());
|
| + ASSERT(predecessor->end()->IsGoto() ||
|
| + predecessor->end()->IsDeoptimize());
|
| ASSERT(predecessor->last_environment()->ast_id() == id);
|
| }
|
| }
|
| @@ -747,19 +748,20 @@ bool HGraph::IsStandardConstant(HConstant* constant) {
|
| HGraphBuilder::IfBuilder::IfBuilder(HGraphBuilder* builder)
|
| : builder_(builder),
|
| finished_(false),
|
| - deopt_then_(false),
|
| - deopt_else_(false),
|
| did_then_(false),
|
| did_else_(false),
|
| + did_else_if_(false),
|
| did_and_(false),
|
| did_or_(false),
|
| captured_(false),
|
| needs_compare_(true),
|
| + pending_merge_block_(false),
|
| split_edge_merge_block_(NULL),
|
| - merge_block_(NULL) {
|
| + merge_at_join_blocks_(NULL),
|
| + normal_merge_at_join_block_count_(0),
|
| + deopt_merge_at_join_block_count_(0) {
|
| HEnvironment* env = builder->environment();
|
| first_true_block_ = builder->CreateBasicBlock(env->Copy());
|
| - last_true_block_ = NULL;
|
| first_false_block_ = builder->CreateBasicBlock(env->Copy());
|
| }
|
|
|
| @@ -769,19 +771,20 @@ HGraphBuilder::IfBuilder::IfBuilder(
|
| HIfContinuation* continuation)
|
| : builder_(builder),
|
| finished_(false),
|
| - deopt_then_(false),
|
| - deopt_else_(false),
|
| did_then_(false),
|
| did_else_(false),
|
| + did_else_if_(false),
|
| did_and_(false),
|
| did_or_(false),
|
| captured_(false),
|
| needs_compare_(false),
|
| + pending_merge_block_(false),
|
| first_true_block_(NULL),
|
| - last_true_block_(NULL),
|
| first_false_block_(NULL),
|
| split_edge_merge_block_(NULL),
|
| - merge_block_(NULL) {
|
| + merge_at_join_blocks_(NULL),
|
| + normal_merge_at_join_block_count_(0),
|
| + deopt_merge_at_join_block_count_(0) {
|
| continuation->Continue(&first_true_block_,
|
| &first_false_block_);
|
| }
|
| @@ -789,6 +792,20 @@ HGraphBuilder::IfBuilder::IfBuilder(
|
|
|
| HControlInstruction* HGraphBuilder::IfBuilder::AddCompare(
|
| HControlInstruction* compare) {
|
| + ASSERT(did_then_ == did_else_);
|
| + if (did_else_) {
|
| + // Handle if-then-elseif
|
| + did_else_if_ = true;
|
| + did_else_ = false;
|
| + did_then_ = false;
|
| + did_and_ = false;
|
| + did_or_ = false;
|
| + pending_merge_block_ = false;
|
| + split_edge_merge_block_ = NULL;
|
| + HEnvironment* env = builder_->environment();
|
| + first_true_block_ = builder_->CreateBasicBlock(env->Copy());
|
| + first_false_block_ = builder_->CreateBasicBlock(env->Copy());
|
| + }
|
| if (split_edge_merge_block_ != NULL) {
|
| HEnvironment* env = first_false_block_->last_environment();
|
| HBasicBlock* split_edge =
|
| @@ -844,29 +861,30 @@ void HGraphBuilder::IfBuilder::And() {
|
|
|
| void HGraphBuilder::IfBuilder::CaptureContinuation(
|
| HIfContinuation* continuation) {
|
| + ASSERT(!did_else_if_);
|
| ASSERT(!finished_);
|
| ASSERT(!captured_);
|
| - HBasicBlock* true_block = last_true_block_ == NULL
|
| - ? first_true_block_
|
| - : last_true_block_;
|
| - HBasicBlock* false_block = did_else_ && (first_false_block_ != NULL)
|
| - ? builder_->current_block()
|
| - : first_false_block_;
|
| +
|
| + HBasicBlock* true_block = NULL;
|
| + HBasicBlock* false_block = NULL;
|
| + Finish(&true_block, &false_block);
|
| + ASSERT(true_block != NULL);
|
| + ASSERT(false_block != NULL);
|
| continuation->Capture(true_block, false_block);
|
| captured_ = true;
|
| + builder_->set_current_block(NULL);
|
| End();
|
| }
|
|
|
|
|
| void HGraphBuilder::IfBuilder::JoinContinuation(HIfContinuation* continuation) {
|
| + ASSERT(!did_else_if_);
|
| ASSERT(!finished_);
|
| ASSERT(!captured_);
|
| - ASSERT(did_then_);
|
| - if (!did_else_) Else();
|
| - HBasicBlock* true_block = last_true_block_ == NULL
|
| - ? first_true_block_
|
| - : last_true_block_;
|
| - HBasicBlock* false_block = builder_->current_block();
|
| + HBasicBlock* true_block = NULL;
|
| + HBasicBlock* false_block = NULL;
|
| + Finish(&true_block, &false_block);
|
| + merge_at_join_blocks_ = NULL;
|
| if (true_block != NULL && !true_block->IsFinished()) {
|
| ASSERT(continuation->IsTrueReachable());
|
| builder_->GotoNoSimulate(true_block, continuation->true_branch());
|
| @@ -897,6 +915,7 @@ void HGraphBuilder::IfBuilder::Then() {
|
| builder_->FinishCurrentBlock(branch);
|
| }
|
| builder_->set_current_block(first_true_block_);
|
| + pending_merge_block_ = true;
|
| }
|
|
|
|
|
| @@ -904,20 +923,17 @@ void HGraphBuilder::IfBuilder::Else() {
|
| ASSERT(did_then_);
|
| ASSERT(!captured_);
|
| ASSERT(!finished_);
|
| - last_true_block_ = builder_->current_block();
|
| + AddMergeAtJoinBlock(false);
|
| builder_->set_current_block(first_false_block_);
|
| + pending_merge_block_ = true;
|
| did_else_ = true;
|
| }
|
|
|
|
|
| void HGraphBuilder::IfBuilder::Deopt(const char* reason) {
|
| ASSERT(did_then_);
|
| - if (did_else_) {
|
| - deopt_else_ = true;
|
| - } else {
|
| - deopt_then_ = true;
|
| - }
|
| builder_->Add<HDeoptimize>(reason, Deoptimizer::EAGER);
|
| + AddMergeAtJoinBlock(true);
|
| }
|
|
|
|
|
| @@ -925,51 +941,99 @@ void HGraphBuilder::IfBuilder::Return(HValue* value) {
|
| HValue* parameter_count = builder_->graph()->GetConstantMinus1();
|
| builder_->FinishExitCurrentBlock(
|
| builder_->New<HReturn>(value, parameter_count));
|
| - if (did_else_) {
|
| - first_false_block_ = NULL;
|
| - } else {
|
| - first_true_block_ = NULL;
|
| + AddMergeAtJoinBlock(false);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::IfBuilder::AddMergeAtJoinBlock(bool deopt) {
|
| + if (!pending_merge_block_) return;
|
| + HBasicBlock* block = builder_->current_block();
|
| + ASSERT(block == NULL || !block->IsFinished());
|
| + MergeAtJoinBlock* record =
|
| + new(builder_->zone()) MergeAtJoinBlock(block, deopt,
|
| + merge_at_join_blocks_);
|
| + merge_at_join_blocks_ = record;
|
| + if (block != NULL) {
|
| + ASSERT(block->end() == NULL);
|
| + if (deopt) {
|
| + normal_merge_at_join_block_count_++;
|
| + } else {
|
| + deopt_merge_at_join_block_count_++;
|
| + }
|
| + }
|
| + builder_->set_current_block(NULL);
|
| + pending_merge_block_ = false;
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::IfBuilder::Finish() {
|
| + ASSERT(!finished_);
|
| + if (!did_then_) {
|
| + Then();
|
| + }
|
| + AddMergeAtJoinBlock(false);
|
| + if (!did_else_) {
|
| + Else();
|
| + AddMergeAtJoinBlock(false);
|
| + }
|
| + finished_ = true;
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::IfBuilder::Finish(HBasicBlock** then_continuation,
|
| + HBasicBlock** else_continuation) {
|
| + Finish();
|
| +
|
| + MergeAtJoinBlock* else_record = merge_at_join_blocks_;
|
| + if (else_continuation != NULL) {
|
| + *else_continuation = else_record->block_;
|
| }
|
| + MergeAtJoinBlock* then_record = else_record->next_;
|
| + if (then_continuation != NULL) {
|
| + *then_continuation = then_record->block_;
|
| + }
|
| + ASSERT(then_record->next_ == NULL);
|
| }
|
|
|
|
|
| void HGraphBuilder::IfBuilder::End() {
|
| - if (!captured_) {
|
| - ASSERT(did_then_);
|
| - if (!did_else_) {
|
| - last_true_block_ = builder_->current_block();
|
| - }
|
| - if (last_true_block_ == NULL || last_true_block_->IsFinished()) {
|
| - ASSERT(did_else_);
|
| - // Return on true. Nothing to do, just continue the false block.
|
| - } else if (first_false_block_ == NULL ||
|
| - (did_else_ && builder_->current_block()->IsFinished())) {
|
| - // Deopt on false. Nothing to do except switching to the true block.
|
| - builder_->set_current_block(last_true_block_);
|
| - } else {
|
| - merge_block_ = builder_->graph()->CreateBasicBlock();
|
| - ASSERT(!finished_);
|
| - if (!did_else_) Else();
|
| - ASSERT(!last_true_block_->IsFinished());
|
| - HBasicBlock* last_false_block = builder_->current_block();
|
| - ASSERT(!last_false_block->IsFinished());
|
| - if (deopt_then_) {
|
| - builder_->GotoNoSimulate(last_false_block, merge_block_);
|
| - builder_->PadEnvironmentForContinuation(last_true_block_,
|
| - merge_block_);
|
| - builder_->GotoNoSimulate(last_true_block_, merge_block_);
|
| - } else {
|
| - builder_->GotoNoSimulate(last_true_block_, merge_block_);
|
| - if (deopt_else_) {
|
| - builder_->PadEnvironmentForContinuation(last_false_block,
|
| - merge_block_);
|
| - }
|
| - builder_->GotoNoSimulate(last_false_block, merge_block_);
|
| + if (captured_) return;
|
| + Finish();
|
| +
|
| + int total_merged_blocks = normal_merge_at_join_block_count_ +
|
| + deopt_merge_at_join_block_count_;
|
| + ASSERT(total_merged_blocks >= 1);
|
| + HBasicBlock* merge_block = total_merged_blocks == 1
|
| + ? NULL : builder_->graph()->CreateBasicBlock();
|
| +
|
| + // Merge non-deopt blocks first to ensure environment has right size for
|
| + // padding.
|
| + MergeAtJoinBlock* current = merge_at_join_blocks_;
|
| + while (current != NULL) {
|
| + if (!current->deopt_ && current->block_ != NULL) {
|
| + // If there is only one block that makes it through to the end of the
|
| + // if, then just set it as the current block and continue rather then
|
| + // creating an unnecessary merge block.
|
| + if (total_merged_blocks == 1) {
|
| + builder_->set_current_block(current->block_);
|
| + return;
|
| }
|
| - builder_->set_current_block(merge_block_);
|
| + builder_->GotoNoSimulate(current->block_, merge_block);
|
| }
|
| + current = current->next_;
|
| }
|
| - finished_ = true;
|
| +
|
| + // Merge deopt blocks, padding when necessary.
|
| + current = merge_at_join_blocks_;
|
| + while (current != NULL) {
|
| + if (current->deopt_ && current->block_ != NULL) {
|
| + builder_->PadEnvironmentForContinuation(current->block_,
|
| + merge_block);
|
| + builder_->GotoNoSimulate(current->block_, merge_block);
|
| + }
|
| + current = current->next_;
|
| + }
|
| + builder_->set_current_block(merge_block);
|
| }
|
|
|
|
|
| @@ -1053,6 +1117,7 @@ void HGraphBuilder::LoopBuilder::Break() {
|
| }
|
|
|
| builder_->GotoNoSimulate(exit_trampoline_block_);
|
| + builder_->set_current_block(NULL);
|
| }
|
|
|
|
|
| @@ -1342,6 +1407,138 @@ void HGraphBuilder::BuildTransitionElementsKind(HValue* object,
|
| }
|
|
|
|
|
| +HValue* HGraphBuilder::BuildUncheckedDictionaryElementLoadHelper(
|
| + HValue* elements,
|
| + HValue* key,
|
| + HValue* hash,
|
| + HValue* mask,
|
| + int current_probe) {
|
| + if (current_probe == kNumberDictionaryProbes) {
|
| + return NULL;
|
| + }
|
| +
|
| + int32_t offset = SeededNumberDictionary::GetProbeOffset(current_probe);
|
| + HValue* raw_index = (current_probe == 0)
|
| + ? hash
|
| + : Add<HAdd>(hash, Add<HConstant>(offset));
|
| + raw_index = Add<HBitwise>(Token::BIT_AND, raw_index, mask);
|
| + int32_t entry_size = SeededNumberDictionary::kEntrySize;
|
| + raw_index = Add<HMul>(raw_index, Add<HConstant>(entry_size));
|
| + raw_index->ClearFlag(HValue::kCanOverflow);
|
| +
|
| + int32_t base_offset = SeededNumberDictionary::kElementsStartIndex;
|
| + HValue* key_index = Add<HAdd>(raw_index, Add<HConstant>(base_offset));
|
| + key_index->ClearFlag(HValue::kCanOverflow);
|
| +
|
| + HValue* candidate_key = Add<HLoadKeyed>(elements, key_index,
|
| + static_cast<HValue*>(NULL),
|
| + FAST_SMI_ELEMENTS);
|
| +
|
| + IfBuilder key_compare(this);
|
| + key_compare.IfNot<HCompareObjectEqAndBranch>(key, candidate_key);
|
| + key_compare.Then();
|
| + {
|
| + // Key at the current probe doesn't match, try at the next probe.
|
| + HValue* result = BuildUncheckedDictionaryElementLoadHelper(
|
| + elements, key, hash, mask, current_probe + 1);
|
| + if (result == NULL) {
|
| + key_compare.Deopt("probes exhausted in keyed load dictionary lookup");
|
| + result = graph()->GetConstantUndefined();
|
| + } else {
|
| + Push(result);
|
| + }
|
| + }
|
| + key_compare.Else();
|
| + {
|
| + // Key at current probe matches. Details must be zero, otherwise the
|
| + // dictionary element requires special handling.
|
| + HValue* details_index = Add<HAdd>(raw_index,
|
| + Add<HConstant>(base_offset + 2));
|
| + details_index->ClearFlag(HValue::kCanOverflow);
|
| +
|
| + HValue* details = Add<HLoadKeyed>(elements, details_index,
|
| + static_cast<HValue*>(NULL),
|
| + FAST_SMI_ELEMENTS);
|
| + IfBuilder details_compare(this);
|
| + details_compare.If<HCompareNumericAndBranch>(details,
|
| + graph()->GetConstant0(),
|
| + Token::NE);
|
| + details_compare.ThenDeopt("keyed load dictionary element not fast case");
|
| +
|
| + details_compare.Else();
|
| + {
|
| + // Key matches and details are zero --> fast case. Load and return the
|
| + // value.
|
| + HValue* result_index = Add<HAdd>(raw_index,
|
| + Add<HConstant>(base_offset + 1));
|
| + result_index->ClearFlag(HValue::kCanOverflow);
|
| +
|
| + Push(Add<HLoadKeyed>(elements, result_index,
|
| + static_cast<HValue*>(NULL),
|
| + FAST_ELEMENTS));
|
| + }
|
| + details_compare.End();
|
| + }
|
| + key_compare.End();
|
| +
|
| + return Pop();
|
| +}
|
| +
|
| +
|
| +HValue* HGraphBuilder::BuildElementIndexHash(HValue* index) {
|
| + int32_t seed_value = static_cast<uint32_t>(isolate()->heap()->HashSeed());
|
| + HValue* seed = Add<HConstant>(seed_value);
|
| + HValue* hash = Add<HBitwise>(Token::BIT_XOR, index, seed);
|
| +
|
| + // hash = ~hash + (hash << 15);
|
| + HValue* shifted_hash = Add<HShl>(hash, Add<HConstant>(15));
|
| + HValue* not_hash = Add<HBitwise>(Token::BIT_XOR, hash,
|
| + graph()->GetConstantMinus1());
|
| + hash = Add<HAdd>(shifted_hash, not_hash);
|
| +
|
| + // hash = hash ^ (hash >> 12);
|
| + shifted_hash = Add<HShr>(hash, Add<HConstant>(12));
|
| + hash = Add<HBitwise>(Token::BIT_XOR, hash, shifted_hash);
|
| +
|
| + // hash = hash + (hash << 2);
|
| + shifted_hash = Add<HShl>(hash, Add<HConstant>(2));
|
| + hash = Add<HAdd>(hash, shifted_hash);
|
| +
|
| + // hash = hash ^ (hash >> 4);
|
| + shifted_hash = Add<HShr>(hash, Add<HConstant>(4));
|
| + hash = Add<HBitwise>(Token::BIT_XOR, hash, shifted_hash);
|
| +
|
| + // hash = hash * 2057;
|
| + hash = Add<HMul>(hash, Add<HConstant>(2057));
|
| + hash->ClearFlag(HValue::kCanOverflow);
|
| +
|
| + // hash = hash ^ (hash >> 16);
|
| + shifted_hash = Add<HShr>(hash, Add<HConstant>(16));
|
| + return Add<HBitwise>(Token::BIT_XOR, hash, shifted_hash);
|
| +}
|
| +
|
| +
|
| +HValue* HGraphBuilder::BuildUncheckedDictionaryElementLoad(HValue* receiver,
|
| + HValue* key) {
|
| + HValue* elements = AddLoadElements(receiver);
|
| +
|
| + HValue* hash = BuildElementIndexHash(key);
|
| +
|
| + HValue* capacity = Add<HLoadKeyed>(
|
| + elements,
|
| + Add<HConstant>(NameDictionary::kCapacityIndex),
|
| + static_cast<HValue*>(NULL),
|
| + FAST_SMI_ELEMENTS);
|
| +
|
| + HValue* mask = Add<HSub>(capacity, graph()->GetConstant1());
|
| + mask->ChangeRepresentation(Representation::Integer32());
|
| + mask->ClearFlag(HValue::kCanOverflow);
|
| +
|
| + return BuildUncheckedDictionaryElementLoadHelper(elements, key,
|
| + hash, mask, 0);
|
| +}
|
| +
|
| +
|
| HValue* HGraphBuilder::BuildNumberToString(HValue* object,
|
| Handle<Type> type) {
|
| NoObservableSideEffectsScope scope(this);
|
| @@ -1861,6 +2058,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
|
| HInstruction* result = AddElementAccess(
|
| external_elements, key, val, bounds_check, elements_kind, is_store);
|
| negative_checker.ElseDeopt("Negative key encountered");
|
| + negative_checker.End();
|
| length_checker.End();
|
| return result;
|
| } else {
|
| @@ -1911,6 +2109,49 @@ 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");
|
| + } 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::BuildAllocateElements(ElementsKind kind,
|
| HValue* capacity) {
|
| int elements_size;
|
| @@ -2099,19 +2340,15 @@ 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->IsConstant() && to->IsConstant() &&
|
| - initial_capacity <= kLoopUnfoldLimit) {
|
| - HConstant* constant_from = HConstant::cast(from);
|
| - HConstant* constant_to = HConstant::cast(to);
|
| + static const int kLoopUnfoldLimit = 8;
|
| + STATIC_ASSERT(JSArray::kPreallocatedArrayElements <= kLoopUnfoldLimit);
|
| + int initial_capacity = -1;
|
| + if (from->IsInteger32Constant() && to->IsInteger32Constant()) {
|
| + int constant_from = from->GetInteger32Constant();
|
| + int constant_to = to->GetInteger32Constant();
|
|
|
| - if (constant_from->HasInteger32Value() &&
|
| - constant_from->Integer32Value() == 0 &&
|
| - constant_to->HasInteger32Value() &&
|
| - constant_to->Integer32Value() == initial_capacity) {
|
| - unfold_loop = true;
|
| + if (constant_from == 0 && constant_to <= kLoopUnfoldLimit) {
|
| + initial_capacity = constant_to;
|
| }
|
| }
|
|
|
| @@ -2121,7 +2358,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);
|
| @@ -2380,7 +2617,14 @@ HGraphBuilder::JSArrayBuilder::JSArrayBuilder(HGraphBuilder* builder,
|
|
|
|
|
| HValue* HGraphBuilder::JSArrayBuilder::EmitMapCode() {
|
| - if (kind_ == GetInitialFastElementsKind()) {
|
| + 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();
|
| @@ -2422,12 +2666,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;
|
| }
|
|
|
| @@ -2451,23 +2697,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.
|
| @@ -2501,7 +2746,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);
|
| }
|
| @@ -6707,6 +6952,11 @@ int HOptimizedGraphBuilder::InliningAstSize(Handle<JSFunction> target) {
|
| Handle<JSFunction> caller = current_info()->closure();
|
| Handle<SharedFunctionInfo> target_shared(target->shared());
|
|
|
| + // Always inline builtins marked for inlining.
|
| + if (target->IsBuiltin()) {
|
| + return target_shared->inline_builtin() ? 0 : kNotInlinable;
|
| + }
|
| +
|
| // Do a quick check on source code length to avoid parsing large
|
| // inlining candidates.
|
| if (target_shared->SourceSize() >
|
| @@ -6716,7 +6966,7 @@ int HOptimizedGraphBuilder::InliningAstSize(Handle<JSFunction> target) {
|
| }
|
|
|
| // Target must be inlineable.
|
| - if (!target->IsInlineable()) {
|
| + if (!target_shared->IsInlineable()) {
|
| TraceInline(target, caller, "target not inlineable");
|
| return kNotInlinable;
|
| }
|
| @@ -7096,9 +7346,6 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr,
|
| case kMathAbs:
|
| case kMathSqrt:
|
| case kMathLog:
|
| - case kMathSin:
|
| - case kMathCos:
|
| - case kMathTan:
|
| if (expr->arguments()->length() == 1) {
|
| HValue* argument = Pop();
|
| Drop(1); // Receiver.
|
| @@ -7177,9 +7424,6 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
|
| case kMathAbs:
|
| case kMathSqrt:
|
| case kMathLog:
|
| - case kMathSin:
|
| - case kMathCos:
|
| - case kMathTan:
|
| if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
|
| AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
|
| HValue* argument = Pop();
|
| @@ -7358,18 +7602,30 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
|
| if (prop != NULL) {
|
| if (!prop->key()->IsPropertyName()) {
|
| // Keyed function call.
|
| - CHECK_ALIVE(VisitArgument(prop->obj()));
|
| -
|
| + CHECK_ALIVE(VisitForValue(prop->obj()));
|
| CHECK_ALIVE(VisitForValue(prop->key()));
|
| +
|
| // Push receiver and key like the non-optimized code generator expects it.
|
| HValue* key = Pop();
|
| HValue* receiver = Pop();
|
| Push(key);
|
| - Push(receiver);
|
| -
|
| + Push(Add<HPushArgument>(receiver));
|
| CHECK_ALIVE(VisitArgumentList(expr->arguments()));
|
|
|
| - call = New<HCallKeyed>(key, argument_count);
|
| + if (expr->IsMonomorphic()) {
|
| + BuildCheckHeapObject(receiver);
|
| + ElementsKind kind = expr->KeyedArrayCallIsHoley()
|
| + ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS;
|
| +
|
| + Handle<Map> map(isolate()->get_initial_js_array_map(kind));
|
| +
|
| + HValue* function = BuildMonomorphicElementAccess(
|
| + receiver, key, NULL, NULL, map, false, STANDARD_STORE);
|
| +
|
| + call = New<HCallFunction>(function, argument_count);
|
| + } else {
|
| + call = New<HCallKeyed>(key, argument_count);
|
| + }
|
| Drop(argument_count + 1); // 1 is the key.
|
| return ast_context()->ReturnInstruction(call, expr->id());
|
| }
|
| @@ -7538,6 +7794,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() &&
|
| @@ -7547,6 +7868,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);
|
| @@ -7555,14 +7920,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);
|
|
|
| @@ -7649,19 +8015,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());
|
| }
|
| }
|
| @@ -8198,14 +8569,27 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(
|
| // Special case for string addition here.
|
| if (op == Token::ADD &&
|
| (left_type->Is(Type::String()) || right_type->Is(Type::String()))) {
|
| + // Validate type feedback for left argument.
|
| if (left_type->Is(Type::String())) {
|
| IfBuilder if_isstring(this);
|
| if_isstring.If<HIsStringAndBranch>(left);
|
| if_isstring.Then();
|
| if_isstring.ElseDeopt("Expected string for LHS of binary operation");
|
| - } else if (left_type->Is(Type::Number())) {
|
| + }
|
| +
|
| + // Validate type feedback for right argument.
|
| + if (right_type->Is(Type::String())) {
|
| + IfBuilder if_isstring(this);
|
| + if_isstring.If<HIsStringAndBranch>(right);
|
| + if_isstring.Then();
|
| + if_isstring.ElseDeopt("Expected string for RHS of binary operation");
|
| + }
|
| +
|
| + // Convert left argument as necessary.
|
| + if (left_type->Is(Type::Number())) {
|
| + ASSERT(right_type->Is(Type::String()));
|
| left = BuildNumberToString(left, left_type);
|
| - } else {
|
| + } else if (!left_type->Is(Type::String())) {
|
| ASSERT(right_type->Is(Type::String()));
|
| HValue* function = AddLoadJSBuiltin(Builtins::STRING_ADD_RIGHT);
|
| Add<HPushArgument>(left);
|
| @@ -8213,14 +8597,11 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(
|
| return NewUncasted<HInvokeFunction>(function, 2);
|
| }
|
|
|
| - if (right_type->Is(Type::String())) {
|
| - IfBuilder if_isstring(this);
|
| - if_isstring.If<HIsStringAndBranch>(right);
|
| - if_isstring.Then();
|
| - if_isstring.ElseDeopt("Expected string for RHS of binary operation");
|
| - } else if (right_type->Is(Type::Number())) {
|
| + // Convert right argument as necessary.
|
| + if (right_type->Is(Type::Number())) {
|
| + ASSERT(left_type->Is(Type::String()));
|
| right = BuildNumberToString(right, right_type);
|
| - } else {
|
| + } else if (!right_type->Is(Type::String())) {
|
| ASSERT(left_type->Is(Type::String()));
|
| HValue* function = AddLoadJSBuiltin(Builtins::STRING_ADD_LEFT);
|
| Add<HPushArgument>(left);
|
| @@ -9116,6 +9497,15 @@ void HOptimizedGraphBuilder::GenerateIsFunction(CallRuntime* call) {
|
| }
|
|
|
|
|
| +void HOptimizedGraphBuilder::GenerateIsMinusZero(CallRuntime* call) {
|
| + ASSERT(call->arguments()->length() == 1);
|
| + CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| + HValue* value = Pop();
|
| + HCompareMinusZeroAndBranch* result = New<HCompareMinusZeroAndBranch>(value);
|
| + return ast_context()->ReturnControl(result, call->id());
|
| +}
|
| +
|
| +
|
| void HOptimizedGraphBuilder::GenerateHasCachedArrayIndex(CallRuntime* call) {
|
| ASSERT(call->arguments()->length() == 1);
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| @@ -9499,36 +9889,6 @@ void HOptimizedGraphBuilder::GenerateMathPow(CallRuntime* call) {
|
| }
|
|
|
|
|
| -void HOptimizedGraphBuilder::GenerateMathSin(CallRuntime* call) {
|
| - ASSERT_EQ(1, call->arguments()->length());
|
| - CHECK_ALIVE(VisitArgumentList(call->arguments()));
|
| - HCallStub* result = New<HCallStub>(CodeStub::TranscendentalCache, 1);
|
| - result->set_transcendental_type(TranscendentalCache::SIN);
|
| - Drop(1);
|
| - return ast_context()->ReturnInstruction(result, call->id());
|
| -}
|
| -
|
| -
|
| -void HOptimizedGraphBuilder::GenerateMathCos(CallRuntime* call) {
|
| - ASSERT_EQ(1, call->arguments()->length());
|
| - CHECK_ALIVE(VisitArgumentList(call->arguments()));
|
| - HCallStub* result = New<HCallStub>(CodeStub::TranscendentalCache, 1);
|
| - result->set_transcendental_type(TranscendentalCache::COS);
|
| - Drop(1);
|
| - return ast_context()->ReturnInstruction(result, call->id());
|
| -}
|
| -
|
| -
|
| -void HOptimizedGraphBuilder::GenerateMathTan(CallRuntime* call) {
|
| - ASSERT_EQ(1, call->arguments()->length());
|
| - CHECK_ALIVE(VisitArgumentList(call->arguments()));
|
| - HCallStub* result = New<HCallStub>(CodeStub::TranscendentalCache, 1);
|
| - result->set_transcendental_type(TranscendentalCache::TAN);
|
| - Drop(1);
|
| - return ast_context()->ReturnInstruction(result, call->id());
|
| -}
|
| -
|
| -
|
| void HOptimizedGraphBuilder::GenerateMathLog(CallRuntime* call) {
|
| ASSERT_EQ(1, call->arguments()->length());
|
| CHECK_ALIVE(VisitArgumentList(call->arguments()));
|
|
|