• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2020 Vasyl Teliman
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_add_synonym.h"
16 
17 #include <utility>
18 
19 #include "source/fuzz/fuzzer_util.h"
20 #include "source/fuzz/instruction_descriptor.h"
21 
22 namespace spvtools {
23 namespace fuzz {
24 
TransformationAddSynonym(protobufs::TransformationAddSynonym message)25 TransformationAddSynonym::TransformationAddSynonym(
26     protobufs::TransformationAddSynonym message)
27     : message_(std::move(message)) {}
28 
TransformationAddSynonym(uint32_t result_id,protobufs::TransformationAddSynonym::SynonymType synonym_type,uint32_t synonym_fresh_id,const protobufs::InstructionDescriptor & insert_before)29 TransformationAddSynonym::TransformationAddSynonym(
30     uint32_t result_id,
31     protobufs::TransformationAddSynonym::SynonymType synonym_type,
32     uint32_t synonym_fresh_id,
33     const protobufs::InstructionDescriptor& insert_before) {
34   message_.set_result_id(result_id);
35   message_.set_synonym_type(synonym_type);
36   message_.set_synonym_fresh_id(synonym_fresh_id);
37   *message_.mutable_insert_before() = insert_before;
38 }
39 
IsApplicable(opt::IRContext * ir_context,const TransformationContext & transformation_context) const40 bool TransformationAddSynonym::IsApplicable(
41     opt::IRContext* ir_context,
42     const TransformationContext& transformation_context) const {
43   assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(
44              message_.synonym_type()) &&
45          "Synonym type is invalid");
46 
47   // |synonym_fresh_id| must be fresh.
48   if (!fuzzerutil::IsFreshId(ir_context, message_.synonym_fresh_id())) {
49     return false;
50   }
51 
52   // Check that |message_.result_id| is valid.
53   auto* synonym = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
54   if (!synonym) {
55     return false;
56   }
57 
58   // Check that we can apply |synonym_type| to |result_id|.
59   if (!IsInstructionValid(ir_context, transformation_context, synonym,
60                           message_.synonym_type())) {
61     return false;
62   }
63 
64   // Check that |insert_before| is valid.
65   auto* insert_before_inst =
66       FindInstruction(message_.insert_before(), ir_context);
67   if (!insert_before_inst) {
68     return false;
69   }
70 
71   const auto* insert_before_inst_block =
72       ir_context->get_instr_block(insert_before_inst);
73   assert(insert_before_inst_block &&
74          "|insert_before_inst| must be in some block");
75 
76   if (transformation_context.GetFactManager()->BlockIsDead(
77           insert_before_inst_block->id())) {
78     // We don't create synonyms in dead blocks.
79     return false;
80   }
81 
82   // Check that we can insert |message._synonymous_instruction| before
83   // |message_.insert_before| instruction. We use OpIAdd to represent some
84   // instruction that can produce a synonym.
85   if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpIAdd,
86                                                     insert_before_inst)) {
87     return false;
88   }
89 
90   // A constant instruction must be present in the module if required.
91   if (IsAdditionalConstantRequired(message_.synonym_type()) &&
92       MaybeGetConstantId(ir_context, transformation_context) == 0) {
93     return false;
94   }
95 
96   // Domination rules must be satisfied.
97   return fuzzerutil::IdIsAvailableBeforeInstruction(
98       ir_context, insert_before_inst, message_.result_id());
99 }
100 
Apply(opt::IRContext * ir_context,TransformationContext * transformation_context) const101 void TransformationAddSynonym::Apply(
102     opt::IRContext* ir_context,
103     TransformationContext* transformation_context) const {
104   // Add a synonymous instruction.
105   auto new_instruction =
106       MakeSynonymousInstruction(ir_context, *transformation_context);
107   auto new_instruction_ptr = new_instruction.get();
108   auto insert_before = FindInstruction(message_.insert_before(), ir_context);
109   insert_before->InsertBefore(std::move(new_instruction));
110 
111   fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id());
112 
113   // Inform the def-use manager about the new instruction and record its basic
114   // block.
115   ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
116   ir_context->set_instr_block(new_instruction_ptr,
117                               ir_context->get_instr_block(insert_before));
118 
119   // Propagate PointeeValueIsIrrelevant fact.
120   const auto* new_synonym_type = ir_context->get_type_mgr()->GetType(
121       fuzzerutil::GetTypeId(ir_context, message_.synonym_fresh_id()));
122   assert(new_synonym_type && "New synonym should have a valid type");
123 
124   if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
125           message_.result_id()) &&
126       new_synonym_type->AsPointer()) {
127     transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
128         message_.synonym_fresh_id());
129   }
130 
131   // Mark two ids as synonymous.
132   transformation_context->GetFactManager()->AddFactDataSynonym(
133       MakeDataDescriptor(message_.result_id(), {}),
134       MakeDataDescriptor(message_.synonym_fresh_id(), {}));
135 }
136 
ToMessage() const137 protobufs::Transformation TransformationAddSynonym::ToMessage() const {
138   protobufs::Transformation result;
139   *result.mutable_add_synonym() = message_;
140   return result;
141 }
142 
IsInstructionValid(opt::IRContext * ir_context,const TransformationContext & transformation_context,opt::Instruction * inst,protobufs::TransformationAddSynonym::SynonymType synonym_type)143 bool TransformationAddSynonym::IsInstructionValid(
144     opt::IRContext* ir_context,
145     const TransformationContext& transformation_context, opt::Instruction* inst,
146     protobufs::TransformationAddSynonym::SynonymType synonym_type) {
147   // Instruction must have a result id, type id. We skip OpUndef and
148   // OpConstantNull.
149   if (!inst || !inst->result_id() || !inst->type_id() ||
150       inst->opcode() == spv::Op::OpUndef ||
151       inst->opcode() == spv::Op::OpConstantNull) {
152     return false;
153   }
154 
155   if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
156                                     *inst)) {
157     return false;
158   }
159 
160   switch (synonym_type) {
161     case protobufs::TransformationAddSynonym::ADD_ZERO:
162     case protobufs::TransformationAddSynonym::SUB_ZERO:
163     case protobufs::TransformationAddSynonym::MUL_ONE: {
164       // The instruction must be either scalar or vector of integers or floats.
165       const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
166       assert(type && "Instruction's result id is invalid");
167 
168       if (const auto* vector = type->AsVector()) {
169         return vector->element_type()->AsInteger() ||
170                vector->element_type()->AsFloat();
171       }
172 
173       return type->AsInteger() || type->AsFloat();
174     }
175     case protobufs::TransformationAddSynonym::BITWISE_OR:
176     case protobufs::TransformationAddSynonym::BITWISE_XOR: {
177       // The instruction must be either an integer or a vector of integers.
178       const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
179       assert(type && "Instruction's result id is invalid");
180 
181       if (const auto* vector = type->AsVector()) {
182         return vector->element_type()->AsInteger();
183       }
184 
185       return type->AsInteger();
186     }
187     case protobufs::TransformationAddSynonym::COPY_OBJECT:
188       // All checks for OpCopyObject are handled by
189       // fuzzerutil::CanMakeSynonymOf.
190       return true;
191     case protobufs::TransformationAddSynonym::LOGICAL_AND:
192     case protobufs::TransformationAddSynonym::LOGICAL_OR: {
193       // The instruction must be either a scalar or a vector of booleans.
194       const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
195       assert(type && "Instruction's result id is invalid");
196       return (type->AsVector() && type->AsVector()->element_type()->AsBool()) ||
197              type->AsBool();
198     }
199     default:
200       assert(false && "Synonym type is not supported");
201       return false;
202   }
203 }
204 
205 std::unique_ptr<opt::Instruction>
MakeSynonymousInstruction(opt::IRContext * ir_context,const TransformationContext & transformation_context) const206 TransformationAddSynonym::MakeSynonymousInstruction(
207     opt::IRContext* ir_context,
208     const TransformationContext& transformation_context) const {
209   auto synonym_type_id =
210       fuzzerutil::GetTypeId(ir_context, message_.result_id());
211   assert(synonym_type_id && "Synonym has invalid type id");
212   auto opcode = spv::Op::OpNop;
213   const auto* synonym_type =
214       ir_context->get_type_mgr()->GetType(synonym_type_id);
215   assert(synonym_type && "Synonym has invalid type");
216 
217   auto is_integral = (synonym_type->AsVector() &&
218                       synonym_type->AsVector()->element_type()->AsInteger()) ||
219                      synonym_type->AsInteger();
220 
221   switch (message_.synonym_type()) {
222     case protobufs::TransformationAddSynonym::SUB_ZERO:
223       opcode = is_integral ? spv::Op::OpISub : spv::Op::OpFSub;
224       break;
225     case protobufs::TransformationAddSynonym::MUL_ONE:
226       opcode = is_integral ? spv::Op::OpIMul : spv::Op::OpFMul;
227       break;
228     case protobufs::TransformationAddSynonym::ADD_ZERO:
229       opcode = is_integral ? spv::Op::OpIAdd : spv::Op::OpFAdd;
230       break;
231     case protobufs::TransformationAddSynonym::LOGICAL_OR:
232       opcode = spv::Op::OpLogicalOr;
233       break;
234     case protobufs::TransformationAddSynonym::LOGICAL_AND:
235       opcode = spv::Op::OpLogicalAnd;
236       break;
237     case protobufs::TransformationAddSynonym::BITWISE_OR:
238       opcode = spv::Op::OpBitwiseOr;
239       break;
240     case protobufs::TransformationAddSynonym::BITWISE_XOR:
241       opcode = spv::Op::OpBitwiseXor;
242       break;
243 
244     case protobufs::TransformationAddSynonym::COPY_OBJECT:
245       return MakeUnique<opt::Instruction>(
246           ir_context, spv::Op::OpCopyObject, synonym_type_id,
247           message_.synonym_fresh_id(),
248           opt::Instruction::OperandList{
249               {SPV_OPERAND_TYPE_ID, {message_.result_id()}}});
250 
251     default:
252       assert(false && "Unhandled synonym type");
253       return nullptr;
254   }
255 
256   return MakeUnique<opt::Instruction>(
257       ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
258       opt::Instruction::OperandList{
259           {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
260           {SPV_OPERAND_TYPE_ID,
261            {MaybeGetConstantId(ir_context, transformation_context)}}});
262 }
263 
MaybeGetConstantId(opt::IRContext * ir_context,const TransformationContext & transformation_context) const264 uint32_t TransformationAddSynonym::MaybeGetConstantId(
265     opt::IRContext* ir_context,
266     const TransformationContext& transformation_context) const {
267   assert(IsAdditionalConstantRequired(message_.synonym_type()) &&
268          "Synonym type doesn't require an additional constant");
269 
270   auto synonym_type_id =
271       fuzzerutil::GetTypeId(ir_context, message_.result_id());
272   assert(synonym_type_id && "Synonym has invalid type id");
273 
274   switch (message_.synonym_type()) {
275     case protobufs::TransformationAddSynonym::ADD_ZERO:
276     case protobufs::TransformationAddSynonym::SUB_ZERO:
277     case protobufs::TransformationAddSynonym::LOGICAL_OR:
278     case protobufs::TransformationAddSynonym::BITWISE_OR:
279     case protobufs::TransformationAddSynonym::BITWISE_XOR:
280       return fuzzerutil::MaybeGetZeroConstant(
281           ir_context, transformation_context, synonym_type_id, false);
282     case protobufs::TransformationAddSynonym::MUL_ONE:
283     case protobufs::TransformationAddSynonym::LOGICAL_AND: {
284       auto synonym_type = ir_context->get_type_mgr()->GetType(synonym_type_id);
285       assert(synonym_type && "Synonym has invalid type");
286 
287       if (const auto* vector = synonym_type->AsVector()) {
288         auto element_type_id =
289             ir_context->get_type_mgr()->GetId(vector->element_type());
290         assert(element_type_id && "Vector's element type is invalid");
291 
292         auto one_word =
293             vector->element_type()->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u;
294         if (auto scalar_one_id = fuzzerutil::MaybeGetScalarConstant(
295                 ir_context, transformation_context, {one_word}, element_type_id,
296                 false)) {
297           return fuzzerutil::MaybeGetCompositeConstant(
298               ir_context, transformation_context,
299               std::vector<uint32_t>(vector->element_count(), scalar_one_id),
300               synonym_type_id, false);
301         }
302 
303         return 0;
304       } else {
305         return fuzzerutil::MaybeGetScalarConstant(
306             ir_context, transformation_context,
307             {synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u},
308             synonym_type_id, false);
309       }
310     }
311     default:
312       // The assertion at the beginning of the function will fail in the debug
313       // mode.
314       return 0;
315   }
316 }
317 
IsAdditionalConstantRequired(protobufs::TransformationAddSynonym::SynonymType synonym_type)318 bool TransformationAddSynonym::IsAdditionalConstantRequired(
319     protobufs::TransformationAddSynonym::SynonymType synonym_type) {
320   switch (synonym_type) {
321     case protobufs::TransformationAddSynonym::ADD_ZERO:
322     case protobufs::TransformationAddSynonym::SUB_ZERO:
323     case protobufs::TransformationAddSynonym::LOGICAL_OR:
324     case protobufs::TransformationAddSynonym::MUL_ONE:
325     case protobufs::TransformationAddSynonym::LOGICAL_AND:
326     case protobufs::TransformationAddSynonym::BITWISE_OR:
327     case protobufs::TransformationAddSynonym::BITWISE_XOR:
328       return true;
329     default:
330       return false;
331   }
332 }
333 
GetFreshIds() const334 std::unordered_set<uint32_t> TransformationAddSynonym::GetFreshIds() const {
335   return {message_.synonym_fresh_id()};
336 }
337 
338 }  // namespace fuzz
339 }  // namespace spvtools
340