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