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