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