1 // Copyright (c) 2020 Google LLC
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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"
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
)
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
47 if (!GetFuzzerContext()->ChoosePercentage(
48 GetFuzzerContext()->GetChanceOfAddingCompositeInsert())) {
52 // It must be possible to insert an OpCompositeInsert instruction
53 // before |instruction_iterator|.
54 if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
55 spv::Op::OpCompositeInsert
, instruction_iterator
)) {
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
68 if (!TransformationCompositeInsert::
69 IsCompositeInstructionSupported(ir_context
,
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
)) {
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
)) {
96 // If there are no available values, then return.
97 if (available_composites
.empty()) {
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
;
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) {
121 uint32_t one_selected_index
=
122 GetFuzzerContext()->GetRandomIndexForCompositeInsert(
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
))) {
137 // If the component is a composite, but we decide not to go deeper,
138 // then end the iteration.
139 if (!GetFuzzerContext()->ChoosePercentage(
141 ->GetChanceOfGoingDeeperToInsertInComposite())) {
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) {
158 if (instruction
->type_id() != current_node_type_id
) {
164 // If there are no objects of the specific type available, check if
165 // FindOrCreateZeroConstant can be called and create a zero constant of
167 uint32_t available_object_id
;
168 if (available_objects
.empty()) {
169 if (!fuzzerutil::CanCreateConstant(GetIRContext(),
170 current_node_type_id
)) {
173 available_object_id
=
174 FindOrCreateZeroConstant(current_node_type_id
, false);
176 available_object_id
=
177 available_objects
[GetFuzzerContext()->RandomIndex(
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
,
193 bool FuzzerPassAddCompositeInserts::ContainsPointer(
194 const opt::analysis::Type
& type
) {
195 switch (type
.kind()) {
196 case opt::analysis::Type::kPointer
:
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
);
215 bool FuzzerPassAddCompositeInserts::ContainsRuntimeArray(
216 const opt::analysis::Type
& type
) {
217 switch (type
.kind()) {
218 case opt::analysis::Type::kRuntimeArray
:
220 case opt::analysis::Type::kStruct
:
221 // If any component of a struct is of type OpTypeRuntimeArray, return
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
);
234 } // namespace spvtools