1 // Copyright (c) 2019 Google LLC
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_FUZZ_FUZZER_UTIL_H_
16 #define SOURCE_FUZZ_FUZZER_UTIL_H_
17 
18 #include <iostream>
19 #include <map>
20 #include <vector>
21 
22 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
23 #include "source/fuzz/transformation_context.h"
24 #include "source/opt/basic_block.h"
25 #include "source/opt/instruction.h"
26 #include "source/opt/ir_context.h"
27 #include "source/opt/module.h"
28 #include "spirv-tools/libspirv.hpp"
29 
30 namespace spvtools {
31 namespace fuzz {
32 
33 // Provides types and global utility methods for use by the fuzzer
34 namespace fuzzerutil {
35 
36 // A silent message consumer.
37 extern const spvtools::MessageConsumer kSilentMessageConsumer;
38 
39 // Function type that produces a SPIR-V module.
40 using ModuleSupplier = std::function<std::unique_ptr<opt::IRContext>()>;
41 
42 // Builds a new opt::IRContext object. Returns true if successful and changes
43 // the |ir_context| parameter. Otherwise (if any errors occur), returns false
44 // and |ir_context| remains unchanged.
45 bool BuildIRContext(spv_target_env target_env,
46                     const spvtools::MessageConsumer& message_consumer,
47                     const std::vector<uint32_t>& binary_in,
48                     spv_validator_options validator_options,
49                     std::unique_ptr<spvtools::opt::IRContext>* ir_context);
50 
51 // Returns true if and only if the module does not define the given id.
52 bool IsFreshId(opt::IRContext* context, uint32_t id);
53 
54 // Updates the module's id bound if needed so that it is large enough to
55 // account for the given id.
56 void UpdateModuleIdBound(opt::IRContext* context, uint32_t id);
57 
58 // Return the block with id |maybe_block_id| if it exists, and nullptr
59 // otherwise.
60 opt::BasicBlock* MaybeFindBlock(opt::IRContext* context,
61                                 uint32_t maybe_block_id);
62 
63 // When adding an edge from |bb_from| to |bb_to| (which are assumed to be blocks
64 // in the same function), it is important to supply |bb_to| with ids that can be
65 // used to augment OpPhi instructions in the case that there is not already such
66 // an edge.  This function returns true if and only if the ids provided in
67 // |phi_ids| suffice for this purpose,
68 bool PhiIdsOkForNewEdge(
69     opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
70     const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
71 
72 // Returns an OpBranchConditional instruction that will create an unreachable
73 // branch from |bb_from_id| to |bb_to_id|. |bool_id| must be a result id of
74 // either OpConstantTrue or OpConstantFalse. Based on the opcode of |bool_id|,
75 // operands of the returned instruction will be positioned in a way that the
76 // branch from |bb_from_id| to |bb_to_id| is always unreachable.
77 opt::Instruction CreateUnreachableEdgeInstruction(opt::IRContext* ir_context,
78                                                   uint32_t bb_from_id,
79                                                   uint32_t bb_to_id,
80                                                   uint32_t bool_id);
81 
82 // Requires that |bool_id| is a valid result id of either OpConstantTrue or
83 // OpConstantFalse, that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids)
84 // holds, and that bb_from ends with "OpBranch %some_block".  Turns OpBranch
85 // into "OpBranchConditional |condition_value| ...", such that control will
86 // branch to %some_block, with |bb_to| being the unreachable alternative.
87 // Updates OpPhi instructions in |bb_to| using |phi_ids| so that the new edge is
88 // valid. |condition_value| above is equal to |true| if |bool_id| is a result id
89 // of an OpConstantTrue instruction.
90 void AddUnreachableEdgeAndUpdateOpPhis(
91     opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
92     uint32_t bool_id,
93     const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
94 
95 // Returns true if and only if |loop_header_id| is a loop header and
96 // |block_id| is a reachable block branching to and dominated by
97 // |loop_header_id|.
98 bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id,
99                      uint32_t loop_header_id);
100 
101 // Returns true if and only if |maybe_loop_header_id| is a loop header and
102 // |block_id| is in the continue construct of the associated loop.
103 bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
104                                     uint32_t maybe_loop_header_id);
105 
106 // If |block| contains |inst|, an iterator for |inst| is returned.
107 // Otherwise |block|->end() is returned.
108 opt::BasicBlock::iterator GetIteratorForInstruction(
109     opt::BasicBlock* block, const opt::Instruction* inst);
110 
111 // Determines whether it is OK to insert an instruction with opcode |opcode|
112 // before |instruction_in_block|.
113 bool CanInsertOpcodeBeforeInstruction(
114     SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block);
115 
116 // Determines whether it is OK to make a synonym of |inst|.
117 // |transformation_context| is used to verify that the result id of |inst|
118 // does not participate in IdIsIrrelevant fact.
119 bool CanMakeSynonymOf(opt::IRContext* ir_context,
120                       const TransformationContext& transformation_context,
121                       const opt::Instruction& inst);
122 
123 // Determines whether the given type is a composite; that is: an array, matrix,
124 // struct or vector.
125 bool IsCompositeType(const opt::analysis::Type* type);
126 
127 // Returns a vector containing the same elements as |repeated_field|.
128 std::vector<uint32_t> RepeatedFieldToVector(
129     const google::protobuf::RepeatedField<uint32_t>& repeated_field);
130 
131 // Given a type id, |base_object_type_id|, returns 0 if the type is not a
132 // composite type or if |index| is too large to be used as an index into the
133 // composite.  Otherwise returns the type id of the type associated with the
134 // composite's index.
135 //
136 // Example: if |base_object_type_id| is 10, and we have:
137 //
138 // %10 = OpTypeStruct %3 %4 %5
139 //
140 // then 3 will be returned if |index| is 0, 5 if |index| is 2, and 0 if index
141 // is 3 or larger.
142 uint32_t WalkOneCompositeTypeIndex(opt::IRContext* context,
143                                    uint32_t base_object_type_id,
144                                    uint32_t index);
145 
146 // Given a type id, |base_object_type_id|, checks that the given sequence of
147 // |indices| is suitable for indexing into this type.  Returns the id of the
148 // type of the final sub-object reached via the indices if they are valid, and
149 // 0 otherwise.
150 uint32_t WalkCompositeTypeIndices(
151     opt::IRContext* context, uint32_t base_object_type_id,
152     const google::protobuf::RepeatedField<google::protobuf::uint32>& indices);
153 
154 // Returns the number of members associated with |struct_type_instruction|,
155 // which must be an OpStructType instruction.
156 uint32_t GetNumberOfStructMembers(
157     const opt::Instruction& struct_type_instruction);
158 
159 // Returns the constant size of the array associated with
160 // |array_type_instruction|, which must be an OpArrayType instruction. Returns
161 // 0 if there is not a static size.
162 uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
163                       opt::IRContext* context);
164 
165 // Returns the bound for indexing into a composite of type
166 // |composite_type_inst|, i.e. the number of fields of a struct, the size of an
167 // array, the number of components of a vector, or the number of columns of a
168 // matrix. |composite_type_inst| must be the type of a composite.
169 uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst,
170                                    opt::IRContext* ir_context);
171 
172 // Returns memory semantics mask for specific storage class.
173 SpvMemorySemanticsMask GetMemorySemanticsForStorageClass(
174     SpvStorageClass storage_class);
175 
176 // Returns true if and only if |context| is valid, according to the validator
177 // instantiated with |validator_options|.  |consumer| is used for error
178 // reporting.
179 bool IsValid(const opt::IRContext* context,
180              spv_validator_options validator_options, MessageConsumer consumer);
181 
182 // Returns true if and only if IsValid(|context|, |validator_options|) holds,
183 // and furthermore every basic block in |context| has its enclosing function as
184 // its parent, and every instruction in |context| has a distinct unique id.
185 // |consumer| is used for error reporting.
186 bool IsValidAndWellFormed(const opt::IRContext* context,
187                           spv_validator_options validator_options,
188                           MessageConsumer consumer);
189 
190 // Returns a clone of |context|, by writing |context| to a binary and then
191 // parsing it again.
192 std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context);
193 
194 // Returns true if and only if |id| is the id of a type that is not a function
195 // type.
196 bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id);
197 
198 // Returns true if and only if |block_id| is a merge block or continue target
199 bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id);
200 
201 // Returns the id of the header of the loop corresponding to the given loop
202 // merge block. Returns 0 if |merge_block_id| is not a loop merge block.
203 uint32_t GetLoopFromMergeBlock(opt::IRContext* ir_context,
204                                uint32_t merge_block_id);
205 
206 // Returns the result id of an instruction of the form:
207 //  %id = OpTypeFunction |type_ids|
208 // or 0 if no such instruction exists.
209 uint32_t FindFunctionType(opt::IRContext* ir_context,
210                           const std::vector<uint32_t>& type_ids);
211 
212 // Returns a type instruction (OpTypeFunction) for |function|.
213 // Returns |nullptr| if type is not found.
214 opt::Instruction* GetFunctionType(opt::IRContext* context,
215                                   const opt::Function* function);
216 
217 // Returns the function with result id |function_id|, or |nullptr| if no such
218 // function exists.
219 opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id);
220 
221 // Returns true if |function| has a block that the termination instruction is
222 // OpKill or OpUnreachable.
223 bool FunctionContainsOpKillOrUnreachable(const opt::Function& function);
224 
225 // Returns |true| if one of entry points has function id |function_id|.
226 bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id);
227 
228 // Checks whether |id| is available (according to dominance rules) at the use
229 // point defined by input operand |use_input_operand_index| of
230 // |use_instruction|. |use_instruction| must be a in some basic block.
231 bool IdIsAvailableAtUse(opt::IRContext* context,
232                         opt::Instruction* use_instruction,
233                         uint32_t use_input_operand_index, uint32_t id);
234 
235 // Checks whether |id| is available (according to dominance rules) at the
236 // program point directly before |instruction|. |instruction| must be in some
237 // basic block.
238 bool IdIsAvailableBeforeInstruction(opt::IRContext* context,
239                                     opt::Instruction* instruction, uint32_t id);
240 
241 // Returns true if and only if |instruction| is an OpFunctionParameter
242 // associated with |function|.
243 bool InstructionIsFunctionParameter(opt::Instruction* instruction,
244                                     opt::Function* function);
245 
246 // Returns the type id of the instruction defined by |result_id|, or 0 if there
247 // is no such result id.
248 uint32_t GetTypeId(opt::IRContext* context, uint32_t result_id);
249 
250 // Given |pointer_type_inst|, which must be an OpTypePointer instruction,
251 // returns the id of the associated pointee type.
252 uint32_t GetPointeeTypeIdFromPointerType(opt::Instruction* pointer_type_inst);
253 
254 // Given |pointer_type_id|, which must be the id of a pointer type, returns the
255 // id of the associated pointee type.
256 uint32_t GetPointeeTypeIdFromPointerType(opt::IRContext* context,
257                                          uint32_t pointer_type_id);
258 
259 // Given |pointer_type_inst|, which must be an OpTypePointer instruction,
260 // returns the associated storage class.
261 SpvStorageClass GetStorageClassFromPointerType(
262     opt::Instruction* pointer_type_inst);
263 
264 // Given |pointer_type_id|, which must be the id of a pointer type, returns the
265 // associated storage class.
266 SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context,
267                                                uint32_t pointer_type_id);
268 
269 // Returns the id of a pointer with pointee type |pointee_type_id| and storage
270 // class |storage_class|, if it exists, and 0 otherwise.
271 uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
272                              SpvStorageClass storage_class);
273 
274 // Given an instruction |inst| and an operand absolute index |absolute_index|,
275 // returns the index of the operand restricted to the input operands.
276 uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst,
277                                         uint32_t absolute_index);
278 
279 // Returns true if and only if |type| is one of the types for which it is legal
280 // to have an OpConstantNull value. This may depend on the capabilities declared
281 // in |context|.
282 bool IsNullConstantSupported(opt::IRContext* context,
283                              const opt::Instruction& type);
284 
285 // Returns true if and only if the SPIR-V version being used requires that
286 // global variables accessed in the static call graph of an entry point need
287 // to be listed in that entry point's interface.
288 bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
289     const opt::IRContext* context);
290 
291 // Adds |id| into the interface of every entry point of the shader.
292 // Does nothing if SPIR-V doesn't require global variables, that are accessed
293 // from an entry point function, to be listed in that function's interface.
294 void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id);
295 
296 // Adds a global variable with storage class |storage_class| to the module, with
297 // type |type_id| and either no initializer or |initializer_id| as an
298 // initializer, depending on whether |initializer_id| is 0. The global variable
299 // has result id |result_id|. Updates module's id bound to accommodate for
300 // |result_id|.
301 //
302 // - |type_id| must be the id of a pointer type with the same storage class as
303 //   |storage_class|.
304 // - |storage_class| must be Private or Workgroup.
305 // - |initializer_id| must be 0 if |storage_class| is Workgroup, and otherwise
306 //   may either be 0 or the id of a constant whose type is the pointee type of
307 //   |type_id|.
308 //
309 // Returns a pointer to the new global variable instruction.
310 opt::Instruction* AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
311                                     uint32_t type_id,
312                                     SpvStorageClass storage_class,
313                                     uint32_t initializer_id);
314 
315 // Adds an instruction to the start of |function_id|, of the form:
316 //   |result_id| = OpVariable |type_id| Function |initializer_id|.
317 // Updates module's id bound to accommodate for |result_id|.
318 //
319 // - |type_id| must be the id of a pointer type with Function storage class.
320 // - |initializer_id| must be the id of a constant with the same type as the
321 //   pointer's pointee type.
322 // - |function_id| must be the id of a function.
323 //
324 // Returns a pointer to the new local variable instruction.
325 opt::Instruction* AddLocalVariable(opt::IRContext* context, uint32_t result_id,
326                                    uint32_t type_id, uint32_t function_id,
327                                    uint32_t initializer_id);
328 
329 // Returns true if the vector |arr| has duplicates.
330 bool HasDuplicates(const std::vector<uint32_t>& arr);
331 
332 // Checks that the given vector |arr| contains a permutation of a range
333 // [lo, hi]. That being said, all elements in the range are present without
334 // duplicates. If |arr| is empty, returns true iff |lo > hi|.
335 bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
336                           uint32_t hi);
337 
338 // Returns OpFunctionParameter instructions corresponding to the function
339 // with result id |function_id|.
340 std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
341                                              uint32_t function_id);
342 
343 // Removes an OpFunctionParameter instruction with result id |parameter_id|
344 // from the its function. Parameter's function must not be an entry-point
345 // function. The function must have a parameter with result id |parameter_id|.
346 //
347 // Prefer using this function to opt::Function::RemoveParameter since
348 // this function also guarantees that |ir_context| has no invalid pointers
349 // to the removed parameter.
350 void RemoveParameter(opt::IRContext* ir_context, uint32_t parameter_id);
351 
352 // Returns all OpFunctionCall instructions that call a function with result id
353 // |function_id|.
354 std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
355                                           uint32_t function_id);
356 
357 // Returns a function that contains OpFunctionParameter instruction with result
358 // id |param_id|. Returns nullptr if the module has no such function.
359 opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
360                                           uint32_t param_id);
361 
362 // Changes the type of function |function_id| so that its return type is
363 // |return_type_id| and its parameters' types are |parameter_type_ids|. If a
364 // suitable function type already exists in the module, it is used, otherwise
365 // |new_function_type_result_id| is used as the result id of a suitable new
366 // function type instruction. If the old type of the function doesn't have any
367 // more users, it is removed from the module. Returns the result id of the
368 // OpTypeFunction instruction that is used as a type of the function with
369 // |function_id|.
370 //
371 // CAUTION: When the old type of the function is removed from the module, its
372 //          memory is deallocated. Be sure not to use any pointers to the old
373 //          type when this function returns.
374 uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id,
375                             uint32_t new_function_type_result_id,
376                             uint32_t return_type_id,
377                             const std::vector<uint32_t>& parameter_type_ids);
378 
379 // Creates new OpTypeFunction instruction in the module. |type_ids| may not be
380 // empty. It may not contain result ids of OpTypeFunction instructions.
381 // |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|.
382 // |result_id| may not equal to 0. Updates module's id bound to accommodate for
383 // |result_id|.
384 void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id,
385                      const std::vector<uint32_t>& type_ids);
386 
387 // Returns a result id of an OpTypeFunction instruction in the module. Creates a
388 // new instruction if required and returns |result_id|. type_ids| may not be
389 // empty. It may not contain result ids of OpTypeFunction instructions.
390 // |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|.
391 // |result_id| must not be equal to 0. Updates module's id bound to accommodate
392 // for |result_id|.
393 uint32_t FindOrCreateFunctionType(opt::IRContext* ir_context,
394                                   uint32_t result_id,
395                                   const std::vector<uint32_t>& type_ids);
396 
397 // Returns a result id of an OpTypeInt instruction if present. Returns 0
398 // otherwise.
399 uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width,
400                              bool is_signed);
401 
402 // Returns a result id of an OpTypeFloat instruction if present. Returns 0
403 // otherwise.
404 uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width);
405 
406 // Returns a result id of an OpTypeBool instruction if present. Returns 0
407 // otherwise.
408 uint32_t MaybeGetBoolType(opt::IRContext* ir_context);
409 
410 // Returns a result id of an OpTypeVector instruction if present. Returns 0
411 // otherwise. |component_type_id| must be a valid result id of an OpTypeInt,
412 // OpTypeFloat or OpTypeBool instruction in the module. |element_count| must be
413 // in the range [2, 4].
414 uint32_t MaybeGetVectorType(opt::IRContext* ir_context,
415                             uint32_t component_type_id, uint32_t element_count);
416 
417 // Returns a result id of an OpTypeStruct instruction whose field types exactly
418 // match |component_type_ids| if such an instruction is present. Returns 0
419 // otherwise. |component_type_ids| may not contain a result id of an
420 // OpTypeFunction.
421 uint32_t MaybeGetStructType(opt::IRContext* ir_context,
422                             const std::vector<uint32_t>& component_type_ids);
423 
424 // Returns a result id of an OpTypeVoid instruction if present. Returns 0
425 // otherwise.
426 uint32_t MaybeGetVoidType(opt::IRContext* ir_context);
427 
428 // Recursive definition is the following:
429 // - if |scalar_or_composite_type_id| is a result id of a scalar type - returns
430 //   a result id of the following constants (depending on the type): int -> 0,
431 //   float -> 0.0, bool -> false.
432 // - otherwise, returns a result id of an OpConstantComposite instruction.
433 //   Every component of the composite constant is looked up by calling this
434 //   function with the type id of that component.
435 // Returns 0 if no such instruction is present in the module.
436 // The returned id either participates in IdIsIrrelevant fact or not, depending
437 // on the |is_irrelevant| parameter.
438 uint32_t MaybeGetZeroConstant(
439     opt::IRContext* ir_context,
440     const TransformationContext& transformation_context,
441     uint32_t scalar_or_composite_type_id, bool is_irrelevant);
442 
443 // Returns true if it is possible to create an OpConstant or an
444 // OpConstantComposite instruction of type |type_id|. That is, returns true if
445 // the type associated with |type_id| and all its constituents are either scalar
446 // or composite.
447 bool CanCreateConstant(opt::IRContext* ir_context, uint32_t type_id);
448 
449 // Returns the result id of an OpConstant instruction. |scalar_type_id| must be
450 // a result id of a scalar type (i.e. int, float or bool). Returns 0 if no such
451 // instruction is present in the module. The returned id either participates in
452 // IdIsIrrelevant fact or not, depending on the |is_irrelevant| parameter.
453 uint32_t MaybeGetScalarConstant(
454     opt::IRContext* ir_context,
455     const TransformationContext& transformation_context,
456     const std::vector<uint32_t>& words, uint32_t scalar_type_id,
457     bool is_irrelevant);
458 
459 // Returns the result id of an OpConstantComposite instruction.
460 // |composite_type_id| must be a result id of a composite type (i.e. vector,
461 // matrix, struct or array). Returns 0 if no such instruction is present in the
462 // module. The returned id either participates in IdIsIrrelevant fact or not,
463 // depending on the |is_irrelevant| parameter.
464 uint32_t MaybeGetCompositeConstant(
465     opt::IRContext* ir_context,
466     const TransformationContext& transformation_context,
467     const std::vector<uint32_t>& component_ids, uint32_t composite_type_id,
468     bool is_irrelevant);
469 
470 // Returns the result id of an OpConstant instruction of integral type.
471 // Returns 0 if no such instruction or type is present in the module.
472 // The returned id either participates in IdIsIrrelevant fact or not, depending
473 // on the |is_irrelevant| parameter.
474 uint32_t MaybeGetIntegerConstant(
475     opt::IRContext* ir_context,
476     const TransformationContext& transformation_context,
477     const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
478     bool is_irrelevant);
479 
480 // Returns the id of a 32-bit integer constant in the module with type
481 // |int_type_id| and value |value|, or 0 if no such constant exists in the
482 // module. |int_type_id| must exist in the module and it must correspond to a
483 // 32-bit integer type.
484 uint32_t MaybeGetIntegerConstantFromValueAndType(opt::IRContext* ir_context,
485                                                  uint32_t value,
486                                                  uint32_t int_type_id);
487 
488 // Returns the result id of an OpConstant instruction of floating-point type.
489 // Returns 0 if no such instruction or type is present in the module.
490 // The returned id either participates in IdIsIrrelevant fact or not, depending
491 // on the |is_irrelevant| parameter.
492 uint32_t MaybeGetFloatConstant(
493     opt::IRContext* ir_context,
494     const TransformationContext& transformation_context,
495     const std::vector<uint32_t>& words, uint32_t width, bool is_irrelevant);
496 
497 // Returns the id of a boolean constant with value |value| if it exists in the
498 // module, or 0 otherwise. The returned id either participates in IdIsIrrelevant
499 // fact or not, depending on the |is_irrelevant| parameter.
500 uint32_t MaybeGetBoolConstant(
501     opt::IRContext* context,
502     const TransformationContext& transformation_context, bool value,
503     bool is_irrelevant);
504 
505 // Returns a vector of words representing the integer |value|, only considering
506 // the last |width| bits. The last |width| bits are sign-extended if the value
507 // is signed, zero-extended if it is unsigned.
508 // |width| must be <= 64.
509 // If |width| <= 32, returns a vector containing one value. If |width| > 64,
510 // returns a vector containing two values, with the first one representing the
511 // lower-order word of the value and the second one representing the
512 // higher-order word.
513 std::vector<uint32_t> IntToWords(uint64_t value, uint32_t width,
514                                  bool is_signed);
515 
516 // Returns a bit pattern that represents a floating-point |value|.
FloatToWord(float value)517 inline uint32_t FloatToWord(float value) {
518   uint32_t result;
519   memcpy(&result, &value, sizeof(uint32_t));
520   return result;
521 }
522 
523 // Returns true if any of the following is true:
524 // - |type1_id| and |type2_id| are the same id
525 // - |type1_id| and |type2_id| refer to integer scalar or vector types, only
526 //   differing by their signedness.
527 bool TypesAreEqualUpToSign(opt::IRContext* ir_context, uint32_t type1_id,
528                            uint32_t type2_id);
529 
530 // Converts repeated field of UInt32Pair to a map. If two or more equal values
531 // of |UInt32Pair::first()| are available in |data|, the last value of
532 // |UInt32Pair::second()| is used.
533 std::map<uint32_t, uint32_t> RepeatedUInt32PairToMap(
534     const google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>& data);
535 
536 // Converts a map into a repeated field of UInt32Pair.
537 google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>
538 MapToRepeatedUInt32Pair(const std::map<uint32_t, uint32_t>& data);
539 
540 // Returns the last instruction in |block_id| before which an instruction with
541 // opcode |opcode| can be inserted, or nullptr if there is no such instruction.
542 opt::Instruction* GetLastInsertBeforeInstruction(opt::IRContext* ir_context,
543                                                  uint32_t block_id,
544                                                  SpvOp opcode);
545 
546 // Checks whether various conditions hold related to the acceptability of
547 // replacing the id use at |use_in_operand_index| of |use_instruction| with a
548 // synonym or another id of appropriate type if the original id is irrelevant.
549 // In particular, this checks that:
550 // - If id use is an index of an irrelevant id (|use_in_operand_index > 0|)
551 //   in OpAccessChain - it can't be replaced.
552 // - The id use is not an index into a struct field in an OpAccessChain - such
553 //   indices must be constants, so it is dangerous to replace them.
554 // - The id use is not a pointer function call argument, on which there are
555 //   restrictions that make replacement problematic.
556 // - The id use is not the Sample parameter of an OpImageTexelPointer
557 //   instruction, as this must satisfy particular requirements.
558 bool IdUseCanBeReplaced(opt::IRContext* ir_context,
559                         const TransformationContext& transformation_context,
560                         opt::Instruction* use_instruction,
561                         uint32_t use_in_operand_index);
562 
563 // Requires that |struct_type_id| is the id of a struct type, and (as per the
564 // SPIR-V spec) that either all or none of the members of |struct_type_id| have
565 // the BuiltIn decoration. Returns true if and only if all members have the
566 // BuiltIn decoration.
567 bool MembersHaveBuiltInDecoration(opt::IRContext* ir_context,
568                                   uint32_t struct_type_id);
569 
570 // Returns true if and only if |id| is decorated with either Block or
571 // BufferBlock.  Even though these decorations are only allowed on struct types,
572 // for convenience |id| can be any result id so that it is possible to call this
573 // method on something that *might* be a struct type.
574 bool HasBlockOrBufferBlockDecoration(opt::IRContext* ir_context, uint32_t id);
575 
576 // Returns true iff splitting block |block_to_split| just before the instruction
577 // |split_before| would separate an OpSampledImage instruction from its usage.
578 bool SplittingBeforeInstructionSeparatesOpSampledImageDefinitionFromUse(
579     opt::BasicBlock* block_to_split, opt::Instruction* split_before);
580 
581 // Returns true if the instruction given has no side effects.
582 // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3758): Add any
583 //  missing instructions to the list. In particular, GLSL extended instructions
584 //  (called using OpExtInst) have not been considered.
585 bool InstructionHasNoSideEffects(const opt::Instruction& instruction);
586 
587 // Returns a set of the ids of all the return blocks that are reachable from
588 // the entry block of |function_id|.
589 // Assumes that the function exists in the module.
590 std::set<uint32_t> GetReachableReturnBlocks(opt::IRContext* ir_context,
591                                             uint32_t function_id);
592 
593 // Returns true if changing terminator instruction to |new_terminator| in the
594 // basic block with id |block_id| preserves domination rules and valid block
595 // order (i.e. dominator must always appear before dominated in the CFG).
596 // Returns false otherwise.
597 bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context,
598                                            uint32_t block_id,
599                                            opt::Instruction new_terminator);
600 
601 // Return the iterator that points to the function with the corresponding
602 // function id. If the function is not found, return the pointer pointing to
603 // module()->end().
604 opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context,
605                                           uint32_t function_id);
606 
607 // Returns true if the instruction with opcode |opcode| does not change its
608 // behaviour depending on the signedness of the operand at
609 // |use_in_operand_index|.
610 // Assumes that the operand must be the id of an integer scalar or vector.
611 bool IsAgnosticToSignednessOfOperand(SpvOp opcode,
612                                      uint32_t use_in_operand_index);
613 
614 // Returns true if |type_id_1| and |type_id_2| represent compatible types
615 // given the context of the instruction with |opcode| (i.e. we can replace
616 // an operand of |opcode| of the first type with an id of the second type
617 // and vice-versa).
618 bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode,
619                         uint32_t use_in_operand_index, uint32_t type_id_1,
620                         uint32_t type_id_2);
621 
622 }  // namespace fuzzerutil
623 }  // namespace fuzz
624 }  // namespace spvtools
625 
626 #endif  // SOURCE_FUZZ_FUZZER_UTIL_H_
627