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