mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-22 11:40:05 +00:00
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:
parent
692a22c5bc
commit
8ba68fa9b9
@ -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 \
|
||||
|
2
CHANGES
2
CHANGES
@ -2,6 +2,8 @@ Revision history for SPIRV-Tools
|
||||
|
||||
v2017.2-dev 2017-11-23
|
||||
- Start v2017.2-dev
|
||||
- Optimizer:
|
||||
- Adding dominance analysis
|
||||
- Fixes:
|
||||
#1004: Use after free of an instruction, in remove-duplicates transform
|
||||
#1007: OpImageRead not required to return 4-component vector
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
133
source/opt/dominator_analysis.h
Normal file
133
source/opt/dominator_analysis.h
Normal 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_
|
419
source/opt/dominator_tree.cpp
Normal file
419
source/opt/dominator_tree.cpp
Normal 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
201
source/opt/dominator_tree.h
Normal 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_
|
@ -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());
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
74
test/opt/dominator_tree/CMakeLists.txt
Normal file
74
test/opt/dominator_tree/CMakeLists.txt
Normal 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
|
||||
)
|
798
test/opt/dominator_tree/generated.cpp
Normal file
798
test/opt/dominator_tree/generated.cpp
Normal 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
|
152
test/opt/dominator_tree/nested_ifs.cpp
Normal file
152
test/opt/dominator_tree/nested_ifs.cpp
Normal 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
|
157
test/opt/dominator_tree/nested_ifs_post.cpp
Normal file
157
test/opt/dominator_tree/nested_ifs_post.cpp
Normal 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
|
433
test/opt/dominator_tree/nested_loops.cpp
Normal file
433
test/opt/dominator_tree/nested_loops.cpp
Normal 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
|
846
test/opt/dominator_tree/nested_loops_with_unreachables.cpp
Normal file
846
test/opt/dominator_tree/nested_loops_with_unreachables.cpp
Normal 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
|
206
test/opt/dominator_tree/post.cpp
Normal file
206
test/opt/dominator_tree/post.cpp
Normal 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
|
175
test/opt/dominator_tree/simple.cpp
Normal file
175
test/opt/dominator_tree/simple.cpp
Normal 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
|
162
test/opt/dominator_tree/switch_case_fallthrough.cpp
Normal file
162
test/opt/dominator_tree/switch_case_fallthrough.cpp
Normal 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
|
120
test/opt/dominator_tree/unreachable_for.cpp
Normal file
120
test/opt/dominator_tree/unreachable_for.cpp
Normal 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
|
118
test/opt/dominator_tree/unreachable_for_post.cpp
Normal file
118
test/opt/dominator_tree/unreachable_for_post.cpp
Normal 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
54
test/opt/function_utils.h
Normal 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_
|
Loading…
Reference in New Issue
Block a user