| // 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 <array> | |
| #include <memory> | |
| #include <string> | |
| #include <vector> | |
| #include "gmock/gmock.h" | |
| #include "source/opt/dominator_analysis.h" | |
| #include "source/opt/iterator.h" | |
| #include "source/opt/pass.h" | |
| #include "test/opt/assembly_builder.h" | |
| #include "test/opt/function_utils.h" | |
| #include "test/opt/pass_fixture.h" | |
| #include "test/opt/pass_utils.h" | |
| namespace spvtools { | |
| namespace opt { | |
| namespace { | |
| 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 DominatorAnalysisBase& dom_tree, const 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 vice versa | |
| void check_no_dominance(const DominatorAnalysisBase& dom_tree, | |
| const 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<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << text << std::endl; | |
| const Function* fn = spvtest::GetFunction(module, 1); | |
| const BasicBlock* entry = spvtest::GetBasicBlock(fn, 10); | |
| EXPECT_EQ(entry, fn->entry().get()) | |
| << "The entry node is not the expected one"; | |
| // Test normal dominator tree | |
| { | |
| DominatorAnalysis dom_tree; | |
| const CFG& cfg = *context->cfg(); | |
| dom_tree.InitializeTree(cfg, fn); | |
| // Inspect the actual tree | |
| 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(static_cast<BasicBlock*>(nullptr), | |
| static_cast<BasicBlock*>(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 | |
| { | |
| PostDominatorAnalysis dom_tree; | |
| const CFG& cfg = *context->cfg(); | |
| dom_tree.InitializeTree(cfg, fn); | |
| // Inspect the actual tree | |
| 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(static_cast<BasicBlock*>(nullptr), | |
| static_cast<BasicBlock*>(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<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << text << std::endl; | |
| const Function* fn = spvtest::GetFunction(module, 1); | |
| const BasicBlock* entry = spvtest::GetBasicBlock(fn, 8); | |
| EXPECT_EQ(entry, fn->entry().get()) | |
| << "The entry node is not the expected one"; | |
| // Check normal dominator tree | |
| { | |
| DominatorAnalysis dom_tree; | |
| const CFG& cfg = *context->cfg(); | |
| dom_tree.InitializeTree(cfg, fn); | |
| // Inspect the actual tree | |
| 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 | |
| { | |
| PostDominatorAnalysis dom_tree; | |
| const CFG& cfg = *context->cfg(); | |
| dom_tree.InitializeTree(cfg, fn); | |
| // Inspect the actual tree | |
| 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<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << text << std::endl; | |
| const Function* fn = spvtest::GetFunction(module, 1); | |
| const BasicBlock* entry = spvtest::GetBasicBlock(fn, 10); | |
| EXPECT_EQ(entry, fn->entry().get()) | |
| << "The entry node is not the expected one"; | |
| // Check normal dominator tree | |
| { | |
| DominatorAnalysis dom_tree; | |
| const CFG& cfg = *context->cfg(); | |
| dom_tree.InitializeTree(cfg, fn); | |
| // Inspect the actual tree | |
| 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)); | |
| std::array<uint32_t, 3> node_order = {{10, 11, 12}}; | |
| { | |
| // Test dominator tree iteration order. | |
| DominatorTree::iterator node_it = dom_tree.GetDomTree().begin(); | |
| DominatorTree::iterator node_end = dom_tree.GetDomTree().end(); | |
| for (uint32_t id : node_order) { | |
| EXPECT_NE(node_it, node_end); | |
| EXPECT_EQ(node_it->id(), id); | |
| node_it++; | |
| } | |
| EXPECT_EQ(node_it, node_end); | |
| } | |
| { | |
| // Same as above, but with const iterators. | |
| DominatorTree::const_iterator node_it = dom_tree.GetDomTree().cbegin(); | |
| DominatorTree::const_iterator node_end = dom_tree.GetDomTree().cend(); | |
| for (uint32_t id : node_order) { | |
| EXPECT_NE(node_it, node_end); | |
| EXPECT_EQ(node_it->id(), id); | |
| node_it++; | |
| } | |
| EXPECT_EQ(node_it, node_end); | |
| } | |
| { | |
| // Test dominator tree iteration order. | |
| DominatorTree::post_iterator node_it = dom_tree.GetDomTree().post_begin(); | |
| DominatorTree::post_iterator node_end = dom_tree.GetDomTree().post_end(); | |
| for (uint32_t id : make_range(node_order.rbegin(), node_order.rend())) { | |
| EXPECT_NE(node_it, node_end); | |
| EXPECT_EQ(node_it->id(), id); | |
| node_it++; | |
| } | |
| EXPECT_EQ(node_it, node_end); | |
| } | |
| { | |
| // Same as above, but with const iterators. | |
| DominatorTree::const_post_iterator node_it = | |
| dom_tree.GetDomTree().post_cbegin(); | |
| DominatorTree::const_post_iterator node_end = | |
| dom_tree.GetDomTree().post_cend(); | |
| for (uint32_t id : make_range(node_order.rbegin(), node_order.rend())) { | |
| EXPECT_NE(node_it, node_end); | |
| EXPECT_EQ(node_it->id(), id); | |
| node_it++; | |
| } | |
| EXPECT_EQ(node_it, node_end); | |
| } | |
| } | |
| // Check post dominator tree | |
| { | |
| PostDominatorAnalysis dom_tree; | |
| const CFG& cfg = *context->cfg(); | |
| dom_tree.InitializeTree(cfg, fn); | |
| // Inspect the actual tree | |
| 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()); | |
| std::array<uint32_t, 3> node_order = {{12, 11, 10}}; | |
| { | |
| // Test dominator tree iteration order. | |
| DominatorTree::iterator node_it = tree.begin(); | |
| DominatorTree::iterator node_end = tree.end(); | |
| for (uint32_t id : node_order) { | |
| EXPECT_NE(node_it, node_end); | |
| EXPECT_EQ(node_it->id(), id); | |
| node_it++; | |
| } | |
| EXPECT_EQ(node_it, node_end); | |
| } | |
| { | |
| // Same as above, but with const iterators. | |
| DominatorTree::const_iterator node_it = tree.cbegin(); | |
| DominatorTree::const_iterator node_end = tree.cend(); | |
| for (uint32_t id : node_order) { | |
| EXPECT_NE(node_it, node_end); | |
| EXPECT_EQ(node_it->id(), id); | |
| node_it++; | |
| } | |
| EXPECT_EQ(node_it, node_end); | |
| } | |
| { | |
| // Test dominator tree iteration order. | |
| DominatorTree::post_iterator node_it = dom_tree.GetDomTree().post_begin(); | |
| DominatorTree::post_iterator node_end = dom_tree.GetDomTree().post_end(); | |
| for (uint32_t id : make_range(node_order.rbegin(), node_order.rend())) { | |
| EXPECT_NE(node_it, node_end); | |
| EXPECT_EQ(node_it->id(), id); | |
| node_it++; | |
| } | |
| EXPECT_EQ(node_it, node_end); | |
| } | |
| { | |
| // Same as above, but with const iterators. | |
| DominatorTree::const_post_iterator node_it = | |
| dom_tree.GetDomTree().post_cbegin(); | |
| DominatorTree::const_post_iterator node_end = | |
| dom_tree.GetDomTree().post_cend(); | |
| for (uint32_t id : make_range(node_order.rbegin(), node_order.rend())) { | |
| EXPECT_NE(node_it, node_end); | |
| EXPECT_EQ(node_it->id(), id); | |
| node_it++; | |
| } | |
| EXPECT_EQ(node_it, node_end); | |
| } | |
| } | |
| } | |
| 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<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << text << std::endl; | |
| const Function* fn = spvtest::GetFunction(module, 1); | |
| const BasicBlock* entry = spvtest::GetBasicBlock(fn, 10); | |
| EXPECT_EQ(entry, fn->entry().get()) | |
| << "The entry node is not the expected one"; | |
| // Check normal dominator tree | |
| { | |
| DominatorAnalysis dom_tree; | |
| const CFG& cfg = *context->cfg(); | |
| dom_tree.InitializeTree(cfg, fn); | |
| // Inspect the actual tree | |
| 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. | |
| { | |
| PostDominatorAnalysis dom_tree; | |
| const CFG& cfg = *context->cfg(); | |
| dom_tree.InitializeTree(cfg, fn); | |
| // (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<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << text << std::endl; | |
| const Function* fn = spvtest::GetFunction(module, 1); | |
| const BasicBlock* entry = spvtest::GetBasicBlock(fn, 10); | |
| EXPECT_EQ(entry, fn->entry().get()) | |
| << "The entry node is not the expected one"; | |
| // Check normal dominator tree | |
| { | |
| DominatorAnalysis dom_tree; | |
| const CFG& cfg = *context->cfg(); | |
| dom_tree.InitializeTree(cfg, fn); | |
| // Inspect the actual tree | |
| 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 | |
| { | |
| PostDominatorAnalysis dom_tree; | |
| const CFG& cfg = *context->cfg(); | |
| dom_tree.InitializeTree(cfg, fn); | |
| // Inspect the actual tree | |
| 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<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << text << std::endl; | |
| const Function* fn = spvtest::GetFunction(module, 1); | |
| const BasicBlock* entry = spvtest::GetBasicBlock(fn, 8); | |
| EXPECT_EQ(entry, fn->entry().get()) | |
| << "The entry node is not the expected one"; | |
| // Check dominator tree | |
| { | |
| DominatorAnalysis dom_tree; | |
| const CFG& cfg = *context->cfg(); | |
| dom_tree.InitializeTree(cfg, fn); | |
| // Inspect the actual tree | |
| 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 | |
| { | |
| PostDominatorAnalysis dom_tree; | |
| const CFG& cfg = *context->cfg(); | |
| dom_tree.InitializeTree(cfg, fn); | |
| // Inspect the actual tree | |
| 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)); | |
| } | |
| } | |
| TEST_F(PassClassTest, DominationForInstructions) { | |
| const std::string text = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %7 = OpTypeBool | |
| %8 = OpConstantTrue %7 | |
| %9 = OpConstant %6 37 | |
| %10 = OpConstant %6 3 | |
| %13 = OpConstant %6 5 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| %12 = OpIAdd %6 %9 %10 | |
| %15 = OpISub %6 %12 %13 | |
| OpSelectionMerge %18 None | |
| OpBranchConditional %8 %16 %17 | |
| %16 = OpLabel | |
| %20 = OpISub %6 %12 %13 | |
| OpBranch %18 | |
| %17 = OpLabel | |
| %21 = OpISub %6 %12 %13 | |
| OpBranch %18 | |
| %18 = OpLabel | |
| %22 = OpISub %6 %12 %13 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| EXPECT_NE(nullptr, context->module()) << "Assembling failed for shader:\n" | |
| << text << std::endl; | |
| { | |
| const DominatorAnalysis* dominator_analysis = context->GetDominatorAnalysis( | |
| spvtest::GetFunction(context->module(), 4)); | |
| EXPECT_TRUE( | |
| dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(12), | |
| context->get_def_use_mgr()->GetDef(15))); | |
| EXPECT_FALSE( | |
| dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15), | |
| context->get_def_use_mgr()->GetDef(12))); | |
| EXPECT_TRUE( | |
| dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(5), | |
| context->get_def_use_mgr()->GetDef(12))); | |
| EXPECT_FALSE( | |
| dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(12), | |
| context->get_def_use_mgr()->GetDef(5))); | |
| EXPECT_TRUE( | |
| dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15), | |
| context->get_def_use_mgr()->GetDef(16))); | |
| EXPECT_TRUE( | |
| dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15), | |
| context->get_def_use_mgr()->GetDef(21))); | |
| EXPECT_TRUE( | |
| dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15), | |
| context->get_def_use_mgr()->GetDef(18))); | |
| EXPECT_TRUE( | |
| dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15), | |
| context->get_def_use_mgr()->GetDef(22))); | |
| EXPECT_FALSE( | |
| dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(20), | |
| context->get_def_use_mgr()->GetDef(22))); | |
| EXPECT_FALSE( | |
| dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(21), | |
| context->get_def_use_mgr()->GetDef(22))); | |
| EXPECT_TRUE( | |
| dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15), | |
| context->get_def_use_mgr()->GetDef(15))); | |
| } | |
| { | |
| const PostDominatorAnalysis* post_dominator_analysis = | |
| context->GetPostDominatorAnalysis( | |
| spvtest::GetFunction(context->module(), 4)); | |
| EXPECT_TRUE(post_dominator_analysis->Dominates( | |
| context->get_def_use_mgr()->GetDef(15), | |
| context->get_def_use_mgr()->GetDef(12))); | |
| EXPECT_FALSE(post_dominator_analysis->Dominates( | |
| context->get_def_use_mgr()->GetDef(12), | |
| context->get_def_use_mgr()->GetDef(15))); | |
| EXPECT_TRUE(post_dominator_analysis->Dominates( | |
| context->get_def_use_mgr()->GetDef(12), | |
| context->get_def_use_mgr()->GetDef(5))); | |
| EXPECT_FALSE(post_dominator_analysis->Dominates( | |
| context->get_def_use_mgr()->GetDef(5), | |
| context->get_def_use_mgr()->GetDef(12))); | |
| EXPECT_FALSE(post_dominator_analysis->Dominates( | |
| context->get_def_use_mgr()->GetDef(16), | |
| context->get_def_use_mgr()->GetDef(15))); | |
| EXPECT_FALSE(post_dominator_analysis->Dominates( | |
| context->get_def_use_mgr()->GetDef(21), | |
| context->get_def_use_mgr()->GetDef(15))); | |
| EXPECT_TRUE(post_dominator_analysis->Dominates( | |
| context->get_def_use_mgr()->GetDef(18), | |
| context->get_def_use_mgr()->GetDef(15))); | |
| EXPECT_TRUE(post_dominator_analysis->Dominates( | |
| context->get_def_use_mgr()->GetDef(22), | |
| context->get_def_use_mgr()->GetDef(15))); | |
| EXPECT_TRUE(post_dominator_analysis->Dominates( | |
| context->get_def_use_mgr()->GetDef(22), | |
| context->get_def_use_mgr()->GetDef(20))); | |
| EXPECT_TRUE(post_dominator_analysis->Dominates( | |
| context->get_def_use_mgr()->GetDef(22), | |
| context->get_def_use_mgr()->GetDef(21))); | |
| EXPECT_TRUE(post_dominator_analysis->Dominates( | |
| context->get_def_use_mgr()->GetDef(15), | |
| context->get_def_use_mgr()->GetDef(15))); | |
| } | |
| } | |
| } // namespace | |
| } // namespace opt | |
| } // namespace spvtools |