[PATCH] D20146: [PM] Port DSE to the new pass manager.
Justin Bogner via llvm-commits
llvm-commits at lists.llvm.org
Tue May 17 13:52:06 PDT 2016
Jake VanAdrighem <jvanadrighem at gmail.com> writes:
> JakeVanAdrighem updated this revision to Diff 57399.
> JakeVanAdrighem marked an inline comment as done.
> JakeVanAdrighem added a comment.
>
> Rename runOnBasicBlock to eliminateDeadStores. Add an overload that
> takes a Function instead of BasicBlock. Made all the helpers
> static. Cleaned up function description comments. clang-format.
>
> I considered discarding this patch and splitting it out but at this
> point I'd rather leave it as a slight refactor + port to the new PM.
Makes sense. This LGTM.
Do you have commit or do you need me to commit this for you?
>
> Repository:
> rL LLVM
>
> http://reviews.llvm.org/D20146
>
> Files:
> include/llvm/InitializePasses.h
> include/llvm/Transforms/Scalar/DeadStoreElimination.h
> lib/Passes/PassBuilder.cpp
> lib/Passes/PassRegistry.def
> lib/Transforms/Scalar/DeadStoreElimination.cpp
> lib/Transforms/Scalar/Scalar.cpp
> test/Transforms/DeadStoreElimination/simple.ll
>
> Index: test/Transforms/DeadStoreElimination/simple.ll
> ===================================================================
> --- test/Transforms/DeadStoreElimination/simple.ll
> +++ test/Transforms/DeadStoreElimination/simple.ll
> @@ -1,4 +1,5 @@
> ; RUN: opt < %s -basicaa -dse -S | FileCheck %s
> +; RUN: opt < %s -aa-pipeline=basic-aa -passes=dse -S | FileCheck %s
> target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128"
>
> declare void @llvm.memset.p0i8.i64(i8* nocapture, i8, i64, i32, i1) nounwind
> Index: lib/Transforms/Scalar/Scalar.cpp
> ===================================================================
> --- lib/Transforms/Scalar/Scalar.cpp
> +++ lib/Transforms/Scalar/Scalar.cpp
> @@ -40,7 +40,7 @@
> initializeDCELegacyPassPass(Registry);
> initializeDeadInstEliminationPass(Registry);
> initializeScalarizerPass(Registry);
> - initializeDSEPass(Registry);
> + initializeDSELegacyPassPass(Registry);
> initializeGVNLegacyPassPass(Registry);
> initializeEarlyCSELegacyPassPass(Registry);
> initializeFlattenCFGPassPass(Registry);
> Index: lib/Transforms/Scalar/DeadStoreElimination.cpp
> ===================================================================
> --- lib/Transforms/Scalar/DeadStoreElimination.cpp
> +++ lib/Transforms/Scalar/DeadStoreElimination.cpp
> @@ -15,7 +15,7 @@
> //
> //===----------------------------------------------------------------------===//
>
> -#include "llvm/Transforms/Scalar.h"
> +#include "llvm/Transforms/Scalar/DeadStoreElimination.h"
> #include "llvm/ADT/STLExtras.h"
> #include "llvm/ADT/SetVector.h"
> #include "llvm/ADT/Statistic.h"
> @@ -36,6 +36,7 @@
> #include "llvm/Pass.h"
> #include "llvm/Support/Debug.h"
> #include "llvm/Support/raw_ostream.h"
> +#include "llvm/Transforms/Scalar.h"
> #include "llvm/Transforms/Utils/Local.h"
> using namespace llvm;
>
> @@ -45,80 +46,15 @@
> STATISTIC(NumFastStores, "Number of stores deleted");
> STATISTIC(NumFastOther , "Number of other instrs removed");
>
> -namespace {
> - struct DSE : public FunctionPass {
> - AliasAnalysis *AA;
> - MemoryDependenceResults *MD;
> - DominatorTree *DT;
> - const TargetLibraryInfo *TLI;
> -
> - static char ID; // Pass identification, replacement for typeid
> - DSE() : FunctionPass(ID), AA(nullptr), MD(nullptr), DT(nullptr) {
> - initializeDSEPass(*PassRegistry::getPassRegistry());
> - }
> -
> - bool runOnFunction(Function &F) override {
> - if (skipFunction(F))
> - return false;
> -
> - AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
> - MD = &getAnalysis<MemoryDependenceWrapperPass>().getMemDep();
> - DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
> - TLI = &getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();
> -
> - bool Changed = false;
> - for (BasicBlock &I : F)
> - // Only check non-dead blocks. Dead blocks may have strange pointer
> - // cycles that will confuse alias analysis.
> - if (DT->isReachableFromEntry(&I))
> - Changed |= runOnBasicBlock(I);
> -
> - AA = nullptr; MD = nullptr; DT = nullptr;
> - return Changed;
> - }
> -
> - bool runOnBasicBlock(BasicBlock &BB);
> - bool MemoryIsNotModifiedBetween(Instruction *FirstI, Instruction *SecondI);
> - bool HandleFree(CallInst *F);
> - bool handleEndBlock(BasicBlock &BB);
> - void RemoveAccessedObjects(const MemoryLocation &LoadedLoc,
> - SmallSetVector<Value *, 16> &DeadStackObjects,
> - const DataLayout &DL);
> -
> - void getAnalysisUsage(AnalysisUsage &AU) const override {
> - AU.setPreservesCFG();
> - AU.addRequired<DominatorTreeWrapperPass>();
> - AU.addRequired<AAResultsWrapperPass>();
> - AU.addRequired<MemoryDependenceWrapperPass>();
> - AU.addRequired<TargetLibraryInfoWrapperPass>();
> - AU.addPreserved<DominatorTreeWrapperPass>();
> - AU.addPreserved<GlobalsAAWrapperPass>();
> - AU.addPreserved<MemoryDependenceWrapperPass>();
> - }
> - };
> -}
> -
> -char DSE::ID = 0;
> -INITIALIZE_PASS_BEGIN(DSE, "dse", "Dead Store Elimination", false, false)
> -INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
> -INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass)
> -INITIALIZE_PASS_DEPENDENCY(GlobalsAAWrapperPass)
> -INITIALIZE_PASS_DEPENDENCY(MemoryDependenceWrapperPass)
> -INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
> -INITIALIZE_PASS_END(DSE, "dse", "Dead Store Elimination", false, false)
> -
> -FunctionPass *llvm::createDeadStoreEliminationPass() { return new DSE(); }
>
> //===----------------------------------------------------------------------===//
> // Helper functions
> //===----------------------------------------------------------------------===//
>
> -/// DeleteDeadInstruction - Delete this instruction. Before we do, go through
> -/// and zero out all the operands of this instruction. If any of them become
> -/// dead, delete them and the computation tree that feeds them.
> -///
> +/// Delete this instruction. Before we do, go through and zero out all the
> +/// operands of this instruction. If any of them become dead, delete them and
> +/// the computation tree that feeds them.
> /// If ValueSet is non-null, remove any deleted instructions from it as well.
> -///
> static void DeleteDeadInstruction(Instruction *I,
> MemoryDependenceResults &MD,
> const TargetLibraryInfo &TLI,
> @@ -156,9 +92,8 @@
> } while (!NowDeadInsts.empty());
> }
>
> -
> -/// hasMemoryWrite - Does this instruction write some memory? This only returns
> -/// true for things that we can analyze with other helpers below.
> +/// Does this instruction write some memory? This only returns true for things
> +/// that we can analyze with other helpers below.
> static bool hasMemoryWrite(Instruction *I, const TargetLibraryInfo &TLI) {
> if (isa<StoreInst>(I))
> return true;
> @@ -197,9 +132,9 @@
> return false;
> }
>
> -/// getLocForWrite - Return a Location stored to by the specified instruction.
> -/// If isRemovable returns true, this function and getLocForRead completely
> -/// describe the memory operations for this instruction.
> +/// Return a Location stored to by the specified instruction. If isRemovable
> +/// returns true, this function and getLocForRead completely describe the memory
> +/// operations for this instruction.
> static MemoryLocation getLocForWrite(Instruction *Inst, AliasAnalysis &AA) {
> if (StoreInst *SI = dyn_cast<StoreInst>(Inst))
> return MemoryLocation::get(SI);
> @@ -228,8 +163,8 @@
> }
> }
>
> -/// getLocForRead - Return the location read by the specified "hasMemoryWrite"
> -/// instruction if any.
> +/// Return the location read by the specified "hasMemoryWrite" instruction if
> +/// any.
> static MemoryLocation getLocForRead(Instruction *Inst,
> const TargetLibraryInfo &TLI) {
> assert(hasMemoryWrite(Inst, TLI) && "Unknown instruction case");
> @@ -241,9 +176,8 @@
> return MemoryLocation();
> }
>
> -
> -/// isRemovable - If the value of this instruction and the memory it writes to
> -/// is unused, may we delete this instruction?
> +/// If the value of this instruction and the memory it writes to is unused, may
> +/// we delete this instruction?
> static bool isRemovable(Instruction *I) {
> // Don't remove volatile/atomic stores.
> if (StoreInst *SI = dyn_cast<StoreInst>(I))
> @@ -307,7 +241,7 @@
> return II && II->getIntrinsicID() == Intrinsic::memset;
> }
>
> -/// getStoredPointerOperand - Return the pointer that is being written to.
> +/// Return the pointer that is being written to.
> static Value *getStoredPointerOperand(Instruction *I) {
> if (StoreInst *SI = dyn_cast<StoreInst>(I))
> return SI->getPointerOperand();
> @@ -458,7 +392,7 @@
> return OverwriteUnknown;
> }
>
> -/// isPossibleSelfRead - If 'Inst' might be a self read (i.e. a noop copy of a
> +/// If 'Inst' might be a self read (i.e. a noop copy of a
> /// memory region into an identical pointer) then it doesn't actually make its
> /// input dead in the traditional sense. Consider this case:
> ///
> @@ -503,212 +437,13 @@
> }
>
>
> -//===----------------------------------------------------------------------===//
> -// DSE Pass
> -//===----------------------------------------------------------------------===//
> -
> -bool DSE::runOnBasicBlock(BasicBlock &BB) {
> - const DataLayout &DL = BB.getModule()->getDataLayout();
> - bool MadeChange = false;
> -
> - // Do a top-down walk on the BB.
> - for (BasicBlock::iterator BBI = BB.begin(), BBE = BB.end(); BBI != BBE; ) {
> - Instruction *Inst = &*BBI++;
> -
> - // Handle 'free' calls specially.
> - if (CallInst *F = isFreeCall(Inst, TLI)) {
> - MadeChange |= HandleFree(F);
> - continue;
> - }
> -
> - // If we find something that writes memory, get its memory dependence.
> - if (!hasMemoryWrite(Inst, *TLI))
> - continue;
> -
> - // If we're storing the same value back to a pointer that we just
> - // loaded from, then the store can be removed.
> - if (StoreInst *SI = dyn_cast<StoreInst>(Inst)) {
> -
> - auto RemoveDeadInstAndUpdateBBI = [&](Instruction *DeadInst) {
> - // DeleteDeadInstruction can delete the current instruction. Save BBI
> - // in case we need it.
> - WeakVH NextInst(&*BBI);
> -
> - DeleteDeadInstruction(DeadInst, *MD, *TLI);
> -
> - if (!NextInst) // Next instruction deleted.
> - BBI = BB.begin();
> - else if (BBI != BB.begin()) // Revisit this instruction if possible.
> - --BBI;
> - ++NumRedundantStores;
> - MadeChange = true;
> - };
> -
> - if (LoadInst *DepLoad = dyn_cast<LoadInst>(SI->getValueOperand())) {
> - if (SI->getPointerOperand() == DepLoad->getPointerOperand() &&
> - isRemovable(SI) &&
> - MemoryIsNotModifiedBetween(DepLoad, SI)) {
> -
> - DEBUG(dbgs() << "DSE: Remove Store Of Load from same pointer:\n "
> - << "LOAD: " << *DepLoad << "\n STORE: " << *SI << '\n');
> -
> - RemoveDeadInstAndUpdateBBI(SI);
> - continue;
> - }
> - }
> -
> - // Remove null stores into the calloc'ed objects
> - Constant *StoredConstant = dyn_cast<Constant>(SI->getValueOperand());
> -
> - if (StoredConstant && StoredConstant->isNullValue() &&
> - isRemovable(SI)) {
> - Instruction *UnderlyingPointer = dyn_cast<Instruction>(
> - GetUnderlyingObject(SI->getPointerOperand(), DL));
> -
> - if (UnderlyingPointer && isCallocLikeFn(UnderlyingPointer, TLI) &&
> - MemoryIsNotModifiedBetween(UnderlyingPointer, SI)) {
> - DEBUG(dbgs()
> - << "DSE: Remove null store to the calloc'ed object:\n DEAD: "
> - << *Inst << "\n OBJECT: " << *UnderlyingPointer << '\n');
> -
> - RemoveDeadInstAndUpdateBBI(SI);
> - continue;
> - }
> - }
> - }
> -
> - MemDepResult InstDep = MD->getDependency(Inst);
> -
> - // Ignore any store where we can't find a local dependence.
> - // FIXME: cross-block DSE would be fun. :)
> - if (!InstDep.isDef() && !InstDep.isClobber())
> - continue;
> -
> - // Figure out what location is being stored to.
> - MemoryLocation Loc = getLocForWrite(Inst, *AA);
> -
> - // If we didn't get a useful location, fail.
> - if (!Loc.Ptr)
> - continue;
> -
> - while (InstDep.isDef() || InstDep.isClobber()) {
> - // Get the memory clobbered by the instruction we depend on. MemDep will
> - // skip any instructions that 'Loc' clearly doesn't interact with. If we
> - // end up depending on a may- or must-aliased load, then we can't optimize
> - // away the store and we bail out. However, if we depend on on something
> - // that overwrites the memory location we *can* potentially optimize it.
> - //
> - // Find out what memory location the dependent instruction stores.
> - Instruction *DepWrite = InstDep.getInst();
> - MemoryLocation DepLoc = getLocForWrite(DepWrite, *AA);
> - // If we didn't get a useful location, or if it isn't a size, bail out.
> - if (!DepLoc.Ptr)
> - break;
> -
> - // If we find a write that is a) removable (i.e., non-volatile), b) is
> - // completely obliterated by the store to 'Loc', and c) which we know that
> - // 'Inst' doesn't load from, then we can remove it.
> - if (isRemovable(DepWrite) &&
> - !isPossibleSelfRead(Inst, Loc, DepWrite, *TLI, *AA)) {
> - int64_t InstWriteOffset, DepWriteOffset;
> - OverwriteResult OR =
> - isOverwrite(Loc, DepLoc, DL, *TLI, DepWriteOffset, InstWriteOffset);
> - if (OR == OverwriteComplete) {
> - DEBUG(dbgs() << "DSE: Remove Dead Store:\n DEAD: "
> - << *DepWrite << "\n KILLER: " << *Inst << '\n');
> -
> - // Delete the store and now-dead instructions that feed it.
> - DeleteDeadInstruction(DepWrite, *MD, *TLI);
> - ++NumFastStores;
> - MadeChange = true;
> -
> - // DeleteDeadInstruction can delete the current instruction in loop
> - // cases, reset BBI.
> - BBI = Inst->getIterator();
> - if (BBI != BB.begin())
> - --BBI;
> - break;
> - } else if ((OR == OverwriteEnd && isShortenableAtTheEnd(DepWrite)) ||
> - ((OR == OverwriteBegin &&
> - isShortenableAtTheBeginning(DepWrite)))) {
> - // TODO: base this on the target vector size so that if the earlier
> - // store was too small to get vector writes anyway then its likely
> - // a good idea to shorten it
> - // Power of 2 vector writes are probably always a bad idea to optimize
> - // as any store/memset/memcpy is likely using vector instructions so
> - // shortening it to not vector size is likely to be slower
> - MemIntrinsic *DepIntrinsic = cast<MemIntrinsic>(DepWrite);
> - unsigned DepWriteAlign = DepIntrinsic->getAlignment();
> - bool IsOverwriteEnd = (OR == OverwriteEnd);
> - if (!IsOverwriteEnd)
> - InstWriteOffset = int64_t(InstWriteOffset + Loc.Size);
> -
> - if ((llvm::isPowerOf2_64(InstWriteOffset) &&
> - DepWriteAlign <= InstWriteOffset) ||
> - ((DepWriteAlign != 0) && InstWriteOffset % DepWriteAlign == 0)) {
> -
> - DEBUG(dbgs() << "DSE: Remove Dead Store:\n OW "
> - << (IsOverwriteEnd ? "END" : "BEGIN") << ": "
> - << *DepWrite << "\n KILLER (offset "
> - << InstWriteOffset << ", " << DepLoc.Size << ")"
> - << *Inst << '\n');
> -
> - int64_t NewLength =
> - IsOverwriteEnd
> - ? InstWriteOffset - DepWriteOffset
> - : DepLoc.Size - (InstWriteOffset - DepWriteOffset);
> -
> - Value *DepWriteLength = DepIntrinsic->getLength();
> - Value *TrimmedLength =
> - ConstantInt::get(DepWriteLength->getType(), NewLength);
> - DepIntrinsic->setLength(TrimmedLength);
> -
> - if (!IsOverwriteEnd) {
> - int64_t OffsetMoved = (InstWriteOffset - DepWriteOffset);
> - Value *Indices[1] = {
> - ConstantInt::get(DepWriteLength->getType(), OffsetMoved)};
> - GetElementPtrInst *NewDestGEP = GetElementPtrInst::CreateInBounds(
> - DepIntrinsic->getRawDest(), Indices, "", DepWrite);
> - DepIntrinsic->setDest(NewDestGEP);
> - }
> - MadeChange = true;
> - }
> - }
> - }
> -
> - // If this is a may-aliased store that is clobbering the store value, we
> - // can keep searching past it for another must-aliased pointer that stores
> - // to the same location. For example, in:
> - // store -> P
> - // store -> Q
> - // store -> P
> - // we can remove the first store to P even though we don't know if P and Q
> - // alias.
> - if (DepWrite == &BB.front()) break;
> -
> - // Can't look past this instruction if it might read 'Loc'.
> - if (AA->getModRefInfo(DepWrite, Loc) & MRI_Ref)
> - break;
> -
> - InstDep = MD->getPointerDependencyFrom(Loc, false,
> - DepWrite->getIterator(), &BB);
> - }
> - }
> -
> - // If this block ends in a return, unwind, or unreachable, all allocas are
> - // dead at its end, which means stores to them are also dead.
> - if (BB.getTerminator()->getNumSuccessors() == 0)
> - MadeChange |= handleEndBlock(BB);
> -
> - return MadeChange;
> -}
> -
> /// Returns true if the memory which is accessed by the second instruction is not
> /// modified between the first and the second instruction.
> /// Precondition: Second instruction must be dominated by the first
> /// instruction.
> -bool DSE::MemoryIsNotModifiedBetween(Instruction *FirstI,
> - Instruction *SecondI) {
> +static bool MemoryIsNotModifiedBetween(Instruction *FirstI,
> + Instruction *SecondI,
> + AliasAnalysis *AA) {
> SmallVector<BasicBlock *, 16> WorkList;
> SmallPtrSet<BasicBlock *, 8> Visited;
> BasicBlock::iterator FirstBBI(FirstI);
> @@ -777,9 +512,11 @@
> }
> }
>
> -/// HandleFree - Handle frees of entire structures whose dependency is a store
> +/// Handle frees of entire structures whose dependency is a store
> /// to a field of that structure.
> -bool DSE::HandleFree(CallInst *F) {
> +static bool HandleFree(CallInst *F, AliasAnalysis *AA,
> + MemoryDependenceResults *MD, DominatorTree *DT,
> + const TargetLibraryInfo *TLI) {
> bool MadeChange = false;
>
> MemoryLocation Loc = MemoryLocation(F->getOperand(0));
> @@ -828,13 +565,43 @@
> return MadeChange;
> }
>
> -/// handleEndBlock - Remove dead stores to stack-allocated locations in the
> -/// function end block. Ex:
> +/// Check to see if the specified location may alias any of the stack objects in
> +/// the DeadStackObjects set. If so, they become live because the location is
> +/// being loaded.
> +static void RemoveAccessedObjects(const MemoryLocation &LoadedLoc,
> + SmallSetVector<Value *, 16> &DeadStackObjects,
> + const DataLayout &DL, AliasAnalysis *AA,
> + const TargetLibraryInfo *TLI) {
> + const Value *UnderlyingPointer = GetUnderlyingObject(LoadedLoc.Ptr, DL);
> +
> + // A constant can't be in the dead pointer set.
> + if (isa<Constant>(UnderlyingPointer))
> + return;
> +
> + // If the kill pointer can be easily reduced to an alloca, don't bother doing
> + // extraneous AA queries.
> + if (isa<AllocaInst>(UnderlyingPointer) || isa<Argument>(UnderlyingPointer)) {
> + DeadStackObjects.remove(const_cast<Value*>(UnderlyingPointer));
> + return;
> + }
> +
> + // Remove objects that could alias LoadedLoc.
> + DeadStackObjects.remove_if([&](Value *I) {
> + // See if the loaded location could alias the stack location.
> + MemoryLocation StackLoc(I, getPointerSize(I, DL, *TLI));
> + return !AA->isNoAlias(StackLoc, LoadedLoc);
> + });
> +}
> +
> +/// Remove dead stores to stack-allocated locations in the function end block.
> +/// Ex:
> /// %A = alloca i32
> /// ...
> /// store i32 1, i32* %A
> /// ret void
> -bool DSE::handleEndBlock(BasicBlock &BB) {
> +static bool handleEndBlock(BasicBlock &BB, AliasAnalysis *AA,
> + MemoryDependenceResults *MD,
> + const TargetLibraryInfo *TLI) {
> bool MadeChange = false;
>
> // Keep track of all of the stack objects that are dead at the end of the
> @@ -967,7 +734,7 @@
>
> // Remove any allocas from the DeadPointer set that are loaded, as this
> // makes any stores above the access live.
> - RemoveAccessedObjects(LoadedLoc, DeadStackObjects, DL);
> + RemoveAccessedObjects(LoadedLoc, DeadStackObjects, DL, AA, TLI);
>
> // If all of the allocas were clobbered by the access then we're not going
> // to find anything else to process.
> @@ -978,29 +745,275 @@
> return MadeChange;
> }
>
> -/// RemoveAccessedObjects - Check to see if the specified location may alias any
> -/// of the stack objects in the DeadStackObjects set. If so, they become live
> -/// because the location is being loaded.
> -void DSE::RemoveAccessedObjects(const MemoryLocation &LoadedLoc,
> - SmallSetVector<Value *, 16> &DeadStackObjects,
> - const DataLayout &DL) {
> - const Value *UnderlyingPointer = GetUnderlyingObject(LoadedLoc.Ptr, DL);
> +static bool eliminateDeadStores(BasicBlock &BB, AliasAnalysis *AA,
> + MemoryDependenceResults *MD, DominatorTree *DT,
> + const TargetLibraryInfo *TLI) {
> + const DataLayout &DL = BB.getModule()->getDataLayout();
> + bool MadeChange = false;
>
> - // A constant can't be in the dead pointer set.
> - if (isa<Constant>(UnderlyingPointer))
> - return;
> + // Do a top-down walk on the BB.
> + for (BasicBlock::iterator BBI = BB.begin(), BBE = BB.end(); BBI != BBE; ) {
> + Instruction *Inst = &*BBI++;
>
> - // If the kill pointer can be easily reduced to an alloca, don't bother doing
> - // extraneous AA queries.
> - if (isa<AllocaInst>(UnderlyingPointer) || isa<Argument>(UnderlyingPointer)) {
> - DeadStackObjects.remove(const_cast<Value*>(UnderlyingPointer));
> - return;
> + // Handle 'free' calls specially.
> + if (CallInst *F = isFreeCall(Inst, TLI)) {
> + MadeChange |= HandleFree(F, AA, MD, DT, TLI);
> + continue;
> + }
> +
> + // If we find something that writes memory, get its memory dependence.
> + if (!hasMemoryWrite(Inst, *TLI))
> + continue;
> +
> + // If we're storing the same value back to a pointer that we just
> + // loaded from, then the store can be removed.
> + if (StoreInst *SI = dyn_cast<StoreInst>(Inst)) {
> +
> + auto RemoveDeadInstAndUpdateBBI = [&](Instruction *DeadInst) {
> + // DeleteDeadInstruction can delete the current instruction. Save BBI
> + // in case we need it.
> + WeakVH NextInst(&*BBI);
> +
> + DeleteDeadInstruction(DeadInst, *MD, *TLI);
> +
> + if (!NextInst) // Next instruction deleted.
> + BBI = BB.begin();
> + else if (BBI != BB.begin()) // Revisit this instruction if possible.
> + --BBI;
> + ++NumRedundantStores;
> + MadeChange = true;
> + };
> +
> + if (LoadInst *DepLoad = dyn_cast<LoadInst>(SI->getValueOperand())) {
> + if (SI->getPointerOperand() == DepLoad->getPointerOperand() &&
> + isRemovable(SI) &&
> + MemoryIsNotModifiedBetween(DepLoad, SI, AA)) {
> +
> + DEBUG(dbgs() << "DSE: Remove Store Of Load from same pointer:\n "
> + << "LOAD: " << *DepLoad << "\n STORE: " << *SI << '\n');
> +
> + RemoveDeadInstAndUpdateBBI(SI);
> + continue;
> + }
> + }
> +
> + // Remove null stores into the calloc'ed objects
> + Constant *StoredConstant = dyn_cast<Constant>(SI->getValueOperand());
> +
> + if (StoredConstant && StoredConstant->isNullValue() &&
> + isRemovable(SI)) {
> + Instruction *UnderlyingPointer = dyn_cast<Instruction>(
> + GetUnderlyingObject(SI->getPointerOperand(), DL));
> +
> + if (UnderlyingPointer && isCallocLikeFn(UnderlyingPointer, TLI) &&
> + MemoryIsNotModifiedBetween(UnderlyingPointer, SI, AA)) {
> + DEBUG(dbgs()
> + << "DSE: Remove null store to the calloc'ed object:\n DEAD: "
> + << *Inst << "\n OBJECT: " << *UnderlyingPointer << '\n');
> +
> + RemoveDeadInstAndUpdateBBI(SI);
> + continue;
> + }
> + }
> + }
> +
> + MemDepResult InstDep = MD->getDependency(Inst);
> +
> + // Ignore any store where we can't find a local dependence.
> + // FIXME: cross-block DSE would be fun. :)
> + if (!InstDep.isDef() && !InstDep.isClobber())
> + continue;
> +
> + // Figure out what location is being stored to.
> + MemoryLocation Loc = getLocForWrite(Inst, *AA);
> +
> + // If we didn't get a useful location, fail.
> + if (!Loc.Ptr)
> + continue;
> +
> + while (InstDep.isDef() || InstDep.isClobber()) {
> + // Get the memory clobbered by the instruction we depend on. MemDep will
> + // skip any instructions that 'Loc' clearly doesn't interact with. If we
> + // end up depending on a may- or must-aliased load, then we can't optimize
> + // away the store and we bail out. However, if we depend on on something
> + // that overwrites the memory location we *can* potentially optimize it.
> + //
> + // Find out what memory location the dependent instruction stores.
> + Instruction *DepWrite = InstDep.getInst();
> + MemoryLocation DepLoc = getLocForWrite(DepWrite, *AA);
> + // If we didn't get a useful location, or if it isn't a size, bail out.
> + if (!DepLoc.Ptr)
> + break;
> +
> + // If we find a write that is a) removable (i.e., non-volatile), b) is
> + // completely obliterated by the store to 'Loc', and c) which we know that
> + // 'Inst' doesn't load from, then we can remove it.
> + if (isRemovable(DepWrite) &&
> + !isPossibleSelfRead(Inst, Loc, DepWrite, *TLI, *AA)) {
> + int64_t InstWriteOffset, DepWriteOffset;
> + OverwriteResult OR =
> + isOverwrite(Loc, DepLoc, DL, *TLI, DepWriteOffset, InstWriteOffset);
> + if (OR == OverwriteComplete) {
> + DEBUG(dbgs() << "DSE: Remove Dead Store:\n DEAD: "
> + << *DepWrite << "\n KILLER: " << *Inst << '\n');
> +
> + // Delete the store and now-dead instructions that feed it.
> + DeleteDeadInstruction(DepWrite, *MD, *TLI);
> + ++NumFastStores;
> + MadeChange = true;
> +
> + // DeleteDeadInstruction can delete the current instruction in loop
> + // cases, reset BBI.
> + BBI = Inst->getIterator();
> + if (BBI != BB.begin())
> + --BBI;
> + break;
> + } else if ((OR == OverwriteEnd && isShortenableAtTheEnd(DepWrite)) ||
> + ((OR == OverwriteBegin &&
> + isShortenableAtTheBeginning(DepWrite)))) {
> + // TODO: base this on the target vector size so that if the earlier
> + // store was too small to get vector writes anyway then its likely
> + // a good idea to shorten it
> + // Power of 2 vector writes are probably always a bad idea to optimize
> + // as any store/memset/memcpy is likely using vector instructions so
> + // shortening it to not vector size is likely to be slower
> + MemIntrinsic *DepIntrinsic = cast<MemIntrinsic>(DepWrite);
> + unsigned DepWriteAlign = DepIntrinsic->getAlignment();
> + bool IsOverwriteEnd = (OR == OverwriteEnd);
> + if (!IsOverwriteEnd)
> + InstWriteOffset = int64_t(InstWriteOffset + Loc.Size);
> +
> + if ((llvm::isPowerOf2_64(InstWriteOffset) &&
> + DepWriteAlign <= InstWriteOffset) ||
> + ((DepWriteAlign != 0) && InstWriteOffset % DepWriteAlign == 0)) {
> +
> + DEBUG(dbgs() << "DSE: Remove Dead Store:\n OW "
> + << (IsOverwriteEnd ? "END" : "BEGIN") << ": "
> + << *DepWrite << "\n KILLER (offset "
> + << InstWriteOffset << ", " << DepLoc.Size << ")"
> + << *Inst << '\n');
> +
> + int64_t NewLength =
> + IsOverwriteEnd
> + ? InstWriteOffset - DepWriteOffset
> + : DepLoc.Size - (InstWriteOffset - DepWriteOffset);
> +
> + Value *DepWriteLength = DepIntrinsic->getLength();
> + Value *TrimmedLength =
> + ConstantInt::get(DepWriteLength->getType(), NewLength);
> + DepIntrinsic->setLength(TrimmedLength);
> +
> + if (!IsOverwriteEnd) {
> + int64_t OffsetMoved = (InstWriteOffset - DepWriteOffset);
> + Value *Indices[1] = {
> + ConstantInt::get(DepWriteLength->getType(), OffsetMoved)};
> + GetElementPtrInst *NewDestGEP = GetElementPtrInst::CreateInBounds(
> + DepIntrinsic->getRawDest(), Indices, "", DepWrite);
> + DepIntrinsic->setDest(NewDestGEP);
> + }
> + MadeChange = true;
> + }
> + }
> + }
> +
> + // If this is a may-aliased store that is clobbering the store value, we
> + // can keep searching past it for another must-aliased pointer that stores
> + // to the same location. For example, in:
> + // store -> P
> + // store -> Q
> + // store -> P
> + // we can remove the first store to P even though we don't know if P and Q
> + // alias.
> + if (DepWrite == &BB.front()) break;
> +
> + // Can't look past this instruction if it might read 'Loc'.
> + if (AA->getModRefInfo(DepWrite, Loc) & MRI_Ref)
> + break;
> +
> + InstDep = MD->getPointerDependencyFrom(Loc, false,
> + DepWrite->getIterator(), &BB);
> + }
> }
>
> - // Remove objects that could alias LoadedLoc.
> - DeadStackObjects.remove_if([&](Value *I) {
> - // See if the loaded location could alias the stack location.
> - MemoryLocation StackLoc(I, getPointerSize(I, DL, *TLI));
> - return !AA->isNoAlias(StackLoc, LoadedLoc);
> - });
> + // If this block ends in a return, unwind, or unreachable, all allocas are
> + // dead at its end, which means stores to them are also dead.
> + if (BB.getTerminator()->getNumSuccessors() == 0)
> + MadeChange |= handleEndBlock(BB, AA, MD, TLI);
> +
> + return MadeChange;
> +}
> +
> +static bool eliminateDeadStores(Function &F, AliasAnalysis *AA,
> + MemoryDependenceResults *MD, DominatorTree *DT,
> + const TargetLibraryInfo *TLI) {
> + bool MadeChange = false;
> + for (BasicBlock &BB : F)
> + // Only check non-dead blocks. Dead blocks may have strange pointer
> + // cycles that will confuse alias analysis.
> + if (DT->isReachableFromEntry(&BB))
> + MadeChange |= eliminateDeadStores(BB, AA, MD, DT, TLI);
> + return MadeChange;
> +}
> +
> +//===----------------------------------------------------------------------===//
> +// DSE Pass
> +//===----------------------------------------------------------------------===//
> +PreservedAnalyses DSEPass::run(Function &F, FunctionAnalysisManager &AM) {
> + AliasAnalysis *AA = &AM.getResult<AAManager>(F);
> + DominatorTree *DT = &AM.getResult<DominatorTreeAnalysis>(F);
> + MemoryDependenceResults *MD = &AM.getResult<MemoryDependenceAnalysis>(F);
> + const TargetLibraryInfo *TLI = &AM.getResult<TargetLibraryAnalysis>(F);
> +
> + return eliminateDeadStores(F, AA, MD, DT, TLI) ? PreservedAnalyses::none()
> + : PreservedAnalyses::all();
> +}
> +
> +/// A legacy pass for the legacy pass manager that wraps \c DSEPass.
> +class DSELegacyPass : public FunctionPass {
> +public:
> + DSELegacyPass() : FunctionPass(ID) {
> + initializeDSELegacyPassPass(*PassRegistry::getPassRegistry());
> + }
> +
> + bool runOnFunction(Function &F) override {
> + if (skipFunction(F))
> + return false;
> +
> + DominatorTree *DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
> + AliasAnalysis *AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
> + MemoryDependenceResults *MD =
> + &getAnalysis<MemoryDependenceWrapperPass>().getMemDep();
> + const TargetLibraryInfo *TLI =
> + &getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();
> +
> + return eliminateDeadStores(F, AA, MD, DT, TLI);
> + }
> +
> + void getAnalysisUsage(AnalysisUsage &AU) const override {
> + AU.setPreservesCFG();
> + AU.addRequired<DominatorTreeWrapperPass>();
> + AU.addRequired<AAResultsWrapperPass>();
> + AU.addRequired<MemoryDependenceWrapperPass>();
> + AU.addRequired<TargetLibraryInfoWrapperPass>();
> + AU.addPreserved<DominatorTreeWrapperPass>();
> + AU.addPreserved<GlobalsAAWrapperPass>();
> + AU.addPreserved<MemoryDependenceWrapperPass>();
> + }
> +
> + static char ID; // Pass identification, replacement for typeid
> +};
> +
> +char DSELegacyPass::ID = 0;
> +INITIALIZE_PASS_BEGIN(DSELegacyPass, "dse", "Dead Store Elimination", false,
> + false)
> +INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
> +INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass)
> +INITIALIZE_PASS_DEPENDENCY(GlobalsAAWrapperPass)
> +INITIALIZE_PASS_DEPENDENCY(MemoryDependenceWrapperPass)
> +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
> +INITIALIZE_PASS_END(DSELegacyPass, "dse", "Dead Store Elimination", false,
> + false)
> +
> +FunctionPass *llvm::createDeadStoreEliminationPass() {
> + return new DSELegacyPass();
> }
> Index: lib/Passes/PassRegistry.def
> ===================================================================
> --- lib/Passes/PassRegistry.def
> +++ lib/Passes/PassRegistry.def
> @@ -111,6 +111,7 @@
> FUNCTION_PASS("aa-eval", AAEvaluator())
> FUNCTION_PASS("adce", ADCEPass())
> FUNCTION_PASS("dce", DCEPass())
> +FUNCTION_PASS("dse", DSEPass())
> FUNCTION_PASS("early-cse", EarlyCSEPass())
> FUNCTION_PASS("instcombine", InstCombinePass())
> FUNCTION_PASS("invalidate<all>", InvalidateAllAnalysesPass())
> Index: lib/Passes/PassBuilder.cpp
> ===================================================================
> --- lib/Passes/PassBuilder.cpp
> +++ lib/Passes/PassBuilder.cpp
> @@ -64,6 +64,7 @@
> #include "llvm/Transforms/PGOInstrumentation.h"
> #include "llvm/Transforms/Scalar/ADCE.h"
> #include "llvm/Transforms/Scalar/DCE.h"
> +#include "llvm/Transforms/Scalar/DeadStoreElimination.h"
> #include "llvm/Transforms/Scalar/EarlyCSE.h"
> #include "llvm/Transforms/Scalar/GVN.h"
> #include "llvm/Transforms/Scalar/LoopRotation.h"
> Index: include/llvm/Transforms/Scalar/DeadStoreElimination.h
> ===================================================================
> --- /dev/null
> +++ include/llvm/Transforms/Scalar/DeadStoreElimination.h
> @@ -0,0 +1,34 @@
> +//===- DeadStoreElimination.h - Fast Dead Store Elimination -------------===//
> +//
> +// The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// This file implements a trivial dead store elimination that only considers
> +// basic-block local redundant stores.
> +//
> +// FIXME: This should eventually be extended to be a post-dominator tree
> +// traversal. Doing so would be pretty trivial.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_TRANSFORMS_SCALAR_DSE_H
> +#define LLVM_TRANSFORMS_SCALAR_DSE_H
> +
> +#include "llvm/IR/Function.h"
> +#include "llvm/IR/PassManager.h"
> +
> +namespace llvm {
> +
> +/// This class implements a trivial dead store elimination. We consider
> +/// only the redundant stores that are local to a single Basic Block.
> +class DSEPass : public PassInfoMixin<DSEPass> {
> +public:
> + PreservedAnalyses run(Function &F, AnalysisManager<Function> &FAM);
> +};
> +}
> +
> +#endif // LLVM_TRANSFORMS_SCALAR_DSE_H
> Index: include/llvm/InitializePasses.h
> ===================================================================
> --- include/llvm/InitializePasses.h
> +++ include/llvm/InitializePasses.h
> @@ -105,7 +105,7 @@
> void initializeDAEPass(PassRegistry&);
> void initializeDAHPass(PassRegistry&);
> void initializeDCELegacyPassPass(PassRegistry&);
> -void initializeDSEPass(PassRegistry&);
> +void initializeDSELegacyPassPass(PassRegistry&);
> void initializeDeadInstEliminationPass(PassRegistry&);
> void initializeDeadMachineInstructionElimPass(PassRegistry&);
> void initializeDelinearizationPass(PassRegistry &);
More information about the llvm-commits
mailing list