1 // Copyright (c) 2020 André Perez Maselco
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_add_vector_shuffle_instructions.h"
16
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
19 #include "source/fuzz/transformation_vector_shuffle.h"
20
21 namespace spvtools {
22 namespace fuzz {
23
FuzzerPassAddVectorShuffleInstructions(opt::IRContext * ir_context,TransformationContext * transformation_context,FuzzerContext * fuzzer_context,protobufs::TransformationSequence * transformations,bool ignore_inapplicable_transformations)24 FuzzerPassAddVectorShuffleInstructions::FuzzerPassAddVectorShuffleInstructions(
25 opt::IRContext* ir_context, TransformationContext* transformation_context,
26 FuzzerContext* fuzzer_context,
27 protobufs::TransformationSequence* transformations,
28 bool ignore_inapplicable_transformations)
29 : FuzzerPass(ir_context, transformation_context, fuzzer_context,
30 transformations, ignore_inapplicable_transformations) {}
31
Apply()32 void FuzzerPassAddVectorShuffleInstructions::Apply() {
33 ForEachInstructionWithInstructionDescriptor(
34 [this](opt::Function* function, opt::BasicBlock* block,
35 opt::BasicBlock::iterator instruction_iterator,
36 const protobufs::InstructionDescriptor& instruction_descriptor)
37 -> void {
38 assert(
39 instruction_iterator->opcode() ==
40 spv::Op(instruction_descriptor.target_instruction_opcode()) &&
41 "The opcode of the instruction we might insert before must be "
42 "the same as the opcode in the descriptor for the instruction");
43
44 // Randomly decide whether to try adding an OpVectorShuffle instruction.
45 if (!GetFuzzerContext()->ChoosePercentage(
46 GetFuzzerContext()->GetChanceOfAddingVectorShuffle())) {
47 return;
48 }
49
50 // It must be valid to insert an OpVectorShuffle instruction
51 // before |instruction_iterator|.
52 if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
53 spv::Op::OpVectorShuffle, instruction_iterator)) {
54 return;
55 }
56
57 // Looks for vectors that we might consider to use as OpVectorShuffle
58 // operands.
59 std::vector<opt::Instruction*> vector_instructions =
60 FindAvailableInstructions(
61 function, block, instruction_iterator,
62 [this, instruction_descriptor](
63 opt::IRContext* ir_context,
64 opt::Instruction* instruction) -> bool {
65 if (!instruction->result_id() || !instruction->type_id()) {
66 return false;
67 }
68
69 if (!ir_context->get_type_mgr()
70 ->GetType(instruction->type_id())
71 ->AsVector()) {
72 return false;
73 }
74
75 if (!GetTransformationContext()
76 ->GetFactManager()
77 ->IdIsIrrelevant(instruction->result_id()) &&
78 !fuzzerutil::CanMakeSynonymOf(ir_context,
79 *GetTransformationContext(),
80 *instruction)) {
81 // If the id is irrelevant, we can use it since it will not
82 // participate in DataSynonym fact. Otherwise, we should be
83 // able to produce a synonym out of the id.
84 return false;
85 }
86
87 return fuzzerutil::IdIsAvailableBeforeInstruction(
88 ir_context,
89 FindInstruction(instruction_descriptor, ir_context),
90 instruction->result_id());
91 });
92
93 // If there are no vector instructions, then return.
94 if (vector_instructions.empty()) {
95 return;
96 }
97
98 auto vector_1_instruction =
99 vector_instructions[GetFuzzerContext()->RandomIndex(
100 vector_instructions)];
101 auto vector_1_type = GetIRContext()
102 ->get_type_mgr()
103 ->GetType(vector_1_instruction->type_id())
104 ->AsVector();
105
106 auto vector_2_instruction =
107 GetFuzzerContext()->RemoveAtRandomIndex(&vector_instructions);
108 auto vector_2_type = GetIRContext()
109 ->get_type_mgr()
110 ->GetType(vector_2_instruction->type_id())
111 ->AsVector();
112
113 // |vector_1| and |vector_2| must have the same element type as each
114 // other. The loop is guaranteed to terminate because each iteration
115 // removes on possible choice for |vector_2|, and there is at least one
116 // choice that will cause the loop to exit - namely |vector_1|.
117 while (vector_1_type->element_type() != vector_2_type->element_type()) {
118 vector_2_instruction =
119 GetFuzzerContext()->RemoveAtRandomIndex(&vector_instructions);
120 vector_2_type = GetIRContext()
121 ->get_type_mgr()
122 ->GetType(vector_2_instruction->type_id())
123 ->AsVector();
124 }
125
126 // Gets components and creates the appropriate result vector type.
127 std::vector<uint32_t> components =
128 GetFuzzerContext()->GetRandomComponentsForVectorShuffle(
129 vector_1_type->element_count() +
130 vector_2_type->element_count());
131 FindOrCreateVectorType(GetIRContext()->get_type_mgr()->GetId(
132 vector_1_type->element_type()),
133 static_cast<uint32_t>(components.size()));
134
135 // Applies the vector shuffle transformation.
136 ApplyTransformation(TransformationVectorShuffle(
137 instruction_descriptor, GetFuzzerContext()->GetFreshId(),
138 vector_1_instruction->result_id(),
139 vector_2_instruction->result_id(), components));
140 });
141 }
142
143 } // namespace fuzz
144 } // namespace spvtools
145