• 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 LIBSPIRV_VAL_FUNCTION_H_
16 #define LIBSPIRV_VAL_FUNCTION_H_
17 
18 #include <functional>
19 #include <list>
20 #include <unordered_map>
21 #include <unordered_set>
22 #include <vector>
23 
24 #include "spirv-tools/libspirv.h"
25 #include "spirv/1.2/spirv.h"
26 #include "val/basic_block.h"
27 #include "val/construct.h"
28 
29 namespace libspirv {
30 
31 struct bb_constr_type_pair_hash {
operatorbb_constr_type_pair_hash32   std::size_t operator()(
33       const std::pair<const BasicBlock*, ConstructType>& p) const {
34     auto h1 = std::hash<const BasicBlock*>{}(p.first);
35     auto h2 = std::hash<std::underlying_type<ConstructType>::type>{}(
36         static_cast<std::underlying_type<ConstructType>::type>(p.second));
37     return (h1 ^ h2);
38   }
39 };
40 
41 enum class FunctionDecl {
42   kFunctionDeclUnknown,      /// < Unknown function declaration
43   kFunctionDeclDeclaration,  /// < Function declaration
44   kFunctionDeclDefinition    /// < Function definition
45 };
46 
47 /// This class manages all function declaration and definitions in a module. It
48 /// handles the state and id information while parsing a function in the SPIR-V
49 /// binary.
50 class Function {
51  public:
52   Function(uint32_t id, uint32_t result_type_id,
53            SpvFunctionControlMask function_control, uint32_t function_type_id);
54 
55   /// Registers a function parameter in the current function
56   /// @return Returns SPV_SUCCESS if the call was successful
57   spv_result_t RegisterFunctionParameter(uint32_t id, uint32_t type_id);
58 
59   /// Sets the declaration type of the current function
60   /// @return Returns SPV_SUCCESS if the call was successful
61   spv_result_t RegisterSetFunctionDeclType(FunctionDecl type);
62 
63   /// Registers a block in the current function. Subsequent block instructions
64   /// will target this block
65   /// @param id The ID of the label of the block
66   /// @return Returns SPV_SUCCESS if the call was successful
67   spv_result_t RegisterBlock(uint32_t id, bool is_definition = true);
68 
69   /// Registers a variable in the current block
70   ///
71   /// @param[in] type_id The type ID of the varaible
72   /// @param[in] id      The ID of the varaible
73   /// @param[in] storage The storage of the variable
74   /// @param[in] init_id The initializer ID of the variable
75   ///
76   /// @return Returns SPV_SUCCESS if the call was successful
77   spv_result_t RegisterBlockVariable(uint32_t type_id, uint32_t id,
78                                      SpvStorageClass storage, uint32_t init_id);
79 
80   /// Registers a loop merge construct in the function
81   ///
82   /// @param[in] merge_id The merge block ID of the loop
83   /// @param[in] continue_id The continue block ID of the loop
84   ///
85   /// @return Returns SPV_SUCCESS if the call was successful
86   spv_result_t RegisterLoopMerge(uint32_t merge_id, uint32_t continue_id);
87 
88   /// Registers a selection merge construct in the function
89   /// @return Returns SPV_SUCCESS if the call was successful
90   spv_result_t RegisterSelectionMerge(uint32_t merge_id);
91 
92   /// Registers the end of the block
93   ///
94   /// @param[in] successors_list A list of ids to the block's successors
95   /// @param[in] branch_instruction the branch instruction that ended the block
96   void RegisterBlockEnd(std::vector<uint32_t> successors_list,
97                         SpvOp branch_instruction);
98 
99   /// Registers the end of the function.  This is idempotent.
100   void RegisterFunctionEnd();
101 
102   /// Returns true if the \p id block is the first block of this function
103   bool IsFirstBlock(uint32_t id) const;
104 
105   /// Returns true if the \p merge_block_id is a BlockType of \p type
106   bool IsBlockType(uint32_t merge_block_id, BlockType type) const;
107 
108   /// Returns a pair consisting of the BasicBlock with \p id and a bool
109   /// which is true if the block has been defined, and false if it is
110   /// declared but not defined. This function will return nullptr if the
111   /// \p id was not declared and not defined at the current point in the binary
112   std::pair<const BasicBlock*, bool> GetBlock(uint32_t id) const;
113   std::pair<BasicBlock*, bool> GetBlock(uint32_t id);
114 
115   /// Returns the first block of the current function
116   const BasicBlock* first_block() const;
117 
118   /// Returns the first block of the current function
119   BasicBlock* first_block();
120 
121   /// Returns a vector of all the blocks in the function
122   const std::vector<BasicBlock*>& ordered_blocks() const;
123 
124   /// Returns a vector of all the blocks in the function
125   std::vector<BasicBlock*>& ordered_blocks();
126 
127   /// Returns a list of all the cfg constructs in the function
128   const std::list<Construct>& constructs() const;
129 
130   /// Returns a list of all the cfg constructs in the function
131   std::list<Construct>& constructs();
132 
133   /// Returns the number of blocks in the current function being parsed
134   size_t block_count() const;
135 
136   /// Returns the id of the funciton
id()137   uint32_t id() const { return id_; }
138 
139   /// Returns the number of blocks in the current function being parsed
140   size_t undefined_block_count() const;
undefined_blocks()141   const std::unordered_set<uint32_t>& undefined_blocks() const {
142     return undefined_blocks_;
143   }
144 
145   /// Returns the block that is currently being parsed in the binary
146   BasicBlock* current_block();
147 
148   /// Returns the block that is currently being parsed in the binary
149   const BasicBlock* current_block() const;
150 
151   // For dominance calculations, we want to analyze all the
152   // blocks in the function, even in degenerate control flow cases
153   // including unreachable blocks.  We therefore make an "augmented CFG"
154   // which is the same as the ordinary CFG but adds:
155   //  - A pseudo-entry node.
156   //  - A pseudo-exit node.
157   //  - A minimal set of edges so that a forward traversal from the
158   //    pseudo-entry node will visit all nodes.
159   //  - A minimal set of edges so that a backward traversal from the
160   //    pseudo-exit node will visit all nodes.
161   // In particular, the pseudo-entry node is the unique source of the
162   // augmented CFG, and the psueo-exit node is the unique sink of the
163   // augmented CFG.
164 
165   /// Returns the pseudo exit block
pseudo_entry_block()166   BasicBlock* pseudo_entry_block() { return &pseudo_entry_block_; }
167 
168   /// Returns the pseudo exit block
pseudo_entry_block()169   const BasicBlock* pseudo_entry_block() const { return &pseudo_entry_block_; }
170 
171   /// Returns the pseudo exit block
pseudo_exit_block()172   BasicBlock* pseudo_exit_block() { return &pseudo_exit_block_; }
173 
174   /// Returns the pseudo exit block
pseudo_exit_block()175   const BasicBlock* pseudo_exit_block() const { return &pseudo_exit_block_; }
176 
177   using GetBlocksFunction =
178       std::function<const std::vector<BasicBlock*>*(const BasicBlock*)>;
179   /// Returns the block successors function for the augmented CFG.
180   GetBlocksFunction AugmentedCFGSuccessorsFunction() const;
181   /// Like AugmentedCFGSuccessorsFunction, but also includes a forward edge from
182   /// a loop header block to its continue target, if they are different blocks.
183   GetBlocksFunction AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const;
184   /// Returns the block predecessors function for the augmented CFG.
185   GetBlocksFunction AugmentedCFGPredecessorsFunction() const;
186 
187   /// Returns the control flow nesting depth of the given basic block.
188   /// This function only works when you have structured control flow.
189   /// This function should only be called after the control flow constructs have
190   /// been identified and dominators have been computed.
191   int GetBlockDepth(BasicBlock* bb);
192 
193   /// Prints a GraphViz digraph of the CFG of the current funciton
194   void PrintDotGraph() const;
195 
196   /// Prints a directed graph of the CFG of the current funciton
197   void PrintBlocks() const;
198 
199  private:
200   // Computes the representation of the augmented CFG.
201   // Populates augmented_successors_map_ and augmented_predecessors_map_.
202   void ComputeAugmentedCFG();
203 
204   // Adds a copy of the given Construct, and tracks it by its entry block.
205   // Returns a reference to the stored construct.
206   Construct& AddConstruct(const Construct& new_construct);
207 
208   // Returns a reference to the construct corresponding to the given entry
209   // block.
210   Construct& FindConstructForEntryBlock(const BasicBlock* entry_block,
211                                         ConstructType t);
212 
213   /// The result id of the OpLabel that defined this block
214   uint32_t id_;
215 
216   /// The type of the function
217   uint32_t function_type_id_;
218 
219   /// The type of the return value
220   uint32_t result_type_id_;
221 
222   /// The control fo the funciton
223   SpvFunctionControlMask function_control_;
224 
225   /// The type of declaration of each function
226   FunctionDecl declaration_type_;
227 
228   // Have we finished parsing this function?
229   bool end_has_been_registered_;
230 
231   /// The blocks in the function mapped by block ID
232   std::unordered_map<uint32_t, BasicBlock> blocks_;
233 
234   /// A list of blocks in the order they appeared in the binary
235   std::vector<BasicBlock*> ordered_blocks_;
236 
237   /// Blocks which are forward referenced by blocks but not defined
238   std::unordered_set<uint32_t> undefined_blocks_;
239 
240   /// The block that is currently being parsed
241   BasicBlock* current_block_;
242 
243   /// A pseudo entry node used in dominance analysis.
244   /// After the function end has been registered, the successor list of the
245   /// pseudo entry node is the minimal set of nodes such that all nodes in the
246   /// CFG can be reached by following successor lists.  That is, the successors
247   /// will be:
248   ///   - Any basic block without predecessors.  This includes the entry
249   ///     block to the function.
250   ///   - A single node from each otherwise unreachable cycle in the CFG, if
251   ///     such cycles exist.
252   /// The pseudo entry node does not appear in the predecessor or successor
253   /// list of any ordinary block.
254   /// It has no predecessors.
255   /// It has Id 0.
256   BasicBlock pseudo_entry_block_;
257 
258   /// A pseudo exit block used in dominance analysis.
259   /// After the function end has been registered, the predecessor list of the
260   /// pseudo exit node is the minimal set of nodes such that all nodes in the
261   /// CFG can be reached by following predecessor lists.  That is, the
262   /// predecessors will be:
263   ///   - Any basic block without successors.  This includes any basic block
264   ///     ending with an OpReturn, OpReturnValue or similar instructions.
265   ///   - A single node from each otherwise unreachable cycle in the CFG, if
266   ///     such cycles exist.
267   /// The pseudo exit node does not appear in the predecessor or successor
268   /// list of any ordinary block.
269   /// It has no successors.
270   BasicBlock pseudo_exit_block_;
271 
272   // Maps a block to its successors in the augmented CFG, if that set is
273   // different from its successors in the ordinary CFG.
274   std::unordered_map<const BasicBlock*, std::vector<BasicBlock*>>
275       augmented_successors_map_;
276   // Maps a block to its predecessors in the augmented CFG, if that set is
277   // different from its predecessors in the ordinary CFG.
278   std::unordered_map<const BasicBlock*, std::vector<BasicBlock*>>
279       augmented_predecessors_map_;
280 
281   // Maps a structured loop header to its CFG successors and also its
282   // continue target if that continue target is not the loop header
283   // itself. This might have duplicates.
284   std::unordered_map<const BasicBlock*, std::vector<BasicBlock*>>
285       loop_header_successors_plus_continue_target_map_;
286 
287   /// The constructs that are available in this function
288   std::list<Construct> cfg_constructs_;
289 
290   /// The variable IDs of the functions
291   std::vector<uint32_t> variable_ids_;
292 
293   /// The function parameter ids of the functions
294   std::vector<uint32_t> parameter_ids_;
295 
296   /// Maps a construct's entry block to the construct(s).
297   /// Since a basic block may be the entry block of different types of
298   /// constructs, the type of the construct should also be specified in order to
299   /// get the unique construct.
300   std::unordered_map<std::pair<const BasicBlock*, ConstructType>, Construct*,
301                      libspirv::bb_constr_type_pair_hash>
302       entry_block_to_construct_;
303 
304   /// This map provides the header block for a given merge block.
305   std::unordered_map<BasicBlock*, BasicBlock*> merge_block_header_;
306 
307   /// Stores the control flow nesting depth of a given basic block
308   std::unordered_map<BasicBlock*, int> block_depth_;
309 };
310 
311 }  /// namespace libspirv
312 
313 #endif  /// LIBSPIRV_VAL_FUNCTION_H_
314