1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-18 18:42:46 +02:00

[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
This commit is contained in:
Whitney Tsang 2020-01-28 03:50:33 +00:00
parent f83f5a6e3c
commit 03f2c49843
4 changed files with 717 additions and 80 deletions

View File

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

View File

@ -1123,7 +1123,7 @@ private:
/// 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();

View File

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

View File

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