//===- 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
