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(protobufs::TransformationReplaceIrrelevantId message)23 TransformationReplaceIrrelevantId::TransformationReplaceIrrelevantId(
24     protobufs::TransformationReplaceIrrelevantId message)
25     : message_(std::move(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   ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds(
111       instruction_to_change);
112   ir_context->get_def_use_mgr()->AnalyzeInstUse(instruction_to_change);
113 
114   // No analyses need to be invalidated, since the transformation is local to a
115   // block, and the def-use analysis has been updated.
116 }
117 
ToMessage() const118 protobufs::Transformation TransformationReplaceIrrelevantId::ToMessage() const {
119   protobufs::Transformation result;
120   *result.mutable_replace_irrelevant_id() = message_;
121   return result;
122 }
123 
GetFreshIds() const124 std::unordered_set<uint32_t> TransformationReplaceIrrelevantId::GetFreshIds()
125     const {
126   return std::unordered_set<uint32_t>();
127 }
128 
129 bool TransformationReplaceIrrelevantId::
AttemptsToReplaceVariableInitializerWithNonConstant(const opt::Instruction & use_instruction,const opt::Instruction & replacement_for_use)130     AttemptsToReplaceVariableInitializerWithNonConstant(
131         const opt::Instruction& use_instruction,
132         const opt::Instruction& replacement_for_use) {
133   return use_instruction.opcode() == SpvOpVariable &&
134          !spvOpcodeIsConstant(replacement_for_use.opcode());
135 }
136 
137 }  // namespace fuzz
138 }  // namespace spvtools
139