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

llvm-svn: 313708
This commit is contained in:
Sanjoy Das 2017-09-20 02:31:57 +00:00
parent b207d12456
commit 09613b122e
9 changed files with 132 additions and 58 deletions

View File

@ -81,6 +81,14 @@ template <class BlockT, class LoopT> class LoopBase {
const LoopBase<BlockT, LoopT> &
operator=(const LoopBase<BlockT, LoopT> &) = 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 <class InstT> bool contains(const InstT *Inst) const {
@ -119,38 +135,50 @@ public:
}
/// Return the loops contained entirely within this loop.
const std::vector<LoopT *> &getSubLoops() const { return SubLoops; }
std::vector<LoopT *> &getSubLoopsVector() { return SubLoops; }
const std::vector<LoopT *> &getSubLoops() const {
assert(!isInvalid() && "Loop not in a valid state!");
return SubLoops;
}
std::vector<LoopT *> &getSubLoopsVector() {
assert(!isInvalid() && "Loop not in a valid state!");
return SubLoops;
}
typedef typename std::vector<LoopT *>::const_iterator iterator;
typedef
typename std::vector<LoopT *>::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<BlockT *> &getBlocks() const { return Blocks; }
const std::vector<BlockT *> &getBlocks() const {
assert(!isInvalid() && "Loop not in a valid state!");
return Blocks;
}
typedef typename std::vector<BlockT *>::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<block_iterator> 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<const BlockT *>(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<BlockT *> &LoopLatches) const {
assert(!isInvalid() && "Loop not in a valid state!");
BlockT *H = getHeader();
for (const auto Pred : children<Inverse<BlockT *>>(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<LoopT *>(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 "<invalidated loop>";
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<BlockT, false> &DomTree) const;
protected:
static void clearLoop(LoopT &L) { L.clear(); }
};
// Implementation in LoopInfoImpl.h

View File

@ -34,6 +34,7 @@ namespace llvm {
template <class BlockT, class LoopT>
void LoopBase<BlockT, LoopT>::getExitingBlocks(
SmallVectorImpl<BlockT *> &ExitingBlocks) const {
assert(!isInvalid() && "Loop not in a valid state!");
for (const auto BB : blocks())
for (const auto &Succ : children<BlockT *>(BB))
if (!contains(Succ)) {
@ -47,6 +48,7 @@ void LoopBase<BlockT, LoopT>::getExitingBlocks(
/// return that block. Otherwise return null.
template <class BlockT, class LoopT>
BlockT *LoopBase<BlockT, LoopT>::getExitingBlock() const {
assert(!isInvalid() && "Loop not in a valid state!");
SmallVector<BlockT *, 8> ExitingBlocks;
getExitingBlocks(ExitingBlocks);
if (ExitingBlocks.size() == 1)
@ -60,6 +62,7 @@ BlockT *LoopBase<BlockT, LoopT>::getExitingBlock() const {
template <class BlockT, class LoopT>
void LoopBase<BlockT, LoopT>::getExitBlocks(
SmallVectorImpl<BlockT *> &ExitBlocks) const {
assert(!isInvalid() && "Loop not in a valid state!");
for (const auto BB : blocks())
for (const auto &Succ : children<BlockT *>(BB))
if (!contains(Succ))
@ -71,6 +74,7 @@ void LoopBase<BlockT, LoopT>::getExitBlocks(
/// return that block. Otherwise return null.
template <class BlockT, class LoopT>
BlockT *LoopBase<BlockT, LoopT>::getExitBlock() const {
assert(!isInvalid() && "Loop not in a valid state!");
SmallVector<BlockT *, 8> ExitBlocks;
getExitBlocks(ExitBlocks);
if (ExitBlocks.size() == 1)
@ -82,6 +86,7 @@ BlockT *LoopBase<BlockT, LoopT>::getExitBlock() const {
template <class BlockT, class LoopT>
void LoopBase<BlockT, LoopT>::getExitEdges(
SmallVectorImpl<Edge> &ExitEdges) const {
assert(!isInvalid() && "Loop not in a valid state!");
for (const auto BB : blocks())
for (const auto &Succ : children<BlockT *>(BB))
if (!contains(Succ))
@ -99,6 +104,7 @@ void LoopBase<BlockT, LoopT>::getExitEdges(
///
template <class BlockT, class LoopT>
BlockT *LoopBase<BlockT, LoopT>::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<BlockT, LoopT>::getLoopPreheader() const {
///
template <class BlockT, class LoopT>
BlockT *LoopBase<BlockT, LoopT>::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<BlockT, LoopT>::getLoopPredecessor() const {
/// A latch block is a block that contains a branch back to the header.
template <class BlockT, class LoopT>
BlockT *LoopBase<BlockT, LoopT>::getLoopLatch() const {
assert(!isInvalid() && "Loop not in a valid state!");
BlockT *Header = getHeader();
BlockT *Latch = nullptr;
for (const auto Pred : children<Inverse<BlockT *>>(Header)) {
@ -174,6 +182,7 @@ BlockT *LoopBase<BlockT, LoopT>::getLoopLatch() const {
template <class BlockT, class LoopT>
void LoopBase<BlockT, LoopT>::addBasicBlockToLoop(
BlockT *NewBB, LoopInfoBase<BlockT, LoopT> &LIB) {
assert(!isInvalid() && "Loop not in a valid state!");
#ifndef NDEBUG
if (!Blocks.empty()) {
auto SameHeader = LIB[getHeader()];
@ -203,6 +212,7 @@ void LoopBase<BlockT, LoopT>::addBasicBlockToLoop(
template <class BlockT, class LoopT>
void LoopBase<BlockT, LoopT>::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<LoopT *>::iterator I = find(SubLoops, OldChild);
@ -215,6 +225,7 @@ void LoopBase<BlockT, LoopT>::replaceChildLoopWith(LoopT *OldChild,
/// verifyLoop - Verify loop structure
template <class BlockT, class LoopT>
void LoopBase<BlockT, LoopT>::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<BlockT, LoopT>::verifyLoop() const {
template <class BlockT, class LoopT>
void LoopBase<BlockT, LoopT>::verifyLoopNest(
DenseSet<const LoopT *> *Loops) const {
assert(!isInvalid() && "Loop not in a valid state!");
Loops->insert(static_cast<const LoopT *>(this));
// Verify this loop.
verifyLoop();

View File

@ -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;

View File

@ -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,

View File

@ -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.

View File

@ -198,9 +198,7 @@ bool LPPassManager::runOnFunction(Function &F) {
LoopWasDeleted = CurrentLoop->isInvalid();
if (Changed)
dumpPassInfo(P, MODIFICATION_MSG, ON_LOOP_MSG,
LoopWasDeleted ? "<deleted>"
: CurrentLoop->getHeader()->getName());
dumpPassInfo(P, MODIFICATION_MSG, ON_LOOP_MSG, CurrentLoop->getName());
dumpPreservedSet(P);
if (LoopWasDeleted) {

View File

@ -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;

View File

@ -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

View File

@ -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: <invalidated loop>
; CHECK: Clearing all analysis results for: <invalidated loop>
; CHECK: Invalidating all non-preserved analyses for: test
; CHECK: Invalidating all non-preserved analyses for: inner1.header
; CHECK: Invalidating analysis: LoopAccessAnalysis on inner1.header