Index: src/hydrogen.cc |
diff --git a/src/hydrogen.cc b/src/hydrogen.cc |
index b47ec8010d25d10a76a1b203fa4bde3e5f39ba01..c73d8d6d80b394d64085b550ebbfd809b47403d9 100644 |
--- a/src/hydrogen.cc |
+++ b/src/hydrogen.cc |
@@ -605,7 +605,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); |
} |
} |
@@ -745,19 +746,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()); |
} |
@@ -767,19 +769,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_); |
} |
@@ -787,6 +790,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 = |
@@ -842,29 +859,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()); |
@@ -895,6 +913,7 @@ void HGraphBuilder::IfBuilder::Then() { |
builder_->FinishCurrentBlock(branch); |
} |
builder_->set_current_block(first_true_block_); |
+ pending_merge_block_ = true; |
} |
@@ -902,20 +921,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); |
} |
@@ -923,51 +939,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); |
} |
@@ -1051,6 +1115,7 @@ void HGraphBuilder::LoopBuilder::Break() { |
} |
builder_->GotoNoSimulate(exit_trampoline_block_); |
+ builder_->set_current_block(NULL); |
} |
@@ -1859,6 +1924,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 { |
@@ -1940,9 +2006,10 @@ HValue* HGraphBuilder::BuildAllocateArrayFromLength( |
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 |
} |
- Push(checked_length); // capacity |
- Push(checked_length); // length |
if_builder.End(); |
// Figure out total size |