fuzzer_pass_donate_modules.h 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  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_PASS_DONATE_MODULES_H_
  15. #define SOURCE_FUZZ_FUZZER_PASS_DONATE_MODULES_H_
  16. #include <vector>
  17. #include "source/fuzz/fuzzer_pass.h"
  18. #include "source/fuzz/fuzzer_util.h"
  19. namespace spvtools {
  20. namespace fuzz {
  21. // A fuzzer pass that randomly adds code from other SPIR-V modules to the module
  22. // being transformed.
  23. class FuzzerPassDonateModules : public FuzzerPass {
  24. public:
  25. FuzzerPassDonateModules(
  26. opt::IRContext* ir_context, TransformationContext* transformation_context,
  27. FuzzerContext* fuzzer_context,
  28. protobufs::TransformationSequence* transformations,
  29. bool ignore_inapplicable_transformations,
  30. std::vector<fuzzerutil::ModuleSupplier> donor_suppliers);
  31. void Apply() override;
  32. // Donates the global declarations and functions of |donor_ir_context| into
  33. // the fuzzer pass's IR context. |make_livesafe| dictates whether the
  34. // functions of the donated module will be made livesafe (see
  35. // FactFunctionIsLivesafe).
  36. void DonateSingleModule(opt::IRContext* donor_ir_context, bool make_livesafe);
  37. private:
  38. // Adapts a storage class coming from a donor module so that it will work
  39. // in a recipient module, e.g. by changing Uniform to Private.
  40. static spv::StorageClass AdaptStorageClass(
  41. spv::StorageClass donor_storage_class);
  42. // Identifies all external instruction set imports in |donor_ir_context| and
  43. // populates |original_id_to_donated_id| with a mapping from the donor's id
  44. // for such an import to a corresponding import in the recipient. Aborts if
  45. // no such corresponding import is available.
  46. void HandleExternalInstructionImports(
  47. opt::IRContext* donor_ir_context,
  48. std::map<uint32_t, uint32_t>* original_id_to_donated_id);
  49. // Considers all types, globals, constants and undefs in |donor_ir_context|.
  50. // For each instruction, uses |original_to_donated_id| to map its result id to
  51. // either (1) the id of an existing identical instruction in the recipient, or
  52. // (2) to a fresh id, in which case the instruction is also added to the
  53. // recipient (with any operand ids that it uses being remapped via
  54. // |original_id_to_donated_id|).
  55. void HandleTypesAndValues(
  56. opt::IRContext* donor_ir_context,
  57. std::map<uint32_t, uint32_t>* original_id_to_donated_id);
  58. // Helper method for HandleTypesAndValues, to handle a single type/value.
  59. void HandleTypeOrValue(
  60. const opt::Instruction& type_or_value,
  61. std::map<uint32_t, uint32_t>* original_id_to_donated_id);
  62. // Assumes that |donor_ir_context| does not exhibit recursion. Considers the
  63. // functions in |donor_ir_context|'s call graph in a reverse-topologically-
  64. // sorted order (leaves-to-root), adding each function to the recipient
  65. // module, rewritten to use fresh ids and using |original_id_to_donated_id| to
  66. // remap ids. The |make_livesafe| argument captures whether the functions in
  67. // the module are required to be made livesafe before being added to the
  68. // recipient.
  69. void HandleFunctions(opt::IRContext* donor_ir_context,
  70. std::map<uint32_t, uint32_t>* original_id_to_donated_id,
  71. bool make_livesafe);
  72. // During donation we will have to ignore some instructions, e.g. because they
  73. // use opcodes that we cannot support or because they reference the ids of
  74. // instructions that have not been donated. This function encapsulates the
  75. // logic for deciding which whether instruction |instruction| from
  76. // |donor_ir_context| can be donated.
  77. bool CanDonateInstruction(
  78. opt::IRContext* donor_ir_context, const opt::Instruction& instruction,
  79. const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
  80. const std::set<uint32_t>& skipped_instructions) const;
  81. // We treat the OpArrayLength instruction specially. In the donor shader this
  82. // instruction yields the length of a runtime array that is the final member
  83. // of a struct. During donation, we will have converted the runtime array
  84. // type, and the associated struct field, into a fixed-size array.
  85. //
  86. // Instead of donating this instruction, we turn it into an OpCopyObject
  87. // instruction that copies the size of the fixed-size array.
  88. void HandleOpArrayLength(
  89. const opt::Instruction& instruction,
  90. std::map<uint32_t, uint32_t>* original_id_to_donated_id,
  91. std::vector<protobufs::Instruction>* donated_instructions) const;
  92. // The instruction |instruction| is required to be an instruction that cannot
  93. // be easily donated, either because it uses an unsupported opcode, has an
  94. // unsupported result type, or uses id operands that could not be donated.
  95. //
  96. // If |instruction| generates a result id, the function attempts to add a
  97. // substitute for |instruction| to |donated_instructions| that has the correct
  98. // result type. If this cannot be done, the instruction's result id is added
  99. // to |skipped_instructions|. The mapping from donor ids to recipient ids is
  100. // managed by |original_id_to_donated_id|.
  101. void HandleDifficultInstruction(
  102. const opt::Instruction& instruction,
  103. std::map<uint32_t, uint32_t>* original_id_to_donated_id,
  104. std::vector<protobufs::Instruction>* donated_instructions,
  105. std::set<uint32_t>* skipped_instructions);
  106. // Adds an instruction based in |instruction| to |donated_instructions| in a
  107. // form ready for donation. The original instruction comes from
  108. // |donor_ir_context|, and |original_id_to_donated_id| maps ids from
  109. // |donor_ir_context| to corresponding ids in the recipient module.
  110. void PrepareInstructionForDonation(
  111. const opt::Instruction& instruction, opt::IRContext* donor_ir_context,
  112. std::map<uint32_t, uint32_t>* original_id_to_donated_id,
  113. std::vector<protobufs::Instruction>* donated_instructions);
  114. // Tries to create a protobufs::LoopLimiterInfo given a loop header basic
  115. // block. Returns true if successful and outputs loop limiter into the |out|
  116. // variable. Otherwise, returns false. |out| contains an undefined value when
  117. // this function returns false.
  118. bool CreateLoopLimiterInfo(
  119. opt::IRContext* donor_ir_context, const opt::BasicBlock& loop_header,
  120. const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
  121. protobufs::LoopLimiterInfo* out);
  122. // Requires that |donated_instructions| represents a prepared version of the
  123. // instructions of |function_to_donate| (which comes from |donor_ir_context|)
  124. // ready for donation, and |original_id_to_donated_id| maps ids from
  125. // |donor_ir_context| to their corresponding ids in the recipient module.
  126. //
  127. // Attempts to add a livesafe version of the function, based on
  128. // |donated_instructions|, to the recipient module. Returns true if the
  129. // donation was successful, false otherwise.
  130. bool MaybeAddLivesafeFunction(
  131. const opt::Function& function_to_donate, opt::IRContext* donor_ir_context,
  132. const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
  133. const std::vector<protobufs::Instruction>& donated_instructions);
  134. // Returns true if and only if |instruction| is a scalar, vector, matrix,
  135. // array or struct; i.e. it is not an opaque type.
  136. bool IsBasicType(const opt::Instruction& instruction) const;
  137. // Functions that supply SPIR-V modules
  138. std::vector<fuzzerutil::ModuleSupplier> donor_suppliers_;
  139. };
  140. } // namespace fuzz
  141. } // namespace spvtools
  142. #endif // SOURCE_FUZZ_FUZZER_PASS_DONATE_MODULES_H_