195 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			195 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- 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
 |