|  | //===- CFGTest.cpp - CFG tests --------------------------------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "llvm/Analysis/CFG.h" | 
|  | #include "llvm/ADT/SmallPtrSet.h" | 
|  | #include "llvm/Analysis/LoopInfo.h" | 
|  | #include "llvm/AsmParser/Parser.h" | 
|  | #include "llvm/IR/Dominators.h" | 
|  | #include "llvm/IR/Function.h" | 
|  | #include "llvm/IR/InstIterator.h" | 
|  | #include "llvm/IR/LLVMContext.h" | 
|  | #include "llvm/IR/LegacyPassManager.h" | 
|  | #include "llvm/IR/Module.h" | 
|  | #include "llvm/InitializePasses.h" | 
|  | #include "llvm/Support/ErrorHandling.h" | 
|  | #include "llvm/Support/SourceMgr.h" | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // This fixture assists in running the isPotentiallyReachable utility four ways | 
|  | // and ensuring it produces the correct answer each time. | 
|  | class IsPotentiallyReachableTest : public testing::Test { | 
|  | protected: | 
|  | void ParseAssembly(const char *Assembly) { | 
|  | SMDiagnostic Error; | 
|  | M = parseAssemblyString(Assembly, Error, Context); | 
|  |  | 
|  | std::string errMsg; | 
|  | raw_string_ostream os(errMsg); | 
|  | Error.print("", os); | 
|  |  | 
|  | // A failure here means that the test itself is buggy. | 
|  | if (!M) | 
|  | report_fatal_error(errMsg.c_str()); | 
|  |  | 
|  | Function *F = M->getFunction("test"); | 
|  | if (F == nullptr) | 
|  | report_fatal_error("Test must have a function named @test"); | 
|  |  | 
|  | A = B = nullptr; | 
|  | for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { | 
|  | if (I->hasName()) { | 
|  | if (I->getName() == "A") | 
|  | A = &*I; | 
|  | else if (I->getName() == "B") | 
|  | B = &*I; | 
|  | } | 
|  | } | 
|  | if (A == nullptr) | 
|  | report_fatal_error("@test must have an instruction %A"); | 
|  | if (B == nullptr) | 
|  | report_fatal_error("@test must have an instruction %B"); | 
|  |  | 
|  | assert(ExclusionSet.empty()); | 
|  | for (auto I = F->begin(), E = F->end(); I != E; ++I) { | 
|  | if (I->hasName() && I->getName().starts_with("excluded")) | 
|  | ExclusionSet.insert(&*I); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ExpectPath(bool ExpectedResult) { | 
|  | static char ID; | 
|  | class IsPotentiallyReachableTestPass : public FunctionPass { | 
|  | public: | 
|  | IsPotentiallyReachableTestPass(bool ExpectedResult, Instruction *A, | 
|  | Instruction *B, | 
|  | SmallPtrSet<BasicBlock *, 4> ExclusionSet) | 
|  | : FunctionPass(ID), ExpectedResult(ExpectedResult), A(A), B(B), | 
|  | ExclusionSet(ExclusionSet) {} | 
|  |  | 
|  | static int initialize() { | 
|  | PassInfo *PI = new PassInfo("isPotentiallyReachable testing pass", "", | 
|  | &ID, nullptr, true, true); | 
|  | PassRegistry::getPassRegistry()->registerPass(*PI, true); | 
|  | initializeLoopInfoWrapperPassPass(*PassRegistry::getPassRegistry()); | 
|  | initializeDominatorTreeWrapperPassPass( | 
|  | *PassRegistry::getPassRegistry()); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void getAnalysisUsage(AnalysisUsage &AU) const override { | 
|  | AU.setPreservesAll(); | 
|  | AU.addRequired<LoopInfoWrapperPass>(); | 
|  | AU.addRequired<DominatorTreeWrapperPass>(); | 
|  | } | 
|  |  | 
|  | bool runOnFunction(Function &F) override { | 
|  | if (!F.hasName() || F.getName() != "test") | 
|  | return false; | 
|  |  | 
|  | LoopInfo *LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo(); | 
|  | DominatorTree *DT = | 
|  | &getAnalysis<DominatorTreeWrapperPass>().getDomTree(); | 
|  | EXPECT_EQ(isPotentiallyReachable(A, B, &ExclusionSet, nullptr, nullptr), | 
|  | ExpectedResult); | 
|  | EXPECT_EQ(isPotentiallyReachable(A, B, &ExclusionSet, DT, nullptr), | 
|  | ExpectedResult); | 
|  | EXPECT_EQ(isPotentiallyReachable(A, B, &ExclusionSet, nullptr, LI), | 
|  | ExpectedResult); | 
|  | EXPECT_EQ(isPotentiallyReachable(A, B, &ExclusionSet, DT, LI), | 
|  | ExpectedResult); | 
|  | return false; | 
|  | } | 
|  | bool ExpectedResult; | 
|  | Instruction *A, *B; | 
|  | SmallPtrSet<BasicBlock *, 4> ExclusionSet; | 
|  | }; | 
|  |  | 
|  | static int initialize = IsPotentiallyReachableTestPass::initialize(); | 
|  | (void)initialize; | 
|  |  | 
|  | IsPotentiallyReachableTestPass *P = | 
|  | new IsPotentiallyReachableTestPass(ExpectedResult, A, B, ExclusionSet); | 
|  | legacy::PassManager PM; | 
|  | PM.add(P); | 
|  | PM.run(*M); | 
|  | } | 
|  |  | 
|  | LLVMContext Context; | 
|  | std::unique_ptr<Module> M; | 
|  | Instruction *A, *B; | 
|  | SmallPtrSet<BasicBlock *, 4> ExclusionSet; | 
|  | }; | 
|  |  | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, SameBlockNoPath) { | 
|  | ParseAssembly( | 
|  | "define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  bitcast i8 undef to i8\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  bitcast i8 undef to i8\n" | 
|  | "  bitcast i8 undef to i8\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  ret void\n" | 
|  | "}\n"); | 
|  | ExpectPath(false); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, SameBlockPath) { | 
|  | ParseAssembly( | 
|  | "define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  bitcast i8 undef to i8\n" | 
|  | "  bitcast i8 undef to i8\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  ret void\n" | 
|  | "}\n"); | 
|  | ExpectPath(true); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, SameBlockNoLoop) { | 
|  | ParseAssembly( | 
|  | "define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  br label %middle\n" | 
|  | "middle:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  bitcast i8 undef to i8\n" | 
|  | "  bitcast i8 undef to i8\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  br label %nextblock\n" | 
|  | "nextblock:\n" | 
|  | "  ret void\n" | 
|  | "}\n"); | 
|  | ExpectPath(false); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, StraightNoPath) { | 
|  | ParseAssembly( | 
|  | "define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  br label %exit\n" | 
|  | "exit:\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  ret void\n" | 
|  | "}"); | 
|  | ExpectPath(false); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, StraightPath) { | 
|  | ParseAssembly( | 
|  | "define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  br label %exit\n" | 
|  | "exit:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  ret void\n" | 
|  | "}"); | 
|  | ExpectPath(true); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, DestUnreachable) { | 
|  | ParseAssembly( | 
|  | "define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  br label %midblock\n" | 
|  | "midblock:\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  ret void\n" | 
|  | "unreachable:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  br label %midblock\n" | 
|  | "}"); | 
|  | ExpectPath(false); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, BranchToReturn) { | 
|  | ParseAssembly( | 
|  | "define void @test(i1 %x) {\n" | 
|  | "entry:\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  br i1 %x, label %block1, label %block2\n" | 
|  | "block1:\n" | 
|  | "  ret void\n" | 
|  | "block2:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  ret void\n" | 
|  | "}"); | 
|  | ExpectPath(true); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, SimpleLoop1) { | 
|  | ParseAssembly( | 
|  | "declare i1 @switch()\n" | 
|  | "\n" | 
|  | "define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  br label %loop\n" | 
|  | "loop:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  %x = call i1 @switch()\n" | 
|  | "  br i1 %x, label %loop, label %exit\n" | 
|  | "exit:\n" | 
|  | "  ret void\n" | 
|  | "}"); | 
|  | ExpectPath(true); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, SimpleLoop2) { | 
|  | ParseAssembly( | 
|  | "declare i1 @switch()\n" | 
|  | "\n" | 
|  | "define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  br label %loop\n" | 
|  | "loop:\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  %x = call i1 @switch()\n" | 
|  | "  br i1 %x, label %loop, label %exit\n" | 
|  | "exit:\n" | 
|  | "  ret void\n" | 
|  | "}"); | 
|  | ExpectPath(false); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, SimpleLoop3) { | 
|  | ParseAssembly( | 
|  | "declare i1 @switch()\n" | 
|  | "\n" | 
|  | "define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  br label %loop\n" | 
|  | "loop:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  %x = call i1 @switch()\n" | 
|  | "  br i1 %x, label %loop, label %exit\n" | 
|  | "exit:\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  ret void\n" | 
|  | "}"); | 
|  | ExpectPath(false); | 
|  | } | 
|  |  | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, OneLoopAfterTheOther1) { | 
|  | ParseAssembly( | 
|  | "declare i1 @switch()\n" | 
|  | "\n" | 
|  | "define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  br label %loop1\n" | 
|  | "loop1:\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  %x = call i1 @switch()\n" | 
|  | "  br i1 %x, label %loop1, label %loop1exit\n" | 
|  | "loop1exit:\n" | 
|  | "  br label %loop2\n" | 
|  | "loop2:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  %y = call i1 @switch()\n" | 
|  | "  br i1 %x, label %loop2, label %loop2exit\n" | 
|  | "loop2exit:" | 
|  | "  ret void\n" | 
|  | "}"); | 
|  | ExpectPath(true); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, OneLoopAfterTheOther2) { | 
|  | ParseAssembly( | 
|  | "declare i1 @switch()\n" | 
|  | "\n" | 
|  | "define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  br label %loop1\n" | 
|  | "loop1:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  %x = call i1 @switch()\n" | 
|  | "  br i1 %x, label %loop1, label %loop1exit\n" | 
|  | "loop1exit:\n" | 
|  | "  br label %loop2\n" | 
|  | "loop2:\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  %y = call i1 @switch()\n" | 
|  | "  br i1 %x, label %loop2, label %loop2exit\n" | 
|  | "loop2exit:" | 
|  | "  ret void\n" | 
|  | "}"); | 
|  | ExpectPath(false); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, OneLoopAfterTheOtherInsideAThirdLoop) { | 
|  | ParseAssembly( | 
|  | "declare i1 @switch()\n" | 
|  | "\n" | 
|  | "define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  br label %outerloop3\n" | 
|  | "outerloop3:\n" | 
|  | "  br label %innerloop1\n" | 
|  | "innerloop1:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  %x = call i1 @switch()\n" | 
|  | "  br i1 %x, label %innerloop1, label %innerloop1exit\n" | 
|  | "innerloop1exit:\n" | 
|  | "  br label %innerloop2\n" | 
|  | "innerloop2:\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  %y = call i1 @switch()\n" | 
|  | "  br i1 %x, label %innerloop2, label %innerloop2exit\n" | 
|  | "innerloop2exit:" | 
|  | "  ;; In outer loop3 now.\n" | 
|  | "  %z = call i1 @switch()\n" | 
|  | "  br i1 %z, label %outerloop3, label %exit\n" | 
|  | "exit:\n" | 
|  | "  ret void\n" | 
|  | "}"); | 
|  | ExpectPath(true); | 
|  | } | 
|  |  | 
|  | static const char *BranchInsideLoopIR = | 
|  | "declare i1 @switch()\n" | 
|  | "\n" | 
|  | "define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  br label %loop\n" | 
|  | "loop:\n" | 
|  | "  %x = call i1 @switch()\n" | 
|  | "  br i1 %x, label %nextloopblock, label %exit\n" | 
|  | "nextloopblock:\n" | 
|  | "  %y = call i1 @switch()\n" | 
|  | "  br i1 %y, label %left, label %right\n" | 
|  | "left:\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  br label %loop\n" | 
|  | "right:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  br label %loop\n" | 
|  | "exit:\n" | 
|  | "  ret void\n" | 
|  | "}"; | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, BranchInsideLoop) { | 
|  | ParseAssembly(BranchInsideLoopIR); | 
|  | ExpectPath(true); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, ModifyTest) { | 
|  | ParseAssembly(BranchInsideLoopIR); | 
|  |  | 
|  | succ_iterator S = succ_begin(&*++M->getFunction("test")->begin()); | 
|  | BasicBlock *OldBB = S[0]; | 
|  | S[0] = S[1]; | 
|  | ExpectPath(false); | 
|  | S[0] = OldBB; | 
|  | ExpectPath(true); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, UnreachableFromEntryTest) { | 
|  | ParseAssembly("define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  ret void\n" | 
|  | "not.reachable:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  ret void\n" | 
|  | "}"); | 
|  | ExpectPath(false); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, UnreachableBlocksTest1) { | 
|  | ParseAssembly("define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  ret void\n" | 
|  | "not.reachable.1:\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  br label %not.reachable.2\n" | 
|  | "not.reachable.2:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  ret void\n" | 
|  | "}"); | 
|  | ExpectPath(true); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, UnreachableBlocksTest2) { | 
|  | ParseAssembly("define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  ret void\n" | 
|  | "not.reachable.1:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  br label %not.reachable.2\n" | 
|  | "not.reachable.2:\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  ret void\n" | 
|  | "}"); | 
|  | ExpectPath(false); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, SimpleExclusionTest) { | 
|  | ParseAssembly("define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  br label %excluded\n" | 
|  | "excluded:\n" | 
|  | "  br label %exit\n" | 
|  | "exit:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  ret void\n" | 
|  | "}"); | 
|  | ExpectPath(false); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, DiamondExcludedTest) { | 
|  | ParseAssembly("declare i1 @switch()\n" | 
|  | "\n" | 
|  | "define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  %x = call i1 @switch()\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  br i1 %x, label %excluded.1, label %excluded.2\n" | 
|  | "excluded.1:\n" | 
|  | "  br label %exit\n" | 
|  | "excluded.2:\n" | 
|  | "  br label %exit\n" | 
|  | "exit:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  ret void\n" | 
|  | "}"); | 
|  | ExpectPath(false); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, DiamondOneSideExcludedTest) { | 
|  | ParseAssembly("declare i1 @switch()\n" | 
|  | "\n" | 
|  | "define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  %x = call i1 @switch()\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  br i1 %x, label %excluded, label %diamond\n" | 
|  | "excluded:\n" | 
|  | "  br label %exit\n" | 
|  | "diamond:\n" | 
|  | "  br label %exit\n" | 
|  | "exit:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  ret void\n" | 
|  | "}"); | 
|  | ExpectPath(true); | 
|  | } | 
|  |  | 
|  | TEST_F(IsPotentiallyReachableTest, UnreachableToReachable) { | 
|  | ParseAssembly("define void @test() {\n" | 
|  | "entry:\n" | 
|  | "  br label %exit\n" | 
|  | "unreachableblock:\n" | 
|  | "  %A = bitcast i8 undef to i8\n" | 
|  | "  br label %exit\n" | 
|  | "exit:\n" | 
|  | "  %B = bitcast i8 undef to i8\n" | 
|  | "  ret void\n" | 
|  | "}"); | 
|  | ExpectPath(true); | 
|  | } |