1 // Copyright (c) 2021 Shiyu Liu
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/transformation_wrap_vector_synonym.h"
16
17 #include "source/fuzz/data_descriptor.h"
18 #include "source/fuzz/fuzzer_util.h"
19 #include "source/opt/instruction.h"
20
21 namespace spvtools {
22 namespace fuzz {
23
TransformationWrapVectorSynonym(protobufs::TransformationWrapVectorSynonym message)24 TransformationWrapVectorSynonym::TransformationWrapVectorSynonym(
25 protobufs::TransformationWrapVectorSynonym message)
26 : message_(std::move(message)) {}
27
TransformationWrapVectorSynonym(uint32_t instruction_id,uint32_t vector_operand1,uint32_t vector_operand2,uint32_t fresh_id,uint32_t pos)28 TransformationWrapVectorSynonym::TransformationWrapVectorSynonym(
29 uint32_t instruction_id, uint32_t vector_operand1, uint32_t vector_operand2,
30 uint32_t fresh_id, uint32_t pos) {
31 message_.set_instruction_id(instruction_id);
32 message_.set_vector_operand1(vector_operand1);
33 message_.set_vector_operand2(vector_operand2);
34 message_.set_fresh_id(fresh_id);
35 message_.set_scalar_position(pos);
36 }
37
IsApplicable(opt::IRContext * ir_context,const TransformationContext & transformation_context) const38 bool TransformationWrapVectorSynonym::IsApplicable(
39 opt::IRContext* ir_context,
40 const TransformationContext& transformation_context) const {
41 // |fresh_id| must be fresh.
42 if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
43 return false;
44 }
45
46 const opt::Instruction* instruction =
47 ir_context->get_def_use_mgr()->GetDef(message_.instruction_id());
48
49 // |instruction_id| must refer to an existing instruction.
50 if (instruction == nullptr) {
51 return false;
52 }
53
54 if (!IsInstructionSupported(ir_context, *instruction)) {
55 return false;
56 }
57
58 // It must be possible to make a synonym of the result id of the scalar
59 // operation
60 if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
61 *instruction)) {
62 return false;
63 }
64
65 // |vector_operand1| and |vector_operand2| must exist.
66 auto vec1 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand1());
67 auto vec2 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand2());
68
69 if (vec1 == nullptr || vec2 == nullptr) {
70 return false;
71 }
72
73 // The 2 vectors must have compatible vector types.
74 auto vec1_type_id = vec1->type_id();
75 auto vec2_type_id = vec2->type_id();
76
77 for (auto operand_index : {0, 1}) {
78 if (!fuzzerutil::TypesAreCompatible(ir_context, instruction->opcode(),
79 operand_index, vec1_type_id,
80 vec2_type_id)) {
81 return false;
82 }
83 }
84
85 auto vec1_type = ir_context->get_def_use_mgr()->GetDef(vec1_type_id);
86 if (vec1_type->opcode() != SpvOpTypeVector) {
87 return false;
88 }
89
90 // A suitable vector for the result type of the new vector instruction must
91 // exist in the module. This is a vector of the right length, whose element
92 // type matches the result type of the scalar instruction.
93 uint32_t vector_size = vec1_type->GetSingleWordInOperand(1);
94 if (!fuzzerutil::MaybeGetVectorType(ir_context, instruction->type_id(),
95 vector_size)) {
96 return false;
97 }
98
99 // |scalar_position| needs to be a non-negative integer less than the vector
100 // length.
101 // OpTypeVector instruction has the component count at index 2.
102 if (message_.scalar_position() >= ir_context->get_def_use_mgr()
103 ->GetDef(vec1_type_id)
104 ->GetSingleWordInOperand(1)) {
105 return false;
106 }
107
108 if (!transformation_context.GetFactManager()->IsSynonymous(
109 MakeDataDescriptor(message_.vector_operand1(),
110 {message_.scalar_position()}),
111 MakeDataDescriptor(instruction->GetSingleWordInOperand(0), {}))) {
112 return false;
113 }
114
115 if (!transformation_context.GetFactManager()->IsSynonymous(
116 MakeDataDescriptor(message_.vector_operand2(),
117 {message_.scalar_position()}),
118 MakeDataDescriptor(instruction->GetSingleWordInOperand(1), {}))) {
119 return false;
120 }
121
122 return true;
123 }
124
Apply(opt::IRContext * ir_context,TransformationContext * transformation_context) const125 void TransformationWrapVectorSynonym::Apply(
126 opt::IRContext* ir_context,
127 TransformationContext* transformation_context) const {
128 // Create an instruction descriptor for the original instruction.
129 auto instruction =
130 ir_context->get_def_use_mgr()->GetDef(message_.instruction_id());
131 auto destination_block = ir_context->get_instr_block(instruction);
132
133 // Populate input operand list with two vectors for vector operation.
134 opt::Instruction::OperandList in_operands;
135 in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.vector_operand1()}});
136 in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.vector_operand2()}});
137
138 // Make a new arithmetic instruction: %fresh_id = OpXX %type_id %result_id1
139 // %result_id2.
140 auto vector_operand_type = ir_context->get_def_use_mgr()->GetDef(
141 fuzzerutil::GetTypeId(ir_context, message_.vector_operand1()));
142 uint32_t vector_size = vector_operand_type->GetSingleWordInOperand(1);
143 auto vec_type_id = fuzzerutil::MaybeGetVectorType(
144 ir_context, instruction->type_id(), vector_size);
145 auto new_instruction = MakeUnique<opt::Instruction>(
146 ir_context, instruction->opcode(), vec_type_id, message_.fresh_id(),
147 std::move(in_operands));
148 auto new_instruction_ptr = new_instruction.get();
149 instruction->InsertBefore(std::move(new_instruction));
150 ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
151 ir_context->set_instr_block(new_instruction_ptr, destination_block);
152
153 // Add |fresh_id| to id bound.
154 fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
155
156 // Add synonyms between |fresh_id| and |instruction_id|.
157 transformation_context->GetFactManager()->AddFactDataSynonym(
158 MakeDataDescriptor(message_.fresh_id(), {message_.scalar_position()}),
159 MakeDataDescriptor(message_.instruction_id(), {}));
160 }
161
ToMessage() const162 protobufs::Transformation TransformationWrapVectorSynonym::ToMessage() const {
163 protobufs::Transformation result;
164 *result.mutable_wrap_vector_synonym() = message_;
165 return result;
166 }
167
GetFreshIds() const168 std::unordered_set<uint32_t> TransformationWrapVectorSynonym::GetFreshIds()
169 const {
170 return std::unordered_set<uint32_t>{message_.fresh_id()};
171 }
172
IsInstructionSupported(opt::IRContext * ir_context,const opt::Instruction & instruction)173 bool TransformationWrapVectorSynonym::IsInstructionSupported(
174 opt::IRContext* ir_context, const opt::Instruction& instruction) {
175 if (!instruction.result_id() || !instruction.type_id()) {
176 return false;
177 }
178 auto type_instruction =
179 ir_context->get_def_use_mgr()->GetDef(instruction.type_id());
180
181 if ((type_instruction->opcode() != SpvOpTypeInt &&
182 type_instruction->opcode() != SpvOpTypeFloat)) {
183 return false;
184 }
185
186 switch (instruction.opcode()) {
187 case SpvOpIAdd:
188 case SpvOpISub:
189 case SpvOpIMul:
190 case SpvOpFAdd:
191 case SpvOpFSub:
192 case SpvOpFMul:
193 return true;
194 default:
195 return false;
196 }
197 }
198
199 } // namespace fuzz
200 } // namespace spvtools
201