fuzzer_pass_replace_irrelevant_ids.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. // Copyright (c) 2020 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_replace_irrelevant_ids.h"
  15. #include "source/fuzz/fuzzer_util.h"
  16. #include "source/fuzz/id_use_descriptor.h"
  17. #include "source/fuzz/transformation_replace_irrelevant_id.h"
  18. namespace spvtools {
  19. namespace fuzz {
  20. // A fuzzer pass that, for every use of an id that has been recorded as
  21. // irrelevant, randomly decides whether to replace it with another id of the
  22. // same type.
  23. FuzzerPassReplaceIrrelevantIds::FuzzerPassReplaceIrrelevantIds(
  24. opt::IRContext* ir_context, TransformationContext* transformation_context,
  25. FuzzerContext* fuzzer_context,
  26. protobufs::TransformationSequence* transformations,
  27. bool ignore_inapplicable_transformations)
  28. : FuzzerPass(ir_context, transformation_context, fuzzer_context,
  29. transformations, ignore_inapplicable_transformations) {}
  30. void FuzzerPassReplaceIrrelevantIds::Apply() {
  31. // Keep track of the irrelevant ids. This includes all the ids that are
  32. // irrelevant according to the fact manager and that are still present in the
  33. // module (some of them may have been removed by previously-run
  34. // transformations).
  35. std::vector<uint32_t> irrelevant_ids;
  36. // Keep a map from the type ids of irrelevant ids to all the ids with that
  37. // type.
  38. std::unordered_map<uint32_t, std::vector<uint32_t>> types_to_ids;
  39. // Find all the irrelevant ids that still exist in the module and all the
  40. // types for which irrelevant ids exist.
  41. for (auto id :
  42. GetTransformationContext()->GetFactManager()->GetIrrelevantIds()) {
  43. // Check that the id still exists in the module.
  44. auto declaration = GetIRContext()->get_def_use_mgr()->GetDef(id);
  45. if (!declaration) {
  46. continue;
  47. }
  48. irrelevant_ids.push_back(id);
  49. // If the type of this id has not been seen before, add a mapping from this
  50. // type id to an empty list in |types_to_ids|. The list will be filled later
  51. // on.
  52. if (types_to_ids.count(declaration->type_id()) == 0) {
  53. types_to_ids.insert({declaration->type_id(), {}});
  54. }
  55. }
  56. // If no irrelevant ids were found, return.
  57. if (irrelevant_ids.empty()) {
  58. return;
  59. }
  60. // For every type for which we have at least one irrelevant id, record all ids
  61. // in the module which have that type. Skip ids of OpFunction instructions as
  62. // we cannot use these as replacements.
  63. for (const auto& pair : GetIRContext()->get_def_use_mgr()->id_to_defs()) {
  64. uint32_t type_id = pair.second->type_id();
  65. if (pair.second->opcode() != spv::Op::OpFunction && type_id &&
  66. types_to_ids.count(type_id)) {
  67. types_to_ids[type_id].push_back(pair.first);
  68. }
  69. }
  70. // Keep a list of all the transformations to perform. We avoid applying the
  71. // transformations while traversing the uses since applying the transformation
  72. // invalidates all analyses, and we want to avoid invalidating and recomputing
  73. // them every time.
  74. std::vector<TransformationReplaceIrrelevantId> transformations_to_apply;
  75. // Loop through all the uses of irrelevant ids, check that the id can be
  76. // replaced and randomly decide whether to apply the transformation.
  77. for (auto irrelevant_id : irrelevant_ids) {
  78. uint32_t type_id =
  79. GetIRContext()->get_def_use_mgr()->GetDef(irrelevant_id)->type_id();
  80. GetIRContext()->get_def_use_mgr()->ForEachUse(
  81. irrelevant_id, [this, &irrelevant_id, &type_id, &types_to_ids,
  82. &transformations_to_apply](opt::Instruction* use_inst,
  83. uint32_t use_index) {
  84. // Randomly decide whether to consider this use.
  85. if (!GetFuzzerContext()->ChoosePercentage(
  86. GetFuzzerContext()->GetChanceOfReplacingIrrelevantId())) {
  87. return;
  88. }
  89. // The id must be used as an input operand.
  90. if (use_index < use_inst->NumOperands() - use_inst->NumInOperands()) {
  91. // The id is used as an output operand, so we cannot replace this
  92. // usage.
  93. return;
  94. }
  95. // Get the input operand index for this use, from the absolute operand
  96. // index.
  97. uint32_t in_index =
  98. fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
  99. // Only go ahead if this id use can be replaced in principle.
  100. if (!fuzzerutil::IdUseCanBeReplaced(GetIRContext(),
  101. *GetTransformationContext(),
  102. use_inst, in_index)) {
  103. return;
  104. }
  105. // Find out which ids could be used to replace this use.
  106. std::vector<uint32_t> available_replacement_ids;
  107. for (auto replacement_id : types_to_ids[type_id]) {
  108. // It would be pointless to replace an id with itself.
  109. if (replacement_id == irrelevant_id) {
  110. continue;
  111. }
  112. // We cannot replace a variable initializer with a non-constant.
  113. if (TransformationReplaceIrrelevantId::
  114. AttemptsToReplaceVariableInitializerWithNonConstant(
  115. *use_inst, *GetIRContext()->get_def_use_mgr()->GetDef(
  116. replacement_id))) {
  117. continue;
  118. }
  119. // Only consider this replacement if the use point is within a basic
  120. // block and the id is available at the use point.
  121. //
  122. // There might be opportunities for replacing a non-block use of an
  123. // irrelevant id - such as the initializer of a global variable -
  124. // with another id, but it would require some care (e.g. to ensure
  125. // that the replacement id is defined earlier) and does not seem
  126. // worth doing.
  127. if (GetIRContext()->get_instr_block(use_inst) &&
  128. fuzzerutil::IdIsAvailableAtUse(GetIRContext(), use_inst,
  129. in_index, replacement_id)) {
  130. available_replacement_ids.push_back(replacement_id);
  131. }
  132. }
  133. // Only go ahead if there is at least one id with which this use can
  134. // be replaced.
  135. if (available_replacement_ids.empty()) {
  136. return;
  137. }
  138. // Choose the replacement id randomly.
  139. uint32_t replacement_id =
  140. available_replacement_ids[GetFuzzerContext()->RandomIndex(
  141. available_replacement_ids)];
  142. // Add this replacement to the list of transformations to apply.
  143. transformations_to_apply.emplace_back(
  144. TransformationReplaceIrrelevantId(
  145. MakeIdUseDescriptorFromUse(GetIRContext(), use_inst,
  146. in_index),
  147. replacement_id));
  148. });
  149. }
  150. // Apply all the transformations.
  151. for (const auto& transformation : transformations_to_apply) {
  152. ApplyTransformation(transformation);
  153. }
  154. }
  155. } // namespace fuzz
  156. } // namespace spvtools