• 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/fuzzer_pass_obfuscate_constants.h"
16 
17 #include <algorithm>
18 #include <cmath>
19 
20 #include "source/fuzz/fuzzer_util.h"
21 #include "source/fuzz/instruction_descriptor.h"
22 #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
23 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
24 #include "source/fuzz/uniform_buffer_element_descriptor.h"
25 #include "source/opt/ir_context.h"
26 
27 namespace spvtools {
28 namespace fuzz {
29 
FuzzerPassObfuscateConstants(opt::IRContext * ir_context,TransformationContext * transformation_context,FuzzerContext * fuzzer_context,protobufs::TransformationSequence * transformations,bool ignore_inapplicable_transformations)30 FuzzerPassObfuscateConstants::FuzzerPassObfuscateConstants(
31     opt::IRContext* ir_context, TransformationContext* transformation_context,
32     FuzzerContext* fuzzer_context,
33     protobufs::TransformationSequence* transformations,
34     bool ignore_inapplicable_transformations)
35     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
36                  transformations, ignore_inapplicable_transformations) {}
37 
ObfuscateBoolConstantViaConstantPair(uint32_t depth,const protobufs::IdUseDescriptor & bool_constant_use,const std::vector<SpvOp> & greater_than_opcodes,const std::vector<SpvOp> & less_than_opcodes,uint32_t constant_id_1,uint32_t constant_id_2,bool first_constant_is_larger)38 void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair(
39     uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
40     const std::vector<SpvOp>& greater_than_opcodes,
41     const std::vector<SpvOp>& less_than_opcodes, uint32_t constant_id_1,
42     uint32_t constant_id_2, bool first_constant_is_larger) {
43   auto bool_constant_opcode = GetIRContext()
44                                   ->get_def_use_mgr()
45                                   ->GetDef(bool_constant_use.id_of_interest())
46                                   ->opcode();
47   assert((bool_constant_opcode == SpvOpConstantFalse ||
48           bool_constant_opcode == SpvOpConstantTrue) &&
49          "Precondition: this must be a usage of a boolean constant.");
50 
51   // Pick an opcode at random.  First randomly decide whether to generate
52   // a 'greater than' or 'less than' kind of opcode, and then select a
53   // random opcode from the resulting subset.
54   SpvOp comparison_opcode;
55   if (GetFuzzerContext()->ChooseEven()) {
56     comparison_opcode = greater_than_opcodes[GetFuzzerContext()->RandomIndex(
57         greater_than_opcodes)];
58   } else {
59     comparison_opcode =
60         less_than_opcodes[GetFuzzerContext()->RandomIndex(less_than_opcodes)];
61   }
62 
63   // We now need to decide how to order constant_id_1 and constant_id_2 such
64   // that 'constant_id_1 comparison_opcode constant_id_2' evaluates to the
65   // boolean constant.
66   const bool is_greater_than_opcode =
67       std::find(greater_than_opcodes.begin(), greater_than_opcodes.end(),
68                 comparison_opcode) != greater_than_opcodes.end();
69   uint32_t lhs_id;
70   uint32_t rhs_id;
71   if ((bool_constant_opcode == SpvOpConstantTrue &&
72        first_constant_is_larger == is_greater_than_opcode) ||
73       (bool_constant_opcode == SpvOpConstantFalse &&
74        first_constant_is_larger != is_greater_than_opcode)) {
75     lhs_id = constant_id_1;
76     rhs_id = constant_id_2;
77   } else {
78     lhs_id = constant_id_2;
79     rhs_id = constant_id_1;
80   }
81 
82   // We can now make a transformation that will replace |bool_constant_use|
83   // with an expression of the form (written using infix notation):
84   // |lhs_id| |comparison_opcode| |rhs_id|
85   auto transformation = TransformationReplaceBooleanConstantWithConstantBinary(
86       bool_constant_use, lhs_id, rhs_id, comparison_opcode,
87       GetFuzzerContext()->GetFreshId());
88   // The transformation should be applicable by construction.
89   assert(
90       transformation.IsApplicable(GetIRContext(), *GetTransformationContext()));
91 
92   // Applying this transformation yields a pointer to the new instruction that
93   // computes the result of the binary expression.
94   auto binary_operator_instruction = transformation.ApplyWithResult(
95       GetIRContext(), GetTransformationContext());
96 
97   // Add this transformation to the sequence of transformations that have been
98   // applied.
99   *GetTransformations()->add_transformation() = transformation.ToMessage();
100 
101   // Having made a binary expression, there may now be opportunities to further
102   // obfuscate the constants used as the LHS and RHS of the expression (e.g. by
103   // replacing them with loads from known uniforms).
104   //
105   // We thus consider operands 0 and 1 (LHS and RHS in turn).
106   for (uint32_t index : {0u, 1u}) {
107     // We randomly decide, based on the current depth of obfuscation, whether
108     // to further obfuscate this operand.
109     if (GetFuzzerContext()->GoDeeperInConstantObfuscation(depth)) {
110       auto in_operand_use = MakeIdUseDescriptor(
111           binary_operator_instruction->GetSingleWordInOperand(index),
112           MakeInstructionDescriptor(binary_operator_instruction->result_id(),
113                                     binary_operator_instruction->opcode(), 0),
114           index);
115       ObfuscateConstant(depth + 1, in_operand_use);
116     }
117   }
118 }
119 
ObfuscateBoolConstantViaFloatConstantPair(uint32_t depth,const protobufs::IdUseDescriptor & bool_constant_use,uint32_t float_constant_id_1,uint32_t float_constant_id_2)120 void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaFloatConstantPair(
121     uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
122     uint32_t float_constant_id_1, uint32_t float_constant_id_2) {
123   auto float_constant_1 = GetIRContext()
124                               ->get_constant_mgr()
125                               ->FindDeclaredConstant(float_constant_id_1)
126                               ->AsFloatConstant();
127   auto float_constant_2 = GetIRContext()
128                               ->get_constant_mgr()
129                               ->FindDeclaredConstant(float_constant_id_2)
130                               ->AsFloatConstant();
131   assert(float_constant_1->words() != float_constant_2->words() &&
132          "The constants should not be identical.");
133   assert(std::isfinite(float_constant_1->GetValueAsDouble()) &&
134          "The constants must be finite numbers.");
135   assert(std::isfinite(float_constant_2->GetValueAsDouble()) &&
136          "The constants must be finite numbers.");
137   bool first_constant_is_larger;
138   assert(float_constant_1->type()->AsFloat()->width() ==
139              float_constant_2->type()->AsFloat()->width() &&
140          "First and second floating-point constants must have the same width.");
141   if (float_constant_1->type()->AsFloat()->width() == 32) {
142     first_constant_is_larger =
143         float_constant_1->GetFloat() > float_constant_2->GetFloat();
144   } else {
145     assert(float_constant_1->type()->AsFloat()->width() == 64 &&
146            "Supported floating-point widths are 32 and 64.");
147     first_constant_is_larger =
148         float_constant_1->GetDouble() > float_constant_2->GetDouble();
149   }
150   std::vector<SpvOp> greater_than_opcodes{
151       SpvOpFOrdGreaterThan, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThan,
152       SpvOpFUnordGreaterThanEqual};
153   std::vector<SpvOp> less_than_opcodes{
154       SpvOpFOrdGreaterThan, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThan,
155       SpvOpFUnordGreaterThanEqual};
156 
157   ObfuscateBoolConstantViaConstantPair(
158       depth, bool_constant_use, greater_than_opcodes, less_than_opcodes,
159       float_constant_id_1, float_constant_id_2, first_constant_is_larger);
160 }
161 
162 void FuzzerPassObfuscateConstants::
ObfuscateBoolConstantViaSignedIntConstantPair(uint32_t depth,const protobufs::IdUseDescriptor & bool_constant_use,uint32_t signed_int_constant_id_1,uint32_t signed_int_constant_id_2)163     ObfuscateBoolConstantViaSignedIntConstantPair(
164         uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
165         uint32_t signed_int_constant_id_1, uint32_t signed_int_constant_id_2) {
166   auto signed_int_constant_1 =
167       GetIRContext()
168           ->get_constant_mgr()
169           ->FindDeclaredConstant(signed_int_constant_id_1)
170           ->AsIntConstant();
171   auto signed_int_constant_2 =
172       GetIRContext()
173           ->get_constant_mgr()
174           ->FindDeclaredConstant(signed_int_constant_id_2)
175           ->AsIntConstant();
176   assert(signed_int_constant_1->words() != signed_int_constant_2->words() &&
177          "The constants should not be identical.");
178   bool first_constant_is_larger;
179   assert(signed_int_constant_1->type()->AsInteger()->width() ==
180              signed_int_constant_2->type()->AsInteger()->width() &&
181          "First and second floating-point constants must have the same width.");
182   assert(signed_int_constant_1->type()->AsInteger()->IsSigned());
183   assert(signed_int_constant_2->type()->AsInteger()->IsSigned());
184   if (signed_int_constant_1->type()->AsFloat()->width() == 32) {
185     first_constant_is_larger =
186         signed_int_constant_1->GetS32() > signed_int_constant_2->GetS32();
187   } else {
188     assert(signed_int_constant_1->type()->AsFloat()->width() == 64 &&
189            "Supported integer widths are 32 and 64.");
190     first_constant_is_larger =
191         signed_int_constant_1->GetS64() > signed_int_constant_2->GetS64();
192   }
193   std::vector<SpvOp> greater_than_opcodes{SpvOpSGreaterThan,
194                                           SpvOpSGreaterThanEqual};
195   std::vector<SpvOp> less_than_opcodes{SpvOpSLessThan, SpvOpSLessThanEqual};
196 
197   ObfuscateBoolConstantViaConstantPair(
198       depth, bool_constant_use, greater_than_opcodes, less_than_opcodes,
199       signed_int_constant_id_1, signed_int_constant_id_2,
200       first_constant_is_larger);
201 }
202 
203 void FuzzerPassObfuscateConstants::
ObfuscateBoolConstantViaUnsignedIntConstantPair(uint32_t depth,const protobufs::IdUseDescriptor & bool_constant_use,uint32_t unsigned_int_constant_id_1,uint32_t unsigned_int_constant_id_2)204     ObfuscateBoolConstantViaUnsignedIntConstantPair(
205         uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
206         uint32_t unsigned_int_constant_id_1,
207         uint32_t unsigned_int_constant_id_2) {
208   auto unsigned_int_constant_1 =
209       GetIRContext()
210           ->get_constant_mgr()
211           ->FindDeclaredConstant(unsigned_int_constant_id_1)
212           ->AsIntConstant();
213   auto unsigned_int_constant_2 =
214       GetIRContext()
215           ->get_constant_mgr()
216           ->FindDeclaredConstant(unsigned_int_constant_id_2)
217           ->AsIntConstant();
218   assert(unsigned_int_constant_1->words() != unsigned_int_constant_2->words() &&
219          "The constants should not be identical.");
220   bool first_constant_is_larger;
221   assert(unsigned_int_constant_1->type()->AsInteger()->width() ==
222              unsigned_int_constant_2->type()->AsInteger()->width() &&
223          "First and second floating-point constants must have the same width.");
224   assert(!unsigned_int_constant_1->type()->AsInteger()->IsSigned());
225   assert(!unsigned_int_constant_2->type()->AsInteger()->IsSigned());
226   if (unsigned_int_constant_1->type()->AsFloat()->width() == 32) {
227     first_constant_is_larger =
228         unsigned_int_constant_1->GetU32() > unsigned_int_constant_2->GetU32();
229   } else {
230     assert(unsigned_int_constant_1->type()->AsFloat()->width() == 64 &&
231            "Supported integer widths are 32 and 64.");
232     first_constant_is_larger =
233         unsigned_int_constant_1->GetU64() > unsigned_int_constant_2->GetU64();
234   }
235   std::vector<SpvOp> greater_than_opcodes{SpvOpUGreaterThan,
236                                           SpvOpUGreaterThanEqual};
237   std::vector<SpvOp> less_than_opcodes{SpvOpULessThan, SpvOpULessThanEqual};
238 
239   ObfuscateBoolConstantViaConstantPair(
240       depth, bool_constant_use, greater_than_opcodes, less_than_opcodes,
241       unsigned_int_constant_id_1, unsigned_int_constant_id_2,
242       first_constant_is_larger);
243 }
244 
245 std::vector<std::vector<uint32_t>>
GetConstantWordsFromUniformsForType(uint32_t type_id)246 FuzzerPassObfuscateConstants::GetConstantWordsFromUniformsForType(
247     uint32_t type_id) {
248   assert(type_id && "Type id can't be 0");
249   std::vector<std::vector<uint32_t>> result;
250 
251   for (const auto& facts_and_types : GetTransformationContext()
252                                          ->GetFactManager()
253                                          ->GetConstantUniformFactsAndTypes()) {
254     if (facts_and_types.second != type_id) {
255       continue;
256     }
257 
258     std::vector<uint32_t> words(facts_and_types.first.constant_word().begin(),
259                                 facts_and_types.first.constant_word().end());
260     if (std::find(result.begin(), result.end(), words) == result.end()) {
261       result.push_back(std::move(words));
262     }
263   }
264 
265   return result;
266 }
267 
ObfuscateBoolConstant(uint32_t depth,const protobufs::IdUseDescriptor & constant_use)268 void FuzzerPassObfuscateConstants::ObfuscateBoolConstant(
269     uint32_t depth, const protobufs::IdUseDescriptor& constant_use) {
270   // We want to replace the boolean constant use with a binary expression over
271   // scalar constants, but only if we can then potentially replace the constants
272   // with uniforms of the same value.
273 
274   auto available_types_with_uniforms =
275       GetTransformationContext()
276           ->GetFactManager()
277           ->GetTypesForWhichUniformValuesAreKnown();
278   if (available_types_with_uniforms.empty()) {
279     // Do not try to obfuscate if we do not have access to any uniform
280     // elements with known values.
281     return;
282   }
283   auto chosen_type_id =
284       available_types_with_uniforms[GetFuzzerContext()->RandomIndex(
285           available_types_with_uniforms)];
286   auto available_constant_words =
287       GetConstantWordsFromUniformsForType(chosen_type_id);
288   if (available_constant_words.size() == 1) {
289     // TODO(afd): for now we only obfuscate a boolean if there are at least
290     //  two constants available from uniforms, so that we can do a
291     //  comparison between them. It would be good to be able to do the
292     //  obfuscation even if there is only one such constant, if there is
293     //  also another regular constant available.
294     return;
295   }
296 
297   assert(!available_constant_words.empty() &&
298          "There exists a fact but no constants - impossible");
299 
300   // We know we have at least two known-to-be-constant uniforms of the chosen
301   // type.  Pick one of them at random.
302   auto constant_index_1 =
303       GetFuzzerContext()->RandomIndex(available_constant_words);
304   uint32_t constant_index_2;
305 
306   // Now choose another one distinct from the first one.
307   do {
308     constant_index_2 =
309         GetFuzzerContext()->RandomIndex(available_constant_words);
310   } while (constant_index_1 == constant_index_2);
311 
312   auto constant_id_1 = FindOrCreateConstant(
313       available_constant_words[constant_index_1], chosen_type_id, false);
314   auto constant_id_2 = FindOrCreateConstant(
315       available_constant_words[constant_index_2], chosen_type_id, false);
316 
317   assert(constant_id_1 != 0 && constant_id_2 != 0 &&
318          "We should not find an available constant with an id of 0.");
319 
320   // Now perform the obfuscation, according to whether the type of the constants
321   // is float, signed int, or unsigned int.
322   auto chosen_type = GetIRContext()->get_type_mgr()->GetType(chosen_type_id);
323   if (chosen_type->AsFloat()) {
324     ObfuscateBoolConstantViaFloatConstantPair(depth, constant_use,
325                                               constant_id_1, constant_id_2);
326   } else {
327     assert(chosen_type->AsInteger() &&
328            "We should only have uniform facts about ints and floats.");
329     if (chosen_type->AsInteger()->IsSigned()) {
330       ObfuscateBoolConstantViaSignedIntConstantPair(
331           depth, constant_use, constant_id_1, constant_id_2);
332     } else {
333       ObfuscateBoolConstantViaUnsignedIntConstantPair(
334           depth, constant_use, constant_id_1, constant_id_2);
335     }
336   }
337 }
338 
ObfuscateScalarConstant(uint32_t,const protobufs::IdUseDescriptor & constant_use)339 void FuzzerPassObfuscateConstants::ObfuscateScalarConstant(
340     uint32_t /*depth*/, const protobufs::IdUseDescriptor& constant_use) {
341   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2670): consider
342   //  additional ways to obfuscate scalar constants.
343 
344   // Check whether we know that any uniforms are guaranteed to be equal to the
345   // scalar constant associated with |constant_use|.
346   auto uniform_descriptors =
347       GetTransformationContext()
348           ->GetFactManager()
349           ->GetUniformDescriptorsForConstant(constant_use.id_of_interest());
350   if (uniform_descriptors.empty()) {
351     // No relevant uniforms, so do not obfuscate.
352     return;
353   }
354 
355   // Choose a random available uniform known to be equal to the constant.
356   const auto& uniform_descriptor =
357       uniform_descriptors[GetFuzzerContext()->RandomIndex(uniform_descriptors)];
358 
359   // Make sure the module has OpConstant instructions for each index used to
360   // access a uniform.
361   for (auto index : uniform_descriptor.index()) {
362     FindOrCreateIntegerConstant({index}, 32, true, false);
363   }
364 
365   // Make sure the module has OpTypePointer that points to the element type of
366   // the uniform.
367   const auto* uniform_variable_instr =
368       FindUniformVariable(uniform_descriptor, GetIRContext(), true);
369   assert(uniform_variable_instr &&
370          "Uniform variable does not exist or not unique.");
371 
372   const auto* uniform_variable_type_intr =
373       GetIRContext()->get_def_use_mgr()->GetDef(
374           uniform_variable_instr->type_id());
375   assert(uniform_variable_type_intr && "Uniform variable has invalid type");
376 
377   auto element_type_id = fuzzerutil::WalkCompositeTypeIndices(
378       GetIRContext(), uniform_variable_type_intr->GetSingleWordInOperand(1),
379       uniform_descriptor.index());
380   assert(element_type_id && "Type of uniform variable is invalid");
381 
382   FindOrCreatePointerType(element_type_id, SpvStorageClassUniform);
383 
384   // Create, apply and record a transformation to replace the constant use with
385   // the result of a load from the chosen uniform.
386   ApplyTransformation(TransformationReplaceConstantWithUniform(
387       constant_use, uniform_descriptor, GetFuzzerContext()->GetFreshId(),
388       GetFuzzerContext()->GetFreshId()));
389 }
390 
ObfuscateConstant(uint32_t depth,const protobufs::IdUseDescriptor & constant_use)391 void FuzzerPassObfuscateConstants::ObfuscateConstant(
392     uint32_t depth, const protobufs::IdUseDescriptor& constant_use) {
393   switch (GetIRContext()
394               ->get_def_use_mgr()
395               ->GetDef(constant_use.id_of_interest())
396               ->opcode()) {
397     case SpvOpConstantTrue:
398     case SpvOpConstantFalse:
399       ObfuscateBoolConstant(depth, constant_use);
400       break;
401     case SpvOpConstant:
402       ObfuscateScalarConstant(depth, constant_use);
403       break;
404     default:
405       assert(false && "The opcode should be one of the above.");
406       break;
407   }
408 }
409 
MaybeAddConstantIdUse(const opt::Instruction & inst,uint32_t in_operand_index,uint32_t base_instruction_result_id,const std::map<SpvOp,uint32_t> & skipped_opcode_count,std::vector<protobufs::IdUseDescriptor> * constant_uses)410 void FuzzerPassObfuscateConstants::MaybeAddConstantIdUse(
411     const opt::Instruction& inst, uint32_t in_operand_index,
412     uint32_t base_instruction_result_id,
413     const std::map<SpvOp, uint32_t>& skipped_opcode_count,
414     std::vector<protobufs::IdUseDescriptor>* constant_uses) {
415   if (inst.GetInOperand(in_operand_index).type != SPV_OPERAND_TYPE_ID) {
416     // The operand is not an id, so it cannot be a constant id.
417     return;
418   }
419   auto operand_id = inst.GetSingleWordInOperand(in_operand_index);
420   auto operand_definition =
421       GetIRContext()->get_def_use_mgr()->GetDef(operand_id);
422   switch (operand_definition->opcode()) {
423     case SpvOpConstantFalse:
424     case SpvOpConstantTrue:
425     case SpvOpConstant: {
426       // The operand is a constant id, so make an id use descriptor and record
427       // it.
428       protobufs::IdUseDescriptor id_use_descriptor;
429       id_use_descriptor.set_id_of_interest(operand_id);
430       id_use_descriptor.mutable_enclosing_instruction()
431           ->set_target_instruction_opcode(inst.opcode());
432       id_use_descriptor.mutable_enclosing_instruction()
433           ->set_base_instruction_result_id(base_instruction_result_id);
434       id_use_descriptor.mutable_enclosing_instruction()
435           ->set_num_opcodes_to_ignore(
436               skipped_opcode_count.find(inst.opcode()) ==
437                       skipped_opcode_count.end()
438                   ? 0
439                   : skipped_opcode_count.at(inst.opcode()));
440       id_use_descriptor.set_in_operand_index(in_operand_index);
441       constant_uses->push_back(id_use_descriptor);
442     } break;
443     default:
444       break;
445   }
446 }
447 
Apply()448 void FuzzerPassObfuscateConstants::Apply() {
449   // First, gather up all the constant uses available in the module, by going
450   // through each block in each function.
451   std::vector<protobufs::IdUseDescriptor> constant_uses;
452   for (auto& function : *GetIRContext()->module()) {
453     for (auto& block : function) {
454       // For each constant use we encounter we are going to make an id use
455       // descriptor. An id use is described with respect to a base instruction;
456       // if there are instructions at the start of the block without result ids,
457       // the base instruction will have to be the block's label.
458       uint32_t base_instruction_result_id = block.id();
459 
460       // An id use descriptor also records how many instructions of a particular
461       // opcode need to be skipped in order to find the instruction of interest
462       // from the base instruction. We maintain a mapping that records a skip
463       // count for each relevant opcode.
464       std::map<SpvOp, uint32_t> skipped_opcode_count;
465 
466       // Go through each instruction in the block.
467       for (auto& inst : block) {
468         if (inst.HasResultId()) {
469           // The instruction has a result id, so can be used as the base
470           // instruction from now on, until another instruction with a result id
471           // is encountered.
472           base_instruction_result_id = inst.result_id();
473           // Opcode skip counts were with respect to the previous base
474           // instruction and are now irrelevant.
475           skipped_opcode_count.clear();
476         }
477 
478         // The instruction must not be an OpVariable, the only id that an
479         // OpVariable uses is an initializer id, which has to remain
480         // constant.
481         if (inst.opcode() != SpvOpVariable) {
482           // Consider each operand of the instruction, and add a constant id
483           // use for the operand if relevant.
484           for (uint32_t in_operand_index = 0;
485                in_operand_index < inst.NumInOperands(); in_operand_index++) {
486             MaybeAddConstantIdUse(inst, in_operand_index,
487                                   base_instruction_result_id,
488                                   skipped_opcode_count, &constant_uses);
489           }
490         }
491 
492         if (!inst.HasResultId()) {
493           // The instruction has no result id, so in order to identify future id
494           // uses for instructions with this opcode from the existing base
495           // instruction, we need to increase the skip count for this opcode.
496           skipped_opcode_count[inst.opcode()] =
497               skipped_opcode_count.find(inst.opcode()) ==
498                       skipped_opcode_count.end()
499                   ? 1
500                   : skipped_opcode_count[inst.opcode()] + 1;
501         }
502       }
503     }
504   }
505 
506   // Go through the constant uses in a random order by repeatedly pulling out a
507   // constant use at a random index.
508   while (!constant_uses.empty()) {
509     auto index = GetFuzzerContext()->RandomIndex(constant_uses);
510     auto constant_use = std::move(constant_uses[index]);
511     constant_uses.erase(constant_uses.begin() + index);
512     // Decide probabilistically whether to skip or obfuscate this constant use.
513     if (!GetFuzzerContext()->ChoosePercentage(
514             GetFuzzerContext()->GetChanceOfObfuscatingConstant())) {
515       continue;
516     }
517     ObfuscateConstant(0, constant_use);
518   }
519 }
520 
521 }  // namespace fuzz
522 }  // namespace spvtools
523