270 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			270 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- llvm/Testing/Support/CFGBuilder.cpp --------------------------------===//
 | |
| //
 | |
| //                     The LLVM Compiler Infrastructure
 | |
| //
 | |
| // This file is distributed under the University of Illinois Open Source
 | |
| // License. See LICENSE.TXT for details.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "CFGBuilder.h"
 | |
| 
 | |
| #include "llvm/IR/IRBuilder.h"
 | |
| #include "llvm/IR/LLVMContext.h"
 | |
| #include "llvm/IR/TypeBuilder.h"
 | |
| #include "llvm/Support/Debug.h"
 | |
| #include "llvm/Support/raw_ostream.h"
 | |
| #include "gtest/gtest.h"
 | |
| 
 | |
| #define DEBUG_TYPE "cfg-builder"
 | |
| 
 | |
| using namespace llvm;
 | |
| 
 | |
| CFGHolder::CFGHolder(StringRef ModuleName, StringRef FunctionName)
 | |
|     : Context(llvm::make_unique<LLVMContext>()),
 | |
|       M(llvm::make_unique<Module>(ModuleName, *Context)) {
 | |
|   FunctionType *FTy = TypeBuilder<void(), false>::get(*Context);
 | |
|   F = cast<Function>(M->getOrInsertFunction(FunctionName, FTy));
 | |
| }
 | |
| CFGHolder::~CFGHolder() = default;
 | |
| 
 | |
| CFGBuilder::CFGBuilder(Function *F, const std::vector<Arc> &InitialArcs,
 | |
|                        std::vector<Update> Updates)
 | |
|     : F(F), Updates(std::move(Updates)) {
 | |
|   assert(F);
 | |
|   buildCFG(InitialArcs);
 | |
| }
 | |
| 
 | |
| static void ConnectBlocks(BasicBlock *From, BasicBlock *To) {
 | |
|   DEBUG(dbgs() << "Creating BB arc " << From->getName() << " -> "
 | |
|                << To->getName() << "\n";
 | |
|         dbgs().flush());
 | |
|   auto *IntTy = IntegerType::get(From->getContext(), 32);
 | |
| 
 | |
|   if (isa<UnreachableInst>(From->getTerminator()))
 | |
|     From->getTerminator()->eraseFromParent();
 | |
|   if (!From->getTerminator()) {
 | |
|     IRBuilder<> IRB(From);
 | |
|     IRB.CreateSwitch(ConstantInt::get(IntTy, 0), To);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   SwitchInst *SI = cast<SwitchInst>(From->getTerminator());
 | |
|   const auto Last = SI->getNumCases();
 | |
| 
 | |
|   auto *IntVal = ConstantInt::get(IntTy, Last);
 | |
|   SI->addCase(IntVal, To);
 | |
| }
 | |
| 
 | |
| static void DisconnectBlocks(BasicBlock *From, BasicBlock *To) {
 | |
|   DEBUG(dbgs() << "Deleting BB arc " << From->getName() << " -> "
 | |
|                << To->getName() << "\n";
 | |
|         dbgs().flush());
 | |
|   SwitchInst *SI = cast<SwitchInst>(From->getTerminator());
 | |
| 
 | |
|   if (SI->getNumCases() == 0) {
 | |
|     SI->eraseFromParent();
 | |
|     IRBuilder<> IRB(From);
 | |
|     IRB.CreateUnreachable();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (SI->getDefaultDest() == To) {
 | |
|     auto FirstC = SI->case_begin();
 | |
|     SI->setDefaultDest(FirstC->getCaseSuccessor());
 | |
|     SI->removeCase(FirstC);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   for (auto CIt = SI->case_begin(); CIt != SI->case_end(); ++CIt)
 | |
|     if (CIt->getCaseSuccessor() == To) {
 | |
|       SI->removeCase(CIt);
 | |
|       return;
 | |
|     }
 | |
| }
 | |
| 
 | |
| BasicBlock *CFGBuilder::getOrAddBlock(StringRef BlockName) {
 | |
|   auto BIt = NameToBlock.find(BlockName);
 | |
|   if (BIt != NameToBlock.end())
 | |
|     return BIt->second;
 | |
| 
 | |
|   auto *BB = BasicBlock::Create(F->getParent()->getContext(), BlockName, F);
 | |
|   IRBuilder<> IRB(BB);
 | |
|   IRB.CreateUnreachable();
 | |
|   NameToBlock[BlockName] = BB;
 | |
|   return BB;
 | |
| }
 | |
| 
 | |
| bool CFGBuilder::connect(const Arc &A) {
 | |
|   BasicBlock *From = getOrAddBlock(A.From);
 | |
|   BasicBlock *To = getOrAddBlock(A.To);
 | |
|   if (Arcs.count(A) != 0)
 | |
|     return false;
 | |
| 
 | |
|   Arcs.insert(A);
 | |
|   ConnectBlocks(From, To);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool CFGBuilder::disconnect(const Arc &A) {
 | |
|   assert(NameToBlock.count(A.From) != 0 && "No block to disconnect (From)");
 | |
|   assert(NameToBlock.count(A.To) != 0 && "No block to disconnect (To)");
 | |
|   if (Arcs.count(A) == 0)
 | |
|     return false;
 | |
| 
 | |
|   BasicBlock *From = getOrAddBlock(A.From);
 | |
|   BasicBlock *To = getOrAddBlock(A.To);
 | |
|   Arcs.erase(A);
 | |
|   DisconnectBlocks(From, To);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void CFGBuilder::buildCFG(const std::vector<Arc> &NewArcs) {
 | |
|   for (const auto &A : NewArcs) {
 | |
|     const bool Connected = connect(A);
 | |
|     (void)Connected;
 | |
|     assert(Connected);
 | |
|   }
 | |
| }
 | |
| 
 | |
| Optional<CFGBuilder::Update> CFGBuilder::getNextUpdate() const {
 | |
|   if (UpdateIdx == Updates.size())
 | |
|     return None;
 | |
|   return Updates[UpdateIdx];
 | |
| }
 | |
| 
 | |
| Optional<CFGBuilder::Update> CFGBuilder::applyUpdate() {
 | |
|   if (UpdateIdx == Updates.size())
 | |
|     return None;
 | |
|   Update NextUpdate = Updates[UpdateIdx++];
 | |
|   if (NextUpdate.Action == ActionKind::Insert)
 | |
|     connect(NextUpdate.Edge);
 | |
|   else
 | |
|     disconnect(NextUpdate.Edge);
 | |
| 
 | |
|   return NextUpdate;
 | |
| }
 | |
| 
 | |
| void CFGBuilder::dump(raw_ostream &OS) const {
 | |
|   OS << "Arcs:\n";
 | |
|   size_t i = 0;
 | |
|   for (const auto &A : Arcs)
 | |
|     OS << "  " << i++ << ":\t" << A.From << " -> " << A.To << "\n";
 | |
| 
 | |
|   OS << "Updates:\n";
 | |
|   i = 0;
 | |
|   for (const auto &U : Updates) {
 | |
|     OS << (i + 1 == UpdateIdx ? "->" : "  ") << i
 | |
|        << ((U.Action == ActionKind::Insert) ? "\tIns " : "\tDel ")
 | |
|        << U.Edge.From << " -> " << U.Edge.To << "\n";
 | |
|     ++i;
 | |
|   }
 | |
| }
 | |
| 
 | |
| //---- CFGBuilder tests ---------------------------------------------------===//
 | |
| 
 | |
| TEST(CFGBuilder, Construction) {
 | |
|   CFGHolder Holder;
 | |
|   std::vector<CFGBuilder::Arc> Arcs = {{"entry", "a"}, {"a", "b"}, {"a", "c"},
 | |
|                                        {"c", "d"},     {"d", "b"}, {"d", "e"},
 | |
|                                        {"d", "f"},     {"e", "f"}};
 | |
|   CFGBuilder B(Holder.F, Arcs, {});
 | |
| 
 | |
|   EXPECT_TRUE(B.getOrAddBlock("entry") == &Holder.F->getEntryBlock());
 | |
|   EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("entry")->getTerminator()));
 | |
|   EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));
 | |
|   EXPECT_TRUE(isa<UnreachableInst>(B.getOrAddBlock("b")->getTerminator()));
 | |
|   EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("d")->getTerminator()));
 | |
| 
 | |
|   auto *DSwitch = cast<SwitchInst>(B.getOrAddBlock("d")->getTerminator());
 | |
|   // d has 3 successors, but one of them if going to be a default case
 | |
|   EXPECT_EQ(DSwitch->getNumCases(), 2U);
 | |
|   EXPECT_FALSE(B.getNextUpdate()); // No updates to apply.
 | |
| }
 | |
| 
 | |
| TEST(CFGBuilder, Insertions) {
 | |
|   CFGHolder Holder;
 | |
|   const auto Insert = CFGBuilder::ActionKind::Insert;
 | |
|   std::vector<CFGBuilder::Update> Updates = {
 | |
|       {Insert, {"entry", "a"}}, {Insert, {"a", "b"}}, {Insert, {"a", "c"}},
 | |
|       {Insert, {"c", "d"}},     {Insert, {"d", "b"}}, {Insert, {"d", "e"}},
 | |
|       {Insert, {"d", "f"}},     {Insert, {"e", "f"}}};
 | |
|   const size_t NumUpdates = Updates.size();
 | |
| 
 | |
|   CFGBuilder B(Holder.F, {}, Updates);
 | |
| 
 | |
|   size_t i = 0;
 | |
|   while (B.applyUpdate())
 | |
|     ++i;
 | |
|   EXPECT_EQ(i, NumUpdates);
 | |
| 
 | |
|   EXPECT_TRUE(B.getOrAddBlock("entry") == &Holder.F->getEntryBlock());
 | |
|   EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("entry")->getTerminator()));
 | |
|   EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));
 | |
|   EXPECT_TRUE(isa<UnreachableInst>(B.getOrAddBlock("b")->getTerminator()));
 | |
|   EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("d")->getTerminator()));
 | |
| 
 | |
|   auto *DSwitch = cast<SwitchInst>(B.getOrAddBlock("d")->getTerminator());
 | |
|   // d has 3 successors, but one of them if going to be a default case
 | |
|   EXPECT_EQ(DSwitch->getNumCases(), 2U);
 | |
|   EXPECT_FALSE(B.getNextUpdate()); // No updates to apply.
 | |
| }
 | |
| 
 | |
| TEST(CFGBuilder, Deletions) {
 | |
|   CFGHolder Holder;
 | |
|   std::vector<CFGBuilder::Arc> Arcs = {
 | |
|       {"entry", "a"}, {"a", "b"}, {"a", "c"}, {"c", "d"}, {"d", "b"}};
 | |
|   const auto Delete = CFGBuilder::ActionKind::Delete;
 | |
|   std::vector<CFGBuilder::Update> Updates = {
 | |
|       {Delete, {"c", "d"}}, {Delete, {"a", "c"}}, {Delete, {"entry", "a"}},
 | |
|   };
 | |
|   const size_t NumUpdates = Updates.size();
 | |
| 
 | |
|   CFGBuilder B(Holder.F, Arcs, Updates);
 | |
| 
 | |
|   EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("entry")->getTerminator()));
 | |
|   EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));
 | |
|   EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("c")->getTerminator()));
 | |
|   EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("d")->getTerminator()));
 | |
| 
 | |
|   auto UpdateC = B.applyUpdate();
 | |
| 
 | |
|   EXPECT_TRUE(UpdateC);
 | |
|   EXPECT_EQ(UpdateC->Action, CFGBuilder::ActionKind::Delete);
 | |
|   EXPECT_EQ(UpdateC->Edge.From, "c");
 | |
|   EXPECT_EQ(UpdateC->Edge.To, "d");
 | |
|   EXPECT_TRUE(isa<UnreachableInst>(B.getOrAddBlock("c")->getTerminator()));
 | |
| 
 | |
|   size_t i = 1;
 | |
|   while (B.applyUpdate())
 | |
|     ++i;
 | |
|   EXPECT_EQ(i, NumUpdates);
 | |
| 
 | |
|   EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));
 | |
|   EXPECT_TRUE(isa<UnreachableInst>(B.getOrAddBlock("entry")->getTerminator()));
 | |
| }
 | |
| 
 | |
| TEST(CFGBuilder, Rebuild) {
 | |
|   CFGHolder Holder;
 | |
|   std::vector<CFGBuilder::Arc> Arcs = {
 | |
|       {"entry", "a"}, {"a", "b"}, {"a", "c"}, {"c", "d"}, {"d", "b"}};
 | |
|   const auto Insert = CFGBuilder::ActionKind::Insert;
 | |
|   const auto Delete = CFGBuilder::ActionKind::Delete;
 | |
|   std::vector<CFGBuilder::Update> Updates = {
 | |
|       {Delete, {"c", "d"}}, {Delete, {"a", "c"}}, {Delete, {"entry", "a"}},
 | |
|       {Insert, {"c", "d"}}, {Insert, {"a", "c"}}, {Insert, {"entry", "a"}},
 | |
|   };
 | |
|   const size_t NumUpdates = Updates.size();
 | |
| 
 | |
|   CFGBuilder B(Holder.F, Arcs, Updates);
 | |
|   size_t i = 0;
 | |
|   while (B.applyUpdate())
 | |
|     ++i;
 | |
|   EXPECT_EQ(i, NumUpdates);
 | |
| 
 | |
|   EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("entry")->getTerminator()));
 | |
|   EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));
 | |
|   EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("c")->getTerminator()));
 | |
|   EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("d")->getTerminator()));
 | |
| }
 |