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/dataflow.h"
16fd4e5da5Sopenharmony_ci
17fd4e5da5Sopenharmony_ci#include <map>
18fd4e5da5Sopenharmony_ci#include <set>
19fd4e5da5Sopenharmony_ci
20fd4e5da5Sopenharmony_ci#include "gtest/gtest.h"
21fd4e5da5Sopenharmony_ci#include "opt/function_utils.h"
22fd4e5da5Sopenharmony_ci#include "source/opt/build_module.h"
23fd4e5da5Sopenharmony_ci
24fd4e5da5Sopenharmony_cinamespace spvtools {
25fd4e5da5Sopenharmony_cinamespace opt {
26fd4e5da5Sopenharmony_cinamespace {
27fd4e5da5Sopenharmony_ci
28fd4e5da5Sopenharmony_ciusing DataFlowTest = ::testing::Test;
29fd4e5da5Sopenharmony_ci
30fd4e5da5Sopenharmony_ci// Simple analyses for testing:
31fd4e5da5Sopenharmony_ci
32fd4e5da5Sopenharmony_ci// Stores the result IDs of visited instructions in visit order.
33fd4e5da5Sopenharmony_cistruct VisitOrder : public ForwardDataFlowAnalysis {
34fd4e5da5Sopenharmony_ci  std::vector<uint32_t> visited_result_ids;
35fd4e5da5Sopenharmony_ci
36fd4e5da5Sopenharmony_ci  VisitOrder(IRContext& context, LabelPosition label_position)
37fd4e5da5Sopenharmony_ci      : ForwardDataFlowAnalysis(context, label_position) {}
38fd4e5da5Sopenharmony_ci
39fd4e5da5Sopenharmony_ci  VisitResult Visit(Instruction* inst) override {
40fd4e5da5Sopenharmony_ci    if (inst->HasResultId()) {
41fd4e5da5Sopenharmony_ci      visited_result_ids.push_back(inst->result_id());
42fd4e5da5Sopenharmony_ci    }
43fd4e5da5Sopenharmony_ci    return DataFlowAnalysis::VisitResult::kResultFixed;
44fd4e5da5Sopenharmony_ci  }
45fd4e5da5Sopenharmony_ci};
46fd4e5da5Sopenharmony_ci
47fd4e5da5Sopenharmony_ci// For each block, stores the set of blocks it can be preceded by.
48fd4e5da5Sopenharmony_ci// For example, with the following CFG:
49fd4e5da5Sopenharmony_ci//    V-----------.
50fd4e5da5Sopenharmony_ci// -> 11 -> 12 -> 13 -> 15
51fd4e5da5Sopenharmony_ci//            \-> 14 ---^
52fd4e5da5Sopenharmony_ci//
53fd4e5da5Sopenharmony_ci// The answer is:
54fd4e5da5Sopenharmony_ci// 11: 11, 12, 13
55fd4e5da5Sopenharmony_ci// 12: 11, 12, 13
56fd4e5da5Sopenharmony_ci// 13: 11, 12, 13
57fd4e5da5Sopenharmony_ci// 14: 11, 12, 13
58fd4e5da5Sopenharmony_ci// 15: 11, 12, 13, 14
59fd4e5da5Sopenharmony_cistruct BackwardReachability : public ForwardDataFlowAnalysis {
60fd4e5da5Sopenharmony_ci  std::map<uint32_t, std::set<uint32_t>> reachable_from;
61fd4e5da5Sopenharmony_ci
62fd4e5da5Sopenharmony_ci  BackwardReachability(IRContext& context)
63fd4e5da5Sopenharmony_ci      : ForwardDataFlowAnalysis(
64fd4e5da5Sopenharmony_ci            context, ForwardDataFlowAnalysis::LabelPosition::kLabelsOnly) {}
65fd4e5da5Sopenharmony_ci
66fd4e5da5Sopenharmony_ci  VisitResult Visit(Instruction* inst) override {
67fd4e5da5Sopenharmony_ci    // Conditional branches can be enqueued from labels, so skip them.
68fd4e5da5Sopenharmony_ci    if (inst->opcode() != spv::Op::OpLabel)
69fd4e5da5Sopenharmony_ci      return DataFlowAnalysis::VisitResult::kResultFixed;
70fd4e5da5Sopenharmony_ci    uint32_t id = inst->result_id();
71fd4e5da5Sopenharmony_ci    VisitResult ret = DataFlowAnalysis::VisitResult::kResultFixed;
72fd4e5da5Sopenharmony_ci    std::set<uint32_t>& precedents = reachable_from[id];
73fd4e5da5Sopenharmony_ci    for (uint32_t pred : context().cfg()->preds(id)) {
74fd4e5da5Sopenharmony_ci      bool pred_inserted = precedents.insert(pred).second;
75fd4e5da5Sopenharmony_ci      if (pred_inserted) {
76fd4e5da5Sopenharmony_ci        ret = DataFlowAnalysis::VisitResult::kResultChanged;
77fd4e5da5Sopenharmony_ci      }
78fd4e5da5Sopenharmony_ci      for (uint32_t block : reachable_from[pred]) {
79fd4e5da5Sopenharmony_ci        bool inserted = precedents.insert(block).second;
80fd4e5da5Sopenharmony_ci        if (inserted) {
81fd4e5da5Sopenharmony_ci          ret = DataFlowAnalysis::VisitResult::kResultChanged;
82fd4e5da5Sopenharmony_ci        }
83fd4e5da5Sopenharmony_ci      }
84fd4e5da5Sopenharmony_ci    }
85fd4e5da5Sopenharmony_ci    return ret;
86fd4e5da5Sopenharmony_ci  }
87fd4e5da5Sopenharmony_ci
88fd4e5da5Sopenharmony_ci  void InitializeWorklist(Function* function,
89fd4e5da5Sopenharmony_ci                          bool is_first_iteration) override {
90fd4e5da5Sopenharmony_ci    // Since successor function is exact, only need one pass.
91fd4e5da5Sopenharmony_ci    if (is_first_iteration) {
92fd4e5da5Sopenharmony_ci      ForwardDataFlowAnalysis::InitializeWorklist(function, true);
93fd4e5da5Sopenharmony_ci    }
94fd4e5da5Sopenharmony_ci  }
95fd4e5da5Sopenharmony_ci};
96fd4e5da5Sopenharmony_ci
97fd4e5da5Sopenharmony_ciTEST_F(DataFlowTest, ReversePostOrder) {
98fd4e5da5Sopenharmony_ci  // Note: labels and IDs are intentionally out of order.
99fd4e5da5Sopenharmony_ci  //
100fd4e5da5Sopenharmony_ci  // CFG: (order of branches is from bottom to top)
101fd4e5da5Sopenharmony_ci  //          V-----------.
102fd4e5da5Sopenharmony_ci  // -> 50 -> 40 -> 20 -> 60 -> 70
103fd4e5da5Sopenharmony_ci  //            \-> 30 ---^
104fd4e5da5Sopenharmony_ci
105fd4e5da5Sopenharmony_ci  // DFS tree with RPO numbering:
106fd4e5da5Sopenharmony_ci  // -> 50[0] -> 40[1] -> 20[2]    60[4] -> 70[5]
107fd4e5da5Sopenharmony_ci  //                  \-> 30[3] ---^
108fd4e5da5Sopenharmony_ci
109fd4e5da5Sopenharmony_ci  const std::string text = R"(
110fd4e5da5Sopenharmony_ci               OpCapability Shader
111fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
112fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
113fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %2 "main"
114fd4e5da5Sopenharmony_ci               OpExecutionMode %2 OriginUpperLeft
115fd4e5da5Sopenharmony_ci               OpSource GLSL 430
116fd4e5da5Sopenharmony_ci          %3 = OpTypeVoid
117fd4e5da5Sopenharmony_ci          %4 = OpTypeFunction %3
118fd4e5da5Sopenharmony_ci          %6 = OpTypeBool
119fd4e5da5Sopenharmony_ci          %5 = OpConstantTrue %6
120fd4e5da5Sopenharmony_ci          %2 = OpFunction %3 None %4
121fd4e5da5Sopenharmony_ci         %50 = OpLabel
122fd4e5da5Sopenharmony_ci         %51 = OpUndef %6
123fd4e5da5Sopenharmony_ci         %52 = OpUndef %6
124fd4e5da5Sopenharmony_ci               OpBranch %40
125fd4e5da5Sopenharmony_ci         %70 = OpLabel
126fd4e5da5Sopenharmony_ci         %69 = OpUndef %6
127fd4e5da5Sopenharmony_ci               OpReturn
128fd4e5da5Sopenharmony_ci         %60 = OpLabel
129fd4e5da5Sopenharmony_ci         %61 = OpUndef %6
130fd4e5da5Sopenharmony_ci               OpBranchConditional %5 %70 %40
131fd4e5da5Sopenharmony_ci         %30 = OpLabel
132fd4e5da5Sopenharmony_ci         %29 = OpUndef %6
133fd4e5da5Sopenharmony_ci               OpBranch %60
134fd4e5da5Sopenharmony_ci         %20 = OpLabel
135fd4e5da5Sopenharmony_ci         %21 = OpUndef %6
136fd4e5da5Sopenharmony_ci               OpBranch %60
137fd4e5da5Sopenharmony_ci         %40 = OpLabel
138fd4e5da5Sopenharmony_ci         %39 = OpUndef %6
139fd4e5da5Sopenharmony_ci               OpBranchConditional %5 %30 %20
140fd4e5da5Sopenharmony_ci               OpFunctionEnd
141fd4e5da5Sopenharmony_ci  )";
142fd4e5da5Sopenharmony_ci
143fd4e5da5Sopenharmony_ci  std::unique_ptr<IRContext> context =
144fd4e5da5Sopenharmony_ci      BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text,
145fd4e5da5Sopenharmony_ci                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
146fd4e5da5Sopenharmony_ci  ASSERT_NE(context, nullptr);
147fd4e5da5Sopenharmony_ci
148fd4e5da5Sopenharmony_ci  Function* function = spvtest::GetFunction(context->module(), 2);
149fd4e5da5Sopenharmony_ci
150fd4e5da5Sopenharmony_ci  std::map<ForwardDataFlowAnalysis::LabelPosition, std::vector<uint32_t>>
151fd4e5da5Sopenharmony_ci      expected_order;
152fd4e5da5Sopenharmony_ci  expected_order[ForwardDataFlowAnalysis::LabelPosition::kLabelsOnly] = {
153fd4e5da5Sopenharmony_ci      50, 40, 20, 30, 60, 70,
154fd4e5da5Sopenharmony_ci  };
155fd4e5da5Sopenharmony_ci  expected_order[ForwardDataFlowAnalysis::LabelPosition::kLabelsAtBeginning] = {
156fd4e5da5Sopenharmony_ci      50, 51, 52, 40, 39, 20, 21, 30, 29, 60, 61, 70, 69,
157fd4e5da5Sopenharmony_ci  };
158fd4e5da5Sopenharmony_ci  expected_order[ForwardDataFlowAnalysis::LabelPosition::kLabelsAtEnd] = {
159fd4e5da5Sopenharmony_ci      51, 52, 50, 39, 40, 21, 20, 29, 30, 61, 60, 69, 70,
160fd4e5da5Sopenharmony_ci  };
161fd4e5da5Sopenharmony_ci  expected_order[ForwardDataFlowAnalysis::LabelPosition::kNoLabels] = {
162fd4e5da5Sopenharmony_ci      51, 52, 39, 21, 29, 61, 69,
163fd4e5da5Sopenharmony_ci  };
164fd4e5da5Sopenharmony_ci
165fd4e5da5Sopenharmony_ci  for (const auto& test_case : expected_order) {
166fd4e5da5Sopenharmony_ci    VisitOrder analysis(*context, test_case.first);
167fd4e5da5Sopenharmony_ci    analysis.Run(function);
168fd4e5da5Sopenharmony_ci    EXPECT_EQ(test_case.second, analysis.visited_result_ids);
169fd4e5da5Sopenharmony_ci  }
170fd4e5da5Sopenharmony_ci}
171fd4e5da5Sopenharmony_ci
172fd4e5da5Sopenharmony_ciTEST_F(DataFlowTest, BackwardReachability) {
173fd4e5da5Sopenharmony_ci  // CFG:
174fd4e5da5Sopenharmony_ci  //    V-----------.
175fd4e5da5Sopenharmony_ci  // -> 11 -> 12 -> 13 -> 15
176fd4e5da5Sopenharmony_ci  //            \-> 14 ---^
177fd4e5da5Sopenharmony_ci
178fd4e5da5Sopenharmony_ci  const std::string text = R"(
179fd4e5da5Sopenharmony_ci               OpCapability Shader
180fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
181fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
182fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %2 "main"
183fd4e5da5Sopenharmony_ci               OpExecutionMode %2 OriginUpperLeft
184fd4e5da5Sopenharmony_ci               OpSource GLSL 430
185fd4e5da5Sopenharmony_ci          %3 = OpTypeVoid
186fd4e5da5Sopenharmony_ci          %4 = OpTypeFunction %3
187fd4e5da5Sopenharmony_ci          %6 = OpTypeBool
188fd4e5da5Sopenharmony_ci          %5 = OpConstantTrue %6
189fd4e5da5Sopenharmony_ci          %2 = OpFunction %3 None %4
190fd4e5da5Sopenharmony_ci         %11 = OpLabel
191fd4e5da5Sopenharmony_ci               OpBranch %12
192fd4e5da5Sopenharmony_ci         %12 = OpLabel
193fd4e5da5Sopenharmony_ci               OpBranchConditional %5 %14 %13
194fd4e5da5Sopenharmony_ci         %13 = OpLabel
195fd4e5da5Sopenharmony_ci               OpBranchConditional %5 %15 %11
196fd4e5da5Sopenharmony_ci         %14 = OpLabel
197fd4e5da5Sopenharmony_ci               OpBranch %15
198fd4e5da5Sopenharmony_ci         %15 = OpLabel
199fd4e5da5Sopenharmony_ci               OpReturn
200fd4e5da5Sopenharmony_ci               OpFunctionEnd
201fd4e5da5Sopenharmony_ci  )";
202fd4e5da5Sopenharmony_ci
203fd4e5da5Sopenharmony_ci  std::unique_ptr<IRContext> context =
204fd4e5da5Sopenharmony_ci      BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text,
205fd4e5da5Sopenharmony_ci                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
206fd4e5da5Sopenharmony_ci  ASSERT_NE(context, nullptr);
207fd4e5da5Sopenharmony_ci
208fd4e5da5Sopenharmony_ci  Function* function = spvtest::GetFunction(context->module(), 2);
209fd4e5da5Sopenharmony_ci
210fd4e5da5Sopenharmony_ci  BackwardReachability analysis(*context);
211fd4e5da5Sopenharmony_ci  analysis.Run(function);
212fd4e5da5Sopenharmony_ci
213fd4e5da5Sopenharmony_ci  std::map<uint32_t, std::set<uint32_t>> expected_result;
214fd4e5da5Sopenharmony_ci  expected_result[11] = {11, 12, 13};
215fd4e5da5Sopenharmony_ci  expected_result[12] = {11, 12, 13};
216fd4e5da5Sopenharmony_ci  expected_result[13] = {11, 12, 13};
217fd4e5da5Sopenharmony_ci  expected_result[14] = {11, 12, 13};
218fd4e5da5Sopenharmony_ci  expected_result[15] = {11, 12, 13, 14};
219fd4e5da5Sopenharmony_ci  EXPECT_EQ(expected_result, analysis.reachable_from);
220fd4e5da5Sopenharmony_ci}
221fd4e5da5Sopenharmony_ci
222fd4e5da5Sopenharmony_ci}  // namespace
223fd4e5da5Sopenharmony_ci}  // namespace opt
224fd4e5da5Sopenharmony_ci}  // namespace spvtools
225