Roll external/abseil_cpp/ 8f739d18b..917bfee46 (2 commits) (#5887)
[KhronosGroup/SPIRV-Tools.git] / source / fuzz / fuzzer_pass_add_composite_inserts.cpp
blob048cdfde940bbfc1aaa130f182657cb71b5c83a6
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.
15 #include "source/fuzz/fuzzer_pass_add_composite_inserts.h"
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
19 #include "source/fuzz/pseudo_random_generator.h"
20 #include "source/fuzz/transformation_composite_insert.h"
22 namespace spvtools {
23 namespace fuzz {
25 FuzzerPassAddCompositeInserts::FuzzerPassAddCompositeInserts(
26 opt::IRContext* ir_context, TransformationContext* transformation_context,
27 FuzzerContext* fuzzer_context,
28 protobufs::TransformationSequence* transformations,
29 bool ignore_inapplicable_transformations)
30 : FuzzerPass(ir_context, transformation_context, fuzzer_context,
31 transformations, ignore_inapplicable_transformations) {}
33 void FuzzerPassAddCompositeInserts::Apply() {
34 ForEachInstructionWithInstructionDescriptor(
35 [this](opt::Function* function, opt::BasicBlock* block,
36 opt::BasicBlock::iterator instruction_iterator,
37 const protobufs::InstructionDescriptor& instruction_descriptor)
38 -> void {
39 assert(
40 instruction_iterator->opcode() ==
41 spv::Op(instruction_descriptor.target_instruction_opcode()) &&
42 "The opcode of the instruction we might insert before must be "
43 "the same as the opcode in the descriptor for the instruction");
45 // Randomly decide whether to try adding an OpCompositeInsert
46 // instruction.
47 if (!GetFuzzerContext()->ChoosePercentage(
48 GetFuzzerContext()->GetChanceOfAddingCompositeInsert())) {
49 return;
52 // It must be possible to insert an OpCompositeInsert instruction
53 // before |instruction_iterator|.
54 if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
55 spv::Op::OpCompositeInsert, instruction_iterator)) {
56 return;
59 // Look for available values that have composite type.
60 std::vector<opt::Instruction*> available_composites =
61 FindAvailableInstructions(
62 function, block, instruction_iterator,
63 [instruction_descriptor](
64 opt::IRContext* ir_context,
65 opt::Instruction* instruction) -> bool {
66 // |instruction| must be a supported instruction of composite
67 // type.
68 if (!TransformationCompositeInsert::
69 IsCompositeInstructionSupported(ir_context,
70 instruction)) {
71 return false;
74 auto instruction_type = ir_context->get_type_mgr()->GetType(
75 instruction->type_id());
77 // No components of the composite can have type
78 // OpTypeRuntimeArray.
79 if (ContainsRuntimeArray(*instruction_type)) {
80 return false;
83 // No components of the composite can be pointers.
84 // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3658):
85 // Structs can have components of pointer type.
86 // FindOrCreateZeroConstant cannot be called on a
87 // pointer. We ignore pointers for now. Consider adding
88 // support for pointer types.
89 if (ContainsPointer(*instruction_type)) {
90 return false;
93 return true;
94 });
96 // If there are no available values, then return.
97 if (available_composites.empty()) {
98 return;
101 // Choose randomly one available composite value.
102 auto available_composite =
103 available_composites[GetFuzzerContext()->RandomIndex(
104 available_composites)];
106 // Take a random component of the chosen composite value. If the chosen
107 // component is itself a composite, then randomly decide whether to take
108 // its component and repeat.
109 uint32_t current_node_type_id = available_composite->type_id();
110 std::vector<uint32_t> path_to_replaced;
111 while (true) {
112 auto current_node_type_inst =
113 GetIRContext()->get_def_use_mgr()->GetDef(current_node_type_id);
114 uint32_t num_of_components = fuzzerutil::GetBoundForCompositeIndex(
115 *current_node_type_inst, GetIRContext());
117 // If the composite is empty, then end the iteration.
118 if (num_of_components == 0) {
119 break;
121 uint32_t one_selected_index =
122 GetFuzzerContext()->GetRandomIndexForCompositeInsert(
123 num_of_components);
125 // Construct a final index by appending the current index.
126 path_to_replaced.push_back(one_selected_index);
127 current_node_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
128 GetIRContext(), current_node_type_id, one_selected_index);
130 // If the component is not a composite then end the iteration.
131 if (!fuzzerutil::IsCompositeType(
132 GetIRContext()->get_type_mgr()->GetType(
133 current_node_type_id))) {
134 break;
137 // If the component is a composite, but we decide not to go deeper,
138 // then end the iteration.
139 if (!GetFuzzerContext()->ChoosePercentage(
140 GetFuzzerContext()
141 ->GetChanceOfGoingDeeperToInsertInComposite())) {
142 break;
146 // Look for available objects that have the type id
147 // |current_node_type_id| and can be inserted.
148 std::vector<opt::Instruction*> available_objects =
149 FindAvailableInstructions(
150 function, block, instruction_iterator,
151 [instruction_descriptor, current_node_type_id](
152 opt::IRContext* /*unused*/,
153 opt::Instruction* instruction) -> bool {
154 if (instruction->result_id() == 0 ||
155 instruction->type_id() == 0) {
156 return false;
158 if (instruction->type_id() != current_node_type_id) {
159 return false;
161 return true;
164 // If there are no objects of the specific type available, check if
165 // FindOrCreateZeroConstant can be called and create a zero constant of
166 // this type.
167 uint32_t available_object_id;
168 if (available_objects.empty()) {
169 if (!fuzzerutil::CanCreateConstant(GetIRContext(),
170 current_node_type_id)) {
171 return;
173 available_object_id =
174 FindOrCreateZeroConstant(current_node_type_id, false);
175 } else {
176 available_object_id =
177 available_objects[GetFuzzerContext()->RandomIndex(
178 available_objects)]
179 ->result_id();
181 auto new_result_id = GetFuzzerContext()->GetFreshId();
183 // Insert an OpCompositeInsert instruction which copies
184 // |available_composite| and in the copy inserts the object
185 // of type |available_object_id| at index |index_to_replace|.
186 ApplyTransformation(TransformationCompositeInsert(
187 instruction_descriptor, new_result_id,
188 available_composite->result_id(), available_object_id,
189 path_to_replaced));
193 bool FuzzerPassAddCompositeInserts::ContainsPointer(
194 const opt::analysis::Type& type) {
195 switch (type.kind()) {
196 case opt::analysis::Type::kPointer:
197 return true;
198 case opt::analysis::Type::kArray:
199 return ContainsPointer(*type.AsArray()->element_type());
200 case opt::analysis::Type::kMatrix:
201 return ContainsPointer(*type.AsMatrix()->element_type());
202 case opt::analysis::Type::kVector:
203 return ContainsPointer(*type.AsVector()->element_type());
204 case opt::analysis::Type::kStruct:
205 return std::any_of(type.AsStruct()->element_types().begin(),
206 type.AsStruct()->element_types().end(),
207 [](const opt::analysis::Type* element_type) {
208 return ContainsPointer(*element_type);
210 default:
211 return false;
215 bool FuzzerPassAddCompositeInserts::ContainsRuntimeArray(
216 const opt::analysis::Type& type) {
217 switch (type.kind()) {
218 case opt::analysis::Type::kRuntimeArray:
219 return true;
220 case opt::analysis::Type::kStruct:
221 // If any component of a struct is of type OpTypeRuntimeArray, return
222 // true.
223 return std::any_of(type.AsStruct()->element_types().begin(),
224 type.AsStruct()->element_types().end(),
225 [](const opt::analysis::Type* element_type) {
226 return ContainsRuntimeArray(*element_type);
228 default:
229 return false;
233 } // namespace fuzz
234 } // namespace spvtools