1 // Copyright (c) 2020 André Perez Maselco
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_add_bit_instruction_synonym.h"
16
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
19
20 namespace spvtools {
21 namespace fuzz {
22
TransformationAddBitInstructionSynonym(protobufs::TransformationAddBitInstructionSynonym message)23 TransformationAddBitInstructionSynonym::TransformationAddBitInstructionSynonym(
24 protobufs::TransformationAddBitInstructionSynonym message)
25 : message_(std::move(message)) {}
26
TransformationAddBitInstructionSynonym(const uint32_t instruction_result_id,const std::vector<uint32_t> & fresh_ids)27 TransformationAddBitInstructionSynonym::TransformationAddBitInstructionSynonym(
28 const uint32_t instruction_result_id,
29 const std::vector<uint32_t>& fresh_ids) {
30 message_.set_instruction_result_id(instruction_result_id);
31 *message_.mutable_fresh_ids() =
32 google::protobuf::RepeatedField<google::protobuf::uint32>(
33 fresh_ids.begin(), fresh_ids.end());
34 }
35
IsApplicable(opt::IRContext * ir_context,const TransformationContext & transformation_context) const36 bool TransformationAddBitInstructionSynonym::IsApplicable(
37 opt::IRContext* ir_context,
38 const TransformationContext& transformation_context) const {
39 auto instruction =
40 ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id());
41
42 // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557):
43 // Right now we only support certain operations. When this issue is addressed
44 // the following conditional can use the function |spvOpcodeIsBit|.
45 // |instruction| must be defined and must be a supported bit instruction.
46 if (!instruction || (instruction->opcode() != SpvOpBitwiseOr &&
47 instruction->opcode() != SpvOpBitwiseXor &&
48 instruction->opcode() != SpvOpBitwiseAnd &&
49 instruction->opcode() != SpvOpNot)) {
50 return false;
51 }
52
53 // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3792):
54 // Right now, only integer operands are supported.
55 if (ir_context->get_type_mgr()->GetType(instruction->type_id())->AsVector()) {
56 return false;
57 }
58
59 // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3791):
60 // This condition could be relaxed if the index exists as another integer
61 // type.
62 // All bit indexes must be defined as 32-bit unsigned integers.
63 uint32_t width = ir_context->get_type_mgr()
64 ->GetType(instruction->type_id())
65 ->AsInteger()
66 ->width();
67 for (uint32_t i = 0; i < width; i++) {
68 if (!fuzzerutil::MaybeGetIntegerConstant(ir_context, transformation_context,
69 {i}, 32, false, false)) {
70 return false;
71 }
72 }
73
74 // |message_.fresh_ids.size| must have the exact number of fresh ids required
75 // to apply the transformation.
76 if (static_cast<uint32_t>(message_.fresh_ids().size()) !=
77 GetRequiredFreshIdCount(ir_context, instruction)) {
78 return false;
79 }
80
81 // All ids in |message_.fresh_ids| must be fresh.
82 for (uint32_t fresh_id : message_.fresh_ids()) {
83 if (!fuzzerutil::IsFreshId(ir_context, fresh_id)) {
84 return false;
85 }
86 }
87
88 return true;
89 }
90
Apply(opt::IRContext * ir_context,TransformationContext * transformation_context) const91 void TransformationAddBitInstructionSynonym::Apply(
92 opt::IRContext* ir_context,
93 TransformationContext* transformation_context) const {
94 auto bit_instruction =
95 ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id());
96
97 // Use an appropriate helper function to add the new instruction and new
98 // synonym fact. The helper function should take care of invalidating
99 // analyses before adding facts.
100 switch (bit_instruction->opcode()) {
101 case SpvOpBitwiseOr:
102 case SpvOpBitwiseXor:
103 case SpvOpBitwiseAnd:
104 case SpvOpNot:
105 AddOpBitwiseOrOpNotSynonym(ir_context, transformation_context,
106 bit_instruction);
107 break;
108 default:
109 assert(false && "Should be unreachable.");
110 break;
111 }
112 }
113
ToMessage() const114 protobufs::Transformation TransformationAddBitInstructionSynonym::ToMessage()
115 const {
116 protobufs::Transformation result;
117 *result.mutable_add_bit_instruction_synonym() = message_;
118 return result;
119 }
120
GetRequiredFreshIdCount(opt::IRContext * ir_context,opt::Instruction * bit_instruction)121 uint32_t TransformationAddBitInstructionSynonym::GetRequiredFreshIdCount(
122 opt::IRContext* ir_context, opt::Instruction* bit_instruction) {
123 // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557):
124 // Right now, only certain operations are supported.
125 switch (bit_instruction->opcode()) {
126 case SpvOpBitwiseOr:
127 case SpvOpBitwiseXor:
128 case SpvOpBitwiseAnd:
129 case SpvOpNot:
130 return (2 + bit_instruction->NumInOperands()) *
131 ir_context->get_type_mgr()
132 ->GetType(bit_instruction->type_id())
133 ->AsInteger()
134 ->width() -
135 1;
136 default:
137 assert(false && "Unsupported bit instruction.");
138 return 0;
139 }
140 }
141
AddOpBitwiseOrOpNotSynonym(opt::IRContext * ir_context,TransformationContext * transformation_context,opt::Instruction * bit_instruction) const142 void TransformationAddBitInstructionSynonym::AddOpBitwiseOrOpNotSynonym(
143 opt::IRContext* ir_context, TransformationContext* transformation_context,
144 opt::Instruction* bit_instruction) const {
145 // Fresh id iterator.
146 auto fresh_id = message_.fresh_ids().begin();
147
148 // |width| is the bit width of operands (8, 16, 32 or 64).
149 const uint32_t width = ir_context->get_type_mgr()
150 ->GetType(bit_instruction->type_id())
151 ->AsInteger()
152 ->width();
153
154 // |count| is the number of bits to be extracted and inserted at a time.
155 const uint32_t count = fuzzerutil::MaybeGetIntegerConstant(
156 ir_context, *transformation_context, {1}, 32, false, false);
157
158 // |extracted_bit_instructions| is the collection of OpBiwise* or OpNot
159 // instructions that evaluate the extracted bits. Those ids will be used to
160 // insert the result bits.
161 std::vector<uint32_t> extracted_bit_instructions(width);
162
163 for (uint32_t i = 0; i < width; i++) {
164 // |offset| is the current bit index.
165 uint32_t offset = fuzzerutil::MaybeGetIntegerConstant(
166 ir_context, *transformation_context, {i}, 32, false, false);
167
168 // |bit_extract_ids| are the two extracted bits from the operands.
169 opt::Instruction::OperandList bit_extract_ids;
170
171 // Extracts the i-th bit from operands.
172 for (auto operand = bit_instruction->begin() + 2;
173 operand != bit_instruction->end(); operand++) {
174 auto bit_extract =
175 opt::Instruction(ir_context, SpvOpBitFieldUExtract,
176 bit_instruction->type_id(), *fresh_id++,
177 {{SPV_OPERAND_TYPE_ID, operand->words},
178 {SPV_OPERAND_TYPE_ID, {offset}},
179 {SPV_OPERAND_TYPE_ID, {count}}});
180 bit_instruction->InsertBefore(MakeUnique<opt::Instruction>(bit_extract));
181 fuzzerutil::UpdateModuleIdBound(ir_context, bit_extract.result_id());
182 bit_extract_ids.push_back(
183 {SPV_OPERAND_TYPE_ID, {bit_extract.result_id()}});
184 }
185
186 // Applies |bit_instruction| to the extracted bits.
187 auto extracted_bit_instruction = opt::Instruction(
188 ir_context, bit_instruction->opcode(), bit_instruction->type_id(),
189 *fresh_id++, bit_extract_ids);
190 bit_instruction->InsertBefore(
191 MakeUnique<opt::Instruction>(extracted_bit_instruction));
192 fuzzerutil::UpdateModuleIdBound(ir_context,
193 extracted_bit_instruction.result_id());
194 extracted_bit_instructions[i] = extracted_bit_instruction.result_id();
195 }
196
197 // The first two ids in |extracted_bit_instructions| are used to insert the
198 // first two bits of the result.
199 uint32_t offset = fuzzerutil::MaybeGetIntegerConstant(
200 ir_context, *transformation_context, {1}, 32, false, false);
201 auto bit_insert = opt::Instruction(
202 ir_context, SpvOpBitFieldInsert, bit_instruction->type_id(), *fresh_id++,
203 {{SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[0]}},
204 {SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[1]}},
205 {SPV_OPERAND_TYPE_ID, {offset}},
206 {SPV_OPERAND_TYPE_ID, {count}}});
207 bit_instruction->InsertBefore(MakeUnique<opt::Instruction>(bit_insert));
208 fuzzerutil::UpdateModuleIdBound(ir_context, bit_insert.result_id());
209
210 // Inserts the remaining bits.
211 for (uint32_t i = 2; i < width; i++) {
212 offset = fuzzerutil::MaybeGetIntegerConstant(
213 ir_context, *transformation_context, {i}, 32, false, false);
214 bit_insert = opt::Instruction(
215 ir_context, SpvOpBitFieldInsert, bit_instruction->type_id(),
216 *fresh_id++,
217 {{SPV_OPERAND_TYPE_ID, {bit_insert.result_id()}},
218 {SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[i]}},
219 {SPV_OPERAND_TYPE_ID, {offset}},
220 {SPV_OPERAND_TYPE_ID, {count}}});
221 bit_instruction->InsertBefore(MakeUnique<opt::Instruction>(bit_insert));
222 fuzzerutil::UpdateModuleIdBound(ir_context, bit_insert.result_id());
223 }
224
225 ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
226
227 // We only add a synonym fact if the bit instruction is not irrelevant, and if
228 // the new result id we would make it synonymous with is not irrelevant. (It
229 // could be irrelevant if we are in a dead block.)
230 if (!transformation_context->GetFactManager()->IdIsIrrelevant(
231 bit_instruction->result_id()) &&
232 !transformation_context->GetFactManager()->IdIsIrrelevant(
233 bit_insert.result_id())) {
234 // Adds the fact that the last |bit_insert| instruction is synonymous of
235 // |bit_instruction|.
236 transformation_context->GetFactManager()->AddFactDataSynonym(
237 MakeDataDescriptor(bit_insert.result_id(), {}),
238 MakeDataDescriptor(bit_instruction->result_id(), {}));
239 }
240 }
241
242 std::unordered_set<uint32_t>
GetFreshIds() const243 TransformationAddBitInstructionSynonym::GetFreshIds() const {
244 std::unordered_set<uint32_t> result;
245 for (auto id : message_.fresh_ids()) {
246 result.insert(id);
247 }
248 return result;
249 }
250
251 } // namespace fuzz
252 } // namespace spvtools
253