• 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/transformation_replace_id_with_synonym.h"
16 
17 #include "gtest/gtest.h"
18 #include "source/fuzz/data_descriptor.h"
19 #include "source/fuzz/fuzzer_util.h"
20 #include "source/fuzz/id_use_descriptor.h"
21 #include "source/fuzz/instruction_descriptor.h"
22 #include "test/fuzz/fuzz_test_util.h"
23 
24 namespace spvtools {
25 namespace fuzz {
26 namespace {
27 
28 // The following shader was obtained from this GLSL, which was then optimized
29 // with spirv-opt -O and manually edited to include some uses of OpCopyObject
30 // (to introduce id synonyms).
31 //
32 // #version 310 es
33 //
34 // precision highp int;
35 // precision highp float;
36 //
37 // layout(set = 0, binding = 0) uniform buf {
38 //   int a;
39 //   int b;
40 //   int c;
41 // };
42 //
43 // layout(location = 0) out vec4 color;
44 //
45 // void main() {
46 //   int x = a;
47 //   float f = 0.0;
48 //   while (x < b) {
49 //     switch(x % 4) {
50 //       case 0:
51 //         color[0] = f;
52 //         break;
53 //       case 1:
54 //         color[1] = f;
55 //         break;
56 //       case 2:
57 //         color[2] = f;
58 //         break;
59 //       case 3:
60 //         color[3] = f;
61 //         break;
62 //       default:
63 //         break;
64 //     }
65 //     if (x > c) {
66 //       x++;
67 //     } else {
68 //       x += 2;
69 //     }
70 //   }
71 //   color[0] += color[1] + float(x);
72 // }
73 const std::string kComplexShader = R"(
74                OpCapability Shader
75           %1 = OpExtInstImport "GLSL.std.450"
76                OpMemoryModel Logical GLSL450
77                OpEntryPoint Fragment %4 "main" %42
78                OpExecutionMode %4 OriginUpperLeft
79                OpSource ESSL 310
80                OpName %4 "main"
81                OpName %9 "buf"
82                OpMemberName %9 0 "a"
83                OpMemberName %9 1 "b"
84                OpMemberName %9 2 "c"
85                OpName %11 ""
86                OpName %42 "color"
87                OpMemberDecorate %9 0 Offset 0
88                OpMemberDecorate %9 1 Offset 4
89                OpMemberDecorate %9 2 Offset 8
90                OpDecorate %9 Block
91                OpDecorate %11 DescriptorSet 0
92                OpDecorate %11 Binding 0
93                OpDecorate %42 Location 0
94           %2 = OpTypeVoid
95           %3 = OpTypeFunction %2
96           %6 = OpTypeInt 32 1
97           %9 = OpTypeStruct %6 %6 %6
98          %10 = OpTypePointer Uniform %9
99          %11 = OpVariable %10 Uniform
100          %12 = OpConstant %6 0
101          %13 = OpTypePointer Uniform %6
102          %16 = OpTypeFloat 32
103          %19 = OpConstant %16 0
104          %26 = OpConstant %6 1
105          %29 = OpTypeBool
106          %32 = OpConstant %6 4
107          %40 = OpTypeVector %16 4
108          %41 = OpTypePointer Output %40
109          %42 = OpVariable %41 Output
110          %44 = OpTypeInt 32 0
111          %45 = OpConstant %44 0
112          %46 = OpTypePointer Output %16
113          %50 = OpConstant %44 1
114          %54 = OpConstant %44 2
115          %58 = OpConstant %44 3
116          %64 = OpConstant %6 2
117           %4 = OpFunction %2 None %3
118           %5 = OpLabel
119         %209 = OpCopyObject %6 %12
120          %14 = OpAccessChain %13 %11 %12
121          %15 = OpLoad %6 %14
122         %200 = OpCopyObject %6 %15
123                OpBranch %20
124          %20 = OpLabel
125          %84 = OpPhi %6 %15 %5 %86 %69
126          %27 = OpAccessChain %13 %11 %26
127          %28 = OpLoad %6 %27
128         %207 = OpCopyObject %6 %84
129         %201 = OpCopyObject %6 %15
130          %30 = OpSLessThan %29 %84 %28
131                OpLoopMerge %22 %69 None
132                OpBranchConditional %30 %21 %22
133          %21 = OpLabel
134          %33 = OpSMod %6 %84 %32
135         %208 = OpCopyObject %6 %33
136                OpSelectionMerge %39 None
137                OpSwitch %33 %38 0 %34 1 %35 2 %36 3 %37
138          %38 = OpLabel
139         %202 = OpCopyObject %6 %15
140                OpBranch %39
141          %34 = OpLabel
142         %210 = OpCopyObject %16 %19
143          %47 = OpAccessChain %46 %42 %45
144                OpStore %47 %19
145                OpBranch %39
146          %35 = OpLabel
147          %51 = OpAccessChain %46 %42 %50
148                OpStore %51 %19
149                OpBranch %39
150          %36 = OpLabel
151         %204 = OpCopyObject %44 %54
152          %55 = OpAccessChain %46 %42 %54
153         %203 = OpCopyObject %46 %55
154                OpStore %55 %19
155                OpBranch %39
156          %37 = OpLabel
157          %59 = OpAccessChain %46 %42 %58
158                OpStore %59 %19
159                OpBranch %39
160          %39 = OpLabel
161         %300 = OpIAdd %6 %15 %15
162          %65 = OpAccessChain %13 %11 %64
163          %66 = OpLoad %6 %65
164          %67 = OpSGreaterThan %29 %84 %66
165                OpSelectionMerge %1000 None
166                OpBranchConditional %67 %68 %72
167          %68 = OpLabel
168          %71 = OpIAdd %6 %84 %26
169                OpBranch %1000
170          %72 = OpLabel
171          %74 = OpIAdd %6 %84 %64
172         %205 = OpCopyObject %6 %74
173                OpBranch %1000
174        %1000 = OpLabel
175          %86 = OpPhi %6 %71 %68 %74 %72
176         %301 = OpPhi %6 %71 %68 %15 %72
177                OpBranch %69
178          %69 = OpLabel
179                OpBranch %20
180          %22 = OpLabel
181          %75 = OpAccessChain %46 %42 %50
182          %76 = OpLoad %16 %75
183          %78 = OpConvertSToF %16 %84
184          %80 = OpAccessChain %46 %42 %45
185         %206 = OpCopyObject %16 %78
186          %81 = OpLoad %16 %80
187          %79 = OpFAdd %16 %76 %78
188          %82 = OpFAdd %16 %81 %79
189                OpStore %80 %82
190                OpReturn
191                OpFunctionEnd
192 )";
193 
MakeSynonymFact(uint32_t first,uint32_t second)194 protobufs::Fact MakeSynonymFact(uint32_t first, uint32_t second) {
195   protobufs::FactDataSynonym data_synonym_fact;
196   *data_synonym_fact.mutable_data1() = MakeDataDescriptor(first, {});
197   *data_synonym_fact.mutable_data2() = MakeDataDescriptor(second, {});
198   protobufs::Fact result;
199   *result.mutable_data_synonym_fact() = data_synonym_fact;
200   return result;
201 }
202 
203 // Equips the fact manager with synonym facts for the above shader.
SetUpIdSynonyms(FactManager * fact_manager)204 void SetUpIdSynonyms(FactManager* fact_manager) {
205   fact_manager->MaybeAddFact(MakeSynonymFact(15, 200));
206   fact_manager->MaybeAddFact(MakeSynonymFact(15, 201));
207   fact_manager->MaybeAddFact(MakeSynonymFact(15, 202));
208   fact_manager->MaybeAddFact(MakeSynonymFact(55, 203));
209   fact_manager->MaybeAddFact(MakeSynonymFact(54, 204));
210   fact_manager->MaybeAddFact(MakeSynonymFact(74, 205));
211   fact_manager->MaybeAddFact(MakeSynonymFact(78, 206));
212   fact_manager->MaybeAddFact(MakeSynonymFact(84, 207));
213   fact_manager->MaybeAddFact(MakeSynonymFact(33, 208));
214   fact_manager->MaybeAddFact(MakeSynonymFact(12, 209));
215   fact_manager->MaybeAddFact(MakeSynonymFact(19, 210));
216 }
217 
TEST(TransformationReplaceIdWithSynonymTest,IllegalTransformations)218 TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) {
219   const auto env = SPV_ENV_UNIVERSAL_1_3;
220   const auto consumer = nullptr;
221   const auto context =
222       BuildModule(env, consumer, kComplexShader, kFuzzAssembleOption);
223   spvtools::ValidatorOptions validator_options;
224   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
225                                                kConsoleMessageConsumer));
226   TransformationContext transformation_context(
227       MakeUnique<FactManager>(context.get()), validator_options);
228   SetUpIdSynonyms(transformation_context.GetFactManager());
229 
230   // %202 cannot replace %15 as in-operand 0 of %300, since %202 does not
231   // dominate %300.
232   auto synonym_does_not_dominate_use = TransformationReplaceIdWithSynonym(
233       MakeIdUseDescriptor(15, MakeInstructionDescriptor(300, SpvOpIAdd, 0), 0),
234       202);
235   ASSERT_FALSE(synonym_does_not_dominate_use.IsApplicable(
236       context.get(), transformation_context));
237 
238   // %202 cannot replace %15 as in-operand 2 of %301, since this is the OpPhi's
239   // incoming value for block %72, and %202 does not dominate %72.
240   auto synonym_does_not_dominate_use_op_phi =
241       TransformationReplaceIdWithSynonym(
242           MakeIdUseDescriptor(15, MakeInstructionDescriptor(301, SpvOpPhi, 0),
243                               2),
244           202);
245   ASSERT_FALSE(synonym_does_not_dominate_use_op_phi.IsApplicable(
246       context.get(), transformation_context));
247 
248   // %200 is not a synonym for %84
249   auto id_in_use_is_not_synonymous = TransformationReplaceIdWithSynonym(
250       MakeIdUseDescriptor(
251           84, MakeInstructionDescriptor(67, SpvOpSGreaterThan, 0), 0),
252       200);
253   ASSERT_FALSE(id_in_use_is_not_synonymous.IsApplicable(
254       context.get(), transformation_context));
255 
256   // %86 is not a synonym for anything (and in particular not for %74)
257   auto id_has_no_synonyms = TransformationReplaceIdWithSynonym(
258       MakeIdUseDescriptor(86, MakeInstructionDescriptor(84, SpvOpPhi, 0), 2),
259       74);
260   ASSERT_FALSE(
261       id_has_no_synonyms.IsApplicable(context.get(), transformation_context));
262 
263   // This would lead to %207 = 'OpCopyObject %type %207' if it were allowed
264   auto synonym_use_is_in_synonym_definition =
265       TransformationReplaceIdWithSynonym(
266           MakeIdUseDescriptor(
267               84, MakeInstructionDescriptor(207, SpvOpCopyObject, 0), 0),
268           207);
269   ASSERT_FALSE(synonym_use_is_in_synonym_definition.IsApplicable(
270       context.get(), transformation_context));
271 
272   // The id use descriptor does not lead to a use (%84 is not used in the
273   // definition of %207)
274   auto bad_id_use_descriptor = TransformationReplaceIdWithSynonym(
275       MakeIdUseDescriptor(
276           84, MakeInstructionDescriptor(200, SpvOpCopyObject, 0), 0),
277       207);
278   ASSERT_FALSE(bad_id_use_descriptor.IsApplicable(context.get(),
279                                                   transformation_context));
280 
281   // This replacement would lead to an access chain into a struct using a
282   // non-constant index.
283   auto bad_access_chain = TransformationReplaceIdWithSynonym(
284       MakeIdUseDescriptor(
285           12, MakeInstructionDescriptor(14, SpvOpAccessChain, 0), 1),
286       209);
287   ASSERT_FALSE(
288       bad_access_chain.IsApplicable(context.get(), transformation_context));
289 }
290 
TEST(TransformationReplaceIdWithSynonymTest,LegalTransformations)291 TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) {
292   const auto env = SPV_ENV_UNIVERSAL_1_3;
293   const auto consumer = nullptr;
294   const auto context =
295       BuildModule(env, consumer, kComplexShader, kFuzzAssembleOption);
296   spvtools::ValidatorOptions validator_options;
297   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
298                                                kConsoleMessageConsumer));
299   TransformationContext transformation_context(
300       MakeUnique<FactManager>(context.get()), validator_options);
301   SetUpIdSynonyms(transformation_context.GetFactManager());
302 
303   auto global_constant_synonym = TransformationReplaceIdWithSynonym(
304       MakeIdUseDescriptor(19, MakeInstructionDescriptor(47, SpvOpStore, 0), 1),
305       210);
306   uint32_t num_uses_of_original_id_before_replacement =
307       context->get_def_use_mgr()->NumUses(19);
308   uint32_t num_uses_of_synonym_before_replacement =
309       context->get_def_use_mgr()->NumUses(210);
310   ASSERT_TRUE(global_constant_synonym.IsApplicable(context.get(),
311                                                    transformation_context));
312   ApplyAndCheckFreshIds(global_constant_synonym, context.get(),
313                         &transformation_context);
314   ASSERT_EQ(num_uses_of_original_id_before_replacement - 1,
315             context->get_def_use_mgr()->NumUses(19));
316   ASSERT_EQ(num_uses_of_synonym_before_replacement + 1,
317             context->get_def_use_mgr()->NumUses(210));
318   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
319                                                kConsoleMessageConsumer));
320 
321   auto replace_vector_access_chain_index = TransformationReplaceIdWithSynonym(
322       MakeIdUseDescriptor(
323           54, MakeInstructionDescriptor(55, SpvOpAccessChain, 0), 1),
324       204);
325   ASSERT_TRUE(replace_vector_access_chain_index.IsApplicable(
326       context.get(), transformation_context));
327   ApplyAndCheckFreshIds(replace_vector_access_chain_index, context.get(),
328                         &transformation_context);
329   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
330                                                kConsoleMessageConsumer));
331 
332   // This is an interesting case because it replaces something that is being
333   // copied with something that is already a synonym.
334   auto regular_replacement = TransformationReplaceIdWithSynonym(
335       MakeIdUseDescriptor(
336           15, MakeInstructionDescriptor(202, SpvOpCopyObject, 0), 0),
337       201);
338   ASSERT_TRUE(
339       regular_replacement.IsApplicable(context.get(), transformation_context));
340   ApplyAndCheckFreshIds(regular_replacement, context.get(),
341                         &transformation_context);
342   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
343                                                kConsoleMessageConsumer));
344 
345   auto regular_replacement2 = TransformationReplaceIdWithSynonym(
346       MakeIdUseDescriptor(55, MakeInstructionDescriptor(203, SpvOpStore, 0), 0),
347       203);
348   ASSERT_TRUE(
349       regular_replacement2.IsApplicable(context.get(), transformation_context));
350   ApplyAndCheckFreshIds(regular_replacement2, context.get(),
351                         &transformation_context);
352   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
353                                                kConsoleMessageConsumer));
354 
355   auto good_op_phi = TransformationReplaceIdWithSynonym(
356       MakeIdUseDescriptor(74, MakeInstructionDescriptor(86, SpvOpPhi, 0), 2),
357       205);
358   ASSERT_TRUE(good_op_phi.IsApplicable(context.get(), transformation_context));
359   ApplyAndCheckFreshIds(good_op_phi, context.get(), &transformation_context);
360   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
361                                                kConsoleMessageConsumer));
362 
363   const std::string after_transformation = R"(
364                OpCapability Shader
365           %1 = OpExtInstImport "GLSL.std.450"
366                OpMemoryModel Logical GLSL450
367                OpEntryPoint Fragment %4 "main" %42
368                OpExecutionMode %4 OriginUpperLeft
369                OpSource ESSL 310
370                OpName %4 "main"
371                OpName %9 "buf"
372                OpMemberName %9 0 "a"
373                OpMemberName %9 1 "b"
374                OpMemberName %9 2 "c"
375                OpName %11 ""
376                OpName %42 "color"
377                OpMemberDecorate %9 0 Offset 0
378                OpMemberDecorate %9 1 Offset 4
379                OpMemberDecorate %9 2 Offset 8
380                OpDecorate %9 Block
381                OpDecorate %11 DescriptorSet 0
382                OpDecorate %11 Binding 0
383                OpDecorate %42 Location 0
384           %2 = OpTypeVoid
385           %3 = OpTypeFunction %2
386           %6 = OpTypeInt 32 1
387           %9 = OpTypeStruct %6 %6 %6
388          %10 = OpTypePointer Uniform %9
389          %11 = OpVariable %10 Uniform
390          %12 = OpConstant %6 0
391          %13 = OpTypePointer Uniform %6
392          %16 = OpTypeFloat 32
393          %19 = OpConstant %16 0
394          %26 = OpConstant %6 1
395          %29 = OpTypeBool
396          %32 = OpConstant %6 4
397          %40 = OpTypeVector %16 4
398          %41 = OpTypePointer Output %40
399          %42 = OpVariable %41 Output
400          %44 = OpTypeInt 32 0
401          %45 = OpConstant %44 0
402          %46 = OpTypePointer Output %16
403          %50 = OpConstant %44 1
404          %54 = OpConstant %44 2
405          %58 = OpConstant %44 3
406          %64 = OpConstant %6 2
407           %4 = OpFunction %2 None %3
408           %5 = OpLabel
409         %209 = OpCopyObject %6 %12
410          %14 = OpAccessChain %13 %11 %12
411          %15 = OpLoad %6 %14
412         %200 = OpCopyObject %6 %15
413                OpBranch %20
414          %20 = OpLabel
415          %84 = OpPhi %6 %15 %5 %86 %69
416          %27 = OpAccessChain %13 %11 %26
417          %28 = OpLoad %6 %27
418         %207 = OpCopyObject %6 %84
419         %201 = OpCopyObject %6 %15
420          %30 = OpSLessThan %29 %84 %28
421                OpLoopMerge %22 %69 None
422                OpBranchConditional %30 %21 %22
423          %21 = OpLabel
424          %33 = OpSMod %6 %84 %32
425         %208 = OpCopyObject %6 %33
426                OpSelectionMerge %39 None
427                OpSwitch %33 %38 0 %34 1 %35 2 %36 3 %37
428          %38 = OpLabel
429         %202 = OpCopyObject %6 %201
430                OpBranch %39
431          %34 = OpLabel
432         %210 = OpCopyObject %16 %19
433          %47 = OpAccessChain %46 %42 %45
434                OpStore %47 %210
435                OpBranch %39
436          %35 = OpLabel
437          %51 = OpAccessChain %46 %42 %50
438                OpStore %51 %19
439                OpBranch %39
440          %36 = OpLabel
441         %204 = OpCopyObject %44 %54
442          %55 = OpAccessChain %46 %42 %204
443         %203 = OpCopyObject %46 %55
444                OpStore %203 %19
445                OpBranch %39
446          %37 = OpLabel
447          %59 = OpAccessChain %46 %42 %58
448                OpStore %59 %19
449                OpBranch %39
450          %39 = OpLabel
451         %300 = OpIAdd %6 %15 %15
452          %65 = OpAccessChain %13 %11 %64
453          %66 = OpLoad %6 %65
454          %67 = OpSGreaterThan %29 %84 %66
455                OpSelectionMerge %1000 None
456                OpBranchConditional %67 %68 %72
457          %68 = OpLabel
458          %71 = OpIAdd %6 %84 %26
459                OpBranch %1000
460          %72 = OpLabel
461          %74 = OpIAdd %6 %84 %64
462         %205 = OpCopyObject %6 %74
463                OpBranch %1000
464        %1000 = OpLabel
465          %86 = OpPhi %6 %71 %68 %205 %72
466         %301 = OpPhi %6 %71 %68 %15 %72
467                OpBranch %69
468          %69 = OpLabel
469                OpBranch %20
470          %22 = OpLabel
471          %75 = OpAccessChain %46 %42 %50
472          %76 = OpLoad %16 %75
473          %78 = OpConvertSToF %16 %84
474          %80 = OpAccessChain %46 %42 %45
475         %206 = OpCopyObject %16 %78
476          %81 = OpLoad %16 %80
477          %79 = OpFAdd %16 %76 %78
478          %82 = OpFAdd %16 %81 %79
479                OpStore %80 %82
480                OpReturn
481                OpFunctionEnd
482   )";
483 
484   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
485 }
486 
TEST(TransformationReplaceIdWithSynonymTest,SynonymsOfVariables)487 TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) {
488   // The following SPIR-V comes from this GLSL, with object copies added:
489   //
490   // #version 310 es
491   //
492   // precision highp int;
493   //
494   // int g;
495   //
496   // void main() {
497   //   int l;
498   //   l = g;
499   //   g = l;
500   // }
501   const std::string shader = R"(
502                OpCapability Shader
503           %1 = OpExtInstImport "GLSL.std.450"
504                OpMemoryModel Logical GLSL450
505                OpEntryPoint Fragment %4 "main"
506                OpExecutionMode %4 OriginUpperLeft
507                OpSource ESSL 310
508                OpName %4 "main"
509                OpName %8 "l"
510                OpName %10 "g"
511           %2 = OpTypeVoid
512           %3 = OpTypeFunction %2
513           %6 = OpTypeInt 32 1
514           %7 = OpTypePointer Function %6
515           %9 = OpTypePointer Private %6
516          %10 = OpVariable %9 Private
517           %4 = OpFunction %2 None %3
518           %5 = OpLabel
519           %8 = OpVariable %7 Function
520         %100 = OpCopyObject %9 %10
521         %101 = OpCopyObject %7 %8
522          %11 = OpLoad %6 %10
523                OpStore %8 %11
524          %12 = OpLoad %6 %8
525                OpStore %10 %12
526                OpReturn
527                OpFunctionEnd
528   )";
529 
530   const auto env = SPV_ENV_UNIVERSAL_1_3;
531   const auto consumer = nullptr;
532   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
533   spvtools::ValidatorOptions validator_options;
534   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
535                                                kConsoleMessageConsumer));
536   TransformationContext transformation_context(
537       MakeUnique<FactManager>(context.get()), validator_options);
538   transformation_context.GetFactManager()->MaybeAddFact(
539       MakeSynonymFact(10, 100));
540   transformation_context.GetFactManager()->MaybeAddFact(
541       MakeSynonymFact(8, 101));
542 
543   // Replace %10 with %100 in:
544   // %11 = OpLoad %6 %10
545   auto replacement1 = TransformationReplaceIdWithSynonym(
546       MakeIdUseDescriptor(10, MakeInstructionDescriptor(11, SpvOpLoad, 0), 0),
547       100);
548   ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context));
549   ApplyAndCheckFreshIds(replacement1, context.get(), &transformation_context);
550   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
551                                                kConsoleMessageConsumer));
552 
553   // Replace %8 with %101 in:
554   // OpStore %8 %11
555   auto replacement2 = TransformationReplaceIdWithSynonym(
556       MakeIdUseDescriptor(8, MakeInstructionDescriptor(11, SpvOpStore, 0), 0),
557       101);
558   ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context));
559   ApplyAndCheckFreshIds(replacement2, context.get(), &transformation_context);
560   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
561                                                kConsoleMessageConsumer));
562 
563   // Replace %8 with %101 in:
564   // %12 = OpLoad %6 %8
565   auto replacement3 = TransformationReplaceIdWithSynonym(
566       MakeIdUseDescriptor(8, MakeInstructionDescriptor(12, SpvOpLoad, 0), 0),
567       101);
568   ASSERT_TRUE(replacement3.IsApplicable(context.get(), transformation_context));
569   ApplyAndCheckFreshIds(replacement3, context.get(), &transformation_context);
570   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
571                                                kConsoleMessageConsumer));
572 
573   // Replace %10 with %100 in:
574   // OpStore %10 %12
575   auto replacement4 = TransformationReplaceIdWithSynonym(
576       MakeIdUseDescriptor(10, MakeInstructionDescriptor(12, SpvOpStore, 0), 0),
577       100);
578   ASSERT_TRUE(replacement4.IsApplicable(context.get(), transformation_context));
579   ApplyAndCheckFreshIds(replacement4, context.get(), &transformation_context);
580   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
581                                                kConsoleMessageConsumer));
582 
583   const std::string after_transformation = R"(
584                OpCapability Shader
585           %1 = OpExtInstImport "GLSL.std.450"
586                OpMemoryModel Logical GLSL450
587                OpEntryPoint Fragment %4 "main"
588                OpExecutionMode %4 OriginUpperLeft
589                OpSource ESSL 310
590                OpName %4 "main"
591                OpName %8 "l"
592                OpName %10 "g"
593           %2 = OpTypeVoid
594           %3 = OpTypeFunction %2
595           %6 = OpTypeInt 32 1
596           %7 = OpTypePointer Function %6
597           %9 = OpTypePointer Private %6
598          %10 = OpVariable %9 Private
599           %4 = OpFunction %2 None %3
600           %5 = OpLabel
601           %8 = OpVariable %7 Function
602         %100 = OpCopyObject %9 %10
603         %101 = OpCopyObject %7 %8
604          %11 = OpLoad %6 %100
605                OpStore %101 %11
606          %12 = OpLoad %6 %101
607                OpStore %100 %12
608                OpReturn
609                OpFunctionEnd
610   )";
611 
612   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
613 }
614 
TEST(TransformationReplaceIdWithSynonymTest,SynonymOfVariableNoGoodInFunctionCall)615 TEST(TransformationReplaceIdWithSynonymTest,
616      SynonymOfVariableNoGoodInFunctionCall) {
617   // The following SPIR-V comes from this GLSL, with an object copy added:
618   //
619   // #version 310 es
620   //
621   // precision highp int;
622   //
623   // void foo(int x) { }
624   //
625   // void main() {
626   //   int a;
627   //   a = 2;
628   //   foo(a);
629   // }
630   const std::string shader = R"(
631                OpCapability Shader
632           %1 = OpExtInstImport "GLSL.std.450"
633                OpMemoryModel Logical GLSL450
634                OpEntryPoint Fragment %4 "main"
635                OpExecutionMode %4 OriginUpperLeft
636                OpSource ESSL 310
637                OpName %4 "main"
638                OpName %10 "foo(i1;"
639                OpName %9 "x"
640                OpName %12 "a"
641                OpName %14 "param"
642           %2 = OpTypeVoid
643           %3 = OpTypeFunction %2
644           %6 = OpTypeInt 32 1
645           %7 = OpTypePointer Function %6
646           %8 = OpTypeFunction %2 %7
647          %13 = OpConstant %6 2
648           %4 = OpFunction %2 None %3
649           %5 = OpLabel
650          %12 = OpVariable %7 Function
651          %14 = OpVariable %7 Function
652                OpStore %12 %13
653          %15 = OpLoad %6 %12
654                OpStore %14 %15
655         %100 = OpCopyObject %7 %14
656          %16 = OpFunctionCall %2 %10 %14
657                OpReturn
658                OpFunctionEnd
659          %10 = OpFunction %2 None %8
660           %9 = OpFunctionParameter %7
661          %11 = OpLabel
662                OpReturn
663                OpFunctionEnd
664   )";
665 
666   const auto env = SPV_ENV_UNIVERSAL_1_3;
667   const auto consumer = nullptr;
668   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
669   spvtools::ValidatorOptions validator_options;
670   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
671                                                kConsoleMessageConsumer));
672   TransformationContext transformation_context(
673       MakeUnique<FactManager>(context.get()), validator_options);
674   transformation_context.GetFactManager()->MaybeAddFact(
675       MakeSynonymFact(14, 100));
676 
677   // Replace %14 with %100 in:
678   // %16 = OpFunctionCall %2 %10 %14
679   auto replacement = TransformationReplaceIdWithSynonym(
680       MakeIdUseDescriptor(
681           14, MakeInstructionDescriptor(16, SpvOpFunctionCall, 0), 1),
682       100);
683   ASSERT_FALSE(replacement.IsApplicable(context.get(), transformation_context));
684 }
685 
TEST(TransformationReplaceIdWithSynonymTest,SynonymsOfAccessChainIndices)686 TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
687   // The following SPIR-V comes from this GLSL, with object copies added:
688   //
689   // #version 310 es
690   //
691   // precision highp float;
692   // precision highp int;
693   //
694   // struct S {
695   //   int[3] a;
696   //   vec4 b;
697   //   bool c;
698   // } d;
699   //
700   // float[20] e;
701   //
702   // struct T {
703   //   float f;
704   //   S g;
705   // } h;
706   //
707   // T[4] i;
708   //
709   // void main() {
710   //   d.a[2] = 10;
711   //   d.b[3] = 11.0;
712   //   d.c = false;
713   //   e[17] = 12.0;
714   //   h.f = 13.0;
715   //   h.g.a[1] = 14;
716   //   h.g.b[0] = 15.0;
717   //   h.g.c = true;
718   //   i[0].f = 16.0;
719   //   i[1].g.a[0] = 17;
720   //   i[2].g.b[1] = 18.0;
721   //   i[3].g.c = true;
722   // }
723   const std::string shader = R"(
724                OpCapability Shader
725           %1 = OpExtInstImport "GLSL.std.450"
726                OpMemoryModel Logical GLSL450
727                OpEntryPoint Fragment %4 "main"
728                OpExecutionMode %4 OriginUpperLeft
729                OpSource ESSL 310
730                OpName %4 "main"
731                OpName %13 "S"
732                OpMemberName %13 0 "a"
733                OpMemberName %13 1 "b"
734                OpMemberName %13 2 "c"
735                OpName %15 "d"
736                OpName %31 "e"
737                OpName %35 "T"
738                OpMemberName %35 0 "f"
739                OpMemberName %35 1 "g"
740                OpName %37 "h"
741                OpName %50 "i"
742           %2 = OpTypeVoid
743           %3 = OpTypeFunction %2
744           %6 = OpTypeInt 32 1
745           %7 = OpTypeInt 32 0
746           %8 = OpConstant %7 3
747           %9 = OpTypeArray %6 %8
748          %10 = OpTypeFloat 32
749          %11 = OpTypeVector %10 4
750          %12 = OpTypeBool
751          %13 = OpTypeStruct %9 %11 %12
752          %14 = OpTypePointer Private %13
753          %15 = OpVariable %14 Private
754          %16 = OpConstant %6 0
755          %17 = OpConstant %6 2
756          %18 = OpConstant %6 10
757          %19 = OpTypePointer Private %6
758          %21 = OpConstant %6 1
759          %22 = OpConstant %10 11
760          %23 = OpTypePointer Private %10
761          %25 = OpConstantFalse %12
762          %26 = OpTypePointer Private %12
763          %28 = OpConstant %7 20
764          %29 = OpTypeArray %10 %28
765          %30 = OpTypePointer Private %29
766          %31 = OpVariable %30 Private
767          %32 = OpConstant %6 17
768          %33 = OpConstant %10 12
769          %35 = OpTypeStruct %10 %13
770          %36 = OpTypePointer Private %35
771          %37 = OpVariable %36 Private
772          %38 = OpConstant %10 13
773          %40 = OpConstant %6 14
774          %42 = OpConstant %10 15
775          %43 = OpConstant %7 0
776          %45 = OpConstantTrue %12
777          %47 = OpConstant %7 4
778          %48 = OpTypeArray %35 %47
779          %49 = OpTypePointer Private %48
780          %50 = OpVariable %49 Private
781          %51 = OpConstant %10 16
782          %54 = OpConstant %10 18
783          %55 = OpConstant %7 1
784          %57 = OpConstant %6 3
785           %4 = OpFunction %2 None %3
786           %5 = OpLabel
787 
788          %100 = OpCopyObject %6 %16 ; 0
789          %101 = OpCopyObject %6 %21 ; 1
790          %102 = OpCopyObject %6 %17 ; 2
791          %103 = OpCopyObject %6 %57 ; 3
792          %104 = OpCopyObject %6 %18 ; 10
793          %105 = OpCopyObject %6 %40 ; 14
794          %106 = OpCopyObject %6 %32 ; 17
795          %107 = OpCopyObject %7 %43 ; 0
796          %108 = OpCopyObject %7 %55 ; 1
797          %109 = OpCopyObject %7  %8 ; 3
798          %110 = OpCopyObject %7 %47 ; 4
799          %111 = OpCopyObject %7 %28 ; 20
800          %112 = OpCopyObject %12 %45 ; true
801 
802          %20 = OpAccessChain %19 %15 %16 %17
803                OpStore %20 %18
804          %24 = OpAccessChain %23 %15 %21 %8
805                OpStore %24 %22
806          %27 = OpAccessChain %26 %15 %17
807                OpStore %27 %25
808          %34 = OpAccessChain %23 %31 %32
809                OpStore %34 %33
810          %39 = OpAccessChain %23 %37 %16
811                OpStore %39 %38
812          %41 = OpAccessChain %19 %37 %21 %16 %21
813                OpStore %41 %40
814          %44 = OpAccessChain %23 %37 %21 %21 %43
815                OpStore %44 %42
816          %46 = OpAccessChain %26 %37 %21 %17
817                OpStore %46 %45
818          %52 = OpAccessChain %23 %50 %16 %16
819                OpStore %52 %51
820          %53 = OpAccessChain %19 %50 %21 %21 %16 %16
821                OpStore %53 %32
822          %56 = OpAccessChain %23 %50 %17 %21 %21 %55
823                OpStore %56 %54
824          %58 = OpInBoundsAccessChain %26 %50 %57 %21 %17
825                OpStore %58 %45
826                OpReturn
827                OpFunctionEnd
828   )";
829 
830   const auto env = SPV_ENV_UNIVERSAL_1_3;
831   const auto consumer = nullptr;
832   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
833   spvtools::ValidatorOptions validator_options;
834   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
835                                                kConsoleMessageConsumer));
836   TransformationContext transformation_context(
837       MakeUnique<FactManager>(context.get()), validator_options);
838   // Add synonym facts corresponding to the OpCopyObject operations that have
839   // been applied to all constants in the module.
840   transformation_context.GetFactManager()->MaybeAddFact(
841       MakeSynonymFact(16, 100));
842   transformation_context.GetFactManager()->MaybeAddFact(
843       MakeSynonymFact(21, 101));
844   transformation_context.GetFactManager()->MaybeAddFact(
845       MakeSynonymFact(17, 102));
846   transformation_context.GetFactManager()->MaybeAddFact(
847       MakeSynonymFact(57, 103));
848   transformation_context.GetFactManager()->MaybeAddFact(
849       MakeSynonymFact(18, 104));
850   transformation_context.GetFactManager()->MaybeAddFact(
851       MakeSynonymFact(40, 105));
852   transformation_context.GetFactManager()->MaybeAddFact(
853       MakeSynonymFact(32, 106));
854   transformation_context.GetFactManager()->MaybeAddFact(
855       MakeSynonymFact(43, 107));
856   transformation_context.GetFactManager()->MaybeAddFact(
857       MakeSynonymFact(55, 108));
858   transformation_context.GetFactManager()->MaybeAddFact(
859       MakeSynonymFact(8, 109));
860   transformation_context.GetFactManager()->MaybeAddFact(
861       MakeSynonymFact(47, 110));
862   transformation_context.GetFactManager()->MaybeAddFact(
863       MakeSynonymFact(28, 111));
864   transformation_context.GetFactManager()->MaybeAddFact(
865       MakeSynonymFact(45, 112));
866 
867   // Replacements of the form %16 -> %100
868 
869   // %20 = OpAccessChain %19 %15 *%16* %17
870   // Corresponds to d.*a*[2]
871   // The index %16 used for a cannot be replaced
872   auto replacement1 = TransformationReplaceIdWithSynonym(
873       MakeIdUseDescriptor(
874           16, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 1),
875       100);
876   ASSERT_FALSE(
877       replacement1.IsApplicable(context.get(), transformation_context));
878 
879   // %39 = OpAccessChain %23 %37 *%16*
880   // Corresponds to h.*f*
881   // The index %16 used for f cannot be replaced
882   auto replacement2 = TransformationReplaceIdWithSynonym(
883       MakeIdUseDescriptor(
884           16, MakeInstructionDescriptor(39, SpvOpAccessChain, 0), 1),
885       100);
886   ASSERT_FALSE(
887       replacement2.IsApplicable(context.get(), transformation_context));
888 
889   // %41 = OpAccessChain %19 %37 %21 *%16* %21
890   // Corresponds to h.g.*a*[1]
891   // The index %16 used for a cannot be replaced
892   auto replacement3 = TransformationReplaceIdWithSynonym(
893       MakeIdUseDescriptor(
894           16, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 2),
895       100);
896   ASSERT_FALSE(
897       replacement3.IsApplicable(context.get(), transformation_context));
898 
899   // %52 = OpAccessChain %23 %50 *%16* %16
900   // Corresponds to i[*0*].f
901   // The index %16 used for 0 *can* be replaced
902   auto replacement4 = TransformationReplaceIdWithSynonym(
903       MakeIdUseDescriptor(
904           16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 1),
905       100);
906   ASSERT_TRUE(replacement4.IsApplicable(context.get(), transformation_context));
907   ApplyAndCheckFreshIds(replacement4, context.get(), &transformation_context);
908   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
909                                                kConsoleMessageConsumer));
910 
911   // %52 = OpAccessChain %23 %50 %16 *%16*
912   // Corresponds to i[0].*f*
913   // The index %16 used for f cannot be replaced
914   auto replacement5 = TransformationReplaceIdWithSynonym(
915       MakeIdUseDescriptor(
916           16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 2),
917       100);
918   ASSERT_FALSE(
919       replacement5.IsApplicable(context.get(), transformation_context));
920 
921   // %53 = OpAccessChain %19 %50 %21 %21 *%16* %16
922   // Corresponds to i[1].g.*a*[0]
923   // The index %16 used for a cannot be replaced
924   auto replacement6 = TransformationReplaceIdWithSynonym(
925       MakeIdUseDescriptor(
926           16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 3),
927       100);
928   ASSERT_FALSE(
929       replacement6.IsApplicable(context.get(), transformation_context));
930 
931   // %53 = OpAccessChain %19 %50 %21 %21 %16 *%16*
932   // Corresponds to i[1].g.a[*0*]
933   // The index %16 used for 0 *can* be replaced
934   auto replacement7 = TransformationReplaceIdWithSynonym(
935       MakeIdUseDescriptor(
936           16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 4),
937       100);
938   ASSERT_TRUE(replacement7.IsApplicable(context.get(), transformation_context));
939   ApplyAndCheckFreshIds(replacement7, context.get(), &transformation_context);
940   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
941                                                kConsoleMessageConsumer));
942 
943   // Replacements of the form %21 -> %101
944 
945   // %24 = OpAccessChain %23 %15 *%21* %8
946   // Corresponds to d.*b*[3]
947   // The index %24 used for b cannot be replaced
948   auto replacement8 = TransformationReplaceIdWithSynonym(
949       MakeIdUseDescriptor(
950           21, MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 1),
951       101);
952   ASSERT_FALSE(
953       replacement8.IsApplicable(context.get(), transformation_context));
954 
955   // %41 = OpAccessChain %19 %37 *%21* %16 %21
956   // Corresponds to h.*g*.a[1]
957   // The index %24 used for g cannot be replaced
958   auto replacement9 = TransformationReplaceIdWithSynonym(
959       MakeIdUseDescriptor(
960           21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 1),
961       101);
962   ASSERT_FALSE(
963       replacement9.IsApplicable(context.get(), transformation_context));
964 
965   // %41 = OpAccessChain %19 %37 %21 %16 *%21*
966   // Corresponds to h.g.a[*1*]
967   // The index %24 used for 1 *can* be replaced
968   auto replacement10 = TransformationReplaceIdWithSynonym(
969       MakeIdUseDescriptor(
970           21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 3),
971       101);
972   ASSERT_TRUE(
973       replacement10.IsApplicable(context.get(), transformation_context));
974   ApplyAndCheckFreshIds(replacement10, context.get(), &transformation_context);
975   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
976                                                kConsoleMessageConsumer));
977 
978   // %44 = OpAccessChain %23 %37 *%21* %21 %43
979   // Corresponds to h.*g*.b[0]
980   // The index %24 used for g cannot be replaced
981   auto replacement11 = TransformationReplaceIdWithSynonym(
982       MakeIdUseDescriptor(
983           21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 1),
984       101);
985   ASSERT_FALSE(
986       replacement11.IsApplicable(context.get(), transformation_context));
987 
988   // %44 = OpAccessChain %23 %37 %21 *%21* %43
989   // Corresponds to h.g.*b*[0]
990   // The index %24 used for b cannot be replaced
991   auto replacement12 = TransformationReplaceIdWithSynonym(
992       MakeIdUseDescriptor(
993           21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 2),
994       101);
995   ASSERT_FALSE(
996       replacement12.IsApplicable(context.get(), transformation_context));
997 
998   // %46 = OpAccessChain %26 %37 *%21* %17
999   // Corresponds to h.*g*.c
1000   // The index %24 used for g cannot be replaced
1001   auto replacement13 = TransformationReplaceIdWithSynonym(
1002       MakeIdUseDescriptor(
1003           21, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 1),
1004       101);
1005   ASSERT_FALSE(
1006       replacement13.IsApplicable(context.get(), transformation_context));
1007 
1008   // %53 = OpAccessChain %19 %50 *%21* %21 %16 %16
1009   // Corresponds to i[*1*].g.a[0]
1010   // The index %24 used for 1 *can* be replaced
1011   auto replacement14 = TransformationReplaceIdWithSynonym(
1012       MakeIdUseDescriptor(
1013           21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 1),
1014       101);
1015   ASSERT_TRUE(
1016       replacement14.IsApplicable(context.get(), transformation_context));
1017   ApplyAndCheckFreshIds(replacement14, context.get(), &transformation_context);
1018   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1019                                                kConsoleMessageConsumer));
1020 
1021   // %53 = OpAccessChain %19 %50 %21 *%21* %16 %16
1022   // Corresponds to i[1].*g*.a[0]
1023   // The index %24 used for g cannot be replaced
1024   auto replacement15 = TransformationReplaceIdWithSynonym(
1025       MakeIdUseDescriptor(
1026           21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 2),
1027       101);
1028   ASSERT_FALSE(
1029       replacement15.IsApplicable(context.get(), transformation_context));
1030 
1031   // %56 = OpAccessChain %23 %50 %17 *%21* %21 %55
1032   // Corresponds to i[2].*g*.b[1]
1033   // The index %24 used for g cannot be replaced
1034   auto replacement16 = TransformationReplaceIdWithSynonym(
1035       MakeIdUseDescriptor(
1036           21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 2),
1037       101);
1038   ASSERT_FALSE(
1039       replacement16.IsApplicable(context.get(), transformation_context));
1040 
1041   // %56 = OpAccessChain %23 %50 %17 %21 *%21* %55
1042   // Corresponds to i[2].g.*b*[1]
1043   // The index %24 used for b cannot be replaced
1044   auto replacement17 = TransformationReplaceIdWithSynonym(
1045       MakeIdUseDescriptor(
1046           21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 3),
1047       101);
1048   ASSERT_FALSE(
1049       replacement17.IsApplicable(context.get(), transformation_context));
1050 
1051   // %58 = OpInBoundsAccessChain %26 %50 %57 *%21* %17
1052   // Corresponds to i[3].*g*.c
1053   // The index %24 used for g cannot be replaced
1054   auto replacement18 = TransformationReplaceIdWithSynonym(
1055       MakeIdUseDescriptor(
1056           21, MakeInstructionDescriptor(58, SpvOpInBoundsAccessChain, 0), 2),
1057       101);
1058   ASSERT_FALSE(
1059       replacement18.IsApplicable(context.get(), transformation_context));
1060 
1061   // Replacements of the form %17 -> %102
1062 
1063   // %20 = OpAccessChain %19 %15 %16 %17
1064   // Corresponds to d.a[*2*]
1065   // The index %17 used for 2 *can* be replaced
1066   auto replacement19 = TransformationReplaceIdWithSynonym(
1067       MakeIdUseDescriptor(
1068           17, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 2),
1069       102);
1070   ASSERT_TRUE(
1071       replacement19.IsApplicable(context.get(), transformation_context));
1072   ApplyAndCheckFreshIds(replacement19, context.get(), &transformation_context);
1073   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1074                                                kConsoleMessageConsumer));
1075 
1076   // %27 = OpAccessChain %26 %15 %17
1077   // Corresponds to d.c
1078   // The index %17 used for c cannot be replaced
1079   auto replacement20 = TransformationReplaceIdWithSynonym(
1080       MakeIdUseDescriptor(
1081           17, MakeInstructionDescriptor(27, SpvOpAccessChain, 0), 1),
1082       102);
1083   ASSERT_FALSE(
1084       replacement20.IsApplicable(context.get(), transformation_context));
1085 
1086   // %46 = OpAccessChain %26 %37 %21 %17
1087   // Corresponds to h.g.*c*
1088   // The index %17 used for c cannot be replaced
1089   auto replacement21 = TransformationReplaceIdWithSynonym(
1090       MakeIdUseDescriptor(
1091           17, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 2),
1092       102);
1093   ASSERT_FALSE(
1094       replacement21.IsApplicable(context.get(), transformation_context));
1095 
1096   // %56 = OpAccessChain %23 %50 %17 %21 %21 %55
1097   // Corresponds to i[*2*].g.b[1]
1098   // The index %17 used for 2 *can* be replaced
1099   auto replacement22 = TransformationReplaceIdWithSynonym(
1100       MakeIdUseDescriptor(
1101           17, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 1),
1102       102);
1103   ASSERT_TRUE(
1104       replacement22.IsApplicable(context.get(), transformation_context));
1105   ApplyAndCheckFreshIds(replacement22, context.get(), &transformation_context);
1106   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1107                                                kConsoleMessageConsumer));
1108 
1109   // %58 = OpInBoundsAccessChain %26 %50 %57 %21 %17
1110   // Corresponds to i[3].g.*c*
1111   // The index %17 used for c cannot be replaced
1112   auto replacement23 = TransformationReplaceIdWithSynonym(
1113       MakeIdUseDescriptor(
1114           17, MakeInstructionDescriptor(58, SpvOpInBoundsAccessChain, 0), 3),
1115       102);
1116   ASSERT_FALSE(
1117       replacement23.IsApplicable(context.get(), transformation_context));
1118 
1119   // Replacements of the form %57 -> %103
1120 
1121   // %58 = OpInBoundsAccessChain %26 %50 *%57* %21 %17
1122   // Corresponds to i[*3*].g.c
1123   // The index %57 used for 3 *can* be replaced
1124   auto replacement24 = TransformationReplaceIdWithSynonym(
1125       MakeIdUseDescriptor(
1126           57, MakeInstructionDescriptor(58, SpvOpInBoundsAccessChain, 0), 1),
1127       103);
1128   ASSERT_TRUE(
1129       replacement24.IsApplicable(context.get(), transformation_context));
1130   ApplyAndCheckFreshIds(replacement24, context.get(), &transformation_context);
1131   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1132                                                kConsoleMessageConsumer));
1133 
1134   // Replacements of the form %32 -> %106
1135 
1136   // %34 = OpAccessChain %23 %31 *%32*
1137   // Corresponds to e[*17*]
1138   // The index %32 used for 17 *can* be replaced
1139   auto replacement25 = TransformationReplaceIdWithSynonym(
1140       MakeIdUseDescriptor(
1141           32, MakeInstructionDescriptor(34, SpvOpAccessChain, 0), 1),
1142       106);
1143   ASSERT_TRUE(
1144       replacement25.IsApplicable(context.get(), transformation_context));
1145   ApplyAndCheckFreshIds(replacement25, context.get(), &transformation_context);
1146   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1147                                                kConsoleMessageConsumer));
1148 
1149   // Replacements of the form %43 -> %107
1150 
1151   // %44 = OpAccessChain %23 %37 %21 %21 *%43*
1152   // Corresponds to h.g.b[*0*]
1153   // The index %43 used for 0 *can* be replaced
1154   auto replacement26 = TransformationReplaceIdWithSynonym(
1155       MakeIdUseDescriptor(
1156           43, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 3),
1157       107);
1158   ASSERT_TRUE(
1159       replacement26.IsApplicable(context.get(), transformation_context));
1160   ApplyAndCheckFreshIds(replacement26, context.get(), &transformation_context);
1161   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1162                                                kConsoleMessageConsumer));
1163 
1164   // Replacements of the form %55 -> %108
1165 
1166   // %56 = OpAccessChain %23 %50 %17 %21 %21 *%55*
1167   // Corresponds to i[2].g.b[*1*]
1168   // The index %55 used for 1 *can* be replaced
1169   auto replacement27 = TransformationReplaceIdWithSynonym(
1170       MakeIdUseDescriptor(
1171           55, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 4),
1172       108);
1173   ASSERT_TRUE(
1174       replacement27.IsApplicable(context.get(), transformation_context));
1175   ApplyAndCheckFreshIds(replacement27, context.get(), &transformation_context);
1176   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1177                                                kConsoleMessageConsumer));
1178 
1179   // Replacements of the form %8 -> %109
1180 
1181   // %24 = OpAccessChain %23 %15 %21 *%8*
1182   // Corresponds to d.b[*3*]
1183   // The index %8 used for 3 *can* be replaced
1184   auto replacement28 = TransformationReplaceIdWithSynonym(
1185       MakeIdUseDescriptor(8, MakeInstructionDescriptor(24, SpvOpAccessChain, 0),
1186                           2),
1187       109);
1188   ASSERT_TRUE(
1189       replacement28.IsApplicable(context.get(), transformation_context));
1190   ApplyAndCheckFreshIds(replacement28, context.get(), &transformation_context);
1191   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1192                                                kConsoleMessageConsumer));
1193 
1194   const std::string after_transformation = R"(
1195                OpCapability Shader
1196           %1 = OpExtInstImport "GLSL.std.450"
1197                OpMemoryModel Logical GLSL450
1198                OpEntryPoint Fragment %4 "main"
1199                OpExecutionMode %4 OriginUpperLeft
1200                OpSource ESSL 310
1201                OpName %4 "main"
1202                OpName %13 "S"
1203                OpMemberName %13 0 "a"
1204                OpMemberName %13 1 "b"
1205                OpMemberName %13 2 "c"
1206                OpName %15 "d"
1207                OpName %31 "e"
1208                OpName %35 "T"
1209                OpMemberName %35 0 "f"
1210                OpMemberName %35 1 "g"
1211                OpName %37 "h"
1212                OpName %50 "i"
1213           %2 = OpTypeVoid
1214           %3 = OpTypeFunction %2
1215           %6 = OpTypeInt 32 1
1216           %7 = OpTypeInt 32 0
1217           %8 = OpConstant %7 3
1218           %9 = OpTypeArray %6 %8
1219          %10 = OpTypeFloat 32
1220          %11 = OpTypeVector %10 4
1221          %12 = OpTypeBool
1222          %13 = OpTypeStruct %9 %11 %12
1223          %14 = OpTypePointer Private %13
1224          %15 = OpVariable %14 Private
1225          %16 = OpConstant %6 0
1226          %17 = OpConstant %6 2
1227          %18 = OpConstant %6 10
1228          %19 = OpTypePointer Private %6
1229          %21 = OpConstant %6 1
1230          %22 = OpConstant %10 11
1231          %23 = OpTypePointer Private %10
1232          %25 = OpConstantFalse %12
1233          %26 = OpTypePointer Private %12
1234          %28 = OpConstant %7 20
1235          %29 = OpTypeArray %10 %28
1236          %30 = OpTypePointer Private %29
1237          %31 = OpVariable %30 Private
1238          %32 = OpConstant %6 17
1239          %33 = OpConstant %10 12
1240          %35 = OpTypeStruct %10 %13
1241          %36 = OpTypePointer Private %35
1242          %37 = OpVariable %36 Private
1243          %38 = OpConstant %10 13
1244          %40 = OpConstant %6 14
1245          %42 = OpConstant %10 15
1246          %43 = OpConstant %7 0
1247          %45 = OpConstantTrue %12
1248          %47 = OpConstant %7 4
1249          %48 = OpTypeArray %35 %47
1250          %49 = OpTypePointer Private %48
1251          %50 = OpVariable %49 Private
1252          %51 = OpConstant %10 16
1253          %54 = OpConstant %10 18
1254          %55 = OpConstant %7 1
1255          %57 = OpConstant %6 3
1256           %4 = OpFunction %2 None %3
1257           %5 = OpLabel
1258 
1259          %100 = OpCopyObject %6 %16 ; 0
1260          %101 = OpCopyObject %6 %21 ; 1
1261          %102 = OpCopyObject %6 %17 ; 2
1262          %103 = OpCopyObject %6 %57 ; 3
1263          %104 = OpCopyObject %6 %18 ; 10
1264          %105 = OpCopyObject %6 %40 ; 14
1265          %106 = OpCopyObject %6 %32 ; 17
1266          %107 = OpCopyObject %7 %43 ; 0
1267          %108 = OpCopyObject %7 %55 ; 1
1268          %109 = OpCopyObject %7  %8 ; 3
1269          %110 = OpCopyObject %7 %47 ; 4
1270          %111 = OpCopyObject %7 %28 ; 20
1271          %112 = OpCopyObject %12 %45 ; true
1272 
1273          %20 = OpAccessChain %19 %15 %16 %102
1274                OpStore %20 %18
1275          %24 = OpAccessChain %23 %15 %21 %109
1276                OpStore %24 %22
1277          %27 = OpAccessChain %26 %15 %17
1278                OpStore %27 %25
1279          %34 = OpAccessChain %23 %31 %106
1280                OpStore %34 %33
1281          %39 = OpAccessChain %23 %37 %16
1282                OpStore %39 %38
1283          %41 = OpAccessChain %19 %37 %21 %16 %101
1284                OpStore %41 %40
1285          %44 = OpAccessChain %23 %37 %21 %21 %107
1286                OpStore %44 %42
1287          %46 = OpAccessChain %26 %37 %21 %17
1288                OpStore %46 %45
1289          %52 = OpAccessChain %23 %50 %100 %16
1290                OpStore %52 %51
1291          %53 = OpAccessChain %19 %50 %101 %21 %16 %100
1292                OpStore %53 %32
1293          %56 = OpAccessChain %23 %50 %102 %21 %21 %108
1294                OpStore %56 %54
1295          %58 = OpInBoundsAccessChain %26 %50 %103 %21 %17
1296                OpStore %58 %45
1297                OpReturn
1298                OpFunctionEnd
1299   )";
1300 
1301   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1302 }
1303 
TEST(TransformationReplaceIdWithSynonymTest,RuntimeArrayTest)1304 TEST(TransformationReplaceIdWithSynonymTest, RuntimeArrayTest) {
1305   // This checks that OpRuntimeArray is correctly handled.
1306   const std::string shader = R"(
1307                OpCapability Shader
1308           %1 = OpExtInstImport "GLSL.std.450"
1309                OpMemoryModel Logical GLSL450
1310                OpEntryPoint Fragment %4 "main"
1311                OpExecutionMode %4 OriginUpperLeft
1312                OpSource ESSL 310
1313                OpDecorate %8 ArrayStride 8
1314                OpMemberDecorate %9 0 Offset 0
1315                OpDecorate %9 BufferBlock
1316                OpDecorate %11 DescriptorSet 0
1317                OpDecorate %11 Binding 0
1318           %2 = OpTypeVoid
1319           %3 = OpTypeFunction %2
1320           %6 = OpTypeInt 32 1
1321           %7 = OpTypeVector %6 2
1322           %8 = OpTypeRuntimeArray %7
1323           %9 = OpTypeStruct %8
1324          %10 = OpTypePointer Uniform %9
1325          %11 = OpVariable %10 Uniform
1326          %12 = OpConstant %6 0
1327          %13 = OpTypeInt 32 0
1328          %14 = OpConstant %13 0
1329          %15 = OpTypePointer Uniform %6
1330           %4 = OpFunction %2 None %3
1331           %5 = OpLabel
1332          %50 = OpCopyObject %6 %12
1333          %51 = OpCopyObject %13 %14
1334          %16 = OpAccessChain %15 %11 %12 %12 %14
1335                OpStore %16 %12
1336                OpReturn
1337                OpFunctionEnd
1338   )";
1339 
1340   const auto env = SPV_ENV_UNIVERSAL_1_3;
1341   const auto consumer = nullptr;
1342   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1343   spvtools::ValidatorOptions validator_options;
1344   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1345                                                kConsoleMessageConsumer));
1346   TransformationContext transformation_context(
1347       MakeUnique<FactManager>(context.get()), validator_options);
1348   // Add synonym fact relating %50 and %12.
1349   transformation_context.GetFactManager()->MaybeAddFact(
1350       MakeSynonymFact(50, 12));
1351   // Add synonym fact relating %51 and %14.
1352   transformation_context.GetFactManager()->MaybeAddFact(
1353       MakeSynonymFact(51, 14));
1354 
1355   // Not legal because the index being replaced is a struct index.
1356   ASSERT_FALSE(
1357       TransformationReplaceIdWithSynonym(
1358           MakeIdUseDescriptor(
1359               12, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 1),
1360           50)
1361           .IsApplicable(context.get(), transformation_context));
1362 
1363   // Fine to replace an index into a runtime array.
1364   auto replacement1 = TransformationReplaceIdWithSynonym(
1365       MakeIdUseDescriptor(
1366           12, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 2),
1367       50);
1368   ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context));
1369   ApplyAndCheckFreshIds(replacement1, context.get(), &transformation_context);
1370 
1371   // Fine to replace an index into a vector inside the runtime array.
1372   auto replacement2 = TransformationReplaceIdWithSynonym(
1373       MakeIdUseDescriptor(
1374           14, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 3),
1375       51);
1376   ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context));
1377   ApplyAndCheckFreshIds(replacement2, context.get(), &transformation_context);
1378 
1379   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1380                                                kConsoleMessageConsumer));
1381 
1382   const std::string after_transformation = R"(
1383                OpCapability Shader
1384           %1 = OpExtInstImport "GLSL.std.450"
1385                OpMemoryModel Logical GLSL450
1386                OpEntryPoint Fragment %4 "main"
1387                OpExecutionMode %4 OriginUpperLeft
1388                OpSource ESSL 310
1389                OpDecorate %8 ArrayStride 8
1390                OpMemberDecorate %9 0 Offset 0
1391                OpDecorate %9 BufferBlock
1392                OpDecorate %11 DescriptorSet 0
1393                OpDecorate %11 Binding 0
1394           %2 = OpTypeVoid
1395           %3 = OpTypeFunction %2
1396           %6 = OpTypeInt 32 1
1397           %7 = OpTypeVector %6 2
1398           %8 = OpTypeRuntimeArray %7
1399           %9 = OpTypeStruct %8
1400          %10 = OpTypePointer Uniform %9
1401          %11 = OpVariable %10 Uniform
1402          %12 = OpConstant %6 0
1403          %13 = OpTypeInt 32 0
1404          %14 = OpConstant %13 0
1405          %15 = OpTypePointer Uniform %6
1406           %4 = OpFunction %2 None %3
1407           %5 = OpLabel
1408          %50 = OpCopyObject %6 %12
1409          %51 = OpCopyObject %13 %14
1410          %16 = OpAccessChain %15 %11 %12 %50 %51
1411                OpStore %16 %12
1412                OpReturn
1413                OpFunctionEnd
1414   )";
1415 
1416   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1417 }
1418 
TEST(TransformationReplaceIdWithSynonymTest,DoNotReplaceSampleParameterOfOpImageTexelPointer)1419 TEST(TransformationReplaceIdWithSynonymTest,
1420      DoNotReplaceSampleParameterOfOpImageTexelPointer) {
1421   const std::string shader = R"(
1422                OpCapability Shader
1423           %1 = OpExtInstImport "GLSL.std.450"
1424                OpMemoryModel Logical GLSL450
1425                OpEntryPoint Fragment %2 "main" %3
1426                OpExecutionMode %2 OriginUpperLeft
1427                OpSource ESSL 310
1428           %4 = OpTypeVoid
1429           %5 = OpTypeFunction %4
1430           %6 = OpTypeInt 32 1
1431           %7 = OpTypePointer Function %6
1432           %8 = OpConstant %6 2
1433           %9 = OpConstant %6 0
1434          %10 = OpConstant %6 10
1435          %11 = OpTypeBool
1436          %12 = OpConstant %6 1
1437          %13 = OpTypeFloat 32
1438          %14 = OpTypePointer Image %13
1439          %15 = OpTypeImage %13 2D 0 0 0 0 Rgba8
1440          %16 = OpTypePointer Private %15
1441           %3 = OpVariable %16 Private
1442          %17 = OpTypeVector %6 2
1443          %18 = OpConstantComposite %17 %9 %9
1444           %2 = OpFunction %4 None %5
1445          %19 = OpLabel
1446         %100 = OpCopyObject %6 %9
1447          %20 = OpImageTexelPointer %14 %3 %18 %9
1448                OpReturn
1449                OpFunctionEnd
1450   )";
1451 
1452   const auto env = SPV_ENV_UNIVERSAL_1_5;
1453   const auto consumer = nullptr;
1454   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1455   spvtools::ValidatorOptions validator_options;
1456   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1457                                                kConsoleMessageConsumer));
1458   TransformationContext transformation_context(
1459       MakeUnique<FactManager>(context.get()), validator_options);
1460   // Add synonym fact relating %100 and %9.
1461   transformation_context.GetFactManager()->MaybeAddFact(
1462       MakeSynonymFact(100, 9));
1463 
1464   // Not legal the Sample argument of OpImageTexelPointer needs to be a zero
1465   // constant.
1466   ASSERT_FALSE(
1467       TransformationReplaceIdWithSynonym(
1468           MakeIdUseDescriptor(
1469               9, MakeInstructionDescriptor(20, SpvOpImageTexelPointer, 0), 2),
1470           100)
1471           .IsApplicable(context.get(), transformation_context));
1472 }
1473 
TEST(TransformationReplaceIdWithSynonymTest,EquivalentIntegerConstants)1474 TEST(TransformationReplaceIdWithSynonymTest, EquivalentIntegerConstants) {
1475   // This checks that replacing an integer constant with an equivalent one with
1476   // different signedness is allowed only when valid.
1477   const std::string shader = R"(
1478                OpCapability Shader
1479           %1 = OpExtInstImport "GLSL.std.450"
1480                OpMemoryModel Logical GLSL450
1481                OpEntryPoint Fragment %2 "main"
1482                OpExecutionMode %2 OriginUpperLeft
1483                OpSource ESSL 310
1484                OpName %2 "main"
1485                OpName %3 "a"
1486                OpDecorate %3 RelaxedPrecision
1487           %4 = OpTypeVoid
1488           %5 = OpTypeBool
1489           %6 = OpConstantTrue %5
1490           %7 = OpTypeFunction %4
1491           %8 = OpTypeInt 32 1
1492           %9 = OpTypePointer Function %8
1493          %10 = OpConstant %8 1
1494          %11 = OpTypeInt 32 0
1495          %12 = OpTypePointer Function %11
1496          %13 = OpConstant %11 1
1497           %2 = OpFunction %4 None %7
1498          %14 = OpLabel
1499           %3 = OpVariable %9 Function
1500          %15 = OpSNegate %8 %10
1501          %16 = OpIAdd %8 %10 %10
1502          %17 = OpSDiv %8 %10 %10
1503          %18 = OpUDiv %11 %13 %13
1504          %19 = OpBitwiseAnd %8 %10 %10
1505          %20 = OpSelect %8 %6 %10 %17
1506          %21 = OpIEqual %5 %10 %10
1507                OpStore %3 %10
1508                OpReturn
1509                OpFunctionEnd
1510   )";
1511 
1512   const auto env = SPV_ENV_UNIVERSAL_1_3;
1513   const auto consumer = nullptr;
1514   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1515   spvtools::ValidatorOptions validator_options;
1516   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1517                                                kConsoleMessageConsumer));
1518   TransformationContext transformation_context(
1519       MakeUnique<FactManager>(context.get()), validator_options);
1520   // Add synonym fact relating %10 and %13 (equivalent integer constant with
1521   // different signedness).
1522   transformation_context.GetFactManager()->MaybeAddFact(
1523       MakeSynonymFact(10, 13));
1524 
1525   // Legal because OpSNegate always considers the integer as signed
1526   auto replacement1 = TransformationReplaceIdWithSynonym(
1527       MakeIdUseDescriptor(10, MakeInstructionDescriptor(15, SpvOpSNegate, 0),
1528                           0),
1529       13);
1530   ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context));
1531   ApplyAndCheckFreshIds(replacement1, context.get(), &transformation_context);
1532 
1533   // Legal because OpIAdd does not care about the signedness of the operands
1534   auto replacement2 = TransformationReplaceIdWithSynonym(
1535       MakeIdUseDescriptor(10, MakeInstructionDescriptor(16, SpvOpIAdd, 0), 0),
1536       13);
1537   ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context));
1538   ApplyAndCheckFreshIds(replacement2, context.get(), &transformation_context);
1539 
1540   // Legal because OpSDiv does not care about the signedness of the operands
1541   auto replacement3 = TransformationReplaceIdWithSynonym(
1542       MakeIdUseDescriptor(10, MakeInstructionDescriptor(17, SpvOpSDiv, 0), 0),
1543       13);
1544   ASSERT_TRUE(replacement3.IsApplicable(context.get(), transformation_context));
1545   ApplyAndCheckFreshIds(replacement3, context.get(), &transformation_context);
1546 
1547   // Not legal because OpUDiv requires unsigned integers
1548   ASSERT_FALSE(TransformationReplaceIdWithSynonym(
1549                    MakeIdUseDescriptor(
1550                        13, MakeInstructionDescriptor(18, SpvOpUDiv, 0), 0),
1551                    10)
1552                    .IsApplicable(context.get(), transformation_context));
1553 
1554   // Legal because OpSDiv does not care about the signedness of the operands
1555   auto replacement4 = TransformationReplaceIdWithSynonym(
1556       MakeIdUseDescriptor(10, MakeInstructionDescriptor(19, SpvOpBitwiseAnd, 0),
1557                           0),
1558       13);
1559   ASSERT_TRUE(replacement4.IsApplicable(context.get(), transformation_context));
1560   ApplyAndCheckFreshIds(replacement4, context.get(), &transformation_context);
1561 
1562   // Not legal because OpSelect requires both operands to have the same type as
1563   // the result type
1564   ASSERT_FALSE(TransformationReplaceIdWithSynonym(
1565                    MakeIdUseDescriptor(
1566                        10, MakeInstructionDescriptor(20, SpvOpUDiv, 0), 1),
1567                    13)
1568                    .IsApplicable(context.get(), transformation_context));
1569 
1570   // Not legal because OpStore requires the object to match the type pointed
1571   // to by the pointer.
1572   ASSERT_FALSE(TransformationReplaceIdWithSynonym(
1573                    MakeIdUseDescriptor(
1574                        10, MakeInstructionDescriptor(21, SpvOpStore, 0), 1),
1575                    13)
1576                    .IsApplicable(context.get(), transformation_context));
1577 
1578   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1579                                                kConsoleMessageConsumer));
1580 
1581   const std::string after_transformation = R"(
1582                OpCapability Shader
1583           %1 = OpExtInstImport "GLSL.std.450"
1584                OpMemoryModel Logical GLSL450
1585                OpEntryPoint Fragment %2 "main"
1586                OpExecutionMode %2 OriginUpperLeft
1587                OpSource ESSL 310
1588                OpName %2 "main"
1589                OpName %3 "a"
1590                OpDecorate %3 RelaxedPrecision
1591           %4 = OpTypeVoid
1592           %5 = OpTypeBool
1593           %6 = OpConstantTrue %5
1594           %7 = OpTypeFunction %4
1595           %8 = OpTypeInt 32 1
1596           %9 = OpTypePointer Function %8
1597          %10 = OpConstant %8 1
1598          %11 = OpTypeInt 32 0
1599          %12 = OpTypePointer Function %11
1600          %13 = OpConstant %11 1
1601           %2 = OpFunction %4 None %7
1602          %14 = OpLabel
1603           %3 = OpVariable %9 Function
1604          %15 = OpSNegate %8 %13
1605          %16 = OpIAdd %8 %13 %10
1606          %17 = OpSDiv %8 %13 %10
1607          %18 = OpUDiv %11 %13 %13
1608          %19 = OpBitwiseAnd %8 %13 %10
1609          %20 = OpSelect %8 %6 %10 %17
1610          %21 = OpIEqual %5 %10 %10
1611                OpStore %3 %10
1612                OpReturn
1613                OpFunctionEnd
1614   )";
1615 
1616   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1617 }
1618 
TEST(TransformationReplaceIdWithSynonymTest,EquivalentIntegerVectorConstants)1619 TEST(TransformationReplaceIdWithSynonymTest, EquivalentIntegerVectorConstants) {
1620   // This checks that replacing an integer constant with an equivalent one with
1621   // different signedness is allowed only when valid.
1622   const std::string shader = R"(
1623                OpCapability Shader
1624           %1 = OpExtInstImport "GLSL.std.450"
1625                OpMemoryModel Logical GLSL450
1626                OpEntryPoint Fragment %2 "main"
1627                OpExecutionMode %2 OriginUpperLeft
1628                OpSource ESSL 310
1629                OpName %2 "main"
1630                OpName %3 "a"
1631                OpDecorate %3 RelaxedPrecision
1632                OpDecorate %4 RelaxedPrecision
1633           %5 = OpTypeVoid
1634           %6 = OpTypeFunction %5
1635           %7 = OpTypeInt 32 1
1636           %8 = OpTypeInt 32 0
1637           %9 = OpTypeVector %7 4
1638          %10 = OpTypeVector %8 4
1639          %11 = OpTypePointer Function %9
1640          %12 = OpConstant %7 1
1641          %13 = OpConstant %8 1
1642          %14 = OpConstantComposite %9 %12 %12 %12 %12
1643          %15 = OpConstantComposite %10 %13 %13 %13 %13
1644          %16 = OpTypePointer Function %7
1645           %2 = OpFunction %5 None %6
1646          %17 = OpLabel
1647           %3 = OpVariable %11 Function
1648          %18 = OpIAdd %9 %14 %14
1649                OpStore %3 %14
1650          %19 = OpAccessChain %16 %3 %13
1651           %4 = OpLoad %7 %19
1652                OpReturn
1653                OpFunctionEnd
1654   )";
1655 
1656   const auto env = SPV_ENV_UNIVERSAL_1_3;
1657   const auto consumer = nullptr;
1658   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1659   spvtools::ValidatorOptions validator_options;
1660   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1661                                                kConsoleMessageConsumer));
1662   TransformationContext transformation_context(
1663       MakeUnique<FactManager>(context.get()), validator_options);
1664   // Add synonym fact relating %10 and %13 (equivalent integer vectors with
1665   // different signedness).
1666   transformation_context.GetFactManager()->MaybeAddFact(
1667       MakeSynonymFact(14, 15));
1668 
1669   // Legal because OpIAdd does not consider the signedness of the operands
1670   auto replacement1 = TransformationReplaceIdWithSynonym(
1671       MakeIdUseDescriptor(14, MakeInstructionDescriptor(18, SpvOpIAdd, 0), 0),
1672       15);
1673   ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context));
1674   ApplyAndCheckFreshIds(replacement1, context.get(), &transformation_context);
1675 
1676   // Not legal because OpStore requires the object to match the type pointed
1677   // to by the pointer.
1678   ASSERT_FALSE(TransformationReplaceIdWithSynonym(
1679                    MakeIdUseDescriptor(
1680                        14, MakeInstructionDescriptor(18, SpvOpStore, 0), 1),
1681                    15)
1682                    .IsApplicable(context.get(), transformation_context));
1683 
1684   // Add synonym fact relating %12 and %13 (equivalent integer constants with
1685   // different signedness).
1686   transformation_context.GetFactManager()->MaybeAddFact(
1687       MakeSynonymFact(12, 13));
1688 
1689   // Legal because the indices of OpAccessChain are always treated as signed
1690   auto replacement2 = TransformationReplaceIdWithSynonym(
1691       MakeIdUseDescriptor(
1692           13, MakeInstructionDescriptor(19, SpvOpAccessChain, 0), 1),
1693       12);
1694   ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context));
1695   ApplyAndCheckFreshIds(replacement2, context.get(), &transformation_context);
1696 
1697   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1698                                                kConsoleMessageConsumer));
1699 
1700   const std::string after_transformation = R"(
1701                OpCapability Shader
1702           %1 = OpExtInstImport "GLSL.std.450"
1703                OpMemoryModel Logical GLSL450
1704                OpEntryPoint Fragment %2 "main"
1705                OpExecutionMode %2 OriginUpperLeft
1706                OpSource ESSL 310
1707                OpName %2 "main"
1708                OpName %3 "a"
1709                OpDecorate %3 RelaxedPrecision
1710                OpDecorate %4 RelaxedPrecision
1711           %5 = OpTypeVoid
1712           %6 = OpTypeFunction %5
1713           %7 = OpTypeInt 32 1
1714           %8 = OpTypeInt 32 0
1715           %9 = OpTypeVector %7 4
1716          %10 = OpTypeVector %8 4
1717          %11 = OpTypePointer Function %9
1718          %12 = OpConstant %7 1
1719          %13 = OpConstant %8 1
1720          %14 = OpConstantComposite %9 %12 %12 %12 %12
1721          %15 = OpConstantComposite %10 %13 %13 %13 %13
1722          %16 = OpTypePointer Function %7
1723           %2 = OpFunction %5 None %6
1724          %17 = OpLabel
1725           %3 = OpVariable %11 Function
1726          %18 = OpIAdd %9 %15 %14
1727                OpStore %3 %14
1728          %19 = OpAccessChain %16 %3 %12
1729           %4 = OpLoad %7 %19
1730                OpReturn
1731                OpFunctionEnd
1732   )";
1733 
1734   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1735 }
1736 
TEST(TransformationReplaceIdWithSynonymTest,IncompatibleTypes)1737 TEST(TransformationReplaceIdWithSynonymTest, IncompatibleTypes) {
1738   const std::string shader = R"(
1739                OpCapability Shader
1740           %1 = OpExtInstImport "GLSL.std.450"
1741                OpMemoryModel Logical GLSL450
1742                OpEntryPoint Fragment %2 "main"
1743                OpExecutionMode %2 OriginUpperLeft
1744                OpSource ESSL 310
1745           %5 = OpTypeVoid
1746           %6 = OpTypeFunction %5
1747           %7 = OpTypeInt 32 1
1748           %8 = OpTypeInt 32 0
1749           %9 = OpTypeFloat 32
1750          %12 = OpConstant %7 1
1751          %13 = OpConstant %8 1
1752          %10 = OpConstant %9 1
1753           %2 = OpFunction %5 None %6
1754          %17 = OpLabel
1755          %18 = OpIAdd %7 %12 %13
1756          %19 = OpFAdd %9 %10 %10
1757                OpReturn
1758                OpFunctionEnd
1759   )";
1760 
1761   const auto env = SPV_ENV_UNIVERSAL_1_3;
1762   const auto consumer = nullptr;
1763   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1764   spvtools::ValidatorOptions validator_options;
1765   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1766                                                kConsoleMessageConsumer));
1767   TransformationContext transformation_context(
1768       MakeUnique<FactManager>(context.get()), validator_options);
1769   auto* op_i_add = context->get_def_use_mgr()->GetDef(18);
1770   ASSERT_TRUE(op_i_add);
1771 
1772   auto* op_f_add = context->get_def_use_mgr()->GetDef(19);
1773   ASSERT_TRUE(op_f_add);
1774 
1775   transformation_context.GetFactManager()->AddFactDataSynonym(
1776       MakeDataDescriptor(12, {}), MakeDataDescriptor(13, {}));
1777   transformation_context.GetFactManager()->AddFactDataSynonym(
1778       MakeDataDescriptor(12, {}), MakeDataDescriptor(10, {}));
1779 
1780   // Synonym differs only in signedness for OpIAdd.
1781   ASSERT_TRUE(TransformationReplaceIdWithSynonym(
1782                   MakeIdUseDescriptorFromUse(context.get(), op_i_add, 0), 13)
1783                   .IsApplicable(context.get(), transformation_context));
1784 
1785   // Synonym has wrong type for OpIAdd.
1786   ASSERT_FALSE(TransformationReplaceIdWithSynonym(
1787                    MakeIdUseDescriptorFromUse(context.get(), op_i_add, 0), 10)
1788                    .IsApplicable(context.get(), transformation_context));
1789 
1790   // Synonym has wrong type for OpFAdd.
1791   ASSERT_FALSE(TransformationReplaceIdWithSynonym(
1792                    MakeIdUseDescriptorFromUse(context.get(), op_f_add, 0), 12)
1793                    .IsApplicable(context.get(), transformation_context));
1794   ASSERT_FALSE(TransformationReplaceIdWithSynonym(
1795                    MakeIdUseDescriptorFromUse(context.get(), op_f_add, 0), 13)
1796                    .IsApplicable(context.get(), transformation_context));
1797 }
1798 
TEST(TransformationReplaceIdWithSynonymTest,AtomicScopeAndMemorySemanticsMustBeConstant)1799 TEST(TransformationReplaceIdWithSynonymTest,
1800      AtomicScopeAndMemorySemanticsMustBeConstant) {
1801   const std::string shader = R"(
1802                OpCapability Shader
1803           %1 = OpExtInstImport "GLSL.std.450"
1804                OpMemoryModel Logical GLSL450
1805                OpEntryPoint GLCompute %4 "main"
1806                OpExecutionMode %4 LocalSize 1 1 1
1807                OpSource ESSL 320
1808                OpSourceExtension "GL_KHR_memory_scope_semantics"
1809                OpMemberDecorate %9 0 Offset 0
1810                OpDecorate %9 Block
1811                OpDecorate %11 DescriptorSet 0
1812                OpDecorate %11 Binding 0
1813           %2 = OpTypeVoid
1814           %3 = OpTypeFunction %2
1815           %6 = OpTypeInt 32 1
1816          %17 = OpTypeInt 32 0
1817           %7 = OpTypePointer Function %6
1818           %9 = OpTypeStruct %6
1819          %10 = OpTypePointer StorageBuffer %9
1820          %11 = OpVariable %10 StorageBuffer
1821          %86 = OpTypeStruct %17
1822          %87 = OpTypePointer Workgroup %86
1823          %88 = OpVariable %87 Workgroup
1824          %12 = OpConstant %6 0
1825          %13 = OpTypePointer StorageBuffer %6
1826          %15 = OpConstant %6 2
1827          %16 = OpConstant %6 64
1828          %89 = OpTypePointer Workgroup %17
1829          %18 = OpConstant %17 1
1830          %19 = OpConstant %17 0
1831          %20 = OpConstant %17 64
1832           %4 = OpFunction %2 None %3
1833           %5 = OpLabel
1834           %8 = OpVariable %7 Function
1835         %100 = OpCopyObject %6 %15 ; A non-constant version of %15
1836         %101 = OpCopyObject %17 %20 ; A non-constant version of %20
1837          %14 = OpAccessChain %13 %11 %12
1838          %90 = OpAccessChain %89 %88 %19
1839          %21 = OpAtomicLoad %6 %14 %15 %20
1840          %22 = OpAtomicExchange %6 %14 %15 %20 %16
1841          %23 = OpAtomicCompareExchange %6 %14 %15 %20 %12 %16 %15
1842          %24 = OpAtomicIIncrement %6 %14 %15 %20
1843          %25 = OpAtomicIDecrement %6 %14 %15 %20
1844          %26 = OpAtomicIAdd %6  %14 %15 %20 %16
1845          %27 = OpAtomicISub %6  %14 %15 %20 %16
1846          %28 = OpAtomicSMin %6  %14 %15 %20 %16
1847          %29 = OpAtomicUMin %17 %90 %15 %20 %18
1848          %30 = OpAtomicSMax %6  %14 %15 %20 %15
1849          %31 = OpAtomicUMax %17 %90 %15 %20 %18
1850          %32 = OpAtomicAnd  %6  %14 %15 %20 %16
1851          %33 = OpAtomicOr   %6  %14 %15 %20 %16
1852          %34 = OpAtomicXor  %6  %14 %15 %20 %16
1853                OpStore %8 %21
1854                OpAtomicStore %14 %15 %20 %12
1855                OpReturn
1856                OpFunctionEnd
1857   )";
1858 
1859   const auto env = SPV_ENV_UNIVERSAL_1_3;
1860   const auto consumer = nullptr;
1861   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1862   spvtools::ValidatorOptions validator_options;
1863   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1864                                                kConsoleMessageConsumer));
1865   TransformationContext transformation_context(
1866       MakeUnique<FactManager>(context.get()), validator_options);
1867 
1868   // Tell the fact manager that %100 and %15 are synonymous
1869   transformation_context.GetFactManager()->AddFactDataSynonym(
1870       MakeDataDescriptor(100, {}), MakeDataDescriptor(15, {}));
1871 
1872   // Tell the fact manager that %101 and %20 are synonymous
1873   transformation_context.GetFactManager()->AddFactDataSynonym(
1874       MakeDataDescriptor(101, {}), MakeDataDescriptor(20, {}));
1875   // OpAtomicLoad
1876   const auto& scope_operand = MakeIdUseDescriptorFromUse(
1877       context.get(), context->get_def_use_mgr()->GetDef(21), 1);
1878   ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand, 100)
1879                    .IsApplicable(context.get(), transformation_context));
1880 
1881   const auto& semantics_operand = MakeIdUseDescriptorFromUse(
1882       context.get(), context->get_def_use_mgr()->GetDef(21), 2);
1883   ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand, 101)
1884                    .IsApplicable(context.get(), transformation_context));
1885   // OpAtomicExchange.
1886   const auto& scope_operand2 = MakeIdUseDescriptorFromUse(
1887       context.get(), context->get_def_use_mgr()->GetDef(22), 1);
1888   ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand2, 100)
1889                    .IsApplicable(context.get(), transformation_context));
1890 
1891   const auto& semantics_operand2 = MakeIdUseDescriptorFromUse(
1892       context.get(), context->get_def_use_mgr()->GetDef(22), 2);
1893   ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand2, 101)
1894                    .IsApplicable(context.get(), transformation_context));
1895   // OpAtomicCompareExchange.
1896   const auto& scope_operand3 = MakeIdUseDescriptorFromUse(
1897       context.get(), context->get_def_use_mgr()->GetDef(23), 1);
1898   ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand3, 100)
1899                    .IsApplicable(context.get(), transformation_context));
1900 
1901   const auto& semantics_equal_operand3 = MakeIdUseDescriptorFromUse(
1902       context.get(), context->get_def_use_mgr()->GetDef(23), 2);
1903   ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_equal_operand3, 101)
1904                    .IsApplicable(context.get(), transformation_context));
1905   const auto& semantics_unequal_operand3 = MakeIdUseDescriptorFromUse(
1906       context.get(), context->get_def_use_mgr()->GetDef(23), 3);
1907   ASSERT_FALSE(
1908       TransformationReplaceIdWithSynonym(semantics_unequal_operand3, 101)
1909           .IsApplicable(context.get(), transformation_context));
1910   // OpAtomicIIncrement.
1911   const auto& scope_operand4 = MakeIdUseDescriptorFromUse(
1912       context.get(), context->get_def_use_mgr()->GetDef(24), 1);
1913   ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand4, 100)
1914                    .IsApplicable(context.get(), transformation_context));
1915 
1916   const auto& semantics_operand4 = MakeIdUseDescriptorFromUse(
1917       context.get(), context->get_def_use_mgr()->GetDef(24), 2);
1918   ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand4, 101)
1919                    .IsApplicable(context.get(), transformation_context));
1920 
1921   // OpAtomicIDecrement.
1922   const auto& scope_operand5 = MakeIdUseDescriptorFromUse(
1923       context.get(), context->get_def_use_mgr()->GetDef(25), 1);
1924   ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand5, 100)
1925                    .IsApplicable(context.get(), transformation_context));
1926 
1927   const auto& semantics_operand5 = MakeIdUseDescriptorFromUse(
1928       context.get(), context->get_def_use_mgr()->GetDef(25), 2);
1929   ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand5, 101)
1930                    .IsApplicable(context.get(), transformation_context));
1931 
1932   // OpAtomicIAdd.
1933   const auto& scope_operand6 = MakeIdUseDescriptorFromUse(
1934       context.get(), context->get_def_use_mgr()->GetDef(26), 1);
1935   ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand6, 100)
1936                    .IsApplicable(context.get(), transformation_context));
1937 
1938   const auto& semantics_operand6 = MakeIdUseDescriptorFromUse(
1939       context.get(), context->get_def_use_mgr()->GetDef(26), 2);
1940   ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand6, 101)
1941                    .IsApplicable(context.get(), transformation_context));
1942   // OpAtomicISub
1943   const auto& scope_operand8 = MakeIdUseDescriptorFromUse(
1944       context.get(), context->get_def_use_mgr()->GetDef(27), 1);
1945   ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand8, 100)
1946                    .IsApplicable(context.get(), transformation_context));
1947 
1948   const auto& semantics_operand8 = MakeIdUseDescriptorFromUse(
1949       context.get(), context->get_def_use_mgr()->GetDef(27), 2);
1950   ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand8, 101)
1951                    .IsApplicable(context.get(), transformation_context));
1952 
1953   // OpAtomicSMin
1954   const auto& scope_operand9 = MakeIdUseDescriptorFromUse(
1955       context.get(), context->get_def_use_mgr()->GetDef(28), 1);
1956   ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand9, 100)
1957                    .IsApplicable(context.get(), transformation_context));
1958 
1959   const auto& semantics_operand9 = MakeIdUseDescriptorFromUse(
1960       context.get(), context->get_def_use_mgr()->GetDef(28), 2);
1961   ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand9, 101)
1962                    .IsApplicable(context.get(), transformation_context));
1963   // OpAtomicUMin
1964   const auto& scope_operand10 = MakeIdUseDescriptorFromUse(
1965       context.get(), context->get_def_use_mgr()->GetDef(29), 1);
1966   ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand10, 100)
1967                    .IsApplicable(context.get(), transformation_context));
1968 
1969   const auto& semantics_operand10 = MakeIdUseDescriptorFromUse(
1970       context.get(), context->get_def_use_mgr()->GetDef(29), 2);
1971   ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand10, 101)
1972                    .IsApplicable(context.get(), transformation_context));
1973 
1974   // OpAtomicSMax
1975   const auto& scope_operand11 = MakeIdUseDescriptorFromUse(
1976       context.get(), context->get_def_use_mgr()->GetDef(30), 1);
1977   ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand11, 100)
1978                    .IsApplicable(context.get(), transformation_context));
1979 
1980   const auto& semantics_operand11 = MakeIdUseDescriptorFromUse(
1981       context.get(), context->get_def_use_mgr()->GetDef(30), 2);
1982   ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand11, 101)
1983                    .IsApplicable(context.get(), transformation_context));
1984   // OpAtomicUMax
1985   const auto& scope_operand12 = MakeIdUseDescriptorFromUse(
1986       context.get(), context->get_def_use_mgr()->GetDef(31), 1);
1987   ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand12, 100)
1988                    .IsApplicable(context.get(), transformation_context));
1989 
1990   const auto& semantics_operand12 = MakeIdUseDescriptorFromUse(
1991       context.get(), context->get_def_use_mgr()->GetDef(31), 2);
1992   ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand12, 101)
1993                    .IsApplicable(context.get(), transformation_context));
1994 
1995   // OpAtomicAnd
1996   const auto& scope_operand13 = MakeIdUseDescriptorFromUse(
1997       context.get(), context->get_def_use_mgr()->GetDef(32), 1);
1998   ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand13, 100)
1999                    .IsApplicable(context.get(), transformation_context));
2000 
2001   const auto& semantics_operand13 = MakeIdUseDescriptorFromUse(
2002       context.get(), context->get_def_use_mgr()->GetDef(32), 2);
2003   ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand13, 101)
2004                    .IsApplicable(context.get(), transformation_context));
2005 
2006   // OpAtomicOr
2007   const auto& scope_operand14 = MakeIdUseDescriptorFromUse(
2008       context.get(), context->get_def_use_mgr()->GetDef(33), 1);
2009   ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand14, 100)
2010                    .IsApplicable(context.get(), transformation_context));
2011 
2012   const auto& semantics_operand14 = MakeIdUseDescriptorFromUse(
2013       context.get(), context->get_def_use_mgr()->GetDef(33), 2);
2014   ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand14, 101)
2015                    .IsApplicable(context.get(), transformation_context));
2016 
2017   // OpAtomicXor
2018   const auto& scope_operand15 = MakeIdUseDescriptorFromUse(
2019       context.get(), context->get_def_use_mgr()->GetDef(34), 1);
2020   ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand15, 100)
2021                    .IsApplicable(context.get(), transformation_context));
2022 
2023   const auto& semantics_operand15 = MakeIdUseDescriptorFromUse(
2024       context.get(), context->get_def_use_mgr()->GetDef(34), 2);
2025   ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand15, 101)
2026                    .IsApplicable(context.get(), transformation_context));
2027 }
2028 
2029 // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): Improve this
2030 //  test so that it covers more atomic operations, and enable the test once the
2031 //  issue is fixed.
TEST(TransformationReplaceIdWithSynonymTest,DISABLED_SignOfAtomicScopeAndMemorySemanticsDoesNotMatter)2032 TEST(TransformationReplaceIdWithSynonymTest,
2033      DISABLED_SignOfAtomicScopeAndMemorySemanticsDoesNotMatter) {
2034   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): both the
2035   //  GLSL comment and the corresponding SPIR-V should be updated to cover a
2036   //  larger number of atomic operations.
2037   // The following SPIR-V came from this GLSL, edited to add some synonyms:
2038   //
2039   // #version 320 es
2040   //
2041   // #extension GL_KHR_memory_scope_semantics : enable
2042   //
2043   // layout(set = 0, binding = 0) buffer Buf {
2044   //   int x;
2045   // };
2046   //
2047   // void main() {
2048   //   int tmp = atomicLoad(x,
2049   //                        gl_ScopeWorkgroup,
2050   //                        gl_StorageSemanticsBuffer,
2051   //                        gl_SemanticsRelaxed);
2052   // }
2053   const std::string shader = R"(
2054                OpCapability Shader
2055           %1 = OpExtInstImport "GLSL.std.450"
2056                OpMemoryModel Logical GLSL450
2057                OpEntryPoint GLCompute %4 "main"
2058                OpExecutionMode %4 LocalSize 1 1 1
2059                OpSource ESSL 320
2060                OpSourceExtension "GL_KHR_memory_scope_semantics"
2061                OpMemberDecorate %9 0 Offset 0
2062                OpDecorate %9 Block
2063                OpDecorate %11 DescriptorSet 0
2064                OpDecorate %11 Binding 0
2065           %2 = OpTypeVoid
2066           %3 = OpTypeFunction %2
2067           %6 = OpTypeInt 32 1
2068           %7 = OpTypePointer Function %6
2069           %9 = OpTypeStruct %6
2070          %10 = OpTypePointer StorageBuffer %9
2071          %11 = OpVariable %10 StorageBuffer
2072          %12 = OpConstant %6 0
2073          %13 = OpTypePointer StorageBuffer %6
2074          %15 = OpConstant %6 2
2075          %16 = OpConstant %6 64
2076          %17 = OpTypeInt 32 0
2077         %100 = OpConstant %17 2 ; The same as %15, but with unsigned int type
2078          %18 = OpConstant %17 1
2079          %19 = OpConstant %17 0
2080          %20 = OpConstant %17 64
2081         %101 = OpConstant %6 64 ; The same as %20, but with signed int type
2082           %4 = OpFunction %2 None %3
2083           %5 = OpLabel
2084           %8 = OpVariable %7 Function
2085          %14 = OpAccessChain %13 %11 %12
2086          %21 = OpAtomicLoad %6 %14 %15 %20
2087                OpStore %8 %21
2088                OpReturn
2089                OpFunctionEnd
2090   )";
2091 
2092   const auto env = SPV_ENV_UNIVERSAL_1_3;
2093   const auto consumer = nullptr;
2094   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2095   spvtools::ValidatorOptions validator_options;
2096   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2097                                                kConsoleMessageConsumer));
2098   TransformationContext transformation_context(
2099       MakeUnique<FactManager>(context.get()), validator_options);
2100 
2101   // Tell the fact manager that %100 and %15 are synonymous
2102   transformation_context.GetFactManager()->AddFactDataSynonym(
2103       MakeDataDescriptor(100, {}), MakeDataDescriptor(15, {}));
2104 
2105   // Tell the fact manager that %101 and %20 are synonymous
2106   transformation_context.GetFactManager()->AddFactDataSynonym(
2107       MakeDataDescriptor(101, {}), MakeDataDescriptor(20, {}));
2108 
2109   {
2110     const auto& scope_operand = MakeIdUseDescriptorFromUse(
2111         context.get(), context->get_def_use_mgr()->GetDef(21), 1);
2112     TransformationReplaceIdWithSynonym replace_scope(scope_operand, 100);
2113     ASSERT_TRUE(
2114         replace_scope.IsApplicable(context.get(), transformation_context));
2115     ApplyAndCheckFreshIds(replace_scope, context.get(),
2116                           &transformation_context);
2117     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
2118         context.get(), validator_options, kConsoleMessageConsumer));
2119   }
2120 
2121   {
2122     const auto& semantics_operand = MakeIdUseDescriptorFromUse(
2123         context.get(), context->get_def_use_mgr()->GetDef(21), 2);
2124     TransformationReplaceIdWithSynonym replace_semantics(semantics_operand,
2125                                                          101);
2126     ASSERT_TRUE(
2127         replace_semantics.IsApplicable(context.get(), transformation_context));
2128     ApplyAndCheckFreshIds(replace_semantics, context.get(),
2129                           &transformation_context);
2130     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
2131         context.get(), validator_options, kConsoleMessageConsumer));
2132   }
2133 
2134   const std::string after_transformation = R"(
2135                OpCapability Shader
2136           %1 = OpExtInstImport "GLSL.std.450"
2137                OpMemoryModel Logical GLSL450
2138                OpEntryPoint GLCompute %4 "main"
2139                OpExecutionMode %4 LocalSize 1 1 1
2140                OpSource ESSL 320
2141                OpSourceExtension "GL_KHR_memory_scope_semantics"
2142                OpMemberDecorate %9 0 Offset 0
2143                OpDecorate %9 Block
2144                OpDecorate %11 DescriptorSet 0
2145                OpDecorate %11 Binding 0
2146           %2 = OpTypeVoid
2147           %3 = OpTypeFunction %2
2148           %6 = OpTypeInt 32 1
2149           %7 = OpTypePointer Function %6
2150           %9 = OpTypeStruct %6
2151          %10 = OpTypePointer StorageBuffer %9
2152          %11 = OpVariable %10 StorageBuffer
2153          %12 = OpConstant %6 0
2154          %13 = OpTypePointer StorageBuffer %6
2155          %15 = OpConstant %6 2
2156          %16 = OpConstant %6 64
2157          %17 = OpTypeInt 32 0
2158         %100 = OpConstant %17 2
2159          %18 = OpConstant %17 1
2160          %19 = OpConstant %17 0
2161          %20 = OpConstant %17 64
2162         %101 = OpConstant %6 64
2163           %4 = OpFunction %2 None %3
2164           %5 = OpLabel
2165           %8 = OpVariable %7 Function
2166          %14 = OpAccessChain %13 %11 %12
2167          %21 = OpAtomicLoad %6 %14 %100 %101
2168                OpStore %8 %21
2169                OpReturn
2170                OpFunctionEnd
2171   )";
2172 
2173   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
2174 }
2175 
2176 }  // namespace
2177 }  // namespace fuzz
2178 }  // namespace spvtools
2179