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