//===- unittests/Analysis/CFGDominatorTree.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 "CFGBuildResult.h" #include "clang/Analysis/Analyses/Dominators.h" #include "gtest/gtest.h" namespace clang { namespace analysis { namespace { TEST(CFGDominatorTree, DomTree) { const char *Code = R"(enum Kind { A }; void f() { switch(Kind{}) { case A: break; } })"; BuildResult Result = BuildCFG(Code); EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus()); // [B3 (ENTRY)] -> [B1] -> [B2] -> [B0 (EXIT)] // switch case A CFG *cfg = Result.getCFG(); // Sanity checks. EXPECT_EQ(cfg->size(), 4u); CFGBlock *ExitBlock = *cfg->begin(); EXPECT_EQ(ExitBlock, &cfg->getExit()); CFGBlock *SwitchBlock = *(cfg->begin() + 1); CFGBlock *CaseABlock = *(cfg->begin() + 2); CFGBlock *EntryBlock = *(cfg->begin() + 3); EXPECT_EQ(EntryBlock, &cfg->getEntry()); // Test the dominator tree. CFGDomTree Dom; Dom.buildDominatorTree(cfg); EXPECT_TRUE(Dom.dominates(ExitBlock, ExitBlock)); EXPECT_FALSE(Dom.properlyDominates(ExitBlock, ExitBlock)); EXPECT_TRUE(Dom.dominates(CaseABlock, ExitBlock)); EXPECT_TRUE(Dom.dominates(SwitchBlock, ExitBlock)); EXPECT_TRUE(Dom.dominates(EntryBlock, ExitBlock)); EXPECT_TRUE(Dom.dominates(CaseABlock, CaseABlock)); EXPECT_FALSE(Dom.properlyDominates(CaseABlock, CaseABlock)); EXPECT_TRUE(Dom.dominates(SwitchBlock, CaseABlock)); EXPECT_TRUE(Dom.dominates(EntryBlock, CaseABlock)); EXPECT_TRUE(Dom.dominates(SwitchBlock, SwitchBlock)); EXPECT_FALSE(Dom.properlyDominates(SwitchBlock, SwitchBlock)); EXPECT_TRUE(Dom.dominates(EntryBlock, SwitchBlock)); EXPECT_TRUE(Dom.dominates(EntryBlock, EntryBlock)); EXPECT_FALSE(Dom.properlyDominates(EntryBlock, EntryBlock)); // Test the post dominator tree. CFGPostDomTree PostDom; PostDom.buildDominatorTree(cfg); EXPECT_TRUE(PostDom.dominates(ExitBlock, EntryBlock)); EXPECT_TRUE(PostDom.dominates(CaseABlock, EntryBlock)); EXPECT_TRUE(PostDom.dominates(SwitchBlock, EntryBlock)); EXPECT_TRUE(PostDom.dominates(EntryBlock, EntryBlock)); EXPECT_FALSE(Dom.properlyDominates(EntryBlock, EntryBlock)); EXPECT_TRUE(PostDom.dominates(ExitBlock, SwitchBlock)); EXPECT_TRUE(PostDom.dominates(CaseABlock, SwitchBlock)); EXPECT_TRUE(PostDom.dominates(SwitchBlock, SwitchBlock)); EXPECT_FALSE(Dom.properlyDominates(SwitchBlock, SwitchBlock)); EXPECT_TRUE(PostDom.dominates(ExitBlock, CaseABlock)); EXPECT_TRUE(PostDom.dominates(CaseABlock, CaseABlock)); EXPECT_FALSE(Dom.properlyDominates(CaseABlock, CaseABlock)); EXPECT_TRUE(PostDom.dominates(ExitBlock, ExitBlock)); EXPECT_FALSE(Dom.properlyDominates(ExitBlock, ExitBlock)); // Tests for the post dominator tree's virtual root. EXPECT_TRUE(PostDom.dominates(nullptr, EntryBlock)); EXPECT_TRUE(PostDom.dominates(nullptr, SwitchBlock)); EXPECT_TRUE(PostDom.dominates(nullptr, CaseABlock)); EXPECT_TRUE(PostDom.dominates(nullptr, ExitBlock)); } TEST(CFGDominatorTree, ControlDependency) { const char *Code = R"(bool coin(); void funcWithBranch() { int x = 0; if (coin()) { if (coin()) { x = 5; } int j = 10 / x; (void)j; } };)"; BuildResult Result = BuildCFG(Code); EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus()); // 1st if 2nd if // [B5 (ENTRY)] -> [B4] -> [B3] -> [B2] -> [B1] -> [B0 (EXIT)] // \ \ / / // \ -------------> / // ------------------------------> CFG *cfg = Result.getCFG(); // Sanity checks. EXPECT_EQ(cfg->size(), 6u); CFGBlock *ExitBlock = *cfg->begin(); EXPECT_EQ(ExitBlock, &cfg->getExit()); CFGBlock *NullDerefBlock = *(cfg->begin() + 1); CFGBlock *SecondThenBlock = *(cfg->begin() + 2); CFGBlock *SecondIfBlock = *(cfg->begin() + 3); CFGBlock *FirstIfBlock = *(cfg->begin() + 4); CFGBlock *EntryBlock = *(cfg->begin() + 5); EXPECT_EQ(EntryBlock, &cfg->getEntry()); ControlDependencyCalculator Control(cfg); EXPECT_TRUE(Control.isControlDependent(SecondThenBlock, SecondIfBlock)); EXPECT_TRUE(Control.isControlDependent(SecondIfBlock, FirstIfBlock)); EXPECT_FALSE(Control.isControlDependent(NullDerefBlock, SecondIfBlock)); } TEST(CFGDominatorTree, ControlDependencyWithLoops) { const char *Code = R"(int test3() { int x,y,z; x = y = z = 1; if (x > 0) { while (x >= 0){ while (y >= x) { x = x-1; y = y/2; } } } z = y; return 0; })"; BuildResult Result = BuildCFG(Code); EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus()); // <- [B2] <- // / \ // [B8 (ENTRY)] -> [B7] -> [B6] ---> [B5] -> [B4] -> [B3] // \ | \ / // \ | <------------- // \ \ // --------> [B1] -> [B0 (EXIT)] CFG *cfg = Result.getCFG(); ControlDependencyCalculator Control(cfg); auto GetBlock = [cfg] (unsigned Index) -> CFGBlock * { assert(Index < cfg->size()); return *(cfg->begin() + Index); }; // While not immediately obvious, the second block in fact post dominates the // fifth, hence B5 is not a control dependency of 2. EXPECT_FALSE(Control.isControlDependent(GetBlock(5), GetBlock(2))); } } // namespace } // namespace analysis } // namespace clang