forked from OSchip/llvm-project
857 lines
32 KiB
C++
857 lines
32 KiB
C++
//===- 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/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<Module> parseIR(LLVMContext &C, const char *IR) {
|
|
SMDiagnostic Err;
|
|
std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
|
|
if (!Mod)
|
|
Err.print("CodeMoverUtilsTests", errs());
|
|
return Mod;
|
|
}
|
|
|
|
static void run(Module &M, StringRef FuncName,
|
|
function_ref<void(Function &F, DominatorTree &DT,
|
|
PostDominatorTree &PDT, DependenceInfo &DI)>
|
|
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<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;
|
|
// 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<Module> 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<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 = getBasicBlockByName(F, "for.body");
|
|
Instruction &PN = ForBody->front();
|
|
assert(isa<PHINode>(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<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));
|
|
});
|
|
}
|
|
|
|
TEST(CodeMoverUtils, IsSafeToMoveTest4) {
|
|
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.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<Module> 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<Module> 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));
|
|
});
|
|
}
|