1fd4e5da5Sopenharmony_ci// Copyright (c) 2021 Google LLC. 2fd4e5da5Sopenharmony_ci// 3fd4e5da5Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License"); 4fd4e5da5Sopenharmony_ci// you may not use this file except in compliance with the License. 5fd4e5da5Sopenharmony_ci// You may obtain a copy of the License at 6fd4e5da5Sopenharmony_ci// 7fd4e5da5Sopenharmony_ci// http://www.apache.org/licenses/LICENSE-2.0 8fd4e5da5Sopenharmony_ci// 9fd4e5da5Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software 10fd4e5da5Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS, 11fd4e5da5Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12fd4e5da5Sopenharmony_ci// See the License for the specific language governing permissions and 13fd4e5da5Sopenharmony_ci// limitations under the License. 14fd4e5da5Sopenharmony_ci 15fd4e5da5Sopenharmony_ci#include "source/opt/control_dependence.h" 16fd4e5da5Sopenharmony_ci 17fd4e5da5Sopenharmony_ci#include <algorithm> 18fd4e5da5Sopenharmony_ci#include <vector> 19fd4e5da5Sopenharmony_ci 20fd4e5da5Sopenharmony_ci#include "gmock/gmock-matchers.h" 21fd4e5da5Sopenharmony_ci#include "gtest/gtest.h" 22fd4e5da5Sopenharmony_ci#include "source/opt/build_module.h" 23fd4e5da5Sopenharmony_ci#include "source/opt/cfg.h" 24fd4e5da5Sopenharmony_ci#include "test/opt/function_utils.h" 25fd4e5da5Sopenharmony_ci 26fd4e5da5Sopenharmony_cinamespace spvtools { 27fd4e5da5Sopenharmony_cinamespace opt { 28fd4e5da5Sopenharmony_ci 29fd4e5da5Sopenharmony_cinamespace { 30fd4e5da5Sopenharmony_civoid GatherEdges(const ControlDependenceAnalysis& cdg, 31fd4e5da5Sopenharmony_ci std::vector<ControlDependence>& ret) { 32fd4e5da5Sopenharmony_ci cdg.ForEachBlockLabel([&](uint32_t label) { 33fd4e5da5Sopenharmony_ci ret.reserve(ret.size() + cdg.GetDependenceTargets(label).size()); 34fd4e5da5Sopenharmony_ci ret.insert(ret.end(), cdg.GetDependenceTargets(label).begin(), 35fd4e5da5Sopenharmony_ci cdg.GetDependenceTargets(label).end()); 36fd4e5da5Sopenharmony_ci }); 37fd4e5da5Sopenharmony_ci std::sort(ret.begin(), ret.end()); 38fd4e5da5Sopenharmony_ci // Verify that reverse graph is the same. 39fd4e5da5Sopenharmony_ci std::vector<ControlDependence> reverse_edges; 40fd4e5da5Sopenharmony_ci reverse_edges.reserve(ret.size()); 41fd4e5da5Sopenharmony_ci cdg.ForEachBlockLabel([&](uint32_t label) { 42fd4e5da5Sopenharmony_ci reverse_edges.insert(reverse_edges.end(), 43fd4e5da5Sopenharmony_ci cdg.GetDependenceSources(label).begin(), 44fd4e5da5Sopenharmony_ci cdg.GetDependenceSources(label).end()); 45fd4e5da5Sopenharmony_ci }); 46fd4e5da5Sopenharmony_ci std::sort(reverse_edges.begin(), reverse_edges.end()); 47fd4e5da5Sopenharmony_ci ASSERT_THAT(reverse_edges, testing::ElementsAreArray(ret)); 48fd4e5da5Sopenharmony_ci} 49fd4e5da5Sopenharmony_ci 50fd4e5da5Sopenharmony_ciusing ControlDependenceTest = ::testing::Test; 51fd4e5da5Sopenharmony_ci 52fd4e5da5Sopenharmony_ciTEST(ControlDependenceTest, DependenceSimpleCFG) { 53fd4e5da5Sopenharmony_ci const std::string text = R"( 54fd4e5da5Sopenharmony_ci OpCapability Addresses 55fd4e5da5Sopenharmony_ci OpCapability Kernel 56fd4e5da5Sopenharmony_ci OpMemoryModel Physical64 OpenCL 57fd4e5da5Sopenharmony_ci OpEntryPoint Kernel %1 "main" 58fd4e5da5Sopenharmony_ci %2 = OpTypeVoid 59fd4e5da5Sopenharmony_ci %3 = OpTypeFunction %2 60fd4e5da5Sopenharmony_ci %4 = OpTypeBool 61fd4e5da5Sopenharmony_ci %5 = OpTypeInt 32 0 62fd4e5da5Sopenharmony_ci %6 = OpConstant %5 0 63fd4e5da5Sopenharmony_ci %7 = OpConstantFalse %4 64fd4e5da5Sopenharmony_ci %8 = OpConstantTrue %4 65fd4e5da5Sopenharmony_ci %9 = OpConstant %5 1 66fd4e5da5Sopenharmony_ci %1 = OpFunction %2 None %3 67fd4e5da5Sopenharmony_ci %10 = OpLabel 68fd4e5da5Sopenharmony_ci OpBranch %11 69fd4e5da5Sopenharmony_ci %11 = OpLabel 70fd4e5da5Sopenharmony_ci OpSwitch %6 %12 1 %13 71fd4e5da5Sopenharmony_ci %12 = OpLabel 72fd4e5da5Sopenharmony_ci OpBranch %14 73fd4e5da5Sopenharmony_ci %13 = OpLabel 74fd4e5da5Sopenharmony_ci OpBranch %14 75fd4e5da5Sopenharmony_ci %14 = OpLabel 76fd4e5da5Sopenharmony_ci OpBranchConditional %8 %15 %16 77fd4e5da5Sopenharmony_ci %15 = OpLabel 78fd4e5da5Sopenharmony_ci OpBranch %19 79fd4e5da5Sopenharmony_ci %16 = OpLabel 80fd4e5da5Sopenharmony_ci OpBranchConditional %8 %17 %18 81fd4e5da5Sopenharmony_ci %17 = OpLabel 82fd4e5da5Sopenharmony_ci OpBranch %18 83fd4e5da5Sopenharmony_ci %18 = OpLabel 84fd4e5da5Sopenharmony_ci OpBranch %19 85fd4e5da5Sopenharmony_ci %19 = OpLabel 86fd4e5da5Sopenharmony_ci OpReturn 87fd4e5da5Sopenharmony_ci OpFunctionEnd 88fd4e5da5Sopenharmony_ci)"; 89fd4e5da5Sopenharmony_ci 90fd4e5da5Sopenharmony_ci // CFG: (all edges pointing downward) 91fd4e5da5Sopenharmony_ci // %10 92fd4e5da5Sopenharmony_ci // | 93fd4e5da5Sopenharmony_ci // %11 94fd4e5da5Sopenharmony_ci // / \ (R: %6 == 1, L: default) 95fd4e5da5Sopenharmony_ci // %12 %13 96fd4e5da5Sopenharmony_ci // \ / 97fd4e5da5Sopenharmony_ci // %14 98fd4e5da5Sopenharmony_ci // T/ \F 99fd4e5da5Sopenharmony_ci // %15 %16 100fd4e5da5Sopenharmony_ci // | T/ |F 101fd4e5da5Sopenharmony_ci // | %17| 102fd4e5da5Sopenharmony_ci // | \ | 103fd4e5da5Sopenharmony_ci // | %18 104fd4e5da5Sopenharmony_ci // | / 105fd4e5da5Sopenharmony_ci // %19 106fd4e5da5Sopenharmony_ci 107fd4e5da5Sopenharmony_ci std::unique_ptr<IRContext> context = 108fd4e5da5Sopenharmony_ci BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, 109fd4e5da5Sopenharmony_ci SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 110fd4e5da5Sopenharmony_ci Module* module = context->module(); 111fd4e5da5Sopenharmony_ci EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 112fd4e5da5Sopenharmony_ci << text << std::endl; 113fd4e5da5Sopenharmony_ci const Function* fn = spvtest::GetFunction(module, 1); 114fd4e5da5Sopenharmony_ci const BasicBlock* entry = spvtest::GetBasicBlock(fn, 10); 115fd4e5da5Sopenharmony_ci EXPECT_EQ(entry, fn->entry().get()) 116fd4e5da5Sopenharmony_ci << "The entry node is not the expected one"; 117fd4e5da5Sopenharmony_ci 118fd4e5da5Sopenharmony_ci { 119fd4e5da5Sopenharmony_ci PostDominatorAnalysis pdom; 120fd4e5da5Sopenharmony_ci const CFG& cfg = *context->cfg(); 121fd4e5da5Sopenharmony_ci pdom.InitializeTree(cfg, fn); 122fd4e5da5Sopenharmony_ci ControlDependenceAnalysis cdg; 123fd4e5da5Sopenharmony_ci cdg.ComputeControlDependenceGraph(cfg, pdom); 124fd4e5da5Sopenharmony_ci 125fd4e5da5Sopenharmony_ci // Test HasBlock. 126fd4e5da5Sopenharmony_ci for (uint32_t id = 10; id <= 19; id++) { 127fd4e5da5Sopenharmony_ci EXPECT_TRUE(cdg.HasBlock(id)); 128fd4e5da5Sopenharmony_ci } 129fd4e5da5Sopenharmony_ci EXPECT_TRUE(cdg.HasBlock(ControlDependenceAnalysis::kPseudoEntryBlock)); 130fd4e5da5Sopenharmony_ci // Check blocks before/after valid range. 131fd4e5da5Sopenharmony_ci EXPECT_FALSE(cdg.HasBlock(5)); 132fd4e5da5Sopenharmony_ci EXPECT_FALSE(cdg.HasBlock(25)); 133fd4e5da5Sopenharmony_ci EXPECT_FALSE(cdg.HasBlock(UINT32_MAX)); 134fd4e5da5Sopenharmony_ci 135fd4e5da5Sopenharmony_ci // Test ForEachBlockLabel. 136fd4e5da5Sopenharmony_ci std::set<uint32_t> block_labels; 137fd4e5da5Sopenharmony_ci cdg.ForEachBlockLabel([&block_labels](uint32_t id) { 138fd4e5da5Sopenharmony_ci bool inserted = block_labels.insert(id).second; 139fd4e5da5Sopenharmony_ci EXPECT_TRUE(inserted); // Should have no duplicates. 140fd4e5da5Sopenharmony_ci }); 141fd4e5da5Sopenharmony_ci EXPECT_THAT(block_labels, testing::ElementsAre(0, 10, 11, 12, 13, 14, 15, 142fd4e5da5Sopenharmony_ci 16, 17, 18, 19)); 143fd4e5da5Sopenharmony_ci 144fd4e5da5Sopenharmony_ci { 145fd4e5da5Sopenharmony_ci // Test WhileEachBlockLabel. 146fd4e5da5Sopenharmony_ci uint32_t iters = 0; 147fd4e5da5Sopenharmony_ci EXPECT_TRUE(cdg.WhileEachBlockLabel([&iters](uint32_t) { 148fd4e5da5Sopenharmony_ci ++iters; 149fd4e5da5Sopenharmony_ci return true; 150fd4e5da5Sopenharmony_ci })); 151fd4e5da5Sopenharmony_ci EXPECT_EQ((uint32_t)block_labels.size(), iters); 152fd4e5da5Sopenharmony_ci iters = 0; 153fd4e5da5Sopenharmony_ci EXPECT_FALSE(cdg.WhileEachBlockLabel([&iters](uint32_t) { 154fd4e5da5Sopenharmony_ci ++iters; 155fd4e5da5Sopenharmony_ci return false; 156fd4e5da5Sopenharmony_ci })); 157fd4e5da5Sopenharmony_ci EXPECT_EQ(1, iters); 158fd4e5da5Sopenharmony_ci } 159fd4e5da5Sopenharmony_ci 160fd4e5da5Sopenharmony_ci // Test IsDependent. 161fd4e5da5Sopenharmony_ci EXPECT_TRUE(cdg.IsDependent(12, 11)); 162fd4e5da5Sopenharmony_ci EXPECT_TRUE(cdg.IsDependent(13, 11)); 163fd4e5da5Sopenharmony_ci EXPECT_TRUE(cdg.IsDependent(15, 14)); 164fd4e5da5Sopenharmony_ci EXPECT_TRUE(cdg.IsDependent(16, 14)); 165fd4e5da5Sopenharmony_ci EXPECT_TRUE(cdg.IsDependent(18, 14)); 166fd4e5da5Sopenharmony_ci EXPECT_TRUE(cdg.IsDependent(17, 16)); 167fd4e5da5Sopenharmony_ci EXPECT_TRUE(cdg.IsDependent(10, 0)); 168fd4e5da5Sopenharmony_ci EXPECT_TRUE(cdg.IsDependent(11, 0)); 169fd4e5da5Sopenharmony_ci EXPECT_TRUE(cdg.IsDependent(14, 0)); 170fd4e5da5Sopenharmony_ci EXPECT_TRUE(cdg.IsDependent(19, 0)); 171fd4e5da5Sopenharmony_ci EXPECT_FALSE(cdg.IsDependent(14, 11)); 172fd4e5da5Sopenharmony_ci EXPECT_FALSE(cdg.IsDependent(17, 14)); 173fd4e5da5Sopenharmony_ci EXPECT_FALSE(cdg.IsDependent(19, 14)); 174fd4e5da5Sopenharmony_ci EXPECT_FALSE(cdg.IsDependent(12, 0)); 175fd4e5da5Sopenharmony_ci 176fd4e5da5Sopenharmony_ci // Test GetDependenceSources/Targets. 177fd4e5da5Sopenharmony_ci std::vector<ControlDependence> edges; 178fd4e5da5Sopenharmony_ci GatherEdges(cdg, edges); 179fd4e5da5Sopenharmony_ci EXPECT_THAT(edges, 180fd4e5da5Sopenharmony_ci testing::ElementsAre( 181fd4e5da5Sopenharmony_ci ControlDependence(0, 10), ControlDependence(0, 11, 10), 182fd4e5da5Sopenharmony_ci ControlDependence(0, 14, 10), ControlDependence(0, 19, 10), 183fd4e5da5Sopenharmony_ci ControlDependence(11, 12), ControlDependence(11, 13), 184fd4e5da5Sopenharmony_ci ControlDependence(14, 15), ControlDependence(14, 16), 185fd4e5da5Sopenharmony_ci ControlDependence(14, 18, 16), ControlDependence(16, 17))); 186fd4e5da5Sopenharmony_ci 187fd4e5da5Sopenharmony_ci const uint32_t expected_condition_ids[] = { 188fd4e5da5Sopenharmony_ci 0, 0, 0, 0, 6, 6, 8, 8, 8, 8, 189fd4e5da5Sopenharmony_ci }; 190fd4e5da5Sopenharmony_ci 191fd4e5da5Sopenharmony_ci for (uint32_t i = 0; i < edges.size(); i++) { 192fd4e5da5Sopenharmony_ci EXPECT_EQ(expected_condition_ids[i], edges[i].GetConditionID(cfg)); 193fd4e5da5Sopenharmony_ci } 194fd4e5da5Sopenharmony_ci } 195fd4e5da5Sopenharmony_ci} 196fd4e5da5Sopenharmony_ci 197fd4e5da5Sopenharmony_ciTEST(ControlDependenceTest, DependencePaperCFG) { 198fd4e5da5Sopenharmony_ci const std::string text = R"( 199fd4e5da5Sopenharmony_ci OpCapability Addresses 200fd4e5da5Sopenharmony_ci OpCapability Kernel 201fd4e5da5Sopenharmony_ci OpMemoryModel Physical64 OpenCL 202fd4e5da5Sopenharmony_ci OpEntryPoint Kernel %101 "main" 203fd4e5da5Sopenharmony_ci %102 = OpTypeVoid 204fd4e5da5Sopenharmony_ci %103 = OpTypeFunction %102 205fd4e5da5Sopenharmony_ci %104 = OpTypeBool 206fd4e5da5Sopenharmony_ci %108 = OpConstantTrue %104 207fd4e5da5Sopenharmony_ci %101 = OpFunction %102 None %103 208fd4e5da5Sopenharmony_ci %1 = OpLabel 209fd4e5da5Sopenharmony_ci OpBranch %2 210fd4e5da5Sopenharmony_ci %2 = OpLabel 211fd4e5da5Sopenharmony_ci OpBranchConditional %108 %3 %7 212fd4e5da5Sopenharmony_ci %3 = OpLabel 213fd4e5da5Sopenharmony_ci OpBranchConditional %108 %4 %5 214fd4e5da5Sopenharmony_ci %4 = OpLabel 215fd4e5da5Sopenharmony_ci OpBranch %6 216fd4e5da5Sopenharmony_ci %5 = OpLabel 217fd4e5da5Sopenharmony_ci OpBranch %6 218fd4e5da5Sopenharmony_ci %6 = OpLabel 219fd4e5da5Sopenharmony_ci OpBranch %8 220fd4e5da5Sopenharmony_ci %7 = OpLabel 221fd4e5da5Sopenharmony_ci OpBranch %8 222fd4e5da5Sopenharmony_ci %8 = OpLabel 223fd4e5da5Sopenharmony_ci OpBranch %9 224fd4e5da5Sopenharmony_ci %9 = OpLabel 225fd4e5da5Sopenharmony_ci OpBranchConditional %108 %10 %11 226fd4e5da5Sopenharmony_ci %10 = OpLabel 227fd4e5da5Sopenharmony_ci OpBranch %11 228fd4e5da5Sopenharmony_ci %11 = OpLabel 229fd4e5da5Sopenharmony_ci OpBranchConditional %108 %12 %9 230fd4e5da5Sopenharmony_ci %12 = OpLabel 231fd4e5da5Sopenharmony_ci OpBranchConditional %108 %13 %2 232fd4e5da5Sopenharmony_ci %13 = OpLabel 233fd4e5da5Sopenharmony_ci OpReturn 234fd4e5da5Sopenharmony_ci OpFunctionEnd 235fd4e5da5Sopenharmony_ci)"; 236fd4e5da5Sopenharmony_ci 237fd4e5da5Sopenharmony_ci // CFG: (edges pointing downward if no arrow) 238fd4e5da5Sopenharmony_ci // %1 239fd4e5da5Sopenharmony_ci // | 240fd4e5da5Sopenharmony_ci // %2 <----+ 241fd4e5da5Sopenharmony_ci // T/ \F | 242fd4e5da5Sopenharmony_ci // %3 \ | 243fd4e5da5Sopenharmony_ci // T/ \F \ | 244fd4e5da5Sopenharmony_ci // %4 %5 %7 | 245fd4e5da5Sopenharmony_ci // \ / / | 246fd4e5da5Sopenharmony_ci // %6 / | 247fd4e5da5Sopenharmony_ci // \ / | 248fd4e5da5Sopenharmony_ci // %8 | 249fd4e5da5Sopenharmony_ci // | | 250fd4e5da5Sopenharmony_ci // %9 <-+ | 251fd4e5da5Sopenharmony_ci // T/ | | | 252fd4e5da5Sopenharmony_ci // %10 | | | 253fd4e5da5Sopenharmony_ci // \ | | | 254fd4e5da5Sopenharmony_ci // %11-F+ | 255fd4e5da5Sopenharmony_ci // T| | 256fd4e5da5Sopenharmony_ci // %12-F---+ 257fd4e5da5Sopenharmony_ci // T| 258fd4e5da5Sopenharmony_ci // %13 259fd4e5da5Sopenharmony_ci 260fd4e5da5Sopenharmony_ci std::unique_ptr<IRContext> context = 261fd4e5da5Sopenharmony_ci BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, 262fd4e5da5Sopenharmony_ci SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 263fd4e5da5Sopenharmony_ci Module* module = context->module(); 264fd4e5da5Sopenharmony_ci EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 265fd4e5da5Sopenharmony_ci << text << std::endl; 266fd4e5da5Sopenharmony_ci const Function* fn = spvtest::GetFunction(module, 101); 267fd4e5da5Sopenharmony_ci const BasicBlock* entry = spvtest::GetBasicBlock(fn, 1); 268fd4e5da5Sopenharmony_ci EXPECT_EQ(entry, fn->entry().get()) 269fd4e5da5Sopenharmony_ci << "The entry node is not the expected one"; 270fd4e5da5Sopenharmony_ci 271fd4e5da5Sopenharmony_ci { 272fd4e5da5Sopenharmony_ci PostDominatorAnalysis pdom; 273fd4e5da5Sopenharmony_ci const CFG& cfg = *context->cfg(); 274fd4e5da5Sopenharmony_ci pdom.InitializeTree(cfg, fn); 275fd4e5da5Sopenharmony_ci ControlDependenceAnalysis cdg; 276fd4e5da5Sopenharmony_ci cdg.ComputeControlDependenceGraph(cfg, pdom); 277fd4e5da5Sopenharmony_ci 278fd4e5da5Sopenharmony_ci std::vector<ControlDependence> edges; 279fd4e5da5Sopenharmony_ci GatherEdges(cdg, edges); 280fd4e5da5Sopenharmony_ci EXPECT_THAT( 281fd4e5da5Sopenharmony_ci edges, testing::ElementsAre( 282fd4e5da5Sopenharmony_ci ControlDependence(0, 1), ControlDependence(0, 2, 1), 283fd4e5da5Sopenharmony_ci ControlDependence(0, 8, 1), ControlDependence(0, 9, 1), 284fd4e5da5Sopenharmony_ci ControlDependence(0, 11, 1), ControlDependence(0, 12, 1), 285fd4e5da5Sopenharmony_ci ControlDependence(0, 13, 1), ControlDependence(2, 3), 286fd4e5da5Sopenharmony_ci ControlDependence(2, 6, 3), ControlDependence(2, 7), 287fd4e5da5Sopenharmony_ci ControlDependence(3, 4), ControlDependence(3, 5), 288fd4e5da5Sopenharmony_ci ControlDependence(9, 10), ControlDependence(11, 9), 289fd4e5da5Sopenharmony_ci ControlDependence(11, 11, 9), ControlDependence(12, 2), 290fd4e5da5Sopenharmony_ci ControlDependence(12, 8, 2), ControlDependence(12, 9, 2), 291fd4e5da5Sopenharmony_ci ControlDependence(12, 11, 2), ControlDependence(12, 12, 2))); 292fd4e5da5Sopenharmony_ci 293fd4e5da5Sopenharmony_ci const uint32_t expected_condition_ids[] = { 294fd4e5da5Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 108, 108, 108, 295fd4e5da5Sopenharmony_ci 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 296fd4e5da5Sopenharmony_ci }; 297fd4e5da5Sopenharmony_ci 298fd4e5da5Sopenharmony_ci for (uint32_t i = 0; i < edges.size(); i++) { 299fd4e5da5Sopenharmony_ci EXPECT_EQ(expected_condition_ids[i], edges[i].GetConditionID(cfg)); 300fd4e5da5Sopenharmony_ci } 301fd4e5da5Sopenharmony_ci } 302fd4e5da5Sopenharmony_ci} 303fd4e5da5Sopenharmony_ci 304fd4e5da5Sopenharmony_ci} // namespace 305fd4e5da5Sopenharmony_ci} // namespace opt 306fd4e5da5Sopenharmony_ci} // namespace spvtools 307