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