1 //===- llvm/Testing/Support/CFGBuilder.cpp --------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "CFGBuilder.h"
11 #include "llvm/IR/CFG.h"
12 #include "llvm/IR/IRBuilder.h"
13 #include "llvm/IR/LLVMContext.h"
14 #include "llvm/IR/Module.h"
15 #include "llvm/Support/Debug.h"
16 #include "llvm/Support/raw_ostream.h"
17 #include "gtest/gtest.h"
19 #define DEBUG_TYPE "cfg-builder"
23 CFGHolder::CFGHolder(StringRef ModuleName
, StringRef FunctionName
)
24 : Context(std::make_unique
<LLVMContext
>()),
25 M(std::make_unique
<Module
>(ModuleName
, *Context
)) {
26 FunctionType
*FTy
= FunctionType::get(Type::getVoidTy(*Context
), {}, false);
27 F
= Function::Create(FTy
, Function::ExternalLinkage
, FunctionName
, M
.get());
29 CFGHolder::~CFGHolder() = default;
31 CFGBuilder::CFGBuilder(Function
*F
, const std::vector
<Arc
> &InitialArcs
,
32 std::vector
<Update
> Updates
)
33 : F(F
), Updates(std::move(Updates
)) {
35 buildCFG(InitialArcs
);
38 static void ConnectBlocks(BasicBlock
*From
, BasicBlock
*To
) {
39 LLVM_DEBUG(dbgs() << "Creating BB arc " << From
->getName() << " -> "
40 << To
->getName() << "\n";
42 auto *IntTy
= IntegerType::get(From
->getContext(), 32);
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
);
52 SwitchInst
*SI
= cast
<SwitchInst
>(From
->getTerminator());
53 const auto Last
= SI
->getNumCases();
55 auto *IntVal
= ConstantInt::get(IntTy
, Last
);
56 SI
->addCase(IntVal
, To
);
59 static void DisconnectBlocks(BasicBlock
*From
, BasicBlock
*To
) {
60 LLVM_DEBUG(dbgs() << "Deleting BB arc " << From
->getName() << " -> "
61 << To
->getName() << "\n";
63 SwitchInst
*SI
= cast
<SwitchInst
>(From
->getTerminator());
65 if (SI
->getNumCases() == 0) {
66 SI
->eraseFromParent();
67 IRBuilder
<> IRB(From
);
68 IRB
.CreateUnreachable();
72 if (SI
->getDefaultDest() == To
) {
73 auto FirstC
= SI
->case_begin();
74 SI
->setDefaultDest(FirstC
->getCaseSuccessor());
75 SI
->removeCase(FirstC
);
79 for (auto CIt
= SI
->case_begin(); CIt
!= SI
->case_end(); ++CIt
)
80 if (CIt
->getCaseSuccessor() == To
) {
86 BasicBlock
*CFGBuilder::getOrAddBlock(StringRef BlockName
) {
87 auto BIt
= NameToBlock
.find(BlockName
);
88 if (BIt
!= NameToBlock
.end())
91 auto *BB
= BasicBlock::Create(F
->getParent()->getContext(), BlockName
, F
);
93 IRB
.CreateUnreachable();
94 NameToBlock
[BlockName
] = BB
;
98 bool CFGBuilder::connect(const Arc
&A
) {
99 BasicBlock
*From
= getOrAddBlock(A
.From
);
100 BasicBlock
*To
= getOrAddBlock(A
.To
);
101 if (Arcs
.count(A
) != 0)
105 ConnectBlocks(From
, To
);
109 bool 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)
115 BasicBlock
*From
= getOrAddBlock(A
.From
);
116 BasicBlock
*To
= getOrAddBlock(A
.To
);
118 DisconnectBlocks(From
, To
);
122 void CFGBuilder::buildCFG(const std::vector
<Arc
> &NewArcs
) {
123 for (const auto &A
: NewArcs
) {
124 const bool Connected
= connect(A
);
130 std::optional
<CFGBuilder::Update
> CFGBuilder::getNextUpdate() const {
131 if (UpdateIdx
== Updates
.size())
133 return Updates
[UpdateIdx
];
136 std::optional
<CFGBuilder::Update
> CFGBuilder::applyUpdate() {
137 if (UpdateIdx
== Updates
.size())
139 Update NextUpdate
= Updates
[UpdateIdx
++];
140 if (NextUpdate
.Action
== ActionKind::Insert
)
141 connect(NextUpdate
.Edge
);
143 disconnect(NextUpdate
.Edge
);
148 void CFGBuilder::dump(raw_ostream
&OS
) const {
151 for (const auto &A
: Arcs
)
152 OS
<< " " << i
++ << ":\t" << A
.From
<< " -> " << A
.To
<< "\n";
156 for (const auto &U
: Updates
) {
157 OS
<< (i
+ 1 == UpdateIdx
? "->" : " ") << i
158 << ((U
.Action
== ActionKind::Insert
) ? "\tIns " : "\tDel ")
159 << U
.Edge
.From
<< " -> " << U
.Edge
.To
<< "\n";
164 //---- CFGBuilder tests ---------------------------------------------------===//
166 TEST(CFGBuilder
, Construction
) {
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
, {});
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()));
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.
185 TEST(CFGBuilder
, Insertions
) {
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();
194 CFGBuilder
B(Holder
.F
, {}, Updates
);
197 while (B
.applyUpdate())
199 EXPECT_EQ(i
, NumUpdates
);
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()));
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.
213 TEST(CFGBuilder
, Deletions
) {
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"}},
221 const size_t NumUpdates
= Updates
.size();
223 CFGBuilder
B(Holder
.F
, Arcs
, Updates
);
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()));
230 auto UpdateC
= B
.applyUpdate();
232 EXPECT_TRUE(UpdateC
);
233 EXPECT_EQ(UpdateC
->Action
, CFGBuilder::ActionKind::Delete
);
234 EXPECT_EQ(UpdateC
->Edge
.From
, "c");
235 EXPECT_EQ(UpdateC
->Edge
.To
, "d");
236 EXPECT_TRUE(isa
<UnreachableInst
>(B
.getOrAddBlock("c")->getTerminator()));
239 while (B
.applyUpdate())
241 EXPECT_EQ(i
, NumUpdates
);
243 EXPECT_TRUE(isa
<SwitchInst
>(B
.getOrAddBlock("a")->getTerminator()));
244 EXPECT_TRUE(isa
<UnreachableInst
>(B
.getOrAddBlock("entry")->getTerminator()));
247 TEST(CFGBuilder
, Rebuild
) {
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"}},
257 const size_t NumUpdates
= Updates
.size();
259 CFGBuilder
B(Holder
.F
, Arcs
, Updates
);
261 while (B
.applyUpdate())
263 EXPECT_EQ(i
, NumUpdates
);
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()));
271 static_assert(std::is_trivially_copyable_v
<succ_iterator
>,
272 "trivially copyable");
273 static_assert(std::is_trivially_copyable_v
<const_succ_iterator
>,
274 "trivially copyable");
275 static_assert(std::is_trivially_copyable_v
<succ_range
>, "trivially copyable");
276 static_assert(std::is_trivially_copyable_v
<const_succ_range
>,
277 "trivially copyable");