1 // Copyright (c) 2019 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_id_with_synonym.h"
16 
17 #include <algorithm>
18 
19 #include "source/fuzz/data_descriptor.h"
20 #include "source/fuzz/fuzzer_util.h"
21 #include "source/fuzz/id_use_descriptor.h"
22 #include "source/opt/types.h"
23 #include "source/util/make_unique.h"
24 
25 namespace spvtools {
26 namespace fuzz {
27 
TransformationReplaceIdWithSynonym(const spvtools::fuzz::protobufs::TransformationReplaceIdWithSynonym & message)28 TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym(
29     const spvtools::fuzz::protobufs::TransformationReplaceIdWithSynonym&
30         message)
31     : message_(message) {}
32 
TransformationReplaceIdWithSynonym(protobufs::IdUseDescriptor id_use_descriptor,uint32_t synonymous_id)33 TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym(
34     protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id) {
35   *message_.mutable_id_use_descriptor() = std::move(id_use_descriptor);
36   message_.set_synonymous_id(synonymous_id);
37 }
38 
IsApplicable(opt::IRContext * ir_context,const TransformationContext & transformation_context) const39 bool TransformationReplaceIdWithSynonym::IsApplicable(
40     opt::IRContext* ir_context,
41     const TransformationContext& transformation_context) const {
42   auto id_of_interest = message_.id_use_descriptor().id_of_interest();
43 
44   // Does the fact manager know about the synonym?
45   auto data_descriptor_for_synonymous_id =
46       MakeDataDescriptor(message_.synonymous_id(), {});
47   if (!transformation_context.GetFactManager()->IsSynonymous(
48           MakeDataDescriptor(id_of_interest, {}),
49           data_descriptor_for_synonymous_id)) {
50     return false;
51   }
52 
53   // Does the id use descriptor in the transformation identify an instruction?
54   auto use_instruction =
55       FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
56   if (!use_instruction) {
57     return false;
58   }
59 
60   uint32_t type_id_of_interest =
61       ir_context->get_def_use_mgr()->GetDef(id_of_interest)->type_id();
62   uint32_t type_id_synonym = ir_context->get_def_use_mgr()
63                                  ->GetDef(message_.synonymous_id())
64                                  ->type_id();
65 
66   // If the id of interest and the synonym are scalar or vector integer
67   // constants with different signedness, their use can only be swapped if the
68   // instruction is agnostic to the signedness of the operand.
69   if (!TypesAreCompatible(ir_context, use_instruction->opcode(),
70                           message_.id_use_descriptor().in_operand_index(),
71                           type_id_of_interest, type_id_synonym)) {
72     return false;
73   }
74 
75   // Is the use suitable for being replaced in principle?
76   if (!fuzzerutil::IdUseCanBeReplaced(
77           ir_context, transformation_context, use_instruction,
78           message_.id_use_descriptor().in_operand_index())) {
79     return false;
80   }
81 
82   // The transformation is applicable if the synonymous id is available at the
83   // use point.
84   return fuzzerutil::IdIsAvailableAtUse(
85       ir_context, use_instruction,
86       message_.id_use_descriptor().in_operand_index(),
87       message_.synonymous_id());
88 }
89 
Apply(spvtools::opt::IRContext * ir_context,TransformationContext *) const90 void TransformationReplaceIdWithSynonym::Apply(
91     spvtools::opt::IRContext* ir_context,
92     TransformationContext* /*unused*/) const {
93   auto instruction_to_change =
94       FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
95   instruction_to_change->SetInOperand(
96       message_.id_use_descriptor().in_operand_index(),
97       {message_.synonymous_id()});
98   ir_context->InvalidateAnalysesExceptFor(
99       opt::IRContext::Analysis::kAnalysisNone);
100 }
101 
ToMessage() const102 protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage()
103     const {
104   protobufs::Transformation result;
105   *result.mutable_replace_id_with_synonym() = message_;
106   return result;
107 }
108 
109 // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3582): Add all
110 //  opcodes that are agnostic to signedness of operands to function.
111 //  This is not exhaustive yet.
IsAgnosticToSignednessOfOperand(SpvOp opcode,uint32_t use_in_operand_index)112 bool TransformationReplaceIdWithSynonym::IsAgnosticToSignednessOfOperand(
113     SpvOp opcode, uint32_t use_in_operand_index) {
114   switch (opcode) {
115     case SpvOpSNegate:
116     case SpvOpNot:
117     case SpvOpIAdd:
118     case SpvOpISub:
119     case SpvOpIMul:
120     case SpvOpSDiv:
121     case SpvOpSRem:
122     case SpvOpSMod:
123     case SpvOpShiftRightLogical:
124     case SpvOpShiftRightArithmetic:
125     case SpvOpShiftLeftLogical:
126     case SpvOpBitwiseOr:
127     case SpvOpBitwiseXor:
128     case SpvOpBitwiseAnd:
129     case SpvOpIEqual:
130     case SpvOpINotEqual:
131     case SpvOpULessThan:
132     case SpvOpSLessThan:
133     case SpvOpUGreaterThan:
134     case SpvOpSGreaterThan:
135     case SpvOpULessThanEqual:
136     case SpvOpSLessThanEqual:
137     case SpvOpUGreaterThanEqual:
138     case SpvOpSGreaterThanEqual:
139       return true;
140     case SpvOpAccessChain:
141       // The signedness of indices does not matter.
142       return use_in_operand_index > 0;
143     default:
144       // Conservatively assume that the id cannot be swapped in other
145       // instructions.
146       return false;
147   }
148 }
149 
TypesAreCompatible(opt::IRContext * ir_context,SpvOp opcode,uint32_t use_in_operand_index,uint32_t type_id_1,uint32_t type_id_2)150 bool TransformationReplaceIdWithSynonym::TypesAreCompatible(
151     opt::IRContext* ir_context, SpvOp opcode, uint32_t use_in_operand_index,
152     uint32_t type_id_1, uint32_t type_id_2) {
153   assert(ir_context->get_type_mgr()->GetType(type_id_1) &&
154          ir_context->get_type_mgr()->GetType(type_id_2) &&
155          "Type ids are invalid");
156 
157   return type_id_1 == type_id_2 ||
158          (IsAgnosticToSignednessOfOperand(opcode, use_in_operand_index) &&
159           fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_1, type_id_2));
160 }
161 
GetFreshIds() const162 std::unordered_set<uint32_t> TransformationReplaceIdWithSynonym::GetFreshIds()
163     const {
164   return std::unordered_set<uint32_t>();
165 }
166 
167 }  // namespace fuzz
168 }  // namespace spvtools
169