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