fuzzer_util.h 31 KB

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