Skip to content

Commit

Permalink
[SimpleLoopUnswitch] Re-fix introduction of UB when hoisted condition…
Browse files Browse the repository at this point in the history
… may be undef or poison

https://bugs.llvm.org/show_bug.cgi?id=27506
https://bugs.llvm.org/show_bug.cgi?id=31652
https://bugs.llvm.org/show_bug.cgi?id=51043

Problems with SimpleLoopUnswitch cause the bug reports above.

```
while (...) {
  if (C) { A }
  else   { B }
}
Into:

C' = freeze(C)
if (C') {
  while (...) { A }
} else {
  while (...) { B }
}
```
This problem can be solved by adding a freeze on hoisted branches(above transform) and has been solved by D29015.
However, D29015 is now reverted by performance regression(2b5a897)

It is not the first time that an added freeze has caused performance regression.
SimplifyCFG also had a problem with UB caused by branching-on-undef, which was solved by adding freeze to the branching condition. (D104569)
Performance regression occurred in D104569, and patches such as D105344 and D105392 were written to minimize it.

This patch will correct the SimpleLoopUnswitch as D104569 handles the SimplyCFG while minimizing performance loss by introducing patches like D105344 and D105392(This patch was rebased with the author's permission)

Reviewed By: reames

Differential Revision: https://reviews.llvm.org/D106041
  • Loading branch information
hyeongyukim committed Oct 11, 2021
1 parent bacb0ca commit 0aeb373
Show file tree
Hide file tree
Showing 2 changed files with 2,359 additions and 7 deletions.
36 changes: 29 additions & 7 deletions llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
Expand Up @@ -28,6 +28,7 @@
#include "llvm/Analysis/MemorySSAUpdater.h"
#include "llvm/Analysis/MustExecute.h"
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
Expand Down Expand Up @@ -109,6 +110,10 @@ static cl::opt<unsigned>
cl::desc("Max number of memory uses to explore during "
"partial unswitching analysis"),
cl::init(100), cl::Hidden);
static cl::opt<bool> FreezeLoopUnswitchCond(
"freeze-loop-unswitch-cond", cl::init(false), cl::Hidden,
cl::desc("If enabled, the freeze instruction will be added to condition "
"of loop unswitch to prevent miscompilation."));

/// Collect all of the loop invariant input values transitively used by the
/// homogeneous instruction graph from a given root.
Expand Down Expand Up @@ -196,15 +201,15 @@ static bool areLoopExitPHIsLoopInvariant(Loop &L, BasicBlock &ExitingBB,
/// Copy a set of loop invariant values \p ToDuplicate and insert them at the
/// end of \p BB and conditionally branch on the copied condition. We only
/// branch on a single value.
static void buildPartialUnswitchConditionalBranch(BasicBlock &BB,
ArrayRef<Value *> Invariants,
bool Direction,
BasicBlock &UnswitchedSucc,
BasicBlock &NormalSucc) {
static void buildPartialUnswitchConditionalBranch(
BasicBlock &BB, ArrayRef<Value *> Invariants, bool Direction,
BasicBlock &UnswitchedSucc, BasicBlock &NormalSucc, bool InsertFreeze) {
IRBuilder<> IRB(&BB);

Value *Cond = Direction ? IRB.CreateOr(Invariants) :
IRB.CreateAnd(Invariants);
if (InsertFreeze)
Cond = IRB.CreateFreeze(Cond, Cond->getName() + ".fr");
IRB.CreateCondBr(Cond, Direction ? &UnswitchedSucc : &NormalSucc,
Direction ? &NormalSucc : &UnswitchedSucc);
}
Expand Down Expand Up @@ -565,7 +570,7 @@ static bool unswitchTrivialBranch(Loop &L, BranchInst &BI, DominatorTree &DT,
"Must have an `and` of `i1`s or `select i1 X, Y, false`s for the"
" condition!");
buildPartialUnswitchConditionalBranch(*OldPH, Invariants, ExitDirection,
*UnswitchedBB, *NewPH);
*UnswitchedBB, *NewPH, false);
}

// Update the dominator tree with the added edge.
Expand Down Expand Up @@ -2124,6 +2129,13 @@ static void unswitchNontrivialInvariants(
SE->forgetTopmostLoop(&L);
}

bool InsertFreeze = false;
if (FreezeLoopUnswitchCond) {
ICFLoopSafetyInfo SafetyInfo;
SafetyInfo.computeLoopSafetyInfo(&L);
InsertFreeze = !SafetyInfo.isGuaranteedToExecute(TI, &DT, &L);
}

// If the edge from this terminator to a successor dominates that successor,
// store a map from each block in its dominator subtree to it. This lets us
// tell when cloning for a particular successor if a block is dominated by
Expand Down Expand Up @@ -2198,6 +2210,11 @@ static void unswitchNontrivialInvariants(
BasicBlock *ClonedPH = ClonedPHs.begin()->second;
BI->setSuccessor(ClonedSucc, ClonedPH);
BI->setSuccessor(1 - ClonedSucc, LoopPH);
if (InsertFreeze) {
auto Cond = BI->getCondition();
if (!isGuaranteedNotToBeUndefOrPoison(Cond, &AC, BI, &DT))
BI->setCondition(new FreezeInst(Cond, Cond->getName() + ".fr", BI));
}
DTUpdates.push_back({DominatorTree::Insert, SplitBB, ClonedPH});
} else {
assert(SI && "Must either be a branch or switch!");
Expand All @@ -2212,6 +2229,11 @@ static void unswitchNontrivialInvariants(
else
Case.setSuccessor(ClonedPHs.find(Case.getCaseSuccessor())->second);

if (InsertFreeze) {
auto Cond = SI->getCondition();
if (!isGuaranteedNotToBeUndefOrPoison(Cond, &AC, SI, &DT))
SI->setCondition(new FreezeInst(Cond, Cond->getName() + ".fr", SI));
}
// We need to use the set to populate domtree updates as even when there
// are multiple cases pointing at the same successor we only want to
// remove and insert one edge in the domtree.
Expand Down Expand Up @@ -2292,7 +2314,7 @@ static void unswitchNontrivialInvariants(
*SplitBB, Invariants, Direction, *ClonedPH, *LoopPH, L, MSSAU);
else
buildPartialUnswitchConditionalBranch(*SplitBB, Invariants, Direction,
*ClonedPH, *LoopPH);
*ClonedPH, *LoopPH, InsertFreeze);
DTUpdates.push_back({DominatorTree::Insert, SplitBB, ClonedPH});

if (MSSAU) {
Expand Down

0 comments on commit 0aeb373

Please sign in to comment.