• 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_replace_id_with_synonym.h"
23 
24 namespace spvtools {
25 namespace fuzz {
26 
FuzzerPassApplyIdSynonyms(opt::IRContext * ir_context,TransformationContext * transformation_context,FuzzerContext * fuzzer_context,protobufs::TransformationSequence * transformations)27 FuzzerPassApplyIdSynonyms::FuzzerPassApplyIdSynonyms(
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 
34 FuzzerPassApplyIdSynonyms::~FuzzerPassApplyIdSynonyms() = default;
35 
Apply()36 void FuzzerPassApplyIdSynonyms::Apply() {
37   for (auto id_with_known_synonyms :
38        GetTransformationContext()
39            ->GetFactManager()
40            ->GetIdsForWhichSynonymsAreKnown(GetIRContext())) {
41     // Gather up all uses of |id_with_known_synonym| as a regular id, and
42     // subsequently iterate over these uses.  We use this separation because,
43     // when considering a given use, we might apply a transformation that will
44     // invalidate the def-use manager.
45     std::vector<std::pair<opt::Instruction*, uint32_t>> uses;
46     GetIRContext()->get_def_use_mgr()->ForEachUse(
47         id_with_known_synonyms,
48         [&uses](opt::Instruction* use_inst, uint32_t use_index) -> void {
49           // We only gather up regular id uses; e.g. we do not include a use of
50           // the id as the scope for an atomic operation.
51           if (use_inst->GetOperand(use_index).type == SPV_OPERAND_TYPE_ID) {
52             uses.emplace_back(
53                 std::pair<opt::Instruction*, uint32_t>(use_inst, use_index));
54           }
55         });
56 
57     for (auto& use : uses) {
58       auto use_inst = use.first;
59       auto use_index = use.second;
60       auto block_containing_use = GetIRContext()->get_instr_block(use_inst);
61       // The use might not be in a block; e.g. it could be a decoration.
62       if (!block_containing_use) {
63         continue;
64       }
65       if (!GetFuzzerContext()->ChoosePercentage(
66               GetFuzzerContext()->GetChanceOfReplacingIdWithSynonym())) {
67         continue;
68       }
69       // |use_index| is the absolute index of the operand.  We require
70       // the index of the operand restricted to input operands only, so
71       // we subtract the number of non-input operands from |use_index|.
72       uint32_t use_in_operand_index =
73           use_index - use_inst->NumOperands() + use_inst->NumInOperands();
74       if (!TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
75               GetIRContext(), use_inst, use_in_operand_index)) {
76         continue;
77       }
78 
79       std::vector<const protobufs::DataDescriptor*> synonyms_to_try;
80       for (auto& data_descriptor :
81            GetTransformationContext()->GetFactManager()->GetSynonymsForId(
82                id_with_known_synonyms, GetIRContext())) {
83         protobufs::DataDescriptor descriptor_for_this_id =
84             MakeDataDescriptor(id_with_known_synonyms, {});
85         if (DataDescriptorEquals()(data_descriptor, &descriptor_for_this_id)) {
86           // Exclude the fact that the id is synonymous with itself.
87           continue;
88         }
89         synonyms_to_try.push_back(data_descriptor);
90       }
91       while (!synonyms_to_try.empty()) {
92         auto synonym_to_try =
93             GetFuzzerContext()->RemoveAtRandomIndex(&synonyms_to_try);
94 
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(SpvOpCompositeExtract,
101                                                           use_inst) &&
102             use_inst->opcode() != SpvOpPhi) {
103           // We cannot insert an extract before this instruction, so this
104           // synonym is no good.
105           continue;
106         }
107 
108         if (!fuzzerutil::IdIsAvailableAtUse(GetIRContext(), use_inst,
109                                             use_in_operand_index,
110                                             synonym_to_try->object())) {
111           continue;
112         }
113 
114         // We either replace the use with an id known to be synonymous (when
115         // the synonym's |index_size| is 0), or an id that will hold the result
116         // of extracting a synonym from a composite (when the synonym's
117         // |index_size| is > 0).
118         uint32_t id_with_which_to_replace_use;
119         if (synonym_to_try->index_size() == 0) {
120           id_with_which_to_replace_use = synonym_to_try->object();
121         } else {
122           id_with_which_to_replace_use = GetFuzzerContext()->GetFreshId();
123           opt::Instruction* instruction_to_insert_before = nullptr;
124 
125           if (use_inst->opcode() != SpvOpPhi) {
126             instruction_to_insert_before = use_inst;
127           } else {
128             auto parent_block_id =
129                 use_inst->GetSingleWordInOperand(use_in_operand_index + 1);
130             auto parent_block_instruction =
131                 GetIRContext()->get_def_use_mgr()->GetDef(parent_block_id);
132             auto parent_block =
133                 GetIRContext()->get_instr_block(parent_block_instruction);
134 
135             instruction_to_insert_before = parent_block->GetMergeInst()
136                                                ? parent_block->GetMergeInst()
137                                                : parent_block->terminator();
138           }
139 
140           ApplyTransformation(TransformationCompositeExtract(
141               MakeInstructionDescriptor(GetIRContext(),
142                                         instruction_to_insert_before),
143               id_with_which_to_replace_use, synonym_to_try->object(),
144               fuzzerutil::RepeatedFieldToVector(synonym_to_try->index())));
145         }
146 
147         ApplyTransformation(TransformationReplaceIdWithSynonym(
148             MakeIdUseDescriptorFromUse(GetIRContext(), use_inst,
149                                        use_in_operand_index),
150             id_with_which_to_replace_use));
151         break;
152       }
153     }
154   }
155 }
156 
157 }  // namespace fuzz
158 }  // namespace spvtools
159