#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/Analysis/BasicAliasAnalysis.h"
#include "llvm/Analysis/BlockFrequencyInfo.h"
#include "llvm/Analysis/BranchProbabilityInfo.h"
#include "llvm/Analysis/CFG.h"
#include "llvm/Analysis/DomTreeUpdater.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/MemorySSA.h"
#include "llvm/Analysis/MemorySSAUpdater.h"
#include "llvm/Analysis/PostDominators.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Transforms/Utils/BreakCriticalEdges.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("BasicBlockUtilsTests", errs());
return Mod;
}
static BasicBlock *getBasicBlockByName(Function &F, StringRef Name) {
for (BasicBlock &BB : F)
if (BB.getName() == Name)
return &BB;
llvm_unreachable("Expected to find basic block!");
}
TEST(BasicBlockUtils, EliminateUnreachableBlocks) {
LLVMContext C;
std::unique_ptr<Module> M = parseIR(C, R"IR(
define i32 @has_unreachable(i1 %cond) {
entry:
br i1 %cond, label %bb0, label %bb1
bb0:
br label %bb1
bb1:
%phi = phi i32 [ 0, %entry ], [ 1, %bb0 ]
ret i32 %phi
bb2:
ret i32 42
}
)IR");
Function *F = M->getFunction("has_unreachable");
DominatorTree DT(*F);
DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager);
EXPECT_EQ(F->size(), (size_t)4);
bool Result = EliminateUnreachableBlocks(*F, &DTU);
EXPECT_TRUE(Result);
EXPECT_EQ(F->size(), (size_t)3);
EXPECT_TRUE(DT.verify());
}
TEST(BasicBlockUtils, SplitEdge_ex1) {
LLVMContext C;
std::unique_ptr<Module> M = parseIR(C, R"IR(
define void @foo(i1 %cond0) {
entry:
br i1 %cond0, label %bb0, label %bb1
bb0:
%0 = mul i32 1, 2
br label %bb1
bb1:
br label %bb2
bb2:
ret void
}
)IR");
Function *F = M->getFunction("foo");
DominatorTree DT(*F);
BasicBlock *SrcBlock;
BasicBlock *DestBlock;
BasicBlock *NewBB;
SrcBlock = getBasicBlockByName(*F, "entry");
DestBlock = getBasicBlockByName(*F, "bb0");
NewBB = SplitEdge(SrcBlock, DestBlock, &DT, nullptr, nullptr);
EXPECT_TRUE(DT.verify());
EXPECT_EQ(NewBB->getSinglePredecessor(), SrcBlock);
EXPECT_EQ(NewBB->getSingleSuccessor(), DestBlock);
EXPECT_EQ(NewBB->getParent(), F);
bool BBFlag = false;
for (BasicBlock &BB : *F) {
if (BB.getName() == NewBB->getName()) {
BBFlag = true;
}
}
EXPECT_TRUE(BBFlag);
}
TEST(BasicBlockUtils, SplitEdge_ex2) {
LLVMContext C;
std::unique_ptr<Module> M = parseIR(C, R"IR(
define void @foo() {
bb0:
br label %bb2
bb1:
br label %bb2
bb2:
ret void
}
)IR");
Function *F = M->getFunction("foo");
DominatorTree DT(*F);
BasicBlock *SrcBlock;
BasicBlock *DestBlock;
BasicBlock *NewBB;
SrcBlock = getBasicBlockByName(*F, "bb0");
DestBlock = getBasicBlockByName(*F, "bb2");
NewBB = SplitEdge(SrcBlock, DestBlock, &DT, nullptr, nullptr);
EXPECT_TRUE(DT.verify());
EXPECT_EQ(NewBB->getSinglePredecessor(), SrcBlock);
EXPECT_EQ(NewBB->getSingleSuccessor(), DestBlock);
EXPECT_EQ(NewBB->getParent(), F);
bool BBFlag = false;
for (BasicBlock &BB : *F) {
if (BB.getName() == NewBB->getName()) {
BBFlag = true;
}
}
EXPECT_TRUE(BBFlag);
}
TEST(BasicBlockUtils, SplitEdge_ex3) {
LLVMContext C;
std::unique_ptr<Module> M = parseIR(C, R"IR(
define i32 @foo(i32 %n) {
entry:
br label %header
header:
%sum.02 = phi i32 [ 0, %entry ], [ %sum.1, %bb3 ]
%0 = phi i32 [ 0, %entry ], [ %4, %bb3 ]
%1 = icmp slt i32 %0, %n
br i1 %1, label %bb0, label %bb1
bb0:
%2 = add nsw i32 %sum.02, 2
br label %bb2
bb1:
%3 = add nsw i32 %sum.02, 1
br label %bb2
bb2:
%sum.1 = phi i32 [ %2, %bb0 ], [ %3, %bb1 ]
br label %bb3
bb3:
%4 = add nsw i32 %0, 1
%5 = icmp slt i32 %4, 100
br i1 %5, label %header, label %bb4
bb4:
%sum.0.lcssa = phi i32 [ %sum.1, %bb3 ]
ret i32 %sum.0.lcssa
}
)IR");
Function *F = M->getFunction("foo");
DominatorTree DT(*F);
LoopInfo LI(DT);
DataLayout DL("e-i64:64-f80:128-n8:16:32:64-S128");
TargetLibraryInfoImpl TLII;
TargetLibraryInfo TLI(TLII);
AssumptionCache AC(*F);
AAResults AA(TLI);
BasicAAResult BAA(DL, *F, TLI, AC, &DT);
AA.addAAResult(BAA);
MemorySSA MSSA(*F, &AA, &DT);
MemorySSAUpdater Updater(&MSSA);
BasicBlock *SrcBlock;
BasicBlock *DestBlock;
BasicBlock *NewBB;
SrcBlock = getBasicBlockByName(*F, "header");
DestBlock = getBasicBlockByName(*F, "bb0");
NewBB = SplitEdge(SrcBlock, DestBlock, &DT, &LI, &Updater);
Updater.getMemorySSA()->verifyMemorySSA();
EXPECT_TRUE(DT.verify());
EXPECT_NE(LI.getLoopFor(SrcBlock), nullptr);
EXPECT_NE(LI.getLoopFor(DestBlock), nullptr);
EXPECT_NE(LI.getLoopFor(NewBB), nullptr);
EXPECT_EQ(NewBB->getSinglePredecessor(), SrcBlock);
EXPECT_EQ(NewBB->getSingleSuccessor(), DestBlock);
EXPECT_EQ(NewBB->getParent(), F);
bool BBFlag = false;
for (BasicBlock &BB : *F) {
if (BB.getName() == NewBB->getName()) {
BBFlag = true;
}
}
EXPECT_TRUE(BBFlag);
}
TEST(BasicBlockUtils, SplitEdge_ex4) {
LLVMContext C;
std::unique_ptr<Module> M = parseIR(C, R"IR(
define void @bar(i32 %cond) personality i8 0 {
entry:
switch i32 %cond, label %exit [
i32 -1, label %continue
i32 0, label %continue
i32 1, label %continue_alt
i32 2, label %continue_alt
]
exit:
ret void
continue:
invoke void @sink() to label %normal unwind label %exception
continue_alt:
invoke void @sink_alt() to label %normal unwind label %exception
exception:
%cleanup = landingpad i8 cleanup
br label %trivial-eh-handler
trivial-eh-handler:
call void @sideeffect(i32 1)
br label %normal
normal:
call void @sideeffect(i32 0)
ret void
}
declare void @sideeffect(i32)
declare void @sink() cold
declare void @sink_alt() cold
)IR");
Function *F = M->getFunction("bar");
DominatorTree DT(*F);
LoopInfo LI(DT);
TargetLibraryInfoImpl TLII;
TargetLibraryInfo TLI(TLII);
AAResults AA(TLI);
MemorySSA MSSA(*F, &AA, &DT);
MemorySSAUpdater MSSAU(&MSSA);
BasicBlock *SrcBlock;
BasicBlock *DestBlock;
SrcBlock = getBasicBlockByName(*F, "continue");
DestBlock = getBasicBlockByName(*F, "exception");
unsigned SuccNum = GetSuccessorNumber(SrcBlock, DestBlock);
Instruction *LatchTerm = SrcBlock->getTerminator();
const CriticalEdgeSplittingOptions Options =
CriticalEdgeSplittingOptions(&DT, &LI, &MSSAU);
bool CriticalEdge =
isCriticalEdge(LatchTerm, SuccNum, Options.MergeIdenticalEdges);
EXPECT_TRUE(CriticalEdge);
bool Ehpad = DestBlock->isEHPad();
EXPECT_TRUE(Ehpad);
BasicBlock *NewBB = SplitEdge(SrcBlock, DestBlock, &DT, &LI, &MSSAU, "");
MSSA.verifyMemorySSA();
EXPECT_TRUE(DT.verify());
EXPECT_NE(NewBB, nullptr);
EXPECT_EQ(NewBB->getSinglePredecessor(), SrcBlock);
EXPECT_EQ(NewBB, SrcBlock->getTerminator()->getSuccessor(SuccNum));
EXPECT_EQ(NewBB->getParent(), F);
bool BBFlag = false;
for (BasicBlock &BB : *F) {
if (BB.getName() == NewBB->getName()) {
BBFlag = true;
break;
}
}
EXPECT_TRUE(BBFlag);
}
TEST(BasicBlockUtils, splitBasicBlockBefore_ex1) {
LLVMContext C;
std::unique_ptr<Module> M = parseIR(C, R"IR(
define void @foo() {
bb0:
%0 = mul i32 1, 2
br label %bb2
bb1:
br label %bb3
bb2:
%1 = phi i32 [ %0, %bb0 ]
br label %bb3
bb3:
ret void
}
)IR");
Function *F = M->getFunction("foo");
DominatorTree DT(*F);
BasicBlock *DestBlock;
BasicBlock *NewBB;
DestBlock = getBasicBlockByName(*F, "bb2");
NewBB = DestBlock->splitBasicBlockBefore(DestBlock->front().getIterator(),
"test");
PHINode *PN = dyn_cast<PHINode>(&(DestBlock->front()));
EXPECT_EQ(PN->getIncomingBlock(0), NewBB);
EXPECT_EQ(NewBB->getName(), "test");
EXPECT_EQ(NewBB->getSingleSuccessor(), DestBlock);
EXPECT_EQ(DestBlock->getSinglePredecessor(), NewBB);
}
#ifndef NDEBUG
TEST(BasicBlockUtils, splitBasicBlockBefore_ex2) {
LLVMContext C;
std::unique_ptr<Module> M = parseIR(C, R"IR(
define void @foo() {
bb0:
%0 = mul i32 1, 2
br label %bb2
bb1:
br label %bb2
bb2:
%1 = phi i32 [ %0, %bb0 ], [ 1, %bb1 ]
br label %bb3
bb3:
ret void
}
)IR");
Function *F = M->getFunction("foo");
DominatorTree DT(*F);
BasicBlock *DestBlock = getBasicBlockByName(*F, "bb2");
ASSERT_DEATH(
{
DestBlock->splitBasicBlockBefore(DestBlock->front().getIterator(),
"test");
},
"cannot split on multi incoming phis");
}
#endif
TEST(BasicBlockUtils, NoUnreachableBlocksToEliminate) {
LLVMContext C;
std::unique_ptr<Module> M = parseIR(C, R"IR(
define i32 @no_unreachable(i1 %cond) {
entry:
br i1 %cond, label %bb0, label %bb1
bb0:
br label %bb1
bb1:
%phi = phi i32 [ 0, %entry ], [ 1, %bb0 ]
ret i32 %phi
}
)IR");
Function *F = M->getFunction("no_unreachable");
DominatorTree DT(*F);
DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager);
EXPECT_EQ(F->size(), (size_t)3);
bool Result = EliminateUnreachableBlocks(*F, &DTU);
EXPECT_FALSE(Result);
EXPECT_EQ(F->size(), (size_t)3);
EXPECT_TRUE(DT.verify());
}
TEST(BasicBlockUtils, SplitBlockPredecessors) {
LLVMContext C;
std::unique_ptr<Module> M = parseIR(C, R"IR(
define i32 @basic_func(i1 %cond) {
entry:
br i1 %cond, label %bb0, label %bb1
bb0:
br label %bb1
bb1:
%phi = phi i32 [ 0, %entry ], [ 1, %bb0 ]
ret i32 %phi
}
)IR");
Function *F = M->getFunction("basic_func");
DominatorTree DT(*F);
SplitBlockPredecessors(&F->getEntryBlock(), {}, "split.entry", &DT);
EXPECT_TRUE(DT.verify());
}
TEST(BasicBlockUtils, SplitCriticalEdge) {
LLVMContext C;
std::unique_ptr<Module> M = parseIR(C, R"IR(
define void @crit_edge(i1 %cond0, i1 %cond1) {
entry:
br i1 %cond0, label %bb0, label %bb1
bb0:
br label %bb1
bb1:
br label %bb2
bb2:
ret void
}
)IR");
Function *F = M->getFunction("crit_edge");
DominatorTree DT(*F);
PostDominatorTree PDT(*F);
CriticalEdgeSplittingOptions CESO(&DT, nullptr, nullptr, &PDT);
EXPECT_EQ(1u, SplitAllCriticalEdges(*F, CESO));
EXPECT_TRUE(DT.verify());
EXPECT_TRUE(PDT.verify());
}
TEST(BasicBlockUtils, SplitIndirectBrCriticalEdgesIgnorePHIs) {
LLVMContext C;
std::unique_ptr<Module> M = parseIR(C, R"IR(
define void @crit_edge(i8* %tgt, i1 %cond0, i1 %cond1) {
entry:
indirectbr i8* %tgt, [label %bb0, label %bb1, label %bb2]
bb0:
br i1 %cond0, label %bb1, label %bb2
bb1:
%p = phi i32 [0, %bb0], [0, %entry]
br i1 %cond1, label %bb3, label %bb4
bb2:
ret void
bb3:
ret void
bb4:
ret void
}
)IR");
Function *F = M->getFunction("crit_edge");
DominatorTree DT(*F);
LoopInfo LI(DT);
BranchProbabilityInfo BPI(*F, LI);
BlockFrequencyInfo BFI(*F, BPI, LI);
ASSERT_TRUE(SplitIndirectBrCriticalEdges(*F, true,
&BPI, &BFI));
BasicBlock *BB1 = getBasicBlockByName(*F, "bb1");
BasicBlock *SplitBB = BB1->getTerminator()->getSuccessor(0);
ASSERT_EQ(2u, SplitBB->getTerminator()->getNumSuccessors());
EXPECT_EQ(BranchProbability(1, 2), BPI.getEdgeProbability(SplitBB, 0u));
EXPECT_EQ(BranchProbability(1, 2), BPI.getEdgeProbability(SplitBB, 1u));
BasicBlock *BB0 = getBasicBlockByName(*F, "bb0");
ASSERT_EQ(2u, BB0->getTerminator()->getNumSuccessors());
EXPECT_EQ(BB0->getTerminator()->getSuccessor(1),
getBasicBlockByName(*F, "bb2"));
}
TEST(BasicBlockUtils, SplitIndirectBrCriticalEdges) {
LLVMContext C;
std::unique_ptr<Module> M = parseIR(C, R"IR(
define void @crit_edge(i8* %tgt, i1 %cond0, i1 %cond1) {
entry:
indirectbr i8* %tgt, [label %bb0, label %bb1, label %bb2]
bb0:
br i1 %cond0, label %bb1, label %bb2
bb1:
%p = phi i32 [0, %bb0], [0, %entry]
br i1 %cond1, label %bb3, label %bb4
bb2:
ret void
bb3:
ret void
bb4:
ret void
}
)IR");
Function *F = M->getFunction("crit_edge");
DominatorTree DT(*F);
LoopInfo LI(DT);
BranchProbabilityInfo BPI(*F, LI);
BlockFrequencyInfo BFI(*F, BPI, LI);
ASSERT_TRUE(SplitIndirectBrCriticalEdges(*F, false,
&BPI, &BFI));
BasicBlock *BB1 = getBasicBlockByName(*F, "bb1");
BasicBlock *SplitBB = BB1->getTerminator()->getSuccessor(0);
ASSERT_EQ(2u, SplitBB->getTerminator()->getNumSuccessors());
EXPECT_EQ(BranchProbability(1, 2), BPI.getEdgeProbability(SplitBB, 0u));
EXPECT_EQ(BranchProbability(1, 2), BPI.getEdgeProbability(SplitBB, 1u));
BasicBlock *BB0 = getBasicBlockByName(*F, "bb0");
ASSERT_EQ(2u, BB0->getTerminator()->getNumSuccessors());
BasicBlock *BB2Clone = BB0->getTerminator()->getSuccessor(1);
BasicBlock *BB2 = getBasicBlockByName(*F, "bb2");
EXPECT_NE(BB2Clone, BB2);
ASSERT_EQ(1u, BB2->getTerminator()->getNumSuccessors());
ASSERT_EQ(1u, BB2Clone->getTerminator()->getNumSuccessors());
EXPECT_EQ(BB2->getTerminator()->getSuccessor(0),
BB2Clone->getTerminator()->getSuccessor(0));
}
TEST(BasicBlockUtils, SetEdgeProbability) {
LLVMContext C;
std::unique_ptr<Module> M = parseIR(C, R"IR(
define void @edge_probability(i32 %0) {
entry:
switch i32 %0, label %LD [
i32 700, label %L0
i32 701, label %L1
i32 702, label %L2
i32 703, label %L3
i32 704, label %L4
i32 705, label %L5
i32 706, label %L6
i32 707, label %L7
i32 708, label %L8
i32 709, label %L9
i32 710, label %L10
i32 711, label %L11
i32 712, label %L12
i32 713, label %L13
i32 714, label %L14
i32 715, label %L15
i32 716, label %L16
i32 717, label %L17
i32 718, label %L18
i32 719, label %L19
], !prof !{!"branch_weights", i32 1, i32 1, i32 1, i32 1, i32 1, i32 451, i32 1,
i32 12, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1,
i32 1, i32 1, i32 1, i32 1, i32 1}
LD:
unreachable
L0:
ret void
L1:
ret void
L2:
ret void
L3:
ret void
L4:
ret void
L5:
ret void
L6:
ret void
L7:
ret void
L8:
ret void
L9:
ret void
L10:
ret void
L11:
ret void
L12:
ret void
L13:
ret void
L14:
ret void
L15:
ret void
L16:
ret void
L17:
ret void
L18:
ret void
L19:
ret void
}
)IR");
Function *F = M->getFunction("edge_probability");
DominatorTree DT(*F);
LoopInfo LI(DT);
BranchProbabilityInfo BPI(*F, LI);
const BasicBlock *EntryBB = getBasicBlockByName(*F, "entry");
const BasicBlock *UnreachableBB = getBasicBlockByName(*F, "LD");
EXPECT_EQ(BranchProbability::getRaw(1),
BPI.getEdgeProbability(EntryBB, UnreachableBB));
}