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