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
|