• 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 #ifndef SOURCE_VAL_FUNCTION_H_
16 #define SOURCE_VAL_FUNCTION_H_
17 
18 #include <functional>
19 #include <list>
20 #include <map>
21 #include <set>
22 #include <string>
23 #include <type_traits>
24 #include <unordered_map>
25 #include <unordered_set>
26 #include <utility>
27 #include <vector>
28 
29 #include "source/latest_version_spirv_header.h"
30 #include "source/val/basic_block.h"
31 #include "source/val/construct.h"
32 #include "spirv-tools/libspirv.h"
33 
34 namespace spvtools {
35 namespace val {
36 
37 struct bb_constr_type_pair_hash {
operatorbb_constr_type_pair_hash38   std::size_t operator()(
39       const std::pair<const BasicBlock*, ConstructType>& p) const {
40     auto h1 = std::hash<const BasicBlock*>{}(p.first);
41     auto h2 = std::hash<std::underlying_type<ConstructType>::type>{}(
42         static_cast<std::underlying_type<ConstructType>::type>(p.second));
43     return (h1 ^ h2);
44   }
45 };
46 
47 enum class FunctionDecl {
48   kFunctionDeclUnknown,      /// < Unknown function declaration
49   kFunctionDeclDeclaration,  /// < Function declaration
50   kFunctionDeclDefinition    /// < Function definition
51 };
52 
53 /// This class manages all function declaration and definitions in a module. It
54 /// handles the state and id information while parsing a function in the SPIR-V
55 /// binary.
56 class Function {
57  public:
58   Function(uint32_t id, uint32_t result_type_id,
59            spv::FunctionControlMask function_control,
60            uint32_t function_type_id);
61 
62   /// Registers a function parameter in the current function
63   /// @return Returns SPV_SUCCESS if the call was successful
64   spv_result_t RegisterFunctionParameter(uint32_t id, uint32_t type_id);
65 
66   /// Sets the declaration type of the current function
67   /// @return Returns SPV_SUCCESS if the call was successful
68   spv_result_t RegisterSetFunctionDeclType(FunctionDecl type);
69 
70   /// Registers a block in the current function. Subsequent block instructions
71   /// will target this block
72   /// @param id The ID of the label of the block
73   /// @return Returns SPV_SUCCESS if the call was successful
74   spv_result_t RegisterBlock(uint32_t id, bool is_definition = true);
75 
76   /// Registers a variable in the current block
77   ///
78   /// @param[in] type_id The type ID of the variable
79   /// @param[in] id      The ID of the variable
80   /// @param[in] storage The storage of the variable
81   /// @param[in] init_id The initializer ID of the variable
82   ///
83   /// @return Returns SPV_SUCCESS if the call was successful
84   spv_result_t RegisterBlockVariable(uint32_t type_id, uint32_t id,
85                                      spv::StorageClass storage,
86                                      uint32_t init_id);
87 
88   /// Registers a loop merge construct in the function
89   ///
90   /// @param[in] merge_id The merge block ID of the loop
91   /// @param[in] continue_id The continue block ID of the loop
92   ///
93   /// @return Returns SPV_SUCCESS if the call was successful
94   spv_result_t RegisterLoopMerge(uint32_t merge_id, uint32_t continue_id);
95 
96   /// Registers a selection merge construct in the function
97   /// @return Returns SPV_SUCCESS if the call was successful
98   spv_result_t RegisterSelectionMerge(uint32_t merge_id);
99 
100   /// Registers the end of the block
101   ///
102   /// @param[in] successors_list A list of ids to the block's successors
103   void RegisterBlockEnd(std::vector<uint32_t> successors_list);
104 
105   /// Registers the end of the function.  This is idempotent.
106   void RegisterFunctionEnd();
107 
108   /// Returns true if the \p id block is the first block of this function
109   bool IsFirstBlock(uint32_t id) const;
110 
111   /// Returns true if the \p merge_block_id is a BlockType of \p type
112   bool IsBlockType(uint32_t merge_block_id, BlockType type) const;
113 
114   /// Returns a pair consisting of the BasicBlock with \p id and a bool
115   /// which is true if the block has been defined, and false if it is
116   /// declared but not defined. This function will return nullptr if the
117   /// \p id was not declared and not defined at the current point in the binary
118   std::pair<const BasicBlock*, bool> GetBlock(uint32_t id) const;
119   std::pair<BasicBlock*, bool> GetBlock(uint32_t id);
120 
121   /// Returns the first block of the current function
122   const BasicBlock* first_block() const;
123 
124   /// Returns the first block of the current function
125   BasicBlock* first_block();
126 
127   /// Returns a vector of all the blocks in the function
128   const std::vector<BasicBlock*>& ordered_blocks() const;
129 
130   /// Returns a vector of all the blocks in the function
131   std::vector<BasicBlock*>& ordered_blocks();
132 
133   /// Returns a list of all the cfg constructs in the function
134   const std::list<Construct>& constructs() const;
135 
136   /// Returns a list of all the cfg constructs in the function
137   std::list<Construct>& constructs();
138 
139   /// Returns the number of blocks in the current function being parsed
140   size_t block_count() const;
141 
142   /// Returns the id of the function
id()143   uint32_t id() const { return id_; }
144 
145   /// Returns return type id of the function
GetResultTypeId()146   uint32_t GetResultTypeId() const { return result_type_id_; }
147 
148   /// Returns the number of blocks in the current function being parsed
149   size_t undefined_block_count() const;
undefined_blocks()150   const std::unordered_set<uint32_t>& undefined_blocks() const {
151     return undefined_blocks_;
152   }
153 
154   /// Returns the block that is currently being parsed in the binary
155   BasicBlock* current_block();
156 
157   /// Returns the block that is currently being parsed in the binary
158   const BasicBlock* current_block() const;
159 
160   // For dominance calculations, we want to analyze all the
161   // blocks in the function, even in degenerate control flow cases
162   // including unreachable blocks.  We therefore make an "augmented CFG"
163   // which is the same as the ordinary CFG but adds:
164   //  - A pseudo-entry node.
165   //  - A pseudo-exit node.
166   //  - A minimal set of edges so that a forward traversal from the
167   //    pseudo-entry node will visit all nodes.
168   //  - A minimal set of edges so that a backward traversal from the
169   //    pseudo-exit node will visit all nodes.
170   // In particular, the pseudo-entry node is the unique source of the
171   // augmented CFG, and the psueo-exit node is the unique sink of the
172   // augmented CFG.
173 
174   /// Returns the pseudo exit block
pseudo_entry_block()175   BasicBlock* pseudo_entry_block() { return &pseudo_entry_block_; }
176 
177   /// Returns the pseudo exit block
pseudo_entry_block()178   const BasicBlock* pseudo_entry_block() const { return &pseudo_entry_block_; }
179 
180   /// Returns the pseudo exit block
pseudo_exit_block()181   BasicBlock* pseudo_exit_block() { return &pseudo_exit_block_; }
182 
183   /// Returns the pseudo exit block
pseudo_exit_block()184   const BasicBlock* pseudo_exit_block() const { return &pseudo_exit_block_; }
185 
186   using GetBlocksFunction =
187       std::function<const std::vector<BasicBlock*>*(const BasicBlock*)>;
188   /// Returns the block successors function for the augmented CFG.
189   GetBlocksFunction AugmentedCFGSuccessorsFunction() const;
190   /// Returns the block predecessors function for the augmented CFG.
191   GetBlocksFunction AugmentedCFGPredecessorsFunction() const;
192   /// Returns the block structural successors function for the augmented CFG.
193   GetBlocksFunction AugmentedStructuralCFGSuccessorsFunction() const;
194   /// Returns the block structural predecessors function for the augmented CFG.
195   GetBlocksFunction AugmentedStructuralCFGPredecessorsFunction() const;
196 
197   /// Returns the control flow nesting depth of the given basic block.
198   /// This function only works when you have structured control flow.
199   /// This function should only be called after the control flow constructs have
200   /// been identified and dominators have been computed.
201   int GetBlockDepth(BasicBlock* bb);
202 
203   /// Prints a GraphViz digraph of the CFG of the current function
204   void PrintDotGraph() const;
205 
206   /// Prints a directed graph of the CFG of the current function
207   void PrintBlocks() const;
208 
209   /// Registers execution model limitation such as "Feature X is only available
210   /// with Execution Model Y".
211   void RegisterExecutionModelLimitation(spv::ExecutionModel model,
212                                         const std::string& message);
213 
214   /// Registers execution model limitation with an |is_compatible| functor.
RegisterExecutionModelLimitation(std::function<bool (spv::ExecutionModel,std::string *)> is_compatible)215   void RegisterExecutionModelLimitation(
216       std::function<bool(spv::ExecutionModel, std::string*)> is_compatible) {
217     execution_model_limitations_.push_back(is_compatible);
218   }
219 
220   /// Registers limitation with an |is_compatible| functor.
RegisterLimitation(std::function<bool (const ValidationState_t & _,const Function *,std::string *)> is_compatible)221   void RegisterLimitation(std::function<bool(const ValidationState_t& _,
222                                              const Function*, std::string*)>
223                               is_compatible) {
224     limitations_.push_back(is_compatible);
225   }
226 
227   bool CheckLimitations(const ValidationState_t& _, const Function* entry_point,
228                         std::string* reason) const;
229 
230   /// Returns true if the given execution model passes the limitations stored in
231   /// execution_model_limitations_. Returns false otherwise and fills optional
232   /// |reason| parameter.
233   bool IsCompatibleWithExecutionModel(spv::ExecutionModel model,
234                                       std::string* reason = nullptr) const;
235 
236   // Inserts id to the set of functions called from this function.
AddFunctionCallTarget(uint32_t call_target_id)237   void AddFunctionCallTarget(uint32_t call_target_id) {
238     function_call_targets_.insert(call_target_id);
239   }
240 
241   // Returns a set with ids of all functions called from this function.
function_call_targets()242   const std::set<uint32_t> function_call_targets() const {
243     return function_call_targets_;
244   }
245 
246   // Returns the block containing the OpSelectionMerge or OpLoopMerge that
247   // references |merge_block|.
248   // Values of |merge_block_header_| inserted by CFGPass, so do not call before
249   // the first iteration of ordered instructions in
250   // ValidateBinaryUsingContextAndValidationState has completed.
GetMergeHeader(BasicBlock * merge_block)251   BasicBlock* GetMergeHeader(BasicBlock* merge_block) {
252     return merge_block_header_[merge_block];
253   }
254 
255   // Returns vector of the blocks containing a OpLoopMerge that references
256   // |continue_target|.
257   // Values of |continue_target_headers_| inserted by CFGPass, so do not call
258   // before the first iteration of ordered instructions in
259   // ValidateBinaryUsingContextAndValidationState has completed.
GetContinueHeaders(BasicBlock * continue_target)260   std::vector<BasicBlock*> GetContinueHeaders(BasicBlock* continue_target) {
261     if (continue_target_headers_.find(continue_target) ==
262         continue_target_headers_.end()) {
263       return {};
264     }
265     return continue_target_headers_[continue_target];
266   }
267 
268  private:
269   // Computes the representation of the augmented CFG.
270   // Populates augmented_successors_map_ and augmented_predecessors_map_.
271   void ComputeAugmentedCFG();
272 
273   // Adds a copy of the given Construct, and tracks it by its entry block.
274   // Returns a reference to the stored construct.
275   Construct& AddConstruct(const Construct& new_construct);
276 
277   // Returns a reference to the construct corresponding to the given entry
278   // block.
279   Construct& FindConstructForEntryBlock(const BasicBlock* entry_block,
280                                         ConstructType t);
281 
282   /// The result id of the OpLabel that defined this block
283   uint32_t id_;
284 
285   /// The type of the function
286   uint32_t function_type_id_;
287 
288   /// The type of the return value
289   uint32_t result_type_id_;
290 
291   /// The control fo the function
292   spv::FunctionControlMask function_control_;
293 
294   /// The type of declaration of each function
295   FunctionDecl declaration_type_;
296 
297   // Have we finished parsing this function?
298   bool end_has_been_registered_;
299 
300   /// The blocks in the function mapped by block ID
301   std::unordered_map<uint32_t, BasicBlock> blocks_;
302 
303   /// A list of blocks in the order they appeared in the binary
304   std::vector<BasicBlock*> ordered_blocks_;
305 
306   /// Blocks which are forward referenced by blocks but not defined
307   std::unordered_set<uint32_t> undefined_blocks_;
308 
309   /// The block that is currently being parsed
310   BasicBlock* current_block_;
311 
312   /// A pseudo entry node used in dominance analysis.
313   /// After the function end has been registered, the successor list of the
314   /// pseudo entry node is the minimal set of nodes such that all nodes in the
315   /// CFG can be reached by following successor lists.  That is, the successors
316   /// will be:
317   ///   - Any basic block without predecessors.  This includes the entry
318   ///     block to the function.
319   ///   - A single node from each otherwise unreachable cycle in the CFG, if
320   ///     such cycles exist.
321   /// The pseudo entry node does not appear in the predecessor or successor
322   /// list of any ordinary block.
323   /// It has no predecessors.
324   /// It has Id 0.
325   BasicBlock pseudo_entry_block_;
326 
327   /// A pseudo exit block used in dominance analysis.
328   /// After the function end has been registered, the predecessor list of the
329   /// pseudo exit node is the minimal set of nodes such that all nodes in the
330   /// CFG can be reached by following predecessor lists.  That is, the
331   /// predecessors will be:
332   ///   - Any basic block without successors.  This includes any basic block
333   ///     ending with an OpReturn, OpReturnValue or similar instructions.
334   ///   - A single node from each otherwise unreachable cycle in the CFG, if
335   ///     such cycles exist.
336   /// The pseudo exit node does not appear in the predecessor or successor
337   /// list of any ordinary block.
338   /// It has no successors.
339   BasicBlock pseudo_exit_block_;
340 
341   // Maps a block to its successors in the augmented CFG, if that set is
342   // different from its successors in the ordinary CFG.
343   std::unordered_map<const BasicBlock*, std::vector<BasicBlock*>>
344       augmented_successors_map_;
345   // Maps a block to its predecessors in the augmented CFG, if that set is
346   // different from its predecessors in the ordinary CFG.
347   std::unordered_map<const BasicBlock*, std::vector<BasicBlock*>>
348       augmented_predecessors_map_;
349 
350   // Maps a structured loop header to its CFG successors and also its
351   // continue target if that continue target is not the loop header
352   // itself. This might have duplicates.
353   std::unordered_map<const BasicBlock*, std::vector<BasicBlock*>>
354       loop_header_successors_plus_continue_target_map_;
355 
356   /// The constructs that are available in this function
357   std::list<Construct> cfg_constructs_;
358 
359   /// The variable IDs of the functions
360   std::vector<uint32_t> variable_ids_;
361 
362   /// The function parameter ids of the functions
363   std::vector<uint32_t> parameter_ids_;
364 
365   /// Maps a construct's entry block to the construct(s).
366   /// Since a basic block may be the entry block of different types of
367   /// constructs, the type of the construct should also be specified in order to
368   /// get the unique construct.
369   std::unordered_map<std::pair<const BasicBlock*, ConstructType>, Construct*,
370                      bb_constr_type_pair_hash>
371       entry_block_to_construct_;
372 
373   /// This map provides the header block for a given merge block.
374   std::unordered_map<BasicBlock*, BasicBlock*> merge_block_header_;
375 
376   /// This map provides the header blocks for a given continue target.
377   std::unordered_map<BasicBlock*, std::vector<BasicBlock*>>
378       continue_target_headers_;
379 
380   /// Stores the control flow nesting depth of a given basic block
381   std::unordered_map<BasicBlock*, int> block_depth_;
382 
383   /// Stores execution model limitations imposed by instructions used within the
384   /// function. The functor stored in the list return true if execution model
385   /// is compatible, false otherwise. If the functor returns false, it can also
386   /// optionally fill the string parameter with the reason for incompatibility.
387   std::list<std::function<bool(spv::ExecutionModel, std::string*)>>
388       execution_model_limitations_;
389 
390   /// Stores limitations imposed by instructions used within the function.
391   /// Similar to execution_model_limitations_;
392   std::list<std::function<bool(const ValidationState_t& _, const Function*,
393                                std::string*)>>
394       limitations_;
395 
396   /// Stores ids of all functions called from this function.
397   std::set<uint32_t> function_call_targets_;
398 };
399 
400 }  // namespace val
401 }  // namespace spvtools
402 
403 #endif  // SOURCE_VAL_FUNCTION_H_
404