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