• 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<spv::Op> & greater_than_opcodes,const std::vector<spv::Op> & 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<spv::Op>& greater_than_opcodes,
41     const std::vector<spv::Op>& 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 == spv::Op::OpConstantFalse ||
48           bool_constant_opcode == spv::Op::OpConstantTrue) &&
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   spv::Op 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 == spv::Op::OpConstantTrue &&
72        first_constant_is_larger == is_greater_than_opcode) ||
73       (bool_constant_opcode == spv::Op::OpConstantFalse &&
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<spv::Op> greater_than_opcodes{
151       spv::Op::OpFOrdGreaterThan, spv::Op::OpFOrdGreaterThanEqual,
152       spv::Op::OpFUnordGreaterThan, spv::Op::OpFUnordGreaterThanEqual};
153   std::vector<spv::Op> less_than_opcodes{
154       spv::Op::OpFOrdGreaterThan, spv::Op::OpFOrdGreaterThanEqual,
155       spv::Op::OpFUnordGreaterThan, spv::Op::OpFUnordGreaterThanEqual};
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<spv::Op> greater_than_opcodes{spv::Op::OpSGreaterThan,
194                                             spv::Op::OpSGreaterThanEqual};
195   std::vector<spv::Op> less_than_opcodes{spv::Op::OpSLessThan,
196                                          spv::Op::OpSLessThanEqual};
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<spv::Op> greater_than_opcodes{spv::Op::OpUGreaterThan,
237                                             spv::Op::OpUGreaterThanEqual};
238   std::vector<spv::Op> less_than_opcodes{spv::Op::OpULessThan,
239                                          spv::Op::OpULessThanEqual};
240 
241   ObfuscateBoolConstantViaConstantPair(
242       depth, bool_constant_use, greater_than_opcodes, less_than_opcodes,
243       unsigned_int_constant_id_1, unsigned_int_constant_id_2,
244       first_constant_is_larger);
245 }
246 
247 std::vector<std::vector<uint32_t>>
GetConstantWordsFromUniformsForType(uint32_t type_id)248 FuzzerPassObfuscateConstants::GetConstantWordsFromUniformsForType(
249     uint32_t type_id) {
250   assert(type_id && "Type id can't be 0");
251   std::vector<std::vector<uint32_t>> result;
252 
253   for (const auto& facts_and_types : GetTransformationContext()
254                                          ->GetFactManager()
255                                          ->GetConstantUniformFactsAndTypes()) {
256     if (facts_and_types.second != type_id) {
257       continue;
258     }
259 
260     std::vector<uint32_t> words(facts_and_types.first.constant_word().begin(),
261                                 facts_and_types.first.constant_word().end());
262     if (std::find(result.begin(), result.end(), words) == result.end()) {
263       result.push_back(std::move(words));
264     }
265   }
266 
267   return result;
268 }
269 
ObfuscateBoolConstant(uint32_t depth,const protobufs::IdUseDescriptor & constant_use)270 void FuzzerPassObfuscateConstants::ObfuscateBoolConstant(
271     uint32_t depth, const protobufs::IdUseDescriptor& constant_use) {
272   // We want to replace the boolean constant use with a binary expression over
273   // scalar constants, but only if we can then potentially replace the constants
274   // with uniforms of the same value.
275 
276   auto available_types_with_uniforms =
277       GetTransformationContext()
278           ->GetFactManager()
279           ->GetTypesForWhichUniformValuesAreKnown();
280   if (available_types_with_uniforms.empty()) {
281     // Do not try to obfuscate if we do not have access to any uniform
282     // elements with known values.
283     return;
284   }
285   auto chosen_type_id =
286       available_types_with_uniforms[GetFuzzerContext()->RandomIndex(
287           available_types_with_uniforms)];
288   auto available_constant_words =
289       GetConstantWordsFromUniformsForType(chosen_type_id);
290   if (available_constant_words.size() == 1) {
291     // TODO(afd): for now we only obfuscate a boolean if there are at least
292     //  two constants available from uniforms, so that we can do a
293     //  comparison between them. It would be good to be able to do the
294     //  obfuscation even if there is only one such constant, if there is
295     //  also another regular constant available.
296     return;
297   }
298 
299   assert(!available_constant_words.empty() &&
300          "There exists a fact but no constants - impossible");
301 
302   // We know we have at least two known-to-be-constant uniforms of the chosen
303   // type.  Pick one of them at random.
304   auto constant_index_1 =
305       GetFuzzerContext()->RandomIndex(available_constant_words);
306   uint32_t constant_index_2;
307 
308   // Now choose another one distinct from the first one.
309   do {
310     constant_index_2 =
311         GetFuzzerContext()->RandomIndex(available_constant_words);
312   } while (constant_index_1 == constant_index_2);
313 
314   auto constant_id_1 = FindOrCreateConstant(
315       available_constant_words[constant_index_1], chosen_type_id, false);
316   auto constant_id_2 = FindOrCreateConstant(
317       available_constant_words[constant_index_2], chosen_type_id, false);
318 
319   assert(constant_id_1 != 0 && constant_id_2 != 0 &&
320          "We should not find an available constant with an id of 0.");
321 
322   // Now perform the obfuscation, according to whether the type of the constants
323   // is float, signed int, or unsigned int.
324   auto chosen_type = GetIRContext()->get_type_mgr()->GetType(chosen_type_id);
325   if (chosen_type->AsFloat()) {
326     ObfuscateBoolConstantViaFloatConstantPair(depth, constant_use,
327                                               constant_id_1, constant_id_2);
328   } else {
329     assert(chosen_type->AsInteger() &&
330            "We should only have uniform facts about ints and floats.");
331     if (chosen_type->AsInteger()->IsSigned()) {
332       ObfuscateBoolConstantViaSignedIntConstantPair(
333           depth, constant_use, constant_id_1, constant_id_2);
334     } else {
335       ObfuscateBoolConstantViaUnsignedIntConstantPair(
336           depth, constant_use, constant_id_1, constant_id_2);
337     }
338   }
339 }
340 
ObfuscateScalarConstant(uint32_t,const protobufs::IdUseDescriptor & constant_use)341 void FuzzerPassObfuscateConstants::ObfuscateScalarConstant(
342     uint32_t /*depth*/, const protobufs::IdUseDescriptor& constant_use) {
343   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2670): consider
344   //  additional ways to obfuscate scalar constants.
345 
346   // Check whether we know that any uniforms are guaranteed to be equal to the
347   // scalar constant associated with |constant_use|.
348   auto uniform_descriptors =
349       GetTransformationContext()
350           ->GetFactManager()
351           ->GetUniformDescriptorsForConstant(constant_use.id_of_interest());
352   if (uniform_descriptors.empty()) {
353     // No relevant uniforms, so do not obfuscate.
354     return;
355   }
356 
357   // Choose a random available uniform known to be equal to the constant.
358   const auto& uniform_descriptor =
359       uniform_descriptors[GetFuzzerContext()->RandomIndex(uniform_descriptors)];
360 
361   // Make sure the module has OpConstant instructions for each index used to
362   // access a uniform.
363   for (auto index : uniform_descriptor.index()) {
364     FindOrCreateIntegerConstant({index}, 32, true, false);
365   }
366 
367   // Make sure the module has OpTypePointer that points to the element type of
368   // the uniform.
369   const auto* uniform_variable_instr =
370       FindUniformVariable(uniform_descriptor, GetIRContext(), true);
371   assert(uniform_variable_instr &&
372          "Uniform variable does not exist or not unique.");
373 
374   const auto* uniform_variable_type_intr =
375       GetIRContext()->get_def_use_mgr()->GetDef(
376           uniform_variable_instr->type_id());
377   assert(uniform_variable_type_intr && "Uniform variable has invalid type");
378 
379   auto element_type_id = fuzzerutil::WalkCompositeTypeIndices(
380       GetIRContext(), uniform_variable_type_intr->GetSingleWordInOperand(1),
381       uniform_descriptor.index());
382   assert(element_type_id && "Type of uniform variable is invalid");
383 
384   FindOrCreatePointerType(element_type_id, spv::StorageClass::Uniform);
385 
386   // Create, apply and record a transformation to replace the constant use with
387   // the result of a load from the chosen uniform.
388   ApplyTransformation(TransformationReplaceConstantWithUniform(
389       constant_use, uniform_descriptor, GetFuzzerContext()->GetFreshId(),
390       GetFuzzerContext()->GetFreshId()));
391 }
392 
ObfuscateConstant(uint32_t depth,const protobufs::IdUseDescriptor & constant_use)393 void FuzzerPassObfuscateConstants::ObfuscateConstant(
394     uint32_t depth, const protobufs::IdUseDescriptor& constant_use) {
395   switch (GetIRContext()
396               ->get_def_use_mgr()
397               ->GetDef(constant_use.id_of_interest())
398               ->opcode()) {
399     case spv::Op::OpConstantTrue:
400     case spv::Op::OpConstantFalse:
401       ObfuscateBoolConstant(depth, constant_use);
402       break;
403     case spv::Op::OpConstant:
404       ObfuscateScalarConstant(depth, constant_use);
405       break;
406     default:
407       assert(false && "The opcode should be one of the above.");
408       break;
409   }
410 }
411 
MaybeAddConstantIdUse(const opt::Instruction & inst,uint32_t in_operand_index,uint32_t base_instruction_result_id,const std::map<spv::Op,uint32_t> & skipped_opcode_count,std::vector<protobufs::IdUseDescriptor> * constant_uses)412 void FuzzerPassObfuscateConstants::MaybeAddConstantIdUse(
413     const opt::Instruction& inst, uint32_t in_operand_index,
414     uint32_t base_instruction_result_id,
415     const std::map<spv::Op, uint32_t>& skipped_opcode_count,
416     std::vector<protobufs::IdUseDescriptor>* constant_uses) {
417   if (inst.GetInOperand(in_operand_index).type != SPV_OPERAND_TYPE_ID) {
418     // The operand is not an id, so it cannot be a constant id.
419     return;
420   }
421   auto operand_id = inst.GetSingleWordInOperand(in_operand_index);
422   auto operand_definition =
423       GetIRContext()->get_def_use_mgr()->GetDef(operand_id);
424   switch (operand_definition->opcode()) {
425     case spv::Op::OpConstantFalse:
426     case spv::Op::OpConstantTrue:
427     case spv::Op::OpConstant: {
428       // The operand is a constant id, so make an id use descriptor and record
429       // it.
430       protobufs::IdUseDescriptor id_use_descriptor;
431       id_use_descriptor.set_id_of_interest(operand_id);
432       id_use_descriptor.mutable_enclosing_instruction()
433           ->set_target_instruction_opcode(uint32_t(inst.opcode()));
434       id_use_descriptor.mutable_enclosing_instruction()
435           ->set_base_instruction_result_id(base_instruction_result_id);
436       id_use_descriptor.mutable_enclosing_instruction()
437           ->set_num_opcodes_to_ignore(
438               skipped_opcode_count.find(inst.opcode()) ==
439                       skipped_opcode_count.end()
440                   ? 0
441                   : skipped_opcode_count.at(inst.opcode()));
442       id_use_descriptor.set_in_operand_index(in_operand_index);
443       constant_uses->push_back(id_use_descriptor);
444     } break;
445     default:
446       break;
447   }
448 }
449 
Apply()450 void FuzzerPassObfuscateConstants::Apply() {
451   // First, gather up all the constant uses available in the module, by going
452   // through each block in each function.
453   std::vector<protobufs::IdUseDescriptor> constant_uses;
454   for (auto& function : *GetIRContext()->module()) {
455     for (auto& block : function) {
456       // For each constant use we encounter we are going to make an id use
457       // descriptor. An id use is described with respect to a base instruction;
458       // if there are instructions at the start of the block without result ids,
459       // the base instruction will have to be the block's label.
460       uint32_t base_instruction_result_id = block.id();
461 
462       // An id use descriptor also records how many instructions of a particular
463       // opcode need to be skipped in order to find the instruction of interest
464       // from the base instruction. We maintain a mapping that records a skip
465       // count for each relevant opcode.
466       std::map<spv::Op, uint32_t> skipped_opcode_count;
467 
468       // Go through each instruction in the block.
469       for (auto& inst : block) {
470         if (inst.HasResultId()) {
471           // The instruction has a result id, so can be used as the base
472           // instruction from now on, until another instruction with a result id
473           // is encountered.
474           base_instruction_result_id = inst.result_id();
475           // Opcode skip counts were with respect to the previous base
476           // instruction and are now irrelevant.
477           skipped_opcode_count.clear();
478         }
479 
480         // The instruction must not be an OpVariable, the only id that an
481         // OpVariable uses is an initializer id, which has to remain
482         // constant.
483         if (inst.opcode() != spv::Op::OpVariable) {
484           // Consider each operand of the instruction, and add a constant id
485           // use for the operand if relevant.
486           for (uint32_t in_operand_index = 0;
487                in_operand_index < inst.NumInOperands(); in_operand_index++) {
488             MaybeAddConstantIdUse(inst, in_operand_index,
489                                   base_instruction_result_id,
490                                   skipped_opcode_count, &constant_uses);
491           }
492         }
493 
494         if (!inst.HasResultId()) {
495           // The instruction has no result id, so in order to identify future id
496           // uses for instructions with this opcode from the existing base
497           // instruction, we need to increase the skip count for this opcode.
498           skipped_opcode_count[inst.opcode()] =
499               skipped_opcode_count.find(inst.opcode()) ==
500                       skipped_opcode_count.end()
501                   ? 1
502                   : skipped_opcode_count[inst.opcode()] + 1;
503         }
504       }
505     }
506   }
507 
508   // Go through the constant uses in a random order by repeatedly pulling out a
509   // constant use at a random index.
510   while (!constant_uses.empty()) {
511     auto index = GetFuzzerContext()->RandomIndex(constant_uses);
512     auto constant_use = std::move(constant_uses[index]);
513     constant_uses.erase(constant_uses.begin() + index);
514     // Decide probabilistically whether to skip or obfuscate this constant use.
515     if (!GetFuzzerContext()->ChoosePercentage(
516             GetFuzzerContext()->GetChanceOfObfuscatingConstant())) {
517       continue;
518     }
519     ObfuscateConstant(0, constant_use);
520   }
521 }
522 
523 }  // namespace fuzz
524 }  // namespace spvtools
525