1fd4e5da5Sopenharmony_ci// Copyright (c) 2018 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/struct_cfg_analysis.h" 16fd4e5da5Sopenharmony_ci 17fd4e5da5Sopenharmony_ci#include "source/opt/ir_context.h" 18fd4e5da5Sopenharmony_ci 19fd4e5da5Sopenharmony_cinamespace spvtools { 20fd4e5da5Sopenharmony_cinamespace opt { 21fd4e5da5Sopenharmony_cinamespace { 22fd4e5da5Sopenharmony_ciconstexpr uint32_t kMergeNodeIndex = 0; 23fd4e5da5Sopenharmony_ciconstexpr uint32_t kContinueNodeIndex = 1; 24fd4e5da5Sopenharmony_ci} // namespace 25fd4e5da5Sopenharmony_ci 26fd4e5da5Sopenharmony_ciStructuredCFGAnalysis::StructuredCFGAnalysis(IRContext* ctx) : context_(ctx) { 27fd4e5da5Sopenharmony_ci // If this is not a shader, there are no merge instructions, and not 28fd4e5da5Sopenharmony_ci // structured CFG to analyze. 29fd4e5da5Sopenharmony_ci if (!context_->get_feature_mgr()->HasCapability(spv::Capability::Shader)) { 30fd4e5da5Sopenharmony_ci return; 31fd4e5da5Sopenharmony_ci } 32fd4e5da5Sopenharmony_ci 33fd4e5da5Sopenharmony_ci for (auto& func : *context_->module()) { 34fd4e5da5Sopenharmony_ci AddBlocksInFunction(&func); 35fd4e5da5Sopenharmony_ci } 36fd4e5da5Sopenharmony_ci} 37fd4e5da5Sopenharmony_ci 38fd4e5da5Sopenharmony_civoid StructuredCFGAnalysis::AddBlocksInFunction(Function* func) { 39fd4e5da5Sopenharmony_ci if (func->begin() == func->end()) return; 40fd4e5da5Sopenharmony_ci 41fd4e5da5Sopenharmony_ci std::list<BasicBlock*> order; 42fd4e5da5Sopenharmony_ci context_->cfg()->ComputeStructuredOrder(func, &*func->begin(), &order); 43fd4e5da5Sopenharmony_ci 44fd4e5da5Sopenharmony_ci struct TraversalInfo { 45fd4e5da5Sopenharmony_ci ConstructInfo cinfo; 46fd4e5da5Sopenharmony_ci uint32_t merge_node; 47fd4e5da5Sopenharmony_ci uint32_t continue_node; 48fd4e5da5Sopenharmony_ci }; 49fd4e5da5Sopenharmony_ci 50fd4e5da5Sopenharmony_ci // Set up a stack to keep track of currently active constructs. 51fd4e5da5Sopenharmony_ci std::vector<TraversalInfo> state; 52fd4e5da5Sopenharmony_ci state.emplace_back(); 53fd4e5da5Sopenharmony_ci state[0].cinfo.containing_construct = 0; 54fd4e5da5Sopenharmony_ci state[0].cinfo.containing_loop = 0; 55fd4e5da5Sopenharmony_ci state[0].cinfo.containing_switch = 0; 56fd4e5da5Sopenharmony_ci state[0].cinfo.in_continue = false; 57fd4e5da5Sopenharmony_ci state[0].merge_node = 0; 58fd4e5da5Sopenharmony_ci state[0].continue_node = 0; 59fd4e5da5Sopenharmony_ci 60fd4e5da5Sopenharmony_ci for (BasicBlock* block : order) { 61fd4e5da5Sopenharmony_ci if (context_->cfg()->IsPseudoEntryBlock(block) || 62fd4e5da5Sopenharmony_ci context_->cfg()->IsPseudoExitBlock(block)) { 63fd4e5da5Sopenharmony_ci continue; 64fd4e5da5Sopenharmony_ci } 65fd4e5da5Sopenharmony_ci 66fd4e5da5Sopenharmony_ci if (block->id() == state.back().merge_node) { 67fd4e5da5Sopenharmony_ci state.pop_back(); 68fd4e5da5Sopenharmony_ci } 69fd4e5da5Sopenharmony_ci 70fd4e5da5Sopenharmony_ci // This works because the structured order is designed to keep the blocks in 71fd4e5da5Sopenharmony_ci // the continue construct between the continue header and the merge node. 72fd4e5da5Sopenharmony_ci if (block->id() == state.back().continue_node) { 73fd4e5da5Sopenharmony_ci state.back().cinfo.in_continue = true; 74fd4e5da5Sopenharmony_ci } 75fd4e5da5Sopenharmony_ci 76fd4e5da5Sopenharmony_ci bb_to_construct_.emplace(std::make_pair(block->id(), state.back().cinfo)); 77fd4e5da5Sopenharmony_ci 78fd4e5da5Sopenharmony_ci if (Instruction* merge_inst = block->GetMergeInst()) { 79fd4e5da5Sopenharmony_ci TraversalInfo new_state; 80fd4e5da5Sopenharmony_ci new_state.merge_node = 81fd4e5da5Sopenharmony_ci merge_inst->GetSingleWordInOperand(kMergeNodeIndex); 82fd4e5da5Sopenharmony_ci new_state.cinfo.containing_construct = block->id(); 83fd4e5da5Sopenharmony_ci 84fd4e5da5Sopenharmony_ci if (merge_inst->opcode() == spv::Op::OpLoopMerge) { 85fd4e5da5Sopenharmony_ci new_state.cinfo.containing_loop = block->id(); 86fd4e5da5Sopenharmony_ci new_state.cinfo.containing_switch = 0; 87fd4e5da5Sopenharmony_ci new_state.continue_node = 88fd4e5da5Sopenharmony_ci merge_inst->GetSingleWordInOperand(kContinueNodeIndex); 89fd4e5da5Sopenharmony_ci if (block->id() == new_state.continue_node) { 90fd4e5da5Sopenharmony_ci new_state.cinfo.in_continue = true; 91fd4e5da5Sopenharmony_ci bb_to_construct_[block->id()].in_continue = true; 92fd4e5da5Sopenharmony_ci } else { 93fd4e5da5Sopenharmony_ci new_state.cinfo.in_continue = false; 94fd4e5da5Sopenharmony_ci } 95fd4e5da5Sopenharmony_ci } else { 96fd4e5da5Sopenharmony_ci new_state.cinfo.containing_loop = state.back().cinfo.containing_loop; 97fd4e5da5Sopenharmony_ci new_state.cinfo.in_continue = state.back().cinfo.in_continue; 98fd4e5da5Sopenharmony_ci new_state.continue_node = state.back().continue_node; 99fd4e5da5Sopenharmony_ci 100fd4e5da5Sopenharmony_ci if (merge_inst->NextNode()->opcode() == spv::Op::OpSwitch) { 101fd4e5da5Sopenharmony_ci new_state.cinfo.containing_switch = block->id(); 102fd4e5da5Sopenharmony_ci } else { 103fd4e5da5Sopenharmony_ci new_state.cinfo.containing_switch = 104fd4e5da5Sopenharmony_ci state.back().cinfo.containing_switch; 105fd4e5da5Sopenharmony_ci } 106fd4e5da5Sopenharmony_ci } 107fd4e5da5Sopenharmony_ci 108fd4e5da5Sopenharmony_ci state.emplace_back(new_state); 109fd4e5da5Sopenharmony_ci merge_blocks_.Set(new_state.merge_node); 110fd4e5da5Sopenharmony_ci } 111fd4e5da5Sopenharmony_ci } 112fd4e5da5Sopenharmony_ci} 113fd4e5da5Sopenharmony_ci 114fd4e5da5Sopenharmony_ciuint32_t StructuredCFGAnalysis::ContainingConstruct(Instruction* inst) { 115fd4e5da5Sopenharmony_ci uint32_t bb = context_->get_instr_block(inst)->id(); 116fd4e5da5Sopenharmony_ci return ContainingConstruct(bb); 117fd4e5da5Sopenharmony_ci} 118fd4e5da5Sopenharmony_ci 119fd4e5da5Sopenharmony_ciuint32_t StructuredCFGAnalysis::MergeBlock(uint32_t bb_id) { 120fd4e5da5Sopenharmony_ci uint32_t header_id = ContainingConstruct(bb_id); 121fd4e5da5Sopenharmony_ci if (header_id == 0) { 122fd4e5da5Sopenharmony_ci return 0; 123fd4e5da5Sopenharmony_ci } 124fd4e5da5Sopenharmony_ci 125fd4e5da5Sopenharmony_ci BasicBlock* header = context_->cfg()->block(header_id); 126fd4e5da5Sopenharmony_ci Instruction* merge_inst = header->GetMergeInst(); 127fd4e5da5Sopenharmony_ci return merge_inst->GetSingleWordInOperand(kMergeNodeIndex); 128fd4e5da5Sopenharmony_ci} 129fd4e5da5Sopenharmony_ci 130fd4e5da5Sopenharmony_ciuint32_t StructuredCFGAnalysis::NestingDepth(uint32_t bb_id) { 131fd4e5da5Sopenharmony_ci uint32_t result = 0; 132fd4e5da5Sopenharmony_ci 133fd4e5da5Sopenharmony_ci // Find the merge block of the current merge construct as long as the block is 134fd4e5da5Sopenharmony_ci // inside a merge construct, exiting one for each iteration. 135fd4e5da5Sopenharmony_ci for (uint32_t merge_block_id = MergeBlock(bb_id); merge_block_id != 0; 136fd4e5da5Sopenharmony_ci merge_block_id = MergeBlock(merge_block_id)) { 137fd4e5da5Sopenharmony_ci result++; 138fd4e5da5Sopenharmony_ci } 139fd4e5da5Sopenharmony_ci 140fd4e5da5Sopenharmony_ci return result; 141fd4e5da5Sopenharmony_ci} 142fd4e5da5Sopenharmony_ci 143fd4e5da5Sopenharmony_ciuint32_t StructuredCFGAnalysis::LoopMergeBlock(uint32_t bb_id) { 144fd4e5da5Sopenharmony_ci uint32_t header_id = ContainingLoop(bb_id); 145fd4e5da5Sopenharmony_ci if (header_id == 0) { 146fd4e5da5Sopenharmony_ci return 0; 147fd4e5da5Sopenharmony_ci } 148fd4e5da5Sopenharmony_ci 149fd4e5da5Sopenharmony_ci BasicBlock* header = context_->cfg()->block(header_id); 150fd4e5da5Sopenharmony_ci Instruction* merge_inst = header->GetMergeInst(); 151fd4e5da5Sopenharmony_ci return merge_inst->GetSingleWordInOperand(kMergeNodeIndex); 152fd4e5da5Sopenharmony_ci} 153fd4e5da5Sopenharmony_ci 154fd4e5da5Sopenharmony_ciuint32_t StructuredCFGAnalysis::LoopContinueBlock(uint32_t bb_id) { 155fd4e5da5Sopenharmony_ci uint32_t header_id = ContainingLoop(bb_id); 156fd4e5da5Sopenharmony_ci if (header_id == 0) { 157fd4e5da5Sopenharmony_ci return 0; 158fd4e5da5Sopenharmony_ci } 159fd4e5da5Sopenharmony_ci 160fd4e5da5Sopenharmony_ci BasicBlock* header = context_->cfg()->block(header_id); 161fd4e5da5Sopenharmony_ci Instruction* merge_inst = header->GetMergeInst(); 162fd4e5da5Sopenharmony_ci return merge_inst->GetSingleWordInOperand(kContinueNodeIndex); 163fd4e5da5Sopenharmony_ci} 164fd4e5da5Sopenharmony_ci 165fd4e5da5Sopenharmony_ciuint32_t StructuredCFGAnalysis::LoopNestingDepth(uint32_t bb_id) { 166fd4e5da5Sopenharmony_ci uint32_t result = 0; 167fd4e5da5Sopenharmony_ci 168fd4e5da5Sopenharmony_ci // Find the merge block of the current loop as long as the block is inside a 169fd4e5da5Sopenharmony_ci // loop, exiting a loop for each iteration. 170fd4e5da5Sopenharmony_ci for (uint32_t merge_block_id = LoopMergeBlock(bb_id); merge_block_id != 0; 171fd4e5da5Sopenharmony_ci merge_block_id = LoopMergeBlock(merge_block_id)) { 172fd4e5da5Sopenharmony_ci result++; 173fd4e5da5Sopenharmony_ci } 174fd4e5da5Sopenharmony_ci 175fd4e5da5Sopenharmony_ci return result; 176fd4e5da5Sopenharmony_ci} 177fd4e5da5Sopenharmony_ci 178fd4e5da5Sopenharmony_ciuint32_t StructuredCFGAnalysis::SwitchMergeBlock(uint32_t bb_id) { 179fd4e5da5Sopenharmony_ci uint32_t header_id = ContainingSwitch(bb_id); 180fd4e5da5Sopenharmony_ci if (header_id == 0) { 181fd4e5da5Sopenharmony_ci return 0; 182fd4e5da5Sopenharmony_ci } 183fd4e5da5Sopenharmony_ci 184fd4e5da5Sopenharmony_ci BasicBlock* header = context_->cfg()->block(header_id); 185fd4e5da5Sopenharmony_ci Instruction* merge_inst = header->GetMergeInst(); 186fd4e5da5Sopenharmony_ci return merge_inst->GetSingleWordInOperand(kMergeNodeIndex); 187fd4e5da5Sopenharmony_ci} 188fd4e5da5Sopenharmony_ci 189fd4e5da5Sopenharmony_cibool StructuredCFGAnalysis::IsContinueBlock(uint32_t bb_id) { 190fd4e5da5Sopenharmony_ci assert(bb_id != 0); 191fd4e5da5Sopenharmony_ci return LoopContinueBlock(bb_id) == bb_id; 192fd4e5da5Sopenharmony_ci} 193fd4e5da5Sopenharmony_ci 194fd4e5da5Sopenharmony_cibool StructuredCFGAnalysis::IsInContainingLoopsContinueConstruct( 195fd4e5da5Sopenharmony_ci uint32_t bb_id) { 196fd4e5da5Sopenharmony_ci auto it = bb_to_construct_.find(bb_id); 197fd4e5da5Sopenharmony_ci if (it == bb_to_construct_.end()) { 198fd4e5da5Sopenharmony_ci return false; 199fd4e5da5Sopenharmony_ci } 200fd4e5da5Sopenharmony_ci return it->second.in_continue; 201fd4e5da5Sopenharmony_ci} 202fd4e5da5Sopenharmony_ci 203fd4e5da5Sopenharmony_cibool StructuredCFGAnalysis::IsInContinueConstruct(uint32_t bb_id) { 204fd4e5da5Sopenharmony_ci while (bb_id != 0) { 205fd4e5da5Sopenharmony_ci if (IsInContainingLoopsContinueConstruct(bb_id)) { 206fd4e5da5Sopenharmony_ci return true; 207fd4e5da5Sopenharmony_ci } 208fd4e5da5Sopenharmony_ci bb_id = ContainingLoop(bb_id); 209fd4e5da5Sopenharmony_ci } 210fd4e5da5Sopenharmony_ci return false; 211fd4e5da5Sopenharmony_ci} 212fd4e5da5Sopenharmony_ci 213fd4e5da5Sopenharmony_cibool StructuredCFGAnalysis::IsMergeBlock(uint32_t bb_id) { 214fd4e5da5Sopenharmony_ci return merge_blocks_.Get(bb_id); 215fd4e5da5Sopenharmony_ci} 216fd4e5da5Sopenharmony_ci 217fd4e5da5Sopenharmony_cistd::unordered_set<uint32_t> 218fd4e5da5Sopenharmony_ciStructuredCFGAnalysis::FindFuncsCalledFromContinue() { 219fd4e5da5Sopenharmony_ci std::unordered_set<uint32_t> called_from_continue; 220fd4e5da5Sopenharmony_ci std::queue<uint32_t> funcs_to_process; 221fd4e5da5Sopenharmony_ci 222fd4e5da5Sopenharmony_ci // First collect the functions that are called directly from a continue 223fd4e5da5Sopenharmony_ci // construct. 224fd4e5da5Sopenharmony_ci for (Function& func : *context_->module()) { 225fd4e5da5Sopenharmony_ci for (auto& bb : func) { 226fd4e5da5Sopenharmony_ci if (IsInContainingLoopsContinueConstruct(bb.id())) { 227fd4e5da5Sopenharmony_ci for (const Instruction& inst : bb) { 228fd4e5da5Sopenharmony_ci if (inst.opcode() == spv::Op::OpFunctionCall) { 229fd4e5da5Sopenharmony_ci funcs_to_process.push(inst.GetSingleWordInOperand(0)); 230fd4e5da5Sopenharmony_ci } 231fd4e5da5Sopenharmony_ci } 232fd4e5da5Sopenharmony_ci } 233fd4e5da5Sopenharmony_ci } 234fd4e5da5Sopenharmony_ci } 235fd4e5da5Sopenharmony_ci 236fd4e5da5Sopenharmony_ci // Now collect all of the functions that are indirectly called as well. 237fd4e5da5Sopenharmony_ci while (!funcs_to_process.empty()) { 238fd4e5da5Sopenharmony_ci uint32_t func_id = funcs_to_process.front(); 239fd4e5da5Sopenharmony_ci funcs_to_process.pop(); 240fd4e5da5Sopenharmony_ci Function* func = context_->GetFunction(func_id); 241fd4e5da5Sopenharmony_ci if (called_from_continue.insert(func_id).second) { 242fd4e5da5Sopenharmony_ci context_->AddCalls(func, &funcs_to_process); 243fd4e5da5Sopenharmony_ci } 244fd4e5da5Sopenharmony_ci } 245fd4e5da5Sopenharmony_ci return called_from_continue; 246fd4e5da5Sopenharmony_ci} 247fd4e5da5Sopenharmony_ci 248fd4e5da5Sopenharmony_ci} // namespace opt 249fd4e5da5Sopenharmony_ci} // namespace spvtools 250