blob: 0f9fb8b18cc19e566bb8f11e90aa527e2a49f071 [file] [log] [blame]
Jakub Kuderskieb370ad2017-07-13 21:16:01 +00001//===- llvm/Testing/Support/CFGBuilder.cpp --------------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "CFGBuilder.h"
11
12#include "llvm/IR/IRBuilder.h"
13#include "llvm/IR/LLVMContext.h"
14#include "llvm/IR/TypeBuilder.h"
15#include "llvm/Support/Debug.h"
16#include "llvm/Support/raw_ostream.h"
17#include "gtest/gtest.h"
18
Jakub Kuderskieb370ad2017-07-13 21:16:01 +000019#define DEBUG_TYPE "cfg-builder"
20
21using namespace llvm;
22
23CFGHolder::CFGHolder(StringRef ModuleName, StringRef FunctionName)
24 : Context(llvm::make_unique<LLVMContext>()),
25 M(llvm::make_unique<Module>(ModuleName, *Context)) {
26 FunctionType *FTy = TypeBuilder<void(), false>::get(*Context);
27 F = cast<Function>(M->getOrInsertFunction(FunctionName, FTy));
28}
29CFGHolder::~CFGHolder() = default;
30
Jakub Kuderskieb370ad2017-07-13 21:16:01 +000031CFGBuilder::CFGBuilder(Function *F, const std::vector<Arc> &InitialArcs,
32 std::vector<Update> Updates)
33 : F(F), Updates(std::move(Updates)) {
34 assert(F);
35 buildCFG(InitialArcs);
36}
37
38static void ConnectBlocks(BasicBlock *From, BasicBlock *To) {
Nicola Zaghend34e60c2018-05-14 12:53:11 +000039 LLVM_DEBUG(dbgs() << "Creating BB arc " << From->getName() << " -> "
40 << To->getName() << "\n";
41 dbgs().flush());
Jakub Kuderskieb370ad2017-07-13 21:16:01 +000042 auto *IntTy = IntegerType::get(From->getContext(), 32);
43
44 if (isa<UnreachableInst>(From->getTerminator()))
45 From->getTerminator()->eraseFromParent();
46 if (!From->getTerminator()) {
47 IRBuilder<> IRB(From);
48 IRB.CreateSwitch(ConstantInt::get(IntTy, 0), To);
49 return;
50 }
51
52 SwitchInst *SI = cast<SwitchInst>(From->getTerminator());
53 const auto Last = SI->getNumCases();
54
55 auto *IntVal = ConstantInt::get(IntTy, Last);
56 SI->addCase(IntVal, To);
57}
58
59static void DisconnectBlocks(BasicBlock *From, BasicBlock *To) {
Nicola Zaghend34e60c2018-05-14 12:53:11 +000060 LLVM_DEBUG(dbgs() << "Deleting BB arc " << From->getName() << " -> "
61 << To->getName() << "\n";
62 dbgs().flush());
Jakub Kuderskieb370ad2017-07-13 21:16:01 +000063 SwitchInst *SI = cast<SwitchInst>(From->getTerminator());
64
65 if (SI->getNumCases() == 0) {
66 SI->eraseFromParent();
67 IRBuilder<> IRB(From);
68 IRB.CreateUnreachable();
69 return;
70 }
71
72 if (SI->getDefaultDest() == To) {
73 auto FirstC = SI->case_begin();
74 SI->setDefaultDest(FirstC->getCaseSuccessor());
75 SI->removeCase(FirstC);
76 return;
77 }
78
79 for (auto CIt = SI->case_begin(); CIt != SI->case_end(); ++CIt)
80 if (CIt->getCaseSuccessor() == To) {
81 SI->removeCase(CIt);
82 return;
83 }
84}
85
86BasicBlock *CFGBuilder::getOrAddBlock(StringRef BlockName) {
87 auto BIt = NameToBlock.find(BlockName);
88 if (BIt != NameToBlock.end())
89 return BIt->second;
90
91 auto *BB = BasicBlock::Create(F->getParent()->getContext(), BlockName, F);
92 IRBuilder<> IRB(BB);
93 IRB.CreateUnreachable();
94 NameToBlock[BlockName] = BB;
95 return BB;
96}
97
98bool CFGBuilder::connect(const Arc &A) {
99 BasicBlock *From = getOrAddBlock(A.From);
100 BasicBlock *To = getOrAddBlock(A.To);
101 if (Arcs.count(A) != 0)
102 return false;
103
104 Arcs.insert(A);
105 ConnectBlocks(From, To);
106 return true;
107}
108
109bool CFGBuilder::disconnect(const Arc &A) {
110 assert(NameToBlock.count(A.From) != 0 && "No block to disconnect (From)");
111 assert(NameToBlock.count(A.To) != 0 && "No block to disconnect (To)");
112 if (Arcs.count(A) == 0)
113 return false;
114
115 BasicBlock *From = getOrAddBlock(A.From);
116 BasicBlock *To = getOrAddBlock(A.To);
117 Arcs.erase(A);
118 DisconnectBlocks(From, To);
119 return true;
120}
121
122void CFGBuilder::buildCFG(const std::vector<Arc> &NewArcs) {
123 for (const auto &A : NewArcs) {
124 const bool Connected = connect(A);
125 (void)Connected;
126 assert(Connected);
127 }
128}
129
130Optional<CFGBuilder::Update> CFGBuilder::getNextUpdate() const {
131 if (UpdateIdx == Updates.size())
132 return None;
133 return Updates[UpdateIdx];
134}
135
136Optional<CFGBuilder::Update> CFGBuilder::applyUpdate() {
137 if (UpdateIdx == Updates.size())
138 return None;
139 Update NextUpdate = Updates[UpdateIdx++];
140 if (NextUpdate.Action == ActionKind::Insert)
Jakub Kuderskid5294672017-07-13 21:52:56 +0000141 connect(NextUpdate.Edge);
Jakub Kuderskieb370ad2017-07-13 21:16:01 +0000142 else
Jakub Kuderskid5294672017-07-13 21:52:56 +0000143 disconnect(NextUpdate.Edge);
Jakub Kuderskieb370ad2017-07-13 21:16:01 +0000144
145 return NextUpdate;
146}
147
148void CFGBuilder::dump(raw_ostream &OS) const {
149 OS << "Arcs:\n";
150 size_t i = 0;
151 for (const auto &A : Arcs)
152 OS << " " << i++ << ":\t" << A.From << " -> " << A.To << "\n";
153
154 OS << "Updates:\n";
155 i = 0;
156 for (const auto &U : Updates) {
157 OS << (i + 1 == UpdateIdx ? "->" : " ") << i
Jakub Kuderskid5294672017-07-13 21:52:56 +0000158 << ((U.Action == ActionKind::Insert) ? "\tIns " : "\tDel ")
159 << U.Edge.From << " -> " << U.Edge.To << "\n";
Jakub Kuderskieb370ad2017-07-13 21:16:01 +0000160 ++i;
161 }
162}
163
164//---- CFGBuilder tests ---------------------------------------------------===//
165
166TEST(CFGBuilder, Construction) {
167 CFGHolder Holder;
168 std::vector<CFGBuilder::Arc> Arcs = {{"entry", "a"}, {"a", "b"}, {"a", "c"},
169 {"c", "d"}, {"d", "b"}, {"d", "e"},
170 {"d", "f"}, {"e", "f"}};
171 CFGBuilder B(Holder.F, Arcs, {});
172
173 EXPECT_TRUE(B.getOrAddBlock("entry") == &Holder.F->getEntryBlock());
174 EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("entry")->getTerminator()));
175 EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));
176 EXPECT_TRUE(isa<UnreachableInst>(B.getOrAddBlock("b")->getTerminator()));
177 EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("d")->getTerminator()));
178
179 auto *DSwitch = cast<SwitchInst>(B.getOrAddBlock("d")->getTerminator());
180 // d has 3 successors, but one of them if going to be a default case
181 EXPECT_EQ(DSwitch->getNumCases(), 2U);
182 EXPECT_FALSE(B.getNextUpdate()); // No updates to apply.
183}
184
185TEST(CFGBuilder, Insertions) {
186 CFGHolder Holder;
187 const auto Insert = CFGBuilder::ActionKind::Insert;
188 std::vector<CFGBuilder::Update> Updates = {
189 {Insert, {"entry", "a"}}, {Insert, {"a", "b"}}, {Insert, {"a", "c"}},
190 {Insert, {"c", "d"}}, {Insert, {"d", "b"}}, {Insert, {"d", "e"}},
191 {Insert, {"d", "f"}}, {Insert, {"e", "f"}}};
192 const size_t NumUpdates = Updates.size();
193
194 CFGBuilder B(Holder.F, {}, Updates);
195
196 size_t i = 0;
197 while (B.applyUpdate())
198 ++i;
199 EXPECT_EQ(i, NumUpdates);
200
201 EXPECT_TRUE(B.getOrAddBlock("entry") == &Holder.F->getEntryBlock());
202 EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("entry")->getTerminator()));
203 EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));
204 EXPECT_TRUE(isa<UnreachableInst>(B.getOrAddBlock("b")->getTerminator()));
205 EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("d")->getTerminator()));
206
207 auto *DSwitch = cast<SwitchInst>(B.getOrAddBlock("d")->getTerminator());
208 // d has 3 successors, but one of them if going to be a default case
209 EXPECT_EQ(DSwitch->getNumCases(), 2U);
210 EXPECT_FALSE(B.getNextUpdate()); // No updates to apply.
211}
212
213TEST(CFGBuilder, Deletions) {
214 CFGHolder Holder;
215 std::vector<CFGBuilder::Arc> Arcs = {
216 {"entry", "a"}, {"a", "b"}, {"a", "c"}, {"c", "d"}, {"d", "b"}};
217 const auto Delete = CFGBuilder::ActionKind::Delete;
218 std::vector<CFGBuilder::Update> Updates = {
219 {Delete, {"c", "d"}}, {Delete, {"a", "c"}}, {Delete, {"entry", "a"}},
220 };
221 const size_t NumUpdates = Updates.size();
222
223 CFGBuilder B(Holder.F, Arcs, Updates);
224
225 EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("entry")->getTerminator()));
226 EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));
227 EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("c")->getTerminator()));
228 EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("d")->getTerminator()));
229
230 auto UpdateC = B.applyUpdate();
231
232 EXPECT_TRUE(UpdateC);
233 EXPECT_EQ(UpdateC->Action, CFGBuilder::ActionKind::Delete);
Jakub Kuderskid5294672017-07-13 21:52:56 +0000234 EXPECT_EQ(UpdateC->Edge.From, "c");
235 EXPECT_EQ(UpdateC->Edge.To, "d");
Jakub Kuderskieb370ad2017-07-13 21:16:01 +0000236 EXPECT_TRUE(isa<UnreachableInst>(B.getOrAddBlock("c")->getTerminator()));
237
238 size_t i = 1;
239 while (B.applyUpdate())
240 ++i;
241 EXPECT_EQ(i, NumUpdates);
242
243 EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));
244 EXPECT_TRUE(isa<UnreachableInst>(B.getOrAddBlock("entry")->getTerminator()));
245}
246
247TEST(CFGBuilder, Rebuild) {
248 CFGHolder Holder;
249 std::vector<CFGBuilder::Arc> Arcs = {
250 {"entry", "a"}, {"a", "b"}, {"a", "c"}, {"c", "d"}, {"d", "b"}};
251 const auto Insert = CFGBuilder::ActionKind::Insert;
252 const auto Delete = CFGBuilder::ActionKind::Delete;
253 std::vector<CFGBuilder::Update> Updates = {
254 {Delete, {"c", "d"}}, {Delete, {"a", "c"}}, {Delete, {"entry", "a"}},
255 {Insert, {"c", "d"}}, {Insert, {"a", "c"}}, {Insert, {"entry", "a"}},
256 };
257 const size_t NumUpdates = Updates.size();
258
259 CFGBuilder B(Holder.F, Arcs, Updates);
260 size_t i = 0;
261 while (B.applyUpdate())
262 ++i;
263 EXPECT_EQ(i, NumUpdates);
264
265 EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("entry")->getTerminator()));
266 EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));
267 EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("c")->getTerminator()));
268 EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("d")->getTerminator()));
269}