Dominator Tree Analysis (#3)
authorStephen McGroarty <smcgro@googlemail.com>
Mon, 27 Nov 2017 21:21:26 +0000 (21:21 +0000)
committerSteven Perron <stevenperron@google.com>
Wed, 6 Dec 2017 03:59:43 +0000 (22:59 -0500)
Support for dominator and post dominator analysis on ir::Functions. This patch contains a DominatorTree class for building the tree and DominatorAnalysis and DominatorAnalysisPass classes for interfacing and caching the built trees.

24 files changed:
Android.mk
CHANGES
source/opt/CMakeLists.txt
source/opt/basic_block.h
source/opt/dominator_analysis.h [new file with mode: 0644]
source/opt/dominator_tree.cpp [new file with mode: 0644]
source/opt/dominator_tree.h [new file with mode: 0644]
source/opt/function.h
source/opt/ir_context.cpp
source/opt/ir_context.h
source/opt/module.h
test/opt/CMakeLists.txt
test/opt/dominator_tree/CMakeLists.txt [new file with mode: 0644]
test/opt/dominator_tree/generated.cpp [new file with mode: 0644]
test/opt/dominator_tree/nested_ifs.cpp [new file with mode: 0644]
test/opt/dominator_tree/nested_ifs_post.cpp [new file with mode: 0644]
test/opt/dominator_tree/nested_loops.cpp [new file with mode: 0644]
test/opt/dominator_tree/nested_loops_with_unreachables.cpp [new file with mode: 0644]
test/opt/dominator_tree/post.cpp [new file with mode: 0644]
test/opt/dominator_tree/simple.cpp [new file with mode: 0644]
test/opt/dominator_tree/switch_case_fallthrough.cpp [new file with mode: 0644]
test/opt/dominator_tree/unreachable_for.cpp [new file with mode: 0644]
test/opt/dominator_tree/unreachable_for_post.cpp [new file with mode: 0644]
test/opt/function_utils.h [new file with mode: 0644]

index c089d02..29ea52d 100644 (file)
@@ -62,6 +62,7 @@ SPVTOOLS_OPT_SRC_FILES := \
                source/opt/dead_variable_elimination.cpp \
                source/opt/decoration_manager.cpp \
                source/opt/def_use_manager.cpp \
+               source/opt/dominator_tree.cpp \
                source/opt/eliminate_dead_constant_pass.cpp \
                source/opt/eliminate_dead_functions_pass.cpp \
                source/opt/flatten_decoration_pass.cpp \
diff --git a/CHANGES b/CHANGES
index c6b92a1..7162ffe 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,8 @@ Revision history for SPIRV-Tools
 
 v2017.2-dev 2017-11-23
  - Start v2017.2-dev
+ - Optimizer:
+   - Adding dominance analysis
  - Fixes:
    #1004: Use after free of an instruction, in remove-duplicates transform
    #1007: OpImageRead not required to return 4-component vector
index 2294e81..0759029 100644 (file)
@@ -25,6 +25,8 @@ add_library(SPIRV-Tools-opt
   dead_variable_elimination.h
   decoration_manager.h
   def_use_manager.h
+  dominator_analysis.h
+  dominator_tree.h
   eliminate_dead_constant_pass.h
   eliminate_dead_functions_pass.h
   flatten_decoration_pass.h
@@ -75,6 +77,7 @@ add_library(SPIRV-Tools-opt
   dead_variable_elimination.cpp
   decoration_manager.cpp
   def_use_manager.cpp
+  dominator_tree.cpp
   eliminate_dead_constant_pass.cpp
   eliminate_dead_functions_pass.cpp
   flatten_decoration_pass.cpp
index ba1552a..8498ec0 100644 (file)
@@ -81,6 +81,8 @@ class BasicBlock {
 
   iterator begin() { return insts_.begin(); }
   iterator end() { return insts_.end(); }
+  const_iterator begin() const { return insts_.cbegin(); }
+  const_iterator end() const { return insts_.cend(); }
   const_iterator cbegin() const { return insts_.cbegin(); }
   const_iterator cend() const { return insts_.cend(); }
 
@@ -98,6 +100,9 @@ class BasicBlock {
     return --insts_.cend();
   }
 
+  // Returns true if the basic block has at least one successor.
+  inline bool hasSuccessor() const { return ctail()->IsBranch(); }
+
   // Runs the given function |f| on each instruction in this basic block, and
   // optionally on the debug line instructions that might precede them.
   inline void ForEachInst(const std::function<void(Instruction*)>& f,
diff --git a/source/opt/dominator_analysis.h b/source/opt/dominator_analysis.h
new file mode 100644 (file)
index 0000000..722a979
--- /dev/null
@@ -0,0 +1,133 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef LIBSPIRV_OPT_DOMINATOR_ANALYSIS_PASS_H_
+#define LIBSPIRV_OPT_DOMINATOR_ANALYSIS_PASS_H_
+
+#include <cstdint>
+#include <map>
+
+#include "dominator_tree.h"
+#include "module.h"
+
+namespace spvtools {
+namespace opt {
+
+// Interface to perform dominator or postdominator analysis on a given function.
+class DominatorAnalysisBase {
+ public:
+  explicit DominatorAnalysisBase(bool is_post_dom) : tree_(is_post_dom) {}
+
+  // Calculates the dominator (or postdominator) tree for given function |f|.
+  inline void InitializeTree(const ir::Function* f, const ir::CFG& cfg) {
+    tree_.InitializeTree(f, cfg);
+  }
+
+  // Returns true if BasicBlock |a| dominates BasicBlock |b|.
+  inline bool Dominates(const ir::BasicBlock* a,
+                        const ir::BasicBlock* b) const {
+    if (!a || !b) return false;
+    return Dominates(a->id(), b->id());
+  }
+
+  // Returns true if BasicBlock |a| dominates BasicBlock |b|. Same as above only
+  // using the BasicBlock IDs.
+  inline bool Dominates(uint32_t a, uint32_t b) const {
+    return tree_.Dominates(a, b);
+  }
+
+  // Returns true if BasicBlock |a| strictly dominates BasicBlock |b|.
+  inline bool StrictlyDominates(const ir::BasicBlock* a,
+                                const ir::BasicBlock* b) const {
+    if (!a || !b) return false;
+    return StrictlyDominates(a->id(), b->id());
+  }
+
+  // Returns true if BasicBlock |a| strictly dominates BasicBlock |b|. Same as
+  // above only using the BasicBlock IDs.
+  inline bool StrictlyDominates(uint32_t a, uint32_t b) const {
+    return tree_.StrictlyDominates(a, b);
+  }
+
+  // Returns the immediate dominator of |node| or returns nullptr if it is has
+  // no dominator.
+  inline ir::BasicBlock* ImmediateDominator(const ir::BasicBlock* node) const {
+    if (!node) return nullptr;
+    return tree_.ImmediateDominator(node);
+  }
+
+  // Returns the immediate dominator of |node_id| or returns nullptr if it is
+  // has no dominator. Same as above but operates on IDs.
+  inline ir::BasicBlock* ImmediateDominator(uint32_t node_id) const {
+    return tree_.ImmediateDominator(node_id);
+  }
+
+  // Returns true if |node| is reachable from the entry.
+  inline bool IsReachable(const ir::BasicBlock* node) const {
+    if (!node) return false;
+    return tree_.ReachableFromRoots(node->id());
+  }
+
+  // Returns true if |node_id| is reachable from the entry.
+  inline bool IsReachable(uint32_t node_id) const {
+    return tree_.ReachableFromRoots(node_id);
+  }
+
+  // Dump the tree structure into the given |out| stream in the dot format.
+  inline void DumpAsDot(std::ostream& out) const { tree_.DumpTreeAsDot(out); }
+
+  // Returns true if this is a postdomiator tree.
+  inline bool IsPostDominator() const { return tree_.IsPostDominator(); }
+
+  // Returns the tree itself for manual operations, such as traversing the
+  // roots.
+  // For normal dominance relationships the methods above should be used.
+  inline DominatorTree& GetDomTree() { return tree_; }
+  inline const DominatorTree& GetDomTree() const { return tree_; }
+
+  // Force the dominator tree to be removed
+  inline void ClearTree() { tree_.ClearTree(); }
+
+  // Applies the std::function |func| to dominator tree nodes in dominator
+  // order.
+  void Visit(std::function<bool(DominatorTreeNode*)> func) {
+    tree_.Visit(func);
+  }
+
+  // Applies the std::function |func| to dominator tree nodes in dominator
+  // order.
+  void Visit(std::function<bool(const DominatorTreeNode*)> func) const {
+    tree_.Visit(func);
+  }
+
+ protected:
+  DominatorTree tree_;
+};
+
+// Derived class for normal dominator analysis.
+class DominatorAnalysis : public DominatorAnalysisBase {
+ public:
+  DominatorAnalysis() : DominatorAnalysisBase(false) {}
+};
+
+// Derived class for postdominator analysis.
+class PostDominatorAnalysis : public DominatorAnalysisBase {
+ public:
+  PostDominatorAnalysis() : DominatorAnalysisBase(true) {}
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // LIBSPIRV_OPT_DOMINATOR_ANALYSIS_PASS_H_
diff --git a/source/opt/dominator_tree.cpp b/source/opt/dominator_tree.cpp
new file mode 100644 (file)
index 0000000..5417dd1
--- /dev/null
@@ -0,0 +1,419 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <iostream>
+#include <memory>
+#include <set>
+
+#include "cfa.h"
+#include "dominator_tree.h"
+
+using namespace spvtools;
+using namespace spvtools::opt;
+
+// Calculates the dominator or postdominator tree for a given function.
+// 1 - Compute the successors and predecessors for each BasicBlock. We add a
+// dummy node for the start node or for postdominators the exit. This node will
+// point to all entry or all exit nodes.
+// 2 - Using the CFA::DepthFirstTraversal get a depth first postordered list of
+// all BasicBlocks. Using the successors (or for postdominator, predecessors)
+// calculated in step 1 to traverse the tree.
+// 3 - Pass the list calculated in step 2 to the CFA::CalculateDominators using
+// the predecessors list (or for postdominator, successors). This will give us a
+// vector of BB pairs. Each BB and its immediate dominator.
+// 4 - Using the list from 3 use those edges to build a tree of
+// DominatorTreeNodes. Each node containing a link to the parent dominator and
+// children which are dominated.
+// 5 - Using the tree from 4, perform a depth first traversal to calculate the
+// preorder and postorder index of each node. We use these indexes to compare
+// nodes against each other for domination checks.
+
+namespace {
+
+// Wrapper around CFA::DepthFirstTraversal to provide an interface to perform
+// depth first search on generic BasicBlock types. Will call post and pre order
+// user defined functions during traversal
+//
+// BBType - BasicBlock type. Will either be ir::BasicBlock or DominatorTreeNode
+// SuccessorLambda - Lamdba matching the signature of 'const
+// std::vector<BBType>*(const BBType *A)'. Will return a vector of the nodes
+// succeding BasicBlock A.
+// PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be
+// called on each node traversed AFTER their children.
+// PreLambda - Lamdba matching the signature of 'void (const BBType*)' will be
+// called on each node traversed BEFORE their children.
+template <typename BBType, typename SuccessorLambda, typename PreLambda,
+          typename PostLambda>
+static void DepthFirstSearch(const BBType* bb, SuccessorLambda successors,
+                             PreLambda pre, PostLambda post) {
+  // Ignore backedge operation.
+  auto nop_backedge = [](const BBType*, const BBType*) {};
+  CFA<BBType>::DepthFirstTraversal(bb, successors, pre, post, nop_backedge);
+}
+
+// Wrapper around CFA::DepthFirstTraversal to provide an interface to perform
+// depth first search on generic BasicBlock types. This overload is for only
+// performing user defined post order.
+//
+// BBType - BasicBlock type. Will either be ir::BasicBlock or DominatorTreeNode
+// SuccessorLambda - Lamdba matching the signature of 'const
+// std::vector<BBType>*(const BBType *A)'. Will return a vector of the nodes
+// succeding BasicBlock A.
+// PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be
+// called on each node traversed after their children.
+template <typename BBType, typename SuccessorLambda, typename PostLambda>
+static void DepthFirstSearchPostOrder(const BBType* bb,
+                                      SuccessorLambda successors,
+                                      PostLambda post) {
+  // Ignore preorder operation.
+  auto nop_preorder = [](const BBType*) {};
+  DepthFirstSearch(bb, successors, nop_preorder, post);
+}
+
+// Small type trait to get the function class type.
+template <typename BBType>
+struct GetFunctionClass {
+  using FunctionType = ir::Function;
+};
+
+// Helper class to compute predecessors and successors for each Basic Block in a
+// function. Through GetPredFunctor and GetSuccessorFunctor it provides an
+// interface to get the successor and predecessor lists for each basic
+// block. This is required by the DepthFirstTraversal and ComputeDominator
+// functions which take as parameter an std::function returning the successors
+// and predecessors respectively.
+//
+// When computing the post-dominator tree, all edges are inverted. So successors
+// returned by this class will be predecessors in the original CFG.
+template <typename BBType>
+class BasicBlockSuccessorHelper {
+  // This should eventually become const ir::BasicBlock.
+  using BasicBlock = BBType;
+  using Function = typename GetFunctionClass<BBType>::FunctionType;
+
+  using BasicBlockListTy = std::vector<BasicBlock*>;
+  using BasicBlockMapTy = std::map<const BasicBlock*, BasicBlockListTy>;
+
+ public:
+  // For compliance with the dominance tree computation, entry nodes are
+  // connected to a single dummy node.
+  BasicBlockSuccessorHelper(Function& func, const BasicBlock* dummy_start_node,
+                            bool post);
+
+  // CFA::CalculateDominators requires std::vector<BasicBlock*>.
+  using GetBlocksFunction =
+      std::function<const std::vector<BasicBlock*>*(const BasicBlock*)>;
+
+  // Returns the list of predecessor functions.
+  GetBlocksFunction GetPredFunctor() {
+    return [this](const BasicBlock* bb) {
+      BasicBlockListTy* v = &this->predecessors_[bb];
+      return v;
+    };
+  }
+
+  // Returns a vector of the list of successor nodes from a given node.
+  GetBlocksFunction GetSuccessorFunctor() {
+    return [this](const BasicBlock* bb) {
+      BasicBlockListTy* v = &this->successors_[bb];
+      return v;
+    };
+  }
+
+ private:
+  bool invert_graph_;
+  BasicBlockMapTy successors_;
+  BasicBlockMapTy predecessors_;
+
+  // Build the successors and predecessors map for each basic blocks |f|.
+  // If |invert_graph_| is true, all edges are reversed (successors becomes
+  // predecessors and vice versa).
+  // For convenience, the start of the graph is |dummy_start_node|.
+  // The dominator tree construction requires a unique entry node, which cannot
+  // be guaranteed for the postdominator graph. The |dummy_start_node| BB is
+  // here to gather all entry nodes.
+  void CreateSuccessorMap(Function& f, const BasicBlock* dummy_start_node);
+};
+
+template <typename BBType>
+BasicBlockSuccessorHelper<BBType>::BasicBlockSuccessorHelper(
+    Function& func, const BasicBlock* dummy_start_node, bool invert)
+    : invert_graph_(invert) {
+  CreateSuccessorMap(func, dummy_start_node);
+}
+
+template <typename BBType>
+void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap(
+    Function& f, const BasicBlock* dummy_start_node) {
+  std::map<uint32_t, BasicBlock*> id_to_BB_map;
+  auto GetSuccessorBasicBlock = [&f, &id_to_BB_map](uint32_t successor_id) {
+    BasicBlock*& Succ = id_to_BB_map[successor_id];
+    if (!Succ) {
+      for (BasicBlock& BBIt : f) {
+        if (successor_id == BBIt.id()) {
+          Succ = &BBIt;
+          break;
+        }
+      }
+    }
+    return Succ;
+  };
+
+  if (invert_graph_) {
+    // For the post dominator tree, we see the inverted graph.
+    // successors_ in the inverted graph are the predecessors in the CFG.
+    // The tree construction requires 1 entry point, so we add a dummy node
+    // that is connected to all function exiting basic blocks.
+    // An exiting basic block is a block with an OpKill, OpUnreachable,
+    // OpReturn or OpReturnValue as terminator instruction.
+    for (BasicBlock& bb : f) {
+      if (bb.hasSuccessor()) {
+        BasicBlockListTy& pred_list = predecessors_[&bb];
+        bb.ForEachSuccessorLabel(
+            [this, &pred_list, &bb,
+             &GetSuccessorBasicBlock](const uint32_t successor_id) {
+              BasicBlock* succ = GetSuccessorBasicBlock(successor_id);
+              // Inverted graph: our successors in the CFG
+              // are our predecessors in the inverted graph.
+              this->successors_[succ].push_back(&bb);
+              pred_list.push_back(succ);
+            });
+      } else {
+        successors_[dummy_start_node].push_back(&bb);
+        predecessors_[&bb].push_back(const_cast<BasicBlock*>(dummy_start_node));
+      }
+    }
+  } else {
+    // Technically, this is not needed, but it unifies
+    // the handling of dominator and postdom tree later on.
+    successors_[dummy_start_node].push_back(f.entry().get());
+    predecessors_[f.entry().get()].push_back(
+        const_cast<BasicBlock*>(dummy_start_node));
+    for (BasicBlock& bb : f) {
+      BasicBlockListTy& succ_list = successors_[&bb];
+
+      bb.ForEachSuccessorLabel([&](const uint32_t successor_id) {
+        BasicBlock* succ = GetSuccessorBasicBlock(successor_id);
+        succ_list.push_back(succ);
+        predecessors_[succ].push_back(&bb);
+      });
+    }
+  }
+}
+
+}  // namespace
+
+namespace spvtools {
+namespace opt {
+
+bool DominatorTree::StrictlyDominates(uint32_t a, uint32_t b) const {
+  if (a == b) return false;
+  return Dominates(a, b);
+}
+
+bool DominatorTree::StrictlyDominates(const ir::BasicBlock* a,
+                                      const ir::BasicBlock* b) const {
+  return DominatorTree::StrictlyDominates(a->id(), b->id());
+}
+
+bool DominatorTree::Dominates(uint32_t a, uint32_t b) const {
+  // Check that both of the inputs are actual nodes.
+  auto a_itr = nodes_.find(a);
+  auto b_itr = nodes_.find(b);
+  if (a_itr == nodes_.end() || b_itr == nodes_.end()) return false;
+
+  // Node A dominates node B if they are the same.
+  if (a == b) return true;
+  const DominatorTreeNode* nodeA = &a_itr->second;
+  const DominatorTreeNode* nodeB = &b_itr->second;
+
+  if (nodeA->dfs_num_pre_ < nodeB->dfs_num_pre_ &&
+      nodeA->dfs_num_post_ > nodeB->dfs_num_post_) {
+    return true;
+  }
+
+  return false;
+}
+
+bool DominatorTree::Dominates(const ir::BasicBlock* A,
+                              const ir::BasicBlock* B) const {
+  return Dominates(A->id(), B->id());
+}
+
+ir::BasicBlock* DominatorTree::ImmediateDominator(
+    const ir::BasicBlock* A) const {
+  return ImmediateDominator(A->id());
+}
+
+ir::BasicBlock* DominatorTree::ImmediateDominator(uint32_t a) const {
+  // Check that A is a valid node in the tree.
+  auto a_itr = nodes_.find(a);
+  if (a_itr == nodes_.end()) return nullptr;
+
+  const DominatorTreeNode* node = &a_itr->second;
+
+  if (node->parent_ == nullptr) {
+    return nullptr;
+  }
+
+  return node->parent_->bb_;
+}
+
+DominatorTreeNode* DominatorTree::GetOrInsertNode(ir::BasicBlock* bb) {
+  DominatorTreeNode* dtn = nullptr;
+
+  std::map<uint32_t, DominatorTreeNode>::iterator node_iter =
+      nodes_.find(bb->id());
+  if (node_iter == nodes_.end()) {
+    dtn = &nodes_.emplace(std::make_pair(bb->id(), DominatorTreeNode{bb}))
+               .first->second;
+  } else
+    dtn = &node_iter->second;
+
+  return dtn;
+}
+
+void DominatorTree::GetDominatorEdges(
+    const ir::Function* f, const ir::BasicBlock* dummy_start_node,
+    std::vector<std::pair<ir::BasicBlock*, ir::BasicBlock*>>* edges) {
+  // Each time the depth first traversal calls the postorder callback
+  // std::function we push that node into the postorder vector to create our
+  // postorder list.
+  std::vector<const ir::BasicBlock*> postorder;
+  auto postorder_function = [&](const ir::BasicBlock* b) {
+    postorder.push_back(b);
+  };
+
+  // CFA::CalculateDominators requires std::vector<ir::BasicBlock*>
+  // BB are derived from F, so we need to const cast it at some point
+  // no modification is made on F.
+  BasicBlockSuccessorHelper<ir::BasicBlock> helper{
+      *const_cast<ir::Function*>(f), dummy_start_node, postdominator_};
+
+  // The successor function tells DepthFirstTraversal how to move to successive
+  // nodes by providing an interface to get a list of successor nodes from any
+  // given node.
+  auto successor_functor = helper.GetSuccessorFunctor();
+
+  // The predecessor functor does the same as the successor functor
+  // but for all nodes preceding a given node.
+  auto predecessor_functor = helper.GetPredFunctor();
+
+  // If we're building a post dominator tree we traverse the tree in reverse
+  // using the predecessor function in place of the successor function and vice
+  // versa.
+  DepthFirstSearchPostOrder(dummy_start_node, successor_functor,
+                            postorder_function);
+  *edges =
+      CFA<ir::BasicBlock>::CalculateDominators(postorder, predecessor_functor);
+}
+
+void DominatorTree::InitializeTree(const ir::Function* f, const ir::CFG& cfg) {
+  ClearTree();
+
+  // Skip over empty functions.
+  if (f->cbegin() == f->cend()) {
+    return;
+  }
+
+  const ir::BasicBlock* dummy_start_node =
+      postdominator_ ? cfg.pseudo_exit_block() : cfg.pseudo_entry_block();
+
+  // Get the immediate dominator for each node.
+  std::vector<std::pair<ir::BasicBlock*, ir::BasicBlock*>> edges;
+  GetDominatorEdges(f, dummy_start_node, &edges);
+
+  // Transform the vector<pair> into the tree structure which we can use to
+  // efficiently query dominance.
+  for (auto edge : edges) {
+    DominatorTreeNode* first = GetOrInsertNode(edge.first);
+
+    if (edge.first == edge.second) {
+      if (std::find(roots_.begin(), roots_.end(), first) == roots_.end())
+        roots_.push_back(first);
+      continue;
+    }
+
+    DominatorTreeNode* second = GetOrInsertNode(edge.second);
+
+    first->parent_ = second;
+    second->children_.push_back(first);
+  }
+
+  int index = 0;
+  auto preFunc = [&index](const DominatorTreeNode* node) {
+    const_cast<DominatorTreeNode*>(node)->dfs_num_pre_ = ++index;
+  };
+
+  auto postFunc = [&index](const DominatorTreeNode* node) {
+    const_cast<DominatorTreeNode*>(node)->dfs_num_post_ = ++index;
+  };
+
+  auto getSucc = [](const DominatorTreeNode* node) { return &node->children_; };
+
+  for (auto root : roots_) DepthFirstSearch(root, getSucc, preFunc, postFunc);
+}
+
+void DominatorTree::DumpTreeAsDot(std::ostream& out_stream) const {
+  out_stream << "digraph {\n";
+  Visit([&out_stream](const DominatorTreeNode* node) {
+
+    // Print the node.
+    if (node->bb_) {
+      out_stream << node->bb_->id() << "[label=\"" << node->bb_->id()
+                 << "\"];\n";
+    }
+
+    // Print the arrow from the parent to this node. Entry nodes will not have
+    // parents so draw them as children from the dummy node.
+    if (node->parent_) {
+      out_stream << node->parent_->bb_->id() << " -> " << node->bb_->id()
+                 << ";\n";
+    }
+
+    // Return true to continue the traversal.
+    return true;
+  });
+  out_stream << "}\n";
+}
+
+bool DominatorTree::Visit(DominatorTreeNode* node,
+                          std::function<bool(DominatorTreeNode*)> func) {
+  // Apply the function to the node.
+  if (!func(node)) return false;
+
+  // Apply the function to every child node.
+  for (DominatorTreeNode* child : node->children_) {
+    if (!Visit(child, func)) return false;
+  }
+
+  return true;
+}
+
+bool DominatorTree::Visit(
+    const DominatorTreeNode* node,
+    std::function<bool(const DominatorTreeNode*)> func) const {
+  // Apply the function to the node.
+  if (!func(node)) return false;
+
+  // Apply the function to every child node.
+  for (const DominatorTreeNode* child : node->children_) {
+    if (!Visit(child, func)) return false;
+  }
+
+  return true;
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/dominator_tree.h b/source/opt/dominator_tree.h
new file mode 100644 (file)
index 0000000..2670384
--- /dev/null
@@ -0,0 +1,201 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef LIBSPIRV_OPT_DOMINATOR_ANALYSIS_TREE_H_
+#define LIBSPIRV_OPT_DOMINATOR_ANALYSIS_TREE_H_
+
+#include <cstdint>
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "cfg.h"
+#include "module.h"
+
+namespace spvtools {
+namespace opt {
+// This helper struct forms the nodes in the tree, with each node containing its
+// children. It also contains two values, for the pre and post indexes in the
+// tree which are used to compare two nodes.
+struct DominatorTreeNode {
+  explicit DominatorTreeNode(ir::BasicBlock* bb)
+      : bb_(bb),
+        parent_(nullptr),
+        children_({}),
+        dfs_num_pre_(-1),
+        dfs_num_post_(-1) {}
+
+  inline uint32_t id() const { return bb_->id(); }
+
+  ir::BasicBlock* bb_;
+  DominatorTreeNode* parent_;
+  std::vector<DominatorTreeNode*> children_;
+
+  // These indexes are used to compare two given nodes. A node is a child or
+  // grandchild of another node if its preorder index is greater than the
+  // first nodes preorder index AND if its postorder index is less than the
+  // first nodes postorder index.
+  int dfs_num_pre_;
+  int dfs_num_post_;
+};
+
+// A class representing a tree of BasicBlocks in a given function, where each
+// node is dominated by its parent.
+class DominatorTree {
+ public:
+  // Map OpLabel ids to dominator tree nodes
+  using DominatorTreeNodeMap = std::map<uint32_t, DominatorTreeNode>;
+  using iterator = DominatorTreeNodeMap::iterator;
+  using const_iterator = DominatorTreeNodeMap::const_iterator;
+
+  // List of DominatorTreeNode to define the list of roots
+  using DominatorTreeNodeList = std::vector<DominatorTreeNode*>;
+  using roots_iterator = DominatorTreeNodeList::iterator;
+  using roots_const_iterator = DominatorTreeNodeList::const_iterator;
+
+  DominatorTree() : postdominator_(false) {}
+  explicit DominatorTree(bool post) : postdominator_(post) {}
+
+  iterator begin() { return nodes_.begin(); }
+  iterator end() { return nodes_.end(); }
+  const_iterator begin() const { return cbegin(); }
+  const_iterator end() const { return cend(); }
+  const_iterator cbegin() const { return nodes_.begin(); }
+  const_iterator cend() const { return nodes_.end(); }
+
+  roots_iterator roots_begin() { return roots_.begin(); }
+  roots_iterator roots_end() { return roots_.end(); }
+  roots_const_iterator roots_begin() const { return roots_cbegin(); }
+  roots_const_iterator roots_end() const { return roots_cend(); }
+  roots_const_iterator roots_cbegin() const { return roots_.begin(); }
+  roots_const_iterator roots_cend() const { return roots_.end(); }
+
+  // Get the unique root of the tree.
+  // It is guaranteed to work on a dominator tree.
+  // post-dominator might have a list.
+  DominatorTreeNode* GetRoot() {
+    assert(roots_.size() == 1);
+    return *roots_.begin();
+  }
+
+  const DominatorTreeNode* GetRoot() const {
+    assert(roots_.size() == 1);
+    return *roots_.begin();
+  }
+
+  const DominatorTreeNodeList& Roots() const { return roots_; }
+
+  // Dumps the tree in the graphvis dot format into the |out_stream|.
+  void DumpTreeAsDot(std::ostream& out_stream) const;
+
+  // Build the (post-)dominator tree for the function |f|
+  // Any existing data will be overwritten
+  void InitializeTree(const ir::Function* f, const ir::CFG& cfg);
+
+  // Check if the basic block |a| dominates the basic block |b|.
+  bool Dominates(const ir::BasicBlock* a, const ir::BasicBlock* b) const;
+
+  // Check if the basic block id |a| dominates the basic block id |b|.
+  bool Dominates(uint32_t a, uint32_t b) const;
+
+  // Check if the basic block |a| strictly dominates the basic block |b|.
+  bool StrictlyDominates(const ir::BasicBlock* a,
+                         const ir::BasicBlock* b) const;
+
+  // Check if the basic block id |a| strictly dominates the basic block id |b|.
+  bool StrictlyDominates(uint32_t a, uint32_t b) const;
+
+  // Returns the immediate dominator of basic block |a|.
+  ir::BasicBlock* ImmediateDominator(const ir::BasicBlock* A) const;
+
+  // Returns the immediate dominator of basic block id |a|.
+  ir::BasicBlock* ImmediateDominator(uint32_t a) const;
+
+  // Returns true if the basic block |a| is reachable by this tree. A node would
+  // be unreachable if it cannot be reached by traversal from the start node or
+  // for a postdominator tree, cannot be reached from the exit nodes.
+  inline bool ReachableFromRoots(const ir::BasicBlock* a) const {
+    if (!a) return false;
+    return ReachableFromRoots(a->id());
+  }
+
+  // Returns true if the basic block id |a| is reachable by this tree.
+  bool ReachableFromRoots(uint32_t a) const;
+
+  // Returns true if this tree is a post dominator tree.
+  bool IsPostDominator() const { return postdominator_; }
+
+  // Clean up the tree.
+  void ClearTree() {
+    nodes_.clear();
+    roots_.clear();
+  }
+
+  // Applies the std::function |func| to all nodes in the dominator tree.
+  bool Visit(std::function<bool(DominatorTreeNode*)> func) {
+    for (auto n : roots_) {
+      if (!Visit(n, func)) return false;
+    }
+    return true;
+  }
+
+  // Applies the std::function |func| to all nodes in the dominator tree.
+  bool Visit(std::function<bool(const DominatorTreeNode*)> func) const {
+    for (auto n : roots_) {
+      if (!Visit(n, func)) return false;
+    }
+    return true;
+  }
+
+  // Applies the std::function |func| to |node| then applies it to nodes
+  // children.
+  bool Visit(DominatorTreeNode* node,
+             std::function<bool(DominatorTreeNode*)> func);
+
+  // Applies the std::function |func| to |node| then applies it to nodes
+  // children.
+  bool Visit(const DominatorTreeNode* node,
+             std::function<bool(const DominatorTreeNode*)> func) const;
+
+ private:
+  // Adds the basic block |bb| to the tree structure if it doesn't already
+  // exist.
+  DominatorTreeNode* GetOrInsertNode(ir::BasicBlock* bb);
+
+  // Wrapper function which gets the list of pairs of each BasicBlocks to its
+  // immediately  dominating BasicBlock and stores the result in the the edges
+  // parameter.
+  //
+  // The |edges| vector will contain the dominator tree as pairs of nodes.
+  // The first node in the pair is a node in the graph. The second node in the
+  // pair is its immediate dominator.
+  // The root of the tree has themself as immediate dominator.
+  void GetDominatorEdges(
+      const ir::Function* f, const ir::BasicBlock* dummy_start_node,
+      std::vector<std::pair<ir::BasicBlock*, ir::BasicBlock*>>* edges);
+
+  // The roots of the tree.
+  std::vector<DominatorTreeNode*> roots_;
+
+  // Pairs each basic block id to the tree node containing that basic block.
+  DominatorTreeNodeMap nodes_;
+
+  // True if this is a post dominator tree.
+  bool postdominator_;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // LIBSPIRV_OPT_DOMINATOR_ANALYSIS_TREE_H_
index 9cd7209..588792c 100644 (file)
@@ -82,6 +82,8 @@ class Function {
 
   iterator begin() { return iterator(&blocks_, blocks_.begin()); }
   iterator end() { return iterator(&blocks_, blocks_.end()); }
+  const_iterator begin() const { return cbegin(); }
+  const_iterator end() const { return cend(); }
   const_iterator cbegin() const {
     return const_iterator(&blocks_, blocks_.cbegin());
   }
index 6576a0d..f1d7103 100644 (file)
@@ -34,6 +34,12 @@ void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) {
   if (set & kAnalysisCFG) {
     BuildCFG();
   }
+  if (set & kAnalysisDominatorAnalysis) {
+    // An invalid dominator tree analysis will be empty so rebuilding it just
+    // means marking it as valid. Each tree will be initalisalised when
+    // requested on a per function basis.
+    valid_analyses_ |= kAnalysisDominatorAnalysis;
+  }
 }
 
 void IRContext::InvalidateAnalysesExceptFor(
@@ -58,6 +64,11 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
   if (analyses_to_invalidate & kAnalysisCFG) {
     cfg_.reset(nullptr);
   }
+  if (analyses_to_invalidate & kAnalysisDominatorAnalysis) {
+    dominator_trees_.clear();
+    post_dominator_trees_.clear();
+  }
+
   valid_analyses_ = Analysis(valid_analyses_ & ~analyses_to_invalidate);
 }
 
@@ -456,5 +467,28 @@ void IRContext::InitializeCombinators() {
 
   valid_analyses_ |= kAnalysisCombinators;
 }
+
+// Gets the dominator analysis for function |f|.
+opt::DominatorAnalysis* IRContext::GetDominatorAnalysis(const ir::Function* f,
+                                                        const ir::CFG& in_cfg) {
+  if (dominator_trees_.find(f) == dominator_trees_.end() ||
+      !AreAnalysesValid(kAnalysisDominatorAnalysis)) {
+    dominator_trees_[f].InitializeTree(f, in_cfg);
+  }
+
+  return &dominator_trees_[f];
+}
+
+// Gets the postdominator analysis for function |f|.
+opt::PostDominatorAnalysis* IRContext::GetPostDominatorAnalysis(
+    const ir::Function* f, const ir::CFG& in_cfg) {
+  if (post_dominator_trees_.find(f) == post_dominator_trees_.end() ||
+      !AreAnalysesValid(kAnalysisDominatorAnalysis)) {
+    post_dominator_trees_[f].InitializeTree(f, in_cfg);
+  }
+
+  return &post_dominator_trees_[f];
+}
+
 }  // namespace ir
 }  // namespace spvtools
index a6a2209..270eeaf 100644 (file)
@@ -18,6 +18,7 @@
 #include "cfg.h"
 #include "decoration_manager.h"
 #include "def_use_manager.h"
+#include "dominator_analysis.h"
 #include "module.h"
 
 #include <algorithm>
@@ -49,7 +50,8 @@ class IRContext {
     kAnalysisDecorations = 1 << 2,
     kAnalysisCombinators = 1 << 3,
     kAnalysisCFG = 1 << 4,
-    kAnalysisEnd = 1 << 5
+    kAnalysisDominatorAnalysis = 1 << 5,
+    kAnalysisEnd = 1 << 6
   };
 
   friend inline Analysis operator|(Analysis lhs, Analysis rhs);
@@ -307,6 +309,24 @@ class IRContext {
     return cfg_.get();
   }
 
+  // Gets the dominator analysis for function |f|.
+  opt::DominatorAnalysis* GetDominatorAnalysis(const ir::Function* f,
+                                               const ir::CFG&);
+
+  // Gets the postdominator analysis for function |f|.
+  opt::PostDominatorAnalysis* GetPostDominatorAnalysis(const ir::Function* f,
+                                                       const ir::CFG&);
+
+  // Remove the dominator tree of |f| from the cache.
+  inline void RemoveDominatorAnalysis(const ir::Function* f) {
+    dominator_trees_.erase(f);
+  }
+
+  // Remove the postdominator tree of |f| from the cache.
+  inline void RemovePostDominatorAnalysis(const ir::Function* f) {
+    post_dominator_trees_.erase(f);
+  }
+
  private:
   // Builds the def-use manager from scratch, even if it was already valid.
   void BuildDefUseManager() {
@@ -375,6 +395,12 @@ class IRContext {
 
   // The CFG for all the functions in |module_|.
   std::unique_ptr<ir::CFG> cfg_;
+
+  // Each function in the module will create its own dominator tree. We cache
+  // the result so it doesn't need to be rebuilt each time.
+  std::map<const ir::Function*, opt::DominatorAnalysis> dominator_trees_;
+  std::map<const ir::Function*, opt::PostDominatorAnalysis>
+      post_dominator_trees_;
 };
 
 inline ir::IRContext::Analysis operator|(ir::IRContext::Analysis lhs,
index d3fe2b5..edc8c02 100644 (file)
@@ -201,6 +201,8 @@ class Module {
   // Iterators for functions contained in this module.
   iterator begin() { return iterator(&functions_, functions_.begin()); }
   iterator end() { return iterator(&functions_, functions_.end()); }
+  const_iterator begin() const { return cbegin(); }
+  const_iterator end() const { return cend(); }
   inline const_iterator cbegin() const;
   inline const_iterator cend() const;
 
index b6e75fc..36af223 100644 (file)
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+add_subdirectory(dominator_tree)
+
 add_spvtools_unittest(TARGET instruction
   SRCS instruction_test.cpp
   LIBS SPIRV-Tools-opt
diff --git a/test/opt/dominator_tree/CMakeLists.txt b/test/opt/dominator_tree/CMakeLists.txt
new file mode 100644 (file)
index 0000000..22778e4
--- /dev/null
@@ -0,0 +1,74 @@
+# Copyright (c) 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+add_spvtools_unittest(TARGET dominator_analysis_simple
+    SRCS ../function_utils.h
+         simple.cpp
+    LIBS SPIRV-Tools-opt
+)
+
+add_spvtools_unittest(TARGET dominator_analysis_post
+    SRCS ../function_utils.h
+         post.cpp
+    LIBS SPIRV-Tools-opt
+)
+
+add_spvtools_unittest(TARGET dominator_analysis_nested_ifs
+    SRCS ../function_utils.h
+         nested_ifs.cpp
+    LIBS SPIRV-Tools-opt
+)
+
+add_spvtools_unittest(TARGET dominator_analysis_nested_ifs_post
+    SRCS ../function_utils.h
+         nested_ifs_post.cpp
+    LIBS SPIRV-Tools-opt
+)
+
+add_spvtools_unittest(TARGET dominator_analysis_nested_loops
+    SRCS ../function_utils.h
+         nested_loops.cpp
+    LIBS SPIRV-Tools-opt
+)
+
+add_spvtools_unittest(TARGET dominator_analysis_nested_loops_with_unreachables
+    SRCS ../function_utils.h
+         nested_loops_with_unreachables.cpp
+    LIBS SPIRV-Tools-opt
+)
+
+add_spvtools_unittest(TARGET dominator_analysis_switch_case_fallthrough
+    SRCS ../function_utils.h
+         switch_case_fallthrough.cpp
+    LIBS SPIRV-Tools-opt
+)
+
+add_spvtools_unittest(TARGET dominator_analysis_unreachable_for
+    SRCS ../function_utils.h
+         unreachable_for.cpp
+    LIBS SPIRV-Tools-opt
+)
+
+add_spvtools_unittest(TARGET dominator_analysis_unreachable_for_post
+    SRCS ../function_utils.h
+         unreachable_for_post.cpp
+    LIBS SPIRV-Tools-opt
+)
+
+add_spvtools_unittest(TARGET dominator_generated
+    SRCS ../function_utils.h
+         generated.cpp
+    LIBS SPIRV-Tools-opt
+)
diff --git a/test/opt/dominator_tree/generated.cpp b/test/opt/dominator_tree/generated.cpp
new file mode 100644 (file)
index 0000000..0ea55db
--- /dev/null
@@ -0,0 +1,798 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gmock/gmock.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "../assembly_builder.h"
+#include "../function_utils.h"
+#include "../pass_fixture.h"
+#include "../pass_utils.h"
+#include "opt/dominator_analysis.h"
+#include "opt/pass.h"
+
+namespace {
+
+using namespace spvtools;
+using ::testing::UnorderedElementsAre;
+
+using PassClassTest = PassTest<::testing::Test>;
+
+// Check that x dominates y, and
+//   if x != y then
+//      x strictly dominates y and
+//      y does not dominate x and
+//      y does not strictly dominate x
+//   if x == x then
+//      x does not strictly dominate itself
+void check_dominance(const opt::DominatorAnalysisBase& dom_tree,
+                     const ir::Function* fn, uint32_t x, uint32_t y) {
+  SCOPED_TRACE("Check dominance properties for Basic Block " +
+               std::to_string(x) + " and " + std::to_string(y));
+  EXPECT_TRUE(dom_tree.Dominates(spvtest::GetBasicBlock(fn, x),
+                                 spvtest::GetBasicBlock(fn, y)));
+  EXPECT_TRUE(dom_tree.Dominates(x, y));
+  if (x == y) {
+    EXPECT_FALSE(dom_tree.StrictlyDominates(x, x));
+  } else {
+    EXPECT_TRUE(dom_tree.StrictlyDominates(x, y));
+    EXPECT_FALSE(dom_tree.Dominates(y, x));
+    EXPECT_FALSE(dom_tree.StrictlyDominates(y, x));
+  }
+}
+
+// Check that x does not dominates y and vise versa
+void check_no_dominance(const opt::DominatorAnalysisBase& dom_tree,
+                        const ir::Function* fn, uint32_t x, uint32_t y) {
+  SCOPED_TRACE("Check no domination for Basic Block " + std::to_string(x) +
+               " and " + std::to_string(y));
+  EXPECT_FALSE(dom_tree.Dominates(spvtest::GetBasicBlock(fn, x),
+                                  spvtest::GetBasicBlock(fn, y)));
+  EXPECT_FALSE(dom_tree.Dominates(x, y));
+  EXPECT_FALSE(dom_tree.StrictlyDominates(spvtest::GetBasicBlock(fn, x),
+                                          spvtest::GetBasicBlock(fn, y)));
+  EXPECT_FALSE(dom_tree.StrictlyDominates(x, y));
+
+  EXPECT_FALSE(dom_tree.Dominates(spvtest::GetBasicBlock(fn, y),
+                                  spvtest::GetBasicBlock(fn, x)));
+  EXPECT_FALSE(dom_tree.Dominates(y, x));
+  EXPECT_FALSE(dom_tree.StrictlyDominates(spvtest::GetBasicBlock(fn, y),
+                                          spvtest::GetBasicBlock(fn, x)));
+  EXPECT_FALSE(dom_tree.StrictlyDominates(y, x));
+}
+
+TEST_F(PassClassTest, DominatorSimpleCFG) {
+  const std::string text = R"(
+               OpCapability Addresses
+               OpCapability Kernel
+               OpMemoryModel Physical64 OpenCL
+               OpEntryPoint Kernel %1 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeBool
+          %5 = OpTypeInt 32 0
+          %6 = OpConstant %5 0
+          %7 = OpConstantFalse %4
+          %8 = OpConstantTrue %4
+          %9 = OpConstant %5 1
+          %1 = OpFunction %2 None %3
+         %10 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+               OpSwitch %6 %12 1 %13
+         %12 = OpLabel
+               OpBranch %14
+         %13 = OpLabel
+               OpBranch %14
+         %14 = OpLabel
+               OpBranchConditional %8 %11 %15
+         %15 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  // clang-format on
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ir::Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+  const ir::Function* fn = spvtest::GetFunction(module, 1);
+  const ir::BasicBlock* entry = spvtest::GetBasicBlock(fn, 10);
+  EXPECT_EQ(entry, fn->entry().get())
+      << "The entry node is not the expected one";
+
+  // Test normal dominator tree
+  {
+    opt::DominatorAnalysis dom_tree;
+    ir::CFG cfg(module);
+    dom_tree.InitializeTree(fn, cfg);
+
+    // Inspect the actual tree
+    opt::DominatorTree& tree = dom_tree.GetDomTree();
+    EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block());
+    EXPECT_TRUE(
+        dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id()));
+
+    // (strict) dominance checks
+    for (uint32_t id : {10, 11, 12, 13, 14, 15})
+      check_dominance(dom_tree, fn, id, id);
+
+    check_dominance(dom_tree, fn, 10, 11);
+    check_dominance(dom_tree, fn, 10, 12);
+    check_dominance(dom_tree, fn, 10, 13);
+    check_dominance(dom_tree, fn, 10, 14);
+    check_dominance(dom_tree, fn, 10, 15);
+
+    check_dominance(dom_tree, fn, 11, 12);
+    check_dominance(dom_tree, fn, 11, 13);
+    check_dominance(dom_tree, fn, 11, 14);
+    check_dominance(dom_tree, fn, 11, 15);
+
+    check_dominance(dom_tree, fn, 14, 15);
+
+    check_no_dominance(dom_tree, fn, 12, 13);
+    check_no_dominance(dom_tree, fn, 12, 14);
+    check_no_dominance(dom_tree, fn, 13, 14);
+
+    // check with some invalid inputs
+    EXPECT_FALSE(dom_tree.Dominates(nullptr, entry));
+    EXPECT_FALSE(dom_tree.Dominates(entry, nullptr));
+    EXPECT_FALSE(dom_tree.Dominates(nullptr, nullptr));
+    EXPECT_FALSE(dom_tree.Dominates(10, 1));
+    EXPECT_FALSE(dom_tree.Dominates(1, 10));
+    EXPECT_FALSE(dom_tree.Dominates(1, 1));
+
+    EXPECT_FALSE(dom_tree.StrictlyDominates(nullptr, entry));
+    EXPECT_FALSE(dom_tree.StrictlyDominates(entry, nullptr));
+    EXPECT_FALSE(dom_tree.StrictlyDominates(nullptr, nullptr));
+    EXPECT_FALSE(dom_tree.StrictlyDominates(10, 1));
+    EXPECT_FALSE(dom_tree.StrictlyDominates(1, 10));
+    EXPECT_FALSE(dom_tree.StrictlyDominates(1, 1));
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr);
+    EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block());
+    EXPECT_EQ(dom_tree.ImmediateDominator(nullptr), nullptr);
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
+              spvtest::GetBasicBlock(fn, 10));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
+              spvtest::GetBasicBlock(fn, 11));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 13)),
+              spvtest::GetBasicBlock(fn, 11));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 14)),
+              spvtest::GetBasicBlock(fn, 11));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 15)),
+              spvtest::GetBasicBlock(fn, 14));
+  }
+
+  // Test post dominator tree
+  {
+    opt::PostDominatorAnalysis dom_tree;
+    ir::CFG cfg(module);
+    dom_tree.InitializeTree(fn, cfg);
+
+    // Inspect the actual tree
+    opt::DominatorTree& tree = dom_tree.GetDomTree();
+    EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_exit_block());
+    EXPECT_TRUE(dom_tree.Dominates(cfg.pseudo_exit_block()->id(), 15));
+
+    // (strict) dominance checks
+    for (uint32_t id : {10, 11, 12, 13, 14, 15})
+      check_dominance(dom_tree, fn, id, id);
+
+    check_dominance(dom_tree, fn, 14, 10);
+    check_dominance(dom_tree, fn, 14, 11);
+    check_dominance(dom_tree, fn, 14, 12);
+    check_dominance(dom_tree, fn, 14, 13);
+
+    check_dominance(dom_tree, fn, 15, 10);
+    check_dominance(dom_tree, fn, 15, 11);
+    check_dominance(dom_tree, fn, 15, 12);
+    check_dominance(dom_tree, fn, 15, 13);
+    check_dominance(dom_tree, fn, 15, 14);
+
+    check_no_dominance(dom_tree, fn, 13, 12);
+    check_no_dominance(dom_tree, fn, 12, 11);
+    check_no_dominance(dom_tree, fn, 13, 11);
+
+    // check with some invalid inputs
+    EXPECT_FALSE(dom_tree.Dominates(nullptr, entry));
+    EXPECT_FALSE(dom_tree.Dominates(entry, nullptr));
+    EXPECT_FALSE(dom_tree.Dominates(nullptr, nullptr));
+    EXPECT_FALSE(dom_tree.Dominates(10, 1));
+    EXPECT_FALSE(dom_tree.Dominates(1, 10));
+    EXPECT_FALSE(dom_tree.Dominates(1, 1));
+
+    EXPECT_FALSE(dom_tree.StrictlyDominates(nullptr, entry));
+    EXPECT_FALSE(dom_tree.StrictlyDominates(entry, nullptr));
+    EXPECT_FALSE(dom_tree.StrictlyDominates(nullptr, nullptr));
+    EXPECT_FALSE(dom_tree.StrictlyDominates(10, 1));
+    EXPECT_FALSE(dom_tree.StrictlyDominates(1, 10));
+    EXPECT_FALSE(dom_tree.StrictlyDominates(1, 1));
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(nullptr), nullptr);
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
+              spvtest::GetBasicBlock(fn, 14));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
+              spvtest::GetBasicBlock(fn, 14));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 13)),
+              spvtest::GetBasicBlock(fn, 14));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 14)),
+              spvtest::GetBasicBlock(fn, 15));
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 15)),
+              cfg.pseudo_exit_block());
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr);
+  }
+}
+
+TEST_F(PassClassTest, DominatorIrreducibleCFG) {
+  const std::string text = R"(
+               OpCapability Addresses
+               OpCapability Kernel
+               OpMemoryModel Physical64 OpenCL
+               OpEntryPoint Kernel %1 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeBool
+          %5 = OpTypeInt 32 0
+          %6 = OpConstantFalse %4
+          %7 = OpConstantTrue %4
+          %1 = OpFunction %2 None %3
+          %8 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+               OpBranchConditional %7 %10 %11
+         %10 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+               OpBranchConditional %7 %10 %12
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  // clang-format on
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ir::Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+  const ir::Function* fn = spvtest::GetFunction(module, 1);
+
+  const ir::BasicBlock* entry = spvtest::GetBasicBlock(fn, 8);
+  EXPECT_EQ(entry, fn->entry().get())
+      << "The entry node is not the expected one";
+
+  // Check normal dominator tree
+  {
+    opt::DominatorAnalysis dom_tree;
+    ir::CFG cfg(module);
+    dom_tree.InitializeTree(fn, cfg);
+
+    // Inspect the actual tree
+    opt::DominatorTree& tree = dom_tree.GetDomTree();
+    EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block());
+    EXPECT_TRUE(
+        dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id()));
+
+    // (strict) dominance checks
+    for (uint32_t id : {8, 9, 10, 11, 12})
+      check_dominance(dom_tree, fn, id, id);
+
+    check_dominance(dom_tree, fn, 8, 9);
+    check_dominance(dom_tree, fn, 8, 10);
+    check_dominance(dom_tree, fn, 8, 11);
+    check_dominance(dom_tree, fn, 8, 12);
+
+    check_dominance(dom_tree, fn, 9, 10);
+    check_dominance(dom_tree, fn, 9, 11);
+    check_dominance(dom_tree, fn, 9, 12);
+
+    check_dominance(dom_tree, fn, 11, 12);
+
+    check_no_dominance(dom_tree, fn, 10, 11);
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr);
+    EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block());
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 9)),
+              spvtest::GetBasicBlock(fn, 8));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)),
+              spvtest::GetBasicBlock(fn, 9));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
+              spvtest::GetBasicBlock(fn, 9));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
+              spvtest::GetBasicBlock(fn, 11));
+  }
+
+  // Check post dominator tree
+  {
+    opt::PostDominatorAnalysis dom_tree;
+    ir::CFG cfg(module);
+    dom_tree.InitializeTree(fn, cfg);
+
+    // Inspect the actual tree
+    opt::DominatorTree& tree = dom_tree.GetDomTree();
+    EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_exit_block());
+    EXPECT_TRUE(dom_tree.Dominates(cfg.pseudo_exit_block()->id(), 12));
+
+    // (strict) dominance checks
+    for (uint32_t id : {8, 9, 10, 11, 12})
+      check_dominance(dom_tree, fn, id, id);
+
+    check_dominance(dom_tree, fn, 12, 8);
+    check_dominance(dom_tree, fn, 12, 10);
+    check_dominance(dom_tree, fn, 12, 11);
+    check_dominance(dom_tree, fn, 12, 12);
+
+    check_dominance(dom_tree, fn, 11, 8);
+    check_dominance(dom_tree, fn, 11, 9);
+    check_dominance(dom_tree, fn, 11, 10);
+
+    check_dominance(dom_tree, fn, 9, 8);
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(entry),
+              spvtest::GetBasicBlock(fn, 9));
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 9)),
+              spvtest::GetBasicBlock(fn, 11));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)),
+              spvtest::GetBasicBlock(fn, 11));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
+              spvtest::GetBasicBlock(fn, 12));
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
+              cfg.pseudo_exit_block());
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr);
+  }
+}
+
+TEST_F(PassClassTest, DominatorLoopToSelf) {
+  const std::string text = R"(
+               OpCapability Addresses
+               OpCapability Kernel
+               OpMemoryModel Physical64 OpenCL
+               OpEntryPoint Kernel %1 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeBool
+          %5 = OpTypeInt 32 0
+          %6 = OpConstant %5 0
+          %7 = OpConstantFalse %4
+          %8 = OpConstantTrue %4
+          %9 = OpConstant %5 1
+          %1 = OpFunction %2 None %3
+         %10 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+               OpSwitch %6 %12 1 %11
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  // clang-format on
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ir::Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+  const ir::Function* fn = spvtest::GetFunction(module, 1);
+
+  const ir::BasicBlock* entry = spvtest::GetBasicBlock(fn, 10);
+  EXPECT_EQ(entry, fn->entry().get())
+      << "The entry node is not the expected one";
+
+  // Check normal dominator tree
+  {
+    opt::DominatorAnalysis dom_tree;
+    ir::CFG cfg(module);
+    dom_tree.InitializeTree(fn, cfg);
+
+    // Inspect the actual tree
+    opt::DominatorTree& tree = dom_tree.GetDomTree();
+    EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block());
+    EXPECT_TRUE(
+        dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id()));
+
+    // (strict) dominance checks
+    for (uint32_t id : {10, 11, 12}) check_dominance(dom_tree, fn, id, id);
+
+    check_dominance(dom_tree, fn, 10, 11);
+    check_dominance(dom_tree, fn, 10, 12);
+    check_dominance(dom_tree, fn, 11, 12);
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr);
+    EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block());
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
+              spvtest::GetBasicBlock(fn, 10));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
+              spvtest::GetBasicBlock(fn, 11));
+  }
+
+  // Check post dominator tree
+  {
+    opt::PostDominatorAnalysis dom_tree;
+    ir::CFG cfg(module);
+    dom_tree.InitializeTree(fn, cfg);
+
+    // Inspect the actual tree
+    opt::DominatorTree& tree = dom_tree.GetDomTree();
+    EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_exit_block());
+    EXPECT_TRUE(dom_tree.Dominates(cfg.pseudo_exit_block()->id(), 12));
+
+    // (strict) dominance checks
+    for (uint32_t id : {10, 11, 12}) check_dominance(dom_tree, fn, id, id);
+
+    check_dominance(dom_tree, fn, 12, 10);
+    check_dominance(dom_tree, fn, 12, 11);
+    check_dominance(dom_tree, fn, 12, 12);
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(entry),
+              spvtest::GetBasicBlock(fn, 11));
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
+              spvtest::GetBasicBlock(fn, 12));
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr);
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
+              cfg.pseudo_exit_block());
+  }
+}
+
+TEST_F(PassClassTest, DominatorUnreachableInLoop) {
+  const std::string text = R"(
+               OpCapability Addresses
+               OpCapability Kernel
+               OpMemoryModel Physical64 OpenCL
+               OpEntryPoint Kernel %1 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeBool
+          %5 = OpTypeInt 32 0
+          %6 = OpConstant %5 0
+          %7 = OpConstantFalse %4
+          %8 = OpConstantTrue %4
+          %9 = OpConstant %5 1
+          %1 = OpFunction %2 None %3
+         %10 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+               OpSwitch %6 %12 1 %13
+         %12 = OpLabel
+               OpBranch %14
+         %13 = OpLabel
+               OpUnreachable
+         %14 = OpLabel
+               OpBranchConditional %8 %11 %15
+         %15 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  // clang-format on
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ir::Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+  const ir::Function* fn = spvtest::GetFunction(module, 1);
+
+  const ir::BasicBlock* entry = spvtest::GetBasicBlock(fn, 10);
+  EXPECT_EQ(entry, fn->entry().get())
+      << "The entry node is not the expected one";
+
+  // Check normal dominator tree
+  {
+    opt::DominatorAnalysis dom_tree;
+    ir::CFG cfg(module);
+    dom_tree.InitializeTree(fn, cfg);
+
+    // Inspect the actual tree
+    opt::DominatorTree& tree = dom_tree.GetDomTree();
+    EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block());
+    EXPECT_TRUE(
+        dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id()));
+
+    // (strict) dominance checks
+    for (uint32_t id : {10, 11, 12, 13, 14, 15})
+      check_dominance(dom_tree, fn, id, id);
+
+    check_dominance(dom_tree, fn, 10, 11);
+    check_dominance(dom_tree, fn, 10, 13);
+    check_dominance(dom_tree, fn, 10, 12);
+    check_dominance(dom_tree, fn, 10, 14);
+    check_dominance(dom_tree, fn, 10, 15);
+
+    check_dominance(dom_tree, fn, 11, 12);
+    check_dominance(dom_tree, fn, 11, 13);
+    check_dominance(dom_tree, fn, 11, 14);
+    check_dominance(dom_tree, fn, 11, 15);
+
+    check_dominance(dom_tree, fn, 12, 14);
+    check_dominance(dom_tree, fn, 12, 15);
+
+    check_dominance(dom_tree, fn, 14, 15);
+
+    check_no_dominance(dom_tree, fn, 13, 12);
+    check_no_dominance(dom_tree, fn, 13, 14);
+    check_no_dominance(dom_tree, fn, 13, 15);
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr);
+    EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block());
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
+              spvtest::GetBasicBlock(fn, 10));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
+              spvtest::GetBasicBlock(fn, 11));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 13)),
+              spvtest::GetBasicBlock(fn, 11));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 14)),
+              spvtest::GetBasicBlock(fn, 12));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 15)),
+              spvtest::GetBasicBlock(fn, 14));
+  }
+
+  // Check post dominator tree.
+  {
+    opt::PostDominatorAnalysis dom_tree;
+    ir::CFG cfg(module);
+    dom_tree.InitializeTree(fn, cfg);
+
+    // (strict) dominance checks.
+    for (uint32_t id : {10, 11, 12, 13, 14, 15})
+      check_dominance(dom_tree, fn, id, id);
+
+    check_no_dominance(dom_tree, fn, 15, 10);
+    check_no_dominance(dom_tree, fn, 15, 11);
+    check_no_dominance(dom_tree, fn, 15, 12);
+    check_no_dominance(dom_tree, fn, 15, 13);
+    check_no_dominance(dom_tree, fn, 15, 14);
+
+    check_dominance(dom_tree, fn, 14, 12);
+
+    check_no_dominance(dom_tree, fn, 13, 10);
+    check_no_dominance(dom_tree, fn, 13, 11);
+    check_no_dominance(dom_tree, fn, 13, 12);
+    check_no_dominance(dom_tree, fn, 13, 14);
+    check_no_dominance(dom_tree, fn, 13, 15);
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)),
+              spvtest::GetBasicBlock(fn, 11));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
+              spvtest::GetBasicBlock(fn, 14));
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr);
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 15)),
+              cfg.pseudo_exit_block());
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 13)),
+              cfg.pseudo_exit_block());
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 14)),
+              cfg.pseudo_exit_block());
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
+              cfg.pseudo_exit_block());
+  }
+}
+
+TEST_F(PassClassTest, DominatorInfinitLoop) {
+  const std::string text = R"(
+               OpCapability Addresses
+               OpCapability Kernel
+               OpMemoryModel Physical64 OpenCL
+               OpEntryPoint Kernel %1 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeBool
+          %5 = OpTypeInt 32 0
+          %6 = OpConstant %5 0
+          %7 = OpConstantFalse %4
+          %8 = OpConstantTrue %4
+          %9 = OpConstant %5 1
+          %1 = OpFunction %2 None %3
+         %10 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+               OpSwitch %6 %12 1 %13
+         %12 = OpLabel
+               OpReturn
+         %13 = OpLabel
+               OpBranch %13
+               OpFunctionEnd
+)";
+  // clang-format on
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ir::Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+  const ir::Function* fn = spvtest::GetFunction(module, 1);
+
+  const ir::BasicBlock* entry = spvtest::GetBasicBlock(fn, 10);
+  EXPECT_EQ(entry, fn->entry().get())
+      << "The entry node is not the expected one";
+  // Check normal dominator tree
+  {
+    opt::DominatorAnalysis dom_tree;
+    ir::CFG cfg(module);
+    dom_tree.InitializeTree(fn, cfg);
+
+    // Inspect the actual tree
+    opt::DominatorTree& tree = dom_tree.GetDomTree();
+    EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block());
+    EXPECT_TRUE(
+        dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id()));
+
+    // (strict) dominance checks
+    for (uint32_t id : {10, 11, 12, 13}) check_dominance(dom_tree, fn, id, id);
+
+    check_dominance(dom_tree, fn, 10, 11);
+    check_dominance(dom_tree, fn, 10, 12);
+    check_dominance(dom_tree, fn, 10, 13);
+
+    check_dominance(dom_tree, fn, 11, 12);
+    check_dominance(dom_tree, fn, 11, 13);
+
+    check_no_dominance(dom_tree, fn, 13, 12);
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr);
+    EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block());
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
+              spvtest::GetBasicBlock(fn, 10));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
+              spvtest::GetBasicBlock(fn, 11));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 13)),
+              spvtest::GetBasicBlock(fn, 11));
+  }
+
+  // Check post dominator tree
+  {
+    opt::PostDominatorAnalysis dom_tree;
+    ir::CFG cfg(module);
+    dom_tree.InitializeTree(fn, cfg);
+
+    // Inspect the actual tree
+    opt::DominatorTree& tree = dom_tree.GetDomTree();
+    EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_exit_block());
+    EXPECT_TRUE(dom_tree.Dominates(cfg.pseudo_exit_block()->id(), 12));
+
+    // (strict) dominance checks
+    for (uint32_t id : {10, 11, 12}) check_dominance(dom_tree, fn, id, id);
+
+    check_dominance(dom_tree, fn, 12, 11);
+    check_dominance(dom_tree, fn, 12, 10);
+
+    // 13 should be completely out of tree as it's unreachable from exit nodes
+    check_no_dominance(dom_tree, fn, 12, 13);
+    check_no_dominance(dom_tree, fn, 11, 13);
+    check_no_dominance(dom_tree, fn, 10, 13);
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr);
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
+              cfg.pseudo_exit_block());
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)),
+              spvtest::GetBasicBlock(fn, 11));
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
+              spvtest::GetBasicBlock(fn, 12));
+  }
+}
+
+TEST_F(PassClassTest, DominatorUnreachableFromEntry) {
+  const std::string text = R"(
+               OpCapability Addresses
+               OpCapability Addresses
+               OpCapability Kernel
+               OpMemoryModel Physical64 OpenCL
+               OpEntryPoint Kernel %1 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeBool
+          %5 = OpTypeInt 32 0
+          %6 = OpConstantFalse %4
+          %7 = OpConstantTrue %4
+          %1 = OpFunction %2 None %3
+          %8 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+               OpReturn
+         %10 = OpLabel
+               OpBranch %9
+               OpFunctionEnd
+)";
+  // clang-format on
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ir::Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+  const ir::Function* fn = spvtest::GetFunction(module, 1);
+
+  const ir::BasicBlock* entry = spvtest::GetBasicBlock(fn, 8);
+  EXPECT_EQ(entry, fn->entry().get())
+      << "The entry node is not the expected one";
+
+  // Check dominator tree
+  {
+    opt::DominatorAnalysis dom_tree;
+    ir::CFG cfg(module);
+    dom_tree.InitializeTree(fn, cfg);
+    // Inspect the actual tree
+    opt::DominatorTree& tree = dom_tree.GetDomTree();
+    EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block());
+    EXPECT_TRUE(
+        dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id()));
+
+    // (strict) dominance checks
+    for (uint32_t id : {8, 9}) check_dominance(dom_tree, fn, id, id);
+
+    check_dominance(dom_tree, fn, 8, 9);
+
+    check_no_dominance(dom_tree, fn, 10, 8);
+    check_no_dominance(dom_tree, fn, 10, 9);
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr);
+    EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block());
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 9)),
+              spvtest::GetBasicBlock(fn, 8));
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)),
+              nullptr);
+  }
+
+  // Check post dominator tree
+  {
+    opt::PostDominatorAnalysis dom_tree;
+    ir::CFG cfg(module);
+    dom_tree.InitializeTree(fn, cfg);
+
+    // Inspect the actual tree
+    opt::DominatorTree& tree = dom_tree.GetDomTree();
+    EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_exit_block());
+    EXPECT_TRUE(dom_tree.Dominates(cfg.pseudo_exit_block()->id(), 9));
+
+    // (strict) dominance checks
+    for (uint32_t id : {8, 9, 10}) check_dominance(dom_tree, fn, id, id);
+
+    check_dominance(dom_tree, fn, 9, 8);
+    check_dominance(dom_tree, fn, 9, 10);
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(entry),
+              spvtest::GetBasicBlock(fn, 9));
+
+    EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr);
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 9)),
+              cfg.pseudo_exit_block());
+    EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)),
+              spvtest::GetBasicBlock(fn, 9));
+  }
+}
+
+}  // namespace
diff --git a/test/opt/dominator_tree/nested_ifs.cpp b/test/opt/dominator_tree/nested_ifs.cpp
new file mode 100644 (file)
index 0000000..fbb2d12
--- /dev/null
@@ -0,0 +1,152 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gmock/gmock.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "../assembly_builder.h"
+#include "../function_utils.h"
+#include "../pass_fixture.h"
+#include "../pass_utils.h"
+#include "opt/dominator_analysis.h"
+#include "opt/pass.h"
+
+namespace {
+
+using namespace spvtools;
+using ::testing::UnorderedElementsAre;
+
+using PassClassTest = PassTest<::testing::Test>;
+
+/*
+  Generated from the following GLSL
+#version 330 core
+layout(location = 0) out vec4 v;
+void main(){
+  if (true) {
+    if (true) {
+      v = vec4(1,1,1,1);
+    } else {
+      v = vec4(2,2,2,2);
+    }
+  } else {
+    if (true) {
+      v = vec4(3,3,3,3);
+    } else {
+      v = vec4(4,4,4,4);
+    }
+  }
+}
+*/
+TEST_F(PassClassTest, UnreachableNestedIfs) {
+  const std::string text = R"(
+        OpCapability Shader
+        %1 = OpExtInstImport "GLSL.std.450"
+             OpMemoryModel Logical GLSL450
+             OpEntryPoint Fragment %4 "main" %15
+             OpExecutionMode %4 OriginUpperLeft
+             OpSource GLSL 330
+             OpName %4 "main"
+             OpName %15 "v"
+             OpDecorate %15 Location 0
+        %2 = OpTypeVoid
+        %3 = OpTypeFunction %2
+        %6 = OpTypeBool
+        %7 = OpConstantTrue %6
+       %12 = OpTypeFloat 32
+       %13 = OpTypeVector %12 4
+       %14 = OpTypePointer Output %13
+       %15 = OpVariable %14 Output
+       %16 = OpConstant %12 1
+       %17 = OpConstantComposite %13 %16 %16 %16 %16
+       %19 = OpConstant %12 2
+       %20 = OpConstantComposite %13 %19 %19 %19 %19
+       %24 = OpConstant %12 3
+       %25 = OpConstantComposite %13 %24 %24 %24 %24
+       %27 = OpConstant %12 4
+       %28 = OpConstantComposite %13 %27 %27 %27 %27
+        %4 = OpFunction %2 None %3
+        %5 = OpLabel
+             OpSelectionMerge %9 None
+             OpBranchConditional %7 %8 %21
+        %8 = OpLabel
+             OpSelectionMerge %11 None
+             OpBranchConditional %7 %10 %18
+       %10 = OpLabel
+             OpStore %15 %17
+             OpBranch %11
+       %18 = OpLabel
+             OpStore %15 %20
+             OpBranch %11
+       %11 = OpLabel
+             OpBranch %9
+       %21 = OpLabel
+             OpSelectionMerge %23 None
+             OpBranchConditional %7 %22 %26
+       %22 = OpLabel
+             OpStore %15 %25
+             OpBranch %23
+       %26 = OpLabel
+             OpStore %15 %28
+             OpBranch %23
+       %23 = OpLabel
+             OpBranch %9
+        %9 = OpLabel
+             OpReturn
+             OpFunctionEnd
+)";
+  // clang-format on
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ir::Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+
+  const ir::Function* f = spvtest::GetFunction(module, 4);
+
+  ir::CFG cfg(module);
+  opt::DominatorAnalysis* analysis = context->GetDominatorAnalysis(f, cfg);
+
+  EXPECT_TRUE(analysis->Dominates(5, 8));
+  EXPECT_TRUE(analysis->Dominates(5, 9));
+  EXPECT_TRUE(analysis->Dominates(5, 21));
+  EXPECT_TRUE(analysis->Dominates(5, 18));
+  EXPECT_TRUE(analysis->Dominates(5, 10));
+  EXPECT_TRUE(analysis->Dominates(5, 11));
+  EXPECT_TRUE(analysis->Dominates(5, 23));
+  EXPECT_TRUE(analysis->Dominates(5, 22));
+  EXPECT_TRUE(analysis->Dominates(5, 26));
+  EXPECT_TRUE(analysis->Dominates(8, 18));
+  EXPECT_TRUE(analysis->Dominates(8, 10));
+  EXPECT_TRUE(analysis->Dominates(8, 11));
+  EXPECT_TRUE(analysis->Dominates(21, 23));
+  EXPECT_TRUE(analysis->Dominates(21, 22));
+  EXPECT_TRUE(analysis->Dominates(21, 26));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 8));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 9));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 21));
+  EXPECT_TRUE(analysis->StrictlyDominates(8, 18));
+  EXPECT_TRUE(analysis->StrictlyDominates(8, 10));
+  EXPECT_TRUE(analysis->StrictlyDominates(8, 11));
+  EXPECT_TRUE(analysis->StrictlyDominates(21, 23));
+  EXPECT_TRUE(analysis->StrictlyDominates(21, 22));
+  EXPECT_TRUE(analysis->StrictlyDominates(21, 26));
+}
+
+}  // namespace
diff --git a/test/opt/dominator_tree/nested_ifs_post.cpp b/test/opt/dominator_tree/nested_ifs_post.cpp
new file mode 100644 (file)
index 0000000..a112af9
--- /dev/null
@@ -0,0 +1,157 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gmock/gmock.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "../assembly_builder.h"
+#include "../function_utils.h"
+#include "../pass_fixture.h"
+#include "../pass_utils.h"
+#include "opt/dominator_analysis.h"
+#include "opt/pass.h"
+
+namespace {
+
+using namespace spvtools;
+using ::testing::UnorderedElementsAre;
+
+using PassClassTest = PassTest<::testing::Test>;
+
+/*
+  Generated from the following GLSL
+#version 330 core
+layout(location = 0) out vec4 v;
+void main(){
+  if (true) {
+    if (true) {
+      v = vec4(1,1,1,1);
+    } else {
+      v = vec4(2,2,2,2);
+    }
+  } else {
+    if (true) {
+      v = vec4(3,3,3,3);
+    } else {
+      v = vec4(4,4,4,4);
+    }
+  }
+}
+*/
+TEST_F(PassClassTest, UnreachableNestedIfs) {
+  const std::string text = R"(
+        OpCapability Shader
+        %1 = OpExtInstImport "GLSL.std.450"
+             OpMemoryModel Logical GLSL450
+             OpEntryPoint Fragment %4 "main" %15
+             OpExecutionMode %4 OriginUpperLeft
+             OpSource GLSL 330
+             OpName %4 "main"
+             OpName %15 "v"
+             OpDecorate %15 Location 0
+        %2 = OpTypeVoid
+        %3 = OpTypeFunction %2
+        %6 = OpTypeBool
+        %7 = OpConstantTrue %6
+       %12 = OpTypeFloat 32
+       %13 = OpTypeVector %12 4
+       %14 = OpTypePointer Output %13
+       %15 = OpVariable %14 Output
+       %16 = OpConstant %12 1
+       %17 = OpConstantComposite %13 %16 %16 %16 %16
+       %19 = OpConstant %12 2
+       %20 = OpConstantComposite %13 %19 %19 %19 %19
+       %24 = OpConstant %12 3
+       %25 = OpConstantComposite %13 %24 %24 %24 %24
+       %27 = OpConstant %12 4
+       %28 = OpConstantComposite %13 %27 %27 %27 %27
+        %4 = OpFunction %2 None %3
+        %5 = OpLabel
+             OpSelectionMerge %9 None
+             OpBranchConditional %7 %8 %21
+        %8 = OpLabel
+             OpSelectionMerge %11 None
+             OpBranchConditional %7 %10 %18
+       %10 = OpLabel
+             OpStore %15 %17
+             OpBranch %11
+       %18 = OpLabel
+             OpStore %15 %20
+             OpBranch %11
+       %11 = OpLabel
+             OpBranch %9
+       %21 = OpLabel
+             OpSelectionMerge %23 None
+             OpBranchConditional %7 %22 %26
+       %22 = OpLabel
+             OpStore %15 %25
+             OpBranch %23
+       %26 = OpLabel
+             OpStore %15 %28
+             OpBranch %23
+       %23 = OpLabel
+             OpBranch %9
+        %9 = OpLabel
+             OpReturn
+             OpFunctionEnd
+)";
+  // clang-format on
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ir::Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+
+  const ir::Function* f = spvtest::GetFunction(module, 4);
+
+  ir::CFG cfg(module);
+  opt::PostDominatorAnalysis* analysis =
+      context->GetPostDominatorAnalysis(f, cfg);
+
+  EXPECT_TRUE(analysis->Dominates(5, 5));
+  EXPECT_TRUE(analysis->Dominates(8, 8));
+  EXPECT_TRUE(analysis->Dominates(9, 9));
+  EXPECT_TRUE(analysis->Dominates(10, 10));
+  EXPECT_TRUE(analysis->Dominates(11, 11));
+  EXPECT_TRUE(analysis->Dominates(18, 18));
+  EXPECT_TRUE(analysis->Dominates(21, 21));
+  EXPECT_TRUE(analysis->Dominates(22, 22));
+  EXPECT_TRUE(analysis->Dominates(23, 23));
+  EXPECT_TRUE(analysis->Dominates(26, 26));
+  EXPECT_TRUE(analysis->Dominates(9, 5));
+  EXPECT_TRUE(analysis->Dominates(9, 11));
+  EXPECT_TRUE(analysis->Dominates(9, 23));
+  EXPECT_TRUE(analysis->Dominates(11, 10));
+  EXPECT_TRUE(analysis->Dominates(11, 18));
+  EXPECT_TRUE(analysis->Dominates(11, 8));
+  EXPECT_TRUE(analysis->Dominates(23, 22));
+  EXPECT_TRUE(analysis->Dominates(23, 26));
+  EXPECT_TRUE(analysis->Dominates(23, 21));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(9, 5));
+  EXPECT_TRUE(analysis->StrictlyDominates(9, 11));
+  EXPECT_TRUE(analysis->StrictlyDominates(9, 23));
+  EXPECT_TRUE(analysis->StrictlyDominates(11, 10));
+  EXPECT_TRUE(analysis->StrictlyDominates(11, 18));
+  EXPECT_TRUE(analysis->StrictlyDominates(11, 8));
+  EXPECT_TRUE(analysis->StrictlyDominates(23, 22));
+  EXPECT_TRUE(analysis->StrictlyDominates(23, 26));
+  EXPECT_TRUE(analysis->StrictlyDominates(23, 21));
+}
+
+}  // namespace
diff --git a/test/opt/dominator_tree/nested_loops.cpp b/test/opt/dominator_tree/nested_loops.cpp
new file mode 100644 (file)
index 0000000..4e5c072
--- /dev/null
@@ -0,0 +1,433 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gmock/gmock.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "../assembly_builder.h"
+#include "../function_utils.h"
+#include "../pass_fixture.h"
+#include "../pass_utils.h"
+#include "opt/dominator_analysis.h"
+#include "opt/pass.h"
+
+namespace {
+
+using namespace spvtools;
+using ::testing::UnorderedElementsAre;
+
+using PassClassTest = PassTest<::testing::Test>;
+
+/*
+  Generated from the following GLSL
+#version 440 core
+layout(location = 0) out vec4 v;
+layout(location = 1) in vec4 in_val;
+void main() {
+  for (int i = 0; i < in_val.x; ++i) {
+    for (int j = 0; j < in_val.y; j++) {
+    }
+  }
+  for (int i = 0; i < in_val.x; ++i) {
+    for (int j = 0; j < in_val.y; j++) {
+    }
+    if (in_val.z == in_val.w) {
+      break;
+    }
+  }
+  int i = 0;
+  while (i < in_val.x) {
+    ++i;
+    for (int j = 0; j < 1; j++) {
+      for (int k = 0; k < 1; k++) {
+      }
+    }
+  }
+  i = 0;
+  while (i < in_val.x) {
+    ++i;
+    if (in_val.z == in_val.w) {
+      continue;
+    }
+    for (int j = 0; j < 1; j++) {
+      for (int k = 0; k < 1; k++) {
+      }
+      if (in_val.z == in_val.w) {
+        break;
+      }
+    }
+  }
+  v = vec4(1,1,1,1);
+}
+*/
+TEST_F(PassClassTest, BasicVisitFromEntryPoint) {
+  const std::string text = R"(
+    OpCapability Shader
+    %1 = OpExtInstImport "GLSL.std.450"
+         OpMemoryModel Logical GLSL450
+         OpEntryPoint Fragment %4 "main" %20 %163
+         OpExecutionMode %4 OriginUpperLeft
+         OpSource GLSL 440
+         OpName %4 "main"
+         OpName %8 "i"
+         OpName %20 "in_val"
+         OpName %28 "j"
+         OpName %45 "i"
+         OpName %56 "j"
+         OpName %81 "i"
+         OpName %94 "j"
+         OpName %102 "k"
+         OpName %134 "j"
+         OpName %142 "k"
+         OpName %163 "v"
+         OpDecorate %20 Location 1
+         OpDecorate %163 Location 0
+    %2 = OpTypeVoid
+    %3 = OpTypeFunction %2
+    %6 = OpTypeInt 32 1
+    %7 = OpTypePointer Function %6
+    %9 = OpConstant %6 0
+   %16 = OpTypeFloat 32
+   %18 = OpTypeVector %16 4
+   %19 = OpTypePointer Input %18
+   %20 = OpVariable %19 Input
+   %21 = OpTypeInt 32 0
+   %22 = OpConstant %21 0
+   %23 = OpTypePointer Input %16
+   %26 = OpTypeBool
+   %36 = OpConstant %21 1
+   %41 = OpConstant %6 1
+   %69 = OpConstant %21 2
+   %72 = OpConstant %21 3
+  %162 = OpTypePointer Output %18
+  %163 = OpVariable %162 Output
+  %164 = OpConstant %16 1
+  %165 = OpConstantComposite %18 %164 %164 %164 %164
+    %4 = OpFunction %2 None %3
+    %5 = OpLabel
+    %8 = OpVariable %7 Function
+   %28 = OpVariable %7 Function
+   %45 = OpVariable %7 Function
+   %56 = OpVariable %7 Function
+   %81 = OpVariable %7 Function
+   %94 = OpVariable %7 Function
+  %102 = OpVariable %7 Function
+  %134 = OpVariable %7 Function
+  %142 = OpVariable %7 Function
+         OpStore %8 %9
+         OpBranch %10
+   %10 = OpLabel
+         OpLoopMerge %12 %13 None
+         OpBranch %14
+   %14 = OpLabel
+   %15 = OpLoad %6 %8
+   %17 = OpConvertSToF %16 %15
+   %24 = OpAccessChain %23 %20 %22
+   %25 = OpLoad %16 %24
+   %27 = OpFOrdLessThan %26 %17 %25
+         OpBranchConditional %27 %11 %12
+   %11 = OpLabel
+         OpStore %28 %9
+         OpBranch %29
+   %29 = OpLabel
+         OpLoopMerge %31 %32 None
+         OpBranch %33
+   %33 = OpLabel
+   %34 = OpLoad %6 %28
+   %35 = OpConvertSToF %16 %34
+   %37 = OpAccessChain %23 %20 %36
+   %38 = OpLoad %16 %37
+   %39 = OpFOrdLessThan %26 %35 %38
+         OpBranchConditional %39 %30 %31
+   %30 = OpLabel
+         OpBranch %32
+   %32 = OpLabel
+   %40 = OpLoad %6 %28
+   %42 = OpIAdd %6 %40 %41
+         OpStore %28 %42
+         OpBranch %29
+   %31 = OpLabel
+         OpBranch %13
+   %13 = OpLabel
+   %43 = OpLoad %6 %8
+   %44 = OpIAdd %6 %43 %41
+         OpStore %8 %44
+         OpBranch %10
+   %12 = OpLabel
+         OpStore %45 %9
+         OpBranch %46
+   %46 = OpLabel
+         OpLoopMerge %48 %49 None
+         OpBranch %50
+   %50 = OpLabel
+   %51 = OpLoad %6 %45
+   %52 = OpConvertSToF %16 %51
+   %53 = OpAccessChain %23 %20 %22
+   %54 = OpLoad %16 %53
+   %55 = OpFOrdLessThan %26 %52 %54
+         OpBranchConditional %55 %47 %48
+   %47 = OpLabel
+         OpStore %56 %9
+         OpBranch %57
+   %57 = OpLabel
+         OpLoopMerge %59 %60 None
+         OpBranch %61
+   %61 = OpLabel
+   %62 = OpLoad %6 %56
+   %63 = OpConvertSToF %16 %62
+   %64 = OpAccessChain %23 %20 %36
+   %65 = OpLoad %16 %64
+   %66 = OpFOrdLessThan %26 %63 %65
+         OpBranchConditional %66 %58 %59
+   %58 = OpLabel
+         OpBranch %60
+   %60 = OpLabel
+   %67 = OpLoad %6 %56
+   %68 = OpIAdd %6 %67 %41
+         OpStore %56 %68
+         OpBranch %57
+   %59 = OpLabel
+   %70 = OpAccessChain %23 %20 %69
+   %71 = OpLoad %16 %70
+   %73 = OpAccessChain %23 %20 %72
+   %74 = OpLoad %16 %73
+   %75 = OpFOrdEqual %26 %71 %74
+         OpSelectionMerge %77 None
+         OpBranchConditional %75 %76 %77
+   %76 = OpLabel
+         OpBranch %48
+   %77 = OpLabel
+         OpBranch %49
+   %49 = OpLabel
+   %79 = OpLoad %6 %45
+   %80 = OpIAdd %6 %79 %41
+         OpStore %45 %80
+         OpBranch %46
+   %48 = OpLabel
+         OpStore %81 %9
+         OpBranch %82
+   %82 = OpLabel
+         OpLoopMerge %84 %85 None
+         OpBranch %86
+   %86 = OpLabel
+   %87 = OpLoad %6 %81
+   %88 = OpConvertSToF %16 %87
+   %89 = OpAccessChain %23 %20 %22
+   %90 = OpLoad %16 %89
+   %91 = OpFOrdLessThan %26 %88 %90
+         OpBranchConditional %91 %83 %84
+   %83 = OpLabel
+   %92 = OpLoad %6 %81
+   %93 = OpIAdd %6 %92 %41
+         OpStore %81 %93
+         OpStore %94 %9
+         OpBranch %95
+   %95 = OpLabel
+         OpLoopMerge %97 %98 None
+         OpBranch %99
+   %99 = OpLabel
+  %100 = OpLoad %6 %94
+  %101 = OpSLessThan %26 %100 %41
+         OpBranchConditional %101 %96 %97
+   %96 = OpLabel
+         OpStore %102 %9
+         OpBranch %103
+  %103 = OpLabel
+         OpLoopMerge %105 %106 None
+         OpBranch %107
+  %107 = OpLabel
+  %108 = OpLoad %6 %102
+  %109 = OpSLessThan %26 %108 %41
+         OpBranchConditional %109 %104 %105
+  %104 = OpLabel
+         OpBranch %106
+  %106 = OpLabel
+  %110 = OpLoad %6 %102
+  %111 = OpIAdd %6 %110 %41
+         OpStore %102 %111
+         OpBranch %103
+  %105 = OpLabel
+         OpBranch %98
+   %98 = OpLabel
+  %112 = OpLoad %6 %94
+  %113 = OpIAdd %6 %112 %41
+         OpStore %94 %113
+         OpBranch %95
+   %97 = OpLabel
+         OpBranch %85
+   %85 = OpLabel
+         OpBranch %82
+   %84 = OpLabel
+         OpStore %81 %9
+         OpBranch %114
+  %114 = OpLabel
+         OpLoopMerge %116 %117 None
+         OpBranch %118
+  %118 = OpLabel
+  %119 = OpLoad %6 %81
+  %120 = OpConvertSToF %16 %119
+  %121 = OpAccessChain %23 %20 %22
+  %122 = OpLoad %16 %121
+  %123 = OpFOrdLessThan %26 %120 %122
+         OpBranchConditional %123 %115 %116
+  %115 = OpLabel
+  %124 = OpLoad %6 %81
+  %125 = OpIAdd %6 %124 %41
+         OpStore %81 %125
+  %126 = OpAccessChain %23 %20 %69
+  %127 = OpLoad %16 %126
+  %128 = OpAccessChain %23 %20 %72
+  %129 = OpLoad %16 %128
+  %130 = OpFOrdEqual %26 %127 %129
+         OpSelectionMerge %132 None
+         OpBranchConditional %130 %131 %132
+  %131 = OpLabel
+         OpBranch %117
+  %132 = OpLabel
+         OpStore %134 %9
+         OpBranch %135
+  %135 = OpLabel
+         OpLoopMerge %137 %138 None
+         OpBranch %139
+  %139 = OpLabel
+  %140 = OpLoad %6 %134
+  %141 = OpSLessThan %26 %140 %41
+         OpBranchConditional %141 %136 %137
+  %136 = OpLabel
+         OpStore %142 %9
+         OpBranch %143
+  %143 = OpLabel
+         OpLoopMerge %145 %146 None
+         OpBranch %147
+  %147 = OpLabel
+  %148 = OpLoad %6 %142
+  %149 = OpSLessThan %26 %148 %41
+         OpBranchConditional %149 %144 %145
+  %144 = OpLabel
+         OpBranch %146
+  %146 = OpLabel
+  %150 = OpLoad %6 %142
+  %151 = OpIAdd %6 %150 %41
+         OpStore %142 %151
+         OpBranch %143
+  %145 = OpLabel
+  %152 = OpAccessChain %23 %20 %69
+  %153 = OpLoad %16 %152
+  %154 = OpAccessChain %23 %20 %72
+  %155 = OpLoad %16 %154
+  %156 = OpFOrdEqual %26 %153 %155
+         OpSelectionMerge %158 None
+         OpBranchConditional %156 %157 %158
+  %157 = OpLabel
+         OpBranch %137
+  %158 = OpLabel
+         OpBranch %138
+  %138 = OpLabel
+  %160 = OpLoad %6 %134
+  %161 = OpIAdd %6 %160 %41
+         OpStore %134 %161
+         OpBranch %135
+  %137 = OpLabel
+         OpBranch %117
+  %117 = OpLabel
+         OpBranch %114
+  %116 = OpLabel
+         OpStore %163 %165
+         OpReturn
+         OpFunctionEnd
+)";
+  // clang-format on
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ir::Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+
+  const ir::Function* f = spvtest::GetFunction(module, 4);
+  ir::CFG cfg(module);
+  opt::DominatorAnalysis* analysis = context->GetDominatorAnalysis(f, cfg);
+
+  EXPECT_TRUE(analysis->Dominates(5, 10));
+  EXPECT_TRUE(analysis->Dominates(5, 46));
+  EXPECT_TRUE(analysis->Dominates(5, 82));
+  EXPECT_TRUE(analysis->Dominates(5, 114));
+  EXPECT_TRUE(analysis->Dominates(5, 116));
+
+  EXPECT_TRUE(analysis->Dominates(10, 14));
+  EXPECT_TRUE(analysis->Dominates(10, 11));
+  EXPECT_TRUE(analysis->Dominates(10, 29));
+  EXPECT_TRUE(analysis->Dominates(10, 33));
+  EXPECT_TRUE(analysis->Dominates(10, 30));
+  EXPECT_TRUE(analysis->Dominates(10, 32));
+  EXPECT_TRUE(analysis->Dominates(10, 31));
+  EXPECT_TRUE(analysis->Dominates(10, 13));
+  EXPECT_TRUE(analysis->Dominates(10, 12));
+
+  EXPECT_TRUE(analysis->Dominates(12, 46));
+
+  EXPECT_TRUE(analysis->Dominates(46, 50));
+  EXPECT_TRUE(analysis->Dominates(46, 47));
+  EXPECT_TRUE(analysis->Dominates(46, 57));
+  EXPECT_TRUE(analysis->Dominates(46, 61));
+  EXPECT_TRUE(analysis->Dominates(46, 58));
+  EXPECT_TRUE(analysis->Dominates(46, 60));
+  EXPECT_TRUE(analysis->Dominates(46, 59));
+  EXPECT_TRUE(analysis->Dominates(46, 77));
+  EXPECT_TRUE(analysis->Dominates(46, 49));
+  EXPECT_TRUE(analysis->Dominates(46, 76));
+  EXPECT_TRUE(analysis->Dominates(46, 48));
+
+  EXPECT_TRUE(analysis->Dominates(48, 82));
+
+  EXPECT_TRUE(analysis->Dominates(82, 86));
+  EXPECT_TRUE(analysis->Dominates(82, 83));
+  EXPECT_TRUE(analysis->Dominates(82, 95));
+  EXPECT_TRUE(analysis->Dominates(82, 99));
+  EXPECT_TRUE(analysis->Dominates(82, 96));
+  EXPECT_TRUE(analysis->Dominates(82, 103));
+  EXPECT_TRUE(analysis->Dominates(82, 107));
+  EXPECT_TRUE(analysis->Dominates(82, 104));
+  EXPECT_TRUE(analysis->Dominates(82, 106));
+  EXPECT_TRUE(analysis->Dominates(82, 105));
+  EXPECT_TRUE(analysis->Dominates(82, 98));
+  EXPECT_TRUE(analysis->Dominates(82, 97));
+  EXPECT_TRUE(analysis->Dominates(82, 85));
+  EXPECT_TRUE(analysis->Dominates(82, 84));
+
+  EXPECT_TRUE(analysis->Dominates(84, 114));
+
+  EXPECT_TRUE(analysis->Dominates(114, 118));
+  EXPECT_TRUE(analysis->Dominates(114, 116));
+  EXPECT_TRUE(analysis->Dominates(114, 115));
+  EXPECT_TRUE(analysis->Dominates(114, 132));
+  EXPECT_TRUE(analysis->Dominates(114, 135));
+  EXPECT_TRUE(analysis->Dominates(114, 139));
+  EXPECT_TRUE(analysis->Dominates(114, 136));
+  EXPECT_TRUE(analysis->Dominates(114, 143));
+  EXPECT_TRUE(analysis->Dominates(114, 147));
+  EXPECT_TRUE(analysis->Dominates(114, 144));
+  EXPECT_TRUE(analysis->Dominates(114, 146));
+  EXPECT_TRUE(analysis->Dominates(114, 145));
+  EXPECT_TRUE(analysis->Dominates(114, 158));
+  EXPECT_TRUE(analysis->Dominates(114, 138));
+  EXPECT_TRUE(analysis->Dominates(114, 137));
+  EXPECT_TRUE(analysis->Dominates(114, 131));
+  EXPECT_TRUE(analysis->Dominates(114, 117));
+}
+
+}  // namespace
diff --git a/test/opt/dominator_tree/nested_loops_with_unreachables.cpp b/test/opt/dominator_tree/nested_loops_with_unreachables.cpp
new file mode 100644 (file)
index 0000000..ab1b6a7
--- /dev/null
@@ -0,0 +1,846 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+#include "../assembly_builder.h"
+#include "../function_utils.h"
+#include "../pass_fixture.h"
+#include "../pass_utils.h"
+#include "opt/dominator_analysis.h"
+#include "opt/pass.h"
+
+namespace {
+
+using namespace spvtools;
+using ::testing::UnorderedElementsAre;
+
+using PassClassTest = PassTest<::testing::Test>;
+
+/*
+Generated from the following GLSL
+#version 440 core
+layout(location = 0) out vec4 v;
+layout(location = 1) in vec4 in_val;
+void main() {
+  for (int i = 0; i < in_val.x; ++i) {
+    for (int j = 0; j < in_val.y; j++) {
+    }
+  }
+  for (int i = 0; i < in_val.x; ++i) {
+    for (int j = 0; j < in_val.y; j++) {
+    }
+    break;
+  }
+  int i = 0;
+  while (i < in_val.x) {
+    ++i;
+    for (int j = 0; j < 1; j++) {
+      for (int k = 0; k < 1; k++) {
+      }
+      break;
+    }
+  }
+  i = 0;
+  while (i < in_val.x) {
+    ++i;
+    continue;
+    for (int j = 0; j < 1; j++) {
+      for (int k = 0; k < 1; k++) {
+      }
+      break;
+    }
+  }
+  v = vec4(1,1,1,1);
+}
+*/
+TEST_F(PassClassTest, BasicVisitFromEntryPoint) {
+  const std::string text = R"(
+    OpCapability Shader
+    %1 = OpExtInstImport "GLSL.std.450"
+         OpMemoryModel Logical GLSL450
+         OpEntryPoint Fragment %4 "main" %20 %141
+         OpExecutionMode %4 OriginUpperLeft
+         OpSource GLSL 440
+         OpName %4 "main"
+         OpName %8 "i"
+         OpName %20 "in_val"
+         OpName %28 "j"
+         OpName %45 "i"
+         OpName %56 "j"
+         OpName %72 "i"
+         OpName %85 "j"
+         OpName %93 "k"
+         OpName %119 "j"
+         OpName %127 "k"
+         OpName %141 "v"
+         OpDecorate %20 Location 1
+         OpDecorate %141 Location 0
+    %2 = OpTypeVoid
+    %3 = OpTypeFunction %2
+    %6 = OpTypeInt 32 1
+    %7 = OpTypePointer Function %6
+    %9 = OpConstant %6 0
+   %16 = OpTypeFloat 32
+   %18 = OpTypeVector %16 4
+   %19 = OpTypePointer Input %18
+   %20 = OpVariable %19 Input
+   %21 = OpTypeInt 32 0
+   %22 = OpConstant %21 0
+   %23 = OpTypePointer Input %16
+   %26 = OpTypeBool
+   %36 = OpConstant %21 1
+   %41 = OpConstant %6 1
+  %140 = OpTypePointer Output %18
+  %141 = OpVariable %140 Output
+  %142 = OpConstant %16 1
+  %143 = OpConstantComposite %18 %142 %142 %142 %142
+    %4 = OpFunction %2 None %3
+    %5 = OpLabel
+    %8 = OpVariable %7 Function
+   %28 = OpVariable %7 Function
+   %45 = OpVariable %7 Function
+   %56 = OpVariable %7 Function
+   %72 = OpVariable %7 Function
+   %85 = OpVariable %7 Function
+   %93 = OpVariable %7 Function
+  %119 = OpVariable %7 Function
+  %127 = OpVariable %7 Function
+         OpStore %8 %9
+         OpBranch %10
+   %10 = OpLabel
+         OpLoopMerge %12 %13 None
+         OpBranch %14
+   %14 = OpLabel
+   %15 = OpLoad %6 %8
+   %17 = OpConvertSToF %16 %15
+   %24 = OpAccessChain %23 %20 %22
+   %25 = OpLoad %16 %24
+   %27 = OpFOrdLessThan %26 %17 %25
+         OpBranchConditional %27 %11 %12
+   %11 = OpLabel
+         OpStore %28 %9
+         OpBranch %29
+   %29 = OpLabel
+         OpLoopMerge %31 %32 None
+         OpBranch %33
+   %33 = OpLabel
+   %34 = OpLoad %6 %28
+   %35 = OpConvertSToF %16 %34
+   %37 = OpAccessChain %23 %20 %36
+   %38 = OpLoad %16 %37
+   %39 = OpFOrdLessThan %26 %35 %38
+         OpBranchConditional %39 %30 %31
+   %30 = OpLabel
+         OpBranch %32
+   %32 = OpLabel
+   %40 = OpLoad %6 %28
+   %42 = OpIAdd %6 %40 %41
+         OpStore %28 %42
+         OpBranch %29
+   %31 = OpLabel
+         OpBranch %13
+   %13 = OpLabel
+   %43 = OpLoad %6 %8
+   %44 = OpIAdd %6 %43 %41
+         OpStore %8 %44
+         OpBranch %10
+   %12 = OpLabel
+         OpStore %45 %9
+         OpBranch %46
+   %46 = OpLabel
+         OpLoopMerge %48 %49 None
+         OpBranch %50
+   %50 = OpLabel
+   %51 = OpLoad %6 %45
+   %52 = OpConvertSToF %16 %51
+   %53 = OpAccessChain %23 %20 %22
+   %54 = OpLoad %16 %53
+   %55 = OpFOrdLessThan %26 %52 %54
+         OpBranchConditional %55 %47 %48
+   %47 = OpLabel
+         OpStore %56 %9
+         OpBranch %57
+   %57 = OpLabel
+         OpLoopMerge %59 %60 None
+         OpBranch %61
+   %61 = OpLabel
+   %62 = OpLoad %6 %56
+   %63 = OpConvertSToF %16 %62
+   %64 = OpAccessChain %23 %20 %36
+   %65 = OpLoad %16 %64
+   %66 = OpFOrdLessThan %26 %63 %65
+         OpBranchConditional %66 %58 %59
+   %58 = OpLabel
+         OpBranch %60
+   %60 = OpLabel
+   %67 = OpLoad %6 %56
+   %68 = OpIAdd %6 %67 %41
+         OpStore %56 %68
+         OpBranch %57
+   %59 = OpLabel
+         OpBranch %48
+   %49 = OpLabel
+   %70 = OpLoad %6 %45
+   %71 = OpIAdd %6 %70 %41
+         OpStore %45 %71
+         OpBranch %46
+   %48 = OpLabel
+         OpStore %72 %9
+         OpBranch %73
+   %73 = OpLabel
+         OpLoopMerge %75 %76 None
+         OpBranch %77
+   %77 = OpLabel
+   %78 = OpLoad %6 %72
+   %79 = OpConvertSToF %16 %78
+   %80 = OpAccessChain %23 %20 %22
+   %81 = OpLoad %16 %80
+   %82 = OpFOrdLessThan %26 %79 %81
+         OpBranchConditional %82 %74 %75
+   %74 = OpLabel
+   %83 = OpLoad %6 %72
+   %84 = OpIAdd %6 %83 %41
+         OpStore %72 %84
+         OpStore %85 %9
+         OpBranch %86
+   %86 = OpLabel
+         OpLoopMerge %88 %89 None
+         OpBranch %90
+   %90 = OpLabel
+   %91 = OpLoad %6 %85
+   %92 = OpSLessThan %26 %91 %41
+         OpBranchConditional %92 %87 %88
+   %87 = OpLabel
+         OpStore %93 %9
+         OpBranch %94
+   %94 = OpLabel
+         OpLoopMerge %96 %97 None
+         OpBranch %98
+   %98 = OpLabel
+   %99 = OpLoad %6 %93
+  %100 = OpSLessThan %26 %99 %41
+         OpBranchConditional %100 %95 %96
+   %95 = OpLabel
+         OpBranch %97
+   %97 = OpLabel
+  %101 = OpLoad %6 %93
+  %102 = OpIAdd %6 %101 %41
+         OpStore %93 %102
+         OpBranch %94
+   %96 = OpLabel
+         OpBranch %88
+   %89 = OpLabel
+  %104 = OpLoad %6 %85
+  %105 = OpIAdd %6 %104 %41
+         OpStore %85 %105
+         OpBranch %86
+   %88 = OpLabel
+         OpBranch %76
+   %76 = OpLabel
+         OpBranch %73
+   %75 = OpLabel
+         OpStore %72 %9
+         OpBranch %106
+  %106 = OpLabel
+         OpLoopMerge %108 %109 None
+         OpBranch %110
+  %110 = OpLabel
+  %111 = OpLoad %6 %72
+  %112 = OpConvertSToF %16 %111
+  %113 = OpAccessChain %23 %20 %22
+  %114 = OpLoad %16 %113
+  %115 = OpFOrdLessThan %26 %112 %114
+         OpBranchConditional %115 %107 %108
+  %107 = OpLabel
+  %116 = OpLoad %6 %72
+  %117 = OpIAdd %6 %116 %41
+         OpStore %72 %117
+         OpBranch %109
+  %109 = OpLabel
+         OpBranch %106
+  %108 = OpLabel
+         OpStore %141 %143
+         OpReturn
+         OpFunctionEnd
+)";
+  // clang-format on
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ir::Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+
+  const ir::Function* f = spvtest::GetFunction(module, 4);
+  ir::CFG cfg(module);
+  opt::DominatorAnalysis* analysis = context->GetDominatorAnalysis(f, cfg);
+
+  EXPECT_TRUE(analysis->Dominates(5, 10));
+  EXPECT_TRUE(analysis->Dominates(5, 14));
+  EXPECT_TRUE(analysis->Dominates(5, 11));
+  EXPECT_TRUE(analysis->Dominates(5, 29));
+  EXPECT_TRUE(analysis->Dominates(5, 33));
+  EXPECT_TRUE(analysis->Dominates(5, 30));
+  EXPECT_TRUE(analysis->Dominates(5, 32));
+  EXPECT_TRUE(analysis->Dominates(5, 31));
+  EXPECT_TRUE(analysis->Dominates(5, 13));
+  EXPECT_TRUE(analysis->Dominates(5, 12));
+  EXPECT_TRUE(analysis->Dominates(5, 46));
+  EXPECT_TRUE(analysis->Dominates(5, 50));
+  EXPECT_TRUE(analysis->Dominates(5, 47));
+  EXPECT_TRUE(analysis->Dominates(5, 57));
+  EXPECT_TRUE(analysis->Dominates(5, 61));
+  EXPECT_TRUE(analysis->Dominates(5, 59));
+  EXPECT_TRUE(analysis->Dominates(5, 58));
+  EXPECT_TRUE(analysis->Dominates(5, 60));
+  EXPECT_TRUE(analysis->Dominates(5, 48));
+  EXPECT_TRUE(analysis->Dominates(5, 73));
+  EXPECT_TRUE(analysis->Dominates(5, 77));
+  EXPECT_TRUE(analysis->Dominates(5, 75));
+  EXPECT_TRUE(analysis->Dominates(5, 106));
+  EXPECT_TRUE(analysis->Dominates(5, 110));
+  EXPECT_TRUE(analysis->Dominates(5, 107));
+  EXPECT_TRUE(analysis->Dominates(5, 108));
+  EXPECT_TRUE(analysis->Dominates(5, 109));
+  EXPECT_TRUE(analysis->Dominates(5, 74));
+  EXPECT_TRUE(analysis->Dominates(5, 86));
+  EXPECT_TRUE(analysis->Dominates(5, 90));
+  EXPECT_TRUE(analysis->Dominates(5, 87));
+  EXPECT_TRUE(analysis->Dominates(5, 94));
+  EXPECT_TRUE(analysis->Dominates(5, 98));
+  EXPECT_TRUE(analysis->Dominates(5, 95));
+  EXPECT_TRUE(analysis->Dominates(5, 97));
+  EXPECT_TRUE(analysis->Dominates(5, 96));
+  EXPECT_TRUE(analysis->Dominates(5, 88));
+  EXPECT_TRUE(analysis->Dominates(5, 76));
+
+  EXPECT_TRUE(analysis->Dominates(10, 14));
+  EXPECT_TRUE(analysis->Dominates(10, 11));
+  EXPECT_TRUE(analysis->Dominates(10, 29));
+  EXPECT_TRUE(analysis->Dominates(10, 33));
+  EXPECT_TRUE(analysis->Dominates(10, 30));
+  EXPECT_TRUE(analysis->Dominates(10, 32));
+  EXPECT_TRUE(analysis->Dominates(10, 31));
+  EXPECT_TRUE(analysis->Dominates(10, 13));
+  EXPECT_TRUE(analysis->Dominates(10, 12));
+  EXPECT_TRUE(analysis->Dominates(10, 46));
+  EXPECT_TRUE(analysis->Dominates(10, 50));
+  EXPECT_TRUE(analysis->Dominates(10, 47));
+  EXPECT_TRUE(analysis->Dominates(10, 57));
+  EXPECT_TRUE(analysis->Dominates(10, 61));
+  EXPECT_TRUE(analysis->Dominates(10, 59));
+  EXPECT_TRUE(analysis->Dominates(10, 58));
+  EXPECT_TRUE(analysis->Dominates(10, 60));
+  EXPECT_TRUE(analysis->Dominates(10, 48));
+  EXPECT_TRUE(analysis->Dominates(10, 73));
+  EXPECT_TRUE(analysis->Dominates(10, 77));
+  EXPECT_TRUE(analysis->Dominates(10, 75));
+  EXPECT_TRUE(analysis->Dominates(10, 106));
+  EXPECT_TRUE(analysis->Dominates(10, 110));
+  EXPECT_TRUE(analysis->Dominates(10, 107));
+  EXPECT_TRUE(analysis->Dominates(10, 108));
+  EXPECT_TRUE(analysis->Dominates(10, 109));
+  EXPECT_TRUE(analysis->Dominates(10, 74));
+  EXPECT_TRUE(analysis->Dominates(10, 86));
+  EXPECT_TRUE(analysis->Dominates(10, 90));
+  EXPECT_TRUE(analysis->Dominates(10, 87));
+  EXPECT_TRUE(analysis->Dominates(10, 94));
+  EXPECT_TRUE(analysis->Dominates(10, 98));
+  EXPECT_TRUE(analysis->Dominates(10, 95));
+  EXPECT_TRUE(analysis->Dominates(10, 97));
+  EXPECT_TRUE(analysis->Dominates(10, 96));
+  EXPECT_TRUE(analysis->Dominates(10, 88));
+  EXPECT_TRUE(analysis->Dominates(10, 76));
+
+  EXPECT_TRUE(analysis->Dominates(14, 11));
+  EXPECT_TRUE(analysis->Dominates(14, 29));
+  EXPECT_TRUE(analysis->Dominates(14, 33));
+  EXPECT_TRUE(analysis->Dominates(14, 30));
+  EXPECT_TRUE(analysis->Dominates(14, 32));
+  EXPECT_TRUE(analysis->Dominates(14, 31));
+
+  EXPECT_TRUE(analysis->Dominates(11, 29));
+  EXPECT_TRUE(analysis->Dominates(11, 33));
+  EXPECT_TRUE(analysis->Dominates(11, 30));
+  EXPECT_TRUE(analysis->Dominates(11, 32));
+  EXPECT_TRUE(analysis->Dominates(11, 31));
+
+  EXPECT_TRUE(analysis->Dominates(29, 33));
+  EXPECT_TRUE(analysis->Dominates(29, 30));
+  EXPECT_TRUE(analysis->Dominates(29, 32));
+  EXPECT_TRUE(analysis->Dominates(29, 31));
+
+  EXPECT_TRUE(analysis->Dominates(33, 30));
+
+  EXPECT_TRUE(analysis->Dominates(12, 46));
+  EXPECT_TRUE(analysis->Dominates(12, 50));
+  EXPECT_TRUE(analysis->Dominates(12, 47));
+  EXPECT_TRUE(analysis->Dominates(12, 57));
+  EXPECT_TRUE(analysis->Dominates(12, 61));
+  EXPECT_TRUE(analysis->Dominates(12, 59));
+  EXPECT_TRUE(analysis->Dominates(12, 58));
+  EXPECT_TRUE(analysis->Dominates(12, 60));
+  EXPECT_TRUE(analysis->Dominates(12, 48));
+  EXPECT_TRUE(analysis->Dominates(12, 73));
+  EXPECT_TRUE(analysis->Dominates(12, 77));
+  EXPECT_TRUE(analysis->Dominates(12, 75));
+  EXPECT_TRUE(analysis->Dominates(12, 106));
+  EXPECT_TRUE(analysis->Dominates(12, 110));
+  EXPECT_TRUE(analysis->Dominates(12, 107));
+  EXPECT_TRUE(analysis->Dominates(12, 108));
+  EXPECT_TRUE(analysis->Dominates(12, 109));
+  EXPECT_TRUE(analysis->Dominates(12, 74));
+  EXPECT_TRUE(analysis->Dominates(12, 86));
+  EXPECT_TRUE(analysis->Dominates(12, 90));
+  EXPECT_TRUE(analysis->Dominates(12, 87));
+  EXPECT_TRUE(analysis->Dominates(12, 94));
+  EXPECT_TRUE(analysis->Dominates(12, 98));
+  EXPECT_TRUE(analysis->Dominates(12, 95));
+  EXPECT_TRUE(analysis->Dominates(12, 97));
+  EXPECT_TRUE(analysis->Dominates(12, 96));
+  EXPECT_TRUE(analysis->Dominates(12, 88));
+  EXPECT_TRUE(analysis->Dominates(12, 76));
+
+  EXPECT_TRUE(analysis->Dominates(46, 50));
+  EXPECT_TRUE(analysis->Dominates(46, 47));
+  EXPECT_TRUE(analysis->Dominates(46, 57));
+  EXPECT_TRUE(analysis->Dominates(46, 61));
+  EXPECT_TRUE(analysis->Dominates(46, 59));
+  EXPECT_TRUE(analysis->Dominates(46, 58));
+  EXPECT_TRUE(analysis->Dominates(46, 60));
+  EXPECT_TRUE(analysis->Dominates(46, 48));
+  EXPECT_TRUE(analysis->Dominates(46, 73));
+  EXPECT_TRUE(analysis->Dominates(46, 77));
+  EXPECT_TRUE(analysis->Dominates(46, 75));
+  EXPECT_TRUE(analysis->Dominates(46, 106));
+  EXPECT_TRUE(analysis->Dominates(46, 110));
+  EXPECT_TRUE(analysis->Dominates(46, 107));
+  EXPECT_TRUE(analysis->Dominates(46, 108));
+  EXPECT_TRUE(analysis->Dominates(46, 109));
+  EXPECT_TRUE(analysis->Dominates(46, 74));
+  EXPECT_TRUE(analysis->Dominates(46, 86));
+  EXPECT_TRUE(analysis->Dominates(46, 90));
+  EXPECT_TRUE(analysis->Dominates(46, 87));
+  EXPECT_TRUE(analysis->Dominates(46, 94));
+  EXPECT_TRUE(analysis->Dominates(46, 98));
+  EXPECT_TRUE(analysis->Dominates(46, 95));
+  EXPECT_TRUE(analysis->Dominates(46, 97));
+  EXPECT_TRUE(analysis->Dominates(46, 96));
+  EXPECT_TRUE(analysis->Dominates(46, 88));
+  EXPECT_TRUE(analysis->Dominates(46, 76));
+
+  EXPECT_TRUE(analysis->Dominates(50, 47));
+  EXPECT_TRUE(analysis->Dominates(50, 57));
+  EXPECT_TRUE(analysis->Dominates(50, 61));
+  EXPECT_TRUE(analysis->Dominates(50, 59));
+  EXPECT_TRUE(analysis->Dominates(50, 58));
+  EXPECT_TRUE(analysis->Dominates(50, 60));
+
+  EXPECT_TRUE(analysis->Dominates(47, 57));
+  EXPECT_TRUE(analysis->Dominates(47, 61));
+  EXPECT_TRUE(analysis->Dominates(47, 59));
+  EXPECT_TRUE(analysis->Dominates(47, 58));
+  EXPECT_TRUE(analysis->Dominates(47, 60));
+
+  EXPECT_TRUE(analysis->Dominates(57, 61));
+  EXPECT_TRUE(analysis->Dominates(57, 59));
+  EXPECT_TRUE(analysis->Dominates(57, 58));
+  EXPECT_TRUE(analysis->Dominates(57, 60));
+
+  EXPECT_TRUE(analysis->Dominates(61, 59));
+
+  EXPECT_TRUE(analysis->Dominates(48, 73));
+  EXPECT_TRUE(analysis->Dominates(48, 77));
+  EXPECT_TRUE(analysis->Dominates(48, 75));
+  EXPECT_TRUE(analysis->Dominates(48, 106));
+  EXPECT_TRUE(analysis->Dominates(48, 110));
+  EXPECT_TRUE(analysis->Dominates(48, 107));
+  EXPECT_TRUE(analysis->Dominates(48, 108));
+  EXPECT_TRUE(analysis->Dominates(48, 109));
+  EXPECT_TRUE(analysis->Dominates(48, 74));
+  EXPECT_TRUE(analysis->Dominates(48, 86));
+  EXPECT_TRUE(analysis->Dominates(48, 90));
+  EXPECT_TRUE(analysis->Dominates(48, 87));
+  EXPECT_TRUE(analysis->Dominates(48, 94));
+  EXPECT_TRUE(analysis->Dominates(48, 98));
+  EXPECT_TRUE(analysis->Dominates(48, 95));
+  EXPECT_TRUE(analysis->Dominates(48, 97));
+  EXPECT_TRUE(analysis->Dominates(48, 96));
+  EXPECT_TRUE(analysis->Dominates(48, 88));
+  EXPECT_TRUE(analysis->Dominates(48, 76));
+
+  EXPECT_TRUE(analysis->Dominates(73, 77));
+  EXPECT_TRUE(analysis->Dominates(73, 75));
+  EXPECT_TRUE(analysis->Dominates(73, 106));
+  EXPECT_TRUE(analysis->Dominates(73, 110));
+  EXPECT_TRUE(analysis->Dominates(73, 107));
+  EXPECT_TRUE(analysis->Dominates(73, 108));
+  EXPECT_TRUE(analysis->Dominates(73, 109));
+  EXPECT_TRUE(analysis->Dominates(73, 74));
+  EXPECT_TRUE(analysis->Dominates(73, 86));
+  EXPECT_TRUE(analysis->Dominates(73, 90));
+  EXPECT_TRUE(analysis->Dominates(73, 87));
+  EXPECT_TRUE(analysis->Dominates(73, 94));
+  EXPECT_TRUE(analysis->Dominates(73, 98));
+  EXPECT_TRUE(analysis->Dominates(73, 95));
+  EXPECT_TRUE(analysis->Dominates(73, 97));
+  EXPECT_TRUE(analysis->Dominates(73, 96));
+  EXPECT_TRUE(analysis->Dominates(73, 88));
+  EXPECT_TRUE(analysis->Dominates(73, 76));
+
+  EXPECT_TRUE(analysis->Dominates(75, 106));
+  EXPECT_TRUE(analysis->Dominates(75, 110));
+  EXPECT_TRUE(analysis->Dominates(75, 107));
+  EXPECT_TRUE(analysis->Dominates(75, 108));
+  EXPECT_TRUE(analysis->Dominates(75, 109));
+
+  EXPECT_TRUE(analysis->Dominates(106, 110));
+  EXPECT_TRUE(analysis->Dominates(106, 107));
+  EXPECT_TRUE(analysis->Dominates(106, 108));
+  EXPECT_TRUE(analysis->Dominates(106, 109));
+
+  EXPECT_TRUE(analysis->Dominates(110, 107));
+
+  EXPECT_TRUE(analysis->Dominates(77, 74));
+  EXPECT_TRUE(analysis->Dominates(77, 86));
+  EXPECT_TRUE(analysis->Dominates(77, 90));
+  EXPECT_TRUE(analysis->Dominates(77, 87));
+  EXPECT_TRUE(analysis->Dominates(77, 94));
+  EXPECT_TRUE(analysis->Dominates(77, 98));
+  EXPECT_TRUE(analysis->Dominates(77, 95));
+  EXPECT_TRUE(analysis->Dominates(77, 97));
+  EXPECT_TRUE(analysis->Dominates(77, 96));
+  EXPECT_TRUE(analysis->Dominates(77, 88));
+
+  EXPECT_TRUE(analysis->Dominates(74, 86));
+  EXPECT_TRUE(analysis->Dominates(74, 90));
+  EXPECT_TRUE(analysis->Dominates(74, 87));
+  EXPECT_TRUE(analysis->Dominates(74, 94));
+  EXPECT_TRUE(analysis->Dominates(74, 98));
+  EXPECT_TRUE(analysis->Dominates(74, 95));
+  EXPECT_TRUE(analysis->Dominates(74, 97));
+  EXPECT_TRUE(analysis->Dominates(74, 96));
+  EXPECT_TRUE(analysis->Dominates(74, 88));
+
+  EXPECT_TRUE(analysis->Dominates(86, 90));
+  EXPECT_TRUE(analysis->Dominates(86, 87));
+  EXPECT_TRUE(analysis->Dominates(86, 94));
+  EXPECT_TRUE(analysis->Dominates(86, 98));
+  EXPECT_TRUE(analysis->Dominates(86, 95));
+  EXPECT_TRUE(analysis->Dominates(86, 97));
+  EXPECT_TRUE(analysis->Dominates(86, 96));
+  EXPECT_TRUE(analysis->Dominates(86, 88));
+
+  EXPECT_TRUE(analysis->Dominates(90, 87));
+  EXPECT_TRUE(analysis->Dominates(90, 94));
+  EXPECT_TRUE(analysis->Dominates(90, 98));
+  EXPECT_TRUE(analysis->Dominates(90, 95));
+  EXPECT_TRUE(analysis->Dominates(90, 97));
+  EXPECT_TRUE(analysis->Dominates(90, 96));
+
+  EXPECT_TRUE(analysis->Dominates(87, 94));
+  EXPECT_TRUE(analysis->Dominates(87, 98));
+  EXPECT_TRUE(analysis->Dominates(87, 95));
+  EXPECT_TRUE(analysis->Dominates(87, 97));
+  EXPECT_TRUE(analysis->Dominates(87, 96));
+
+  EXPECT_TRUE(analysis->Dominates(94, 98));
+  EXPECT_TRUE(analysis->Dominates(94, 95));
+  EXPECT_TRUE(analysis->Dominates(94, 97));
+  EXPECT_TRUE(analysis->Dominates(94, 96));
+
+  EXPECT_TRUE(analysis->Dominates(98, 95));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 10));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 14));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 11));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 29));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 33));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 30));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 32));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 31));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 13));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 12));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 46));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 50));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 47));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 57));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 61));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 59));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 58));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 60));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 48));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 73));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 77));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 75));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 106));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 110));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 107));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 108));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 109));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 74));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 86));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 90));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 87));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 94));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 98));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 95));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 97));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 96));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 88));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 76));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 14));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 11));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 29));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 33));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 30));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 32));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 31));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 13));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 12));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 46));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 50));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 47));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 57));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 61));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 59));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 58));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 60));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 48));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 73));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 77));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 75));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 106));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 110));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 107));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 108));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 109));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 74));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 86));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 90));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 87));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 94));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 98));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 95));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 97));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 96));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 88));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 76));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(14, 11));
+  EXPECT_TRUE(analysis->StrictlyDominates(14, 29));
+  EXPECT_TRUE(analysis->StrictlyDominates(14, 33));
+  EXPECT_TRUE(analysis->StrictlyDominates(14, 30));
+  EXPECT_TRUE(analysis->StrictlyDominates(14, 32));
+  EXPECT_TRUE(analysis->StrictlyDominates(14, 31));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(11, 29));
+  EXPECT_TRUE(analysis->StrictlyDominates(11, 33));
+  EXPECT_TRUE(analysis->StrictlyDominates(11, 30));
+  EXPECT_TRUE(analysis->StrictlyDominates(11, 32));
+  EXPECT_TRUE(analysis->StrictlyDominates(11, 31));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(29, 33));
+  EXPECT_TRUE(analysis->StrictlyDominates(29, 30));
+  EXPECT_TRUE(analysis->StrictlyDominates(29, 32));
+  EXPECT_TRUE(analysis->StrictlyDominates(29, 31));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(33, 30));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 46));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 50));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 47));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 57));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 61));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 59));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 58));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 60));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 48));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 73));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 77));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 75));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 106));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 110));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 107));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 108));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 109));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 74));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 86));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 90));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 87));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 94));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 98));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 95));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 97));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 96));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 88));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 76));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 50));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 47));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 57));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 61));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 59));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 58));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 60));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 48));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 73));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 77));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 75));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 106));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 110));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 107));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 108));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 109));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 74));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 86));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 90));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 87));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 94));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 98));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 95));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 97));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 96));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 88));
+  EXPECT_TRUE(analysis->StrictlyDominates(46, 76));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(50, 47));
+  EXPECT_TRUE(analysis->StrictlyDominates(50, 57));
+  EXPECT_TRUE(analysis->StrictlyDominates(50, 61));
+  EXPECT_TRUE(analysis->StrictlyDominates(50, 59));
+  EXPECT_TRUE(analysis->StrictlyDominates(50, 58));
+  EXPECT_TRUE(analysis->StrictlyDominates(50, 60));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(47, 57));
+  EXPECT_TRUE(analysis->StrictlyDominates(47, 61));
+  EXPECT_TRUE(analysis->StrictlyDominates(47, 59));
+  EXPECT_TRUE(analysis->StrictlyDominates(47, 58));
+  EXPECT_TRUE(analysis->StrictlyDominates(47, 60));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(57, 61));
+  EXPECT_TRUE(analysis->StrictlyDominates(57, 59));
+  EXPECT_TRUE(analysis->StrictlyDominates(57, 58));
+  EXPECT_TRUE(analysis->StrictlyDominates(57, 60));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(61, 59));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 73));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 77));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 75));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 106));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 110));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 107));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 108));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 109));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 74));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 86));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 90));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 87));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 94));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 98));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 95));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 97));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 96));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 88));
+  EXPECT_TRUE(analysis->StrictlyDominates(48, 76));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 77));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 75));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 106));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 110));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 107));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 108));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 109));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 74));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 86));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 90));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 87));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 94));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 98));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 95));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 97));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 96));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 88));
+  EXPECT_TRUE(analysis->StrictlyDominates(73, 76));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(75, 106));
+  EXPECT_TRUE(analysis->StrictlyDominates(75, 110));
+  EXPECT_TRUE(analysis->StrictlyDominates(75, 107));
+  EXPECT_TRUE(analysis->StrictlyDominates(75, 108));
+  EXPECT_TRUE(analysis->StrictlyDominates(75, 109));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(106, 110));
+  EXPECT_TRUE(analysis->StrictlyDominates(106, 107));
+  EXPECT_TRUE(analysis->StrictlyDominates(106, 108));
+  EXPECT_TRUE(analysis->StrictlyDominates(106, 109));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(110, 107));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(77, 74));
+  EXPECT_TRUE(analysis->StrictlyDominates(77, 86));
+  EXPECT_TRUE(analysis->StrictlyDominates(77, 90));
+  EXPECT_TRUE(analysis->StrictlyDominates(77, 87));
+  EXPECT_TRUE(analysis->StrictlyDominates(77, 94));
+  EXPECT_TRUE(analysis->StrictlyDominates(77, 98));
+  EXPECT_TRUE(analysis->StrictlyDominates(77, 95));
+  EXPECT_TRUE(analysis->StrictlyDominates(77, 97));
+  EXPECT_TRUE(analysis->StrictlyDominates(77, 96));
+  EXPECT_TRUE(analysis->StrictlyDominates(77, 88));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(74, 86));
+  EXPECT_TRUE(analysis->StrictlyDominates(74, 90));
+  EXPECT_TRUE(analysis->StrictlyDominates(74, 87));
+  EXPECT_TRUE(analysis->StrictlyDominates(74, 94));
+  EXPECT_TRUE(analysis->StrictlyDominates(74, 98));
+  EXPECT_TRUE(analysis->StrictlyDominates(74, 95));
+  EXPECT_TRUE(analysis->StrictlyDominates(74, 97));
+  EXPECT_TRUE(analysis->StrictlyDominates(74, 96));
+  EXPECT_TRUE(analysis->StrictlyDominates(74, 88));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(86, 90));
+  EXPECT_TRUE(analysis->StrictlyDominates(86, 87));
+  EXPECT_TRUE(analysis->StrictlyDominates(86, 94));
+  EXPECT_TRUE(analysis->StrictlyDominates(86, 98));
+  EXPECT_TRUE(analysis->StrictlyDominates(86, 95));
+  EXPECT_TRUE(analysis->StrictlyDominates(86, 97));
+  EXPECT_TRUE(analysis->StrictlyDominates(86, 96));
+  EXPECT_TRUE(analysis->StrictlyDominates(86, 88));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(90, 87));
+  EXPECT_TRUE(analysis->StrictlyDominates(90, 94));
+  EXPECT_TRUE(analysis->StrictlyDominates(90, 98));
+  EXPECT_TRUE(analysis->StrictlyDominates(90, 95));
+  EXPECT_TRUE(analysis->StrictlyDominates(90, 97));
+  EXPECT_TRUE(analysis->StrictlyDominates(90, 96));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(87, 94));
+  EXPECT_TRUE(analysis->StrictlyDominates(87, 98));
+  EXPECT_TRUE(analysis->StrictlyDominates(87, 95));
+  EXPECT_TRUE(analysis->StrictlyDominates(87, 97));
+  EXPECT_TRUE(analysis->StrictlyDominates(87, 96));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(94, 98));
+  EXPECT_TRUE(analysis->StrictlyDominates(94, 95));
+  EXPECT_TRUE(analysis->StrictlyDominates(94, 97));
+  EXPECT_TRUE(analysis->StrictlyDominates(94, 96));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(98, 95));
+}
+
+}  // namespace
diff --git a/test/opt/dominator_tree/post.cpp b/test/opt/dominator_tree/post.cpp
new file mode 100644 (file)
index 0000000..2c786ae
--- /dev/null
@@ -0,0 +1,206 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+#include "../assembly_builder.h"
+#include "../function_utils.h"
+#include "../pass_fixture.h"
+#include "../pass_utils.h"
+#include "opt/dominator_analysis.h"
+#include "opt/pass.h"
+
+namespace {
+
+using namespace spvtools;
+using ::testing::UnorderedElementsAre;
+
+using PassClassTest = PassTest<::testing::Test>;
+
+/*
+  Generated from the following GLSL
+#version 440 core
+layout(location = 0) out vec4 c;
+layout(location = 1)in vec4 in_val;
+void main(){
+  if ( in_val.x < 10) {
+    int z = 0;
+    int i = 0;
+    for (i = 0; i < in_val.y; ++i) {
+        z += i;
+    }
+    c = vec4(i,i,i,i);
+  } else {
+        c = vec4(1,1,1,1);
+  }
+}
+*/
+TEST_F(PassClassTest, BasicVisitFromEntryPoint) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %9 %43
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 440
+               OpName %4 "main"
+               OpName %9 "in_val"
+               OpName %22 "z"
+               OpName %24 "i"
+               OpName %43 "c"
+               OpDecorate %9 Location 1
+               OpDecorate %43 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypePointer Input %7
+          %9 = OpVariable %8 Input
+         %10 = OpTypeInt 32 0
+         %11 = OpConstant %10 0
+         %12 = OpTypePointer Input %6
+         %15 = OpConstant %6 10
+         %16 = OpTypeBool
+         %20 = OpTypeInt 32 1
+         %21 = OpTypePointer Function %20
+         %23 = OpConstant %20 0
+         %32 = OpConstant %10 1
+         %40 = OpConstant %20 1
+         %42 = OpTypePointer Output %7
+         %43 = OpVariable %42 Output
+         %54 = OpConstant %6 1
+         %55 = OpConstantComposite %7 %54 %54 %54 %54
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %22 = OpVariable %21 Function
+         %24 = OpVariable %21 Function
+         %13 = OpAccessChain %12 %9 %11
+         %14 = OpLoad %6 %13
+         %17 = OpFOrdLessThan %16 %14 %15
+               OpSelectionMerge %19 None
+               OpBranchConditional %17 %18 %53
+         %18 = OpLabel
+               OpStore %22 %23
+               OpStore %24 %23
+               OpStore %24 %23
+               OpBranch %25
+         %25 = OpLabel
+               OpLoopMerge %27 %28 None
+               OpBranch %29
+         %29 = OpLabel
+         %30 = OpLoad %20 %24
+         %31 = OpConvertSToF %6 %30
+         %33 = OpAccessChain %12 %9 %32
+         %34 = OpLoad %6 %33
+         %35 = OpFOrdLessThan %16 %31 %34
+               OpBranchConditional %35 %26 %27
+         %26 = OpLabel
+         %36 = OpLoad %20 %24
+         %37 = OpLoad %20 %22
+         %38 = OpIAdd %20 %37 %36
+               OpStore %22 %38
+               OpBranch %28
+         %28 = OpLabel
+         %39 = OpLoad %20 %24
+         %41 = OpIAdd %20 %39 %40
+               OpStore %24 %41
+               OpBranch %25
+         %27 = OpLabel
+         %44 = OpLoad %20 %24
+         %45 = OpConvertSToF %6 %44
+         %46 = OpLoad %20 %24
+         %47 = OpConvertSToF %6 %46
+         %48 = OpLoad %20 %24
+         %49 = OpConvertSToF %6 %48
+         %50 = OpLoad %20 %24
+         %51 = OpConvertSToF %6 %50
+         %52 = OpCompositeConstruct %7 %45 %47 %49 %51
+               OpStore %43 %52
+               OpBranch %19
+         %53 = OpLabel
+               OpStore %43 %55
+               OpBranch %19
+         %19 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  // clang-format on
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ir::Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+
+  const ir::Function* f = spvtest::GetFunction(module, 4);
+  ir::CFG cfg(module);
+  opt::PostDominatorAnalysis* analysis =
+      context->GetPostDominatorAnalysis(f, cfg);
+
+  EXPECT_TRUE(analysis->Dominates(19, 18));
+  EXPECT_TRUE(analysis->Dominates(19, 5));
+  EXPECT_TRUE(analysis->Dominates(19, 53));
+  EXPECT_TRUE(analysis->Dominates(19, 19));
+  EXPECT_TRUE(analysis->Dominates(19, 25));
+  EXPECT_TRUE(analysis->Dominates(19, 29));
+  EXPECT_TRUE(analysis->Dominates(19, 27));
+  EXPECT_TRUE(analysis->Dominates(19, 26));
+  EXPECT_TRUE(analysis->Dominates(19, 28));
+
+  EXPECT_TRUE(analysis->Dominates(27, 18));
+  EXPECT_TRUE(analysis->Dominates(27, 25));
+  EXPECT_TRUE(analysis->Dominates(27, 29));
+  EXPECT_TRUE(analysis->Dominates(27, 27));
+  EXPECT_TRUE(analysis->Dominates(27, 26));
+  EXPECT_TRUE(analysis->Dominates(27, 28));
+
+  EXPECT_FALSE(analysis->Dominates(27, 19));
+  EXPECT_FALSE(analysis->Dominates(27, 5));
+  EXPECT_FALSE(analysis->Dominates(27, 53));
+
+  EXPECT_FALSE(analysis->StrictlyDominates(19, 19));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(19, 18));
+  EXPECT_TRUE(analysis->StrictlyDominates(19, 5));
+  EXPECT_TRUE(analysis->StrictlyDominates(19, 53));
+  EXPECT_TRUE(analysis->StrictlyDominates(19, 25));
+  EXPECT_TRUE(analysis->StrictlyDominates(19, 29));
+  EXPECT_TRUE(analysis->StrictlyDominates(19, 27));
+  EXPECT_TRUE(analysis->StrictlyDominates(19, 26));
+  EXPECT_TRUE(analysis->StrictlyDominates(19, 28));
+
+  // These would be expected true for a normal, non post, dominator tree
+  EXPECT_FALSE(analysis->Dominates(5, 18));
+  EXPECT_FALSE(analysis->Dominates(5, 53));
+  EXPECT_FALSE(analysis->Dominates(5, 19));
+  EXPECT_FALSE(analysis->Dominates(5, 25));
+  EXPECT_FALSE(analysis->Dominates(5, 29));
+  EXPECT_FALSE(analysis->Dominates(5, 27));
+  EXPECT_FALSE(analysis->Dominates(5, 26));
+  EXPECT_FALSE(analysis->Dominates(5, 28));
+
+  EXPECT_FALSE(analysis->StrictlyDominates(5, 18));
+  EXPECT_FALSE(analysis->StrictlyDominates(5, 53));
+  EXPECT_FALSE(analysis->StrictlyDominates(5, 19));
+  EXPECT_FALSE(analysis->StrictlyDominates(5, 25));
+  EXPECT_FALSE(analysis->StrictlyDominates(5, 29));
+  EXPECT_FALSE(analysis->StrictlyDominates(5, 27));
+  EXPECT_FALSE(analysis->StrictlyDominates(5, 26));
+  EXPECT_FALSE(analysis->StrictlyDominates(5, 28));
+}
+
+}  // namespace
diff --git a/test/opt/dominator_tree/simple.cpp b/test/opt/dominator_tree/simple.cpp
new file mode 100644 (file)
index 0000000..4cba7dc
--- /dev/null
@@ -0,0 +1,175 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+#include "../assembly_builder.h"
+#include "../function_utils.h"
+#include "../pass_fixture.h"
+#include "../pass_utils.h"
+#include "opt/dominator_analysis.h"
+#include "opt/pass.h"
+
+namespace {
+
+using namespace spvtools;
+using ::testing::UnorderedElementsAre;
+
+using PassClassTest = PassTest<::testing::Test>;
+
+/*
+Generated from the following GLSL
+#version 440 core
+layout(location = 0) out vec4 c;
+layout(location = 1)in vec4 in_val;
+void main(){
+  if ( in_val.x < 10) {
+    int z = 0;
+    int i = 0;
+    for (i = 0; i < in_val.y; ++i) {
+        z += i;
+    }
+    c = vec4(i,i,i,i);
+  } else {
+        c = vec4(1,1,1,1);
+  }
+}
+*/
+TEST_F(PassClassTest, BasicVisitFromEntryPoint) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %9 %43
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 440
+               OpName %4 "main"
+               OpName %9 "in_val"
+               OpName %22 "z"
+               OpName %24 "i"
+               OpName %43 "c"
+               OpDecorate %9 Location 1
+               OpDecorate %43 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypePointer Input %7
+          %9 = OpVariable %8 Input
+         %10 = OpTypeInt 32 0
+         %11 = OpConstant %10 0
+         %12 = OpTypePointer Input %6
+         %15 = OpConstant %6 10
+         %16 = OpTypeBool
+         %20 = OpTypeInt 32 1
+         %21 = OpTypePointer Function %20
+         %23 = OpConstant %20 0
+         %32 = OpConstant %10 1
+         %40 = OpConstant %20 1
+         %42 = OpTypePointer Output %7
+         %43 = OpVariable %42 Output
+         %54 = OpConstant %6 1
+         %55 = OpConstantComposite %7 %54 %54 %54 %54
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %22 = OpVariable %21 Function
+         %24 = OpVariable %21 Function
+         %13 = OpAccessChain %12 %9 %11
+         %14 = OpLoad %6 %13
+         %17 = OpFOrdLessThan %16 %14 %15
+               OpSelectionMerge %19 None
+               OpBranchConditional %17 %18 %53
+         %18 = OpLabel
+               OpStore %22 %23
+               OpStore %24 %23
+               OpStore %24 %23
+               OpBranch %25
+         %25 = OpLabel
+               OpLoopMerge %27 %28 None
+               OpBranch %29
+         %29 = OpLabel
+         %30 = OpLoad %20 %24
+         %31 = OpConvertSToF %6 %30
+         %33 = OpAccessChain %12 %9 %32
+         %34 = OpLoad %6 %33
+         %35 = OpFOrdLessThan %16 %31 %34
+               OpBranchConditional %35 %26 %27
+         %26 = OpLabel
+         %36 = OpLoad %20 %24
+         %37 = OpLoad %20 %22
+         %38 = OpIAdd %20 %37 %36
+               OpStore %22 %38
+               OpBranch %28
+         %28 = OpLabel
+         %39 = OpLoad %20 %24
+         %41 = OpIAdd %20 %39 %40
+               OpStore %24 %41
+               OpBranch %25
+         %27 = OpLabel
+         %44 = OpLoad %20 %24
+         %45 = OpConvertSToF %6 %44
+         %46 = OpLoad %20 %24
+         %47 = OpConvertSToF %6 %46
+         %48 = OpLoad %20 %24
+         %49 = OpConvertSToF %6 %48
+         %50 = OpLoad %20 %24
+         %51 = OpConvertSToF %6 %50
+         %52 = OpCompositeConstruct %7 %45 %47 %49 %51
+               OpStore %43 %52
+               OpBranch %19
+         %53 = OpLabel
+               OpStore %43 %55
+               OpBranch %19
+         %19 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  // clang-format on
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ir::Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+  const ir::Function* f = spvtest::GetFunction(module, 4);
+
+  ir::CFG cfg(module);
+  opt::DominatorAnalysis* analysis = context->GetDominatorAnalysis(f, cfg);
+
+  opt::DominatorTree& tree = analysis->GetDomTree();
+
+  EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block());
+  EXPECT_TRUE(analysis->Dominates(5, 18));
+  EXPECT_TRUE(analysis->Dominates(5, 53));
+  EXPECT_TRUE(analysis->Dominates(5, 19));
+  EXPECT_TRUE(analysis->Dominates(5, 25));
+  EXPECT_TRUE(analysis->Dominates(5, 29));
+  EXPECT_TRUE(analysis->Dominates(5, 27));
+  EXPECT_TRUE(analysis->Dominates(5, 26));
+  EXPECT_TRUE(analysis->Dominates(5, 28));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 18));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 53));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 19));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 25));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 29));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 27));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 26));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 28));
+}
+
+}  // namespace
diff --git a/test/opt/dominator_tree/switch_case_fallthrough.cpp b/test/opt/dominator_tree/switch_case_fallthrough.cpp
new file mode 100644 (file)
index 0000000..2b2e27b
--- /dev/null
@@ -0,0 +1,162 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+#include "../assembly_builder.h"
+#include "../function_utils.h"
+#include "../pass_fixture.h"
+#include "../pass_utils.h"
+#include "opt/dominator_analysis.h"
+#include "opt/pass.h"
+
+namespace {
+
+using namespace spvtools;
+using ::testing::UnorderedElementsAre;
+
+using PassClassTest = PassTest<::testing::Test>;
+
+/*
+  Generated from the following GLSL
+#version 440 core
+layout(location = 0) out vec4 v;
+layout(location = 1) in vec4 in_val;
+void main() {
+  int i;
+  switch (int(in_val.x)) {
+    case 0:
+      i = 0;
+    case 1:
+      i = 1;
+      break;
+    case 2:
+      i = 2;
+    case 3:
+      i = 3;
+    case 4:
+      i = 4;
+      break;
+    default:
+     i = 0;
+  }
+  v = vec4(i, i, i, i);
+}
+*/
+TEST_F(PassClassTest, UnreachableNestedIfs) {
+  const std::string text = R"(
+    OpCapability Shader
+    %1 = OpExtInstImport "GLSL.std.450"
+         OpMemoryModel Logical GLSL450
+         OpEntryPoint Fragment %4 "main" %9 %35
+         OpExecutionMode %4 OriginUpperLeft
+         OpSource GLSL 440
+         OpName %4 "main"
+         OpName %9 "in_val"
+         OpName %25 "i"
+         OpName %35 "v"
+         OpDecorate %9 Location 1
+         OpDecorate %35 Location 0
+    %2 = OpTypeVoid
+    %3 = OpTypeFunction %2
+    %6 = OpTypeFloat 32
+    %7 = OpTypeVector %6 4
+    %8 = OpTypePointer Input %7
+    %9 = OpVariable %8 Input
+   %10 = OpTypeInt 32 0
+   %11 = OpConstant %10 0
+   %12 = OpTypePointer Input %6
+   %15 = OpTypeInt 32 1
+   %24 = OpTypePointer Function %15
+   %26 = OpConstant %15 0
+   %27 = OpConstant %15 1
+   %29 = OpConstant %15 2
+   %30 = OpConstant %15 3
+   %31 = OpConstant %15 4
+   %34 = OpTypePointer Output %7
+   %35 = OpVariable %34 Output
+    %4 = OpFunction %2 None %3
+    %5 = OpLabel
+   %25 = OpVariable %24 Function
+   %13 = OpAccessChain %12 %9 %11
+   %14 = OpLoad %6 %13
+   %16 = OpConvertFToS %15 %14
+         OpSelectionMerge %23 None
+         OpSwitch %16 %22 0 %17 1 %18 2 %19 3 %20 4 %21
+   %22 = OpLabel
+         OpStore %25 %26
+         OpBranch %23
+   %17 = OpLabel
+         OpStore %25 %26
+         OpBranch %18
+   %18 = OpLabel
+         OpStore %25 %27
+         OpBranch %23
+   %19 = OpLabel
+         OpStore %25 %29
+         OpBranch %20
+   %20 = OpLabel
+         OpStore %25 %30
+         OpBranch %21
+   %21 = OpLabel
+         OpStore %25 %31
+         OpBranch %23
+   %23 = OpLabel
+   %36 = OpLoad %15 %25
+   %37 = OpConvertSToF %6 %36
+   %38 = OpLoad %15 %25
+   %39 = OpConvertSToF %6 %38
+   %40 = OpLoad %15 %25
+   %41 = OpConvertSToF %6 %40
+   %42 = OpLoad %15 %25
+   %43 = OpConvertSToF %6 %42
+   %44 = OpCompositeConstruct %7 %37 %39 %41 %43
+         OpStore %35 %44
+         OpReturn
+         OpFunctionEnd
+)";
+  // clang-format on
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ir::Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+
+  const ir::Function* f = spvtest::GetFunction(module, 4);
+  ir::CFG cfg(module);
+  opt::DominatorAnalysis* analysis = context->GetDominatorAnalysis(f, cfg);
+
+  EXPECT_TRUE(analysis->Dominates(5, 5));
+  EXPECT_TRUE(analysis->Dominates(5, 17));
+  EXPECT_TRUE(analysis->Dominates(5, 18));
+  EXPECT_TRUE(analysis->Dominates(5, 19));
+  EXPECT_TRUE(analysis->Dominates(5, 20));
+  EXPECT_TRUE(analysis->Dominates(5, 21));
+  EXPECT_TRUE(analysis->Dominates(5, 22));
+  EXPECT_TRUE(analysis->Dominates(5, 23));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 17));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 18));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 19));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 20));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 21));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 22));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 23));
+}
+
+}  // namespace
diff --git a/test/opt/dominator_tree/unreachable_for.cpp b/test/opt/dominator_tree/unreachable_for.cpp
new file mode 100644 (file)
index 0000000..a720cf4
--- /dev/null
@@ -0,0 +1,120 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+#include "../assembly_builder.h"
+#include "../function_utils.h"
+#include "../pass_fixture.h"
+#include "../pass_utils.h"
+#include "opt/dominator_analysis.h"
+#include "opt/pass.h"
+
+namespace {
+
+using namespace spvtools;
+using ::testing::UnorderedElementsAre;
+
+using PassClassTest = PassTest<::testing::Test>;
+
+/*
+  Generated from the following GLSL
+#version 440 core
+void main() {
+  for (int i = 0; i < 1; i++) {
+    break;
+  }
+}
+*/
+TEST_F(PassClassTest, UnreachableNestedIfs) {
+  const std::string text = R"(
+    OpCapability Shader
+    %1 = OpExtInstImport "GLSL.std.450"
+         OpMemoryModel Logical GLSL450
+         OpEntryPoint Fragment %4 "main"
+         OpExecutionMode %4 OriginUpperLeft
+         OpSource GLSL 440
+         OpName %4 "main"
+         OpName %8 "i"
+    %2 = OpTypeVoid
+    %3 = OpTypeFunction %2
+    %6 = OpTypeInt 32 1
+    %7 = OpTypePointer Function %6
+    %9 = OpConstant %6 0
+   %16 = OpConstant %6 1
+   %17 = OpTypeBool
+    %4 = OpFunction %2 None %3
+    %5 = OpLabel
+    %8 = OpVariable %7 Function
+         OpStore %8 %9
+         OpBranch %10
+   %10 = OpLabel
+         OpLoopMerge %12 %13 None
+         OpBranch %14
+   %14 = OpLabel
+   %15 = OpLoad %6 %8
+   %18 = OpSLessThan %17 %15 %16
+         OpBranchConditional %18 %11 %12
+   %11 = OpLabel
+         OpBranch %12
+   %13 = OpLabel
+   %20 = OpLoad %6 %8
+   %21 = OpIAdd %6 %20 %16
+         OpStore %8 %21
+         OpBranch %10
+   %12 = OpLabel
+         OpReturn
+         OpFunctionEnd
+)";
+  // clang-format on
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ir::Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+
+  const ir::Function* f = spvtest::GetFunction(module, 4);
+  ir::CFG cfg(module);
+  opt::DominatorAnalysis* analysis = context->GetDominatorAnalysis(f, cfg);
+  EXPECT_TRUE(analysis->Dominates(5, 5));
+  EXPECT_TRUE(analysis->Dominates(5, 10));
+  EXPECT_TRUE(analysis->Dominates(5, 14));
+  EXPECT_TRUE(analysis->Dominates(5, 11));
+  EXPECT_TRUE(analysis->Dominates(5, 12));
+  EXPECT_TRUE(analysis->Dominates(10, 10));
+  EXPECT_TRUE(analysis->Dominates(10, 14));
+  EXPECT_TRUE(analysis->Dominates(10, 11));
+  EXPECT_TRUE(analysis->Dominates(10, 12));
+  EXPECT_TRUE(analysis->Dominates(14, 14));
+  EXPECT_TRUE(analysis->Dominates(14, 11));
+  EXPECT_TRUE(analysis->Dominates(14, 12));
+  EXPECT_TRUE(analysis->Dominates(11, 11));
+  EXPECT_TRUE(analysis->Dominates(12, 12));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 10));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 14));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 11));
+  EXPECT_TRUE(analysis->StrictlyDominates(5, 12));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 14));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 11));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 12));
+  EXPECT_TRUE(analysis->StrictlyDominates(14, 11));
+  EXPECT_TRUE(analysis->StrictlyDominates(14, 12));
+}
+
+}  // namespace
diff --git a/test/opt/dominator_tree/unreachable_for_post.cpp b/test/opt/dominator_tree/unreachable_for_post.cpp
new file mode 100644 (file)
index 0000000..18a112e
--- /dev/null
@@ -0,0 +1,118 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+#include "../assembly_builder.h"
+#include "../function_utils.h"
+#include "../pass_fixture.h"
+#include "../pass_utils.h"
+#include "opt/dominator_analysis.h"
+#include "opt/pass.h"
+
+namespace {
+
+using namespace spvtools;
+using ::testing::UnorderedElementsAre;
+
+using PassClassTest = PassTest<::testing::Test>;
+
+/*
+  Generated from the following GLSL
+#version 440 core
+void main() {
+  for (int i = 0; i < 1; i++) {
+    break;
+  }
+}
+*/
+TEST_F(PassClassTest, UnreachableNestedIfs) {
+  const std::string text = R"(
+    OpCapability Shader
+    %1 = OpExtInstImport "GLSL.std.450"
+         OpMemoryModel Logical GLSL450
+         OpEntryPoint Fragment %4 "main"
+         OpExecutionMode %4 OriginUpperLeft
+         OpSource GLSL 440
+         OpName %4 "main"
+         OpName %8 "i"
+    %2 = OpTypeVoid
+    %3 = OpTypeFunction %2
+    %6 = OpTypeInt 32 1
+    %7 = OpTypePointer Function %6
+    %9 = OpConstant %6 0
+   %16 = OpConstant %6 1
+   %17 = OpTypeBool
+    %4 = OpFunction %2 None %3
+    %5 = OpLabel
+    %8 = OpVariable %7 Function
+         OpStore %8 %9
+         OpBranch %10
+   %10 = OpLabel
+         OpLoopMerge %12 %13 None
+         OpBranch %14
+   %14 = OpLabel
+   %15 = OpLoad %6 %8
+   %18 = OpSLessThan %17 %15 %16
+         OpBranchConditional %18 %11 %12
+   %11 = OpLabel
+         OpBranch %12
+   %13 = OpLabel
+   %20 = OpLoad %6 %8
+   %21 = OpIAdd %6 %20 %16
+         OpStore %8 %21
+         OpBranch %10
+   %12 = OpLabel
+         OpReturn
+         OpFunctionEnd
+)";
+  // clang-format on
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ir::Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+
+  const ir::Function* f = spvtest::GetFunction(module, 4);
+
+  ir::CFG cfg(module);
+  opt::PostDominatorAnalysis* analysis =
+      context->GetPostDominatorAnalysis(f, cfg);
+
+  EXPECT_TRUE(analysis->Dominates(12, 12));
+  EXPECT_TRUE(analysis->Dominates(12, 14));
+  EXPECT_TRUE(analysis->Dominates(12, 11));
+  EXPECT_TRUE(analysis->Dominates(12, 10));
+  EXPECT_TRUE(analysis->Dominates(12, 5));
+  EXPECT_TRUE(analysis->Dominates(14, 14));
+  EXPECT_TRUE(analysis->Dominates(14, 10));
+  EXPECT_TRUE(analysis->Dominates(14, 5));
+  EXPECT_TRUE(analysis->Dominates(10, 10));
+  EXPECT_TRUE(analysis->Dominates(10, 5));
+  EXPECT_TRUE(analysis->Dominates(5, 5));
+
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 14));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 11));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 10));
+  EXPECT_TRUE(analysis->StrictlyDominates(12, 5));
+  EXPECT_TRUE(analysis->StrictlyDominates(14, 10));
+  EXPECT_TRUE(analysis->StrictlyDominates(14, 5));
+  EXPECT_TRUE(analysis->StrictlyDominates(10, 5));
+}
+
+}  // namespace
diff --git a/test/opt/function_utils.h b/test/opt/function_utils.h
new file mode 100644 (file)
index 0000000..a392e31
--- /dev/null
@@ -0,0 +1,54 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef LIBSPIRV_TEST_OPT_FUNCTION_UTILS_H_
+#define LIBSPIRV_TEST_OPT_FUNCTION_UTILS_H_
+
+#include "opt/function.h"
+#include "opt/module.h"
+
+namespace spvtest {
+
+spvtools::ir::Function* GetFunction(spvtools::ir::Module* module, uint32_t id) {
+  for (spvtools::ir::Function& f : *module) {
+    if (f.result_id() == id) {
+      return &f;
+    }
+  }
+  return nullptr;
+}
+
+const spvtools::ir::Function* GetFunction(const spvtools::ir::Module* module,
+                                          uint32_t id) {
+  for (const spvtools::ir::Function& f : *module) {
+    if (f.result_id() == id) {
+      return &f;
+    }
+  }
+  return nullptr;
+}
+
+const spvtools::ir::BasicBlock* GetBasicBlock(const spvtools::ir::Function* fn,
+                                              uint32_t id) {
+  for (const spvtools::ir::BasicBlock& bb : *fn) {
+    if (bb.id() == id) {
+      return &bb;
+    }
+  }
+  return nullptr;
+}
+
+}  // namespace spvtest
+
+#endif  // LIBSPIRV_TEST_OPT_FUNCTION_UTILS_H_