aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
diff options
context:
space:
mode:
authorChandler Carruth <chandlerc@gmail.com>2018-07-07 01:12:56 +0000
committerChandler Carruth <chandlerc@gmail.com>2018-07-07 01:12:56 +0000
commitd8b0c8ce1b30035221a8623fea5b4a0735fcac5c (patch)
treeb10a6ac5c3e755984029f85c70f6b72af1f4f8f4 /llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
parent2c27e33a580f4802b564b6a2b9628f6b4b5e21dc (diff)
downloadllvm-d8b0c8ce1b30035221a8623fea5b4a0735fcac5c.zip
llvm-d8b0c8ce1b30035221a8623fea5b4a0735fcac5c.tar.gz
llvm-d8b0c8ce1b30035221a8623fea5b4a0735fcac5c.tar.bz2
[PM/LoopUnswitch] Fix PR37889, producing the correct loop nest structure
after trivial unswitching. This PR illustrates that a fundamental analysis update was not performed with the new loop unswitch. This update is also somewhat fundamental to the core idea of the new loop unswitch -- we actually *update* the CFG based on the unswitching. In order to do that, we need to update the loop nest in addition to the domtree. For some reason, when writing trivial unswitching, I thought that the loop nest structure cannot be changed by the transformation. But the PR helps illustrate that it clearly can. I've expanded this to a number of different test cases that try to cover the different cases of this. When we unswitch, we move an exit edge of a loop out of the loop. If this exit edge changes which loop reached by an exit is the innermost loop, it changes the parent of the loop. Essentially, this transformation may hoist the inner loop up the nest. I've added the simple logic to handle this reliably in the trivial unswitching case. This just requires updating LoopInfo and rebuilding LCSSA on the impacted loops. In the trivial case, we don't even need to handle dedicated exits because we're only hoisting the one loop and we just split its preheader. I've also ported all of these tests to non-trivial unswitching and verified that the logic already there correctly handles the loop nest updates necessary. Differential Revision: https://reviews.llvm.org/D48851 llvm-svn: 336477
Diffstat (limited to 'llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp')
-rw-r--r--llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp83
1 files changed, 81 insertions, 2 deletions
diff --git a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
index 8c9bd7e..9c9ed2a 100644
--- a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
+++ b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
@@ -1,4 +1,4 @@
-//===- SimpleLoopUnswitch.cpp - Hoist loop-invariant control flow ---------===//
+///===- SimpleLoopUnswitch.cpp - Hoist loop-invariant control flow ---------===//
//
// The LLVM Compiler Infrastructure
//
@@ -239,6 +239,76 @@ static void rewritePHINodesForExitAndUnswitchedBlocks(BasicBlock &ExitBB,
}
}
+/// Hoist the current loop up to the innermost loop containing a remaining exit.
+///
+/// Because we've removed an exit from the loop, we may have changed the set of
+/// loops reachable and need to move the current loop up the loop nest or even
+/// to an entirely separate nest.
+static void hoistLoopToNewParent(Loop &L, BasicBlock &Preheader,
+ DominatorTree &DT, LoopInfo &LI) {
+ // If the loop is already at the top level, we can't hoist it anywhere.
+ Loop *OldParentL = L.getParentLoop();
+ if (!OldParentL)
+ return;
+
+ SmallVector<BasicBlock *, 4> Exits;
+ L.getExitBlocks(Exits);
+ Loop *NewParentL = nullptr;
+ for (auto *ExitBB : Exits)
+ if (Loop *ExitL = LI.getLoopFor(ExitBB))
+ if (!NewParentL || NewParentL->contains(ExitL))
+ NewParentL = ExitL;
+
+ if (NewParentL == OldParentL)
+ return;
+
+ // The new parent loop (if different) should always contain the old one.
+ if (NewParentL)
+ assert(NewParentL->contains(OldParentL) &&
+ "Can only hoist this loop up the nest!");
+
+ // The preheader will need to move with the body of this loop. However,
+ // because it isn't in this loop we also need to update the primary loop map.
+ assert(OldParentL == LI.getLoopFor(&Preheader) &&
+ "Parent loop of this loop should contain this loop's preheader!");
+ LI.changeLoopFor(&Preheader, NewParentL);
+
+ // Remove this loop from its old parent.
+ OldParentL->removeChildLoop(&L);
+
+ // Add the loop either to the new parent or as a top-level loop.
+ if (NewParentL)
+ NewParentL->addChildLoop(&L);
+ else
+ LI.addTopLevelLoop(&L);
+
+ // Remove this loops blocks from the old parent and every other loop up the
+ // nest until reaching the new parent. Also update all of these
+ // no-longer-containing loops to reflect the nesting change.
+ for (Loop *OldContainingL = OldParentL; OldContainingL != NewParentL;
+ OldContainingL = OldContainingL->getParentLoop()) {
+ llvm::erase_if(OldContainingL->getBlocksVector(),
+ [&](const BasicBlock *BB) {
+ return BB == &Preheader || L.contains(BB);
+ });
+
+ OldContainingL->getBlocksSet().erase(&Preheader);
+ for (BasicBlock *BB : L.blocks())
+ OldContainingL->getBlocksSet().erase(BB);
+
+ // Because we just hoisted a loop out of this one, we have essentially
+ // created new exit paths from it. That means we need to form LCSSA PHI
+ // nodes for values used in the no-longer-nested loop.
+ formLCSSA(*OldContainingL, DT, &LI, nullptr);
+
+ // We shouldn't need to form dedicated exits because the exit introduced
+ // here is the (just split by unswitching) preheader. As such, it is
+ // necessarily dedicated.
+ assert(OldContainingL->hasDedicatedExits() &&
+ "Unexpected predecessor of hoisted loop preheader!");
+ }
+}
+
/// Unswitch a trivial branch if the condition is loop invariant.
///
/// This routine should only be called when loop code leading to the branch has
@@ -405,6 +475,11 @@ static bool unswitchTrivialBranch(Loop &L, BranchInst &BI, DominatorTree &DT,
for (Value *Invariant : Invariants)
replaceLoopInvariantUses(L, Invariant, *Replacement);
+ // If this was full unswitching, we may have changed the nesting relationship
+ // for this loop so hoist it to its correct parent if needed.
+ if (FullUnswitch)
+ hoistLoopToNewParent(L, *NewPH, DT, LI);
+
++NumTrivial;
++NumBranches;
return true;
@@ -632,8 +707,12 @@ static bool unswitchTrivialSwitch(Loop &L, SwitchInst &SI, DominatorTree &DT,
DTUpdates.push_back({DT.Insert, OldPH, UnswitchedBB});
}
DT.applyUpdates(DTUpdates);
-
assert(DT.verify(DominatorTree::VerificationLevel::Fast));
+
+ // We may have changed the nesting relationship for this loop so hoist it to
+ // its correct parent if needed.
+ hoistLoopToNewParent(L, *NewPH, DT, LI);
+
++NumTrivial;
++NumSwitches;
return true;