summaryrefslogtreecommitdiffstats
path: root/llvm/lib/IR/Dominators.cpp
diff options
context:
space:
mode:
authorBrian M. Rzycki <brzycki@gmail.com>2018-01-04 21:57:32 +0000
committerBrian M. Rzycki <brzycki@gmail.com>2018-01-04 21:57:32 +0000
commitcdad6c0b60d5df0cf610a902838652f54d207d6f (patch)
treec882323147a3e8894092e18073e083965e053400 /llvm/lib/IR/Dominators.cpp
parent6161a0b3b0ae3160c2317ad31f273973e1f889b0 (diff)
downloadbcm5719-llvm-cdad6c0b60d5df0cf610a902838652f54d207d6f.tar.gz
bcm5719-llvm-cdad6c0b60d5df0cf610a902838652f54d207d6f.zip
[JumpThreading] Preservation of DT and LVI across the pass
Summary: See D37528 for a previous (non-deferred) version of this patch and its description. Preserves dominance in a deferred manner using a new class DeferredDominance. This reduces the performance impact of updating the DominatorTree at every edge insertion and deletion. A user may call DDT->flush() within JumpThreading for an up-to-date DT. This patch currently has one flush() at the end of runImpl() to ensure DT is preserved across the pass. LVI is also preserved to help subsequent passes such as CorrelatedValuePropagation. LVI is simpler to maintain and is done immediately (not deferred). The code to perfom the preversation was minimally altered and was simply marked as preserved for the PassManager to be informed. This extends the analysis available to JumpThreading for future enhancements. One example is loop boundary threading. Reviewers: dberlin, kuhar, sebpop Reviewed By: kuhar, sebpop Subscribers: hiraditya, llvm-commits Differential Revision: https://reviews.llvm.org/D40146 llvm-svn: 321825
Diffstat (limited to 'llvm/lib/IR/Dominators.cpp')
-rw-r--r--llvm/lib/IR/Dominators.cpp188
1 files changed, 188 insertions, 0 deletions
diff --git a/llvm/lib/IR/Dominators.cpp b/llvm/lib/IR/Dominators.cpp
index ad448a3f240..e44e845b324 100644
--- a/llvm/lib/IR/Dominators.cpp
+++ b/llvm/lib/IR/Dominators.cpp
@@ -18,6 +18,7 @@
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/IR/CFG.h"
+#include "llvm/IR/Constants.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Support/CommandLine.h"
@@ -389,3 +390,190 @@ void DominatorTreeWrapperPass::print(raw_ostream &OS, const Module *) const {
DT.print(OS);
}
+//===----------------------------------------------------------------------===//
+// DeferredDominance Implementation
+//===----------------------------------------------------------------------===//
+//
+// The implementation details of the DeferredDominance class which allows
+// one to queue updates to a DominatorTree.
+//
+//===----------------------------------------------------------------------===//
+
+/// \brief Queues multiple updates and discards duplicates.
+void DeferredDominance::applyUpdates(
+ ArrayRef<DominatorTree::UpdateType> Updates) {
+ SmallVector<DominatorTree::UpdateType, 8> Seen;
+ for (auto U : Updates)
+ // Avoid duplicates to applyUpdate() to save on analysis.
+ if (std::none_of(Seen.begin(), Seen.end(),
+ [U](DominatorTree::UpdateType S) { return S == U; })) {
+ Seen.push_back(U);
+ applyUpdate(U.getKind(), U.getFrom(), U.getTo());
+ }
+}
+
+/// \brief Helper method for a single edge insertion. It's almost always better
+/// to batch updates and call applyUpdates to quickly remove duplicate edges.
+/// This is best used when there is only a single insertion needed to update
+/// Dominators.
+void DeferredDominance::insertEdge(BasicBlock *From, BasicBlock *To) {
+ applyUpdate(DominatorTree::Insert, From, To);
+}
+
+/// \brief Helper method for a single edge deletion. It's almost always better
+/// to batch updates and call applyUpdates to quickly remove duplicate edges.
+/// This is best used when there is only a single deletion needed to update
+/// Dominators.
+void DeferredDominance::deleteEdge(BasicBlock *From, BasicBlock *To) {
+ applyUpdate(DominatorTree::Delete, From, To);
+}
+
+/// \brief Delays the deletion of a basic block until a flush() event.
+void DeferredDominance::deleteBB(BasicBlock *DelBB) {
+ assert(DelBB && "Invalid push_back of nullptr DelBB.");
+ assert(pred_empty(DelBB) && "DelBB has one or more predecessors.");
+ // DelBB is unreachable and all its instructions are dead.
+ while (!DelBB->empty()) {
+ Instruction &I = DelBB->back();
+ // Replace used instructions with an arbitrary value (undef).
+ if (!I.use_empty())
+ I.replaceAllUsesWith(llvm::UndefValue::get(I.getType()));
+ DelBB->getInstList().pop_back();
+ }
+ // Make sure DelBB has a valid terminator instruction. As long as DelBB is a
+ // Child of Function F it must contain valid IR.
+ new UnreachableInst(DelBB->getContext(), DelBB);
+ DeletedBBs.insert(DelBB);
+}
+
+/// \brief Returns true if DelBB is awaiting deletion at a flush() event.
+bool DeferredDominance::pendingDeletedBB(BasicBlock *DelBB) {
+ if (DeletedBBs.empty())
+ return false;
+ return DeletedBBs.count(DelBB) != 0;
+}
+
+/// \brief Flushes all pending updates and block deletions. Returns a
+/// correct DominatorTree reference to be used by the caller for analysis.
+DominatorTree &DeferredDominance::flush() {
+ // Updates to DT must happen before blocks are deleted below. Otherwise the
+ // DT traversal will encounter badref blocks and assert.
+ if (!PendUpdates.empty()) {
+ DT.applyUpdates(PendUpdates);
+ PendUpdates.clear();
+ }
+ flushDelBB();
+ return DT;
+}
+
+/// \brief Drops all internal state and forces a (slow) recalculation of the
+/// DominatorTree based on the current state of the LLVM IR in F. This should
+/// only be used in corner cases such as the Entry block of F being deleted.
+void DeferredDominance::recalculate(Function &F) {
+ // flushDelBB must be flushed before the recalculation. The state of the IR
+ // must be consistent before the DT traversal algorithm determines the
+ // actual DT.
+ if (flushDelBB() || !PendUpdates.empty()) {
+ DT.recalculate(F);
+ PendUpdates.clear();
+ }
+}
+
+/// \brief Debug method to help view the state of pending updates.
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void DeferredDominance::dump() const {
+ raw_ostream &OS = llvm::dbgs();
+ OS << "PendUpdates:\n";
+ int I = 0;
+ for (auto U : PendUpdates) {
+ OS << " " << I << " : ";
+ ++I;
+ if (U.getKind() == DominatorTree::Insert)
+ OS << "Insert, ";
+ else
+ OS << "Delete, ";
+ BasicBlock *From = U.getFrom();
+ if (From) {
+ auto S = From->getName();
+ if (!From->hasName())
+ S = "(no name)";
+ OS << S << "(" << From << "), ";
+ } else {
+ OS << "(badref), ";
+ }
+ BasicBlock *To = U.getTo();
+ if (To) {
+ auto S = To->getName();
+ if (!To->hasName())
+ S = "(no_name)";
+ OS << S << "(" << To << ")\n";
+ } else {
+ OS << "(badref)\n";
+ }
+ }
+ OS << "DeletedBBs:\n";
+ I = 0;
+ for (auto BB : DeletedBBs) {
+ OS << " " << I << " : ";
+ ++I;
+ if (BB->hasName())
+ OS << BB->getName() << "(";
+ else
+ OS << "(no_name)(";
+ OS << BB << ")\n";
+ }
+}
+#endif
+
+/// Apply an update (Kind, From, To) to the internal queued updates. The
+/// update is only added when determined to be necessary. Checks for
+/// self-domination, unnecessary updates, duplicate requests, and balanced
+/// pairs of requests are all performed. Returns true if the update is
+/// queued and false if it is discarded.
+bool DeferredDominance::applyUpdate(DominatorTree::UpdateKind Kind,
+ BasicBlock *From, BasicBlock *To) {
+ if (From == To)
+ return false; // Cannot dominate self; discard update.
+
+ // Discard updates by inspecting the current state of successors of From.
+ // Since applyUpdate() must be called *after* the Terminator of From is
+ // altered we can determine if the update is unnecessary.
+ bool HasEdge = std::any_of(succ_begin(From), succ_end(From),
+ [To](BasicBlock *B) { return B == To; });
+ if (Kind == DominatorTree::Insert && !HasEdge)
+ return false; // Unnecessary Insert: edge does not exist in IR.
+ if (Kind == DominatorTree::Delete && HasEdge)
+ return false; // Unnecessary Delete: edge still exists in IR.
+
+ // Analyze pending updates to determine if the update is unnecessary.
+ DominatorTree::UpdateType Update = {Kind, From, To};
+ DominatorTree::UpdateType Invert = {Kind != DominatorTree::Insert
+ ? DominatorTree::Insert
+ : DominatorTree::Delete,
+ From, To};
+ for (auto I = PendUpdates.begin(), E = PendUpdates.end(); I != E; ++I) {
+ if (Update == *I)
+ return false; // Discard duplicate updates.
+ if (Invert == *I) {
+ // Update and Invert are both valid (equivalent to a no-op). Remove
+ // Invert from PendUpdates and discard the Update.
+ PendUpdates.erase(I);
+ return false;
+ }
+ }
+ PendUpdates.push_back(Update); // Save the valid update.
+ return true;
+}
+
+/// Performs all pending basic block deletions. We have to defer the deletion
+/// of these blocks until after the DominatorTree updates are applied. The
+/// internal workings of the DominatorTree code expect every update's From
+/// and To blocks to exist and to be a member of the same Function.
+bool DeferredDominance::flushDelBB() {
+ if (DeletedBBs.empty())
+ return false;
+ for (auto *BB : DeletedBBs)
+ BB->eraseFromParent();
+ DeletedBBs.clear();
+ return true;
+}
OpenPOWER on IntegriCloud