diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2021-11-19 20:06:13 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2021-11-19 20:06:13 +0000 |
commit | c0981da47d5696fe36474fcf86b4ce03ae3ff818 (patch) | |
tree | f42add1021b9f2ac6a69ac7cf6c4499962739a45 /llvm/lib/Analysis/MemorySSA.cpp | |
parent | 344a3780b2e33f6ca763666c380202b18aab72a3 (diff) | |
download | src-c0981da47d5696fe36474fcf86b4ce03ae3ff818.tar.gz src-c0981da47d5696fe36474fcf86b4ce03ae3ff818.zip |
Diffstat (limited to 'llvm/lib/Analysis/MemorySSA.cpp')
-rw-r--r-- | llvm/lib/Analysis/MemorySSA.cpp | 207 |
1 files changed, 164 insertions, 43 deletions
diff --git a/llvm/lib/Analysis/MemorySSA.cpp b/llvm/lib/Analysis/MemorySSA.cpp index b402b0467f5d..ac20e20f0c0d 100644 --- a/llvm/lib/Analysis/MemorySSA.cpp +++ b/llvm/lib/Analysis/MemorySSA.cpp @@ -90,22 +90,18 @@ bool llvm::VerifyMemorySSA = true; #else bool llvm::VerifyMemorySSA = false; #endif -/// Enables memory ssa as a dependency for loop passes in legacy pass manager. -cl::opt<bool> llvm::EnableMSSALoopDependency( - "enable-mssa-loop-dependency", cl::Hidden, cl::init(true), - cl::desc("Enable MemorySSA dependency for loop pass manager")); static cl::opt<bool, true> VerifyMemorySSAX("verify-memoryssa", cl::location(VerifyMemorySSA), cl::Hidden, cl::desc("Enable verification of MemorySSA.")); -namespace llvm { +const static char LiveOnEntryStr[] = "liveOnEntry"; + +namespace { /// An assembly annotator class to print Memory SSA information in /// comments. class MemorySSAAnnotatedWriter : public AssemblyAnnotationWriter { - friend class MemorySSA; - const MemorySSA *MSSA; public: @@ -124,7 +120,34 @@ public: } }; -} // end namespace llvm +/// An assembly annotator class to print Memory SSA information in +/// comments. +class MemorySSAWalkerAnnotatedWriter : public AssemblyAnnotationWriter { + MemorySSA *MSSA; + MemorySSAWalker *Walker; + +public: + MemorySSAWalkerAnnotatedWriter(MemorySSA *M) + : MSSA(M), Walker(M->getWalker()) {} + + void emitInstructionAnnot(const Instruction *I, + formatted_raw_ostream &OS) override { + if (MemoryAccess *MA = MSSA->getMemoryAccess(I)) { + MemoryAccess *Clobber = Walker->getClobberingMemoryAccess(MA); + OS << "; " << *MA; + if (Clobber) { + OS << " - clobbered by "; + if (MSSA->isLiveOnEntryDef(Clobber)) + OS << LiveOnEntryStr; + else + OS << *Clobber; + } + OS << "\n"; + } + } +}; + +} // namespace namespace { @@ -286,6 +309,7 @@ instructionClobbersQuery(const MemoryDef *MD, const MemoryLocation &UseLoc, case Intrinsic::invariant_end: case Intrinsic::assume: case Intrinsic::experimental_noalias_scope_decl: + case Intrinsic::pseudoprobe: return {false, AliasResult(AliasResult::NoAlias)}; case Intrinsic::dbg_addr: case Intrinsic::dbg_declare: @@ -1016,7 +1040,8 @@ public: // updated if a new clobber is found by this SkipSelf search. If this // additional query becomes heavily used we may decide to cache the result. // Walker instantiations will decide how to set the SkipSelf bool. - MemoryAccess *getClobberingMemoryAccessBase(MemoryAccess *, unsigned &, bool); + MemoryAccess *getClobberingMemoryAccessBase(MemoryAccess *, unsigned &, bool, + bool UseInvariantGroup = true); }; /// A MemorySSAWalker that does AA walks to disambiguate accesses. It no @@ -1041,6 +1066,11 @@ public: unsigned &UWL) { return Walker->getClobberingMemoryAccessBase(MA, Loc, UWL); } + // This method is not accessible outside of this file. + MemoryAccess *getClobberingMemoryAccessWithoutInvariantGroup(MemoryAccess *MA, + unsigned &UWL) { + return Walker->getClobberingMemoryAccessBase(MA, UWL, false, false); + } MemoryAccess *getClobberingMemoryAccess(MemoryAccess *MA) override { unsigned UpwardWalkLimit = MaxCheckLimit; @@ -1437,10 +1467,13 @@ void MemorySSA::OptimizeUses::optimizeUsesInBlock( unsigned UpwardWalkLimit = MaxCheckLimit; while (UpperBound > LocInfo.LowerBound) { if (isa<MemoryPhi>(VersionStack[UpperBound])) { - // For phis, use the walker, see where we ended up, go there + // For phis, use the walker, see where we ended up, go there. + // The invariant.group handling in MemorySSA is ad-hoc and doesn't + // support updates, so don't use it to optimize uses. MemoryAccess *Result = - Walker->getClobberingMemoryAccess(MU, UpwardWalkLimit); - // We are guaranteed to find it or something is wrong + Walker->getClobberingMemoryAccessWithoutInvariantGroup( + MU, UpwardWalkLimit); + // We are guaranteed to find it or something is wrong. while (VersionStack[UpperBound] != Result) { assert(UpperBound != 0); --UpperBound; @@ -1750,6 +1783,7 @@ MemoryUseOrDef *MemorySSA::createNewAccess(Instruction *I, break; case Intrinsic::assume: case Intrinsic::experimental_noalias_scope_decl: + case Intrinsic::pseudoprobe: return nullptr; } } @@ -1864,10 +1898,17 @@ void MemorySSA::print(raw_ostream &OS) const { LLVM_DUMP_METHOD void MemorySSA::dump() const { print(dbgs()); } #endif -void MemorySSA::verifyMemorySSA() const { - verifyOrderingDominationAndDefUses(F); +void MemorySSA::verifyMemorySSA(VerificationLevel VL) const { +#if !defined(NDEBUG) && defined(EXPENSIVE_CHECKS) + VL = VerificationLevel::Full; +#endif + +#ifndef NDEBUG + verifyOrderingDominationAndDefUses(F, VL); verifyDominationNumbers(F); - verifyPrevDefInPhis(F); + if (VL == VerificationLevel::Full) + verifyPrevDefInPhis(F); +#endif // Previously, the verification used to also verify that the clobberingAccess // cached by MemorySSA is the same as the clobberingAccess found at a later // query to AA. This does not hold true in general due to the current fragility @@ -1881,7 +1922,6 @@ void MemorySSA::verifyMemorySSA() const { } void MemorySSA::verifyPrevDefInPhis(Function &F) const { -#if !defined(NDEBUG) && defined(EXPENSIVE_CHECKS) for (const BasicBlock &BB : F) { if (MemoryPhi *Phi = getMemoryAccess(&BB)) { for (unsigned I = 0, E = Phi->getNumIncomingValues(); I != E; ++I) { @@ -1896,6 +1936,8 @@ void MemorySSA::verifyPrevDefInPhis(Function &F) const { auto *LastAcc = &*(--DefList->end()); assert(LastAcc == IncAcc && "Incorrect incoming access into phi."); + (void)IncAcc; + (void)LastAcc; break; } DTNode = DTNode->getIDom(); @@ -1911,13 +1953,11 @@ void MemorySSA::verifyPrevDefInPhis(Function &F) const { } } } -#endif } /// Verify that all of the blocks we believe to have valid domination numbers /// actually have valid domination numbers. void MemorySSA::verifyDominationNumbers(const Function &F) const { -#ifndef NDEBUG if (BlockNumberingValid.empty()) return; @@ -1943,13 +1983,13 @@ void MemorySSA::verifyDominationNumbers(const Function &F) const { unsigned long ThisNumber = ThisNumberIter->second; assert(ThisNumber > LastNumber && "Domination numbers should be strictly increasing!"); + (void)LastNumber; LastNumber = ThisNumber; } } assert(ValidBlocks.empty() && "All valid BasicBlocks should exist in F -- dangling pointers?"); -#endif } /// Verify ordering: the order and existence of MemoryAccesses matches the @@ -1958,8 +1998,8 @@ void MemorySSA::verifyDominationNumbers(const Function &F) const { /// Verify def-uses: the immediate use information - walk all the memory /// accesses and verifying that, for each use, it appears in the appropriate /// def's use list -void MemorySSA::verifyOrderingDominationAndDefUses(Function &F) const { -#if !defined(NDEBUG) +void MemorySSA::verifyOrderingDominationAndDefUses(Function &F, + VerificationLevel VL) const { // Walk all the blocks, comparing what the lookups think and what the access // lists think, as well as the order in the blocks vs the order in the access // lists. @@ -1974,19 +2014,21 @@ void MemorySSA::verifyOrderingDominationAndDefUses(Function &F) const { ActualAccesses.push_back(Phi); ActualDefs.push_back(Phi); // Verify domination - for (const Use &U : Phi->uses()) + for (const Use &U : Phi->uses()) { assert(dominates(Phi, U) && "Memory PHI does not dominate it's uses"); -#if defined(EXPENSIVE_CHECKS) - // Verify def-uses. - assert(Phi->getNumOperands() == static_cast<unsigned>(std::distance( - pred_begin(&B), pred_end(&B))) && - "Incomplete MemoryPhi Node"); - for (unsigned I = 0, E = Phi->getNumIncomingValues(); I != E; ++I) { - verifyUseInDefs(Phi->getIncomingValue(I), Phi); - assert(is_contained(predecessors(&B), Phi->getIncomingBlock(I)) && - "Incoming phi block not a block predecessor"); + (void)U; + } + // Verify def-uses for full verify. + if (VL == VerificationLevel::Full) { + assert(Phi->getNumOperands() == static_cast<unsigned>(std::distance( + pred_begin(&B), pred_end(&B))) && + "Incomplete MemoryPhi Node"); + for (unsigned I = 0, E = Phi->getNumIncomingValues(); I != E; ++I) { + verifyUseInDefs(Phi->getIncomingValue(I), Phi); + assert(is_contained(predecessors(&B), Phi->getIncomingBlock(I)) && + "Incoming phi block not a block predecessor"); + } } -#endif } for (Instruction &I : B) { @@ -2002,14 +2044,15 @@ void MemorySSA::verifyOrderingDominationAndDefUses(Function &F) const { // Verify ordering. ActualDefs.push_back(MA); // Verify domination. - for (const Use &U : MD->uses()) + for (const Use &U : MD->uses()) { assert(dominates(MD, U) && "Memory Def does not dominate it's uses"); + (void)U; + } } -#if defined(EXPENSIVE_CHECKS) - // Verify def-uses. - verifyUseInDefs(MA->getDefiningAccess(), MA); -#endif + // Verify def-uses for full verify. + if (VL == VerificationLevel::Full) + verifyUseInDefs(MA->getDefiningAccess(), MA); } } // Either we hit the assert, really have no accesses, or we have both @@ -2044,13 +2087,11 @@ void MemorySSA::verifyOrderingDominationAndDefUses(Function &F) const { } ActualDefs.clear(); } -#endif } /// Verify the def-use lists in MemorySSA, by verifying that \p Use /// appears in the use list of \p Def. void MemorySSA::verifyUseInDefs(MemoryAccess *Def, MemoryAccess *Use) const { -#ifndef NDEBUG // The live on entry use may cause us to get a NULL def here if (!Def) assert(isLiveOnEntryDef(Use) && @@ -2058,7 +2099,6 @@ void MemorySSA::verifyUseInDefs(MemoryAccess *Def, MemoryAccess *Use) const { else assert(is_contained(Def->users(), Use) && "Did not find use in def's use list"); -#endif } /// Perform a local numbering on blocks so that instruction ordering can be @@ -2138,8 +2178,6 @@ bool MemorySSA::dominates(const MemoryAccess *Dominator, return dominates(Dominator, cast<MemoryAccess>(Dominatee.getUser())); } -const static char LiveOnEntryStr[] = "liveOnEntry"; - void MemoryAccess::print(raw_ostream &OS) const { switch (getValueID()) { case MemoryPhiVal: return static_cast<const MemoryPhi *>(this)->print(OS); @@ -2355,6 +2393,16 @@ PreservedAnalyses MemorySSAPrinterPass::run(Function &F, return PreservedAnalyses::all(); } +PreservedAnalyses MemorySSAWalkerPrinterPass::run(Function &F, + FunctionAnalysisManager &AM) { + auto &MSSA = AM.getResult<MemorySSAAnalysis>(F).getMSSA(); + OS << "MemorySSA (walker) for function: " << F.getName() << "\n"; + MemorySSAWalkerAnnotatedWriter Writer(&MSSA); + F.print(OS, &Writer); + + return PreservedAnalyses::all(); +} + PreservedAnalyses MemorySSAVerifierPass::run(Function &F, FunctionAnalysisManager &AM) { AM.getResult<MemorySSAAnalysis>(F).getMSSA().verifyMemorySSA(); @@ -2438,15 +2486,88 @@ MemorySSA::ClobberWalkerBase<AliasAnalysisType>::getClobberingMemoryAccessBase( return Clobber; } +static const Instruction * +getInvariantGroupClobberingInstruction(Instruction &I, DominatorTree &DT) { + if (!I.hasMetadata(LLVMContext::MD_invariant_group) || I.isVolatile()) + return nullptr; + + // We consider bitcasts and zero GEPs to be the same pointer value. Start by + // stripping bitcasts and zero GEPs, then we will recursively look at loads + // and stores through bitcasts and zero GEPs. + Value *PointerOperand = getLoadStorePointerOperand(&I)->stripPointerCasts(); + + // It's not safe to walk the use list of a global value because function + // passes aren't allowed to look outside their functions. + // FIXME: this could be fixed by filtering instructions from outside of + // current function. + if (isa<Constant>(PointerOperand)) + return nullptr; + + // Queue to process all pointers that are equivalent to load operand. + SmallVector<const Value *, 8> PointerUsesQueue; + PointerUsesQueue.push_back(PointerOperand); + + const Instruction *MostDominatingInstruction = &I; + + // FIXME: This loop is O(n^2) because dominates can be O(n) and in worst case + // we will see all the instructions. It may not matter in practice. If it + // does, we will have to support MemorySSA construction and updates. + while (!PointerUsesQueue.empty()) { + const Value *Ptr = PointerUsesQueue.pop_back_val(); + assert(Ptr && !isa<GlobalValue>(Ptr) && + "Null or GlobalValue should not be inserted"); + + for (const User *Us : Ptr->users()) { + auto *U = dyn_cast<Instruction>(Us); + if (!U || U == &I || !DT.dominates(U, MostDominatingInstruction)) + continue; + + // Add bitcasts and zero GEPs to queue. + if (isa<BitCastInst>(U)) { + PointerUsesQueue.push_back(U); + continue; + } + if (auto *GEP = dyn_cast<GetElementPtrInst>(U)) { + if (GEP->hasAllZeroIndices()) + PointerUsesQueue.push_back(U); + continue; + } + + // If we hit a load/store with an invariant.group metadata and the same + // pointer operand, we can assume that value pointed to by the pointer + // operand didn't change. + if (U->hasMetadata(LLVMContext::MD_invariant_group) && + getLoadStorePointerOperand(U) == Ptr && !U->isVolatile()) { + MostDominatingInstruction = U; + } + } + } + return MostDominatingInstruction == &I ? nullptr : MostDominatingInstruction; +} + template <typename AliasAnalysisType> MemoryAccess * MemorySSA::ClobberWalkerBase<AliasAnalysisType>::getClobberingMemoryAccessBase( - MemoryAccess *MA, unsigned &UpwardWalkLimit, bool SkipSelf) { + MemoryAccess *MA, unsigned &UpwardWalkLimit, bool SkipSelf, + bool UseInvariantGroup) { auto *StartingAccess = dyn_cast<MemoryUseOrDef>(MA); // If this is a MemoryPhi, we can't do anything. if (!StartingAccess) return MA; + if (UseInvariantGroup) { + if (auto *I = getInvariantGroupClobberingInstruction( + *StartingAccess->getMemoryInst(), MSSA->getDomTree())) { + assert(isa<LoadInst>(I) || isa<StoreInst>(I)); + + auto *ClobberMA = MSSA->getMemoryAccess(I); + assert(ClobberMA); + if (isa<MemoryUse>(ClobberMA)) + return ClobberMA->getDefiningAccess(); + return ClobberMA; + } + } + bool IsOptimized = false; // If this is an already optimized use or def, return the optimized result. |