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