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.
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 \
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
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
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
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(); }
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,
--- /dev/null
+// 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_
--- /dev/null
+// 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
--- /dev/null
+// 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_
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());
}
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(
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);
}
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
#include "cfg.h"
#include "decoration_manager.h"
#include "def_use_manager.h"
+#include "dominator_analysis.h"
#include "module.h"
#include <algorithm>
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);
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() {
// 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,
// 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;
# 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
--- /dev/null
+# 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
+)
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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_