• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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