Chromium Code Reviews| Index: src/hydrogen.cc |
| diff --git a/src/hydrogen.cc b/src/hydrogen.cc |
| index 3e6f0504b4c09d670f3a74233fac2c0ac4f7def1..86463f6fcbb16a80021a2a9f6c2094aacd71ec50 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,65 @@ 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_sucsessors = 1; // one edge from the pre-header |
|
fschneider
2012/02/02 14:59:50
s/sucsessors/successors/g
danno
2012/02/06 16:54:57
Done.
|
| + // Header always dominates everything. |
| + MarkAsLoopSuccessorDominator(); |
| + for (int j = block_id(); j <= last->block_id(); ++j) { |
|
fschneider
2012/02/02 14:59:50
Maybe this be simplified: The header is always mar
danno
2012/02/06 16:54:57
You have to iterate over every block's successors,
|
| + HBasicBlock* dominator_candidate = graph_->blocks()->at(j); |
| + const ZoneList<HBasicBlock*>* predecessor_list = |
| + dominator_candidate->predecessors(); |
| + int predecessor_count = predecessor_list->length(); |
| + for (int i = 0; i < predecessor_count; ++i) { |
|
fschneider
2012/02/02 14:59:50
Maybe it would make sense to introduce a HPredeces
danno
2012/02/06 16:54:57
Done.
|
| + HBasicBlock* predecessor = predecessor_list->at(i); |
| + // Don't count back edges. |
| + if (predecessor->block_id() < dominator_candidate->block_id()) { |
| + outstanding_sucsessors--; |
| + } |
| + } |
| + |
| + // 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_sucsessors >= 0); |
| + HBasicBlock* parent_loop_header = dominator_candidate->parent_loop_header(); |
| + if (outstanding_sucsessors == 0 && |
| + (parent_loop_header == this && !dominator_candidate->IsLoopHeader())) { |
| + MarkAsLoopSuccessorDominator(); |
|
fschneider
2012/02/02 14:59:50
I guess this should be:
candidate_dominator->Mark
danno
2012/02/06 16:54:57
Done.
|
| + } |
| + HControlInstruction* end_instr = dominator_candidate->end(); |
| + int successor_count = end_instr->SuccessorCount(); |
| + for (int current = 0; current < successor_count; ++current) { |
|
fschneider
2012/02/02 14:59:50
You can use HSuccessorIterator here.
danno
2012/02/06 16:54:57
Done.
|
| + HBasicBlock* successor = end_instr->SuccessorAt(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_sucsessors++; |
| + } |
| + } |
| + } |
| +} |
| + |
| + |
| int HBasicBlock::PredecessorIndexOf(HBasicBlock* predecessor) const { |
| for (int i = 0; i < predecessors_.length(); ++i) { |
| if (predecessors_[i] == predecessor) return i; |
| @@ -754,10 +814,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)); |
| @@ -1375,7 +1437,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); |
| @@ -1400,6 +1463,7 @@ class HGlobalValueNumberer BASE_EMBEDDED { |
| bool HGlobalValueNumberer::Analyze() { |
| + removed_side_effects_ = false; |
| ComputeBlockSideEffects(); |
| if (FLAG_loop_invariant_code_motion) { |
| LoopInvariantCodeMotion(); |
| @@ -1411,6 +1475,9 @@ bool HGlobalValueNumberer::Analyze() { |
| void HGlobalValueNumberer::ComputeBlockSideEffects() { |
| + for (int i = 0; i < loop_side_effects_.length(); ++i) { |
|
fschneider
2012/02/02 14:59:50
Comment on why resetting the loop side effect sets
danno
2012/02/06 16:54:57
Done.
|
| + 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); |
| @@ -1448,18 +1515,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", |
| @@ -1468,25 +1539,68 @@ 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->HasObservableSideEffects() && |
|
fschneider
2012/02/02 14:59:50
maybe just say:
if (!can_hoist && instr->IsTrans
danno
2012/02/06 16:54:57
Done.
|
| + instr->HasOneTimeSideEffects()) { |
| + // 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 not_in_nested_loop = block == loop_header || |
| + (block->parent_loop_header() == |
| + loop_header && !block->IsLoopHeader()); |
| + can_hoist = !not_in_nested_loop && |
|
fschneider
2012/02/02 14:59:50
The double negation !not... should be avoided for
danno
2012/02/06 16:54:57
Done.
|
| + 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) { |
| + // Hoisting an instruction to the preheader causes its DependsOn or |
| + // SideEffects flags to not prevent hosting OneTimeSideEffecct |
|
fschneider
2012/02/02 14:59:50
I find the double negation in the comment confusin
danno
2012/02/06 16:54:57
Done.
|
| + // instructions. |
| + accumulated_first_time_depends->Add(instr->DependsOnFlags()); |
| + GVNFlagSet converted_changes = |
| + HValue::ConvertChangesToDependsFlags(instr->SideEffectFlags()); |
| + accumulated_first_time_depends->Add(converted_changes); |
| + } |
| instr = next; |
| } |
| } |
| @@ -1547,6 +1661,7 @@ void HGlobalValueNumberer::AnalyzeBlock(HBasicBlock* block, HValueMap* map) { |
| } |
| if (instr->CheckFlag(HValue::kUseGVN)) { |
| ASSERT(!instr->HasObservableSideEffects()); |
| + ASSERT(!instr->HasSideEffects() || instr->HasOneTimeSideEffects()); |
| HValue* other = map->Lookup(instr); |
| if (other != NULL) { |
| ASSERT(instr->Equals(other) && other->Equals(instr)); |
| @@ -2394,7 +2509,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); |
| } |
| } |