Dominator Tree Analysis (#3)

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.
This commit is contained in:
Stephen McGroarty 2017-11-27 21:21:26 +00:00 committed by Steven Perron
parent 692a22c5bc
commit 8ba68fa9b9
24 changed files with 4126 additions and 1 deletions

View File

@ -62,6 +62,7 @@ SPVTOOLS_OPT_SRC_FILES := \
source/opt/dead_variable_elimination.cpp \
source/opt/decoration_manager.cpp \
source/opt/def_use_manager.cpp \
source/opt/dominator_tree.cpp \
source/opt/eliminate_dead_constant_pass.cpp \
source/opt/eliminate_dead_functions_pass.cpp \
source/opt/flatten_decoration_pass.cpp \

View File

@ -2,6 +2,8 @@ Revision history for SPIRV-Tools
v2017.2-dev 2017-11-23
- Start v2017.2-dev
- Optimizer:
- Adding dominance analysis
- Fixes:
#1004: Use after free of an instruction, in remove-duplicates transform
#1007: OpImageRead not required to return 4-component vector

View File

@ -25,6 +25,8 @@ add_library(SPIRV-Tools-opt
dead_variable_elimination.h
decoration_manager.h
def_use_manager.h
dominator_analysis.h
dominator_tree.h
eliminate_dead_constant_pass.h
eliminate_dead_functions_pass.h
flatten_decoration_pass.h
@ -75,6 +77,7 @@ add_library(SPIRV-Tools-opt
dead_variable_elimination.cpp
decoration_manager.cpp
def_use_manager.cpp
dominator_tree.cpp
eliminate_dead_constant_pass.cpp
eliminate_dead_functions_pass.cpp
flatten_decoration_pass.cpp

View File

@ -81,6 +81,8 @@ class BasicBlock {
iterator begin() { return insts_.begin(); }
iterator end() { return insts_.end(); }
const_iterator begin() const { return insts_.cbegin(); }
const_iterator end() const { return insts_.cend(); }
const_iterator cbegin() const { return insts_.cbegin(); }
const_iterator cend() const { return insts_.cend(); }
@ -98,6 +100,9 @@ class BasicBlock {
return --insts_.cend();
}
// Returns true if the basic block has at least one successor.
inline bool hasSuccessor() const { return ctail()->IsBranch(); }
// Runs the given function |f| on each instruction in this basic block, and
// optionally on the debug line instructions that might precede them.
inline void ForEachInst(const std::function<void(Instruction*)>& f,

View File

@ -0,0 +1,133 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef LIBSPIRV_OPT_DOMINATOR_ANALYSIS_PASS_H_
#define LIBSPIRV_OPT_DOMINATOR_ANALYSIS_PASS_H_
#include <cstdint>
#include <map>
#include "dominator_tree.h"
#include "module.h"
namespace spvtools {
namespace opt {
// Interface to perform dominator or postdominator analysis on a given function.
class DominatorAnalysisBase {
public:
explicit DominatorAnalysisBase(bool is_post_dom) : tree_(is_post_dom) {}
// Calculates the dominator (or postdominator) tree for given function |f|.
inline void InitializeTree(const ir::Function* f, const ir::CFG& cfg) {
tree_.InitializeTree(f, cfg);
}
// Returns true if BasicBlock |a| dominates BasicBlock |b|.
inline bool Dominates(const ir::BasicBlock* a,
const ir::BasicBlock* b) const {
if (!a || !b) return false;
return Dominates(a->id(), b->id());
}
// Returns true if BasicBlock |a| dominates BasicBlock |b|. Same as above only
// using the BasicBlock IDs.
inline bool Dominates(uint32_t a, uint32_t b) const {
return tree_.Dominates(a, b);
}
// Returns true if BasicBlock |a| strictly dominates BasicBlock |b|.
inline bool StrictlyDominates(const ir::BasicBlock* a,
const ir::BasicBlock* b) const {
if (!a || !b) return false;
return StrictlyDominates(a->id(), b->id());
}
// Returns true if BasicBlock |a| strictly dominates BasicBlock |b|. Same as
// above only using the BasicBlock IDs.
inline bool StrictlyDominates(uint32_t a, uint32_t b) const {
return tree_.StrictlyDominates(a, b);
}
// Returns the immediate dominator of |node| or returns nullptr if it is has
// no dominator.
inline ir::BasicBlock* ImmediateDominator(const ir::BasicBlock* node) const {
if (!node) return nullptr;
return tree_.ImmediateDominator(node);
}
// Returns the immediate dominator of |node_id| or returns nullptr if it is
// has no dominator. Same as above but operates on IDs.
inline ir::BasicBlock* ImmediateDominator(uint32_t node_id) const {
return tree_.ImmediateDominator(node_id);
}
// Returns true if |node| is reachable from the entry.
inline bool IsReachable(const ir::BasicBlock* node) const {
if (!node) return false;
return tree_.ReachableFromRoots(node->id());
}
// Returns true if |node_id| is reachable from the entry.
inline bool IsReachable(uint32_t node_id) const {
return tree_.ReachableFromRoots(node_id);
}
// Dump the tree structure into the given |out| stream in the dot format.
inline void DumpAsDot(std::ostream& out) const { tree_.DumpTreeAsDot(out); }
// Returns true if this is a postdomiator tree.
inline bool IsPostDominator() const { return tree_.IsPostDominator(); }
// Returns the tree itself for manual operations, such as traversing the
// roots.
// For normal dominance relationships the methods above should be used.
inline DominatorTree& GetDomTree() { return tree_; }
inline const DominatorTree& GetDomTree() const { return tree_; }
// Force the dominator tree to be removed
inline void ClearTree() { tree_.ClearTree(); }
// Applies the std::function |func| to dominator tree nodes in dominator
// order.
void Visit(std::function<bool(DominatorTreeNode*)> func) {
tree_.Visit(func);
}
// Applies the std::function |func| to dominator tree nodes in dominator
// order.
void Visit(std::function<bool(const DominatorTreeNode*)> func) const {
tree_.Visit(func);
}
protected:
DominatorTree tree_;
};
// Derived class for normal dominator analysis.
class DominatorAnalysis : public DominatorAnalysisBase {
public:
DominatorAnalysis() : DominatorAnalysisBase(false) {}
};
// Derived class for postdominator analysis.
class PostDominatorAnalysis : public DominatorAnalysisBase {
public:
PostDominatorAnalysis() : DominatorAnalysisBase(true) {}
};
} // namespace opt
} // namespace spvtools
#endif // LIBSPIRV_OPT_DOMINATOR_ANALYSIS_PASS_H_

View File

@ -0,0 +1,419 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <iostream>
#include <memory>
#include <set>
#include "cfa.h"
#include "dominator_tree.h"
using namespace spvtools;
using namespace spvtools::opt;
// Calculates the dominator or postdominator tree for a given function.
// 1 - Compute the successors and predecessors for each BasicBlock. We add a
// dummy node for the start node or for postdominators the exit. This node will
// point to all entry or all exit nodes.
// 2 - Using the CFA::DepthFirstTraversal get a depth first postordered list of
// all BasicBlocks. Using the successors (or for postdominator, predecessors)
// calculated in step 1 to traverse the tree.
// 3 - Pass the list calculated in step 2 to the CFA::CalculateDominators using
// the predecessors list (or for postdominator, successors). This will give us a
// vector of BB pairs. Each BB and its immediate dominator.
// 4 - Using the list from 3 use those edges to build a tree of
// DominatorTreeNodes. Each node containing a link to the parent dominator and
// children which are dominated.
// 5 - Using the tree from 4, perform a depth first traversal to calculate the
// preorder and postorder index of each node. We use these indexes to compare
// nodes against each other for domination checks.
namespace {
// Wrapper around CFA::DepthFirstTraversal to provide an interface to perform
// depth first search on generic BasicBlock types. Will call post and pre order
// user defined functions during traversal
//
// BBType - BasicBlock type. Will either be ir::BasicBlock or DominatorTreeNode
// SuccessorLambda - Lamdba matching the signature of 'const
// std::vector<BBType>*(const BBType *A)'. Will return a vector of the nodes
// succeding BasicBlock A.
// PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be
// called on each node traversed AFTER their children.
// PreLambda - Lamdba matching the signature of 'void (const BBType*)' will be
// called on each node traversed BEFORE their children.
template <typename BBType, typename SuccessorLambda, typename PreLambda,
typename PostLambda>
static void DepthFirstSearch(const BBType* bb, SuccessorLambda successors,
PreLambda pre, PostLambda post) {
// Ignore backedge operation.
auto nop_backedge = [](const BBType*, const BBType*) {};
CFA<BBType>::DepthFirstTraversal(bb, successors, pre, post, nop_backedge);
}
// Wrapper around CFA::DepthFirstTraversal to provide an interface to perform
// depth first search on generic BasicBlock types. This overload is for only
// performing user defined post order.
//
// BBType - BasicBlock type. Will either be ir::BasicBlock or DominatorTreeNode
// SuccessorLambda - Lamdba matching the signature of 'const
// std::vector<BBType>*(const BBType *A)'. Will return a vector of the nodes
// succeding BasicBlock A.
// PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be
// called on each node traversed after their children.
template <typename BBType, typename SuccessorLambda, typename PostLambda>
static void DepthFirstSearchPostOrder(const BBType* bb,
SuccessorLambda successors,
PostLambda post) {
// Ignore preorder operation.
auto nop_preorder = [](const BBType*) {};
DepthFirstSearch(bb, successors, nop_preorder, post);
}
// Small type trait to get the function class type.
template <typename BBType>
struct GetFunctionClass {
using FunctionType = ir::Function;
};
// Helper class to compute predecessors and successors for each Basic Block in a
// function. Through GetPredFunctor and GetSuccessorFunctor it provides an
// interface to get the successor and predecessor lists for each basic
// block. This is required by the DepthFirstTraversal and ComputeDominator
// functions which take as parameter an std::function returning the successors
// and predecessors respectively.
//
// When computing the post-dominator tree, all edges are inverted. So successors
// returned by this class will be predecessors in the original CFG.
template <typename BBType>
class BasicBlockSuccessorHelper {
// This should eventually become const ir::BasicBlock.
using BasicBlock = BBType;
using Function = typename GetFunctionClass<BBType>::FunctionType;
using BasicBlockListTy = std::vector<BasicBlock*>;
using BasicBlockMapTy = std::map<const BasicBlock*, BasicBlockListTy>;
public:
// For compliance with the dominance tree computation, entry nodes are
// connected to a single dummy node.
BasicBlockSuccessorHelper(Function& func, const BasicBlock* dummy_start_node,
bool post);
// CFA::CalculateDominators requires std::vector<BasicBlock*>.
using GetBlocksFunction =
std::function<const std::vector<BasicBlock*>*(const BasicBlock*)>;
// Returns the list of predecessor functions.
GetBlocksFunction GetPredFunctor() {
return [this](const BasicBlock* bb) {
BasicBlockListTy* v = &this->predecessors_[bb];
return v;
};
}
// Returns a vector of the list of successor nodes from a given node.
GetBlocksFunction GetSuccessorFunctor() {
return [this](const BasicBlock* bb) {
BasicBlockListTy* v = &this->successors_[bb];
return v;
};
}
private:
bool invert_graph_;
BasicBlockMapTy successors_;
BasicBlockMapTy predecessors_;
// Build the successors and predecessors map for each basic blocks |f|.
// If |invert_graph_| is true, all edges are reversed (successors becomes
// predecessors and vice versa).
// For convenience, the start of the graph is |dummy_start_node|.
// The dominator tree construction requires a unique entry node, which cannot
// be guaranteed for the postdominator graph. The |dummy_start_node| BB is
// here to gather all entry nodes.
void CreateSuccessorMap(Function& f, const BasicBlock* dummy_start_node);
};
template <typename BBType>
BasicBlockSuccessorHelper<BBType>::BasicBlockSuccessorHelper(
Function& func, const BasicBlock* dummy_start_node, bool invert)
: invert_graph_(invert) {
CreateSuccessorMap(func, dummy_start_node);
}
template <typename BBType>
void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap(
Function& f, const BasicBlock* dummy_start_node) {
std::map<uint32_t, BasicBlock*> id_to_BB_map;
auto GetSuccessorBasicBlock = [&f, &id_to_BB_map](uint32_t successor_id) {
BasicBlock*& Succ = id_to_BB_map[successor_id];
if (!Succ) {
for (BasicBlock& BBIt : f) {
if (successor_id == BBIt.id()) {
Succ = &BBIt;
break;
}
}
}
return Succ;
};
if (invert_graph_) {
// For the post dominator tree, we see the inverted graph.
// successors_ in the inverted graph are the predecessors in the CFG.
// The tree construction requires 1 entry point, so we add a dummy node
// that is connected to all function exiting basic blocks.
// An exiting basic block is a block with an OpKill, OpUnreachable,
// OpReturn or OpReturnValue as terminator instruction.
for (BasicBlock& bb : f) {
if (bb.hasSuccessor()) {
BasicBlockListTy& pred_list = predecessors_[&bb];
bb.ForEachSuccessorLabel(
[this, &pred_list, &bb,
&GetSuccessorBasicBlock](const uint32_t successor_id) {
BasicBlock* succ = GetSuccessorBasicBlock(successor_id);
// Inverted graph: our successors in the CFG
// are our predecessors in the inverted graph.
this->successors_[succ].push_back(&bb);
pred_list.push_back(succ);
});
} else {
successors_[dummy_start_node].push_back(&bb);
predecessors_[&bb].push_back(const_cast<BasicBlock*>(dummy_start_node));
}
}
} else {
// Technically, this is not needed, but it unifies
// the handling of dominator and postdom tree later on.
successors_[dummy_start_node].push_back(f.entry().get());
predecessors_[f.entry().get()].push_back(
const_cast<BasicBlock*>(dummy_start_node));
for (BasicBlock& bb : f) {
BasicBlockListTy& succ_list = successors_[&bb];
bb.ForEachSuccessorLabel([&](const uint32_t successor_id) {
BasicBlock* succ = GetSuccessorBasicBlock(successor_id);
succ_list.push_back(succ);
predecessors_[succ].push_back(&bb);
});
}
}
}
} // namespace
namespace spvtools {
namespace opt {
bool DominatorTree::StrictlyDominates(uint32_t a, uint32_t b) const {
if (a == b) return false;
return Dominates(a, b);
}
bool DominatorTree::StrictlyDominates(const ir::BasicBlock* a,
const ir::BasicBlock* b) const {
return DominatorTree::StrictlyDominates(a->id(), b->id());
}
bool DominatorTree::Dominates(uint32_t a, uint32_t b) const {
// Check that both of the inputs are actual nodes.
auto a_itr = nodes_.find(a);
auto b_itr = nodes_.find(b);
if (a_itr == nodes_.end() || b_itr == nodes_.end()) return false;
// Node A dominates node B if they are the same.
if (a == b) return true;
const DominatorTreeNode* nodeA = &a_itr->second;
const DominatorTreeNode* nodeB = &b_itr->second;
if (nodeA->dfs_num_pre_ < nodeB->dfs_num_pre_ &&
nodeA->dfs_num_post_ > nodeB->dfs_num_post_) {
return true;
}
return false;
}
bool DominatorTree::Dominates(const ir::BasicBlock* A,
const ir::BasicBlock* B) const {
return Dominates(A->id(), B->id());
}
ir::BasicBlock* DominatorTree::ImmediateDominator(
const ir::BasicBlock* A) const {
return ImmediateDominator(A->id());
}
ir::BasicBlock* DominatorTree::ImmediateDominator(uint32_t a) const {
// Check that A is a valid node in the tree.
auto a_itr = nodes_.find(a);
if (a_itr == nodes_.end()) return nullptr;
const DominatorTreeNode* node = &a_itr->second;
if (node->parent_ == nullptr) {
return nullptr;
}
return node->parent_->bb_;
}
DominatorTreeNode* DominatorTree::GetOrInsertNode(ir::BasicBlock* bb) {
DominatorTreeNode* dtn = nullptr;
std::map<uint32_t, DominatorTreeNode>::iterator node_iter =
nodes_.find(bb->id());
if (node_iter == nodes_.end()) {
dtn = &nodes_.emplace(std::make_pair(bb->id(), DominatorTreeNode{bb}))
.first->second;
} else
dtn = &node_iter->second;
return dtn;
}
void DominatorTree::GetDominatorEdges(
const ir::Function* f, const ir::BasicBlock* dummy_start_node,
std::vector<std::pair<ir::BasicBlock*, ir::BasicBlock*>>* edges) {
// Each time the depth first traversal calls the postorder callback
// std::function we push that node into the postorder vector to create our
// postorder list.
std::vector<const ir::BasicBlock*> postorder;
auto postorder_function = [&](const ir::BasicBlock* b) {
postorder.push_back(b);
};
// CFA::CalculateDominators requires std::vector<ir::BasicBlock*>
// BB are derived from F, so we need to const cast it at some point
// no modification is made on F.
BasicBlockSuccessorHelper<ir::BasicBlock> helper{
*const_cast<ir::Function*>(f), dummy_start_node, postdominator_};
// The successor function tells DepthFirstTraversal how to move to successive
// nodes by providing an interface to get a list of successor nodes from any
// given node.
auto successor_functor = helper.GetSuccessorFunctor();
// The predecessor functor does the same as the successor functor
// but for all nodes preceding a given node.
auto predecessor_functor = helper.GetPredFunctor();
// If we're building a post dominator tree we traverse the tree in reverse
// using the predecessor function in place of the successor function and vice
// versa.
DepthFirstSearchPostOrder(dummy_start_node, successor_functor,
postorder_function);
*edges =
CFA<ir::BasicBlock>::CalculateDominators(postorder, predecessor_functor);
}
void DominatorTree::InitializeTree(const ir::Function* f, const ir::CFG& cfg) {
ClearTree();
// Skip over empty functions.
if (f->cbegin() == f->cend()) {
return;
}
const ir::BasicBlock* dummy_start_node =
postdominator_ ? cfg.pseudo_exit_block() : cfg.pseudo_entry_block();
// Get the immediate dominator for each node.
std::vector<std::pair<ir::BasicBlock*, ir::BasicBlock*>> edges;
GetDominatorEdges(f, dummy_start_node, &edges);
// Transform the vector<pair> into the tree structure which we can use to
// efficiently query dominance.
for (auto edge : edges) {
DominatorTreeNode* first = GetOrInsertNode(edge.first);
if (edge.first == edge.second) {
if (std::find(roots_.begin(), roots_.end(), first) == roots_.end())
roots_.push_back(first);
continue;
}
DominatorTreeNode* second = GetOrInsertNode(edge.second);
first->parent_ = second;
second->children_.push_back(first);
}
int index = 0;
auto preFunc = [&index](const DominatorTreeNode* node) {
const_cast<DominatorTreeNode*>(node)->dfs_num_pre_ = ++index;
};
auto postFunc = [&index](const DominatorTreeNode* node) {
const_cast<DominatorTreeNode*>(node)->dfs_num_post_ = ++index;
};
auto getSucc = [](const DominatorTreeNode* node) { return &node->children_; };
for (auto root : roots_) DepthFirstSearch(root, getSucc, preFunc, postFunc);
}
void DominatorTree::DumpTreeAsDot(std::ostream& out_stream) const {
out_stream << "digraph {\n";
Visit([&out_stream](const DominatorTreeNode* node) {
// Print the node.
if (node->bb_) {
out_stream << node->bb_->id() << "[label=\"" << node->bb_->id()
<< "\"];\n";
}
// Print the arrow from the parent to this node. Entry nodes will not have
// parents so draw them as children from the dummy node.
if (node->parent_) {
out_stream << node->parent_->bb_->id() << " -> " << node->bb_->id()
<< ";\n";
}
// Return true to continue the traversal.
return true;
});
out_stream << "}\n";
}
bool DominatorTree::Visit(DominatorTreeNode* node,
std::function<bool(DominatorTreeNode*)> func) {
// Apply the function to the node.
if (!func(node)) return false;
// Apply the function to every child node.
for (DominatorTreeNode* child : node->children_) {
if (!Visit(child, func)) return false;
}
return true;
}
bool DominatorTree::Visit(
const DominatorTreeNode* node,
std::function<bool(const DominatorTreeNode*)> func) const {
// Apply the function to the node.
if (!func(node)) return false;
// Apply the function to every child node.
for (const DominatorTreeNode* child : node->children_) {
if (!Visit(child, func)) return false;
}
return true;
}
} // namespace opt
} // namespace spvtools

201
source/opt/dominator_tree.h Normal file
View File

@ -0,0 +1,201 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef LIBSPIRV_OPT_DOMINATOR_ANALYSIS_TREE_H_
#define LIBSPIRV_OPT_DOMINATOR_ANALYSIS_TREE_H_
#include <cstdint>
#include <map>
#include <utility>
#include <vector>
#include "cfg.h"
#include "module.h"
namespace spvtools {
namespace opt {
// This helper struct forms the nodes in the tree, with each node containing its
// children. It also contains two values, for the pre and post indexes in the
// tree which are used to compare two nodes.
struct DominatorTreeNode {
explicit DominatorTreeNode(ir::BasicBlock* bb)
: bb_(bb),
parent_(nullptr),
children_({}),
dfs_num_pre_(-1),
dfs_num_post_(-1) {}
inline uint32_t id() const { return bb_->id(); }
ir::BasicBlock* bb_;
DominatorTreeNode* parent_;
std::vector<DominatorTreeNode*> children_;
// These indexes are used to compare two given nodes. A node is a child or
// grandchild of another node if its preorder index is greater than the
// first nodes preorder index AND if its postorder index is less than the
// first nodes postorder index.
int dfs_num_pre_;
int dfs_num_post_;
};
// A class representing a tree of BasicBlocks in a given function, where each
// node is dominated by its parent.
class DominatorTree {
public:
// Map OpLabel ids to dominator tree nodes
using DominatorTreeNodeMap = std::map<uint32_t, DominatorTreeNode>;
using iterator = DominatorTreeNodeMap::iterator;
using const_iterator = DominatorTreeNodeMap::const_iterator;
// List of DominatorTreeNode to define the list of roots
using DominatorTreeNodeList = std::vector<DominatorTreeNode*>;
using roots_iterator = DominatorTreeNodeList::iterator;
using roots_const_iterator = DominatorTreeNodeList::const_iterator;
DominatorTree() : postdominator_(false) {}
explicit DominatorTree(bool post) : postdominator_(post) {}
iterator begin() { return nodes_.begin(); }
iterator end() { return nodes_.end(); }
const_iterator begin() const { return cbegin(); }
const_iterator end() const { return cend(); }
const_iterator cbegin() const { return nodes_.begin(); }
const_iterator cend() const { return nodes_.end(); }
roots_iterator roots_begin() { return roots_.begin(); }
roots_iterator roots_end() { return roots_.end(); }
roots_const_iterator roots_begin() const { return roots_cbegin(); }
roots_const_iterator roots_end() const { return roots_cend(); }
roots_const_iterator roots_cbegin() const { return roots_.begin(); }
roots_const_iterator roots_cend() const { return roots_.end(); }
// Get the unique root of the tree.
// It is guaranteed to work on a dominator tree.
// post-dominator might have a list.
DominatorTreeNode* GetRoot() {
assert(roots_.size() == 1);
return *roots_.begin();
}
const DominatorTreeNode* GetRoot() const {
assert(roots_.size() == 1);
return *roots_.begin();
}
const DominatorTreeNodeList& Roots() const { return roots_; }
// Dumps the tree in the graphvis dot format into the |out_stream|.
void DumpTreeAsDot(std::ostream& out_stream) const;
// Build the (post-)dominator tree for the function |f|
// Any existing data will be overwritten
void InitializeTree(const ir::Function* f, const ir::CFG& cfg);
// Check if the basic block |a| dominates the basic block |b|.
bool Dominates(const ir::BasicBlock* a, const ir::BasicBlock* b) const;
// Check if the basic block id |a| dominates the basic block id |b|.
bool Dominates(uint32_t a, uint32_t b) const;
// Check if the basic block |a| strictly dominates the basic block |b|.
bool StrictlyDominates(const ir::BasicBlock* a,
const ir::BasicBlock* b) const;
// Check if the basic block id |a| strictly dominates the basic block id |b|.
bool StrictlyDominates(uint32_t a, uint32_t b) const;
// Returns the immediate dominator of basic block |a|.
ir::BasicBlock* ImmediateDominator(const ir::BasicBlock* A) const;
// Returns the immediate dominator of basic block id |a|.
ir::BasicBlock* ImmediateDominator(uint32_t a) const;
// Returns true if the basic block |a| is reachable by this tree. A node would
// be unreachable if it cannot be reached by traversal from the start node or
// for a postdominator tree, cannot be reached from the exit nodes.
inline bool ReachableFromRoots(const ir::BasicBlock* a) const {
if (!a) return false;
return ReachableFromRoots(a->id());
}
// Returns true if the basic block id |a| is reachable by this tree.
bool ReachableFromRoots(uint32_t a) const;
// Returns true if this tree is a post dominator tree.
bool IsPostDominator() const { return postdominator_; }
// Clean up the tree.
void ClearTree() {
nodes_.clear();
roots_.clear();
}
// Applies the std::function |func| to all nodes in the dominator tree.
bool Visit(std::function<bool(DominatorTreeNode*)> func) {
for (auto n : roots_) {
if (!Visit(n, func)) return false;
}
return true;
}
// Applies the std::function |func| to all nodes in the dominator tree.
bool Visit(std::function<bool(const DominatorTreeNode*)> func) const {
for (auto n : roots_) {
if (!Visit(n, func)) return false;
}
return true;
}
// Applies the std::function |func| to |node| then applies it to nodes
// children.
bool Visit(DominatorTreeNode* node,
std::function<bool(DominatorTreeNode*)> func);
// Applies the std::function |func| to |node| then applies it to nodes
// children.
bool Visit(const DominatorTreeNode* node,
std::function<bool(const DominatorTreeNode*)> func) const;
private:
// Adds the basic block |bb| to the tree structure if it doesn't already
// exist.
DominatorTreeNode* GetOrInsertNode(ir::BasicBlock* bb);
// Wrapper function which gets the list of pairs of each BasicBlocks to its
// immediately dominating BasicBlock and stores the result in the the edges
// parameter.
//
// The |edges| vector will contain the dominator tree as pairs of nodes.
// The first node in the pair is a node in the graph. The second node in the
// pair is its immediate dominator.
// The root of the tree has themself as immediate dominator.
void GetDominatorEdges(
const ir::Function* f, const ir::BasicBlock* dummy_start_node,
std::vector<std::pair<ir::BasicBlock*, ir::BasicBlock*>>* edges);
// The roots of the tree.
std::vector<DominatorTreeNode*> roots_;
// Pairs each basic block id to the tree node containing that basic block.
DominatorTreeNodeMap nodes_;
// True if this is a post dominator tree.
bool postdominator_;
};
} // namespace opt
} // namespace spvtools
#endif // LIBSPIRV_OPT_DOMINATOR_ANALYSIS_TREE_H_

View File

@ -82,6 +82,8 @@ class Function {
iterator begin() { return iterator(&blocks_, blocks_.begin()); }
iterator end() { return iterator(&blocks_, blocks_.end()); }
const_iterator begin() const { return cbegin(); }
const_iterator end() const { return cend(); }
const_iterator cbegin() const {
return const_iterator(&blocks_, blocks_.cbegin());
}

View File

@ -34,6 +34,12 @@ void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) {
if (set & kAnalysisCFG) {
BuildCFG();
}
if (set & kAnalysisDominatorAnalysis) {
// An invalid dominator tree analysis will be empty so rebuilding it just
// means marking it as valid. Each tree will be initalisalised when
// requested on a per function basis.
valid_analyses_ |= kAnalysisDominatorAnalysis;
}
}
void IRContext::InvalidateAnalysesExceptFor(
@ -58,6 +64,11 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
if (analyses_to_invalidate & kAnalysisCFG) {
cfg_.reset(nullptr);
}
if (analyses_to_invalidate & kAnalysisDominatorAnalysis) {
dominator_trees_.clear();
post_dominator_trees_.clear();
}
valid_analyses_ = Analysis(valid_analyses_ & ~analyses_to_invalidate);
}
@ -456,5 +467,28 @@ void IRContext::InitializeCombinators() {
valid_analyses_ |= kAnalysisCombinators;
}
// Gets the dominator analysis for function |f|.
opt::DominatorAnalysis* IRContext::GetDominatorAnalysis(const ir::Function* f,
const ir::CFG& in_cfg) {
if (dominator_trees_.find(f) == dominator_trees_.end() ||
!AreAnalysesValid(kAnalysisDominatorAnalysis)) {
dominator_trees_[f].InitializeTree(f, in_cfg);
}
return &dominator_trees_[f];
}
// Gets the postdominator analysis for function |f|.
opt::PostDominatorAnalysis* IRContext::GetPostDominatorAnalysis(
const ir::Function* f, const ir::CFG& in_cfg) {
if (post_dominator_trees_.find(f) == post_dominator_trees_.end() ||
!AreAnalysesValid(kAnalysisDominatorAnalysis)) {
post_dominator_trees_[f].InitializeTree(f, in_cfg);
}
return &post_dominator_trees_[f];
}
} // namespace ir
} // namespace spvtools

View File

@ -18,6 +18,7 @@
#include "cfg.h"
#include "decoration_manager.h"
#include "def_use_manager.h"
#include "dominator_analysis.h"
#include "module.h"
#include <algorithm>
@ -49,7 +50,8 @@ class IRContext {
kAnalysisDecorations = 1 << 2,
kAnalysisCombinators = 1 << 3,
kAnalysisCFG = 1 << 4,
kAnalysisEnd = 1 << 5
kAnalysisDominatorAnalysis = 1 << 5,
kAnalysisEnd = 1 << 6
};
friend inline Analysis operator|(Analysis lhs, Analysis rhs);
@ -307,6 +309,24 @@ class IRContext {
return cfg_.get();
}
// Gets the dominator analysis for function |f|.
opt::DominatorAnalysis* GetDominatorAnalysis(const ir::Function* f,
const ir::CFG&);
// Gets the postdominator analysis for function |f|.
opt::PostDominatorAnalysis* GetPostDominatorAnalysis(const ir::Function* f,
const ir::CFG&);
// Remove the dominator tree of |f| from the cache.
inline void RemoveDominatorAnalysis(const ir::Function* f) {
dominator_trees_.erase(f);
}
// Remove the postdominator tree of |f| from the cache.
inline void RemovePostDominatorAnalysis(const ir::Function* f) {
post_dominator_trees_.erase(f);
}
private:
// Builds the def-use manager from scratch, even if it was already valid.
void BuildDefUseManager() {
@ -375,6 +395,12 @@ class IRContext {
// The CFG for all the functions in |module_|.
std::unique_ptr<ir::CFG> cfg_;
// Each function in the module will create its own dominator tree. We cache
// the result so it doesn't need to be rebuilt each time.
std::map<const ir::Function*, opt::DominatorAnalysis> dominator_trees_;
std::map<const ir::Function*, opt::PostDominatorAnalysis>
post_dominator_trees_;
};
inline ir::IRContext::Analysis operator|(ir::IRContext::Analysis lhs,

View File

@ -201,6 +201,8 @@ class Module {
// Iterators for functions contained in this module.
iterator begin() { return iterator(&functions_, functions_.begin()); }
iterator end() { return iterator(&functions_, functions_.end()); }
const_iterator begin() const { return cbegin(); }
const_iterator end() const { return cend(); }
inline const_iterator cbegin() const;
inline const_iterator cend() const;

View File

@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
add_subdirectory(dominator_tree)
add_spvtools_unittest(TARGET instruction
SRCS instruction_test.cpp
LIBS SPIRV-Tools-opt

View File

@ -0,0 +1,74 @@
# Copyright (c) 2017 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
add_spvtools_unittest(TARGET dominator_analysis_simple
SRCS ../function_utils.h
simple.cpp
LIBS SPIRV-Tools-opt
)
add_spvtools_unittest(TARGET dominator_analysis_post
SRCS ../function_utils.h
post.cpp
LIBS SPIRV-Tools-opt
)
add_spvtools_unittest(TARGET dominator_analysis_nested_ifs
SRCS ../function_utils.h
nested_ifs.cpp
LIBS SPIRV-Tools-opt
)
add_spvtools_unittest(TARGET dominator_analysis_nested_ifs_post
SRCS ../function_utils.h
nested_ifs_post.cpp
LIBS SPIRV-Tools-opt
)
add_spvtools_unittest(TARGET dominator_analysis_nested_loops
SRCS ../function_utils.h
nested_loops.cpp
LIBS SPIRV-Tools-opt
)
add_spvtools_unittest(TARGET dominator_analysis_nested_loops_with_unreachables
SRCS ../function_utils.h
nested_loops_with_unreachables.cpp
LIBS SPIRV-Tools-opt
)
add_spvtools_unittest(TARGET dominator_analysis_switch_case_fallthrough
SRCS ../function_utils.h
switch_case_fallthrough.cpp
LIBS SPIRV-Tools-opt
)
add_spvtools_unittest(TARGET dominator_analysis_unreachable_for
SRCS ../function_utils.h
unreachable_for.cpp
LIBS SPIRV-Tools-opt
)
add_spvtools_unittest(TARGET dominator_analysis_unreachable_for_post
SRCS ../function_utils.h
unreachable_for_post.cpp
LIBS SPIRV-Tools-opt
)
add_spvtools_unittest(TARGET dominator_generated
SRCS ../function_utils.h
generated.cpp
LIBS SPIRV-Tools-opt
)

View File

@ -0,0 +1,798 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gmock/gmock.h>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "../assembly_builder.h"
#include "../function_utils.h"
#include "../pass_fixture.h"
#include "../pass_utils.h"
#include "opt/dominator_analysis.h"
#include "opt/pass.h"
namespace {
using namespace spvtools;
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
// Check that x dominates y, and
// if x != y then
// x strictly dominates y and
// y does not dominate x and
// y does not strictly dominate x
// if x == x then
// x does not strictly dominate itself
void check_dominance(const opt::DominatorAnalysisBase& dom_tree,
const ir::Function* fn, uint32_t x, uint32_t y) {
SCOPED_TRACE("Check dominance properties for Basic Block " +
std::to_string(x) + " and " + std::to_string(y));
EXPECT_TRUE(dom_tree.Dominates(spvtest::GetBasicBlock(fn, x),
spvtest::GetBasicBlock(fn, y)));
EXPECT_TRUE(dom_tree.Dominates(x, y));
if (x == y) {
EXPECT_FALSE(dom_tree.StrictlyDominates(x, x));
} else {
EXPECT_TRUE(dom_tree.StrictlyDominates(x, y));
EXPECT_FALSE(dom_tree.Dominates(y, x));
EXPECT_FALSE(dom_tree.StrictlyDominates(y, x));
}
}
// Check that x does not dominates y and vise versa
void check_no_dominance(const opt::DominatorAnalysisBase& dom_tree,
const ir::Function* fn, uint32_t x, uint32_t y) {
SCOPED_TRACE("Check no domination for Basic Block " + std::to_string(x) +
" and " + std::to_string(y));
EXPECT_FALSE(dom_tree.Dominates(spvtest::GetBasicBlock(fn, x),
spvtest::GetBasicBlock(fn, y)));
EXPECT_FALSE(dom_tree.Dominates(x, y));
EXPECT_FALSE(dom_tree.StrictlyDominates(spvtest::GetBasicBlock(fn, x),
spvtest::GetBasicBlock(fn, y)));
EXPECT_FALSE(dom_tree.StrictlyDominates(x, y));
EXPECT_FALSE(dom_tree.Dominates(spvtest::GetBasicBlock(fn, y),
spvtest::GetBasicBlock(fn, x)));
EXPECT_FALSE(dom_tree.Dominates(y, x));
EXPECT_FALSE(dom_tree.StrictlyDominates(spvtest::GetBasicBlock(fn, y),
spvtest::GetBasicBlock(fn, x)));
EXPECT_FALSE(dom_tree.StrictlyDominates(y, x));
}
TEST_F(PassClassTest, DominatorSimpleCFG) {
const std::string text = R"(
OpCapability Addresses
OpCapability Kernel
OpMemoryModel Physical64 OpenCL
OpEntryPoint Kernel %1 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpTypeBool
%5 = OpTypeInt 32 0
%6 = OpConstant %5 0
%7 = OpConstantFalse %4
%8 = OpConstantTrue %4
%9 = OpConstant %5 1
%1 = OpFunction %2 None %3
%10 = OpLabel
OpBranch %11
%11 = OpLabel
OpSwitch %6 %12 1 %13
%12 = OpLabel
OpBranch %14
%13 = OpLabel
OpBranch %14
%14 = OpLabel
OpBranchConditional %8 %11 %15
%15 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const ir::Function* fn = spvtest::GetFunction(module, 1);
const ir::BasicBlock* entry = spvtest::GetBasicBlock(fn, 10);
EXPECT_EQ(entry, fn->entry().get())
<< "The entry node is not the expected one";
// Test normal dominator tree
{
opt::DominatorAnalysis dom_tree;
ir::CFG cfg(module);
dom_tree.InitializeTree(fn, cfg);
// Inspect the actual tree
opt::DominatorTree& tree = dom_tree.GetDomTree();
EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block());
EXPECT_TRUE(
dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id()));
// (strict) dominance checks
for (uint32_t id : {10, 11, 12, 13, 14, 15})
check_dominance(dom_tree, fn, id, id);
check_dominance(dom_tree, fn, 10, 11);
check_dominance(dom_tree, fn, 10, 12);
check_dominance(dom_tree, fn, 10, 13);
check_dominance(dom_tree, fn, 10, 14);
check_dominance(dom_tree, fn, 10, 15);
check_dominance(dom_tree, fn, 11, 12);
check_dominance(dom_tree, fn, 11, 13);
check_dominance(dom_tree, fn, 11, 14);
check_dominance(dom_tree, fn, 11, 15);
check_dominance(dom_tree, fn, 14, 15);
check_no_dominance(dom_tree, fn, 12, 13);
check_no_dominance(dom_tree, fn, 12, 14);
check_no_dominance(dom_tree, fn, 13, 14);
// check with some invalid inputs
EXPECT_FALSE(dom_tree.Dominates(nullptr, entry));
EXPECT_FALSE(dom_tree.Dominates(entry, nullptr));
EXPECT_FALSE(dom_tree.Dominates(nullptr, nullptr));
EXPECT_FALSE(dom_tree.Dominates(10, 1));
EXPECT_FALSE(dom_tree.Dominates(1, 10));
EXPECT_FALSE(dom_tree.Dominates(1, 1));
EXPECT_FALSE(dom_tree.StrictlyDominates(nullptr, entry));
EXPECT_FALSE(dom_tree.StrictlyDominates(entry, nullptr));
EXPECT_FALSE(dom_tree.StrictlyDominates(nullptr, nullptr));
EXPECT_FALSE(dom_tree.StrictlyDominates(10, 1));
EXPECT_FALSE(dom_tree.StrictlyDominates(1, 10));
EXPECT_FALSE(dom_tree.StrictlyDominates(1, 1));
EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr);
EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block());
EXPECT_EQ(dom_tree.ImmediateDominator(nullptr), nullptr);
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
spvtest::GetBasicBlock(fn, 10));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
spvtest::GetBasicBlock(fn, 11));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 13)),
spvtest::GetBasicBlock(fn, 11));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 14)),
spvtest::GetBasicBlock(fn, 11));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 15)),
spvtest::GetBasicBlock(fn, 14));
}
// Test post dominator tree
{
opt::PostDominatorAnalysis dom_tree;
ir::CFG cfg(module);
dom_tree.InitializeTree(fn, cfg);
// Inspect the actual tree
opt::DominatorTree& tree = dom_tree.GetDomTree();
EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_exit_block());
EXPECT_TRUE(dom_tree.Dominates(cfg.pseudo_exit_block()->id(), 15));
// (strict) dominance checks
for (uint32_t id : {10, 11, 12, 13, 14, 15})
check_dominance(dom_tree, fn, id, id);
check_dominance(dom_tree, fn, 14, 10);
check_dominance(dom_tree, fn, 14, 11);
check_dominance(dom_tree, fn, 14, 12);
check_dominance(dom_tree, fn, 14, 13);
check_dominance(dom_tree, fn, 15, 10);
check_dominance(dom_tree, fn, 15, 11);
check_dominance(dom_tree, fn, 15, 12);
check_dominance(dom_tree, fn, 15, 13);
check_dominance(dom_tree, fn, 15, 14);
check_no_dominance(dom_tree, fn, 13, 12);
check_no_dominance(dom_tree, fn, 12, 11);
check_no_dominance(dom_tree, fn, 13, 11);
// check with some invalid inputs
EXPECT_FALSE(dom_tree.Dominates(nullptr, entry));
EXPECT_FALSE(dom_tree.Dominates(entry, nullptr));
EXPECT_FALSE(dom_tree.Dominates(nullptr, nullptr));
EXPECT_FALSE(dom_tree.Dominates(10, 1));
EXPECT_FALSE(dom_tree.Dominates(1, 10));
EXPECT_FALSE(dom_tree.Dominates(1, 1));
EXPECT_FALSE(dom_tree.StrictlyDominates(nullptr, entry));
EXPECT_FALSE(dom_tree.StrictlyDominates(entry, nullptr));
EXPECT_FALSE(dom_tree.StrictlyDominates(nullptr, nullptr));
EXPECT_FALSE(dom_tree.StrictlyDominates(10, 1));
EXPECT_FALSE(dom_tree.StrictlyDominates(1, 10));
EXPECT_FALSE(dom_tree.StrictlyDominates(1, 1));
EXPECT_EQ(dom_tree.ImmediateDominator(nullptr), nullptr);
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
spvtest::GetBasicBlock(fn, 14));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
spvtest::GetBasicBlock(fn, 14));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 13)),
spvtest::GetBasicBlock(fn, 14));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 14)),
spvtest::GetBasicBlock(fn, 15));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 15)),
cfg.pseudo_exit_block());
EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr);
}
}
TEST_F(PassClassTest, DominatorIrreducibleCFG) {
const std::string text = R"(
OpCapability Addresses
OpCapability Kernel
OpMemoryModel Physical64 OpenCL
OpEntryPoint Kernel %1 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpTypeBool
%5 = OpTypeInt 32 0
%6 = OpConstantFalse %4
%7 = OpConstantTrue %4
%1 = OpFunction %2 None %3
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpBranchConditional %7 %10 %11
%10 = OpLabel
OpBranch %11
%11 = OpLabel
OpBranchConditional %7 %10 %12
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const ir::Function* fn = spvtest::GetFunction(module, 1);
const ir::BasicBlock* entry = spvtest::GetBasicBlock(fn, 8);
EXPECT_EQ(entry, fn->entry().get())
<< "The entry node is not the expected one";
// Check normal dominator tree
{
opt::DominatorAnalysis dom_tree;
ir::CFG cfg(module);
dom_tree.InitializeTree(fn, cfg);
// Inspect the actual tree
opt::DominatorTree& tree = dom_tree.GetDomTree();
EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block());
EXPECT_TRUE(
dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id()));
// (strict) dominance checks
for (uint32_t id : {8, 9, 10, 11, 12})
check_dominance(dom_tree, fn, id, id);
check_dominance(dom_tree, fn, 8, 9);
check_dominance(dom_tree, fn, 8, 10);
check_dominance(dom_tree, fn, 8, 11);
check_dominance(dom_tree, fn, 8, 12);
check_dominance(dom_tree, fn, 9, 10);
check_dominance(dom_tree, fn, 9, 11);
check_dominance(dom_tree, fn, 9, 12);
check_dominance(dom_tree, fn, 11, 12);
check_no_dominance(dom_tree, fn, 10, 11);
EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr);
EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block());
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 9)),
spvtest::GetBasicBlock(fn, 8));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)),
spvtest::GetBasicBlock(fn, 9));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
spvtest::GetBasicBlock(fn, 9));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
spvtest::GetBasicBlock(fn, 11));
}
// Check post dominator tree
{
opt::PostDominatorAnalysis dom_tree;
ir::CFG cfg(module);
dom_tree.InitializeTree(fn, cfg);
// Inspect the actual tree
opt::DominatorTree& tree = dom_tree.GetDomTree();
EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_exit_block());
EXPECT_TRUE(dom_tree.Dominates(cfg.pseudo_exit_block()->id(), 12));
// (strict) dominance checks
for (uint32_t id : {8, 9, 10, 11, 12})
check_dominance(dom_tree, fn, id, id);
check_dominance(dom_tree, fn, 12, 8);
check_dominance(dom_tree, fn, 12, 10);
check_dominance(dom_tree, fn, 12, 11);
check_dominance(dom_tree, fn, 12, 12);
check_dominance(dom_tree, fn, 11, 8);
check_dominance(dom_tree, fn, 11, 9);
check_dominance(dom_tree, fn, 11, 10);
check_dominance(dom_tree, fn, 9, 8);
EXPECT_EQ(dom_tree.ImmediateDominator(entry),
spvtest::GetBasicBlock(fn, 9));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 9)),
spvtest::GetBasicBlock(fn, 11));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)),
spvtest::GetBasicBlock(fn, 11));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
spvtest::GetBasicBlock(fn, 12));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
cfg.pseudo_exit_block());
EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr);
}
}
TEST_F(PassClassTest, DominatorLoopToSelf) {
const std::string text = R"(
OpCapability Addresses
OpCapability Kernel
OpMemoryModel Physical64 OpenCL
OpEntryPoint Kernel %1 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpTypeBool
%5 = OpTypeInt 32 0
%6 = OpConstant %5 0
%7 = OpConstantFalse %4
%8 = OpConstantTrue %4
%9 = OpConstant %5 1
%1 = OpFunction %2 None %3
%10 = OpLabel
OpBranch %11
%11 = OpLabel
OpSwitch %6 %12 1 %11
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const ir::Function* fn = spvtest::GetFunction(module, 1);
const ir::BasicBlock* entry = spvtest::GetBasicBlock(fn, 10);
EXPECT_EQ(entry, fn->entry().get())
<< "The entry node is not the expected one";
// Check normal dominator tree
{
opt::DominatorAnalysis dom_tree;
ir::CFG cfg(module);
dom_tree.InitializeTree(fn, cfg);
// Inspect the actual tree
opt::DominatorTree& tree = dom_tree.GetDomTree();
EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block());
EXPECT_TRUE(
dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id()));
// (strict) dominance checks
for (uint32_t id : {10, 11, 12}) check_dominance(dom_tree, fn, id, id);
check_dominance(dom_tree, fn, 10, 11);
check_dominance(dom_tree, fn, 10, 12);
check_dominance(dom_tree, fn, 11, 12);
EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr);
EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block());
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
spvtest::GetBasicBlock(fn, 10));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
spvtest::GetBasicBlock(fn, 11));
}
// Check post dominator tree
{
opt::PostDominatorAnalysis dom_tree;
ir::CFG cfg(module);
dom_tree.InitializeTree(fn, cfg);
// Inspect the actual tree
opt::DominatorTree& tree = dom_tree.GetDomTree();
EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_exit_block());
EXPECT_TRUE(dom_tree.Dominates(cfg.pseudo_exit_block()->id(), 12));
// (strict) dominance checks
for (uint32_t id : {10, 11, 12}) check_dominance(dom_tree, fn, id, id);
check_dominance(dom_tree, fn, 12, 10);
check_dominance(dom_tree, fn, 12, 11);
check_dominance(dom_tree, fn, 12, 12);
EXPECT_EQ(dom_tree.ImmediateDominator(entry),
spvtest::GetBasicBlock(fn, 11));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
spvtest::GetBasicBlock(fn, 12));
EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr);
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
cfg.pseudo_exit_block());
}
}
TEST_F(PassClassTest, DominatorUnreachableInLoop) {
const std::string text = R"(
OpCapability Addresses
OpCapability Kernel
OpMemoryModel Physical64 OpenCL
OpEntryPoint Kernel %1 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpTypeBool
%5 = OpTypeInt 32 0
%6 = OpConstant %5 0
%7 = OpConstantFalse %4
%8 = OpConstantTrue %4
%9 = OpConstant %5 1
%1 = OpFunction %2 None %3
%10 = OpLabel
OpBranch %11
%11 = OpLabel
OpSwitch %6 %12 1 %13
%12 = OpLabel
OpBranch %14
%13 = OpLabel
OpUnreachable
%14 = OpLabel
OpBranchConditional %8 %11 %15
%15 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const ir::Function* fn = spvtest::GetFunction(module, 1);
const ir::BasicBlock* entry = spvtest::GetBasicBlock(fn, 10);
EXPECT_EQ(entry, fn->entry().get())
<< "The entry node is not the expected one";
// Check normal dominator tree
{
opt::DominatorAnalysis dom_tree;
ir::CFG cfg(module);
dom_tree.InitializeTree(fn, cfg);
// Inspect the actual tree
opt::DominatorTree& tree = dom_tree.GetDomTree();
EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block());
EXPECT_TRUE(
dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id()));
// (strict) dominance checks
for (uint32_t id : {10, 11, 12, 13, 14, 15})
check_dominance(dom_tree, fn, id, id);
check_dominance(dom_tree, fn, 10, 11);
check_dominance(dom_tree, fn, 10, 13);
check_dominance(dom_tree, fn, 10, 12);
check_dominance(dom_tree, fn, 10, 14);
check_dominance(dom_tree, fn, 10, 15);
check_dominance(dom_tree, fn, 11, 12);
check_dominance(dom_tree, fn, 11, 13);
check_dominance(dom_tree, fn, 11, 14);
check_dominance(dom_tree, fn, 11, 15);
check_dominance(dom_tree, fn, 12, 14);
check_dominance(dom_tree, fn, 12, 15);
check_dominance(dom_tree, fn, 14, 15);
check_no_dominance(dom_tree, fn, 13, 12);
check_no_dominance(dom_tree, fn, 13, 14);
check_no_dominance(dom_tree, fn, 13, 15);
EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr);
EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block());
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
spvtest::GetBasicBlock(fn, 10));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
spvtest::GetBasicBlock(fn, 11));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 13)),
spvtest::GetBasicBlock(fn, 11));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 14)),
spvtest::GetBasicBlock(fn, 12));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 15)),
spvtest::GetBasicBlock(fn, 14));
}
// Check post dominator tree.
{
opt::PostDominatorAnalysis dom_tree;
ir::CFG cfg(module);
dom_tree.InitializeTree(fn, cfg);
// (strict) dominance checks.
for (uint32_t id : {10, 11, 12, 13, 14, 15})
check_dominance(dom_tree, fn, id, id);
check_no_dominance(dom_tree, fn, 15, 10);
check_no_dominance(dom_tree, fn, 15, 11);
check_no_dominance(dom_tree, fn, 15, 12);
check_no_dominance(dom_tree, fn, 15, 13);
check_no_dominance(dom_tree, fn, 15, 14);
check_dominance(dom_tree, fn, 14, 12);
check_no_dominance(dom_tree, fn, 13, 10);
check_no_dominance(dom_tree, fn, 13, 11);
check_no_dominance(dom_tree, fn, 13, 12);
check_no_dominance(dom_tree, fn, 13, 14);
check_no_dominance(dom_tree, fn, 13, 15);
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)),
spvtest::GetBasicBlock(fn, 11));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
spvtest::GetBasicBlock(fn, 14));
EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr);
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 15)),
cfg.pseudo_exit_block());
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 13)),
cfg.pseudo_exit_block());
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 14)),
cfg.pseudo_exit_block());
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
cfg.pseudo_exit_block());
}
}
TEST_F(PassClassTest, DominatorInfinitLoop) {
const std::string text = R"(
OpCapability Addresses
OpCapability Kernel
OpMemoryModel Physical64 OpenCL
OpEntryPoint Kernel %1 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpTypeBool
%5 = OpTypeInt 32 0
%6 = OpConstant %5 0
%7 = OpConstantFalse %4
%8 = OpConstantTrue %4
%9 = OpConstant %5 1
%1 = OpFunction %2 None %3
%10 = OpLabel
OpBranch %11
%11 = OpLabel
OpSwitch %6 %12 1 %13
%12 = OpLabel
OpReturn
%13 = OpLabel
OpBranch %13
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const ir::Function* fn = spvtest::GetFunction(module, 1);
const ir::BasicBlock* entry = spvtest::GetBasicBlock(fn, 10);
EXPECT_EQ(entry, fn->entry().get())
<< "The entry node is not the expected one";
// Check normal dominator tree
{
opt::DominatorAnalysis dom_tree;
ir::CFG cfg(module);
dom_tree.InitializeTree(fn, cfg);
// Inspect the actual tree
opt::DominatorTree& tree = dom_tree.GetDomTree();
EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block());
EXPECT_TRUE(
dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id()));
// (strict) dominance checks
for (uint32_t id : {10, 11, 12, 13}) check_dominance(dom_tree, fn, id, id);
check_dominance(dom_tree, fn, 10, 11);
check_dominance(dom_tree, fn, 10, 12);
check_dominance(dom_tree, fn, 10, 13);
check_dominance(dom_tree, fn, 11, 12);
check_dominance(dom_tree, fn, 11, 13);
check_no_dominance(dom_tree, fn, 13, 12);
EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr);
EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block());
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
spvtest::GetBasicBlock(fn, 10));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
spvtest::GetBasicBlock(fn, 11));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 13)),
spvtest::GetBasicBlock(fn, 11));
}
// Check post dominator tree
{
opt::PostDominatorAnalysis dom_tree;
ir::CFG cfg(module);
dom_tree.InitializeTree(fn, cfg);
// Inspect the actual tree
opt::DominatorTree& tree = dom_tree.GetDomTree();
EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_exit_block());
EXPECT_TRUE(dom_tree.Dominates(cfg.pseudo_exit_block()->id(), 12));
// (strict) dominance checks
for (uint32_t id : {10, 11, 12}) check_dominance(dom_tree, fn, id, id);
check_dominance(dom_tree, fn, 12, 11);
check_dominance(dom_tree, fn, 12, 10);
// 13 should be completely out of tree as it's unreachable from exit nodes
check_no_dominance(dom_tree, fn, 12, 13);
check_no_dominance(dom_tree, fn, 11, 13);
check_no_dominance(dom_tree, fn, 10, 13);
EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr);
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)),
cfg.pseudo_exit_block());
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)),
spvtest::GetBasicBlock(fn, 11));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)),
spvtest::GetBasicBlock(fn, 12));
}
}
TEST_F(PassClassTest, DominatorUnreachableFromEntry) {
const std::string text = R"(
OpCapability Addresses
OpCapability Addresses
OpCapability Kernel
OpMemoryModel Physical64 OpenCL
OpEntryPoint Kernel %1 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpTypeBool
%5 = OpTypeInt 32 0
%6 = OpConstantFalse %4
%7 = OpConstantTrue %4
%1 = OpFunction %2 None %3
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpReturn
%10 = OpLabel
OpBranch %9
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const ir::Function* fn = spvtest::GetFunction(module, 1);
const ir::BasicBlock* entry = spvtest::GetBasicBlock(fn, 8);
EXPECT_EQ(entry, fn->entry().get())
<< "The entry node is not the expected one";
// Check dominator tree
{
opt::DominatorAnalysis dom_tree;
ir::CFG cfg(module);
dom_tree.InitializeTree(fn, cfg);
// Inspect the actual tree
opt::DominatorTree& tree = dom_tree.GetDomTree();
EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block());
EXPECT_TRUE(
dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id()));
// (strict) dominance checks
for (uint32_t id : {8, 9}) check_dominance(dom_tree, fn, id, id);
check_dominance(dom_tree, fn, 8, 9);
check_no_dominance(dom_tree, fn, 10, 8);
check_no_dominance(dom_tree, fn, 10, 9);
EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr);
EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block());
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 9)),
spvtest::GetBasicBlock(fn, 8));
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)),
nullptr);
}
// Check post dominator tree
{
opt::PostDominatorAnalysis dom_tree;
ir::CFG cfg(module);
dom_tree.InitializeTree(fn, cfg);
// Inspect the actual tree
opt::DominatorTree& tree = dom_tree.GetDomTree();
EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_exit_block());
EXPECT_TRUE(dom_tree.Dominates(cfg.pseudo_exit_block()->id(), 9));
// (strict) dominance checks
for (uint32_t id : {8, 9, 10}) check_dominance(dom_tree, fn, id, id);
check_dominance(dom_tree, fn, 9, 8);
check_dominance(dom_tree, fn, 9, 10);
EXPECT_EQ(dom_tree.ImmediateDominator(entry),
spvtest::GetBasicBlock(fn, 9));
EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr);
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 9)),
cfg.pseudo_exit_block());
EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)),
spvtest::GetBasicBlock(fn, 9));
}
}
} // namespace

View File

@ -0,0 +1,152 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gmock/gmock.h>
#include <memory>
#include <string>
#include <vector>
#include "../assembly_builder.h"
#include "../function_utils.h"
#include "../pass_fixture.h"
#include "../pass_utils.h"
#include "opt/dominator_analysis.h"
#include "opt/pass.h"
namespace {
using namespace spvtools;
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
Generated from the following GLSL
#version 330 core
layout(location = 0) out vec4 v;
void main(){
if (true) {
if (true) {
v = vec4(1,1,1,1);
} else {
v = vec4(2,2,2,2);
}
} else {
if (true) {
v = vec4(3,3,3,3);
} else {
v = vec4(4,4,4,4);
}
}
}
*/
TEST_F(PassClassTest, UnreachableNestedIfs) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %15
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 330
OpName %4 "main"
OpName %15 "v"
OpDecorate %15 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeBool
%7 = OpConstantTrue %6
%12 = OpTypeFloat 32
%13 = OpTypeVector %12 4
%14 = OpTypePointer Output %13
%15 = OpVariable %14 Output
%16 = OpConstant %12 1
%17 = OpConstantComposite %13 %16 %16 %16 %16
%19 = OpConstant %12 2
%20 = OpConstantComposite %13 %19 %19 %19 %19
%24 = OpConstant %12 3
%25 = OpConstantComposite %13 %24 %24 %24 %24
%27 = OpConstant %12 4
%28 = OpConstantComposite %13 %27 %27 %27 %27
%4 = OpFunction %2 None %3
%5 = OpLabel
OpSelectionMerge %9 None
OpBranchConditional %7 %8 %21
%8 = OpLabel
OpSelectionMerge %11 None
OpBranchConditional %7 %10 %18
%10 = OpLabel
OpStore %15 %17
OpBranch %11
%18 = OpLabel
OpStore %15 %20
OpBranch %11
%11 = OpLabel
OpBranch %9
%21 = OpLabel
OpSelectionMerge %23 None
OpBranchConditional %7 %22 %26
%22 = OpLabel
OpStore %15 %25
OpBranch %23
%26 = OpLabel
OpStore %15 %28
OpBranch %23
%23 = OpLabel
OpBranch %9
%9 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const ir::Function* f = spvtest::GetFunction(module, 4);
ir::CFG cfg(module);
opt::DominatorAnalysis* analysis = context->GetDominatorAnalysis(f, cfg);
EXPECT_TRUE(analysis->Dominates(5, 8));
EXPECT_TRUE(analysis->Dominates(5, 9));
EXPECT_TRUE(analysis->Dominates(5, 21));
EXPECT_TRUE(analysis->Dominates(5, 18));
EXPECT_TRUE(analysis->Dominates(5, 10));
EXPECT_TRUE(analysis->Dominates(5, 11));
EXPECT_TRUE(analysis->Dominates(5, 23));
EXPECT_TRUE(analysis->Dominates(5, 22));
EXPECT_TRUE(analysis->Dominates(5, 26));
EXPECT_TRUE(analysis->Dominates(8, 18));
EXPECT_TRUE(analysis->Dominates(8, 10));
EXPECT_TRUE(analysis->Dominates(8, 11));
EXPECT_TRUE(analysis->Dominates(21, 23));
EXPECT_TRUE(analysis->Dominates(21, 22));
EXPECT_TRUE(analysis->Dominates(21, 26));
EXPECT_TRUE(analysis->StrictlyDominates(5, 8));
EXPECT_TRUE(analysis->StrictlyDominates(5, 9));
EXPECT_TRUE(analysis->StrictlyDominates(5, 21));
EXPECT_TRUE(analysis->StrictlyDominates(8, 18));
EXPECT_TRUE(analysis->StrictlyDominates(8, 10));
EXPECT_TRUE(analysis->StrictlyDominates(8, 11));
EXPECT_TRUE(analysis->StrictlyDominates(21, 23));
EXPECT_TRUE(analysis->StrictlyDominates(21, 22));
EXPECT_TRUE(analysis->StrictlyDominates(21, 26));
}
} // namespace

View File

@ -0,0 +1,157 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gmock/gmock.h>
#include <memory>
#include <string>
#include <vector>
#include "../assembly_builder.h"
#include "../function_utils.h"
#include "../pass_fixture.h"
#include "../pass_utils.h"
#include "opt/dominator_analysis.h"
#include "opt/pass.h"
namespace {
using namespace spvtools;
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
Generated from the following GLSL
#version 330 core
layout(location = 0) out vec4 v;
void main(){
if (true) {
if (true) {
v = vec4(1,1,1,1);
} else {
v = vec4(2,2,2,2);
}
} else {
if (true) {
v = vec4(3,3,3,3);
} else {
v = vec4(4,4,4,4);
}
}
}
*/
TEST_F(PassClassTest, UnreachableNestedIfs) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %15
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 330
OpName %4 "main"
OpName %15 "v"
OpDecorate %15 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeBool
%7 = OpConstantTrue %6
%12 = OpTypeFloat 32
%13 = OpTypeVector %12 4
%14 = OpTypePointer Output %13
%15 = OpVariable %14 Output
%16 = OpConstant %12 1
%17 = OpConstantComposite %13 %16 %16 %16 %16
%19 = OpConstant %12 2
%20 = OpConstantComposite %13 %19 %19 %19 %19
%24 = OpConstant %12 3
%25 = OpConstantComposite %13 %24 %24 %24 %24
%27 = OpConstant %12 4
%28 = OpConstantComposite %13 %27 %27 %27 %27
%4 = OpFunction %2 None %3
%5 = OpLabel
OpSelectionMerge %9 None
OpBranchConditional %7 %8 %21
%8 = OpLabel
OpSelectionMerge %11 None
OpBranchConditional %7 %10 %18
%10 = OpLabel
OpStore %15 %17
OpBranch %11
%18 = OpLabel
OpStore %15 %20
OpBranch %11
%11 = OpLabel
OpBranch %9
%21 = OpLabel
OpSelectionMerge %23 None
OpBranchConditional %7 %22 %26
%22 = OpLabel
OpStore %15 %25
OpBranch %23
%26 = OpLabel
OpStore %15 %28
OpBranch %23
%23 = OpLabel
OpBranch %9
%9 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const ir::Function* f = spvtest::GetFunction(module, 4);
ir::CFG cfg(module);
opt::PostDominatorAnalysis* analysis =
context->GetPostDominatorAnalysis(f, cfg);
EXPECT_TRUE(analysis->Dominates(5, 5));
EXPECT_TRUE(analysis->Dominates(8, 8));
EXPECT_TRUE(analysis->Dominates(9, 9));
EXPECT_TRUE(analysis->Dominates(10, 10));
EXPECT_TRUE(analysis->Dominates(11, 11));
EXPECT_TRUE(analysis->Dominates(18, 18));
EXPECT_TRUE(analysis->Dominates(21, 21));
EXPECT_TRUE(analysis->Dominates(22, 22));
EXPECT_TRUE(analysis->Dominates(23, 23));
EXPECT_TRUE(analysis->Dominates(26, 26));
EXPECT_TRUE(analysis->Dominates(9, 5));
EXPECT_TRUE(analysis->Dominates(9, 11));
EXPECT_TRUE(analysis->Dominates(9, 23));
EXPECT_TRUE(analysis->Dominates(11, 10));
EXPECT_TRUE(analysis->Dominates(11, 18));
EXPECT_TRUE(analysis->Dominates(11, 8));
EXPECT_TRUE(analysis->Dominates(23, 22));
EXPECT_TRUE(analysis->Dominates(23, 26));
EXPECT_TRUE(analysis->Dominates(23, 21));
EXPECT_TRUE(analysis->StrictlyDominates(9, 5));
EXPECT_TRUE(analysis->StrictlyDominates(9, 11));
EXPECT_TRUE(analysis->StrictlyDominates(9, 23));
EXPECT_TRUE(analysis->StrictlyDominates(11, 10));
EXPECT_TRUE(analysis->StrictlyDominates(11, 18));
EXPECT_TRUE(analysis->StrictlyDominates(11, 8));
EXPECT_TRUE(analysis->StrictlyDominates(23, 22));
EXPECT_TRUE(analysis->StrictlyDominates(23, 26));
EXPECT_TRUE(analysis->StrictlyDominates(23, 21));
}
} // namespace

View File

@ -0,0 +1,433 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gmock/gmock.h>
#include <memory>
#include <string>
#include <vector>
#include "../assembly_builder.h"
#include "../function_utils.h"
#include "../pass_fixture.h"
#include "../pass_utils.h"
#include "opt/dominator_analysis.h"
#include "opt/pass.h"
namespace {
using namespace spvtools;
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
Generated from the following GLSL
#version 440 core
layout(location = 0) out vec4 v;
layout(location = 1) in vec4 in_val;
void main() {
for (int i = 0; i < in_val.x; ++i) {
for (int j = 0; j < in_val.y; j++) {
}
}
for (int i = 0; i < in_val.x; ++i) {
for (int j = 0; j < in_val.y; j++) {
}
if (in_val.z == in_val.w) {
break;
}
}
int i = 0;
while (i < in_val.x) {
++i;
for (int j = 0; j < 1; j++) {
for (int k = 0; k < 1; k++) {
}
}
}
i = 0;
while (i < in_val.x) {
++i;
if (in_val.z == in_val.w) {
continue;
}
for (int j = 0; j < 1; j++) {
for (int k = 0; k < 1; k++) {
}
if (in_val.z == in_val.w) {
break;
}
}
}
v = vec4(1,1,1,1);
}
*/
TEST_F(PassClassTest, BasicVisitFromEntryPoint) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %20 %163
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 440
OpName %4 "main"
OpName %8 "i"
OpName %20 "in_val"
OpName %28 "j"
OpName %45 "i"
OpName %56 "j"
OpName %81 "i"
OpName %94 "j"
OpName %102 "k"
OpName %134 "j"
OpName %142 "k"
OpName %163 "v"
OpDecorate %20 Location 1
OpDecorate %163 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpTypeFloat 32
%18 = OpTypeVector %16 4
%19 = OpTypePointer Input %18
%20 = OpVariable %19 Input
%21 = OpTypeInt 32 0
%22 = OpConstant %21 0
%23 = OpTypePointer Input %16
%26 = OpTypeBool
%36 = OpConstant %21 1
%41 = OpConstant %6 1
%69 = OpConstant %21 2
%72 = OpConstant %21 3
%162 = OpTypePointer Output %18
%163 = OpVariable %162 Output
%164 = OpConstant %16 1
%165 = OpConstantComposite %18 %164 %164 %164 %164
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%28 = OpVariable %7 Function
%45 = OpVariable %7 Function
%56 = OpVariable %7 Function
%81 = OpVariable %7 Function
%94 = OpVariable %7 Function
%102 = OpVariable %7 Function
%134 = OpVariable %7 Function
%142 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%15 = OpLoad %6 %8
%17 = OpConvertSToF %16 %15
%24 = OpAccessChain %23 %20 %22
%25 = OpLoad %16 %24
%27 = OpFOrdLessThan %26 %17 %25
OpBranchConditional %27 %11 %12
%11 = OpLabel
OpStore %28 %9
OpBranch %29
%29 = OpLabel
OpLoopMerge %31 %32 None
OpBranch %33
%33 = OpLabel
%34 = OpLoad %6 %28
%35 = OpConvertSToF %16 %34
%37 = OpAccessChain %23 %20 %36
%38 = OpLoad %16 %37
%39 = OpFOrdLessThan %26 %35 %38
OpBranchConditional %39 %30 %31
%30 = OpLabel
OpBranch %32
%32 = OpLabel
%40 = OpLoad %6 %28
%42 = OpIAdd %6 %40 %41
OpStore %28 %42
OpBranch %29
%31 = OpLabel
OpBranch %13
%13 = OpLabel
%43 = OpLoad %6 %8
%44 = OpIAdd %6 %43 %41
OpStore %8 %44
OpBranch %10
%12 = OpLabel
OpStore %45 %9
OpBranch %46
%46 = OpLabel
OpLoopMerge %48 %49 None
OpBranch %50
%50 = OpLabel
%51 = OpLoad %6 %45
%52 = OpConvertSToF %16 %51
%53 = OpAccessChain %23 %20 %22
%54 = OpLoad %16 %53
%55 = OpFOrdLessThan %26 %52 %54
OpBranchConditional %55 %47 %48
%47 = OpLabel
OpStore %56 %9
OpBranch %57
%57 = OpLabel
OpLoopMerge %59 %60 None
OpBranch %61
%61 = OpLabel
%62 = OpLoad %6 %56
%63 = OpConvertSToF %16 %62
%64 = OpAccessChain %23 %20 %36
%65 = OpLoad %16 %64
%66 = OpFOrdLessThan %26 %63 %65
OpBranchConditional %66 %58 %59
%58 = OpLabel
OpBranch %60
%60 = OpLabel
%67 = OpLoad %6 %56
%68 = OpIAdd %6 %67 %41
OpStore %56 %68
OpBranch %57
%59 = OpLabel
%70 = OpAccessChain %23 %20 %69
%71 = OpLoad %16 %70
%73 = OpAccessChain %23 %20 %72
%74 = OpLoad %16 %73
%75 = OpFOrdEqual %26 %71 %74
OpSelectionMerge %77 None
OpBranchConditional %75 %76 %77
%76 = OpLabel
OpBranch %48
%77 = OpLabel
OpBranch %49
%49 = OpLabel
%79 = OpLoad %6 %45
%80 = OpIAdd %6 %79 %41
OpStore %45 %80
OpBranch %46
%48 = OpLabel
OpStore %81 %9
OpBranch %82
%82 = OpLabel
OpLoopMerge %84 %85 None
OpBranch %86
%86 = OpLabel
%87 = OpLoad %6 %81
%88 = OpConvertSToF %16 %87
%89 = OpAccessChain %23 %20 %22
%90 = OpLoad %16 %89
%91 = OpFOrdLessThan %26 %88 %90
OpBranchConditional %91 %83 %84
%83 = OpLabel
%92 = OpLoad %6 %81
%93 = OpIAdd %6 %92 %41
OpStore %81 %93
OpStore %94 %9
OpBranch %95
%95 = OpLabel
OpLoopMerge %97 %98 None
OpBranch %99
%99 = OpLabel
%100 = OpLoad %6 %94
%101 = OpSLessThan %26 %100 %41
OpBranchConditional %101 %96 %97
%96 = OpLabel
OpStore %102 %9
OpBranch %103
%103 = OpLabel
OpLoopMerge %105 %106 None
OpBranch %107
%107 = OpLabel
%108 = OpLoad %6 %102
%109 = OpSLessThan %26 %108 %41
OpBranchConditional %109 %104 %105
%104 = OpLabel
OpBranch %106
%106 = OpLabel
%110 = OpLoad %6 %102
%111 = OpIAdd %6 %110 %41
OpStore %102 %111
OpBranch %103
%105 = OpLabel
OpBranch %98
%98 = OpLabel
%112 = OpLoad %6 %94
%113 = OpIAdd %6 %112 %41
OpStore %94 %113
OpBranch %95
%97 = OpLabel
OpBranch %85
%85 = OpLabel
OpBranch %82
%84 = OpLabel
OpStore %81 %9
OpBranch %114
%114 = OpLabel
OpLoopMerge %116 %117 None
OpBranch %118
%118 = OpLabel
%119 = OpLoad %6 %81
%120 = OpConvertSToF %16 %119
%121 = OpAccessChain %23 %20 %22
%122 = OpLoad %16 %121
%123 = OpFOrdLessThan %26 %120 %122
OpBranchConditional %123 %115 %116
%115 = OpLabel
%124 = OpLoad %6 %81
%125 = OpIAdd %6 %124 %41
OpStore %81 %125
%126 = OpAccessChain %23 %20 %69
%127 = OpLoad %16 %126
%128 = OpAccessChain %23 %20 %72
%129 = OpLoad %16 %128
%130 = OpFOrdEqual %26 %127 %129
OpSelectionMerge %132 None
OpBranchConditional %130 %131 %132
%131 = OpLabel
OpBranch %117
%132 = OpLabel
OpStore %134 %9
OpBranch %135
%135 = OpLabel
OpLoopMerge %137 %138 None
OpBranch %139
%139 = OpLabel
%140 = OpLoad %6 %134
%141 = OpSLessThan %26 %140 %41
OpBranchConditional %141 %136 %137
%136 = OpLabel
OpStore %142 %9
OpBranch %143
%143 = OpLabel
OpLoopMerge %145 %146 None
OpBranch %147
%147 = OpLabel
%148 = OpLoad %6 %142
%149 = OpSLessThan %26 %148 %41
OpBranchConditional %149 %144 %145
%144 = OpLabel
OpBranch %146
%146 = OpLabel
%150 = OpLoad %6 %142
%151 = OpIAdd %6 %150 %41
OpStore %142 %151
OpBranch %143
%145 = OpLabel
%152 = OpAccessChain %23 %20 %69
%153 = OpLoad %16 %152
%154 = OpAccessChain %23 %20 %72
%155 = OpLoad %16 %154
%156 = OpFOrdEqual %26 %153 %155
OpSelectionMerge %158 None
OpBranchConditional %156 %157 %158
%157 = OpLabel
OpBranch %137
%158 = OpLabel
OpBranch %138
%138 = OpLabel
%160 = OpLoad %6 %134
%161 = OpIAdd %6 %160 %41
OpStore %134 %161
OpBranch %135
%137 = OpLabel
OpBranch %117
%117 = OpLabel
OpBranch %114
%116 = OpLabel
OpStore %163 %165
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const ir::Function* f = spvtest::GetFunction(module, 4);
ir::CFG cfg(module);
opt::DominatorAnalysis* analysis = context->GetDominatorAnalysis(f, cfg);
EXPECT_TRUE(analysis->Dominates(5, 10));
EXPECT_TRUE(analysis->Dominates(5, 46));
EXPECT_TRUE(analysis->Dominates(5, 82));
EXPECT_TRUE(analysis->Dominates(5, 114));
EXPECT_TRUE(analysis->Dominates(5, 116));
EXPECT_TRUE(analysis->Dominates(10, 14));
EXPECT_TRUE(analysis->Dominates(10, 11));
EXPECT_TRUE(analysis->Dominates(10, 29));
EXPECT_TRUE(analysis->Dominates(10, 33));
EXPECT_TRUE(analysis->Dominates(10, 30));
EXPECT_TRUE(analysis->Dominates(10, 32));
EXPECT_TRUE(analysis->Dominates(10, 31));
EXPECT_TRUE(analysis->Dominates(10, 13));
EXPECT_TRUE(analysis->Dominates(10, 12));
EXPECT_TRUE(analysis->Dominates(12, 46));
EXPECT_TRUE(analysis->Dominates(46, 50));
EXPECT_TRUE(analysis->Dominates(46, 47));
EXPECT_TRUE(analysis->Dominates(46, 57));
EXPECT_TRUE(analysis->Dominates(46, 61));
EXPECT_TRUE(analysis->Dominates(46, 58));
EXPECT_TRUE(analysis->Dominates(46, 60));
EXPECT_TRUE(analysis->Dominates(46, 59));
EXPECT_TRUE(analysis->Dominates(46, 77));
EXPECT_TRUE(analysis->Dominates(46, 49));
EXPECT_TRUE(analysis->Dominates(46, 76));
EXPECT_TRUE(analysis->Dominates(46, 48));
EXPECT_TRUE(analysis->Dominates(48, 82));
EXPECT_TRUE(analysis->Dominates(82, 86));
EXPECT_TRUE(analysis->Dominates(82, 83));
EXPECT_TRUE(analysis->Dominates(82, 95));
EXPECT_TRUE(analysis->Dominates(82, 99));
EXPECT_TRUE(analysis->Dominates(82, 96));
EXPECT_TRUE(analysis->Dominates(82, 103));
EXPECT_TRUE(analysis->Dominates(82, 107));
EXPECT_TRUE(analysis->Dominates(82, 104));
EXPECT_TRUE(analysis->Dominates(82, 106));
EXPECT_TRUE(analysis->Dominates(82, 105));
EXPECT_TRUE(analysis->Dominates(82, 98));
EXPECT_TRUE(analysis->Dominates(82, 97));
EXPECT_TRUE(analysis->Dominates(82, 85));
EXPECT_TRUE(analysis->Dominates(82, 84));
EXPECT_TRUE(analysis->Dominates(84, 114));
EXPECT_TRUE(analysis->Dominates(114, 118));
EXPECT_TRUE(analysis->Dominates(114, 116));
EXPECT_TRUE(analysis->Dominates(114, 115));
EXPECT_TRUE(analysis->Dominates(114, 132));
EXPECT_TRUE(analysis->Dominates(114, 135));
EXPECT_TRUE(analysis->Dominates(114, 139));
EXPECT_TRUE(analysis->Dominates(114, 136));
EXPECT_TRUE(analysis->Dominates(114, 143));
EXPECT_TRUE(analysis->Dominates(114, 147));
EXPECT_TRUE(analysis->Dominates(114, 144));
EXPECT_TRUE(analysis->Dominates(114, 146));
EXPECT_TRUE(analysis->Dominates(114, 145));
EXPECT_TRUE(analysis->Dominates(114, 158));
EXPECT_TRUE(analysis->Dominates(114, 138));
EXPECT_TRUE(analysis->Dominates(114, 137));
EXPECT_TRUE(analysis->Dominates(114, 131));
EXPECT_TRUE(analysis->Dominates(114, 117));
}
} // namespace

View File

@ -0,0 +1,846 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include "../assembly_builder.h"
#include "../function_utils.h"
#include "../pass_fixture.h"
#include "../pass_utils.h"
#include "opt/dominator_analysis.h"
#include "opt/pass.h"
namespace {
using namespace spvtools;
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
Generated from the following GLSL
#version 440 core
layout(location = 0) out vec4 v;
layout(location = 1) in vec4 in_val;
void main() {
for (int i = 0; i < in_val.x; ++i) {
for (int j = 0; j < in_val.y; j++) {
}
}
for (int i = 0; i < in_val.x; ++i) {
for (int j = 0; j < in_val.y; j++) {
}
break;
}
int i = 0;
while (i < in_val.x) {
++i;
for (int j = 0; j < 1; j++) {
for (int k = 0; k < 1; k++) {
}
break;
}
}
i = 0;
while (i < in_val.x) {
++i;
continue;
for (int j = 0; j < 1; j++) {
for (int k = 0; k < 1; k++) {
}
break;
}
}
v = vec4(1,1,1,1);
}
*/
TEST_F(PassClassTest, BasicVisitFromEntryPoint) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %20 %141
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 440
OpName %4 "main"
OpName %8 "i"
OpName %20 "in_val"
OpName %28 "j"
OpName %45 "i"
OpName %56 "j"
OpName %72 "i"
OpName %85 "j"
OpName %93 "k"
OpName %119 "j"
OpName %127 "k"
OpName %141 "v"
OpDecorate %20 Location 1
OpDecorate %141 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpTypeFloat 32
%18 = OpTypeVector %16 4
%19 = OpTypePointer Input %18
%20 = OpVariable %19 Input
%21 = OpTypeInt 32 0
%22 = OpConstant %21 0
%23 = OpTypePointer Input %16
%26 = OpTypeBool
%36 = OpConstant %21 1
%41 = OpConstant %6 1
%140 = OpTypePointer Output %18
%141 = OpVariable %140 Output
%142 = OpConstant %16 1
%143 = OpConstantComposite %18 %142 %142 %142 %142
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%28 = OpVariable %7 Function
%45 = OpVariable %7 Function
%56 = OpVariable %7 Function
%72 = OpVariable %7 Function
%85 = OpVariable %7 Function
%93 = OpVariable %7 Function
%119 = OpVariable %7 Function
%127 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%15 = OpLoad %6 %8
%17 = OpConvertSToF %16 %15
%24 = OpAccessChain %23 %20 %22
%25 = OpLoad %16 %24
%27 = OpFOrdLessThan %26 %17 %25
OpBranchConditional %27 %11 %12
%11 = OpLabel
OpStore %28 %9
OpBranch %29
%29 = OpLabel
OpLoopMerge %31 %32 None
OpBranch %33
%33 = OpLabel
%34 = OpLoad %6 %28
%35 = OpConvertSToF %16 %34
%37 = OpAccessChain %23 %20 %36
%38 = OpLoad %16 %37
%39 = OpFOrdLessThan %26 %35 %38
OpBranchConditional %39 %30 %31
%30 = OpLabel
OpBranch %32
%32 = OpLabel
%40 = OpLoad %6 %28
%42 = OpIAdd %6 %40 %41
OpStore %28 %42
OpBranch %29
%31 = OpLabel
OpBranch %13
%13 = OpLabel
%43 = OpLoad %6 %8
%44 = OpIAdd %6 %43 %41
OpStore %8 %44
OpBranch %10
%12 = OpLabel
OpStore %45 %9
OpBranch %46
%46 = OpLabel
OpLoopMerge %48 %49 None
OpBranch %50
%50 = OpLabel
%51 = OpLoad %6 %45
%52 = OpConvertSToF %16 %51
%53 = OpAccessChain %23 %20 %22
%54 = OpLoad %16 %53
%55 = OpFOrdLessThan %26 %52 %54
OpBranchConditional %55 %47 %48
%47 = OpLabel
OpStore %56 %9
OpBranch %57
%57 = OpLabel
OpLoopMerge %59 %60 None
OpBranch %61
%61 = OpLabel
%62 = OpLoad %6 %56
%63 = OpConvertSToF %16 %62
%64 = OpAccessChain %23 %20 %36
%65 = OpLoad %16 %64
%66 = OpFOrdLessThan %26 %63 %65
OpBranchConditional %66 %58 %59
%58 = OpLabel
OpBranch %60
%60 = OpLabel
%67 = OpLoad %6 %56
%68 = OpIAdd %6 %67 %41
OpStore %56 %68
OpBranch %57
%59 = OpLabel
OpBranch %48
%49 = OpLabel
%70 = OpLoad %6 %45
%71 = OpIAdd %6 %70 %41
OpStore %45 %71
OpBranch %46
%48 = OpLabel
OpStore %72 %9
OpBranch %73
%73 = OpLabel
OpLoopMerge %75 %76 None
OpBranch %77
%77 = OpLabel
%78 = OpLoad %6 %72
%79 = OpConvertSToF %16 %78
%80 = OpAccessChain %23 %20 %22
%81 = OpLoad %16 %80
%82 = OpFOrdLessThan %26 %79 %81
OpBranchConditional %82 %74 %75
%74 = OpLabel
%83 = OpLoad %6 %72
%84 = OpIAdd %6 %83 %41
OpStore %72 %84
OpStore %85 %9
OpBranch %86
%86 = OpLabel
OpLoopMerge %88 %89 None
OpBranch %90
%90 = OpLabel
%91 = OpLoad %6 %85
%92 = OpSLessThan %26 %91 %41
OpBranchConditional %92 %87 %88
%87 = OpLabel
OpStore %93 %9
OpBranch %94
%94 = OpLabel
OpLoopMerge %96 %97 None
OpBranch %98
%98 = OpLabel
%99 = OpLoad %6 %93
%100 = OpSLessThan %26 %99 %41
OpBranchConditional %100 %95 %96
%95 = OpLabel
OpBranch %97
%97 = OpLabel
%101 = OpLoad %6 %93
%102 = OpIAdd %6 %101 %41
OpStore %93 %102
OpBranch %94
%96 = OpLabel
OpBranch %88
%89 = OpLabel
%104 = OpLoad %6 %85
%105 = OpIAdd %6 %104 %41
OpStore %85 %105
OpBranch %86
%88 = OpLabel
OpBranch %76
%76 = OpLabel
OpBranch %73
%75 = OpLabel
OpStore %72 %9
OpBranch %106
%106 = OpLabel
OpLoopMerge %108 %109 None
OpBranch %110
%110 = OpLabel
%111 = OpLoad %6 %72
%112 = OpConvertSToF %16 %111
%113 = OpAccessChain %23 %20 %22
%114 = OpLoad %16 %113
%115 = OpFOrdLessThan %26 %112 %114
OpBranchConditional %115 %107 %108
%107 = OpLabel
%116 = OpLoad %6 %72
%117 = OpIAdd %6 %116 %41
OpStore %72 %117
OpBranch %109
%109 = OpLabel
OpBranch %106
%108 = OpLabel
OpStore %141 %143
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const ir::Function* f = spvtest::GetFunction(module, 4);
ir::CFG cfg(module);
opt::DominatorAnalysis* analysis = context->GetDominatorAnalysis(f, cfg);
EXPECT_TRUE(analysis->Dominates(5, 10));
EXPECT_TRUE(analysis->Dominates(5, 14));
EXPECT_TRUE(analysis->Dominates(5, 11));
EXPECT_TRUE(analysis->Dominates(5, 29));
EXPECT_TRUE(analysis->Dominates(5, 33));
EXPECT_TRUE(analysis->Dominates(5, 30));
EXPECT_TRUE(analysis->Dominates(5, 32));
EXPECT_TRUE(analysis->Dominates(5, 31));
EXPECT_TRUE(analysis->Dominates(5, 13));
EXPECT_TRUE(analysis->Dominates(5, 12));
EXPECT_TRUE(analysis->Dominates(5, 46));
EXPECT_TRUE(analysis->Dominates(5, 50));
EXPECT_TRUE(analysis->Dominates(5, 47));
EXPECT_TRUE(analysis->Dominates(5, 57));
EXPECT_TRUE(analysis->Dominates(5, 61));
EXPECT_TRUE(analysis->Dominates(5, 59));
EXPECT_TRUE(analysis->Dominates(5, 58));
EXPECT_TRUE(analysis->Dominates(5, 60));
EXPECT_TRUE(analysis->Dominates(5, 48));
EXPECT_TRUE(analysis->Dominates(5, 73));
EXPECT_TRUE(analysis->Dominates(5, 77));
EXPECT_TRUE(analysis->Dominates(5, 75));
EXPECT_TRUE(analysis->Dominates(5, 106));
EXPECT_TRUE(analysis->Dominates(5, 110));
EXPECT_TRUE(analysis->Dominates(5, 107));
EXPECT_TRUE(analysis->Dominates(5, 108));
EXPECT_TRUE(analysis->Dominates(5, 109));
EXPECT_TRUE(analysis->Dominates(5, 74));
EXPECT_TRUE(analysis->Dominates(5, 86));
EXPECT_TRUE(analysis->Dominates(5, 90));
EXPECT_TRUE(analysis->Dominates(5, 87));
EXPECT_TRUE(analysis->Dominates(5, 94));
EXPECT_TRUE(analysis->Dominates(5, 98));
EXPECT_TRUE(analysis->Dominates(5, 95));
EXPECT_TRUE(analysis->Dominates(5, 97));
EXPECT_TRUE(analysis->Dominates(5, 96));
EXPECT_TRUE(analysis->Dominates(5, 88));
EXPECT_TRUE(analysis->Dominates(5, 76));
EXPECT_TRUE(analysis->Dominates(10, 14));
EXPECT_TRUE(analysis->Dominates(10, 11));
EXPECT_TRUE(analysis->Dominates(10, 29));
EXPECT_TRUE(analysis->Dominates(10, 33));
EXPECT_TRUE(analysis->Dominates(10, 30));
EXPECT_TRUE(analysis->Dominates(10, 32));
EXPECT_TRUE(analysis->Dominates(10, 31));
EXPECT_TRUE(analysis->Dominates(10, 13));
EXPECT_TRUE(analysis->Dominates(10, 12));
EXPECT_TRUE(analysis->Dominates(10, 46));
EXPECT_TRUE(analysis->Dominates(10, 50));
EXPECT_TRUE(analysis->Dominates(10, 47));
EXPECT_TRUE(analysis->Dominates(10, 57));
EXPECT_TRUE(analysis->Dominates(10, 61));
EXPECT_TRUE(analysis->Dominates(10, 59));
EXPECT_TRUE(analysis->Dominates(10, 58));
EXPECT_TRUE(analysis->Dominates(10, 60));
EXPECT_TRUE(analysis->Dominates(10, 48));
EXPECT_TRUE(analysis->Dominates(10, 73));
EXPECT_TRUE(analysis->Dominates(10, 77));
EXPECT_TRUE(analysis->Dominates(10, 75));
EXPECT_TRUE(analysis->Dominates(10, 106));
EXPECT_TRUE(analysis->Dominates(10, 110));
EXPECT_TRUE(analysis->Dominates(10, 107));
EXPECT_TRUE(analysis->Dominates(10, 108));
EXPECT_TRUE(analysis->Dominates(10, 109));
EXPECT_TRUE(analysis->Dominates(10, 74));
EXPECT_TRUE(analysis->Dominates(10, 86));
EXPECT_TRUE(analysis->Dominates(10, 90));
EXPECT_TRUE(analysis->Dominates(10, 87));
EXPECT_TRUE(analysis->Dominates(10, 94));
EXPECT_TRUE(analysis->Dominates(10, 98));
EXPECT_TRUE(analysis->Dominates(10, 95));
EXPECT_TRUE(analysis->Dominates(10, 97));
EXPECT_TRUE(analysis->Dominates(10, 96));
EXPECT_TRUE(analysis->Dominates(10, 88));
EXPECT_TRUE(analysis->Dominates(10, 76));
EXPECT_TRUE(analysis->Dominates(14, 11));
EXPECT_TRUE(analysis->Dominates(14, 29));
EXPECT_TRUE(analysis->Dominates(14, 33));
EXPECT_TRUE(analysis->Dominates(14, 30));
EXPECT_TRUE(analysis->Dominates(14, 32));
EXPECT_TRUE(analysis->Dominates(14, 31));
EXPECT_TRUE(analysis->Dominates(11, 29));
EXPECT_TRUE(analysis->Dominates(11, 33));
EXPECT_TRUE(analysis->Dominates(11, 30));
EXPECT_TRUE(analysis->Dominates(11, 32));
EXPECT_TRUE(analysis->Dominates(11, 31));
EXPECT_TRUE(analysis->Dominates(29, 33));
EXPECT_TRUE(analysis->Dominates(29, 30));
EXPECT_TRUE(analysis->Dominates(29, 32));
EXPECT_TRUE(analysis->Dominates(29, 31));
EXPECT_TRUE(analysis->Dominates(33, 30));
EXPECT_TRUE(analysis->Dominates(12, 46));
EXPECT_TRUE(analysis->Dominates(12, 50));
EXPECT_TRUE(analysis->Dominates(12, 47));
EXPECT_TRUE(analysis->Dominates(12, 57));
EXPECT_TRUE(analysis->Dominates(12, 61));
EXPECT_TRUE(analysis->Dominates(12, 59));
EXPECT_TRUE(analysis->Dominates(12, 58));
EXPECT_TRUE(analysis->Dominates(12, 60));
EXPECT_TRUE(analysis->Dominates(12, 48));
EXPECT_TRUE(analysis->Dominates(12, 73));
EXPECT_TRUE(analysis->Dominates(12, 77));
EXPECT_TRUE(analysis->Dominates(12, 75));
EXPECT_TRUE(analysis->Dominates(12, 106));
EXPECT_TRUE(analysis->Dominates(12, 110));
EXPECT_TRUE(analysis->Dominates(12, 107));
EXPECT_TRUE(analysis->Dominates(12, 108));
EXPECT_TRUE(analysis->Dominates(12, 109));
EXPECT_TRUE(analysis->Dominates(12, 74));
EXPECT_TRUE(analysis->Dominates(12, 86));
EXPECT_TRUE(analysis->Dominates(12, 90));
EXPECT_TRUE(analysis->Dominates(12, 87));
EXPECT_TRUE(analysis->Dominates(12, 94));
EXPECT_TRUE(analysis->Dominates(12, 98));
EXPECT_TRUE(analysis->Dominates(12, 95));
EXPECT_TRUE(analysis->Dominates(12, 97));
EXPECT_TRUE(analysis->Dominates(12, 96));
EXPECT_TRUE(analysis->Dominates(12, 88));
EXPECT_TRUE(analysis->Dominates(12, 76));
EXPECT_TRUE(analysis->Dominates(46, 50));
EXPECT_TRUE(analysis->Dominates(46, 47));
EXPECT_TRUE(analysis->Dominates(46, 57));
EXPECT_TRUE(analysis->Dominates(46, 61));
EXPECT_TRUE(analysis->Dominates(46, 59));
EXPECT_TRUE(analysis->Dominates(46, 58));
EXPECT_TRUE(analysis->Dominates(46, 60));
EXPECT_TRUE(analysis->Dominates(46, 48));
EXPECT_TRUE(analysis->Dominates(46, 73));
EXPECT_TRUE(analysis->Dominates(46, 77));
EXPECT_TRUE(analysis->Dominates(46, 75));
EXPECT_TRUE(analysis->Dominates(46, 106));
EXPECT_TRUE(analysis->Dominates(46, 110));
EXPECT_TRUE(analysis->Dominates(46, 107));
EXPECT_TRUE(analysis->Dominates(46, 108));
EXPECT_TRUE(analysis->Dominates(46, 109));
EXPECT_TRUE(analysis->Dominates(46, 74));
EXPECT_TRUE(analysis->Dominates(46, 86));
EXPECT_TRUE(analysis->Dominates(46, 90));
EXPECT_TRUE(analysis->Dominates(46, 87));
EXPECT_TRUE(analysis->Dominates(46, 94));
EXPECT_TRUE(analysis->Dominates(46, 98));
EXPECT_TRUE(analysis->Dominates(46, 95));
EXPECT_TRUE(analysis->Dominates(46, 97));
EXPECT_TRUE(analysis->Dominates(46, 96));
EXPECT_TRUE(analysis->Dominates(46, 88));
EXPECT_TRUE(analysis->Dominates(46, 76));
EXPECT_TRUE(analysis->Dominates(50, 47));
EXPECT_TRUE(analysis->Dominates(50, 57));
EXPECT_TRUE(analysis->Dominates(50, 61));
EXPECT_TRUE(analysis->Dominates(50, 59));
EXPECT_TRUE(analysis->Dominates(50, 58));
EXPECT_TRUE(analysis->Dominates(50, 60));
EXPECT_TRUE(analysis->Dominates(47, 57));
EXPECT_TRUE(analysis->Dominates(47, 61));
EXPECT_TRUE(analysis->Dominates(47, 59));
EXPECT_TRUE(analysis->Dominates(47, 58));
EXPECT_TRUE(analysis->Dominates(47, 60));
EXPECT_TRUE(analysis->Dominates(57, 61));
EXPECT_TRUE(analysis->Dominates(57, 59));
EXPECT_TRUE(analysis->Dominates(57, 58));
EXPECT_TRUE(analysis->Dominates(57, 60));
EXPECT_TRUE(analysis->Dominates(61, 59));
EXPECT_TRUE(analysis->Dominates(48, 73));
EXPECT_TRUE(analysis->Dominates(48, 77));
EXPECT_TRUE(analysis->Dominates(48, 75));
EXPECT_TRUE(analysis->Dominates(48, 106));
EXPECT_TRUE(analysis->Dominates(48, 110));
EXPECT_TRUE(analysis->Dominates(48, 107));
EXPECT_TRUE(analysis->Dominates(48, 108));
EXPECT_TRUE(analysis->Dominates(48, 109));
EXPECT_TRUE(analysis->Dominates(48, 74));
EXPECT_TRUE(analysis->Dominates(48, 86));
EXPECT_TRUE(analysis->Dominates(48, 90));
EXPECT_TRUE(analysis->Dominates(48, 87));
EXPECT_TRUE(analysis->Dominates(48, 94));
EXPECT_TRUE(analysis->Dominates(48, 98));
EXPECT_TRUE(analysis->Dominates(48, 95));
EXPECT_TRUE(analysis->Dominates(48, 97));
EXPECT_TRUE(analysis->Dominates(48, 96));
EXPECT_TRUE(analysis->Dominates(48, 88));
EXPECT_TRUE(analysis->Dominates(48, 76));
EXPECT_TRUE(analysis->Dominates(73, 77));
EXPECT_TRUE(analysis->Dominates(73, 75));
EXPECT_TRUE(analysis->Dominates(73, 106));
EXPECT_TRUE(analysis->Dominates(73, 110));
EXPECT_TRUE(analysis->Dominates(73, 107));
EXPECT_TRUE(analysis->Dominates(73, 108));
EXPECT_TRUE(analysis->Dominates(73, 109));
EXPECT_TRUE(analysis->Dominates(73, 74));
EXPECT_TRUE(analysis->Dominates(73, 86));
EXPECT_TRUE(analysis->Dominates(73, 90));
EXPECT_TRUE(analysis->Dominates(73, 87));
EXPECT_TRUE(analysis->Dominates(73, 94));
EXPECT_TRUE(analysis->Dominates(73, 98));
EXPECT_TRUE(analysis->Dominates(73, 95));
EXPECT_TRUE(analysis->Dominates(73, 97));
EXPECT_TRUE(analysis->Dominates(73, 96));
EXPECT_TRUE(analysis->Dominates(73, 88));
EXPECT_TRUE(analysis->Dominates(73, 76));
EXPECT_TRUE(analysis->Dominates(75, 106));
EXPECT_TRUE(analysis->Dominates(75, 110));
EXPECT_TRUE(analysis->Dominates(75, 107));
EXPECT_TRUE(analysis->Dominates(75, 108));
EXPECT_TRUE(analysis->Dominates(75, 109));
EXPECT_TRUE(analysis->Dominates(106, 110));
EXPECT_TRUE(analysis->Dominates(106, 107));
EXPECT_TRUE(analysis->Dominates(106, 108));
EXPECT_TRUE(analysis->Dominates(106, 109));
EXPECT_TRUE(analysis->Dominates(110, 107));
EXPECT_TRUE(analysis->Dominates(77, 74));
EXPECT_TRUE(analysis->Dominates(77, 86));
EXPECT_TRUE(analysis->Dominates(77, 90));
EXPECT_TRUE(analysis->Dominates(77, 87));
EXPECT_TRUE(analysis->Dominates(77, 94));
EXPECT_TRUE(analysis->Dominates(77, 98));
EXPECT_TRUE(analysis->Dominates(77, 95));
EXPECT_TRUE(analysis->Dominates(77, 97));
EXPECT_TRUE(analysis->Dominates(77, 96));
EXPECT_TRUE(analysis->Dominates(77, 88));
EXPECT_TRUE(analysis->Dominates(74, 86));
EXPECT_TRUE(analysis->Dominates(74, 90));
EXPECT_TRUE(analysis->Dominates(74, 87));
EXPECT_TRUE(analysis->Dominates(74, 94));
EXPECT_TRUE(analysis->Dominates(74, 98));
EXPECT_TRUE(analysis->Dominates(74, 95));
EXPECT_TRUE(analysis->Dominates(74, 97));
EXPECT_TRUE(analysis->Dominates(74, 96));
EXPECT_TRUE(analysis->Dominates(74, 88));
EXPECT_TRUE(analysis->Dominates(86, 90));
EXPECT_TRUE(analysis->Dominates(86, 87));
EXPECT_TRUE(analysis->Dominates(86, 94));
EXPECT_TRUE(analysis->Dominates(86, 98));
EXPECT_TRUE(analysis->Dominates(86, 95));
EXPECT_TRUE(analysis->Dominates(86, 97));
EXPECT_TRUE(analysis->Dominates(86, 96));
EXPECT_TRUE(analysis->Dominates(86, 88));
EXPECT_TRUE(analysis->Dominates(90, 87));
EXPECT_TRUE(analysis->Dominates(90, 94));
EXPECT_TRUE(analysis->Dominates(90, 98));
EXPECT_TRUE(analysis->Dominates(90, 95));
EXPECT_TRUE(analysis->Dominates(90, 97));
EXPECT_TRUE(analysis->Dominates(90, 96));
EXPECT_TRUE(analysis->Dominates(87, 94));
EXPECT_TRUE(analysis->Dominates(87, 98));
EXPECT_TRUE(analysis->Dominates(87, 95));
EXPECT_TRUE(analysis->Dominates(87, 97));
EXPECT_TRUE(analysis->Dominates(87, 96));
EXPECT_TRUE(analysis->Dominates(94, 98));
EXPECT_TRUE(analysis->Dominates(94, 95));
EXPECT_TRUE(analysis->Dominates(94, 97));
EXPECT_TRUE(analysis->Dominates(94, 96));
EXPECT_TRUE(analysis->Dominates(98, 95));
EXPECT_TRUE(analysis->StrictlyDominates(5, 10));
EXPECT_TRUE(analysis->StrictlyDominates(5, 14));
EXPECT_TRUE(analysis->StrictlyDominates(5, 11));
EXPECT_TRUE(analysis->StrictlyDominates(5, 29));
EXPECT_TRUE(analysis->StrictlyDominates(5, 33));
EXPECT_TRUE(analysis->StrictlyDominates(5, 30));
EXPECT_TRUE(analysis->StrictlyDominates(5, 32));
EXPECT_TRUE(analysis->StrictlyDominates(5, 31));
EXPECT_TRUE(analysis->StrictlyDominates(5, 13));
EXPECT_TRUE(analysis->StrictlyDominates(5, 12));
EXPECT_TRUE(analysis->StrictlyDominates(5, 46));
EXPECT_TRUE(analysis->StrictlyDominates(5, 50));
EXPECT_TRUE(analysis->StrictlyDominates(5, 47));
EXPECT_TRUE(analysis->StrictlyDominates(5, 57));
EXPECT_TRUE(analysis->StrictlyDominates(5, 61));
EXPECT_TRUE(analysis->StrictlyDominates(5, 59));
EXPECT_TRUE(analysis->StrictlyDominates(5, 58));
EXPECT_TRUE(analysis->StrictlyDominates(5, 60));
EXPECT_TRUE(analysis->StrictlyDominates(5, 48));
EXPECT_TRUE(analysis->StrictlyDominates(5, 73));
EXPECT_TRUE(analysis->StrictlyDominates(5, 77));
EXPECT_TRUE(analysis->StrictlyDominates(5, 75));
EXPECT_TRUE(analysis->StrictlyDominates(5, 106));
EXPECT_TRUE(analysis->StrictlyDominates(5, 110));
EXPECT_TRUE(analysis->StrictlyDominates(5, 107));
EXPECT_TRUE(analysis->StrictlyDominates(5, 108));
EXPECT_TRUE(analysis->StrictlyDominates(5, 109));
EXPECT_TRUE(analysis->StrictlyDominates(5, 74));
EXPECT_TRUE(analysis->StrictlyDominates(5, 86));
EXPECT_TRUE(analysis->StrictlyDominates(5, 90));
EXPECT_TRUE(analysis->StrictlyDominates(5, 87));
EXPECT_TRUE(analysis->StrictlyDominates(5, 94));
EXPECT_TRUE(analysis->StrictlyDominates(5, 98));
EXPECT_TRUE(analysis->StrictlyDominates(5, 95));
EXPECT_TRUE(analysis->StrictlyDominates(5, 97));
EXPECT_TRUE(analysis->StrictlyDominates(5, 96));
EXPECT_TRUE(analysis->StrictlyDominates(5, 88));
EXPECT_TRUE(analysis->StrictlyDominates(5, 76));
EXPECT_TRUE(analysis->StrictlyDominates(10, 14));
EXPECT_TRUE(analysis->StrictlyDominates(10, 11));
EXPECT_TRUE(analysis->StrictlyDominates(10, 29));
EXPECT_TRUE(analysis->StrictlyDominates(10, 33));
EXPECT_TRUE(analysis->StrictlyDominates(10, 30));
EXPECT_TRUE(analysis->StrictlyDominates(10, 32));
EXPECT_TRUE(analysis->StrictlyDominates(10, 31));
EXPECT_TRUE(analysis->StrictlyDominates(10, 13));
EXPECT_TRUE(analysis->StrictlyDominates(10, 12));
EXPECT_TRUE(analysis->StrictlyDominates(10, 46));
EXPECT_TRUE(analysis->StrictlyDominates(10, 50));
EXPECT_TRUE(analysis->StrictlyDominates(10, 47));
EXPECT_TRUE(analysis->StrictlyDominates(10, 57));
EXPECT_TRUE(analysis->StrictlyDominates(10, 61));
EXPECT_TRUE(analysis->StrictlyDominates(10, 59));
EXPECT_TRUE(analysis->StrictlyDominates(10, 58));
EXPECT_TRUE(analysis->StrictlyDominates(10, 60));
EXPECT_TRUE(analysis->StrictlyDominates(10, 48));
EXPECT_TRUE(analysis->StrictlyDominates(10, 73));
EXPECT_TRUE(analysis->StrictlyDominates(10, 77));
EXPECT_TRUE(analysis->StrictlyDominates(10, 75));
EXPECT_TRUE(analysis->StrictlyDominates(10, 106));
EXPECT_TRUE(analysis->StrictlyDominates(10, 110));
EXPECT_TRUE(analysis->StrictlyDominates(10, 107));
EXPECT_TRUE(analysis->StrictlyDominates(10, 108));
EXPECT_TRUE(analysis->StrictlyDominates(10, 109));
EXPECT_TRUE(analysis->StrictlyDominates(10, 74));
EXPECT_TRUE(analysis->StrictlyDominates(10, 86));
EXPECT_TRUE(analysis->StrictlyDominates(10, 90));
EXPECT_TRUE(analysis->StrictlyDominates(10, 87));
EXPECT_TRUE(analysis->StrictlyDominates(10, 94));
EXPECT_TRUE(analysis->StrictlyDominates(10, 98));
EXPECT_TRUE(analysis->StrictlyDominates(10, 95));
EXPECT_TRUE(analysis->StrictlyDominates(10, 97));
EXPECT_TRUE(analysis->StrictlyDominates(10, 96));
EXPECT_TRUE(analysis->StrictlyDominates(10, 88));
EXPECT_TRUE(analysis->StrictlyDominates(10, 76));
EXPECT_TRUE(analysis->StrictlyDominates(14, 11));
EXPECT_TRUE(analysis->StrictlyDominates(14, 29));
EXPECT_TRUE(analysis->StrictlyDominates(14, 33));
EXPECT_TRUE(analysis->StrictlyDominates(14, 30));
EXPECT_TRUE(analysis->StrictlyDominates(14, 32));
EXPECT_TRUE(analysis->StrictlyDominates(14, 31));
EXPECT_TRUE(analysis->StrictlyDominates(11, 29));
EXPECT_TRUE(analysis->StrictlyDominates(11, 33));
EXPECT_TRUE(analysis->StrictlyDominates(11, 30));
EXPECT_TRUE(analysis->StrictlyDominates(11, 32));
EXPECT_TRUE(analysis->StrictlyDominates(11, 31));
EXPECT_TRUE(analysis->StrictlyDominates(29, 33));
EXPECT_TRUE(analysis->StrictlyDominates(29, 30));
EXPECT_TRUE(analysis->StrictlyDominates(29, 32));
EXPECT_TRUE(analysis->StrictlyDominates(29, 31));
EXPECT_TRUE(analysis->StrictlyDominates(33, 30));
EXPECT_TRUE(analysis->StrictlyDominates(12, 46));
EXPECT_TRUE(analysis->StrictlyDominates(12, 50));
EXPECT_TRUE(analysis->StrictlyDominates(12, 47));
EXPECT_TRUE(analysis->StrictlyDominates(12, 57));
EXPECT_TRUE(analysis->StrictlyDominates(12, 61));
EXPECT_TRUE(analysis->StrictlyDominates(12, 59));
EXPECT_TRUE(analysis->StrictlyDominates(12, 58));
EXPECT_TRUE(analysis->StrictlyDominates(12, 60));
EXPECT_TRUE(analysis->StrictlyDominates(12, 48));
EXPECT_TRUE(analysis->StrictlyDominates(12, 73));
EXPECT_TRUE(analysis->StrictlyDominates(12, 77));
EXPECT_TRUE(analysis->StrictlyDominates(12, 75));
EXPECT_TRUE(analysis->StrictlyDominates(12, 106));
EXPECT_TRUE(analysis->StrictlyDominates(12, 110));
EXPECT_TRUE(analysis->StrictlyDominates(12, 107));
EXPECT_TRUE(analysis->StrictlyDominates(12, 108));
EXPECT_TRUE(analysis->StrictlyDominates(12, 109));
EXPECT_TRUE(analysis->StrictlyDominates(12, 74));
EXPECT_TRUE(analysis->StrictlyDominates(12, 86));
EXPECT_TRUE(analysis->StrictlyDominates(12, 90));
EXPECT_TRUE(analysis->StrictlyDominates(12, 87));
EXPECT_TRUE(analysis->StrictlyDominates(12, 94));
EXPECT_TRUE(analysis->StrictlyDominates(12, 98));
EXPECT_TRUE(analysis->StrictlyDominates(12, 95));
EXPECT_TRUE(analysis->StrictlyDominates(12, 97));
EXPECT_TRUE(analysis->StrictlyDominates(12, 96));
EXPECT_TRUE(analysis->StrictlyDominates(12, 88));
EXPECT_TRUE(analysis->StrictlyDominates(12, 76));
EXPECT_TRUE(analysis->StrictlyDominates(46, 50));
EXPECT_TRUE(analysis->StrictlyDominates(46, 47));
EXPECT_TRUE(analysis->StrictlyDominates(46, 57));
EXPECT_TRUE(analysis->StrictlyDominates(46, 61));
EXPECT_TRUE(analysis->StrictlyDominates(46, 59));
EXPECT_TRUE(analysis->StrictlyDominates(46, 58));
EXPECT_TRUE(analysis->StrictlyDominates(46, 60));
EXPECT_TRUE(analysis->StrictlyDominates(46, 48));
EXPECT_TRUE(analysis->StrictlyDominates(46, 73));
EXPECT_TRUE(analysis->StrictlyDominates(46, 77));
EXPECT_TRUE(analysis->StrictlyDominates(46, 75));
EXPECT_TRUE(analysis->StrictlyDominates(46, 106));
EXPECT_TRUE(analysis->StrictlyDominates(46, 110));
EXPECT_TRUE(analysis->StrictlyDominates(46, 107));
EXPECT_TRUE(analysis->StrictlyDominates(46, 108));
EXPECT_TRUE(analysis->StrictlyDominates(46, 109));
EXPECT_TRUE(analysis->StrictlyDominates(46, 74));
EXPECT_TRUE(analysis->StrictlyDominates(46, 86));
EXPECT_TRUE(analysis->StrictlyDominates(46, 90));
EXPECT_TRUE(analysis->StrictlyDominates(46, 87));
EXPECT_TRUE(analysis->StrictlyDominates(46, 94));
EXPECT_TRUE(analysis->StrictlyDominates(46, 98));
EXPECT_TRUE(analysis->StrictlyDominates(46, 95));
EXPECT_TRUE(analysis->StrictlyDominates(46, 97));
EXPECT_TRUE(analysis->StrictlyDominates(46, 96));
EXPECT_TRUE(analysis->StrictlyDominates(46, 88));
EXPECT_TRUE(analysis->StrictlyDominates(46, 76));
EXPECT_TRUE(analysis->StrictlyDominates(50, 47));
EXPECT_TRUE(analysis->StrictlyDominates(50, 57));
EXPECT_TRUE(analysis->StrictlyDominates(50, 61));
EXPECT_TRUE(analysis->StrictlyDominates(50, 59));
EXPECT_TRUE(analysis->StrictlyDominates(50, 58));
EXPECT_TRUE(analysis->StrictlyDominates(50, 60));
EXPECT_TRUE(analysis->StrictlyDominates(47, 57));
EXPECT_TRUE(analysis->StrictlyDominates(47, 61));
EXPECT_TRUE(analysis->StrictlyDominates(47, 59));
EXPECT_TRUE(analysis->StrictlyDominates(47, 58));
EXPECT_TRUE(analysis->StrictlyDominates(47, 60));
EXPECT_TRUE(analysis->StrictlyDominates(57, 61));
EXPECT_TRUE(analysis->StrictlyDominates(57, 59));
EXPECT_TRUE(analysis->StrictlyDominates(57, 58));
EXPECT_TRUE(analysis->StrictlyDominates(57, 60));
EXPECT_TRUE(analysis->StrictlyDominates(61, 59));
EXPECT_TRUE(analysis->StrictlyDominates(48, 73));
EXPECT_TRUE(analysis->StrictlyDominates(48, 77));
EXPECT_TRUE(analysis->StrictlyDominates(48, 75));
EXPECT_TRUE(analysis->StrictlyDominates(48, 106));
EXPECT_TRUE(analysis->StrictlyDominates(48, 110));
EXPECT_TRUE(analysis->StrictlyDominates(48, 107));
EXPECT_TRUE(analysis->StrictlyDominates(48, 108));
EXPECT_TRUE(analysis->StrictlyDominates(48, 109));
EXPECT_TRUE(analysis->StrictlyDominates(48, 74));
EXPECT_TRUE(analysis->StrictlyDominates(48, 86));
EXPECT_TRUE(analysis->StrictlyDominates(48, 90));
EXPECT_TRUE(analysis->StrictlyDominates(48, 87));
EXPECT_TRUE(analysis->StrictlyDominates(48, 94));
EXPECT_TRUE(analysis->StrictlyDominates(48, 98));
EXPECT_TRUE(analysis->StrictlyDominates(48, 95));
EXPECT_TRUE(analysis->StrictlyDominates(48, 97));
EXPECT_TRUE(analysis->StrictlyDominates(48, 96));
EXPECT_TRUE(analysis->StrictlyDominates(48, 88));
EXPECT_TRUE(analysis->StrictlyDominates(48, 76));
EXPECT_TRUE(analysis->StrictlyDominates(73, 77));
EXPECT_TRUE(analysis->StrictlyDominates(73, 75));
EXPECT_TRUE(analysis->StrictlyDominates(73, 106));
EXPECT_TRUE(analysis->StrictlyDominates(73, 110));
EXPECT_TRUE(analysis->StrictlyDominates(73, 107));
EXPECT_TRUE(analysis->StrictlyDominates(73, 108));
EXPECT_TRUE(analysis->StrictlyDominates(73, 109));
EXPECT_TRUE(analysis->StrictlyDominates(73, 74));
EXPECT_TRUE(analysis->StrictlyDominates(73, 86));
EXPECT_TRUE(analysis->StrictlyDominates(73, 90));
EXPECT_TRUE(analysis->StrictlyDominates(73, 87));
EXPECT_TRUE(analysis->StrictlyDominates(73, 94));
EXPECT_TRUE(analysis->StrictlyDominates(73, 98));
EXPECT_TRUE(analysis->StrictlyDominates(73, 95));
EXPECT_TRUE(analysis->StrictlyDominates(73, 97));
EXPECT_TRUE(analysis->StrictlyDominates(73, 96));
EXPECT_TRUE(analysis->StrictlyDominates(73, 88));
EXPECT_TRUE(analysis->StrictlyDominates(73, 76));
EXPECT_TRUE(analysis->StrictlyDominates(75, 106));
EXPECT_TRUE(analysis->StrictlyDominates(75, 110));
EXPECT_TRUE(analysis->StrictlyDominates(75, 107));
EXPECT_TRUE(analysis->StrictlyDominates(75, 108));
EXPECT_TRUE(analysis->StrictlyDominates(75, 109));
EXPECT_TRUE(analysis->StrictlyDominates(106, 110));
EXPECT_TRUE(analysis->StrictlyDominates(106, 107));
EXPECT_TRUE(analysis->StrictlyDominates(106, 108));
EXPECT_TRUE(analysis->StrictlyDominates(106, 109));
EXPECT_TRUE(analysis->StrictlyDominates(110, 107));
EXPECT_TRUE(analysis->StrictlyDominates(77, 74));
EXPECT_TRUE(analysis->StrictlyDominates(77, 86));
EXPECT_TRUE(analysis->StrictlyDominates(77, 90));
EXPECT_TRUE(analysis->StrictlyDominates(77, 87));
EXPECT_TRUE(analysis->StrictlyDominates(77, 94));
EXPECT_TRUE(analysis->StrictlyDominates(77, 98));
EXPECT_TRUE(analysis->StrictlyDominates(77, 95));
EXPECT_TRUE(analysis->StrictlyDominates(77, 97));
EXPECT_TRUE(analysis->StrictlyDominates(77, 96));
EXPECT_TRUE(analysis->StrictlyDominates(77, 88));
EXPECT_TRUE(analysis->StrictlyDominates(74, 86));
EXPECT_TRUE(analysis->StrictlyDominates(74, 90));
EXPECT_TRUE(analysis->StrictlyDominates(74, 87));
EXPECT_TRUE(analysis->StrictlyDominates(74, 94));
EXPECT_TRUE(analysis->StrictlyDominates(74, 98));
EXPECT_TRUE(analysis->StrictlyDominates(74, 95));
EXPECT_TRUE(analysis->StrictlyDominates(74, 97));
EXPECT_TRUE(analysis->StrictlyDominates(74, 96));
EXPECT_TRUE(analysis->StrictlyDominates(74, 88));
EXPECT_TRUE(analysis->StrictlyDominates(86, 90));
EXPECT_TRUE(analysis->StrictlyDominates(86, 87));
EXPECT_TRUE(analysis->StrictlyDominates(86, 94));
EXPECT_TRUE(analysis->StrictlyDominates(86, 98));
EXPECT_TRUE(analysis->StrictlyDominates(86, 95));
EXPECT_TRUE(analysis->StrictlyDominates(86, 97));
EXPECT_TRUE(analysis->StrictlyDominates(86, 96));
EXPECT_TRUE(analysis->StrictlyDominates(86, 88));
EXPECT_TRUE(analysis->StrictlyDominates(90, 87));
EXPECT_TRUE(analysis->StrictlyDominates(90, 94));
EXPECT_TRUE(analysis->StrictlyDominates(90, 98));
EXPECT_TRUE(analysis->StrictlyDominates(90, 95));
EXPECT_TRUE(analysis->StrictlyDominates(90, 97));
EXPECT_TRUE(analysis->StrictlyDominates(90, 96));
EXPECT_TRUE(analysis->StrictlyDominates(87, 94));
EXPECT_TRUE(analysis->StrictlyDominates(87, 98));
EXPECT_TRUE(analysis->StrictlyDominates(87, 95));
EXPECT_TRUE(analysis->StrictlyDominates(87, 97));
EXPECT_TRUE(analysis->StrictlyDominates(87, 96));
EXPECT_TRUE(analysis->StrictlyDominates(94, 98));
EXPECT_TRUE(analysis->StrictlyDominates(94, 95));
EXPECT_TRUE(analysis->StrictlyDominates(94, 97));
EXPECT_TRUE(analysis->StrictlyDominates(94, 96));
EXPECT_TRUE(analysis->StrictlyDominates(98, 95));
}
} // namespace

View File

@ -0,0 +1,206 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include "../assembly_builder.h"
#include "../function_utils.h"
#include "../pass_fixture.h"
#include "../pass_utils.h"
#include "opt/dominator_analysis.h"
#include "opt/pass.h"
namespace {
using namespace spvtools;
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
Generated from the following GLSL
#version 440 core
layout(location = 0) out vec4 c;
layout(location = 1)in vec4 in_val;
void main(){
if ( in_val.x < 10) {
int z = 0;
int i = 0;
for (i = 0; i < in_val.y; ++i) {
z += i;
}
c = vec4(i,i,i,i);
} else {
c = vec4(1,1,1,1);
}
}
*/
TEST_F(PassClassTest, BasicVisitFromEntryPoint) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %9 %43
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 440
OpName %4 "main"
OpName %9 "in_val"
OpName %22 "z"
OpName %24 "i"
OpName %43 "c"
OpDecorate %9 Location 1
OpDecorate %43 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%8 = OpTypePointer Input %7
%9 = OpVariable %8 Input
%10 = OpTypeInt 32 0
%11 = OpConstant %10 0
%12 = OpTypePointer Input %6
%15 = OpConstant %6 10
%16 = OpTypeBool
%20 = OpTypeInt 32 1
%21 = OpTypePointer Function %20
%23 = OpConstant %20 0
%32 = OpConstant %10 1
%40 = OpConstant %20 1
%42 = OpTypePointer Output %7
%43 = OpVariable %42 Output
%54 = OpConstant %6 1
%55 = OpConstantComposite %7 %54 %54 %54 %54
%4 = OpFunction %2 None %3
%5 = OpLabel
%22 = OpVariable %21 Function
%24 = OpVariable %21 Function
%13 = OpAccessChain %12 %9 %11
%14 = OpLoad %6 %13
%17 = OpFOrdLessThan %16 %14 %15
OpSelectionMerge %19 None
OpBranchConditional %17 %18 %53
%18 = OpLabel
OpStore %22 %23
OpStore %24 %23
OpStore %24 %23
OpBranch %25
%25 = OpLabel
OpLoopMerge %27 %28 None
OpBranch %29
%29 = OpLabel
%30 = OpLoad %20 %24
%31 = OpConvertSToF %6 %30
%33 = OpAccessChain %12 %9 %32
%34 = OpLoad %6 %33
%35 = OpFOrdLessThan %16 %31 %34
OpBranchConditional %35 %26 %27
%26 = OpLabel
%36 = OpLoad %20 %24
%37 = OpLoad %20 %22
%38 = OpIAdd %20 %37 %36
OpStore %22 %38
OpBranch %28
%28 = OpLabel
%39 = OpLoad %20 %24
%41 = OpIAdd %20 %39 %40
OpStore %24 %41
OpBranch %25
%27 = OpLabel
%44 = OpLoad %20 %24
%45 = OpConvertSToF %6 %44
%46 = OpLoad %20 %24
%47 = OpConvertSToF %6 %46
%48 = OpLoad %20 %24
%49 = OpConvertSToF %6 %48
%50 = OpLoad %20 %24
%51 = OpConvertSToF %6 %50
%52 = OpCompositeConstruct %7 %45 %47 %49 %51
OpStore %43 %52
OpBranch %19
%53 = OpLabel
OpStore %43 %55
OpBranch %19
%19 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const ir::Function* f = spvtest::GetFunction(module, 4);
ir::CFG cfg(module);
opt::PostDominatorAnalysis* analysis =
context->GetPostDominatorAnalysis(f, cfg);
EXPECT_TRUE(analysis->Dominates(19, 18));
EXPECT_TRUE(analysis->Dominates(19, 5));
EXPECT_TRUE(analysis->Dominates(19, 53));
EXPECT_TRUE(analysis->Dominates(19, 19));
EXPECT_TRUE(analysis->Dominates(19, 25));
EXPECT_TRUE(analysis->Dominates(19, 29));
EXPECT_TRUE(analysis->Dominates(19, 27));
EXPECT_TRUE(analysis->Dominates(19, 26));
EXPECT_TRUE(analysis->Dominates(19, 28));
EXPECT_TRUE(analysis->Dominates(27, 18));
EXPECT_TRUE(analysis->Dominates(27, 25));
EXPECT_TRUE(analysis->Dominates(27, 29));
EXPECT_TRUE(analysis->Dominates(27, 27));
EXPECT_TRUE(analysis->Dominates(27, 26));
EXPECT_TRUE(analysis->Dominates(27, 28));
EXPECT_FALSE(analysis->Dominates(27, 19));
EXPECT_FALSE(analysis->Dominates(27, 5));
EXPECT_FALSE(analysis->Dominates(27, 53));
EXPECT_FALSE(analysis->StrictlyDominates(19, 19));
EXPECT_TRUE(analysis->StrictlyDominates(19, 18));
EXPECT_TRUE(analysis->StrictlyDominates(19, 5));
EXPECT_TRUE(analysis->StrictlyDominates(19, 53));
EXPECT_TRUE(analysis->StrictlyDominates(19, 25));
EXPECT_TRUE(analysis->StrictlyDominates(19, 29));
EXPECT_TRUE(analysis->StrictlyDominates(19, 27));
EXPECT_TRUE(analysis->StrictlyDominates(19, 26));
EXPECT_TRUE(analysis->StrictlyDominates(19, 28));
// These would be expected true for a normal, non post, dominator tree
EXPECT_FALSE(analysis->Dominates(5, 18));
EXPECT_FALSE(analysis->Dominates(5, 53));
EXPECT_FALSE(analysis->Dominates(5, 19));
EXPECT_FALSE(analysis->Dominates(5, 25));
EXPECT_FALSE(analysis->Dominates(5, 29));
EXPECT_FALSE(analysis->Dominates(5, 27));
EXPECT_FALSE(analysis->Dominates(5, 26));
EXPECT_FALSE(analysis->Dominates(5, 28));
EXPECT_FALSE(analysis->StrictlyDominates(5, 18));
EXPECT_FALSE(analysis->StrictlyDominates(5, 53));
EXPECT_FALSE(analysis->StrictlyDominates(5, 19));
EXPECT_FALSE(analysis->StrictlyDominates(5, 25));
EXPECT_FALSE(analysis->StrictlyDominates(5, 29));
EXPECT_FALSE(analysis->StrictlyDominates(5, 27));
EXPECT_FALSE(analysis->StrictlyDominates(5, 26));
EXPECT_FALSE(analysis->StrictlyDominates(5, 28));
}
} // namespace

View File

@ -0,0 +1,175 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include "../assembly_builder.h"
#include "../function_utils.h"
#include "../pass_fixture.h"
#include "../pass_utils.h"
#include "opt/dominator_analysis.h"
#include "opt/pass.h"
namespace {
using namespace spvtools;
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
Generated from the following GLSL
#version 440 core
layout(location = 0) out vec4 c;
layout(location = 1)in vec4 in_val;
void main(){
if ( in_val.x < 10) {
int z = 0;
int i = 0;
for (i = 0; i < in_val.y; ++i) {
z += i;
}
c = vec4(i,i,i,i);
} else {
c = vec4(1,1,1,1);
}
}
*/
TEST_F(PassClassTest, BasicVisitFromEntryPoint) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %9 %43
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 440
OpName %4 "main"
OpName %9 "in_val"
OpName %22 "z"
OpName %24 "i"
OpName %43 "c"
OpDecorate %9 Location 1
OpDecorate %43 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%8 = OpTypePointer Input %7
%9 = OpVariable %8 Input
%10 = OpTypeInt 32 0
%11 = OpConstant %10 0
%12 = OpTypePointer Input %6
%15 = OpConstant %6 10
%16 = OpTypeBool
%20 = OpTypeInt 32 1
%21 = OpTypePointer Function %20
%23 = OpConstant %20 0
%32 = OpConstant %10 1
%40 = OpConstant %20 1
%42 = OpTypePointer Output %7
%43 = OpVariable %42 Output
%54 = OpConstant %6 1
%55 = OpConstantComposite %7 %54 %54 %54 %54
%4 = OpFunction %2 None %3
%5 = OpLabel
%22 = OpVariable %21 Function
%24 = OpVariable %21 Function
%13 = OpAccessChain %12 %9 %11
%14 = OpLoad %6 %13
%17 = OpFOrdLessThan %16 %14 %15
OpSelectionMerge %19 None
OpBranchConditional %17 %18 %53
%18 = OpLabel
OpStore %22 %23
OpStore %24 %23
OpStore %24 %23
OpBranch %25
%25 = OpLabel
OpLoopMerge %27 %28 None
OpBranch %29
%29 = OpLabel
%30 = OpLoad %20 %24
%31 = OpConvertSToF %6 %30
%33 = OpAccessChain %12 %9 %32
%34 = OpLoad %6 %33
%35 = OpFOrdLessThan %16 %31 %34
OpBranchConditional %35 %26 %27
%26 = OpLabel
%36 = OpLoad %20 %24
%37 = OpLoad %20 %22
%38 = OpIAdd %20 %37 %36
OpStore %22 %38
OpBranch %28
%28 = OpLabel
%39 = OpLoad %20 %24
%41 = OpIAdd %20 %39 %40
OpStore %24 %41
OpBranch %25
%27 = OpLabel
%44 = OpLoad %20 %24
%45 = OpConvertSToF %6 %44
%46 = OpLoad %20 %24
%47 = OpConvertSToF %6 %46
%48 = OpLoad %20 %24
%49 = OpConvertSToF %6 %48
%50 = OpLoad %20 %24
%51 = OpConvertSToF %6 %50
%52 = OpCompositeConstruct %7 %45 %47 %49 %51
OpStore %43 %52
OpBranch %19
%53 = OpLabel
OpStore %43 %55
OpBranch %19
%19 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const ir::Function* f = spvtest::GetFunction(module, 4);
ir::CFG cfg(module);
opt::DominatorAnalysis* analysis = context->GetDominatorAnalysis(f, cfg);
opt::DominatorTree& tree = analysis->GetDomTree();
EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block());
EXPECT_TRUE(analysis->Dominates(5, 18));
EXPECT_TRUE(analysis->Dominates(5, 53));
EXPECT_TRUE(analysis->Dominates(5, 19));
EXPECT_TRUE(analysis->Dominates(5, 25));
EXPECT_TRUE(analysis->Dominates(5, 29));
EXPECT_TRUE(analysis->Dominates(5, 27));
EXPECT_TRUE(analysis->Dominates(5, 26));
EXPECT_TRUE(analysis->Dominates(5, 28));
EXPECT_TRUE(analysis->StrictlyDominates(5, 18));
EXPECT_TRUE(analysis->StrictlyDominates(5, 53));
EXPECT_TRUE(analysis->StrictlyDominates(5, 19));
EXPECT_TRUE(analysis->StrictlyDominates(5, 25));
EXPECT_TRUE(analysis->StrictlyDominates(5, 29));
EXPECT_TRUE(analysis->StrictlyDominates(5, 27));
EXPECT_TRUE(analysis->StrictlyDominates(5, 26));
EXPECT_TRUE(analysis->StrictlyDominates(5, 28));
}
} // namespace

View File

@ -0,0 +1,162 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include "../assembly_builder.h"
#include "../function_utils.h"
#include "../pass_fixture.h"
#include "../pass_utils.h"
#include "opt/dominator_analysis.h"
#include "opt/pass.h"
namespace {
using namespace spvtools;
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
Generated from the following GLSL
#version 440 core
layout(location = 0) out vec4 v;
layout(location = 1) in vec4 in_val;
void main() {
int i;
switch (int(in_val.x)) {
case 0:
i = 0;
case 1:
i = 1;
break;
case 2:
i = 2;
case 3:
i = 3;
case 4:
i = 4;
break;
default:
i = 0;
}
v = vec4(i, i, i, i);
}
*/
TEST_F(PassClassTest, UnreachableNestedIfs) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %9 %35
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 440
OpName %4 "main"
OpName %9 "in_val"
OpName %25 "i"
OpName %35 "v"
OpDecorate %9 Location 1
OpDecorate %35 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%8 = OpTypePointer Input %7
%9 = OpVariable %8 Input
%10 = OpTypeInt 32 0
%11 = OpConstant %10 0
%12 = OpTypePointer Input %6
%15 = OpTypeInt 32 1
%24 = OpTypePointer Function %15
%26 = OpConstant %15 0
%27 = OpConstant %15 1
%29 = OpConstant %15 2
%30 = OpConstant %15 3
%31 = OpConstant %15 4
%34 = OpTypePointer Output %7
%35 = OpVariable %34 Output
%4 = OpFunction %2 None %3
%5 = OpLabel
%25 = OpVariable %24 Function
%13 = OpAccessChain %12 %9 %11
%14 = OpLoad %6 %13
%16 = OpConvertFToS %15 %14
OpSelectionMerge %23 None
OpSwitch %16 %22 0 %17 1 %18 2 %19 3 %20 4 %21
%22 = OpLabel
OpStore %25 %26
OpBranch %23
%17 = OpLabel
OpStore %25 %26
OpBranch %18
%18 = OpLabel
OpStore %25 %27
OpBranch %23
%19 = OpLabel
OpStore %25 %29
OpBranch %20
%20 = OpLabel
OpStore %25 %30
OpBranch %21
%21 = OpLabel
OpStore %25 %31
OpBranch %23
%23 = OpLabel
%36 = OpLoad %15 %25
%37 = OpConvertSToF %6 %36
%38 = OpLoad %15 %25
%39 = OpConvertSToF %6 %38
%40 = OpLoad %15 %25
%41 = OpConvertSToF %6 %40
%42 = OpLoad %15 %25
%43 = OpConvertSToF %6 %42
%44 = OpCompositeConstruct %7 %37 %39 %41 %43
OpStore %35 %44
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const ir::Function* f = spvtest::GetFunction(module, 4);
ir::CFG cfg(module);
opt::DominatorAnalysis* analysis = context->GetDominatorAnalysis(f, cfg);
EXPECT_TRUE(analysis->Dominates(5, 5));
EXPECT_TRUE(analysis->Dominates(5, 17));
EXPECT_TRUE(analysis->Dominates(5, 18));
EXPECT_TRUE(analysis->Dominates(5, 19));
EXPECT_TRUE(analysis->Dominates(5, 20));
EXPECT_TRUE(analysis->Dominates(5, 21));
EXPECT_TRUE(analysis->Dominates(5, 22));
EXPECT_TRUE(analysis->Dominates(5, 23));
EXPECT_TRUE(analysis->StrictlyDominates(5, 17));
EXPECT_TRUE(analysis->StrictlyDominates(5, 18));
EXPECT_TRUE(analysis->StrictlyDominates(5, 19));
EXPECT_TRUE(analysis->StrictlyDominates(5, 20));
EXPECT_TRUE(analysis->StrictlyDominates(5, 21));
EXPECT_TRUE(analysis->StrictlyDominates(5, 22));
EXPECT_TRUE(analysis->StrictlyDominates(5, 23));
}
} // namespace

View File

@ -0,0 +1,120 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include "../assembly_builder.h"
#include "../function_utils.h"
#include "../pass_fixture.h"
#include "../pass_utils.h"
#include "opt/dominator_analysis.h"
#include "opt/pass.h"
namespace {
using namespace spvtools;
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
Generated from the following GLSL
#version 440 core
void main() {
for (int i = 0; i < 1; i++) {
break;
}
}
*/
TEST_F(PassClassTest, UnreachableNestedIfs) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 440
OpName %4 "main"
OpName %8 "i"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 1
%17 = OpTypeBool
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpBranch %12
%13 = OpLabel
%20 = OpLoad %6 %8
%21 = OpIAdd %6 %20 %16
OpStore %8 %21
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const ir::Function* f = spvtest::GetFunction(module, 4);
ir::CFG cfg(module);
opt::DominatorAnalysis* analysis = context->GetDominatorAnalysis(f, cfg);
EXPECT_TRUE(analysis->Dominates(5, 5));
EXPECT_TRUE(analysis->Dominates(5, 10));
EXPECT_TRUE(analysis->Dominates(5, 14));
EXPECT_TRUE(analysis->Dominates(5, 11));
EXPECT_TRUE(analysis->Dominates(5, 12));
EXPECT_TRUE(analysis->Dominates(10, 10));
EXPECT_TRUE(analysis->Dominates(10, 14));
EXPECT_TRUE(analysis->Dominates(10, 11));
EXPECT_TRUE(analysis->Dominates(10, 12));
EXPECT_TRUE(analysis->Dominates(14, 14));
EXPECT_TRUE(analysis->Dominates(14, 11));
EXPECT_TRUE(analysis->Dominates(14, 12));
EXPECT_TRUE(analysis->Dominates(11, 11));
EXPECT_TRUE(analysis->Dominates(12, 12));
EXPECT_TRUE(analysis->StrictlyDominates(5, 10));
EXPECT_TRUE(analysis->StrictlyDominates(5, 14));
EXPECT_TRUE(analysis->StrictlyDominates(5, 11));
EXPECT_TRUE(analysis->StrictlyDominates(5, 12));
EXPECT_TRUE(analysis->StrictlyDominates(10, 14));
EXPECT_TRUE(analysis->StrictlyDominates(10, 11));
EXPECT_TRUE(analysis->StrictlyDominates(10, 12));
EXPECT_TRUE(analysis->StrictlyDominates(14, 11));
EXPECT_TRUE(analysis->StrictlyDominates(14, 12));
}
} // namespace

View File

@ -0,0 +1,118 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include "../assembly_builder.h"
#include "../function_utils.h"
#include "../pass_fixture.h"
#include "../pass_utils.h"
#include "opt/dominator_analysis.h"
#include "opt/pass.h"
namespace {
using namespace spvtools;
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
Generated from the following GLSL
#version 440 core
void main() {
for (int i = 0; i < 1; i++) {
break;
}
}
*/
TEST_F(PassClassTest, UnreachableNestedIfs) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 440
OpName %4 "main"
OpName %8 "i"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 1
%17 = OpTypeBool
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpBranch %12
%13 = OpLabel
%20 = OpLoad %6 %8
%21 = OpIAdd %6 %20 %16
OpStore %8 %21
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const ir::Function* f = spvtest::GetFunction(module, 4);
ir::CFG cfg(module);
opt::PostDominatorAnalysis* analysis =
context->GetPostDominatorAnalysis(f, cfg);
EXPECT_TRUE(analysis->Dominates(12, 12));
EXPECT_TRUE(analysis->Dominates(12, 14));
EXPECT_TRUE(analysis->Dominates(12, 11));
EXPECT_TRUE(analysis->Dominates(12, 10));
EXPECT_TRUE(analysis->Dominates(12, 5));
EXPECT_TRUE(analysis->Dominates(14, 14));
EXPECT_TRUE(analysis->Dominates(14, 10));
EXPECT_TRUE(analysis->Dominates(14, 5));
EXPECT_TRUE(analysis->Dominates(10, 10));
EXPECT_TRUE(analysis->Dominates(10, 5));
EXPECT_TRUE(analysis->Dominates(5, 5));
EXPECT_TRUE(analysis->StrictlyDominates(12, 14));
EXPECT_TRUE(analysis->StrictlyDominates(12, 11));
EXPECT_TRUE(analysis->StrictlyDominates(12, 10));
EXPECT_TRUE(analysis->StrictlyDominates(12, 5));
EXPECT_TRUE(analysis->StrictlyDominates(14, 10));
EXPECT_TRUE(analysis->StrictlyDominates(14, 5));
EXPECT_TRUE(analysis->StrictlyDominates(10, 5));
}
} // namespace

54
test/opt/function_utils.h Normal file
View File

@ -0,0 +1,54 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef LIBSPIRV_TEST_OPT_FUNCTION_UTILS_H_
#define LIBSPIRV_TEST_OPT_FUNCTION_UTILS_H_
#include "opt/function.h"
#include "opt/module.h"
namespace spvtest {
spvtools::ir::Function* GetFunction(spvtools::ir::Module* module, uint32_t id) {
for (spvtools::ir::Function& f : *module) {
if (f.result_id() == id) {
return &f;
}
}
return nullptr;
}
const spvtools::ir::Function* GetFunction(const spvtools::ir::Module* module,
uint32_t id) {
for (const spvtools::ir::Function& f : *module) {
if (f.result_id() == id) {
return &f;
}
}
return nullptr;
}
const spvtools::ir::BasicBlock* GetBasicBlock(const spvtools::ir::Function* fn,
uint32_t id) {
for (const spvtools::ir::BasicBlock& bb : *fn) {
if (bb.id() == id) {
return &bb;
}
}
return nullptr;
}
} // namespace spvtest
#endif // LIBSPIRV_TEST_OPT_FUNCTION_UTILS_H_