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