llvm-project
277 строк · 9.1 Кб
1//===- llvm/Testing/Support/CFGBuilder.cpp --------------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9#include "CFGBuilder.h"10
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"18
19#define DEBUG_TYPE "cfg-builder"20
21using namespace llvm;22
23CFGHolder::CFGHolder(StringRef ModuleName, StringRef FunctionName)24: Context(std::make_unique<LLVMContext>()),25M(std::make_unique<Module>(ModuleName, *Context)) {26FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Context), {}, false);27F = Function::Create(FTy, Function::ExternalLinkage, FunctionName, M.get());28}
29CFGHolder::~CFGHolder() = default;30
31CFGBuilder::CFGBuilder(Function *F, const std::vector<Arc> &InitialArcs,32std::vector<Update> Updates)33: F(F), Updates(std::move(Updates)) {34assert(F);35buildCFG(InitialArcs);36}
37
38static void ConnectBlocks(BasicBlock *From, BasicBlock *To) {39LLVM_DEBUG(dbgs() << "Creating BB arc " << From->getName() << " -> "40<< To->getName() << "\n";41dbgs().flush());42auto *IntTy = IntegerType::get(From->getContext(), 32);43
44if (isa<UnreachableInst>(From->getTerminator()))45From->getTerminator()->eraseFromParent();46if (!From->getTerminator()) {47IRBuilder<> IRB(From);48IRB.CreateSwitch(ConstantInt::get(IntTy, 0), To);49return;50}51
52SwitchInst *SI = cast<SwitchInst>(From->getTerminator());53const auto Last = SI->getNumCases();54
55auto *IntVal = ConstantInt::get(IntTy, Last);56SI->addCase(IntVal, To);57}
58
59static void DisconnectBlocks(BasicBlock *From, BasicBlock *To) {60LLVM_DEBUG(dbgs() << "Deleting BB arc " << From->getName() << " -> "61<< To->getName() << "\n";62dbgs().flush());63SwitchInst *SI = cast<SwitchInst>(From->getTerminator());64
65if (SI->getNumCases() == 0) {66SI->eraseFromParent();67IRBuilder<> IRB(From);68IRB.CreateUnreachable();69return;70}71
72if (SI->getDefaultDest() == To) {73auto FirstC = SI->case_begin();74SI->setDefaultDest(FirstC->getCaseSuccessor());75SI->removeCase(FirstC);76return;77}78
79for (auto CIt = SI->case_begin(); CIt != SI->case_end(); ++CIt)80if (CIt->getCaseSuccessor() == To) {81SI->removeCase(CIt);82return;83}84}
85
86BasicBlock *CFGBuilder::getOrAddBlock(StringRef BlockName) {87auto BIt = NameToBlock.find(BlockName);88if (BIt != NameToBlock.end())89return BIt->second;90
91auto *BB = BasicBlock::Create(F->getParent()->getContext(), BlockName, F);92IRBuilder<> IRB(BB);93IRB.CreateUnreachable();94NameToBlock[BlockName] = BB;95return BB;96}
97
98bool CFGBuilder::connect(const Arc &A) {99BasicBlock *From = getOrAddBlock(A.From);100BasicBlock *To = getOrAddBlock(A.To);101if (Arcs.count(A) != 0)102return false;103
104Arcs.insert(A);105ConnectBlocks(From, To);106return true;107}
108
109bool CFGBuilder::disconnect(const Arc &A) {110assert(NameToBlock.count(A.From) != 0 && "No block to disconnect (From)");111assert(NameToBlock.count(A.To) != 0 && "No block to disconnect (To)");112if (Arcs.count(A) == 0)113return false;114
115BasicBlock *From = getOrAddBlock(A.From);116BasicBlock *To = getOrAddBlock(A.To);117Arcs.erase(A);118DisconnectBlocks(From, To);119return true;120}
121
122void CFGBuilder::buildCFG(const std::vector<Arc> &NewArcs) {123for (const auto &A : NewArcs) {124const bool Connected = connect(A);125(void)Connected;126assert(Connected);127}128}
129
130std::optional<CFGBuilder::Update> CFGBuilder::getNextUpdate() const {131if (UpdateIdx == Updates.size())132return std::nullopt;133return Updates[UpdateIdx];134}
135
136std::optional<CFGBuilder::Update> CFGBuilder::applyUpdate() {137if (UpdateIdx == Updates.size())138return std::nullopt;139Update NextUpdate = Updates[UpdateIdx++];140if (NextUpdate.Action == ActionKind::Insert)141connect(NextUpdate.Edge);142else143disconnect(NextUpdate.Edge);144
145return NextUpdate;146}
147
148void CFGBuilder::dump(raw_ostream &OS) const {149OS << "Arcs:\n";150size_t i = 0;151for (const auto &A : Arcs)152OS << " " << i++ << ":\t" << A.From << " -> " << A.To << "\n";153
154OS << "Updates:\n";155i = 0;156for (const auto &U : Updates) {157OS << (i + 1 == UpdateIdx ? "->" : " ") << i158<< ((U.Action == ActionKind::Insert) ? "\tIns " : "\tDel ")159<< U.Edge.From << " -> " << U.Edge.To << "\n";160++i;161}162}
163
164//---- CFGBuilder tests ---------------------------------------------------===//
165
166TEST(CFGBuilder, Construction) {167CFGHolder Holder;168std::vector<CFGBuilder::Arc> Arcs = {{"entry", "a"}, {"a", "b"}, {"a", "c"},169{"c", "d"}, {"d", "b"}, {"d", "e"},170{"d", "f"}, {"e", "f"}};171CFGBuilder B(Holder.F, Arcs, {});172
173EXPECT_TRUE(B.getOrAddBlock("entry") == &Holder.F->getEntryBlock());174EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("entry")->getTerminator()));175EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));176EXPECT_TRUE(isa<UnreachableInst>(B.getOrAddBlock("b")->getTerminator()));177EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("d")->getTerminator()));178
179auto *DSwitch = cast<SwitchInst>(B.getOrAddBlock("d")->getTerminator());180// d has 3 successors, but one of them if going to be a default case181EXPECT_EQ(DSwitch->getNumCases(), 2U);182EXPECT_FALSE(B.getNextUpdate()); // No updates to apply.183}
184
185TEST(CFGBuilder, Insertions) {186CFGHolder Holder;187const auto Insert = CFGBuilder::ActionKind::Insert;188std::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"}}};192const size_t NumUpdates = Updates.size();193
194CFGBuilder B(Holder.F, {}, Updates);195
196size_t i = 0;197while (B.applyUpdate())198++i;199EXPECT_EQ(i, NumUpdates);200
201EXPECT_TRUE(B.getOrAddBlock("entry") == &Holder.F->getEntryBlock());202EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("entry")->getTerminator()));203EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));204EXPECT_TRUE(isa<UnreachableInst>(B.getOrAddBlock("b")->getTerminator()));205EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("d")->getTerminator()));206
207auto *DSwitch = cast<SwitchInst>(B.getOrAddBlock("d")->getTerminator());208// d has 3 successors, but one of them if going to be a default case209EXPECT_EQ(DSwitch->getNumCases(), 2U);210EXPECT_FALSE(B.getNextUpdate()); // No updates to apply.211}
212
213TEST(CFGBuilder, Deletions) {214CFGHolder Holder;215std::vector<CFGBuilder::Arc> Arcs = {216{"entry", "a"}, {"a", "b"}, {"a", "c"}, {"c", "d"}, {"d", "b"}};217const auto Delete = CFGBuilder::ActionKind::Delete;218std::vector<CFGBuilder::Update> Updates = {219{Delete, {"c", "d"}}, {Delete, {"a", "c"}}, {Delete, {"entry", "a"}},220};221const size_t NumUpdates = Updates.size();222
223CFGBuilder B(Holder.F, Arcs, Updates);224
225EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("entry")->getTerminator()));226EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));227EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("c")->getTerminator()));228EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("d")->getTerminator()));229
230auto UpdateC = B.applyUpdate();231
232EXPECT_TRUE(UpdateC);233EXPECT_EQ(UpdateC->Action, CFGBuilder::ActionKind::Delete);234EXPECT_EQ(UpdateC->Edge.From, "c");235EXPECT_EQ(UpdateC->Edge.To, "d");236EXPECT_TRUE(isa<UnreachableInst>(B.getOrAddBlock("c")->getTerminator()));237
238size_t i = 1;239while (B.applyUpdate())240++i;241EXPECT_EQ(i, NumUpdates);242
243EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));244EXPECT_TRUE(isa<UnreachableInst>(B.getOrAddBlock("entry")->getTerminator()));245}
246
247TEST(CFGBuilder, Rebuild) {248CFGHolder Holder;249std::vector<CFGBuilder::Arc> Arcs = {250{"entry", "a"}, {"a", "b"}, {"a", "c"}, {"c", "d"}, {"d", "b"}};251const auto Insert = CFGBuilder::ActionKind::Insert;252const auto Delete = CFGBuilder::ActionKind::Delete;253std::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};257const size_t NumUpdates = Updates.size();258
259CFGBuilder B(Holder.F, Arcs, Updates);260size_t i = 0;261while (B.applyUpdate())262++i;263EXPECT_EQ(i, NumUpdates);264
265EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("entry")->getTerminator()));266EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));267EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("c")->getTerminator()));268EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("d")->getTerminator()));269}
270
271static_assert(std::is_trivially_copyable_v<succ_iterator>,272"trivially copyable");273static_assert(std::is_trivially_copyable_v<const_succ_iterator>,274"trivially copyable");275static_assert(std::is_trivially_copyable_v<succ_range>, "trivially copyable");276static_assert(std::is_trivially_copyable_v<const_succ_range>,277"trivially copyable");278