/src/spirv-tools/source/val/function.h
Line | Count | Source |
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 { |
38 | | std::size_t operator()( |
39 | 288k | const std::pair<const BasicBlock*, ConstructType>& p) const { |
40 | 288k | auto h1 = std::hash<const BasicBlock*>{}(p.first); |
41 | 288k | auto h2 = std::hash<std::underlying_type<ConstructType>::type>{}( |
42 | 288k | static_cast<std::underlying_type<ConstructType>::type>(p.second)); |
43 | 288k | return (h1 ^ h2); |
44 | 288k | } |
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 |
143 | 3.47M | uint32_t id() const { return id_; } |
144 | | |
145 | | /// Returns return type id of the function |
146 | 48.0k | 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; |
150 | 36 | const std::unordered_set<uint32_t>& undefined_blocks() const { |
151 | 36 | return undefined_blocks_; |
152 | 36 | } |
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 |
175 | 295k | BasicBlock* pseudo_entry_block() { return &pseudo_entry_block_; } |
176 | | |
177 | | /// Returns the pseudo exit block |
178 | 0 | const BasicBlock* pseudo_entry_block() const { return &pseudo_entry_block_; } |
179 | | |
180 | | /// Returns the pseudo exit block |
181 | 30.6k | BasicBlock* pseudo_exit_block() { return &pseudo_exit_block_; } |
182 | | |
183 | | /// Returns the pseudo exit block |
184 | 0 | 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. |
215 | | void RegisterExecutionModelLimitation( |
216 | 5.99k | std::function<bool(spv::ExecutionModel, std::string*)> is_compatible) { |
217 | 5.99k | execution_model_limitations_.push_back(is_compatible); |
218 | 5.99k | } |
219 | | |
220 | | /// Registers limitation with an |is_compatible| functor. |
221 | | void RegisterLimitation(std::function<bool(const ValidationState_t& _, |
222 | | const Function*, std::string*)> |
223 | 3.79k | is_compatible) { |
224 | 3.79k | limitations_.push_back(is_compatible); |
225 | 3.79k | } |
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. |
237 | 31.7k | void AddFunctionCallTarget(uint32_t call_target_id) { |
238 | 31.7k | function_call_targets_.insert(call_target_id); |
239 | 31.7k | } |
240 | | |
241 | | // Returns a set with ids of all functions called from this function. |
242 | 81.2k | const std::set<uint32_t> function_call_targets() const { |
243 | 81.2k | return function_call_targets_; |
244 | 81.2k | } |
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. |
251 | 0 | BasicBlock* GetMergeHeader(BasicBlock* merge_block) { |
252 | 0 | return merge_block_header_[merge_block]; |
253 | 0 | } |
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. |
260 | 0 | std::vector<BasicBlock*> GetContinueHeaders(BasicBlock* continue_target) { |
261 | 0 | if (continue_target_headers_.find(continue_target) == |
262 | 0 | continue_target_headers_.end()) { |
263 | 0 | return {}; |
264 | 0 | } |
265 | 0 | return continue_target_headers_[continue_target]; |
266 | 0 | } |
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 OpFunction |
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_ |