• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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