1 // Copyright (c) 2021 Shiyu Liu
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/transformation_wrap_vector_synonym.h"
17 #include "source/fuzz/data_descriptor.h"
18 #include "source/fuzz/fuzzer_util.h"
19 #include "source/opt/instruction.h"
24 TransformationWrapVectorSynonym::TransformationWrapVectorSynonym(
25 protobufs::TransformationWrapVectorSynonym message
)
26 : message_(std::move(message
)) {}
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
);
38 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())) {
46 const opt::Instruction
* instruction
=
47 ir_context
->get_def_use_mgr()->GetDef(message_
.instruction_id());
49 // |instruction_id| must refer to an existing instruction.
50 if (instruction
== nullptr) {
54 if (!IsInstructionSupported(ir_context
, *instruction
)) {
58 // It must be possible to make a synonym of the result id of the scalar
60 if (!fuzzerutil::CanMakeSynonymOf(ir_context
, transformation_context
,
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());
69 if (vec1
== nullptr || vec2
== nullptr) {
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();
77 for (auto operand_index
: {0, 1}) {
78 if (!fuzzerutil::TypesAreCompatible(ir_context
, instruction
->opcode(),
79 operand_index
, vec1_type_id
,
85 auto vec1_type
= ir_context
->get_def_use_mgr()->GetDef(vec1_type_id
);
86 if (vec1_type
->opcode() != spv::Op::OpTypeVector
) {
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(),
99 // |scalar_position| needs to be a non-negative integer less than the vector
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)) {
108 if (!transformation_context
.GetFactManager()->IsSynonymous(
109 MakeDataDescriptor(message_
.vector_operand1(),
110 {message_
.scalar_position()}),
111 MakeDataDescriptor(instruction
->GetSingleWordInOperand(0), {}))) {
115 if (!transformation_context
.GetFactManager()->IsSynonymous(
116 MakeDataDescriptor(message_
.vector_operand2(),
117 {message_
.scalar_position()}),
118 MakeDataDescriptor(instruction
->GetSingleWordInOperand(1), {}))) {
125 void TransformationWrapVectorSynonym::Apply(
126 opt::IRContext
* ir_context
,
127 TransformationContext
* transformation_context
) const {
128 // Create an instruction descriptor for the original instruction.
130 ir_context
->get_def_use_mgr()->GetDef(message_
.instruction_id());
131 auto destination_block
= ir_context
->get_instr_block(instruction
);
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()}});
138 // Make a new arithmetic instruction: %fresh_id = OpXX %type_id %result_id1
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
);
153 // Add |fresh_id| to id bound.
154 fuzzerutil::UpdateModuleIdBound(ir_context
, message_
.fresh_id());
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(), {}));
162 protobufs::Transformation
TransformationWrapVectorSynonym::ToMessage() const {
163 protobufs::Transformation result
;
164 *result
.mutable_wrap_vector_synonym() = message_
;
168 std::unordered_set
<uint32_t> TransformationWrapVectorSynonym::GetFreshIds()
170 return std::unordered_set
<uint32_t>{message_
.fresh_id()};
173 bool TransformationWrapVectorSynonym::IsInstructionSupported(
174 opt::IRContext
* ir_context
, const opt::Instruction
& instruction
) {
175 if (!instruction
.result_id() || !instruction
.type_id()) {
178 auto type_instruction
=
179 ir_context
->get_def_use_mgr()->GetDef(instruction
.type_id());
181 if ((type_instruction
->opcode() != spv::Op::OpTypeInt
&&
182 type_instruction
->opcode() != spv::Op::OpTypeFloat
)) {
186 switch (instruction
.opcode()) {
187 case spv::Op::OpIAdd
:
188 case spv::Op::OpISub
:
189 case spv::Op::OpIMul
:
190 case spv::Op::OpFAdd
:
191 case spv::Op::OpFSub
:
192 case spv::Op::OpFMul
:
200 } // namespace spvtools