//===- CodeMoverUtils.cpp - Unit tests for CodeMoverUtils ---------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Transforms/Utils/CodeMoverUtils.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/DependenceAnalysis.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/PostDominators.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/AsmParser/Parser.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #include "gtest/gtest.h" using namespace llvm; static std::unique_ptr parseIR(LLVMContext &C, const char *IR) { SMDiagnostic Err; std::unique_ptr Mod = parseAssemblyString(IR, Err, C); if (!Mod) Err.print("CodeMoverUtilsTests", errs()); return Mod; } static void run(Module &M, StringRef FuncName, function_ref Test) { auto *F = M.getFunction(FuncName); DominatorTree DT(*F); PostDominatorTree PDT(*F); TargetLibraryInfoImpl TLII; TargetLibraryInfo TLI(TLII); AssumptionCache AC(*F); AliasAnalysis AA(TLI); LoopInfo LI(DT); ScalarEvolution SE(*F, TLI, AC, DT, LI); DependenceInfo DI(F, &AA, &SE, &LI); Test(*F, DT, PDT, DI); } 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 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 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 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 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 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 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; // void unsafecall(); // void foo(int * __restrict__ A, int * __restrict__ B, int * __restrict__ C, // long N) { // X = N / 1; // safecall(); // unsafecall1(); // unsafecall2(); // for (long i = 0; i < N; ++i) { // A[5] = 5; // A[i] = 0; // B[i] = A[i]; // C[i] = A[i]; // A[6] = 6; // } // } std::unique_ptr M = parseIR( 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) { BasicBlock *Entry = getBasicBlockByName(F, "entry"); Instruction *CI_safecall = Entry->front().getNextNode(); assert(isa(CI_safecall) && "Expecting CI_safecall to be a CallInst"); Instruction *CI_unsafecall = CI_safecall->getNextNode()->getNextNode(); assert(isa(CI_unsafecall) && "Expecting CI_unsafecall to be a CallInst"); BasicBlock *ForBody = getBasicBlockByName(F, "for.body"); Instruction &PN = ForBody->front(); assert(isa(PN) && "Expecting PN to be a PHINode"); 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 = getInstructionByName(F, "arrayidx_A6")->getNextNode(); // Can move after CI_safecall, as it does not throw, not synchronize, or // must return. EXPECT_TRUE(isSafeToMoveBefore(*CI_safecall->getPrevNode(), *CI_safecall->getNextNode(), DT, &PDT, &DI)); // Cannot move CI_unsafecall, as it may throw. EXPECT_FALSE(isSafeToMoveBefore(*CI_unsafecall->getNextNode(), *CI_unsafecall, DT, &PDT, &DI)); // Moving instruction to non control flow equivalent places are not // supported. EXPECT_FALSE( isSafeToMoveBefore(*SI_A5, *Entry->getTerminator(), DT, &PDT, &DI)); // Moving PHINode is not supported. EXPECT_FALSE(isSafeToMoveBefore(PN, *PN.getNextNode()->getNextNode(), DT, &PDT, &DI)); // Cannot move non-PHINode before PHINode. EXPECT_FALSE(isSafeToMoveBefore(*PN.getNextNode(), PN, DT, &PDT, &DI)); // Moving Terminator is not supported. EXPECT_FALSE(isSafeToMoveBefore(*Entry->getTerminator(), *PN.getNextNode(), DT, &PDT, &DI)); // Cannot move %arrayidx_A after SI, as SI is its user. EXPECT_FALSE(isSafeToMoveBefore(*SI->getPrevNode(), *SI->getNextNode(), DT, &PDT, &DI)); // Cannot move SI before %arrayidx_A, as %arrayidx_A is its operand. EXPECT_FALSE( isSafeToMoveBefore(*SI, *SI->getPrevNode(), DT, &PDT, &DI)); // Cannot move LI2 after SI_A6, as there is a flow dependence. EXPECT_FALSE( isSafeToMoveBefore(*LI2, *SI_A6->getNextNode(), DT, &PDT, &DI)); // Cannot move SI after LI1, as there is a anti dependence. EXPECT_FALSE( isSafeToMoveBefore(*SI, *LI1->getNextNode(), DT, &PDT, &DI)); // Cannot move SI_A5 after SI, as there is a output dependence. EXPECT_FALSE(isSafeToMoveBefore(*SI_A5, *LI1, DT, &PDT, &DI)); // Can move LI2 before LI1, as there is only an input dependence. EXPECT_TRUE(isSafeToMoveBefore(*LI2, *LI1, DT, &PDT, &DI)); }); } TEST(CodeMoverUtils, IsSafeToMoveTest2) { LLVMContext C; std::unique_ptr 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 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)); }); } TEST(CodeMoverUtils, IsSafeToMoveTest4) { LLVMContext C; std::unique_ptr M = parseIR(C, R"(define void @foo(i1 %cond, i32 %op0, i32 %op1) { entry: br i1 %cond, label %if.end.first, label %if.then.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.end.second, label %if.then.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, IsSafeToMoveTest5) { LLVMContext C; std::unique_ptr M = parseIR(C, R"(define void @dependence(i32* noalias %A, i32* noalias %B){ entry: store i32 0, i32* %A, align 4 ; storeA0 store i32 2, i32* %A, align 4 ; storeA1 %tmp0 = load i32, i32* %A, align 4 ; loadA0 store i32 1, i32* %B, align 4 ; storeB0 %tmp1 = load i32, i32* %A, align 4 ; loadA1 store i32 2, i32* %A, align 4 ; storeA2 store i32 4, i32* %B, align 4 ; StoreB1 %tmp2 = load i32, i32* %A, align 4 ; loadA2 %tmp3 = load i32, i32* %A, align 4 ; loadA3 %tmp4 = load i32, i32* %B, align 4 ; loadB2 %tmp5 = load i32, i32* %B, align 4 ; loadB3 ret void })"); run(*M, "dependence", [&](Function &F, DominatorTree &DT, PostDominatorTree &PDT, DependenceInfo &DI) { Instruction *LoadA0 = getInstructionByName(F, "tmp0"); Instruction *LoadA1 = getInstructionByName(F, "tmp1"); Instruction *LoadA2 = getInstructionByName(F, "tmp2"); Instruction *LoadA3 = getInstructionByName(F, "tmp3"); Instruction *LoadB2 = getInstructionByName(F, "tmp4"); Instruction *LoadB3 = getInstructionByName(F, "tmp5"); Instruction *StoreA1 = LoadA0->getPrevNode(); Instruction *StoreA0 = StoreA1->getPrevNode(); Instruction *StoreB0 = LoadA0->getNextNode(); Instruction *StoreB1 = LoadA2->getPrevNode(); Instruction *StoreA2 = StoreB1->getPrevNode(); // Input forward dependency EXPECT_TRUE(isSafeToMoveBefore(*LoadA2, *LoadB2, DT, &PDT, &DI)); // Input backward dependency EXPECT_TRUE(isSafeToMoveBefore(*LoadA3, *LoadA2, DT, &PDT, &DI)); // Output forward dependency EXPECT_FALSE(isSafeToMoveBefore(*StoreA0, *LoadA0, DT, &PDT, &DI)); // Output backward dependency EXPECT_FALSE(isSafeToMoveBefore(*StoreA1, *StoreA0, DT, &PDT, &DI)); // Flow forward dependency EXPECT_FALSE(isSafeToMoveBefore(*StoreA1, *StoreB0, DT, &PDT, &DI)); // Flow backward dependency EXPECT_FALSE(isSafeToMoveBefore(*LoadA0, *StoreA1, DT, &PDT, &DI)); // Anti forward dependency EXPECT_FALSE(isSafeToMoveBefore(*LoadA1, *StoreB1, DT, &PDT, &DI)); // Anti backward dependency EXPECT_FALSE(isSafeToMoveBefore(*StoreA2, *LoadA1, DT, &PDT, &DI)); // No input backward dependency EXPECT_TRUE(isSafeToMoveBefore(*LoadB2, *LoadA3, DT, &PDT, &DI)); // No input forward dependency EXPECT_TRUE(isSafeToMoveBefore(*LoadA3, *LoadB3, DT, &PDT, &DI)); // No Output forward dependency EXPECT_TRUE(isSafeToMoveBefore(*StoreA2, *LoadA2, DT, &PDT, &DI)); // No Output backward dependency EXPECT_TRUE(isSafeToMoveBefore(*StoreB1, *StoreA2, DT, &PDT, &DI)); // No flow forward dependency EXPECT_TRUE(isSafeToMoveBefore(*StoreB0, *StoreA2, DT, &PDT, &DI)); // No flow backward dependency EXPECT_TRUE(isSafeToMoveBefore(*LoadA1, *StoreB0, DT, &PDT, &DI)); // No anti backward dependency EXPECT_TRUE(isSafeToMoveBefore(*StoreB0, *LoadA0, DT, &PDT, &DI)); // No anti forward dependency EXPECT_TRUE(isSafeToMoveBefore(*LoadA0, *LoadA1, DT, &PDT, &DI)); }); } TEST(CodeMoverUtils, IsSafeToMoveTest6) { LLVMContext C; std::unique_ptr M = parseIR( C, R"(define void @dependence(i1 %cond, i32* noalias %A, i32* noalias %B){ entry: br i1 %cond, label %bb0, label %bb1 bb0: br label %bb1 bb1: store i32 0, i32* %A, align 4 ; storeA0 br i1 %cond, label %bb2, label %bb3 bb2: br label %bb3 bb3: store i32 2, i32* %A, align 4 ; storeA1 br i1 %cond, label %bb4, label %bb5 bb4: br label %bb5 bb5: %tmp0 = load i32, i32* %A, align 4 ; loadA0 br i1 %cond, label %bb6, label %bb7 bb6: br label %bb7 bb7: store i32 1, i32* %B, align 4 ; storeB0 br i1 %cond, label %bb8, label %bb9 bb8: br label %bb9 bb9: %tmp1 = load i32, i32* %A, align 4 ; loadA1 br i1 %cond, label %bb10, label %bb11 bb10: br label %bb11 bb11: store i32 2, i32* %A, align 4 ; storeA2 br i1 %cond, label %bb12, label %bb13 bb12: br label %bb13 bb13: store i32 4, i32* %B, align 4 ; StoreB1 br i1 %cond, label %bb14, label %bb15 bb14: br label %bb15 bb15: %tmp2 = load i32, i32* %A, align 4 ; loadA2 br i1 %cond, label %bb16, label %bb17 bb16: br label %bb17 bb17: %tmp3 = load i32, i32* %A, align 4 ; loadA3 br i1 %cond, label %bb18, label %bb19 bb18: br label %bb19 bb19: %tmp4 = load i32, i32* %B, align 4 ; loadB2 br i1 %cond, label %bb20, label %bb21 bb20: br label %bb21 bb21: %tmp5 = load i32, i32* %B, align 4 ; loadB3 ret void })"); run(*M, "dependence", [&](Function &F, DominatorTree &DT, PostDominatorTree &PDT, DependenceInfo &DI) { BasicBlock *BB1 = getBasicBlockByName(F, "bb1"); BasicBlock *BB3 = getBasicBlockByName(F, "bb3"); BasicBlock *BB7 = getBasicBlockByName(F, "bb7"); BasicBlock *BB11 = getBasicBlockByName(F, "bb11"); BasicBlock *BB13 = getBasicBlockByName(F, "bb13"); Instruction *LoadA0 = getInstructionByName(F, "tmp0"); Instruction *LoadA1 = getInstructionByName(F, "tmp1"); Instruction *LoadA2 = getInstructionByName(F, "tmp2"); Instruction *LoadA3 = getInstructionByName(F, "tmp3"); Instruction *LoadB2 = getInstructionByName(F, "tmp4"); Instruction *LoadB3 = getInstructionByName(F, "tmp5"); Instruction &StoreA1 = BB3->front(); Instruction &StoreA0 = BB1->front(); Instruction &StoreB0 = BB7->front(); Instruction &StoreB1 = BB13->front(); Instruction &StoreA2 = BB11->front(); // Input forward dependency EXPECT_TRUE(isSafeToMoveBefore(*LoadA2, *LoadB2, DT, &PDT, &DI)); // Input backward dependency EXPECT_TRUE(isSafeToMoveBefore(*LoadA3, *LoadA2, DT, &PDT, &DI)); // Output forward dependency EXPECT_FALSE(isSafeToMoveBefore(StoreA0, *LoadA0, DT, &PDT, &DI)); // Output backward dependency EXPECT_FALSE(isSafeToMoveBefore(StoreA1, StoreA0, DT, &PDT, &DI)); // Flow forward dependency EXPECT_FALSE(isSafeToMoveBefore(StoreA1, StoreB0, DT, &PDT, &DI)); // Flow backward dependency EXPECT_FALSE(isSafeToMoveBefore(*LoadA0, StoreA1, DT, &PDT, &DI)); // Anti forward dependency EXPECT_FALSE(isSafeToMoveBefore(*LoadA1, StoreB1, DT, &PDT, &DI)); // Anti backward dependency EXPECT_FALSE(isSafeToMoveBefore(StoreA2, *LoadA1, DT, &PDT, &DI)); // No input backward dependency EXPECT_TRUE(isSafeToMoveBefore(*LoadB2, *LoadA3, DT, &PDT, &DI)); // No input forward dependency EXPECT_TRUE(isSafeToMoveBefore(*LoadA3, *LoadB3, DT, &PDT, &DI)); // No Output forward dependency EXPECT_TRUE(isSafeToMoveBefore(StoreA2, *LoadA2, DT, &PDT, &DI)); // No Output backward dependency EXPECT_TRUE(isSafeToMoveBefore(StoreB1, StoreA2, DT, &PDT, &DI)); // No flow forward dependency EXPECT_TRUE(isSafeToMoveBefore(StoreB0, StoreA2, DT, &PDT, &DI)); // No flow backward dependency EXPECT_TRUE(isSafeToMoveBefore(*LoadA1, StoreB0, DT, &PDT, &DI)); // No anti backward dependency EXPECT_TRUE(isSafeToMoveBefore(StoreB0, *LoadA0, DT, &PDT, &DI)); // No anti forward dependency EXPECT_TRUE(isSafeToMoveBefore(*LoadA0, *LoadA1, DT, &PDT, &DI)); }); }