[llvm] 78dc649 - [CodeMoverUtils] Improve IsControlFlowEquivalent.
Whitney Tsang via llvm-commits
llvm-commits at lists.llvm.org
Tue Jan 28 06:18:18 PST 2020
Author: Whitney Tsang
Date: 2020-01-28T14:18:00Z
New Revision: 78dc64989c2f5c075ca74af9dac0c1cb4a2b1f4b
URL: https://github.com/llvm/llvm-project/commit/78dc64989c2f5c075ca74af9dac0c1cb4a2b1f4b
DIFF: https://github.com/llvm/llvm-project/commit/78dc64989c2f5c075ca74af9dac0c1cb4a2b1f4b.diff
LOG: [CodeMoverUtils] Improve IsControlFlowEquivalent.
Summary:
Currently IsControlFlowEquivalent determine if two blocks are control
flow equivalent by checking if A dominates B and B post dominates A.
There exists blocks that are control flow equivalent even if they don't
satisfy the A dominates B and B post dominates A condition.
For example,
if (cond)
A
if (cond)
B
In the PR, we determine if two blocks are control flow equivalent by
also checking if the two sets of conditions A and B depends on are
equivalent.
Reviewer: jdoerfert, Meinersbur, dmgreen, etiotto, bmahjour, fhahn,
hfinkel, kbarton
Reviewed By: fhahn
Subscribers: hiraditya, llvm-commits
Tag: LLVM
Differential Revision: https://reviews.llvm.org/D71578
Added:
Modified:
llvm/include/llvm/Transforms/Utils/CodeMoverUtils.h
llvm/lib/Transforms/Scalar/LoopFuse.cpp
llvm/lib/Transforms/Utils/CodeMoverUtils.cpp
llvm/unittests/Transforms/Utils/CodeMoverUtilsTest.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/Transforms/Utils/CodeMoverUtils.h b/llvm/include/llvm/Transforms/Utils/CodeMoverUtils.h
index 32eb7cc2ab04..d1ee37708458 100644
--- a/llvm/include/llvm/Transforms/Utils/CodeMoverUtils.h
+++ b/llvm/include/llvm/Transforms/Utils/CodeMoverUtils.h
@@ -23,33 +23,30 @@ class Instruction;
class PostDominatorTree;
/// Return true if \p I0 and \p I1 are control flow equivalent.
-/// Two instructions are control flow equivalent if when one executes,
-/// the other is guaranteed to execute. This is determined using dominators
-/// and post-dominators: if A dominates B and B post-dominates A then A and B
-/// are control-flow equivalent.
+/// Two instructions are control flow equivalent if their basic blocks are
+/// control flow equivalent.
bool isControlFlowEquivalent(const Instruction &I0, const Instruction &I1,
const DominatorTree &DT,
const PostDominatorTree &PDT);
/// Return true if \p BB0 and \p BB1 are control flow equivalent.
/// Two basic blocks are control flow equivalent if when one executes, the other
-/// is guaranteed to execute. This is determined using dominators and
-/// post-dominators: if A dominates B and B post-dominates A then A and B are
-/// control-flow equivalent.
+/// is guaranteed to execute.
bool isControlFlowEquivalent(const BasicBlock &BB0, const BasicBlock &BB1,
const DominatorTree &DT,
const PostDominatorTree &PDT);
/// Return true if \p I can be safely moved before \p InsertPoint.
bool isSafeToMoveBefore(Instruction &I, Instruction &InsertPoint,
- const DominatorTree &DT, const PostDominatorTree &PDT,
+ DominatorTree &DT, const PostDominatorTree &PDT,
DependenceInfo &DI);
-/// Move instructions from \p FromBB bottom up to the beginning of \p ToBB
-/// when proven safe.
-void moveInstsBottomUp(BasicBlock &FromBB, BasicBlock &ToBB,
- const DominatorTree &DT, const PostDominatorTree &PDT,
- DependenceInfo &DI);
+/// Move instructions, in an order-preserving manner, from \p FromBB to the
+/// beginning of \p ToBB when proven safe.
+void moveInstructionsToTheBeginning(BasicBlock &FromBB, BasicBlock &ToBB,
+ DominatorTree &DT,
+ const PostDominatorTree &PDT,
+ DependenceInfo &DI);
} // end namespace llvm
diff --git a/llvm/lib/Transforms/Scalar/LoopFuse.cpp b/llvm/lib/Transforms/Scalar/LoopFuse.cpp
index e1738f08eb23..430d4bc23de9 100644
--- a/llvm/lib/Transforms/Scalar/LoopFuse.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopFuse.cpp
@@ -1123,7 +1123,7 @@ struct LoopFuser {
/// Move instructions from FC0.Latch to FC1.Latch. If FC0.Latch has an unique
/// successor, then merge FC0.Latch with its unique successor.
void mergeLatch(const FusionCandidate &FC0, const FusionCandidate &FC1) {
- moveInstsBottomUp(*FC0.Latch, *FC1.Latch, DT, PDT, DI);
+ moveInstructionsToTheBeginning(*FC0.Latch, *FC1.Latch, DT, PDT, DI);
if (BasicBlock *Succ = FC0.Latch->getUniqueSuccessor()) {
MergeBlockIntoPredecessor(Succ, &DTU, &LI);
DTU.flush();
diff --git a/llvm/lib/Transforms/Utils/CodeMoverUtils.cpp b/llvm/lib/Transforms/Utils/CodeMoverUtils.cpp
index 93395ac761ab..19973165d25f 100644
--- a/llvm/lib/Transforms/Utils/CodeMoverUtils.cpp
+++ b/llvm/lib/Transforms/Utils/CodeMoverUtils.cpp
@@ -12,8 +12,10 @@
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/Utils/CodeMoverUtils.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/DependenceAnalysis.h"
+#include "llvm/Analysis/OrderedInstructions.h"
#include "llvm/Analysis/PostDominators.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/Dominators.h"
@@ -30,6 +32,189 @@ STATISTIC(NotControlFlowEquivalent,
STATISTIC(NotMovedPHINode, "Movement of PHINodes are not supported");
STATISTIC(NotMovedTerminator, "Movement of Terminator are not supported");
+namespace {
+/// Represent a control condition. A control condition is a condition of a
+/// terminator to decide which successors to execute. The pointer field
+/// represents the address of the condition of the terminator. The integer field
+/// is a bool, it is true when the basic block is executed when V is true. For
+/// example, `br %cond, bb0, bb1` %cond is a control condition of bb0 with the
+/// integer field equals to true, while %cond is a control condition of bb1 with
+/// the integer field equals to false.
+using ControlCondition = PointerIntPair<Value *, 1, bool>;
+#ifndef NDEBUG
+raw_ostream &operator<<(raw_ostream &OS, const ControlCondition &C) {
+ OS << "[" << *C.getPointer() << ", " << (C.getInt() ? "true" : "false")
+ << "]";
+ return OS;
+}
+#endif
+
+/// Represent a set of control conditions required to execute ToBB from FromBB.
+class ControlConditions {
+ using ConditionVectorTy = SmallVector<ControlCondition, 6>;
+
+ /// A SmallVector of control conditions.
+ ConditionVectorTy Conditions;
+
+public:
+ /// Return a ControlConditions which stores all conditions required to execute
+ /// \p BB from \p Dominator. If \p MaxLookup is non-zero, it limits the
+ /// number of conditions to collect. Return None if not all conditions are
+ /// collected successfully, or we hit the limit.
+ static Optional<const ControlConditions>
+ collectControlConditions(const BasicBlock &BB, const BasicBlock &Dominator,
+ const DominatorTree &DT,
+ const PostDominatorTree &PDT,
+ unsigned MaxLookup = 6);
+
+ /// Return true if there exists no control conditions required to execute ToBB
+ /// from FromBB.
+ bool isUnconditional() const { return Conditions.empty(); }
+
+ /// Return a constant reference of Conditions.
+ const ConditionVectorTy &getControlConditions() const { return Conditions; }
+
+ /// Add \p V as one of the ControlCondition in Condition with IsTrueCondition
+ /// equals to \p True. Return true if inserted successfully.
+ bool addControlCondition(ControlCondition C);
+
+ /// Return true if for all control conditions in Conditions, there exists an
+ /// equivalent control condition in \p Other.Conditions.
+ bool isEquivalent(const ControlConditions &Other) const;
+
+ /// Return true if \p C1 and \p C2 are equivalent.
+ static bool isEquivalent(const ControlCondition &C1,
+ const ControlCondition &C2);
+
+private:
+ ControlConditions() = default;
+
+ static bool isEquivalent(const Value &V1, const Value &V2);
+ static bool isInverse(const Value &V1, const Value &V2);
+};
+} // namespace
+
+Optional<const ControlConditions> ControlConditions::collectControlConditions(
+ const BasicBlock &BB, const BasicBlock &Dominator, const DominatorTree &DT,
+ const PostDominatorTree &PDT, unsigned MaxLookup) {
+ assert(DT.dominates(&Dominator, &BB) && "Expecting Dominator to dominate BB");
+
+ ControlConditions Conditions;
+ unsigned NumConditions = 0;
+
+ // BB is executed unconditional from itself.
+ if (&Dominator == &BB)
+ return Conditions;
+
+ const BasicBlock *CurBlock = &BB;
+ // Walk up the dominator tree from the associated DT node for BB to the
+ // associated DT node for Dominator.
+ do {
+ assert(DT.getNode(CurBlock) && "Expecting a valid DT node for CurBlock");
+ BasicBlock *IDom = DT.getNode(CurBlock)->getIDom()->getBlock();
+ assert(DT.dominates(&Dominator, IDom) &&
+ "Expecting Dominator to dominate IDom");
+
+ // Limitation: can only handle branch instruction currently.
+ const BranchInst *BI = dyn_cast<BranchInst>(IDom->getTerminator());
+ if (!BI)
+ return None;
+
+ bool Inserted = false;
+ if (PDT.dominates(CurBlock, IDom)) {
+ LLVM_DEBUG(dbgs() << CurBlock->getName()
+ << " is executed unconditionally from "
+ << IDom->getName() << "\n");
+ } else if (PDT.dominates(CurBlock, BI->getSuccessor(0))) {
+ LLVM_DEBUG(dbgs() << CurBlock->getName() << " is executed when \""
+ << *BI->getCondition() << "\" is true from "
+ << IDom->getName() << "\n");
+ Inserted = Conditions.addControlCondition(
+ ControlCondition(BI->getCondition(), true));
+ } else if (PDT.dominates(CurBlock, BI->getSuccessor(1))) {
+ LLVM_DEBUG(dbgs() << CurBlock->getName() << " is executed when \""
+ << *BI->getCondition() << "\" is false from "
+ << IDom->getName() << "\n");
+ Inserted = Conditions.addControlCondition(
+ ControlCondition(BI->getCondition(), false));
+ } else
+ return None;
+
+ if (Inserted)
+ ++NumConditions;
+
+ if (MaxLookup != 0 && NumConditions > MaxLookup)
+ return None;
+
+ CurBlock = IDom;
+ } while (CurBlock != &Dominator);
+
+ return Conditions;
+}
+
+bool ControlConditions::addControlCondition(ControlCondition C) {
+ bool Inserted = false;
+ if (none_of(Conditions, [&C](ControlCondition &Exists) {
+ return ControlConditions::isEquivalent(C, Exists);
+ })) {
+ Conditions.push_back(C);
+ Inserted = true;
+ }
+
+ LLVM_DEBUG(dbgs() << (Inserted ? "Inserted " : "Not inserted ") << C << "\n");
+ return Inserted;
+}
+
+bool ControlConditions::isEquivalent(const ControlConditions &Other) const {
+ if (Conditions.empty() && Other.Conditions.empty())
+ return true;
+
+ if (Conditions.size() != Other.Conditions.size())
+ return false;
+
+ return all_of(Conditions, [&Other](const ControlCondition &C) {
+ return any_of(Other.Conditions, [&C](const ControlCondition &OtherC) {
+ return ControlConditions::isEquivalent(C, OtherC);
+ });
+ });
+}
+
+bool ControlConditions::isEquivalent(const ControlCondition &C1,
+ const ControlCondition &C2) {
+ if (C1.getInt() == C2.getInt()) {
+ if (isEquivalent(*C1.getPointer(), *C2.getPointer()))
+ return true;
+ } else if (isInverse(*C1.getPointer(), *C2.getPointer()))
+ return true;
+
+ return false;
+}
+
+// FIXME: Use SCEV and reuse GVN/CSE logic to check for equivalence between
+// Values.
+// Currently, isEquivalent rely on other passes to ensure equivalent conditions
+// have the same value, e.g. GVN.
+bool ControlConditions::isEquivalent(const Value &V1, const Value &V2) {
+ return &V1 == &V2;
+}
+
+bool ControlConditions::isInverse(const Value &V1, const Value &V2) {
+ if (const CmpInst *Cmp1 = dyn_cast<CmpInst>(&V1))
+ if (const CmpInst *Cmp2 = dyn_cast<CmpInst>(&V2)) {
+ if (Cmp1->getPredicate() == Cmp2->getInversePredicate() &&
+ Cmp1->getOperand(0) == Cmp2->getOperand(0) &&
+ Cmp1->getOperand(1) == Cmp2->getOperand(1))
+ return true;
+
+ if (Cmp1->getPredicate() ==
+ CmpInst::getSwappedPredicate(Cmp2->getInversePredicate()) &&
+ Cmp1->getOperand(0) == Cmp2->getOperand(1) &&
+ Cmp1->getOperand(1) == Cmp2->getOperand(0))
+ return true;
+ }
+ return false;
+}
+
bool llvm::isControlFlowEquivalent(const Instruction &I0, const Instruction &I1,
const DominatorTree &DT,
const PostDominatorTree &PDT) {
@@ -42,8 +227,30 @@ bool llvm::isControlFlowEquivalent(const BasicBlock &BB0, const BasicBlock &BB1,
if (&BB0 == &BB1)
return true;
- return ((DT.dominates(&BB0, &BB1) && PDT.dominates(&BB1, &BB0)) ||
- (PDT.dominates(&BB0, &BB1) && DT.dominates(&BB1, &BB0)));
+ if ((DT.dominates(&BB0, &BB1) && PDT.dominates(&BB1, &BB0)) ||
+ (PDT.dominates(&BB0, &BB1) && DT.dominates(&BB1, &BB0)))
+ return true;
+
+ // If the set of conditions required to execute BB0 and BB1 from their common
+ // dominator are the same, then BB0 and BB1 are control flow equivalent.
+ const BasicBlock *CommonDominator = DT.findNearestCommonDominator(&BB0, &BB1);
+ LLVM_DEBUG(dbgs() << "The nearest common dominator of " << BB0.getName()
+ << " and " << BB1.getName() << " is "
+ << CommonDominator->getName() << "\n");
+
+ Optional<const ControlConditions> BB0Conditions =
+ ControlConditions::collectControlConditions(BB0, *CommonDominator, DT,
+ PDT);
+ if (BB0Conditions == None)
+ return false;
+
+ Optional<const ControlConditions> BB1Conditions =
+ ControlConditions::collectControlConditions(BB1, *CommonDominator, DT,
+ PDT);
+ if (BB1Conditions == None)
+ return false;
+
+ return BB0Conditions->isEquivalent(*BB1Conditions);
}
static bool reportInvalidCandidate(const Instruction &I,
@@ -90,8 +297,7 @@ collectInstructionsInBetween(Instruction &StartInst, const Instruction &EndInst,
}
bool llvm::isSafeToMoveBefore(Instruction &I, Instruction &InsertPoint,
- const DominatorTree &DT,
- const PostDominatorTree &PDT,
+ DominatorTree &DT, const PostDominatorTree &PDT,
DependenceInfo &DI) {
// Cannot move itself before itself.
if (&I == &InsertPoint)
@@ -111,9 +317,9 @@ bool llvm::isSafeToMoveBefore(Instruction &I, Instruction &InsertPoint,
if (!isControlFlowEquivalent(I, InsertPoint, DT, PDT))
return reportInvalidCandidate(I, NotControlFlowEquivalent);
- // As I and InsertPoint are control flow equivalent, if I dominates
- // InsertPoint, then I comes before InsertPoint.
- const bool MoveForward = DT.dominates(&I, &InsertPoint);
+ OrderedInstructions OI(&DT);
+ DT.updateDFSNumbers();
+ const bool MoveForward = OI.dfsBefore(&I, &InsertPoint);
if (MoveForward) {
// When I is being moved forward, we need to make sure the InsertPoint
// dominates every users. Or else, a user may be using an undefined I.
@@ -126,7 +332,7 @@ bool llvm::isSafeToMoveBefore(Instruction &I, Instruction &InsertPoint,
// dominates the InsertPoint. Or else, an operand may be undefined for I.
for (const Value *Op : I.operands())
if (auto *OpInst = dyn_cast<Instruction>(Op))
- if (&InsertPoint == OpInst || !DT.dominates(OpInst, &InsertPoint))
+ if (&InsertPoint == OpInst || !OI.dominates(OpInst, &InsertPoint))
return false;
}
@@ -174,9 +380,10 @@ bool llvm::isSafeToMoveBefore(Instruction &I, Instruction &InsertPoint,
return true;
}
-void llvm::moveInstsBottomUp(BasicBlock &FromBB, BasicBlock &ToBB,
- const DominatorTree &DT,
- const PostDominatorTree &PDT, DependenceInfo &DI) {
+void llvm::moveInstructionsToTheBeginning(BasicBlock &FromBB, BasicBlock &ToBB,
+ DominatorTree &DT,
+ const PostDominatorTree &PDT,
+ DependenceInfo &DI) {
for (auto It = ++FromBB.rbegin(); It != FromBB.rend();) {
Instruction *MovePos = ToBB.getFirstNonPHIOrDbg();
Instruction &I = *It;
diff --git a/llvm/unittests/Transforms/Utils/CodeMoverUtilsTest.cpp b/llvm/unittests/Transforms/Utils/CodeMoverUtilsTest.cpp
index 887c9c955821..cf764bf76f06 100644
--- a/llvm/unittests/Transforms/Utils/CodeMoverUtilsTest.cpp
+++ b/llvm/unittests/Transforms/Utils/CodeMoverUtilsTest.cpp
@@ -45,7 +45,389 @@ static void run(Module &M, StringRef FuncName,
Test(*F, DT, PDT, DI);
}
-TEST(CodeMoverUtils, BasicTest) {
+static BasicBlock *getBasicBlockByName(Function &F, StringRef Name) {
+ for (BasicBlock &BB : F)
+ if (BB.getName() == Name)
+ return &BB;
+ llvm_unreachable("Expected to find basic block!");
+}
+
+static Instruction *getInstructionByName(Function &F, StringRef Name) {
+ for (BasicBlock &BB : F)
+ for (Instruction &I : BB)
+ if (I.getName() == Name)
+ return &I;
+ llvm_unreachable("Expected to find instruction!");
+}
+
+TEST(CodeMoverUtils, IsControlFlowEquivalentSimpleTest) {
+ LLVMContext C;
+
+ // void foo(int &i, bool cond1, bool cond2) {
+ // if (cond1)
+ // i = 1;
+ // if (cond1)
+ // i = 2;
+ // if (cond2)
+ // i = 3;
+ // }
+ std::unique_ptr<Module> M =
+ parseIR(C, R"(define void @foo(i32* %i, i1 %cond1, i1 %cond2) {
+ entry:
+ br i1 %cond1, label %if.first, label %if.first.end
+ if.first:
+ store i32 1, i32* %i, align 4
+ br label %if.first.end
+ if.first.end:
+ br i1 %cond1, label %if.second, label %if.second.end
+ if.second:
+ store i32 2, i32* %i, align 4
+ br label %if.second.end
+ if.second.end:
+ br i1 %cond2, label %if.third, label %if.third.end
+ if.third:
+ store i32 3, i32* %i, align 4
+ br label %if.third.end
+ if.third.end:
+ ret void
+ })");
+ run(*M, "foo",
+ [&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,
+ DependenceInfo &DI) {
+ BasicBlock *FirstIfBody = getBasicBlockByName(F, "if.first");
+ EXPECT_TRUE(
+ isControlFlowEquivalent(*FirstIfBody, *FirstIfBody, DT, PDT));
+ BasicBlock *SecondIfBody = getBasicBlockByName(F, "if.second");
+ EXPECT_TRUE(
+ isControlFlowEquivalent(*FirstIfBody, *SecondIfBody, DT, PDT));
+
+ BasicBlock *ThirdIfBody = getBasicBlockByName(F, "if.third");
+ EXPECT_FALSE(
+ isControlFlowEquivalent(*FirstIfBody, *ThirdIfBody, DT, PDT));
+ EXPECT_FALSE(
+ isControlFlowEquivalent(*SecondIfBody, *ThirdIfBody, DT, PDT));
+ });
+}
+
+TEST(CodeMoverUtils, IsControlFlowEquivalentOppositeCondTest) {
+ LLVMContext C;
+
+ // void foo(int &i, unsigned X, unsigned Y) {
+ // if (X < Y)
+ // i = 1;
+ // if (Y > X)
+ // i = 2;
+ // if (X >= Y)
+ // i = 3;
+ // else
+ // i = 4;
+ // if (X == Y)
+ // i = 5;
+ // if (Y == X)
+ // i = 6;
+ // else
+ // i = 7;
+ // if (X != Y)
+ // i = 8;
+ // else
+ // i = 9;
+ // }
+ std::unique_ptr<Module> M =
+ parseIR(C, R"(define void @foo(i32* %i, i32 %X, i32 %Y) {
+ entry:
+ %cmp1 = icmp ult i32 %X, %Y
+ br i1 %cmp1, label %if.first, label %if.first.end
+ if.first:
+ store i32 1, i32* %i, align 4
+ br label %if.first.end
+ if.first.end:
+ %cmp2 = icmp ugt i32 %Y, %X
+ br i1 %cmp2, label %if.second, label %if.second.end
+ if.second:
+ store i32 2, i32* %i, align 4
+ br label %if.second.end
+ if.second.end:
+ %cmp3 = icmp uge i32 %X, %Y
+ br i1 %cmp3, label %if.third, label %if.third.else
+ if.third:
+ store i32 3, i32* %i, align 4
+ br label %if.third.end
+ if.third.else:
+ store i32 4, i32* %i, align 4
+ br label %if.third.end
+ if.third.end:
+ %cmp4 = icmp eq i32 %X, %Y
+ br i1 %cmp4, label %if.fourth, label %if.fourth.end
+ if.fourth:
+ store i32 5, i32* %i, align 4
+ br label %if.fourth.end
+ if.fourth.end:
+ %cmp5 = icmp eq i32 %Y, %X
+ br i1 %cmp5, label %if.fifth, label %if.fifth.else
+ if.fifth:
+ store i32 6, i32* %i, align 4
+ br label %if.fifth.end
+ if.fifth.else:
+ store i32 7, i32* %i, align 4
+ br label %if.fifth.end
+ if.fifth.end:
+ %cmp6 = icmp ne i32 %X, %Y
+ br i1 %cmp6, label %if.sixth, label %if.sixth.else
+ if.sixth:
+ store i32 8, i32* %i, align 4
+ br label %if.sixth.end
+ if.sixth.else:
+ store i32 9, i32* %i, align 4
+ br label %if.sixth.end
+ if.sixth.end:
+ ret void
+ })");
+ run(*M, "foo",
+ [&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,
+ DependenceInfo &DI) {
+ BasicBlock *FirstIfBody = getBasicBlockByName(F, "if.first");
+ BasicBlock *SecondIfBody = getBasicBlockByName(F, "if.second");
+ BasicBlock *ThirdIfBody = getBasicBlockByName(F, "if.third");
+ BasicBlock *ThirdElseBody = getBasicBlockByName(F, "if.third.else");
+ EXPECT_TRUE(
+ isControlFlowEquivalent(*FirstIfBody, *ThirdElseBody, DT, PDT));
+ EXPECT_TRUE(
+ isControlFlowEquivalent(*SecondIfBody, *ThirdElseBody, DT, PDT));
+ EXPECT_FALSE(
+ isControlFlowEquivalent(*ThirdIfBody, *ThirdElseBody, DT, PDT));
+
+ BasicBlock *FourthIfBody = getBasicBlockByName(F, "if.fourth");
+ BasicBlock *FifthIfBody = getBasicBlockByName(F, "if.fifth");
+ BasicBlock *FifthElseBody = getBasicBlockByName(F, "if.fifth.else");
+ EXPECT_FALSE(
+ isControlFlowEquivalent(*FifthIfBody, *FifthElseBody, DT, PDT));
+ BasicBlock *SixthIfBody = getBasicBlockByName(F, "if.sixth");
+ EXPECT_TRUE(
+ isControlFlowEquivalent(*FifthElseBody, *SixthIfBody, DT, PDT));
+ BasicBlock *SixthElseBody = getBasicBlockByName(F, "if.sixth.else");
+ EXPECT_TRUE(
+ isControlFlowEquivalent(*FourthIfBody, *SixthElseBody, DT, PDT));
+ EXPECT_TRUE(
+ isControlFlowEquivalent(*FifthIfBody, *SixthElseBody, DT, PDT));
+ });
+}
+
+TEST(CodeMoverUtils, IsControlFlowEquivalentCondNestTest) {
+ LLVMContext C;
+
+ // void foo(int &i, bool cond1, bool cond2) {
+ // if (cond1)
+ // if (cond2)
+ // i = 1;
+ // if (cond2)
+ // if (cond1)
+ // i = 2;
+ // }
+ std::unique_ptr<Module> M =
+ parseIR(C, R"(define void @foo(i32* %i, i1 %cond1, i1 %cond2) {
+ entry:
+ br i1 %cond1, label %if.outer.first, label %if.first.end
+ if.outer.first:
+ br i1 %cond2, label %if.inner.first, label %if.first.end
+ if.inner.first:
+ store i32 1, i32* %i, align 4
+ br label %if.first.end
+ if.first.end:
+ br i1 %cond2, label %if.outer.second, label %if.second.end
+ if.outer.second:
+ br i1 %cond1, label %if.inner.second, label %if.second.end
+ if.inner.second:
+ store i32 2, i32* %i, align 4
+ br label %if.second.end
+ if.second.end:
+ ret void
+ })");
+ run(*M, "foo",
+ [&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,
+ DependenceInfo &DI) {
+ BasicBlock *FirstOuterIfBody = getBasicBlockByName(F, "if.outer.first");
+ BasicBlock *FirstInnerIfBody = getBasicBlockByName(F, "if.inner.first");
+ BasicBlock *SecondOuterIfBody =
+ getBasicBlockByName(F, "if.outer.second");
+ BasicBlock *SecondInnerIfBody =
+ getBasicBlockByName(F, "if.inner.second");
+ EXPECT_TRUE(isControlFlowEquivalent(*FirstInnerIfBody,
+ *SecondInnerIfBody, DT, PDT));
+ EXPECT_FALSE(isControlFlowEquivalent(*FirstOuterIfBody,
+ *SecondOuterIfBody, DT, PDT));
+ EXPECT_FALSE(isControlFlowEquivalent(*FirstOuterIfBody,
+ *SecondInnerIfBody, DT, PDT));
+ EXPECT_FALSE(isControlFlowEquivalent(*FirstInnerIfBody,
+ *SecondOuterIfBody, DT, PDT));
+ });
+}
+
+TEST(CodeMoverUtils, IsControlFlowEquivalentImbalanceTest) {
+ LLVMContext C;
+
+ // void foo(int &i, bool cond1, bool cond2) {
+ // if (cond1)
+ // if (cond2)
+ // if (cond3)
+ // i = 1;
+ // if (cond2)
+ // if (cond3)
+ // i = 2;
+ // if (cond1)
+ // if (cond1)
+ // i = 3;
+ // if (cond1)
+ // i = 4;
+ // }
+ std::unique_ptr<Module> M = parseIR(
+ C, R"(define void @foo(i32* %i, i1 %cond1, i1 %cond2, i1 %cond3) {
+ entry:
+ br i1 %cond1, label %if.outer.first, label %if.first.end
+ if.outer.first:
+ br i1 %cond2, label %if.middle.first, label %if.first.end
+ if.middle.first:
+ br i1 %cond3, label %if.inner.first, label %if.first.end
+ if.inner.first:
+ store i32 1, i32* %i, align 4
+ br label %if.first.end
+ if.first.end:
+ br i1 %cond2, label %if.outer.second, label %if.second.end
+ if.outer.second:
+ br i1 %cond3, label %if.inner.second, label %if.second.end
+ if.inner.second:
+ store i32 2, i32* %i, align 4
+ br label %if.second.end
+ if.second.end:
+ br i1 %cond1, label %if.outer.third, label %if.third.end
+ if.outer.third:
+ br i1 %cond1, label %if.inner.third, label %if.third.end
+ if.inner.third:
+ store i32 3, i32* %i, align 4
+ br label %if.third.end
+ if.third.end:
+ br i1 %cond1, label %if.fourth, label %if.fourth.end
+ if.fourth:
+ store i32 4, i32* %i, align 4
+ br label %if.fourth.end
+ if.fourth.end:
+ ret void
+ })");
+ run(*M, "foo",
+ [&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,
+ DependenceInfo &DI) {
+ BasicBlock *FirstIfBody = getBasicBlockByName(F, "if.inner.first");
+ BasicBlock *SecondIfBody = getBasicBlockByName(F, "if.inner.second");
+ EXPECT_FALSE(
+ isControlFlowEquivalent(*FirstIfBody, *SecondIfBody, DT, PDT));
+
+ BasicBlock *ThirdIfBody = getBasicBlockByName(F, "if.inner.third");
+ BasicBlock *FourthIfBody = getBasicBlockByName(F, "if.fourth");
+ EXPECT_TRUE(
+ isControlFlowEquivalent(*ThirdIfBody, *FourthIfBody, DT, PDT));
+ });
+}
+
+TEST(CodeMoverUtils, IsControlFlowEquivalentPointerTest) {
+ LLVMContext C;
+
+ // void foo(int &i, int *cond) {
+ // if (*cond)
+ // i = 1;
+ // if (*cond)
+ // i = 2;
+ // *cond = 1;
+ // if (*cond)
+ // i = 3;
+ // }
+ std::unique_ptr<Module> M =
+ parseIR(C, R"(define void @foo(i32* %i, i32* %cond) {
+ entry:
+ %0 = load i32, i32* %cond, align 4
+ %tobool1 = icmp ne i32 %0, 0
+ br i1 %tobool1, label %if.first, label %if.first.end
+ if.first:
+ store i32 1, i32* %i, align 4
+ br label %if.first.end
+ if.first.end:
+ %1 = load i32, i32* %cond, align 4
+ %tobool2 = icmp ne i32 %1, 0
+ br i1 %tobool2, label %if.second, label %if.second.end
+ if.second:
+ store i32 2, i32* %i, align 4
+ br label %if.second.end
+ if.second.end:
+ store i32 1, i32* %cond, align 4
+ %2 = load i32, i32* %cond, align 4
+ %tobool3 = icmp ne i32 %2, 0
+ br i1 %tobool3, label %if.third, label %if.third.end
+ if.third:
+ store i32 3, i32* %i, align 4
+ br label %if.third.end
+ if.third.end:
+ ret void
+ })");
+ run(*M, "foo",
+ [&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,
+ DependenceInfo &DI) {
+ BasicBlock *FirstIfBody = getBasicBlockByName(F, "if.first");
+ BasicBlock *SecondIfBody = getBasicBlockByName(F, "if.second");
+ // Limitation: if we can prove cond haven't been modify between %0 and
+ // %1, then we can prove FirstIfBody and SecondIfBody are control flow
+ // equivalent.
+ EXPECT_FALSE(
+ isControlFlowEquivalent(*FirstIfBody, *SecondIfBody, DT, PDT));
+
+ BasicBlock *ThirdIfBody = getBasicBlockByName(F, "if.third");
+ EXPECT_FALSE(
+ isControlFlowEquivalent(*FirstIfBody, *ThirdIfBody, DT, PDT));
+ EXPECT_FALSE(
+ isControlFlowEquivalent(*SecondIfBody, *ThirdIfBody, DT, PDT));
+ });
+}
+
+TEST(CodeMoverUtils, IsControlFlowEquivalentNotPostdomTest) {
+ LLVMContext C;
+
+ // void foo(bool cond1, bool cond2) {
+ // if (cond1) {
+ // if (cond2)
+ // return;
+ // } else
+ // if (cond2)
+ // return;
+ // return;
+ // }
+ std::unique_ptr<Module> M =
+ parseIR(C, R"(define void @foo(i1 %cond1, i1 %cond2) {
+ idom:
+ br i1 %cond1, label %succ0, label %succ1
+ succ0:
+ br i1 %cond2, label %succ0ret, label %succ0succ1
+ succ0ret:
+ ret void
+ succ0succ1:
+ br label %bb
+ succ1:
+ br i1 %cond2, label %succ1ret, label %succ1succ1
+ succ1ret:
+ ret void
+ succ1succ1:
+ br label %bb
+ bb:
+ ret void
+ })");
+ run(*M, "foo",
+ [&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,
+ DependenceInfo &DI) {
+ BasicBlock &Idom = F.front();
+ assert(Idom.getName() == "idom" && "Expecting BasicBlock idom");
+ BasicBlock &BB = F.back();
+ assert(BB.getName() == "bb" && "Expecting BasicBlock bb");
+ EXPECT_FALSE(isControlFlowEquivalent(Idom, BB, DT, PDT));
+ });
+}
+
+TEST(CodeMoverUtils, IsSafeToMoveTest1) {
LLVMContext C;
// void safecall() noexcept willreturn nosync;
@@ -65,73 +447,59 @@ TEST(CodeMoverUtils, BasicTest) {
// }
// }
std::unique_ptr<Module> M = parseIR(
- C, "define void @foo(i32* noalias %A, i32* noalias %B, i32* noalias %C\n"
- " , i64 %N) {\n"
- "entry:\n"
- " %X = sdiv i64 1, %N\n"
- " call void @safecall()\n"
- " %cmp1 = icmp slt i64 0, %N\n"
- " call void @unsafecall1()\n"
- " call void @unsafecall2()\n"
- " br i1 %cmp1, label %for.body, label %for.end\n"
- "for.body:\n"
- " %i = phi i64 [ 0, %entry ], [ %inc, %for.body ]\n"
- " %arrayidx_A5 = getelementptr inbounds i32, i32* %A, i64 5\n"
- " store i32 5, i32* %arrayidx_A5, align 4\n"
- " %arrayidx_A = getelementptr inbounds i32, i32* %A, i64 %i\n"
- " store i32 0, i32* %arrayidx_A, align 4\n"
- " %load1 = load i32, i32* %arrayidx_A, align 4\n"
- " %arrayidx_B = getelementptr inbounds i32, i32* %B, i64 %i\n"
- " store i32 %load1, i32* %arrayidx_B, align 4\n"
- " %load2 = load i32, i32* %arrayidx_A, align 4\n"
- " %arrayidx_C = getelementptr inbounds i32, i32* %C, i64 %i\n"
- " store i32 %load2, i32* %arrayidx_C, align 4\n"
- " %arrayidx_A6 = getelementptr inbounds i32, i32* %A, i64 6\n"
- " store i32 6, i32* %arrayidx_A6, align 4\n"
- " %inc = add nsw i64 %i, 1\n"
- " %cmp = icmp slt i64 %inc, %N\n"
- " br i1 %cmp, label %for.body, label %for.end\n"
- "for.end:\n"
- " ret void\n"
- "}\n"
- "declare void @safecall() nounwind nosync willreturn\n"
- "declare void @unsafecall1()\n"
- "declare void @unsafecall2()\n");
+ C, R"(define void @foo(i32* noalias %A, i32* noalias %B, i32* noalias %C
+ , i64 %N) {
+ entry:
+ %X = sdiv i64 1, %N
+ call void @safecall()
+ %cmp1 = icmp slt i64 0, %N
+ call void @unsafecall1()
+ call void @unsafecall2()
+ br i1 %cmp1, label %for.body, label %for.end
+ for.body:
+ %i = phi i64 [ 0, %entry ], [ %inc, %for.body ]
+ %arrayidx_A5 = getelementptr inbounds i32, i32* %A, i64 5
+ store i32 5, i32* %arrayidx_A5, align 4
+ %arrayidx_A = getelementptr inbounds i32, i32* %A, i64 %i
+ store i32 0, i32* %arrayidx_A, align 4
+ %load1 = load i32, i32* %arrayidx_A, align 4
+ %arrayidx_B = getelementptr inbounds i32, i32* %B, i64 %i
+ store i32 %load1, i32* %arrayidx_B, align 4
+ %load2 = load i32, i32* %arrayidx_A, align 4
+ %arrayidx_C = getelementptr inbounds i32, i32* %C, i64 %i
+ store i32 %load2, i32* %arrayidx_C, align 4
+ %arrayidx_A6 = getelementptr inbounds i32, i32* %A, i64 6
+ store i32 6, i32* %arrayidx_A6, align 4
+ %inc = add nsw i64 %i, 1
+ %cmp = icmp slt i64 %inc, %N
+ br i1 %cmp, label %for.body, label %for.end
+ for.end:
+ ret void
+ }
+ declare void @safecall() nounwind nosync willreturn
+ declare void @unsafecall1()
+ declare void @unsafecall2())");
run(*M, "foo",
[&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,
DependenceInfo &DI) {
- Function::iterator FI = F.begin();
- BasicBlock *Entry = &*(FI++);
- assert(Entry->getName() == "entry" && "Expecting BasicBlock entry");
+ BasicBlock *Entry = getBasicBlockByName(F, "entry");
Instruction *CI_safecall = Entry->front().getNextNode();
assert(isa<CallInst>(CI_safecall) &&
"Expecting CI_safecall to be a CallInst");
Instruction *CI_unsafecall = CI_safecall->getNextNode()->getNextNode();
assert(isa<CallInst>(CI_unsafecall) &&
"Expecting CI_unsafecall to be a CallInst");
- BasicBlock *ForBody = &*(FI++);
- assert(ForBody->getName() == "for.body" &&
- "Expecting BasicBlock for.body");
+ BasicBlock *ForBody = getBasicBlockByName(F, "for.body");
Instruction &PN = ForBody->front();
assert(isa<PHINode>(PN) && "Expecting PN to be a PHINode");
- Instruction *SI_A5 = PN.getNextNode()->getNextNode();
- assert(isa<StoreInst>(SI_A5) &&
- SI_A5->getOperand(1)->getName() == "arrayidx_A5" &&
- "Expecting store to arrayidx_A5");
- Instruction *SI = SI_A5->getNextNode()->getNextNode();
- assert(isa<StoreInst>(SI) &&
- SI->getOperand(1)->getName() == "arrayidx_A" &&
- "Expecting store to arrayidx_A");
- Instruction *LI1 = SI->getNextNode();
- assert(LI1->getName() == "load1" && "Expecting LI1 to be load1");
- Instruction *LI2 = LI1->getNextNode()->getNextNode()->getNextNode();
- assert(LI2->getName() == "load2" && "Expecting LI2 to be load2");
+ Instruction *SI_A5 =
+ getInstructionByName(F, "arrayidx_A5")->getNextNode();
+ Instruction *SI = getInstructionByName(F, "arrayidx_A")->getNextNode();
+ Instruction *LI1 = getInstructionByName(F, "load1");
+ Instruction *LI2 = getInstructionByName(F, "load2");
Instruction *SI_A6 =
- LI2->getNextNode()->getNextNode()->getNextNode()->getNextNode();
- assert(isa<StoreInst>(SI_A6) &&
- SI_A6->getOperand(1)->getName() == "arrayidx_A6" &&
- "Expecting store to arrayidx_A6");
+ getInstructionByName(F, "arrayidx_A6")->getNextNode();
// Can move after CI_safecall, as it does not throw, not synchronize, or
// must return.
@@ -180,3 +548,68 @@ TEST(CodeMoverUtils, BasicTest) {
EXPECT_TRUE(isSafeToMoveBefore(*LI2, *LI1, DT, PDT, DI));
});
}
+
+TEST(CodeMoverUtils, IsSafeToMoveTest2) {
+ LLVMContext C;
+
+ std::unique_ptr<Module> M =
+ parseIR(C, R"(define void @foo(i1 %cond, i32 %op0, i32 %op1) {
+ entry:
+ br i1 %cond, label %if.then.first, label %if.end.first
+ if.then.first:
+ %add = add i32 %op0, %op1
+ %user = add i32 %add, 1
+ br label %if.end.first
+ if.end.first:
+ br i1 %cond, label %if.then.second, label %if.end.second
+ if.then.second:
+ %sub_op0 = add i32 %op0, 1
+ %sub = sub i32 %sub_op0, %op1
+ br label %if.end.second
+ if.end.second:
+ ret void
+ })");
+
+ run(*M, "foo",
+ [&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,
+ DependenceInfo &DI) {
+ Instruction *AddInst = getInstructionByName(F, "add");
+ Instruction *SubInst = getInstructionByName(F, "sub");
+
+ // Cannot move as %user uses %add and %sub doesn't dominates %user.
+ EXPECT_FALSE(isSafeToMoveBefore(*AddInst, *SubInst, DT, PDT, DI));
+
+ // Cannot move as %sub_op0 is an operand of %sub and %add doesn't
+ // dominates %sub_op0.
+ EXPECT_FALSE(isSafeToMoveBefore(*SubInst, *AddInst, DT, PDT, DI));
+ });
+}
+
+TEST(CodeMoverUtils, IsSafeToMoveTest3) {
+ LLVMContext C;
+
+ std::unique_ptr<Module> M = parseIR(C, R"(define void @foo(i64 %N) {
+ entry:
+ br label %for.body
+ for.body:
+ %i = phi i64 [ 0, %entry ], [ %inc, %for.latch ]
+ %inc = add nsw i64 %i, 1
+ br label %for.latch
+ for.latch:
+ %cmp = icmp slt i64 %inc, %N
+ br i1 %cmp, label %for.body, label %for.end
+ for.end:
+ ret void
+ })");
+
+ run(*M, "foo",
+ [&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,
+ DependenceInfo &DI) {
+ Instruction *IncInst = getInstructionByName(F, "inc");
+ Instruction *CmpInst = getInstructionByName(F, "cmp");
+
+ // Can move as the incoming block of %inc for %i (%for.latch) dominated
+ // by %cmp.
+ EXPECT_TRUE(isSafeToMoveBefore(*IncInst, *CmpInst, DT, PDT, DI));
+ });
+}
More information about the llvm-commits
mailing list