Index: src/hydrogen.cc |
diff --git a/src/hydrogen.cc b/src/hydrogen.cc |
index 857bbf9d37ac401e738757832aefeb373ca0c216..7ec1f5bf95e1cf5b7b28405db9402b974cb822e9 100644 |
--- a/src/hydrogen.cc |
+++ b/src/hydrogen.cc |
@@ -70,7 +70,8 @@ HBasicBlock::HBasicBlock(HGraph* graph) |
deleted_phis_(4), |
parent_loop_header_(NULL), |
is_inline_return_target_(false), |
- is_deoptimizing_(false) { } |
+ is_deoptimizing_(false), |
+ dominates_loop_successors_(false) { } |
void HBasicBlock::AttachLoopInformation() { |
@@ -315,6 +316,62 @@ void HBasicBlock::AssignCommonDominator(HBasicBlock* other) { |
} |
+void HBasicBlock::AssignLoopSuccessorDominators() { |
+ // Mark blocks that dominate all subsequent reachable blocks inside their |
+ // loop. Exploit the fact that blocks are sorted in reverse post order. When |
+ // the loop is visited in increasing block id order, if the number of |
+ // non-loop-exiting successor edges at the dominator_candidate block doesn't |
+ // exceed the number of previously encountered predecessor edges, there is no |
+ // path from the loop header to any block with higher id that doesn't go |
+ // through the dominator_candidate block. In this case, the |
+ // dominator_candidate block is guaranteed to dominate all blocks reachable |
+ // from it with higher ids. |
+ HBasicBlock* last = loop_information()->GetLastBackEdge(); |
+ int outstanding_successors = 1; // one edge from the pre-header |
+ // Header always dominates everything. |
+ MarkAsLoopSuccessorDominator(); |
+ for (int j = block_id(); j <= last->block_id(); ++j) { |
+ HBasicBlock* dominator_candidate = graph_->blocks()->at(j); |
+ for (HPredecessorIterator it(dominator_candidate); !it.Done(); |
+ it.Advance()) { |
+ HBasicBlock* predecessor = it.Current(); |
+ // Don't count back edges. |
+ if (predecessor->block_id() < dominator_candidate->block_id()) { |
+ outstanding_successors--; |
+ } |
+ } |
+ |
+ // If more successors than predecessors have been seen in the loop up to |
+ // now, it's not possible to guarantee that the current block dominates |
+ // all of the blocks with higher IDs. In this case, assume conservatively |
+ // that those paths through loop that don't go through the current block |
+ // contain all of the loop's dependencies. Also be careful to record |
+ // dominator information about the current loop that's being processed, |
+ // and not nested loops, which will be processed when |
+ // AssignLoopSuccessorDominators gets called on their header. |
+ ASSERT(outstanding_successors >= 0); |
+ HBasicBlock* parent_loop_header = dominator_candidate->parent_loop_header(); |
+ if (outstanding_successors == 0 && |
+ (parent_loop_header == this && !dominator_candidate->IsLoopHeader())) { |
+ dominator_candidate->MarkAsLoopSuccessorDominator(); |
+ } |
+ HControlInstruction* end = dominator_candidate->end(); |
+ for (HSuccessorIterator it(end); !it.Done(); it.Advance()) { |
+ HBasicBlock* successor = it.Current(); |
+ // Only count successors that remain inside the loop and don't loop back |
+ // to a loop header. |
+ if (successor->block_id() > dominator_candidate->block_id() && |
+ successor->block_id() <= last->block_id()) { |
+ // Backwards edges must land on loop headers. |
+ ASSERT(successor->block_id() > dominator_candidate->block_id() || |
+ successor->IsLoopHeader()); |
+ outstanding_successors++; |
+ } |
+ } |
+ } |
+} |
+ |
+ |
int HBasicBlock::PredecessorIndexOf(HBasicBlock* predecessor) const { |
for (int i = 0; i < predecessors_.length(); ++i) { |
if (predecessors_[i] == predecessor) return i; |
@@ -750,10 +807,12 @@ void HGraph::Postorder(HBasicBlock* block, |
void HGraph::AssignDominators() { |
HPhase phase("Assign dominators", this); |
for (int i = 0; i < blocks_.length(); ++i) { |
- if (blocks_[i]->IsLoopHeader()) { |
+ HBasicBlock* block = blocks_[i]; |
+ if (block->IsLoopHeader()) { |
// Only the first predecessor of a loop header is from outside the loop. |
// All others are back edges, and thus cannot dominate the loop header. |
- blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->first()); |
+ block->AssignCommonDominator(block->predecessors()->first()); |
+ block->AssignLoopSuccessorDominators(); |
} else { |
for (int j = blocks_[i]->predecessors()->length() - 1; j >= 0; --j) { |
blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j)); |
@@ -1371,7 +1430,8 @@ class HGlobalValueNumberer BASE_EMBEDDED { |
void LoopInvariantCodeMotion(); |
void ProcessLoopBlock(HBasicBlock* block, |
HBasicBlock* before_loop, |
- GVNFlagSet loop_kills); |
+ GVNFlagSet loop_kills, |
+ GVNFlagSet* accumulated_first_time_depends); |
bool AllowCodeMotion(); |
bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header); |
@@ -1396,6 +1456,7 @@ class HGlobalValueNumberer BASE_EMBEDDED { |
bool HGlobalValueNumberer::Analyze() { |
+ removed_side_effects_ = false; |
ComputeBlockSideEffects(); |
if (FLAG_loop_invariant_code_motion) { |
LoopInvariantCodeMotion(); |
@@ -1407,6 +1468,12 @@ bool HGlobalValueNumberer::Analyze() { |
void HGlobalValueNumberer::ComputeBlockSideEffects() { |
+ // The Analyze phase of GVN can be called multiple times. Clear loop side |
+ // effects before computing them to erase the contents from previous Analyze |
+ // passes. |
+ for (int i = 0; i < loop_side_effects_.length(); ++i) { |
+ loop_side_effects_[i].RemoveAll(); |
+ } |
for (int i = graph_->blocks()->length() - 1; i >= 0; --i) { |
// Compute side effects for the block. |
HBasicBlock* block = graph_->blocks()->at(i); |
@@ -1444,18 +1511,22 @@ void HGlobalValueNumberer::LoopInvariantCodeMotion() { |
block->block_id(), |
side_effects.ToIntegral()); |
+ GVNFlagSet accumulated_first_time_depends; |
HBasicBlock* last = block->loop_information()->GetLastBackEdge(); |
for (int j = block->block_id(); j <= last->block_id(); ++j) { |
- ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects); |
+ ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects, |
+ &accumulated_first_time_depends); |
} |
} |
} |
} |
-void HGlobalValueNumberer::ProcessLoopBlock(HBasicBlock* block, |
- HBasicBlock* loop_header, |
- GVNFlagSet loop_kills) { |
+void HGlobalValueNumberer::ProcessLoopBlock( |
+ HBasicBlock* block, |
+ HBasicBlock* loop_header, |
+ GVNFlagSet loop_kills, |
+ GVNFlagSet* accumulated_first_time_depends) { |
HBasicBlock* pre_header = loop_header->predecessors()->at(0); |
GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills); |
TraceGVN("Loop invariant motion for B%d depends_flags=0x%x\n", |
@@ -1464,25 +1535,65 @@ void HGlobalValueNumberer::ProcessLoopBlock(HBasicBlock* block, |
HInstruction* instr = block->first(); |
while (instr != NULL) { |
HInstruction* next = instr->next(); |
- if (instr->CheckFlag(HValue::kUseGVN) && |
- !instr->gvn_flags().ContainsAnyOf(depends_flags)) { |
- TraceGVN("Checking instruction %d (%s)\n", |
+ bool hoisted = false; |
+ if (instr->CheckFlag(HValue::kUseGVN)) { |
+ TraceGVN("Checking instruction %d (%s) instruction GVN flags 0x%X, " |
+ "loop kills 0x%X\n", |
instr->id(), |
- instr->Mnemonic()); |
- bool inputs_loop_invariant = true; |
- for (int i = 0; i < instr->OperandCount(); ++i) { |
- if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) { |
- inputs_loop_invariant = false; |
- } |
+ instr->Mnemonic(), |
+ instr->gvn_flags().ToIntegral(), |
+ depends_flags.ToIntegral()); |
+ bool can_hoist = !instr->gvn_flags().ContainsAnyOf(depends_flags); |
+ if (!can_hoist && instr->IsTransitionElementsKind()) { |
+ // It's only possible to hoist one time side effects if there are no |
+ // dependencies on their changes from the loop header to the current |
+ // instruction. |
+ GVNFlagSet converted_changes = |
+ HValue::ConvertChangesToDependsFlags(instr->ChangesFlags()); |
+ TraceGVN("Checking dependencies on one-time instruction %d (%s) " |
+ "converted changes 0x%X, accumulated depends 0x%X\n", |
+ instr->id(), |
+ instr->Mnemonic(), |
+ converted_changes.ToIntegral(), |
+ accumulated_first_time_depends->ToIntegral()); |
+ // It's possible to hoist one-time side effects from the current loop |
+ // loop only if they dominate all of the successor blocks in the same |
+ // loop and there are not any instructions that have Changes/DependsOn |
+ // that intervene between it and the beginning of the loop header. |
+ bool in_nested_loop = block != loop_header && |
+ ((block->parent_loop_header() != loop_header) || |
+ block->IsLoopHeader()); |
+ can_hoist = !in_nested_loop && |
+ block->IsLoopSuccessorDominator() && |
+ !accumulated_first_time_depends->ContainsAnyOf(converted_changes); |
} |
- if (inputs_loop_invariant && ShouldMove(instr, loop_header)) { |
- TraceGVN("Found loop invariant instruction %d\n", instr->id()); |
- // Move the instruction out of the loop. |
- instr->Unlink(); |
- instr->InsertBefore(pre_header->end()); |
+ if (can_hoist) { |
+ bool inputs_loop_invariant = true; |
+ for (int i = 0; i < instr->OperandCount(); ++i) { |
+ if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) { |
+ inputs_loop_invariant = false; |
+ } |
+ } |
+ |
+ if (inputs_loop_invariant && ShouldMove(instr, loop_header)) { |
+ TraceGVN("Hoisting loop invariant instruction %d\n", instr->id()); |
+ // Move the instruction out of the loop. |
+ instr->Unlink(); |
+ instr->InsertBefore(pre_header->end()); |
+ if (instr->HasSideEffects()) removed_side_effects_ = true; |
+ hoisted = true; |
+ } |
} |
} |
+ if (!hoisted) { |
+ // If an instruction is not hoisted, we have to account for its side |
+ // effects when hoisting later HTransitionElementsKind instructions. |
+ accumulated_first_time_depends->Add(instr->DependsOnFlags()); |
+ GVNFlagSet converted_changes = |
+ HValue::ConvertChangesToDependsFlags(instr->SideEffectFlags()); |
+ accumulated_first_time_depends->Add(converted_changes); |
+ } |
instr = next; |
} |
} |
@@ -2390,7 +2501,8 @@ HGraph* HGraphBuilder::CreateGraph() { |
// could only be discovered by removing side-effect-generating instructions |
// during the first pass. |
if (FLAG_smi_only_arrays && removed_side_effects) { |
- gvn.Analyze(); |
+ removed_side_effects = gvn.Analyze(); |
+ ASSERT(!removed_side_effects); |
} |
} |
@@ -7260,7 +7372,10 @@ void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) { |
} |
PrintEmptyProperty("xhandlers"); |
- PrintEmptyProperty("flags"); |
+ const char* flags = current->IsLoopSuccessorDominator() |
+ ? "dom-loop-succ" |
+ : ""; |
+ PrintStringProperty("flags", flags); |
if (current->dominator() != NULL) { |
PrintBlockProperty("dominator", current->dominator()->block_id()); |