1 // Copyright (c) 2020 Google LLC
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_replace_irrelevant_id.h"
16 
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/id_use_descriptor.h"
19 
20 namespace spvtools {
21 namespace fuzz {
22 
TransformationReplaceIrrelevantId(const protobufs::TransformationReplaceIrrelevantId & message)23 TransformationReplaceIrrelevantId::TransformationReplaceIrrelevantId(
24     const protobufs::TransformationReplaceIrrelevantId& message)
25     : message_(message) {}
26 
TransformationReplaceIrrelevantId(const protobufs::IdUseDescriptor & id_use_descriptor,uint32_t replacement_id)27 TransformationReplaceIrrelevantId::TransformationReplaceIrrelevantId(
28     const protobufs::IdUseDescriptor& id_use_descriptor,
29     uint32_t replacement_id) {
30   *message_.mutable_id_use_descriptor() = id_use_descriptor;
31   message_.set_replacement_id(replacement_id);
32 }
33 
IsApplicable(opt::IRContext * ir_context,const TransformationContext & transformation_context) const34 bool TransformationReplaceIrrelevantId::IsApplicable(
35     opt::IRContext* ir_context,
36     const TransformationContext& transformation_context) const {
37   auto id_of_interest = message_.id_use_descriptor().id_of_interest();
38 
39   // The id must be irrelevant.
40   if (!transformation_context.GetFactManager()->IdIsIrrelevant(
41           id_of_interest)) {
42     return false;
43   }
44 
45   // Find the instruction containing the id use, which must exist.
46   auto use_instruction =
47       FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
48   if (!use_instruction) {
49     return false;
50   }
51 
52   // Check that the replacement id exists and retrieve its definition.
53   auto replacement_id_def =
54       ir_context->get_def_use_mgr()->GetDef(message_.replacement_id());
55   if (!replacement_id_def) {
56     return false;
57   }
58 
59   // The type of the id of interest and of the replacement id must be the same.
60   uint32_t type_id_of_interest =
61       ir_context->get_def_use_mgr()->GetDef(id_of_interest)->type_id();
62   uint32_t type_replacement_id = replacement_id_def->type_id();
63   if (type_id_of_interest != type_replacement_id) {
64     return false;
65   }
66 
67   // The replacement id must not be the result of an OpFunction instruction.
68   if (replacement_id_def->opcode() == SpvOpFunction) {
69     return false;
70   }
71 
72   // Consistency check: an irrelevant id cannot be a pointer.
73   assert(
74       !ir_context->get_type_mgr()->GetType(type_id_of_interest)->AsPointer() &&
75       "An irrelevant id cannot be a pointer");
76 
77   uint32_t use_in_operand_index =
78       message_.id_use_descriptor().in_operand_index();
79 
80   // The id use must be replaceable with any other id of the same type.
81   if (!fuzzerutil::IdUseCanBeReplaced(ir_context, transformation_context,
82                                       use_instruction, use_in_operand_index)) {
83     return false;
84   }
85 
86   if (AttemptsToReplaceVariableInitializerWithNonConstant(
87           *use_instruction, *replacement_id_def)) {
88     return false;
89   }
90 
91   // The id must be available to use at the use point.
92   return fuzzerutil::IdIsAvailableAtUse(
93       ir_context, use_instruction,
94       message_.id_use_descriptor().in_operand_index(),
95       message_.replacement_id());
96 }
97 
Apply(opt::IRContext * ir_context,TransformationContext *) const98 void TransformationReplaceIrrelevantId::Apply(
99     opt::IRContext* ir_context,
100     TransformationContext* /* transformation_context */) const {
101   // Find the instruction.
102   auto instruction_to_change =
103       FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
104 
105   // Replace the instruction.
106   instruction_to_change->SetInOperand(
107       message_.id_use_descriptor().in_operand_index(),
108       {message_.replacement_id()});
109 
110   // Invalidate the analyses, since the usage of ids has been changed.
111   ir_context->InvalidateAnalysesExceptFor(
112       opt::IRContext::Analysis::kAnalysisNone);
113 }
114 
ToMessage() const115 protobufs::Transformation TransformationReplaceIrrelevantId::ToMessage() const {
116   protobufs::Transformation result;
117   *result.mutable_replace_irrelevant_id() = message_;
118   return result;
119 }
120 
GetFreshIds() const121 std::unordered_set<uint32_t> TransformationReplaceIrrelevantId::GetFreshIds()
122     const {
123   return std::unordered_set<uint32_t>();
124 }
125 
126 bool TransformationReplaceIrrelevantId::
AttemptsToReplaceVariableInitializerWithNonConstant(const opt::Instruction & use_instruction,const opt::Instruction & replacement_for_use)127     AttemptsToReplaceVariableInitializerWithNonConstant(
128         const opt::Instruction& use_instruction,
129         const opt::Instruction& replacement_for_use) {
130   return use_instruction.opcode() == SpvOpVariable &&
131          !spvOpcodeIsConstant(replacement_for_use.opcode());
132 }
133 
134 }  // namespace fuzz
135 }  // namespace spvtools
136