From 198959c487f744f9a4649640c0dfab318ba154fd Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Wed, 20 Sep 2017 02:31:57 +0000 Subject: [PATCH] Tighten the invariants around LoopBase::invalidate Summary: With this change: - Methods in LoopBase trip an assert if the receiver has been invalidated - LoopBase::clear frees up the memory held the LoopBase instance This change also shuffles things around as necessary to work with this stricter invariant. Reviewers: chandlerc Subscribers: mehdi_amini, mcrosier, llvm-commits Differential Revision: https://reviews.llvm.org/D38055 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@313708 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Analysis/LoopInfo.h | 81 +++++++++++++++++----- include/llvm/Analysis/LoopInfoImpl.h | 12 ++++ include/llvm/Transforms/Scalar/LoopPassManager.h | 3 +- include/llvm/Transforms/Utils/UnrollLoop.h | 30 ++++++-- lib/Analysis/LoopInfo.cpp | 5 +- lib/Analysis/LoopPass.cpp | 4 +- lib/Transforms/Scalar/LoopUnrollPass.cpp | 13 ++-- lib/Transforms/Utils/LoopUnroll.cpp | 38 +++++----- .../LoopUnroll/unroll-loop-invalidation.ll | 4 +- 9 files changed, 132 insertions(+), 58 deletions(-) diff --git a/include/llvm/Analysis/LoopInfo.h b/include/llvm/Analysis/LoopInfo.h index 7e97bbec649..fc26de391d9 100644 --- a/include/llvm/Analysis/LoopInfo.h +++ b/include/llvm/Analysis/LoopInfo.h @@ -81,6 +81,14 @@ template class LoopBase { const LoopBase & operator=(const LoopBase &) = delete; + void clear() { + IsInvalid = true; + SubLoops.clear(); + Blocks.clear(); + DenseBlockSet.clear(); + ParentLoop = nullptr; + } + public: /// This creates an empty loop. LoopBase() : ParentLoop(nullptr) {} @@ -89,20 +97,25 @@ public: /// for consistency with loop depth values used for basic blocks, where depth /// 0 is used for blocks not inside any loops. unsigned getLoopDepth() const { + assert(!isInvalid() && "Loop not in a valid state!"); unsigned D = 1; for (const LoopT *CurLoop = ParentLoop; CurLoop; CurLoop = CurLoop->ParentLoop) ++D; return D; } - BlockT *getHeader() const { return Blocks.front(); } + BlockT *getHeader() const { return getBlocks().front(); } LoopT *getParentLoop() const { return ParentLoop; } /// This is a raw interface for bypassing addChildLoop. - void setParentLoop(LoopT *L) { ParentLoop = L; } + void setParentLoop(LoopT *L) { + assert(!isInvalid() && "Loop not in a valid state!"); + ParentLoop = L; + } /// Return true if the specified loop is contained within in this loop. bool contains(const LoopT *L) const { + assert(!isInvalid() && "Loop not in a valid state!"); if (L == this) return true; if (!L) @@ -111,7 +124,10 @@ public: } /// Return true if the specified basic block is in this loop. - bool contains(const BlockT *BB) const { return DenseBlockSet.count(BB); } + bool contains(const BlockT *BB) const { + assert(!isInvalid() && "Loop not in a valid state!"); + return DenseBlockSet.count(BB); + } /// Return true if the specified instruction is in this loop. template bool contains(const InstT *Inst) const { @@ -119,38 +135,50 @@ public: } /// Return the loops contained entirely within this loop. - const std::vector &getSubLoops() const { return SubLoops; } - std::vector &getSubLoopsVector() { return SubLoops; } + const std::vector &getSubLoops() const { + assert(!isInvalid() && "Loop not in a valid state!"); + return SubLoops; + } + std::vector &getSubLoopsVector() { + assert(!isInvalid() && "Loop not in a valid state!"); + return SubLoops; + } typedef typename std::vector::const_iterator iterator; typedef typename std::vector::const_reverse_iterator reverse_iterator; - iterator begin() const { return SubLoops.begin(); } - iterator end() const { return SubLoops.end(); } - reverse_iterator rbegin() const { return SubLoops.rbegin(); } - reverse_iterator rend() const { return SubLoops.rend(); } - bool empty() const { return SubLoops.empty(); } + iterator begin() const { return getSubLoops().begin(); } + iterator end() const { return getSubLoops().end(); } + reverse_iterator rbegin() const { return getSubLoops().rbegin(); } + reverse_iterator rend() const { return getSubLoops().rend(); } + bool empty() const { return getSubLoops().empty(); } /// Get a list of the basic blocks which make up this loop. - const std::vector &getBlocks() const { return Blocks; } + const std::vector &getBlocks() const { + assert(!isInvalid() && "Loop not in a valid state!"); + return Blocks; + } typedef typename std::vector::const_iterator block_iterator; - block_iterator block_begin() const { return Blocks.begin(); } - block_iterator block_end() const { return Blocks.end(); } + block_iterator block_begin() const { return getBlocks().begin(); } + block_iterator block_end() const { return getBlocks().end(); } inline iterator_range blocks() const { + assert(!isInvalid() && "Loop not in a valid state!"); return make_range(block_begin(), block_end()); } /// Get the number of blocks in this loop in constant time. - unsigned getNumBlocks() const { return Blocks.size(); } - /// Invalidate the loop, indicating that it is no longer a loop. - void invalidate() { IsInvalid = true; } + unsigned getNumBlocks() const { + assert(!isInvalid() && "Loop not in a valid state!"); + return Blocks.size(); + } /// Return true if this loop is no longer valid. - bool isInvalid() { return IsInvalid; } + bool isInvalid() const { return IsInvalid; } /// True if terminator in the block can branch to another block that is /// outside of the current loop. bool isLoopExiting(const BlockT *BB) const { + assert(!isInvalid() && "Loop not in a valid state!"); for (const auto &Succ : children(BB)) { if (!contains(Succ)) return true; @@ -163,6 +191,7 @@ public: /// This function is useful when there are multiple latches in a loop /// because \fn getLoopLatch will return nullptr in that case. bool isLoopLatch(const BlockT *BB) const { + assert(!isInvalid() && "Loop not in a valid state!"); assert(contains(BB) && "block does not belong to the loop"); BlockT *Header = getHeader(); @@ -173,6 +202,7 @@ public: /// Calculate the number of back edges to the loop header. unsigned getNumBackEdges() const { + assert(!isInvalid() && "Loop not in a valid state!"); unsigned NumBackEdges = 0; BlockT *H = getHeader(); @@ -235,6 +265,7 @@ public: /// Return all loop latch blocks of this loop. A latch block is a block that /// contains a branch back to the header. void getLoopLatches(SmallVectorImpl &LoopLatches) const { + assert(!isInvalid() && "Loop not in a valid state!"); BlockT *H = getHeader(); for (const auto Pred : children>(H)) if (contains(Pred)) @@ -261,6 +292,7 @@ public: /// Add the specified loop to be a child of this loop. /// This updates the loop depth of the new child. void addChildLoop(LoopT *NewChild) { + assert(!isInvalid() && "Loop not in a valid state!"); assert(!NewChild->ParentLoop && "NewChild already has a parent!"); NewChild->ParentLoop = static_cast(this); SubLoops.push_back(NewChild); @@ -269,6 +301,7 @@ public: /// This removes the specified child from being a subloop of this loop. The /// loop is not deleted, as it will presumably be inserted into another loop. LoopT *removeChildLoop(iterator I) { + assert(!isInvalid() && "Loop not in a valid state!"); assert(I != SubLoops.end() && "Cannot remove end iterator!"); LoopT *Child = *I; assert(Child->ParentLoop == this && "Child is not a child of this loop!"); @@ -281,21 +314,27 @@ public: /// This should only be used by transformations that create new loops. Other /// transformations should use addBasicBlockToLoop. void addBlockEntry(BlockT *BB) { + assert(!isInvalid() && "Loop not in a valid state!"); Blocks.push_back(BB); DenseBlockSet.insert(BB); } /// interface to reverse Blocks[from, end of loop] in this loop void reverseBlock(unsigned from) { + assert(!isInvalid() && "Loop not in a valid state!"); std::reverse(Blocks.begin() + from, Blocks.end()); } /// interface to do reserve() for Blocks - void reserveBlocks(unsigned size) { Blocks.reserve(size); } + void reserveBlocks(unsigned size) { + assert(!isInvalid() && "Loop not in a valid state!"); + Blocks.reserve(size); + } /// This method is used to move BB (which must be part of this loop) to be the /// loop header of the loop (the block that dominates all others). void moveToHeader(BlockT *BB) { + assert(!isInvalid() && "Loop not in a valid state!"); if (Blocks[0] == BB) return; for (unsigned i = 0;; ++i) { @@ -312,6 +351,7 @@ public: /// Blocks as appropriate. This does not update the mapping in the LoopInfo /// class. void removeBlockFromLoop(BlockT *BB) { + assert(!isInvalid() && "Loop not in a valid state!"); auto I = find(Blocks, BB); assert(I != Blocks.end() && "N is not in this list!"); Blocks.erase(I); @@ -494,6 +534,8 @@ public: LocRange getLocRange() const; StringRef getName() const { + if (isInvalid()) + return ""; if (BasicBlock *Header = getHeader()) if (Header->hasName()) return Header->getName(); @@ -673,6 +715,9 @@ public: void print(raw_ostream &OS) const; void verify(const DominatorTreeBase &DomTree) const; + +protected: + static void clearLoop(LoopT &L) { L.clear(); } }; // Implementation in LoopInfoImpl.h diff --git a/include/llvm/Analysis/LoopInfoImpl.h b/include/llvm/Analysis/LoopInfoImpl.h index 91298feb6fe..c6eb6d6991f 100644 --- a/include/llvm/Analysis/LoopInfoImpl.h +++ b/include/llvm/Analysis/LoopInfoImpl.h @@ -34,6 +34,7 @@ namespace llvm { template void LoopBase::getExitingBlocks( SmallVectorImpl &ExitingBlocks) const { + assert(!isInvalid() && "Loop not in a valid state!"); for (const auto BB : blocks()) for (const auto &Succ : children(BB)) if (!contains(Succ)) { @@ -47,6 +48,7 @@ void LoopBase::getExitingBlocks( /// return that block. Otherwise return null. template BlockT *LoopBase::getExitingBlock() const { + assert(!isInvalid() && "Loop not in a valid state!"); SmallVector ExitingBlocks; getExitingBlocks(ExitingBlocks); if (ExitingBlocks.size() == 1) @@ -60,6 +62,7 @@ BlockT *LoopBase::getExitingBlock() const { template void LoopBase::getExitBlocks( SmallVectorImpl &ExitBlocks) const { + assert(!isInvalid() && "Loop not in a valid state!"); for (const auto BB : blocks()) for (const auto &Succ : children(BB)) if (!contains(Succ)) @@ -71,6 +74,7 @@ void LoopBase::getExitBlocks( /// return that block. Otherwise return null. template BlockT *LoopBase::getExitBlock() const { + assert(!isInvalid() && "Loop not in a valid state!"); SmallVector ExitBlocks; getExitBlocks(ExitBlocks); if (ExitBlocks.size() == 1) @@ -82,6 +86,7 @@ BlockT *LoopBase::getExitBlock() const { template void LoopBase::getExitEdges( SmallVectorImpl &ExitEdges) const { + assert(!isInvalid() && "Loop not in a valid state!"); for (const auto BB : blocks()) for (const auto &Succ : children(BB)) if (!contains(Succ)) @@ -99,6 +104,7 @@ void LoopBase::getExitEdges( /// template BlockT *LoopBase::getLoopPreheader() const { + assert(!isInvalid() && "Loop not in a valid state!"); // Keep track of nodes outside the loop branching to the header... BlockT *Out = getLoopPredecessor(); if (!Out) @@ -126,6 +132,7 @@ BlockT *LoopBase::getLoopPreheader() const { /// template BlockT *LoopBase::getLoopPredecessor() const { + assert(!isInvalid() && "Loop not in a valid state!"); // Keep track of nodes outside the loop branching to the header... BlockT *Out = nullptr; @@ -148,6 +155,7 @@ BlockT *LoopBase::getLoopPredecessor() const { /// A latch block is a block that contains a branch back to the header. template BlockT *LoopBase::getLoopLatch() const { + assert(!isInvalid() && "Loop not in a valid state!"); BlockT *Header = getHeader(); BlockT *Latch = nullptr; for (const auto Pred : children>(Header)) { @@ -174,6 +182,7 @@ BlockT *LoopBase::getLoopLatch() const { template void LoopBase::addBasicBlockToLoop( BlockT *NewBB, LoopInfoBase &LIB) { + assert(!isInvalid() && "Loop not in a valid state!"); #ifndef NDEBUG if (!Blocks.empty()) { auto SameHeader = LIB[getHeader()]; @@ -203,6 +212,7 @@ void LoopBase::addBasicBlockToLoop( template void LoopBase::replaceChildLoopWith(LoopT *OldChild, LoopT *NewChild) { + assert(!isInvalid() && "Loop not in a valid state!"); assert(OldChild->ParentLoop == this && "This loop is already broken!"); assert(!NewChild->ParentLoop && "NewChild already has a parent!"); typename std::vector::iterator I = find(SubLoops, OldChild); @@ -215,6 +225,7 @@ void LoopBase::replaceChildLoopWith(LoopT *OldChild, /// verifyLoop - Verify loop structure template void LoopBase::verifyLoop() const { + assert(!isInvalid() && "Loop not in a valid state!"); #ifndef NDEBUG assert(!Blocks.empty() && "Loop header is missing"); @@ -301,6 +312,7 @@ void LoopBase::verifyLoop() const { template void LoopBase::verifyLoopNest( DenseSet *Loops) const { + assert(!isInvalid() && "Loop not in a valid state!"); Loops->insert(static_cast(this)); // Verify this loop. verifyLoop(); diff --git a/include/llvm/Transforms/Scalar/LoopPassManager.h b/include/llvm/Transforms/Scalar/LoopPassManager.h index 715b11d3d97..3da006f4d5c 100644 --- a/include/llvm/Transforms/Scalar/LoopPassManager.h +++ b/include/llvm/Transforms/Scalar/LoopPassManager.h @@ -166,7 +166,8 @@ public: /// the rest of the pass management infrastructure. void markLoopAsDeleted(Loop &L) { LAM.clear(L); - assert(CurrentL->contains(&L) && "Cannot delete a loop outside of the " + assert(&L == CurrentL || + CurrentL->contains(&L) && "Cannot delete a loop outside of the " "subloop tree currently being processed."); if (&L == CurrentL) SkipCurrentLoop = true; diff --git a/include/llvm/Transforms/Utils/UnrollLoop.h b/include/llvm/Transforms/Utils/UnrollLoop.h index 8e3e12620e8..1f7af92edc6 100644 --- a/include/llvm/Transforms/Utils/UnrollLoop.h +++ b/include/llvm/Transforms/Utils/UnrollLoop.h @@ -39,13 +39,29 @@ const Loop* addClonedBlockToLoopInfo(BasicBlock *OriginalBB, BasicBlock *ClonedBB, LoopInfo *LI, NewLoopsMap &NewLoops); -bool UnrollLoop(Loop *L, unsigned Count, unsigned TripCount, bool Force, - bool AllowRuntime, bool AllowExpensiveTripCount, - bool PreserveCondBr, bool PreserveOnlyFirst, - unsigned TripMultiple, unsigned PeelCount, bool UnrollRemainder, - LoopInfo *LI, ScalarEvolution *SE, DominatorTree *DT, - AssumptionCache *AC, OptimizationRemarkEmitter *ORE, - bool PreserveLCSSA); +/// Represents the result of a \c UnrollLoop invocation. +enum class LoopUnrollStatus { + /// The loop was not modified. + Unmodified, + + /// The loop was partially unrolled -- we still have a loop, but with a + /// smaller trip count. We may also have emitted epilogue loop if the loop + /// had a non-constant trip count. + PartiallyUnrolled, + + /// The loop was fully unrolled into straight-line code. We no longer have + /// any back-edges. + FullyUnrolled +}; + +LoopUnrollStatus UnrollLoop(Loop *L, unsigned Count, unsigned TripCount, + bool Force, bool AllowRuntime, + bool AllowExpensiveTripCount, bool PreserveCondBr, + bool PreserveOnlyFirst, unsigned TripMultiple, + unsigned PeelCount, bool UnrollRemainder, + LoopInfo *LI, ScalarEvolution *SE, + DominatorTree *DT, AssumptionCache *AC, + OptimizationRemarkEmitter *ORE, bool PreserveLCSSA); bool UnrollRuntimeLoopRemainder(Loop *L, unsigned Count, bool AllowExpensiveTripCount, diff --git a/lib/Analysis/LoopInfo.cpp b/lib/Analysis/LoopInfo.cpp index e82f552c9d0..d279a3e7fd0 100644 --- a/lib/Analysis/LoopInfo.cpp +++ b/lib/Analysis/LoopInfo.cpp @@ -16,6 +16,7 @@ #include "llvm/Analysis/LoopInfo.h" #include "llvm/ADT/DepthFirstIterator.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Analysis/LoopInfoImpl.h" #include "llvm/Analysis/LoopIterator.h" @@ -619,9 +620,11 @@ bool LoopInfo::invalidate(Function &F, const PreservedAnalyses &PA, void LoopInfo::markAsErased(Loop *Unloop) { assert(!Unloop->isInvalid() && "Loop has already been erased!"); - Unloop->invalidate(); RemovedLoops.push_back(Unloop); + auto InvalidateOnExit = + make_scope_exit([&]() { BaseT::clearLoop(*Unloop); }); + // First handle the special case of no parent loop to simplify the algorithm. if (!Unloop->getParentLoop()) { // Since BBLoop had no parent, Unloop blocks are no longer in a loop. diff --git a/lib/Analysis/LoopPass.cpp b/lib/Analysis/LoopPass.cpp index e988f6444a5..6496c6039e4 100644 --- a/lib/Analysis/LoopPass.cpp +++ b/lib/Analysis/LoopPass.cpp @@ -198,9 +198,7 @@ bool LPPassManager::runOnFunction(Function &F) { LoopWasDeleted = CurrentLoop->isInvalid(); if (Changed) - dumpPassInfo(P, MODIFICATION_MSG, ON_LOOP_MSG, - LoopWasDeleted ? "" - : CurrentLoop->getHeader()->getName()); + dumpPassInfo(P, MODIFICATION_MSG, ON_LOOP_MSG, CurrentLoop->getName()); dumpPreservedSet(P); if (LoopWasDeleted) { diff --git a/lib/Transforms/Scalar/LoopUnrollPass.cpp b/lib/Transforms/Scalar/LoopUnrollPass.cpp index 1008f2c606b..40fcf97d657 100644 --- a/lib/Transforms/Scalar/LoopUnrollPass.cpp +++ b/lib/Transforms/Scalar/LoopUnrollPass.cpp @@ -1045,18 +1045,19 @@ static bool tryToUnrollLoop( UP.Count = TripCount; // Unroll the loop. - if (!UnrollLoop(L, UP.Count, TripCount, UP.Force, UP.Runtime, - UP.AllowExpensiveTripCount, UseUpperBound, MaxOrZero, - TripMultiple, UP.PeelCount, UP.UnrollRemainder, - LI, &SE, &DT, &AC, &ORE, - PreserveLCSSA)) + LoopUnrollStatus UnrollStatus = UnrollLoop( + L, UP.Count, TripCount, UP.Force, UP.Runtime, UP.AllowExpensiveTripCount, + UseUpperBound, MaxOrZero, TripMultiple, UP.PeelCount, UP.UnrollRemainder, + LI, &SE, &DT, &AC, &ORE, PreserveLCSSA); + if (UnrollStatus == LoopUnrollStatus::Unmodified) return false; // If loop has an unroll count pragma or unrolled by explicitly set count // mark loop as unrolled to prevent unrolling beyond that requested. // If the loop was peeled, we already "used up" the profile information // we had, so we don't want to unroll or peel again. - if (IsCountSetExplicitly || UP.PeelCount) + if (UnrollStatus != LoopUnrollStatus::FullyUnrolled && + (IsCountSetExplicitly || UP.PeelCount)) SetLoopAlreadyUnrolled(L); return true; diff --git a/lib/Transforms/Utils/LoopUnroll.cpp b/lib/Transforms/Utils/LoopUnroll.cpp index a3643e35ad4..24ffe668682 100644 --- a/lib/Transforms/Utils/LoopUnroll.cpp +++ b/lib/Transforms/Utils/LoopUnroll.cpp @@ -255,8 +255,7 @@ static bool isEpilogProfitable(Loop *L) { return false; } -/// Unroll the given loop by Count. The loop must be in LCSSA form. Returns true -/// if unrolling was successful, or false if the loop was unmodified. Unrolling +/// Unroll the given loop by Count. The loop must be in LCSSA form. Unrolling /// can only fail when the loop's latch block is not terminated by a conditional /// branch instruction. However, if the trip count (and multiple) are not known, /// loop unrolling will mostly produce more code that is no faster. @@ -285,38 +284,36 @@ static bool isEpilogProfitable(Loop *L) { /// runtime-unroll the loop if computing RuntimeTripCount will be expensive and /// AllowExpensiveTripCount is false. /// -/// If we want to perform PGO-based loop peeling, PeelCount is set to the +/// If we want to perform PGO-based loop peeling, PeelCount is set to the /// number of iterations we want to peel off. /// /// The LoopInfo Analysis that is passed will be kept consistent. /// /// This utility preserves LoopInfo. It will also preserve ScalarEvolution and /// DominatorTree if they are non-null. -bool llvm::UnrollLoop(Loop *L, unsigned Count, unsigned TripCount, bool Force, - bool AllowRuntime, bool AllowExpensiveTripCount, - bool PreserveCondBr, bool PreserveOnlyFirst, - unsigned TripMultiple, unsigned PeelCount, - bool UnrollRemainder, LoopInfo *LI, - ScalarEvolution *SE, DominatorTree *DT, - AssumptionCache *AC, OptimizationRemarkEmitter *ORE, - bool PreserveLCSSA) { +LoopUnrollStatus llvm::UnrollLoop( + Loop *L, unsigned Count, unsigned TripCount, bool Force, bool AllowRuntime, + bool AllowExpensiveTripCount, bool PreserveCondBr, bool PreserveOnlyFirst, + unsigned TripMultiple, unsigned PeelCount, bool UnrollRemainder, + LoopInfo *LI, ScalarEvolution *SE, DominatorTree *DT, AssumptionCache *AC, + OptimizationRemarkEmitter *ORE, bool PreserveLCSSA) { BasicBlock *Preheader = L->getLoopPreheader(); if (!Preheader) { DEBUG(dbgs() << " Can't unroll; loop preheader-insertion failed.\n"); - return false; + return LoopUnrollStatus::Unmodified; } BasicBlock *LatchBlock = L->getLoopLatch(); if (!LatchBlock) { DEBUG(dbgs() << " Can't unroll; loop exit-block-insertion failed.\n"); - return false; + return LoopUnrollStatus::Unmodified; } // Loops with indirectbr cannot be cloned. if (!L->isSafeToClone()) { DEBUG(dbgs() << " Can't unroll; Loop body cannot be cloned.\n"); - return false; + return LoopUnrollStatus::Unmodified; } // The current loop unroll pass can only unroll loops with a single latch @@ -330,7 +327,7 @@ bool llvm::UnrollLoop(Loop *L, unsigned Count, unsigned TripCount, bool Force, // The loop-rotate pass can be helpful to avoid this in many cases. DEBUG(dbgs() << " Can't unroll; loop not terminated by a conditional branch.\n"); - return false; + return LoopUnrollStatus::Unmodified; } auto CheckSuccessors = [&](unsigned S1, unsigned S2) { @@ -340,14 +337,14 @@ bool llvm::UnrollLoop(Loop *L, unsigned Count, unsigned TripCount, bool Force, if (!CheckSuccessors(0, 1) && !CheckSuccessors(1, 0)) { DEBUG(dbgs() << "Can't unroll; only loops with one conditional latch" " exiting the loop can be unrolled\n"); - return false; + return LoopUnrollStatus::Unmodified; } if (Header->hasAddressTaken()) { // The loop-rotate pass can be helpful to avoid this in many cases. DEBUG(dbgs() << " Won't unroll loop: address of header block is taken.\n"); - return false; + return LoopUnrollStatus::Unmodified; } if (TripCount != 0) @@ -363,7 +360,7 @@ bool llvm::UnrollLoop(Loop *L, unsigned Count, unsigned TripCount, bool Force, // Don't enter the unroll code if there is nothing to do. if (TripCount == 0 && Count < 2 && PeelCount == 0) { DEBUG(dbgs() << "Won't unroll; almost nothing to do\n"); - return false; + return LoopUnrollStatus::Unmodified; } assert(Count > 0); @@ -439,7 +436,7 @@ bool llvm::UnrollLoop(Loop *L, unsigned Count, unsigned TripCount, bool Force, DEBUG( dbgs() << "Wont unroll; remainder loop could not be generated" "when assuming runtime trip count\n"); - return false; + return LoopUnrollStatus::Unmodified; } } @@ -864,7 +861,8 @@ bool llvm::UnrollLoop(Loop *L, unsigned Count, unsigned TripCount, bool Force, } } - return true; + return CompletelyUnroll ? LoopUnrollStatus::FullyUnrolled + : LoopUnrollStatus::PartiallyUnrolled; } /// Given an llvm.loop loop id metadata node, returns the loop hint metadata diff --git a/test/Transforms/LoopUnroll/unroll-loop-invalidation.ll b/test/Transforms/LoopUnroll/unroll-loop-invalidation.ll index ea79d7164dc..e1e45ae156a 100644 --- a/test/Transforms/LoopUnroll/unroll-loop-invalidation.ll +++ b/test/Transforms/LoopUnroll/unroll-loop-invalidation.ll @@ -22,8 +22,8 @@ ; CHECK: Running analysis: LoopAccessAnalysis on outer.header ; CHECK: Finished Loop pass manager run. ; CHECK: Running pass: LoopUnrollPass -; CHECK: Clearing all analysis results for: inner2.header -; CHECK: Clearing all analysis results for: outer.header +; CHECK: Clearing all analysis results for: +; CHECK: Clearing all analysis results for: ; CHECK: Invalidating all non-preserved analyses for: test ; CHECK: Invalidating all non-preserved analyses for: inner1.header ; CHECK: Invalidating analysis: LoopAccessAnalysis on inner1.header -- 2.11.0