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