[MachineSink] Improve the compile time by preserving the dominance information

as long as possible.

** Context **

Each time the dominance information is modified, the dominator tree analysis
switches in a slow query mode. After a few queries without any modification on
the dominator tree, it performs an expensive update of its internal structure to
provide fast queries again.

** Problem **

Prior to this patch, the MachineSink pass was splitting the critical edges on
demand while relying heavy on the dominator tree information. In some cases,
this leads to pathological behavior where:
- We end up in the slow query mode right after splitting an edge.
- We update the dominance information.
- We break the dominance information again, thus ending up in the slow query
  mode and so on.

** Proposed Solution **

To mitigate this effect, this patch postpones all the splitting of the edges at
the end of each iteration of the main loop.
The benefits are:
- The dominance information is valid for the life time of an iteration.
- This simplifies the code as we do not have to special treat instructions that
  are sunk on critical edges. Indeed, the related block will be available
  through the next iteration.

The downside is that when edges splitting is required, this incurs an additional
iteration of the main loop compared to the previous scheme.

** Performance **

Thanks to this patch, the motivating example compiles in 6+ minutes instead of
10+ minutes. No test case added as the motivating example as nothing special but
being huge!

I have measured only noise for both the compile time and the runtime on the llvm
test-suite + SPECs with Os and O3.

Note: The current implementation of MachineBasicBlock::SplitCriticalEdge also
uses the dominance information and therefore, hits this problem. A subsequent
patch will address that.

<rdar://problem/17894619>

llvm-svn: 215410
This commit is contained in:
Quentin Colombet 2014-08-11 23:52:01 +00:00
parent 6b2f5b47d2
commit 5cded89d12
1 changed files with 60 additions and 39 deletions

View File

@ -17,6 +17,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/Passes.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/Statistic.h" #include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/AliasAnalysis.h"
@ -55,6 +56,10 @@ namespace {
// Remember which edges have been considered for breaking. // Remember which edges have been considered for breaking.
SmallSet<std::pair<MachineBasicBlock*,MachineBasicBlock*>, 8> SmallSet<std::pair<MachineBasicBlock*,MachineBasicBlock*>, 8>
CEBCandidates; CEBCandidates;
// Remember which edges we are about to split.
// This is different from CEBCandidates since those edges
// will be split.
SetVector<std::pair<MachineBasicBlock*,MachineBasicBlock*> > ToSplit;
public: public:
static char ID; // Pass identification static char ID; // Pass identification
@ -83,10 +88,22 @@ namespace {
bool isWorthBreakingCriticalEdge(MachineInstr *MI, bool isWorthBreakingCriticalEdge(MachineInstr *MI,
MachineBasicBlock *From, MachineBasicBlock *From,
MachineBasicBlock *To); MachineBasicBlock *To);
MachineBasicBlock *SplitCriticalEdge(MachineInstr *MI, /// \brief Postpone the splitting of the given critical
MachineBasicBlock *From, /// edge (\p From, \p To).
MachineBasicBlock *To, ///
bool BreakPHIEdge); /// We do not split the edges on the fly. Indeed, this invalidates
/// the dominance information and thus triggers a lot of updates
/// of that information underneath.
/// Instead, we postpone all the splits after each iteration of
/// the main loop. That way, the information is at least valid
/// for the lifetime of an iteration.
///
/// \return True if the edge is marked as toSplit, false otherwise.
/// False can be retruned if, for instance, this is not profitable.
bool PostponeSplitCriticalEdge(MachineInstr *MI,
MachineBasicBlock *From,
MachineBasicBlock *To,
bool BreakPHIEdge);
bool SinkInstruction(MachineInstr *MI, bool &SawStore); bool SinkInstruction(MachineInstr *MI, bool &SawStore);
bool AllUsesDominatedByBlock(unsigned Reg, MachineBasicBlock *MBB, bool AllUsesDominatedByBlock(unsigned Reg, MachineBasicBlock *MBB,
MachineBasicBlock *DefMBB, MachineBasicBlock *DefMBB,
@ -229,10 +246,24 @@ bool MachineSinking::runOnMachineFunction(MachineFunction &MF) {
// Process all basic blocks. // Process all basic blocks.
CEBCandidates.clear(); CEBCandidates.clear();
ToSplit.clear();
for (MachineFunction::iterator I = MF.begin(), E = MF.end(); for (MachineFunction::iterator I = MF.begin(), E = MF.end();
I != E; ++I) I != E; ++I)
MadeChange |= ProcessBlock(*I); MadeChange |= ProcessBlock(*I);
// If we have anything we marked as toSplit, split it now.
for (auto &Pair : ToSplit) {
auto NewSucc = Pair.first->SplitCriticalEdge(Pair.second, this);
if (NewSucc != nullptr) {
DEBUG(dbgs() << " *** Splitting critical edge:"
" BB#" << Pair.first->getNumber()
<< " -- BB#" << NewSucc->getNumber()
<< " -- BB#" << Pair.second->getNumber() << '\n');
MadeChange = true;
++NumSplit;
} else
DEBUG(dbgs() << " *** Not legal to break critical edge\n");
}
// If this iteration over the code changed anything, keep iterating. // If this iteration over the code changed anything, keep iterating.
if (!MadeChange) break; if (!MadeChange) break;
EverMadeChange = true; EverMadeChange = true;
@ -329,21 +360,21 @@ bool MachineSinking::isWorthBreakingCriticalEdge(MachineInstr *MI,
return false; return false;
} }
MachineBasicBlock *MachineSinking::SplitCriticalEdge(MachineInstr *MI, bool MachineSinking::PostponeSplitCriticalEdge(MachineInstr *MI,
MachineBasicBlock *FromBB, MachineBasicBlock *FromBB,
MachineBasicBlock *ToBB, MachineBasicBlock *ToBB,
bool BreakPHIEdge) { bool BreakPHIEdge) {
if (!isWorthBreakingCriticalEdge(MI, FromBB, ToBB)) if (!isWorthBreakingCriticalEdge(MI, FromBB, ToBB))
return nullptr; return false;
// Avoid breaking back edge. From == To means backedge for single BB loop. // Avoid breaking back edge. From == To means backedge for single BB loop.
if (!SplitEdges || FromBB == ToBB) if (!SplitEdges || FromBB == ToBB)
return nullptr; return false;
// Check for backedges of more "complex" loops. // Check for backedges of more "complex" loops.
if (LI->getLoopFor(FromBB) == LI->getLoopFor(ToBB) && if (LI->getLoopFor(FromBB) == LI->getLoopFor(ToBB) &&
LI->isLoopHeader(ToBB)) LI->isLoopHeader(ToBB))
return nullptr; return false;
// It's not always legal to break critical edges and sink the computation // It's not always legal to break critical edges and sink the computation
// to the edge. // to the edge.
@ -390,11 +421,13 @@ MachineBasicBlock *MachineSinking::SplitCriticalEdge(MachineInstr *MI,
if (*PI == FromBB) if (*PI == FromBB)
continue; continue;
if (!DT->dominates(ToBB, *PI)) if (!DT->dominates(ToBB, *PI))
return nullptr; return false;
} }
} }
return FromBB->SplitCriticalEdge(ToBB, this); ToSplit.insert(std::make_pair(FromBB, ToBB));
return true;
} }
static bool AvoidsSinking(MachineInstr *MI, MachineRegisterInfo *MRI) { static bool AvoidsSinking(MachineInstr *MI, MachineRegisterInfo *MRI) {
@ -656,21 +689,16 @@ bool MachineSinking::SinkInstruction(MachineInstr *MI, bool &SawStore) {
if (!TryBreak) if (!TryBreak)
DEBUG(dbgs() << "Sinking along critical edge.\n"); DEBUG(dbgs() << "Sinking along critical edge.\n");
else { else {
MachineBasicBlock *NewSucc = // Mark this edge as to be split.
SplitCriticalEdge(MI, ParentBlock, SuccToSinkTo, BreakPHIEdge); // If the edge can actually be split, the next iteration of the main loop
if (!NewSucc) { // will sink MI in the newly created block.
bool Status =
PostponeSplitCriticalEdge(MI, ParentBlock, SuccToSinkTo, BreakPHIEdge);
if (!Status)
DEBUG(dbgs() << " *** PUNTING: Not legal or profitable to " DEBUG(dbgs() << " *** PUNTING: Not legal or profitable to "
"break critical edge\n"); "break critical edge\n");
return false; // The instruction will not be sunk this time.
} else { return false;
DEBUG(dbgs() << " *** Splitting critical edge:"
" BB#" << ParentBlock->getNumber()
<< " -- BB#" << NewSucc->getNumber()
<< " -- BB#" << SuccToSinkTo->getNumber() << '\n');
SuccToSinkTo = NewSucc;
++NumSplit;
BreakPHIEdge = false;
}
} }
} }
@ -678,20 +706,13 @@ bool MachineSinking::SinkInstruction(MachineInstr *MI, bool &SawStore) {
// BreakPHIEdge is true if all the uses are in the successor MBB being // BreakPHIEdge is true if all the uses are in the successor MBB being
// sunken into and they are all PHI nodes. In this case, machine-sink must // sunken into and they are all PHI nodes. In this case, machine-sink must
// break the critical edge first. // break the critical edge first.
MachineBasicBlock *NewSucc = SplitCriticalEdge(MI, ParentBlock, bool Status = PostponeSplitCriticalEdge(MI, ParentBlock,
SuccToSinkTo, BreakPHIEdge); SuccToSinkTo, BreakPHIEdge);
if (!NewSucc) { if (!Status)
DEBUG(dbgs() << " *** PUNTING: Not legal or profitable to " DEBUG(dbgs() << " *** PUNTING: Not legal or profitable to "
"break critical edge\n"); "break critical edge\n");
return false; // The instruction will not be sunk this time.
} return false;
DEBUG(dbgs() << " *** Splitting critical edge:"
" BB#" << ParentBlock->getNumber()
<< " -- BB#" << NewSucc->getNumber()
<< " -- BB#" << SuccToSinkTo->getNumber() << '\n');
SuccToSinkTo = NewSucc;
++NumSplit;
} }
// Determine where to insert into. Skip phi nodes. // Determine where to insert into. Skip phi nodes.