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