[IR] Add hasNPredecessors, hasNPredecessorsOrMore to BasicBlock

Add methods to BasicBlock which make it easier to efficiently check
whether a block has N (or more) predecessors.

This can be more efficient than using pred_size(), which is a linear
time operation.

We might consider adding similar methods for successors. I haven't done
so in this patch because succ_size() is already O(1).

With this patch applied, I measured a 0.065% compile-time reduction in
user time for running `opt -O3` on the sqlite3 amalgamation (30 trials).
The change in mergeStoreIntoSuccessor alone saves 45 million linked list
iterations in a stage2 Release build of llc.

See llvm.org/PR39702 for a harder but more general way of achieving
similar results.

Differential Revision: https://reviews.llvm.org/D54686

llvm-svn: 347256
This commit is contained in:
Vedant Kumar 2018-11-19 19:54:27 +00:00
parent 740122fb8c
commit 4de31bba51
11 changed files with 61 additions and 22 deletions

View File

@ -1405,6 +1405,40 @@ auto apply_tuple(F &&f, Tuple &&t) -> decltype(detail::apply_tuple_impl(
Indices{});
}
/// Return true if the sequence [Begin, End) has exactly N items. Runs in O(N)
/// time. Not meant for use with random-access iterators.
template <typename IterTy>
bool hasNItems(
IterTy &&Begin, IterTy &&End, unsigned N,
typename std::enable_if<
!std::is_same<
typename std::iterator_traits<typename std::remove_reference<
decltype(Begin)>::type>::iterator_category,
std::random_access_iterator_tag>::value,
void>::type * = nullptr) {
for (; N; --N, ++Begin)
if (Begin == End)
return false; // Too few.
return Begin == End;
}
/// Return true if the sequence [Begin, End) has N or more items. Runs in O(N)
/// time. Not meant for use with random-access iterators.
template <typename IterTy>
bool hasNItemsOrMore(
IterTy &&Begin, IterTy &&End, unsigned N,
typename std::enable_if<
!std::is_same<
typename std::iterator_traits<typename std::remove_reference<
decltype(Begin)>::type>::iterator_category,
std::random_access_iterator_tag>::value,
void>::type * = nullptr) {
for (; N; --N, ++Begin)
if (Begin == End)
return false; // Too few.
return true;
}
} // end namespace llvm
#endif // LLVM_ADT_STLEXTRAS_H

View File

@ -237,6 +237,12 @@ public:
static_cast<const BasicBlock *>(this)->getUniquePredecessor());
}
/// Return true if this block has exactly N predecessors.
bool hasNPredecessors(unsigned N) const;
/// Return true if this block has N predecessors or more.
bool hasNPredecessorsOrMore(unsigned N) const;
/// Return the successor of this block if it has a single successor.
/// Otherwise return a null pointer.
///

View File

@ -117,6 +117,8 @@ inline const_pred_iterator pred_end(const BasicBlock *BB) {
inline bool pred_empty(const BasicBlock *BB) {
return pred_begin(BB) == pred_end(BB);
}
/// Get the number of predecessors of \p BB. This is a linear time operation.
/// Use \ref BasicBlock::hasNPredecessors() or hasNPredecessorsOrMore if able.
inline unsigned pred_size(const BasicBlock *BB) {
return std::distance(pred_begin(BB), pred_end(BB));
}

View File

@ -1022,7 +1022,7 @@ void MemorySSAUpdater::wireOldPredecessorsToNewImmediatePredecessor(
MemoryPhi *Phi = MSSA->getMemoryAccess(Old);
if (!Phi)
return;
if (pred_size(Old) == 1) {
if (Old->hasNPredecessors(1)) {
assert(pred_size(New) == Preds.size() &&
"Should have moved all predecessors.");
MSSA->moveTo(Phi, New, MemorySSA::Beginning);

View File

@ -260,6 +260,14 @@ const BasicBlock *BasicBlock::getUniquePredecessor() const {
return PredBB;
}
bool BasicBlock::hasNPredecessors(unsigned N) const {
return hasNItems(pred_begin(this), pred_end(this), N);
}
bool BasicBlock::hasNPredecessorsOrMore(unsigned N) const {
return hasNItemsOrMore(pred_begin(this), pred_end(this), N);
}
const BasicBlock *BasicBlock::getSingleSuccessor() const {
succ_const_iterator SI = succ_begin(this), E = succ_end(this);
if (SI == E) return nullptr; // no successors

View File

@ -130,20 +130,11 @@ void Value::destroyValueName() {
}
bool Value::hasNUses(unsigned N) const {
const_use_iterator UI = use_begin(), E = use_end();
for (; N; --N, ++UI)
if (UI == E) return false; // Too few.
return UI == E;
return hasNItems(use_begin(), use_end(), N);
}
bool Value::hasNUsesOrMore(unsigned N) const {
const_use_iterator UI = use_begin(), E = use_end();
for (; N; --N, ++UI)
if (UI == E) return false; // Too few.
return true;
return hasNItemsOrMore(use_begin(), use_end(), N);
}
bool Value::isUsedInBasicBlock(const BasicBlock *BB) const {

View File

@ -403,7 +403,7 @@ PartialInlinerImpl::computeOutliningColdRegionsInfo(Function *F,
auto IsSingleEntry = [](SmallVectorImpl<BasicBlock *> &BlockList) {
BasicBlock *Dom = BlockList.front();
return BlockList.size() > 1 && pred_size(Dom) == 1;
return BlockList.size() > 1 && Dom->hasNPredecessors(1);
};
auto IsSingleExit =

View File

@ -1526,7 +1526,7 @@ bool InstCombiner::mergeStoreIntoSuccessor(StoreInst &SI) {
// Check if the successor block has exactly 2 incoming edges.
BasicBlock *StoreBB = SI.getParent();
BasicBlock *DestBB = StoreBB->getTerminator()->getSuccessor(0);
if (pred_size(DestBB) != 2)
if (!DestBB->hasNPredecessors(2))
return false;
// Capture the other block (the block that doesn't contain our store).

View File

@ -682,10 +682,9 @@ void llvm::MergeBasicBlockIntoOnlyPred(BasicBlock *DestBB,
// DTU updates: Collect all the edges that enter
// PredBB. These dominator edges will be redirected to DestBB.
std::vector <DominatorTree::UpdateType> Updates;
SmallVector<DominatorTree::UpdateType, 32> Updates;
if (DTU) {
Updates.reserve(1 + (2 * pred_size(PredBB)));
Updates.push_back({DominatorTree::Delete, PredBB, DestBB});
for (auto I = pred_begin(PredBB), E = pred_end(PredBB); I != E; ++I) {
Updates.push_back({DominatorTree::Delete, *I, PredBB});
@ -989,9 +988,8 @@ bool llvm::TryToSimplifyUncondBranchFromEmptyBlock(BasicBlock *BB,
LLVM_DEBUG(dbgs() << "Killing Trivial BB: \n" << *BB);
std::vector<DominatorTree::UpdateType> Updates;
SmallVector<DominatorTree::UpdateType, 32> Updates;
if (DTU) {
Updates.reserve(1 + (2 * pred_size(BB)));
Updates.push_back({DominatorTree::Delete, BB, Succ});
// All predecessors of BB will be moved to Succ.
for (auto I = pred_begin(BB), E = pred_end(BB); I != E; ++I) {

View File

@ -693,7 +693,7 @@ Value *SimplifyCFGOpt::isValueEqualityComparison(Instruction *TI) {
if (SwitchInst *SI = dyn_cast<SwitchInst>(TI)) {
// Do not permit merging of large switch instructions into their
// predecessors unless there is only one predecessor.
if (SI->getNumSuccessors() * pred_size(SI->getParent()) <= 128)
if (!SI->getParent()->hasNPredecessorsOrMore(128 / SI->getNumSuccessors()))
CV = SI->getCondition();
} else if (BranchInst *BI = dyn_cast<BranchInst>(TI))
if (BI->isConditional() && BI->getCondition()->hasOneUse())
@ -2890,7 +2890,7 @@ static Value *ensureValueAvailableInSuccessor(Value *V, BasicBlock *BB,
if (!AlternativeV)
break;
assert(pred_size(Succ) == 2);
assert(Succ->hasNPredecessors(2));
auto PredI = pred_begin(Succ);
BasicBlock *OtherPredBB = *PredI == BB ? *++PredI : *PredI;
if (PHI->getIncomingValueForBlock(OtherPredBB) == AlternativeV)
@ -5774,7 +5774,7 @@ bool SimplifyCFGOpt::SimplifyUncondBranch(BranchInst *BI,
// backedge, so we can eliminate BB.
bool NeedCanonicalLoop =
Options.NeedCanonicalLoop &&
(LoopHeaders && pred_size(BB) > 1 &&
(LoopHeaders && BB->hasNPredecessorsOrMore(2) &&
(LoopHeaders->count(BB) || LoopHeaders->count(Succ)));
BasicBlock::iterator I = BB->getFirstNonPHIOrDbg()->getIterator();
if (I->isTerminator() && BB != &BB->getParent()->getEntryBlock() &&

View File

@ -468,7 +468,7 @@ void VPlan::updateDominatorTree(DominatorTree *DT, BasicBlock *LoopPreHeaderBB,
"One successor of a basic block does not lead to the other.");
assert(InterimSucc->getSinglePredecessor() &&
"Interim successor has more than one predecessor.");
assert(pred_size(PostDomSucc) == 2 &&
assert(PostDomSucc->hasNPredecessors(2) &&
"PostDom successor has more than two predecessors.");
DT->addNewBlock(InterimSucc, BB);
DT->addNewBlock(PostDomSucc, BB);