fuzzer_pass_apply_id_synonyms.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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. #include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
  15. #include "source/fuzz/data_descriptor.h"
  16. #include "source/fuzz/fuzzer_util.h"
  17. #include "source/fuzz/id_use_descriptor.h"
  18. #include "source/fuzz/instruction_descriptor.h"
  19. #include "source/fuzz/transformation_composite_extract.h"
  20. #include "source/fuzz/transformation_compute_data_synonym_fact_closure.h"
  21. #include "source/fuzz/transformation_replace_id_with_synonym.h"
  22. namespace spvtools {
  23. namespace fuzz {
  24. FuzzerPassApplyIdSynonyms::FuzzerPassApplyIdSynonyms(
  25. opt::IRContext* ir_context, TransformationContext* transformation_context,
  26. FuzzerContext* fuzzer_context,
  27. protobufs::TransformationSequence* transformations,
  28. bool ignore_inapplicable_transformations)
  29. : FuzzerPass(ir_context, transformation_context, fuzzer_context,
  30. transformations, ignore_inapplicable_transformations) {}
  31. void FuzzerPassApplyIdSynonyms::Apply() {
  32. // Compute a closure of data synonym facts, to enrich the pool of synonyms
  33. // that are available.
  34. ApplyTransformation(TransformationComputeDataSynonymFactClosure(
  35. GetFuzzerContext()
  36. ->GetMaximumEquivalenceClassSizeForDataSynonymFactClosure()));
  37. for (auto id_with_known_synonyms : GetTransformationContext()
  38. ->GetFactManager()
  39. ->GetIdsForWhichSynonymsAreKnown()) {
  40. // Gather up all uses of |id_with_known_synonym| as a regular id, and
  41. // subsequently iterate over these uses. We use this separation because,
  42. // when considering a given use, we might apply a transformation that will
  43. // invalidate the def-use manager.
  44. std::vector<std::pair<opt::Instruction*, uint32_t>> uses;
  45. GetIRContext()->get_def_use_mgr()->ForEachUse(
  46. id_with_known_synonyms,
  47. [&uses](opt::Instruction* use_inst, uint32_t use_index) -> void {
  48. // We only gather up regular id uses; e.g. we do not include a use of
  49. // the id as the scope for an atomic operation.
  50. if (use_inst->GetOperand(use_index).type == SPV_OPERAND_TYPE_ID) {
  51. uses.emplace_back(
  52. std::pair<opt::Instruction*, uint32_t>(use_inst, use_index));
  53. }
  54. });
  55. for (const auto& use : uses) {
  56. auto use_inst = use.first;
  57. auto use_index = use.second;
  58. auto block_containing_use = GetIRContext()->get_instr_block(use_inst);
  59. // The use might not be in a block; e.g. it could be a decoration.
  60. if (!block_containing_use) {
  61. continue;
  62. }
  63. if (!GetFuzzerContext()->ChoosePercentage(
  64. GetFuzzerContext()->GetChanceOfReplacingIdWithSynonym())) {
  65. continue;
  66. }
  67. // |use_index| is the absolute index of the operand. We require
  68. // the index of the operand restricted to input operands only.
  69. uint32_t use_in_operand_index =
  70. fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
  71. if (!fuzzerutil::IdUseCanBeReplaced(GetIRContext(),
  72. *GetTransformationContext(), use_inst,
  73. use_in_operand_index)) {
  74. continue;
  75. }
  76. std::vector<const protobufs::DataDescriptor*> synonyms_to_try;
  77. for (const auto* data_descriptor :
  78. GetTransformationContext()->GetFactManager()->GetSynonymsForId(
  79. id_with_known_synonyms)) {
  80. protobufs::DataDescriptor descriptor_for_this_id =
  81. MakeDataDescriptor(id_with_known_synonyms, {});
  82. if (DataDescriptorEquals()(data_descriptor, &descriptor_for_this_id)) {
  83. // Exclude the fact that the id is synonymous with itself.
  84. continue;
  85. }
  86. if (DataDescriptorsHaveCompatibleTypes(
  87. use_inst->opcode(), use_in_operand_index,
  88. descriptor_for_this_id, *data_descriptor)) {
  89. synonyms_to_try.push_back(data_descriptor);
  90. }
  91. }
  92. while (!synonyms_to_try.empty()) {
  93. auto synonym_to_try =
  94. GetFuzzerContext()->RemoveAtRandomIndex(&synonyms_to_try);
  95. // If the synonym's |index_size| is zero, the synonym represents an id.
  96. // Otherwise it represents some element of a composite structure, in
  97. // which case we need to be able to add an extract instruction to get
  98. // that element out.
  99. if (synonym_to_try->index_size() > 0 &&
  100. !fuzzerutil::CanInsertOpcodeBeforeInstruction(
  101. spv::Op::OpCompositeExtract, use_inst) &&
  102. use_inst->opcode() != spv::Op::OpPhi) {
  103. // We cannot insert an extract before this instruction, so this
  104. // synonym is no good.
  105. continue;
  106. }
  107. if (!fuzzerutil::IdIsAvailableAtUse(GetIRContext(), use_inst,
  108. use_in_operand_index,
  109. synonym_to_try->object())) {
  110. continue;
  111. }
  112. // We either replace the use with an id known to be synonymous (when
  113. // the synonym's |index_size| is 0), or an id that will hold the result
  114. // of extracting a synonym from a composite (when the synonym's
  115. // |index_size| is > 0).
  116. uint32_t id_with_which_to_replace_use;
  117. if (synonym_to_try->index_size() == 0) {
  118. id_with_which_to_replace_use = synonym_to_try->object();
  119. } else {
  120. id_with_which_to_replace_use = GetFuzzerContext()->GetFreshId();
  121. opt::Instruction* instruction_to_insert_before = nullptr;
  122. if (use_inst->opcode() != spv::Op::OpPhi) {
  123. instruction_to_insert_before = use_inst;
  124. } else {
  125. auto parent_block_id =
  126. use_inst->GetSingleWordInOperand(use_in_operand_index + 1);
  127. auto parent_block_instruction =
  128. GetIRContext()->get_def_use_mgr()->GetDef(parent_block_id);
  129. auto parent_block =
  130. GetIRContext()->get_instr_block(parent_block_instruction);
  131. instruction_to_insert_before = parent_block->GetMergeInst()
  132. ? parent_block->GetMergeInst()
  133. : parent_block->terminator();
  134. }
  135. if (GetTransformationContext()->GetFactManager()->BlockIsDead(
  136. GetIRContext()
  137. ->get_instr_block(instruction_to_insert_before)
  138. ->id())) {
  139. // We cannot create a synonym via a composite extraction in a dead
  140. // block, as the resulting id is irrelevant.
  141. continue;
  142. }
  143. assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
  144. synonym_to_try->object()) &&
  145. "Irrelevant ids can't participate in DataSynonym facts");
  146. ApplyTransformation(TransformationCompositeExtract(
  147. MakeInstructionDescriptor(GetIRContext(),
  148. instruction_to_insert_before),
  149. id_with_which_to_replace_use, synonym_to_try->object(),
  150. fuzzerutil::RepeatedFieldToVector(synonym_to_try->index())));
  151. assert(GetTransformationContext()->GetFactManager()->IsSynonymous(
  152. MakeDataDescriptor(id_with_which_to_replace_use, {}),
  153. *synonym_to_try) &&
  154. "The extracted id must be synonymous with the component from "
  155. "which it was extracted.");
  156. }
  157. ApplyTransformation(TransformationReplaceIdWithSynonym(
  158. MakeIdUseDescriptorFromUse(GetIRContext(), use_inst,
  159. use_in_operand_index),
  160. id_with_which_to_replace_use));
  161. break;
  162. }
  163. }
  164. }
  165. }
  166. bool FuzzerPassApplyIdSynonyms::DataDescriptorsHaveCompatibleTypes(
  167. spv::Op opcode, uint32_t use_in_operand_index,
  168. const protobufs::DataDescriptor& dd1,
  169. const protobufs::DataDescriptor& dd2) {
  170. auto base_object_type_id_1 =
  171. fuzzerutil::GetTypeId(GetIRContext(), dd1.object());
  172. auto base_object_type_id_2 =
  173. fuzzerutil::GetTypeId(GetIRContext(), dd2.object());
  174. assert(base_object_type_id_1 && base_object_type_id_2 &&
  175. "Data descriptors are invalid");
  176. auto type_id_1 = fuzzerutil::WalkCompositeTypeIndices(
  177. GetIRContext(), base_object_type_id_1, dd1.index());
  178. auto type_id_2 = fuzzerutil::WalkCompositeTypeIndices(
  179. GetIRContext(), base_object_type_id_2, dd2.index());
  180. assert(type_id_1 && type_id_2 && "Data descriptors have invalid types");
  181. return fuzzerutil::TypesAreCompatible(
  182. GetIRContext(), opcode, use_in_operand_index, type_id_1, type_id_2);
  183. }
  184. } // namespace fuzz
  185. } // namespace spvtools