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