Coverage Report

Created: 2026-02-14 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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_