Skip to content

Commit

Permalink
[JumpThreading] Preservation of DT and LVI across the pass
Browse files Browse the repository at this point in the history
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: 320612
  • Loading branch information
bmrzycki committed Dec 13, 2017
1 parent f22f5fe commit d989af9
Show file tree
Hide file tree
Showing 12 changed files with 893 additions and 99 deletions.
190 changes: 190 additions & 0 deletions llvm/include/llvm/IR/DeferredDominance.h
@@ -0,0 +1,190 @@
//===- DeferredDominance.h - Deferred Dominators ----------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the DeferredDominance class, which provides deferred
// updates to Dominators.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_IR_DEFERREDDOMINANCE_H
#define LLVM_IR_DEFERREDDOMINANCE_H

#include "llvm/ADT/SmallSet.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Instructions.h"

namespace llvm {

/// \brief Class to defer updates to a DominatorTree.
///
/// Definition: Applying updates to every edge insertion and deletion is
/// expensive and not necessary. When one needs the DominatorTree for analysis
/// they can request a flush() to perform a larger batch update. This has the
/// advantage of the DominatorTree inspecting the set of updates to find
/// duplicates or unnecessary subtree updates.
///
/// The scope of DeferredDominance operates at a Function level.
///
/// It is not necessary for the user to scrub the updates for duplicates or
/// updates that point to the same block (Delete, BB_A, BB_A). Performance
/// can be gained if the caller attempts to batch updates before submitting
/// to applyUpdates(ArrayRef) in cases where duplicate edge requests will
/// occur.
///
/// It is required for the state of the LLVM IR to be applied *before*
/// submitting updates. The update routines must analyze the current state
/// between a pair of (From, To) basic blocks to determine if the update
/// needs to be queued.
/// Example (good):
/// TerminatorInstructionBB->removeFromParent();
/// DDT->deleteEdge(BB, Successor);
/// Example (bad):
/// DDT->deleteEdge(BB, Successor);
/// TerminatorInstructionBB->removeFromParent();
class DeferredDominance {
public:
DeferredDominance(DominatorTree &DT_) : DT(DT_) {}

/// \brief Queues multiple updates and discards duplicates.
void 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());
}
}

void insertEdge(BasicBlock *From, BasicBlock *To) {
applyUpdate(DominatorTree::Insert, From, To);
}

void deleteEdge(BasicBlock *From, BasicBlock *To) {
applyUpdate(DominatorTree::Delete, From, To);
}

/// \brief Delays the deletion of a basic block until a flush() event.
void 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 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 &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 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.
LLVM_DUMP_METHOD void dump() const;

private:
DominatorTree &DT;
SmallVector<DominatorTree::UpdateType, 16> PendUpdates;
SmallPtrSet<BasicBlock *, 8> DeletedBBs;

/// 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 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 flushDelBB() {
if (DeletedBBs.empty())
return false;
for (auto *BB : DeletedBBs)
BB->eraseFromParent();
DeletedBBs.clear();
return true;
}
};

} // end namespace llvm

#endif // LLVM_IR_DEFERREDDOMINANCE_H
6 changes: 4 additions & 2 deletions llvm/include/llvm/Transforms/Scalar/JumpThreading.h
Expand Up @@ -34,6 +34,7 @@ class BinaryOperator;
class BranchInst;
class CmpInst;
class Constant;
class DeferredDominance;
class Function;
class Instruction;
class IntrinsicInst;
Expand Down Expand Up @@ -77,6 +78,7 @@ class JumpThreadingPass : public PassInfoMixin<JumpThreadingPass> {
TargetLibraryInfo *TLI;
LazyValueInfo *LVI;
AliasAnalysis *AA;
DeferredDominance *DDT;
std::unique_ptr<BlockFrequencyInfo> BFI;
std::unique_ptr<BranchProbabilityInfo> BPI;
bool HasProfileData = false;
Expand Down Expand Up @@ -107,8 +109,8 @@ class JumpThreadingPass : public PassInfoMixin<JumpThreadingPass> {

// Glue for old PM.
bool runImpl(Function &F, TargetLibraryInfo *TLI_, LazyValueInfo *LVI_,
AliasAnalysis *AA_, bool HasProfileData_,
std::unique_ptr<BlockFrequencyInfo> BFI_,
AliasAnalysis *AA_, DeferredDominance *DDT_,
bool HasProfileData_, std::unique_ptr<BlockFrequencyInfo> BFI_,
std::unique_ptr<BranchProbabilityInfo> BPI_);

PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
Expand Down
3 changes: 2 additions & 1 deletion llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h
Expand Up @@ -27,6 +27,7 @@ namespace llvm {

class BlockFrequencyInfo;
class BranchProbabilityInfo;
class DeferredDominance;
class DominatorTree;
class Function;
class Instruction;
Expand All @@ -38,7 +39,7 @@ class TargetLibraryInfo;
class Value;

/// Delete the specified block, which must have no predecessors.
void DeleteDeadBlock(BasicBlock *BB);
void DeleteDeadBlock(BasicBlock *BB, DeferredDominance *DDT = nullptr);

/// We know that BB has one predecessor. If there are any single-entry PHI nodes
/// in it, fold them away. This handles the case when all entries to the PHI
Expand Down
21 changes: 14 additions & 7 deletions llvm/include/llvm/Transforms/Utils/Local.h
Expand Up @@ -24,6 +24,7 @@
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DeferredDominance.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/GetElementPtrTypeIterator.h"
#include "llvm/IR/Operator.h"
Expand Down Expand Up @@ -109,7 +110,8 @@ struct SimplifyCFGOptions {
/// conditions and indirectbr addresses this might make dead if
/// DeleteDeadConditions is true.
bool ConstantFoldTerminator(BasicBlock *BB, bool DeleteDeadConditions = false,
const TargetLibraryInfo *TLI = nullptr);
const TargetLibraryInfo *TLI = nullptr,
DeferredDominance *DDT = nullptr);

//===----------------------------------------------------------------------===//
// Local dead code elimination.
Expand Down Expand Up @@ -163,18 +165,21 @@ bool SimplifyInstructionsInBlock(BasicBlock *BB,
///
/// .. and delete the predecessor corresponding to the '1', this will attempt to
/// recursively fold the 'and' to 0.
void RemovePredecessorAndSimplify(BasicBlock *BB, BasicBlock *Pred);
void RemovePredecessorAndSimplify(BasicBlock *BB, BasicBlock *Pred,
DeferredDominance *DDT = nullptr);

/// BB is a block with one predecessor and its predecessor is known to have one
/// successor (BB!). Eliminate the edge between them, moving the instructions in
/// the predecessor into BB. This deletes the predecessor block.
void MergeBasicBlockIntoOnlyPred(BasicBlock *BB, DominatorTree *DT = nullptr);
void MergeBasicBlockIntoOnlyPred(BasicBlock *BB, DominatorTree *DT = nullptr,
DeferredDominance *DDT = nullptr);

/// BB is known to contain an unconditional branch, and contains no instructions
/// other than PHI nodes, potential debug intrinsics and the branch. If
/// possible, eliminate BB by rewriting all the predecessors to branch to the
/// successor block and return true. If we can't transform, return false.
bool TryToSimplifyUncondBranchFromEmptyBlock(BasicBlock *BB);
bool TryToSimplifyUncondBranchFromEmptyBlock(BasicBlock *BB,
DeferredDominance *DDT = nullptr);

/// Check for and eliminate duplicate PHI nodes in this block. This doesn't try
/// to be clever about PHI nodes which differ only in the order of the incoming
Expand Down Expand Up @@ -374,7 +379,8 @@ unsigned removeAllNonTerminatorAndEHPadInstructions(BasicBlock *BB);
/// Insert an unreachable instruction before the specified
/// instruction, making it and the rest of the code in the block dead.
unsigned changeToUnreachable(Instruction *I, bool UseLLVMTrap,
bool PreserveLCSSA = false);
bool PreserveLCSSA = false,
DeferredDominance *DDT = nullptr);

/// Convert the CallInst to InvokeInst with the specified unwind edge basic
/// block. This also splits the basic block where CI is located, because
Expand All @@ -389,12 +395,13 @@ BasicBlock *changeToInvokeAndSplitBasicBlock(CallInst *CI,
///
/// \param BB Block whose terminator will be replaced. Its terminator must
/// have an unwind successor.
void removeUnwindEdge(BasicBlock *BB);
void removeUnwindEdge(BasicBlock *BB, DeferredDominance *DDT = nullptr);

/// Remove all blocks that can not be reached from the function's entry.
///
/// Returns true if any basic block was removed.
bool removeUnreachableBlocks(Function &F, LazyValueInfo *LVI = nullptr);
bool removeUnreachableBlocks(Function &F, LazyValueInfo *LVI = nullptr,
DeferredDominance *DDT = nullptr);

/// Combine the metadata of two instructions so that K can replace J
///
Expand Down
54 changes: 54 additions & 0 deletions llvm/lib/IR/Dominators.cpp
Expand Up @@ -18,6 +18,7 @@
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/DeferredDominance.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Support/CommandLine.h"
Expand Down Expand Up @@ -389,3 +390,56 @@ 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.
//
//===----------------------------------------------------------------------===//

#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
2 changes: 2 additions & 0 deletions llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
Expand Up @@ -77,6 +77,7 @@ namespace {
bool runOnFunction(Function &F) override;

void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<DominatorTreeWrapperPass>();
AU.addRequired<LazyValueInfoWrapperPass>();
AU.addPreserved<GlobalsAAWrapperPass>();
}
Expand All @@ -88,6 +89,7 @@ char CorrelatedValuePropagation::ID = 0;

INITIALIZE_PASS_BEGIN(CorrelatedValuePropagation, "correlated-propagation",
"Value Propagation", false, false)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
INITIALIZE_PASS_DEPENDENCY(LazyValueInfoWrapperPass)
INITIALIZE_PASS_END(CorrelatedValuePropagation, "correlated-propagation",
"Value Propagation", false, false)
Expand Down

0 comments on commit d989af9

Please sign in to comment.