xref: /aosp_15_r20/external/angle/third_party/spirv-tools/src/source/val/function.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 // Copyright (c) 2015-2016 The Khronos Group Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "source/val/function.h"
16 
17 #include <algorithm>
18 #include <cassert>
19 #include <sstream>
20 #include <unordered_map>
21 #include <utility>
22 
23 #include "source/cfa.h"
24 #include "source/val/basic_block.h"
25 #include "source/val/construct.h"
26 #include "source/val/validate.h"
27 
28 namespace spvtools {
29 namespace val {
30 
31 // Universal Limit of ResultID + 1
32 static const uint32_t kInvalidId = 0x400000;
33 
Function(uint32_t function_id,uint32_t result_type_id,spv::FunctionControlMask function_control,uint32_t function_type_id)34 Function::Function(uint32_t function_id, uint32_t result_type_id,
35                    spv::FunctionControlMask function_control,
36                    uint32_t function_type_id)
37     : id_(function_id),
38       function_type_id_(function_type_id),
39       result_type_id_(result_type_id),
40       function_control_(function_control),
41       declaration_type_(FunctionDecl::kFunctionDeclUnknown),
42       end_has_been_registered_(false),
43       blocks_(),
44       current_block_(nullptr),
45       pseudo_entry_block_(0),
46       pseudo_exit_block_(kInvalidId),
47       cfg_constructs_(),
48       variable_ids_(),
49       parameter_ids_() {}
50 
IsFirstBlock(uint32_t block_id) const51 bool Function::IsFirstBlock(uint32_t block_id) const {
52   return !ordered_blocks_.empty() && *first_block() == block_id;
53 }
54 
RegisterFunctionParameter(uint32_t parameter_id,uint32_t type_id)55 spv_result_t Function::RegisterFunctionParameter(uint32_t parameter_id,
56                                                  uint32_t type_id) {
57   assert(current_block_ == nullptr &&
58          "RegisterFunctionParameter can only be called when parsing the binary "
59          "outside of a block");
60   // TODO(umar): Validate function parameter type order and count
61   // TODO(umar): Use these variables to validate parameter type
62   (void)parameter_id;
63   (void)type_id;
64   return SPV_SUCCESS;
65 }
66 
RegisterLoopMerge(uint32_t merge_id,uint32_t continue_id)67 spv_result_t Function::RegisterLoopMerge(uint32_t merge_id,
68                                          uint32_t continue_id) {
69   RegisterBlock(merge_id, false);
70   RegisterBlock(continue_id, false);
71   BasicBlock& merge_block = blocks_.at(merge_id);
72   BasicBlock& continue_target_block = blocks_.at(continue_id);
73   assert(current_block_ &&
74          "RegisterLoopMerge must be called when called within a block");
75   current_block_->RegisterStructuralSuccessor(&merge_block);
76   current_block_->RegisterStructuralSuccessor(&continue_target_block);
77 
78   current_block_->set_type(kBlockTypeLoop);
79   merge_block.set_type(kBlockTypeMerge);
80   continue_target_block.set_type(kBlockTypeContinue);
81   Construct& loop_construct =
82       AddConstruct({ConstructType::kLoop, current_block_, &merge_block});
83   Construct& continue_construct =
84       AddConstruct({ConstructType::kContinue, &continue_target_block});
85 
86   continue_construct.set_corresponding_constructs({&loop_construct});
87   loop_construct.set_corresponding_constructs({&continue_construct});
88   merge_block_header_[&merge_block] = current_block_;
89   if (continue_target_headers_.find(&continue_target_block) ==
90       continue_target_headers_.end()) {
91     continue_target_headers_[&continue_target_block] = {current_block_};
92   } else {
93     continue_target_headers_[&continue_target_block].push_back(current_block_);
94   }
95 
96   return SPV_SUCCESS;
97 }
98 
RegisterSelectionMerge(uint32_t merge_id)99 spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) {
100   RegisterBlock(merge_id, false);
101   BasicBlock& merge_block = blocks_.at(merge_id);
102   current_block_->set_type(kBlockTypeSelection);
103   merge_block.set_type(kBlockTypeMerge);
104   merge_block_header_[&merge_block] = current_block_;
105   current_block_->RegisterStructuralSuccessor(&merge_block);
106 
107   AddConstruct({ConstructType::kSelection, current_block(), &merge_block});
108 
109   return SPV_SUCCESS;
110 }
111 
RegisterSetFunctionDeclType(FunctionDecl type)112 spv_result_t Function::RegisterSetFunctionDeclType(FunctionDecl type) {
113   assert(declaration_type_ == FunctionDecl::kFunctionDeclUnknown);
114   declaration_type_ = type;
115   return SPV_SUCCESS;
116 }
117 
RegisterBlock(uint32_t block_id,bool is_definition)118 spv_result_t Function::RegisterBlock(uint32_t block_id, bool is_definition) {
119   assert(
120       declaration_type_ == FunctionDecl::kFunctionDeclDefinition &&
121       "RegisterBlocks can only be called after declaration_type_ is defined");
122 
123   std::unordered_map<uint32_t, BasicBlock>::iterator inserted_block;
124   bool success = false;
125   tie(inserted_block, success) =
126       blocks_.insert({block_id, BasicBlock(block_id)});
127   if (is_definition) {  // new block definition
128     assert(current_block_ == nullptr &&
129            "Register Block can only be called when parsing a binary outside of "
130            "a BasicBlock");
131 
132     undefined_blocks_.erase(block_id);
133     current_block_ = &inserted_block->second;
134     ordered_blocks_.push_back(current_block_);
135   } else if (success) {  // Block doesn't exist but this is not a definition
136     undefined_blocks_.insert(block_id);
137   }
138 
139   return SPV_SUCCESS;
140 }
141 
RegisterBlockEnd(std::vector<uint32_t> next_list)142 void Function::RegisterBlockEnd(std::vector<uint32_t> next_list) {
143   assert(
144       current_block_ &&
145       "RegisterBlockEnd can only be called when parsing a binary in a block");
146   std::vector<BasicBlock*> next_blocks;
147   next_blocks.reserve(next_list.size());
148 
149   std::unordered_map<uint32_t, BasicBlock>::iterator inserted_block;
150   bool success;
151   for (uint32_t successor_id : next_list) {
152     tie(inserted_block, success) =
153         blocks_.insert({successor_id, BasicBlock(successor_id)});
154     if (success) {
155       undefined_blocks_.insert(successor_id);
156     }
157     next_blocks.push_back(&inserted_block->second);
158   }
159 
160   if (current_block_->is_type(kBlockTypeLoop)) {
161     // For each loop header, record the set of its successors, and include
162     // its continue target if the continue target is not the loop header
163     // itself.
164     std::vector<BasicBlock*>& next_blocks_plus_continue_target =
165         loop_header_successors_plus_continue_target_map_[current_block_];
166     next_blocks_plus_continue_target = next_blocks;
167     auto continue_target =
168         FindConstructForEntryBlock(current_block_, ConstructType::kLoop)
169             .corresponding_constructs()
170             .back()
171             ->entry_block();
172     if (continue_target != current_block_) {
173       next_blocks_plus_continue_target.push_back(continue_target);
174     }
175   }
176 
177   current_block_->RegisterSuccessors(next_blocks);
178   current_block_ = nullptr;
179   return;
180 }
181 
RegisterFunctionEnd()182 void Function::RegisterFunctionEnd() {
183   if (!end_has_been_registered_) {
184     end_has_been_registered_ = true;
185 
186     ComputeAugmentedCFG();
187   }
188 }
189 
block_count() const190 size_t Function::block_count() const { return blocks_.size(); }
191 
undefined_block_count() const192 size_t Function::undefined_block_count() const {
193   return undefined_blocks_.size();
194 }
195 
ordered_blocks() const196 const std::vector<BasicBlock*>& Function::ordered_blocks() const {
197   return ordered_blocks_;
198 }
ordered_blocks()199 std::vector<BasicBlock*>& Function::ordered_blocks() { return ordered_blocks_; }
200 
current_block() const201 const BasicBlock* Function::current_block() const { return current_block_; }
current_block()202 BasicBlock* Function::current_block() { return current_block_; }
203 
constructs() const204 const std::list<Construct>& Function::constructs() const {
205   return cfg_constructs_;
206 }
constructs()207 std::list<Construct>& Function::constructs() { return cfg_constructs_; }
208 
first_block() const209 const BasicBlock* Function::first_block() const {
210   if (ordered_blocks_.empty()) return nullptr;
211   return ordered_blocks_[0];
212 }
first_block()213 BasicBlock* Function::first_block() {
214   if (ordered_blocks_.empty()) return nullptr;
215   return ordered_blocks_[0];
216 }
217 
IsBlockType(uint32_t merge_block_id,BlockType type) const218 bool Function::IsBlockType(uint32_t merge_block_id, BlockType type) const {
219   bool ret = false;
220   const BasicBlock* block;
221   std::tie(block, std::ignore) = GetBlock(merge_block_id);
222   if (block) {
223     ret = block->is_type(type);
224   }
225   return ret;
226 }
227 
GetBlock(uint32_t block_id) const228 std::pair<const BasicBlock*, bool> Function::GetBlock(uint32_t block_id) const {
229   const auto b = blocks_.find(block_id);
230   if (b != end(blocks_)) {
231     const BasicBlock* block = &(b->second);
232     bool defined =
233         undefined_blocks_.find(block->id()) == std::end(undefined_blocks_);
234     return std::make_pair(block, defined);
235   } else {
236     return std::make_pair(nullptr, false);
237   }
238 }
239 
GetBlock(uint32_t block_id)240 std::pair<BasicBlock*, bool> Function::GetBlock(uint32_t block_id) {
241   const BasicBlock* out;
242   bool defined;
243   std::tie(out, defined) =
244       const_cast<const Function*>(this)->GetBlock(block_id);
245   return std::make_pair(const_cast<BasicBlock*>(out), defined);
246 }
247 
AugmentedCFGSuccessorsFunction() const248 Function::GetBlocksFunction Function::AugmentedCFGSuccessorsFunction() const {
249   return [this](const BasicBlock* block) {
250     auto where = augmented_successors_map_.find(block);
251     return where == augmented_successors_map_.end() ? block->successors()
252                                                     : &(*where).second;
253   };
254 }
255 
AugmentedCFGPredecessorsFunction() const256 Function::GetBlocksFunction Function::AugmentedCFGPredecessorsFunction() const {
257   return [this](const BasicBlock* block) {
258     auto where = augmented_predecessors_map_.find(block);
259     return where == augmented_predecessors_map_.end() ? block->predecessors()
260                                                       : &(*where).second;
261   };
262 }
263 
AugmentedStructuralCFGSuccessorsFunction() const264 Function::GetBlocksFunction Function::AugmentedStructuralCFGSuccessorsFunction()
265     const {
266   return [this](const BasicBlock* block) {
267     auto where = augmented_successors_map_.find(block);
268     return where == augmented_successors_map_.end()
269                ? block->structural_successors()
270                : &(*where).second;
271   };
272 }
273 
274 Function::GetBlocksFunction
AugmentedStructuralCFGPredecessorsFunction() const275 Function::AugmentedStructuralCFGPredecessorsFunction() const {
276   return [this](const BasicBlock* block) {
277     auto where = augmented_predecessors_map_.find(block);
278     return where == augmented_predecessors_map_.end()
279                ? block->structural_predecessors()
280                : &(*where).second;
281   };
282 }
283 
ComputeAugmentedCFG()284 void Function::ComputeAugmentedCFG() {
285   // Compute the successors of the pseudo-entry block, and
286   // the predecessors of the pseudo exit block.
287   auto succ_func = [](const BasicBlock* b) {
288     return b->structural_successors();
289   };
290   auto pred_func = [](const BasicBlock* b) {
291     return b->structural_predecessors();
292   };
293   CFA<BasicBlock>::ComputeAugmentedCFG(
294       ordered_blocks_, &pseudo_entry_block_, &pseudo_exit_block_,
295       &augmented_successors_map_, &augmented_predecessors_map_, succ_func,
296       pred_func);
297 }
298 
AddConstruct(const Construct & new_construct)299 Construct& Function::AddConstruct(const Construct& new_construct) {
300   cfg_constructs_.push_back(new_construct);
301   auto& result = cfg_constructs_.back();
302   entry_block_to_construct_[std::make_pair(new_construct.entry_block(),
303                                            new_construct.type())] = &result;
304   return result;
305 }
306 
FindConstructForEntryBlock(const BasicBlock * entry_block,ConstructType type)307 Construct& Function::FindConstructForEntryBlock(const BasicBlock* entry_block,
308                                                 ConstructType type) {
309   auto where =
310       entry_block_to_construct_.find(std::make_pair(entry_block, type));
311   assert(where != entry_block_to_construct_.end());
312   auto construct_ptr = (*where).second;
313   assert(construct_ptr);
314   return *construct_ptr;
315 }
316 
GetBlockDepth(BasicBlock * bb)317 int Function::GetBlockDepth(BasicBlock* bb) {
318   // Guard against nullptr.
319   if (!bb) {
320     return 0;
321   }
322   // Only calculate the depth if it's not already calculated.
323   // This function uses memoization to avoid duplicate CFG depth calculations.
324   if (block_depth_.find(bb) != block_depth_.end()) {
325     return block_depth_[bb];
326   }
327   // Avoid recursion. Something is wrong if the same block is encountered
328   // multiple times.
329   block_depth_[bb] = 0;
330 
331   BasicBlock* bb_dom = bb->immediate_dominator();
332   if (!bb_dom || bb == bb_dom) {
333     // This block has no dominator, so it's at depth 0.
334     block_depth_[bb] = 0;
335   } else if (bb->is_type(kBlockTypeContinue)) {
336     // This rule must precede the rule for merge blocks in order to set up
337     // depths correctly. If a block is both a merge and continue then the merge
338     // is nested within the continue's loop (or the graph is incorrect).
339     // The depth of the continue block entry point is 1 + loop header depth.
340     Construct* continue_construct =
341         entry_block_to_construct_[std::make_pair(bb, ConstructType::kContinue)];
342     assert(continue_construct);
343     // Continue construct has only 1 corresponding construct (loop header).
344     Construct* loop_construct =
345         continue_construct->corresponding_constructs()[0];
346     assert(loop_construct);
347     BasicBlock* loop_header = loop_construct->entry_block();
348     // The continue target may be the loop itself (while 1).
349     // In such cases, the depth of the continue block is: 1 + depth of the
350     // loop's dominator block.
351     if (loop_header == bb) {
352       block_depth_[bb] = 1 + GetBlockDepth(bb_dom);
353     } else {
354       block_depth_[bb] = 1 + GetBlockDepth(loop_header);
355     }
356   } else if (bb->is_type(kBlockTypeMerge)) {
357     // If this is a merge block, its depth is equal to the block before
358     // branching.
359     BasicBlock* header = merge_block_header_[bb];
360     assert(header);
361     block_depth_[bb] = GetBlockDepth(header);
362   } else if (bb_dom->is_type(kBlockTypeSelection) ||
363              bb_dom->is_type(kBlockTypeLoop)) {
364     // The dominator of the given block is a header block. So, the nesting
365     // depth of this block is: 1 + nesting depth of the header.
366     block_depth_[bb] = 1 + GetBlockDepth(bb_dom);
367   } else {
368     block_depth_[bb] = GetBlockDepth(bb_dom);
369   }
370   return block_depth_[bb];
371 }
372 
RegisterExecutionModelLimitation(spv::ExecutionModel model,const std::string & message)373 void Function::RegisterExecutionModelLimitation(spv::ExecutionModel model,
374                                                 const std::string& message) {
375   execution_model_limitations_.push_back(
376       [model, message](spv::ExecutionModel in_model, std::string* out_message) {
377         if (model != in_model) {
378           if (out_message) {
379             *out_message = message;
380           }
381           return false;
382         }
383         return true;
384       });
385 }
386 
IsCompatibleWithExecutionModel(spv::ExecutionModel model,std::string * reason) const387 bool Function::IsCompatibleWithExecutionModel(spv::ExecutionModel model,
388                                               std::string* reason) const {
389   bool return_value = true;
390   std::stringstream ss_reason;
391 
392   for (const auto& is_compatible : execution_model_limitations_) {
393     std::string message;
394     if (!is_compatible(model, &message)) {
395       if (!reason) return false;
396       return_value = false;
397       if (!message.empty()) {
398         ss_reason << message << "\n";
399       }
400     }
401   }
402 
403   if (!return_value && reason) {
404     *reason = ss_reason.str();
405   }
406 
407   return return_value;
408 }
409 
CheckLimitations(const ValidationState_t & _,const Function * entry_point,std::string * reason) const410 bool Function::CheckLimitations(const ValidationState_t& _,
411                                 const Function* entry_point,
412                                 std::string* reason) const {
413   bool return_value = true;
414   std::stringstream ss_reason;
415 
416   for (const auto& is_compatible : limitations_) {
417     std::string message;
418     if (!is_compatible(_, entry_point, &message)) {
419       if (!reason) return false;
420       return_value = false;
421       if (!message.empty()) {
422         ss_reason << message << "\n";
423       }
424     }
425   }
426 
427   if (!return_value && reason) {
428     *reason = ss_reason.str();
429   }
430 
431   return return_value;
432 }
433 
434 }  // namespace val
435 }  // namespace spvtools
436