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