• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "source/fuzz/transformation_add_dead_continue.h"
16 
17 #include "gtest/gtest.h"
18 #include "source/fuzz/fuzzer_util.h"
19 #include "test/fuzz/fuzz_test_util.h"
20 
21 namespace spvtools {
22 namespace fuzz {
23 namespace {
24 
TEST(TransformationAddDeadContinueTest,SimpleExample)25 TEST(TransformationAddDeadContinueTest, SimpleExample) {
26   // For a simple loop, checks that some dead continue scenarios are possible,
27   // checks that some invalid scenarios are indeed not allowed, and then applies
28   // a transformation.
29 
30   // The SPIR-V for this test is adapted from the following GLSL, by separating
31   // some assignments into their own basic blocks, and adding constants for true
32   // and false:
33   //
34   // void main() {
35   //   int x = 0;
36   //   for (int i = 0; i < 10; i++) {
37   //     x = x + i;
38   //     x = x + i;
39   //   }
40   // }
41 
42   std::string shader = R"(
43                OpCapability Shader
44           %1 = OpExtInstImport "GLSL.std.450"
45                OpMemoryModel Logical GLSL450
46                OpEntryPoint Fragment %4 "main"
47                OpExecutionMode %4 OriginUpperLeft
48                OpSource ESSL 310
49                OpName %4 "main"
50                OpName %8 "x"
51                OpName %10 "i"
52           %2 = OpTypeVoid
53           %3 = OpTypeFunction %2
54           %6 = OpTypeInt 32 1
55           %7 = OpTypePointer Function %6
56           %9 = OpConstant %6 0
57          %17 = OpConstant %6 10
58          %18 = OpTypeBool
59          %41 = OpConstantTrue %18
60          %42 = OpConstantFalse %18
61          %27 = OpConstant %6 1
62           %4 = OpFunction %2 None %3
63           %5 = OpLabel
64           %8 = OpVariable %7 Function
65          %10 = OpVariable %7 Function
66                OpStore %8 %9
67                OpStore %10 %9
68                OpBranch %11
69          %11 = OpLabel
70                OpLoopMerge %13 %14 None
71                OpBranch %15
72          %15 = OpLabel
73          %16 = OpLoad %6 %10
74          %19 = OpSLessThan %18 %16 %17
75                OpBranchConditional %19 %12 %13
76          %12 = OpLabel
77          %20 = OpLoad %6 %8
78          %21 = OpLoad %6 %10
79          %22 = OpIAdd %6 %20 %21
80                OpStore %8 %22
81                OpBranch %40
82          %40 = OpLabel
83          %23 = OpLoad %6 %8
84          %24 = OpLoad %6 %10
85          %25 = OpIAdd %6 %23 %24
86                OpStore %8 %25
87                OpBranch %14
88          %14 = OpLabel
89          %26 = OpLoad %6 %10
90          %28 = OpIAdd %6 %26 %27
91                OpStore %10 %28
92                OpBranch %11
93          %13 = OpLabel
94                OpReturn
95                OpFunctionEnd
96   )";
97 
98   const auto env = SPV_ENV_UNIVERSAL_1_3;
99   const auto consumer = nullptr;
100   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
101   spvtools::ValidatorOptions validator_options;
102   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
103                                                kConsoleMessageConsumer));
104   TransformationContext transformation_context(
105       MakeUnique<FactManager>(context.get()), validator_options);
106   // These are all possibilities.
107   ASSERT_TRUE(TransformationAddDeadContinue(11, true, {})
108                   .IsApplicable(context.get(), transformation_context));
109   ASSERT_TRUE(TransformationAddDeadContinue(11, false, {})
110                   .IsApplicable(context.get(), transformation_context));
111   ASSERT_TRUE(TransformationAddDeadContinue(12, true, {})
112                   .IsApplicable(context.get(), transformation_context));
113   ASSERT_TRUE(TransformationAddDeadContinue(12, false, {})
114                   .IsApplicable(context.get(), transformation_context));
115   ASSERT_TRUE(TransformationAddDeadContinue(40, true, {})
116                   .IsApplicable(context.get(), transformation_context));
117   ASSERT_TRUE(TransformationAddDeadContinue(40, false, {})
118                   .IsApplicable(context.get(), transformation_context));
119 
120   // Inapplicable: 100 is not a block id.
121   ASSERT_FALSE(TransformationAddDeadContinue(100, true, {})
122                    .IsApplicable(context.get(), transformation_context));
123 
124   // Inapplicable: 10 is not in a loop.
125   ASSERT_FALSE(TransformationAddDeadContinue(10, true, {})
126                    .IsApplicable(context.get(), transformation_context));
127 
128   // Inapplicable: 15 does not branch unconditionally to a single successor.
129   ASSERT_FALSE(TransformationAddDeadContinue(15, true, {})
130                    .IsApplicable(context.get(), transformation_context));
131 
132   // Inapplicable: 13 is not in a loop and has no successor.
133   ASSERT_FALSE(TransformationAddDeadContinue(13, true, {})
134                    .IsApplicable(context.get(), transformation_context));
135 
136   // Inapplicable: 14 is the loop continue target, so it's not OK to jump to
137   // the loop continue from there.
138   ASSERT_FALSE(TransformationAddDeadContinue(14, false, {})
139                    .IsApplicable(context.get(), transformation_context));
140 
141   // These are the transformations we will apply.
142   auto transformation1 = TransformationAddDeadContinue(11, true, {});
143   auto transformation2 = TransformationAddDeadContinue(12, false, {});
144   auto transformation3 = TransformationAddDeadContinue(40, true, {});
145 
146   ASSERT_TRUE(
147       transformation1.IsApplicable(context.get(), transformation_context));
148   ApplyAndCheckFreshIds(transformation1, context.get(),
149                         &transformation_context);
150   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
151                                                kConsoleMessageConsumer));
152 
153   ASSERT_TRUE(
154       transformation2.IsApplicable(context.get(), transformation_context));
155   ApplyAndCheckFreshIds(transformation2, context.get(),
156                         &transformation_context);
157   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
158                                                kConsoleMessageConsumer));
159 
160   ASSERT_TRUE(
161       transformation3.IsApplicable(context.get(), transformation_context));
162   ApplyAndCheckFreshIds(transformation3, context.get(),
163                         &transformation_context);
164   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
165                                                kConsoleMessageConsumer));
166 
167   std::string after_transformation = R"(
168                OpCapability Shader
169           %1 = OpExtInstImport "GLSL.std.450"
170                OpMemoryModel Logical GLSL450
171                OpEntryPoint Fragment %4 "main"
172                OpExecutionMode %4 OriginUpperLeft
173                OpSource ESSL 310
174                OpName %4 "main"
175                OpName %8 "x"
176                OpName %10 "i"
177           %2 = OpTypeVoid
178           %3 = OpTypeFunction %2
179           %6 = OpTypeInt 32 1
180           %7 = OpTypePointer Function %6
181           %9 = OpConstant %6 0
182          %17 = OpConstant %6 10
183          %18 = OpTypeBool
184          %41 = OpConstantTrue %18
185          %42 = OpConstantFalse %18
186          %27 = OpConstant %6 1
187           %4 = OpFunction %2 None %3
188           %5 = OpLabel
189           %8 = OpVariable %7 Function
190          %10 = OpVariable %7 Function
191                OpStore %8 %9
192                OpStore %10 %9
193                OpBranch %11
194          %11 = OpLabel
195                OpLoopMerge %13 %14 None
196                OpBranchConditional %41 %15 %14
197          %15 = OpLabel
198          %16 = OpLoad %6 %10
199          %19 = OpSLessThan %18 %16 %17
200                OpBranchConditional %19 %12 %13
201          %12 = OpLabel
202          %20 = OpLoad %6 %8
203          %21 = OpLoad %6 %10
204          %22 = OpIAdd %6 %20 %21
205                OpStore %8 %22
206                OpBranchConditional %42 %14 %40
207          %40 = OpLabel
208          %23 = OpLoad %6 %8
209          %24 = OpLoad %6 %10
210          %25 = OpIAdd %6 %23 %24
211                OpStore %8 %25
212                OpBranchConditional %41 %14 %14
213          %14 = OpLabel
214          %26 = OpLoad %6 %10
215          %28 = OpIAdd %6 %26 %27
216                OpStore %10 %28
217                OpBranch %11
218          %13 = OpLabel
219                OpReturn
220                OpFunctionEnd
221   )";
222 
223   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
224 }
225 
TEST(TransformationAddDeadContinueTest,LoopNest)226 TEST(TransformationAddDeadContinueTest, LoopNest) {
227   // Checks some allowed and disallowed scenarios for a nest of loops, including
228   // continuing a loop from an if or switch.
229 
230   // The SPIR-V for this test is adapted from the following GLSL:
231   //
232   // void main() {
233   //   int x, y;
234   //   do {
235   //     x++;
236   //     for (int j = 0; j < 100; j++) {
237   //       y++;
238   //       if (x == y) {
239   //         x++;
240   //         if (x == 2) {
241   //           y++;
242   //         }
243   //         switch (x) {
244   //           case 0:
245   //             x = 2;
246   //           default:
247   //             break;
248   //         }
249   //       }
250   //     }
251   //   } while (x > y);
252   //
253   //   for (int i = 0; i < 100; i++) {
254   //     x++;
255   //   }
256   // }
257 
258   std::string shader = R"(
259                OpCapability Shader
260           %1 = OpExtInstImport "GLSL.std.450"
261                OpMemoryModel Logical GLSL450
262                OpEntryPoint Fragment %4 "main"
263                OpExecutionMode %4 OriginUpperLeft
264                OpSource ESSL 310
265                OpName %4 "main"
266                OpName %12 "x"
267                OpName %16 "j"
268                OpName %27 "y"
269                OpName %55 "i"
270           %2 = OpTypeVoid
271           %3 = OpTypeFunction %2
272          %10 = OpTypeInt 32 1
273          %11 = OpTypePointer Function %10
274          %14 = OpConstant %10 1
275          %17 = OpConstant %10 0
276          %24 = OpConstant %10 100
277          %25 = OpTypeBool
278          %38 = OpConstant %10 2
279          %67 = OpConstantTrue %25
280          %68 = OpConstantFalse %25
281           %4 = OpFunction %2 None %3
282           %5 = OpLabel
283          %12 = OpVariable %11 Function
284          %16 = OpVariable %11 Function
285          %27 = OpVariable %11 Function
286          %55 = OpVariable %11 Function
287                OpBranch %6
288           %6 = OpLabel
289                OpLoopMerge %8 %9 None
290                OpBranch %7
291           %7 = OpLabel
292          %13 = OpLoad %10 %12
293          %15 = OpIAdd %10 %13 %14
294                OpStore %12 %15
295                OpStore %16 %17
296                OpBranch %18
297          %18 = OpLabel
298                OpLoopMerge %20 %21 None
299                OpBranch %22
300          %22 = OpLabel
301          %23 = OpLoad %10 %16
302          %26 = OpSLessThan %25 %23 %24
303                OpBranchConditional %26 %19 %20
304          %19 = OpLabel
305          %28 = OpLoad %10 %27
306          %29 = OpIAdd %10 %28 %14
307                OpStore %27 %29
308          %30 = OpLoad %10 %12
309          %31 = OpLoad %10 %27
310          %32 = OpIEqual %25 %30 %31
311                OpSelectionMerge %34 None
312                OpBranchConditional %32 %33 %34
313          %33 = OpLabel
314          %35 = OpLoad %10 %12
315          %36 = OpIAdd %10 %35 %14
316                OpStore %12 %36
317          %37 = OpLoad %10 %12
318          %39 = OpIEqual %25 %37 %38
319                OpSelectionMerge %41 None
320                OpBranchConditional %39 %40 %41
321          %40 = OpLabel
322          %42 = OpLoad %10 %27
323          %43 = OpIAdd %10 %42 %14
324                OpStore %27 %43
325                OpBranch %41
326          %41 = OpLabel
327          %44 = OpLoad %10 %12
328                OpSelectionMerge %47 None
329                OpSwitch %44 %46 0 %45
330          %46 = OpLabel
331                OpBranch %47
332          %45 = OpLabel
333                OpStore %12 %38
334                OpBranch %46
335          %47 = OpLabel
336                OpBranch %34
337          %34 = OpLabel
338                OpBranch %21
339          %21 = OpLabel
340          %50 = OpLoad %10 %16
341          %51 = OpIAdd %10 %50 %14
342                OpStore %16 %51
343                OpBranch %18
344          %20 = OpLabel
345                OpBranch %9
346           %9 = OpLabel
347          %52 = OpLoad %10 %12
348          %53 = OpLoad %10 %27
349          %54 = OpSGreaterThan %25 %52 %53
350                OpBranchConditional %54 %6 %8
351           %8 = OpLabel
352                OpStore %55 %17
353                OpBranch %56
354          %56 = OpLabel
355                OpLoopMerge %58 %59 None
356                OpBranch %60
357          %60 = OpLabel
358          %61 = OpLoad %10 %55
359          %62 = OpSLessThan %25 %61 %24
360                OpBranchConditional %62 %57 %58
361          %57 = OpLabel
362          %63 = OpLoad %10 %12
363          %64 = OpIAdd %10 %63 %14
364                OpStore %12 %64
365                OpBranch %59
366          %59 = OpLabel
367          %65 = OpLoad %10 %55
368          %66 = OpIAdd %10 %65 %14
369                OpStore %55 %66
370                OpBranch %56
371          %58 = OpLabel
372                OpReturn
373                OpFunctionEnd
374   )";
375 
376   const auto env = SPV_ENV_UNIVERSAL_1_3;
377   const auto consumer = nullptr;
378   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
379   spvtools::ValidatorOptions validator_options;
380   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
381                                                kConsoleMessageConsumer));
382   TransformationContext transformation_context(
383       MakeUnique<FactManager>(context.get()), validator_options);
384   std::vector<uint32_t> good = {6, 7, 18, 20, 34, 40, 45, 46, 47, 56, 57};
385   std::vector<uint32_t> bad = {5, 8, 9, 19, 21, 22, 33, 41, 58, 59, 60};
386 
387   for (uint32_t from_block : bad) {
388     ASSERT_FALSE(TransformationAddDeadContinue(from_block, true, {})
389                      .IsApplicable(context.get(), transformation_context));
390   }
391   for (uint32_t from_block : good) {
392     const TransformationAddDeadContinue transformation(from_block, true, {});
393     ASSERT_TRUE(
394         transformation.IsApplicable(context.get(), transformation_context));
395     ApplyAndCheckFreshIds(transformation, context.get(),
396                           &transformation_context);
397     ASSERT_FALSE(
398         transformation.IsApplicable(context.get(), transformation_context));
399   }
400 
401   std::string after_transformation = R"(
402                OpCapability Shader
403           %1 = OpExtInstImport "GLSL.std.450"
404                OpMemoryModel Logical GLSL450
405                OpEntryPoint Fragment %4 "main"
406                OpExecutionMode %4 OriginUpperLeft
407                OpSource ESSL 310
408                OpName %4 "main"
409                OpName %12 "x"
410                OpName %16 "j"
411                OpName %27 "y"
412                OpName %55 "i"
413           %2 = OpTypeVoid
414           %3 = OpTypeFunction %2
415          %10 = OpTypeInt 32 1
416          %11 = OpTypePointer Function %10
417          %14 = OpConstant %10 1
418          %17 = OpConstant %10 0
419          %24 = OpConstant %10 100
420          %25 = OpTypeBool
421          %38 = OpConstant %10 2
422          %67 = OpConstantTrue %25
423          %68 = OpConstantFalse %25
424           %4 = OpFunction %2 None %3
425           %5 = OpLabel
426          %12 = OpVariable %11 Function
427          %16 = OpVariable %11 Function
428          %27 = OpVariable %11 Function
429          %55 = OpVariable %11 Function
430                OpBranch %6
431           %6 = OpLabel
432                OpLoopMerge %8 %9 None
433                OpBranchConditional %67 %7 %9
434           %7 = OpLabel
435          %13 = OpLoad %10 %12
436          %15 = OpIAdd %10 %13 %14
437                OpStore %12 %15
438                OpStore %16 %17
439                OpBranchConditional %67 %18 %9
440          %18 = OpLabel
441                OpLoopMerge %20 %21 None
442                OpBranchConditional %67 %22 %21
443          %22 = OpLabel
444          %23 = OpLoad %10 %16
445          %26 = OpSLessThan %25 %23 %24
446                OpBranchConditional %26 %19 %20
447          %19 = OpLabel
448          %28 = OpLoad %10 %27
449          %29 = OpIAdd %10 %28 %14
450                OpStore %27 %29
451          %30 = OpLoad %10 %12
452          %31 = OpLoad %10 %27
453          %32 = OpIEqual %25 %30 %31
454                OpSelectionMerge %34 None
455                OpBranchConditional %32 %33 %34
456          %33 = OpLabel
457          %35 = OpLoad %10 %12
458          %36 = OpIAdd %10 %35 %14
459                OpStore %12 %36
460          %37 = OpLoad %10 %12
461          %39 = OpIEqual %25 %37 %38
462                OpSelectionMerge %41 None
463                OpBranchConditional %39 %40 %41
464          %40 = OpLabel
465          %42 = OpLoad %10 %27
466          %43 = OpIAdd %10 %42 %14
467                OpStore %27 %43
468                OpBranchConditional %67 %41 %21
469          %41 = OpLabel
470          %44 = OpLoad %10 %12
471                OpSelectionMerge %47 None
472                OpSwitch %44 %46 0 %45
473          %46 = OpLabel
474                OpBranchConditional %67 %47 %21
475          %45 = OpLabel
476                OpStore %12 %38
477                OpBranchConditional %67 %46 %21
478          %47 = OpLabel
479                OpBranchConditional %67 %34 %21
480          %34 = OpLabel
481                OpBranchConditional %67 %21 %21
482          %21 = OpLabel
483          %50 = OpLoad %10 %16
484          %51 = OpIAdd %10 %50 %14
485                OpStore %16 %51
486                OpBranch %18
487          %20 = OpLabel
488                OpBranchConditional %67 %9 %9
489           %9 = OpLabel
490          %52 = OpLoad %10 %12
491          %53 = OpLoad %10 %27
492          %54 = OpSGreaterThan %25 %52 %53
493                OpBranchConditional %54 %6 %8
494           %8 = OpLabel
495                OpStore %55 %17
496                OpBranch %56
497          %56 = OpLabel
498                OpLoopMerge %58 %59 None
499                OpBranchConditional %67 %60 %59
500          %60 = OpLabel
501          %61 = OpLoad %10 %55
502          %62 = OpSLessThan %25 %61 %24
503                OpBranchConditional %62 %57 %58
504          %57 = OpLabel
505          %63 = OpLoad %10 %12
506          %64 = OpIAdd %10 %63 %14
507                OpStore %12 %64
508                OpBranchConditional %67 %59 %59
509          %59 = OpLabel
510          %65 = OpLoad %10 %55
511          %66 = OpIAdd %10 %65 %14
512                OpStore %55 %66
513                OpBranch %56
514          %58 = OpLabel
515                OpReturn
516                OpFunctionEnd
517   )";
518 
519   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
520 }
521 
TEST(TransformationAddDeadConditionalTest,LoopInContinueConstruct)522 TEST(TransformationAddDeadConditionalTest, LoopInContinueConstruct) {
523   // Considers some scenarios where there is a loop in a loop's continue
524   // construct.
525 
526   // The SPIR-V for this test is adapted from the following GLSL, with inlining
527   // applied so that the loop from foo is in the main loop's continue construct:
528   //
529   // int foo() {
530   //   int result = 0;
531   //   for (int j = 0; j < 10; j++) {
532   //     result++;
533   //   }
534   //   return result;
535   // }
536   //
537   // void main() {
538   //   for (int i = 0; i < 100; i += foo()) {
539   //   }
540   // }
541 
542   std::string shader = R"(
543                OpCapability Shader
544           %1 = OpExtInstImport "GLSL.std.450"
545                OpMemoryModel Logical GLSL450
546                OpEntryPoint Fragment %4 "main"
547                OpExecutionMode %4 OriginUpperLeft
548                OpSource ESSL 310
549                OpName %4 "main"
550                OpName %31 "i"
551           %2 = OpTypeVoid
552           %3 = OpTypeFunction %2
553           %6 = OpTypeInt 32 1
554           %7 = OpTypeFunction %6
555          %10 = OpTypePointer Function %6
556          %12 = OpConstant %6 0
557          %20 = OpConstant %6 10
558          %21 = OpTypeBool
559         %100 = OpConstantFalse %21
560          %24 = OpConstant %6 1
561          %38 = OpConstant %6 100
562           %4 = OpFunction %2 None %3
563           %5 = OpLabel
564          %43 = OpVariable %10 Function
565          %44 = OpVariable %10 Function
566          %45 = OpVariable %10 Function
567          %31 = OpVariable %10 Function
568                OpStore %31 %12
569                OpBranch %32
570          %32 = OpLabel
571                OpLoopMerge %34 %35 None
572                OpBranch %36
573          %36 = OpLabel
574          %37 = OpLoad %6 %31
575          %39 = OpSLessThan %21 %37 %38
576                OpBranchConditional %39 %33 %34
577          %33 = OpLabel
578                OpBranch %35
579          %35 = OpLabel
580                OpStore %43 %12
581                OpStore %44 %12
582                OpBranch %46
583          %46 = OpLabel
584                OpLoopMerge %47 %48 None
585                OpBranch %49
586          %49 = OpLabel
587          %50 = OpLoad %6 %44
588          %51 = OpSLessThan %21 %50 %20
589                OpBranchConditional %51 %52 %47
590          %52 = OpLabel
591          %53 = OpLoad %6 %43
592                OpBranch %101
593         %101 = OpLabel
594          %54 = OpIAdd %6 %53 %24
595                OpStore %43 %54
596                OpBranch %48
597          %48 = OpLabel
598          %55 = OpLoad %6 %44
599          %56 = OpIAdd %6 %55 %24
600                OpStore %44 %56
601                OpBranch %46
602          %47 = OpLabel
603          %57 = OpLoad %6 %43
604                OpStore %45 %57
605          %40 = OpLoad %6 %45
606          %41 = OpLoad %6 %31
607          %42 = OpIAdd %6 %41 %40
608                OpStore %31 %42
609                OpBranch %32
610          %34 = OpLabel
611                OpReturn
612                OpFunctionEnd
613   )";
614 
615   const auto env = SPV_ENV_UNIVERSAL_1_3;
616   const auto consumer = nullptr;
617   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
618   spvtools::ValidatorOptions validator_options;
619   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
620                                                kConsoleMessageConsumer));
621   TransformationContext transformation_context(
622       MakeUnique<FactManager>(context.get()), validator_options);
623   std::vector<uint32_t> good = {32, 33, 46, 52, 101};
624   std::vector<uint32_t> bad = {5, 34, 36, 35, 47, 49, 48};
625 
626   for (uint32_t from_block : bad) {
627     ASSERT_FALSE(TransformationAddDeadContinue(from_block, false, {})
628                      .IsApplicable(context.get(), transformation_context));
629   }
630   for (uint32_t from_block : good) {
631     const TransformationAddDeadContinue transformation(from_block, false, {});
632     ASSERT_TRUE(
633         transformation.IsApplicable(context.get(), transformation_context));
634     ApplyAndCheckFreshIds(transformation, context.get(),
635                           &transformation_context);
636     ASSERT_FALSE(
637         transformation.IsApplicable(context.get(), transformation_context));
638   }
639 
640   std::string after_transformation = R"(
641                OpCapability Shader
642           %1 = OpExtInstImport "GLSL.std.450"
643                OpMemoryModel Logical GLSL450
644                OpEntryPoint Fragment %4 "main"
645                OpExecutionMode %4 OriginUpperLeft
646                OpSource ESSL 310
647                OpName %4 "main"
648                OpName %31 "i"
649           %2 = OpTypeVoid
650           %3 = OpTypeFunction %2
651           %6 = OpTypeInt 32 1
652           %7 = OpTypeFunction %6
653          %10 = OpTypePointer Function %6
654          %12 = OpConstant %6 0
655          %20 = OpConstant %6 10
656          %21 = OpTypeBool
657         %100 = OpConstantFalse %21
658          %24 = OpConstant %6 1
659          %38 = OpConstant %6 100
660           %4 = OpFunction %2 None %3
661           %5 = OpLabel
662          %43 = OpVariable %10 Function
663          %44 = OpVariable %10 Function
664          %45 = OpVariable %10 Function
665          %31 = OpVariable %10 Function
666                OpStore %31 %12
667                OpBranch %32
668          %32 = OpLabel
669                OpLoopMerge %34 %35 None
670                OpBranchConditional %100 %35 %36
671          %36 = OpLabel
672          %37 = OpLoad %6 %31
673          %39 = OpSLessThan %21 %37 %38
674                OpBranchConditional %39 %33 %34
675          %33 = OpLabel
676                OpBranchConditional %100 %35 %35
677          %35 = OpLabel
678                OpStore %43 %12
679                OpStore %44 %12
680                OpBranch %46
681          %46 = OpLabel
682                OpLoopMerge %47 %48 None
683                OpBranchConditional %100 %48 %49
684          %49 = OpLabel
685          %50 = OpLoad %6 %44
686          %51 = OpSLessThan %21 %50 %20
687                OpBranchConditional %51 %52 %47
688          %52 = OpLabel
689          %53 = OpLoad %6 %43
690                OpBranchConditional %100 %48 %101
691         %101 = OpLabel
692          %54 = OpIAdd %6 %53 %24
693                OpStore %43 %54
694                OpBranchConditional %100 %48 %48
695          %48 = OpLabel
696          %55 = OpLoad %6 %44
697          %56 = OpIAdd %6 %55 %24
698                OpStore %44 %56
699                OpBranch %46
700          %47 = OpLabel
701          %57 = OpLoad %6 %43
702                OpStore %45 %57
703          %40 = OpLoad %6 %45
704          %41 = OpLoad %6 %31
705          %42 = OpIAdd %6 %41 %40
706                OpStore %31 %42
707                OpBranch %32
708          %34 = OpLabel
709                OpReturn
710                OpFunctionEnd
711   )";
712 
713   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
714 }
715 
TEST(TransformationAddDeadContinueTest,PhiInstructions)716 TEST(TransformationAddDeadContinueTest, PhiInstructions) {
717   // Checks that the transformation works in the presence of phi instructions.
718 
719   // The SPIR-V for this test is adapted from the following GLSL, with a bit of
720   // extra and artificial work to get some interesting uses of OpPhi:
721   //
722   // void main() {
723   //   int x; int y;
724   //   float f;
725   //   x = 2;
726   //   f = 3.0;
727   //   if (x > y) {
728   //     x = 3;
729   //     f = 4.0;
730   //   } else {
731   //     x = x + 2;
732   //     f = f + 10.0;
733   //   }
734   //   while (x < y) {
735   //     x = x + 1;
736   //     f = f + 1.0;
737   //   }
738   //   y = x;
739   //   f = f + 3.0;
740   // }
741 
742   std::string shader = R"(
743                OpCapability Shader
744           %1 = OpExtInstImport "GLSL.std.450"
745                OpMemoryModel Logical GLSL450
746                OpEntryPoint Fragment %4 "main"
747                OpExecutionMode %4 OriginUpperLeft
748                OpSource ESSL 310
749                OpName %4 "main"
750                OpName %8 "x"
751                OpName %12 "f"
752                OpName %15 "y"
753           %2 = OpTypeVoid
754           %3 = OpTypeFunction %2
755           %6 = OpTypeInt 32 1
756           %7 = OpTypePointer Function %6
757           %9 = OpConstant %6 2
758          %10 = OpTypeFloat 32
759          %11 = OpTypePointer Function %10
760          %13 = OpConstant %10 3
761          %17 = OpTypeBool
762          %80 = OpConstantTrue %17
763          %21 = OpConstant %6 3
764          %22 = OpConstant %10 4
765          %27 = OpConstant %10 10
766          %38 = OpConstant %6 1
767          %41 = OpConstant %10 1
768          %46 = OpUndef %6
769           %4 = OpFunction %2 None %3
770           %5 = OpLabel
771           %8 = OpVariable %7 Function
772          %12 = OpVariable %11 Function
773          %15 = OpVariable %7 Function
774                OpStore %8 %9
775                OpStore %12 %13
776          %18 = OpSGreaterThan %17 %9 %46
777                OpSelectionMerge %20 None
778                OpBranchConditional %18 %19 %23
779          %19 = OpLabel
780                OpStore %8 %21
781                OpStore %12 %22
782                OpBranch %20
783          %23 = OpLabel
784          %25 = OpIAdd %6 %9 %9
785                OpStore %8 %25
786                OpBranch %70
787          %70 = OpLabel
788          %28 = OpFAdd %10 %13 %27
789                OpStore %12 %28
790                OpBranch %20
791          %20 = OpLabel
792          %52 = OpPhi %10 %22 %19 %28 %70
793          %48 = OpPhi %6 %21 %19 %25 %70
794                OpBranch %29
795          %29 = OpLabel
796          %51 = OpPhi %10 %52 %20 %100 %32
797          %47 = OpPhi %6 %48 %20 %101 %32
798                OpLoopMerge %31 %32 None
799                OpBranch %33
800          %33 = OpLabel
801          %36 = OpSLessThan %17 %47 %46
802                OpBranchConditional %36 %30 %31
803          %30 = OpLabel
804          %39 = OpIAdd %6 %47 %38
805                OpStore %8 %39
806                OpBranch %75
807          %75 = OpLabel
808          %42 = OpFAdd %10 %51 %41
809                OpStore %12 %42
810                OpBranch %32
811          %32 = OpLabel
812         %100 = OpPhi %10 %42 %75
813         %101 = OpPhi %6 %39 %75
814                OpBranch %29
815          %31 = OpLabel
816          %71 = OpPhi %6 %47 %33
817          %72 = OpPhi %10 %51 %33
818                OpStore %15 %71
819          %45 = OpFAdd %10 %72 %13
820                OpStore %12 %45
821                OpReturn
822                OpFunctionEnd
823   )";
824 
825   const auto env = SPV_ENV_UNIVERSAL_1_3;
826   const auto consumer = nullptr;
827   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
828   spvtools::ValidatorOptions validator_options;
829   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
830                                                kConsoleMessageConsumer));
831   TransformationContext transformation_context(
832       MakeUnique<FactManager>(context.get()), validator_options);
833   std::vector<uint32_t> bad = {5, 19, 20, 23, 31, 32, 33, 70};
834 
835   std::vector<uint32_t> good = {29, 30, 75};
836 
837   for (uint32_t from_block : bad) {
838     ASSERT_FALSE(TransformationAddDeadContinue(from_block, true, {})
839                      .IsApplicable(context.get(), transformation_context));
840   }
841   auto transformation1 = TransformationAddDeadContinue(29, true, {13, 21});
842   ASSERT_TRUE(
843       transformation1.IsApplicable(context.get(), transformation_context));
844   ApplyAndCheckFreshIds(transformation1, context.get(),
845                         &transformation_context);
846 
847   auto transformation2 = TransformationAddDeadContinue(30, true, {22, 46});
848   ASSERT_TRUE(
849       transformation2.IsApplicable(context.get(), transformation_context));
850   ApplyAndCheckFreshIds(transformation2, context.get(),
851                         &transformation_context);
852 
853   // 75 already has the continue block as a successor, so we should not provide
854   // phi ids.
855   auto transformationBad = TransformationAddDeadContinue(75, true, {27, 46});
856   ASSERT_FALSE(
857       transformationBad.IsApplicable(context.get(), transformation_context));
858 
859   auto transformation3 = TransformationAddDeadContinue(75, true, {});
860   ASSERT_TRUE(
861       transformation3.IsApplicable(context.get(), transformation_context));
862   ApplyAndCheckFreshIds(transformation3, context.get(),
863                         &transformation_context);
864 
865   std::string after_transformation = R"(
866                OpCapability Shader
867           %1 = OpExtInstImport "GLSL.std.450"
868                OpMemoryModel Logical GLSL450
869                OpEntryPoint Fragment %4 "main"
870                OpExecutionMode %4 OriginUpperLeft
871                OpSource ESSL 310
872                OpName %4 "main"
873                OpName %8 "x"
874                OpName %12 "f"
875                OpName %15 "y"
876           %2 = OpTypeVoid
877           %3 = OpTypeFunction %2
878           %6 = OpTypeInt 32 1
879           %7 = OpTypePointer Function %6
880           %9 = OpConstant %6 2
881          %10 = OpTypeFloat 32
882          %11 = OpTypePointer Function %10
883          %13 = OpConstant %10 3
884          %17 = OpTypeBool
885          %80 = OpConstantTrue %17
886          %21 = OpConstant %6 3
887          %22 = OpConstant %10 4
888          %27 = OpConstant %10 10
889          %38 = OpConstant %6 1
890          %41 = OpConstant %10 1
891          %46 = OpUndef %6
892           %4 = OpFunction %2 None %3
893           %5 = OpLabel
894           %8 = OpVariable %7 Function
895          %12 = OpVariable %11 Function
896          %15 = OpVariable %7 Function
897                OpStore %8 %9
898                OpStore %12 %13
899          %18 = OpSGreaterThan %17 %9 %46
900                OpSelectionMerge %20 None
901                OpBranchConditional %18 %19 %23
902          %19 = OpLabel
903                OpStore %8 %21
904                OpStore %12 %22
905                OpBranch %20
906          %23 = OpLabel
907          %25 = OpIAdd %6 %9 %9
908                OpStore %8 %25
909                OpBranch %70
910          %70 = OpLabel
911          %28 = OpFAdd %10 %13 %27
912                OpStore %12 %28
913                OpBranch %20
914          %20 = OpLabel
915          %52 = OpPhi %10 %22 %19 %28 %70
916          %48 = OpPhi %6 %21 %19 %25 %70
917                OpBranch %29
918          %29 = OpLabel
919          %51 = OpPhi %10 %52 %20 %100 %32
920          %47 = OpPhi %6 %48 %20 %101 %32
921                OpLoopMerge %31 %32 None
922                OpBranchConditional %80 %33 %32
923          %33 = OpLabel
924          %36 = OpSLessThan %17 %47 %46
925                OpBranchConditional %36 %30 %31
926          %30 = OpLabel
927          %39 = OpIAdd %6 %47 %38
928                OpStore %8 %39
929                OpBranchConditional %80 %75 %32
930          %75 = OpLabel
931          %42 = OpFAdd %10 %51 %41
932                OpStore %12 %42
933                OpBranchConditional %80 %32 %32
934          %32 = OpLabel
935         %100 = OpPhi %10 %42 %75 %13 %29 %22 %30
936         %101 = OpPhi %6 %39 %75 %21 %29 %46 %30
937                OpBranch %29
938          %31 = OpLabel
939          %71 = OpPhi %6 %47 %33
940          %72 = OpPhi %10 %51 %33
941                OpStore %15 %71
942          %45 = OpFAdd %10 %72 %13
943                OpStore %12 %45
944                OpReturn
945                OpFunctionEnd
946   )";
947 
948   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
949 }
950 
TEST(TransformationAddDeadContinueTest,RespectDominanceRules1)951 TEST(TransformationAddDeadContinueTest, RespectDominanceRules1) {
952   // Checks that a dead continue cannot be added if it would prevent a block
953   // later in the loop from dominating the loop's continue construct, in the
954   // case where said block defines and id that is used in the loop's continue
955   // construct.
956 
957   std::string shader = R"(
958                OpCapability Shader
959           %1 = OpExtInstImport "GLSL.std.450"
960                OpMemoryModel Logical GLSL450
961                OpEntryPoint Fragment %4 "main"
962                OpExecutionMode %4 OriginUpperLeft
963                OpSource ESSL 310
964                OpName %4 "main"
965           %2 = OpTypeVoid
966           %3 = OpTypeFunction %2
967          %10 = OpTypeBool
968          %11 = OpConstantFalse %10
969           %4 = OpFunction %2 None %3
970           %5 = OpLabel
971                OpBranch %6
972           %6 = OpLabel
973                OpLoopMerge %8 %9 None
974                OpBranch %7
975           %7 = OpLabel
976          %21 = OpCopyObject %10 %11
977                OpBranch %9
978           %9 = OpLabel
979          %20 = OpPhi %10 %21 %7
980                OpBranchConditional %11 %6 %8
981           %8 = OpLabel
982                OpBranch %12
983          %12 = OpLabel
984                OpLoopMerge %14 %15 None
985                OpBranch %13
986          %13 = OpLabel
987                OpBranch %22
988          %22 = OpLabel
989          %23 = OpCopyObject %10 %11
990                OpBranch %25
991          %25 = OpLabel
992                OpBranch %15
993          %15 = OpLabel
994          %26 = OpCopyObject %10 %23
995                OpBranchConditional %11 %12 %14
996          %14 = OpLabel
997                OpReturn
998                OpFunctionEnd
999   )";
1000 
1001   const auto env = SPV_ENV_UNIVERSAL_1_3;
1002   const auto consumer = nullptr;
1003   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1004   spvtools::ValidatorOptions validator_options;
1005   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1006                                                kConsoleMessageConsumer));
1007   TransformationContext transformation_context(
1008       MakeUnique<FactManager>(context.get()), validator_options);
1009   // This transformation is not applicable because the dead continue from the
1010   // loop body prevents the definition of %23 later in the loop body from
1011   // dominating its use in the loop's continue target.
1012   auto bad_transformation = TransformationAddDeadContinue(13, false, {});
1013   ASSERT_FALSE(
1014       bad_transformation.IsApplicable(context.get(), transformation_context));
1015 
1016   auto good_transformation_1 = TransformationAddDeadContinue(7, false, {});
1017   ASSERT_TRUE(good_transformation_1.IsApplicable(context.get(),
1018                                                  transformation_context));
1019   ApplyAndCheckFreshIds(good_transformation_1, context.get(),
1020                         &transformation_context);
1021 
1022   auto good_transformation_2 = TransformationAddDeadContinue(22, false, {});
1023   ASSERT_TRUE(good_transformation_2.IsApplicable(context.get(),
1024                                                  transformation_context));
1025   ApplyAndCheckFreshIds(good_transformation_2, context.get(),
1026                         &transformation_context);
1027 
1028   // This transformation is OK, because the definition of %21 in the loop body
1029   // is only used in an OpPhi in the loop's continue target.
1030   auto good_transformation_3 = TransformationAddDeadContinue(6, false, {11});
1031   ASSERT_TRUE(good_transformation_3.IsApplicable(context.get(),
1032                                                  transformation_context));
1033   ApplyAndCheckFreshIds(good_transformation_3, context.get(),
1034                         &transformation_context);
1035 
1036   std::string after_transformations = R"(
1037                OpCapability Shader
1038           %1 = OpExtInstImport "GLSL.std.450"
1039                OpMemoryModel Logical GLSL450
1040                OpEntryPoint Fragment %4 "main"
1041                OpExecutionMode %4 OriginUpperLeft
1042                OpSource ESSL 310
1043                OpName %4 "main"
1044           %2 = OpTypeVoid
1045           %3 = OpTypeFunction %2
1046          %10 = OpTypeBool
1047          %11 = OpConstantFalse %10
1048           %4 = OpFunction %2 None %3
1049           %5 = OpLabel
1050                OpBranch %6
1051           %6 = OpLabel
1052                OpLoopMerge %8 %9 None
1053                OpBranchConditional %11 %9 %7
1054           %7 = OpLabel
1055          %21 = OpCopyObject %10 %11
1056                OpBranchConditional %11 %9 %9
1057           %9 = OpLabel
1058          %20 = OpPhi %10 %21 %7 %11 %6
1059                OpBranchConditional %11 %6 %8
1060           %8 = OpLabel
1061                OpBranch %12
1062          %12 = OpLabel
1063                OpLoopMerge %14 %15 None
1064                OpBranch %13
1065          %13 = OpLabel
1066                OpBranch %22
1067          %22 = OpLabel
1068          %23 = OpCopyObject %10 %11
1069                OpBranchConditional %11 %15 %25
1070          %25 = OpLabel
1071                OpBranch %15
1072          %15 = OpLabel
1073          %26 = OpCopyObject %10 %23
1074                OpBranchConditional %11 %12 %14
1075          %14 = OpLabel
1076                OpReturn
1077                OpFunctionEnd
1078   )";
1079 
1080   ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
1081 }
1082 
TEST(TransformationAddDeadContinueTest,RespectDominanceRules2)1083 TEST(TransformationAddDeadContinueTest, RespectDominanceRules2) {
1084   // Checks that a dead continue cannot be added if it would lead to a use after
1085   // the loop failing to be dominated by its definition.
1086 
1087   std::string shader = R"(
1088                OpCapability Shader
1089           %1 = OpExtInstImport "GLSL.std.450"
1090                OpMemoryModel Logical GLSL450
1091                OpEntryPoint Fragment %4 "main"
1092                OpExecutionMode %4 OriginUpperLeft
1093                OpSource ESSL 310
1094                OpName %4 "main"
1095           %2 = OpTypeVoid
1096           %3 = OpTypeFunction %2
1097          %10 = OpTypeBool
1098          %11 = OpConstantFalse %10
1099           %4 = OpFunction %2 None %3
1100           %5 = OpLabel
1101                OpBranch %100
1102         %100 = OpLabel
1103                OpLoopMerge %101 %102 None
1104                OpBranch %103
1105         %103 = OpLabel
1106         %200 = OpCopyObject %10 %11
1107                OpBranch %104
1108         %104 = OpLabel
1109                OpBranch %102
1110         %102 = OpLabel
1111                OpBranchConditional %11 %100 %101
1112         %101 = OpLabel
1113         %201 = OpCopyObject %10 %200
1114                OpReturn
1115                OpFunctionEnd
1116   )";
1117 
1118   const auto env = SPV_ENV_UNIVERSAL_1_3;
1119   const auto consumer = nullptr;
1120   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1121   spvtools::ValidatorOptions validator_options;
1122   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1123                                                kConsoleMessageConsumer));
1124   TransformationContext transformation_context(
1125       MakeUnique<FactManager>(context.get()), validator_options);
1126   // This transformation would shortcut the part of the loop body that defines
1127   // an id used after the loop.
1128   auto bad_transformation = TransformationAddDeadContinue(100, false, {});
1129   ASSERT_FALSE(
1130       bad_transformation.IsApplicable(context.get(), transformation_context));
1131 }
1132 
TEST(TransformationAddDeadContinueTest,RespectDominanceRules3)1133 TEST(TransformationAddDeadContinueTest, RespectDominanceRules3) {
1134   // Checks that a dead continue cannot be added if it would lead to a dominance
1135   // problem with an id used in an OpPhi after the loop.
1136 
1137   std::string shader = R"(
1138                OpCapability Shader
1139           %1 = OpExtInstImport "GLSL.std.450"
1140                OpMemoryModel Logical GLSL450
1141                OpEntryPoint Fragment %4 "main"
1142                OpExecutionMode %4 OriginUpperLeft
1143                OpSource ESSL 310
1144                OpName %4 "main"
1145           %2 = OpTypeVoid
1146           %3 = OpTypeFunction %2
1147          %10 = OpTypeBool
1148          %11 = OpConstantFalse %10
1149           %4 = OpFunction %2 None %3
1150           %5 = OpLabel
1151                OpBranch %100
1152         %100 = OpLabel
1153                OpLoopMerge %101 %102 None
1154                OpBranch %103
1155         %103 = OpLabel
1156         %200 = OpCopyObject %10 %11
1157                OpBranch %104
1158         %104 = OpLabel
1159                OpBranch %102
1160         %102 = OpLabel
1161                OpBranchConditional %11 %100 %101
1162         %101 = OpLabel
1163         %201 = OpPhi %10 %200 %102
1164                OpReturn
1165                OpFunctionEnd
1166   )";
1167 
1168   const auto env = SPV_ENV_UNIVERSAL_1_3;
1169   const auto consumer = nullptr;
1170   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1171   spvtools::ValidatorOptions validator_options;
1172   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1173                                                kConsoleMessageConsumer));
1174   TransformationContext transformation_context(
1175       MakeUnique<FactManager>(context.get()), validator_options);
1176   // This transformation would shortcut the part of the loop body that defines
1177   // an id used after the loop.
1178   auto bad_transformation = TransformationAddDeadContinue(100, false, {});
1179   ASSERT_FALSE(
1180       bad_transformation.IsApplicable(context.get(), transformation_context));
1181 }
1182 
TEST(TransformationAddDeadContinueTest,Miscellaneous1)1183 TEST(TransformationAddDeadContinueTest, Miscellaneous1) {
1184   // A miscellaneous test that exposed a bug in spirv-fuzz.
1185 
1186   std::string shader = R"(
1187                OpCapability Shader
1188           %1 = OpExtInstImport "GLSL.std.450"
1189                OpMemoryModel Logical GLSL450
1190                OpEntryPoint Fragment %4 "main" %586 %623
1191                OpExecutionMode %4 OriginUpperLeft
1192                OpSource ESSL 310
1193                OpMemberDecorate %34 0 Offset 0
1194                OpDecorate %34 Block
1195                OpDecorate %36 DescriptorSet 0
1196                OpDecorate %36 Binding 0
1197                OpDecorate %586 BuiltIn FragCoord
1198                OpMemberDecorate %591 0 Offset 0
1199                OpDecorate %591 Block
1200                OpDecorate %593 DescriptorSet 0
1201                OpDecorate %593 Binding 1
1202                OpDecorate %623 Location 0
1203           %2 = OpTypeVoid
1204           %3 = OpTypeFunction %2
1205           %6 = OpTypeInt 32 1
1206           %7 = OpTypePointer Function %6
1207           %9 = OpConstant %6 0
1208          %16 = OpConstant %6 2
1209          %17 = OpTypeBool
1210          %27 = OpTypeFloat 32
1211          %28 = OpTypeVector %27 2
1212          %29 = OpTypeMatrix %28 2
1213          %30 = OpTypePointer Private %29
1214          %31 = OpVariable %30 Private
1215          %34 = OpTypeStruct %27
1216          %35 = OpTypePointer Uniform %34
1217          %36 = OpVariable %35 Uniform
1218          %37 = OpTypePointer Uniform %27
1219          %40 = OpTypePointer Private %27
1220          %43 = OpConstant %6 1
1221          %62 = OpConstant %6 3
1222          %64 = OpTypeVector %27 3
1223          %65 = OpTypeMatrix %64 2
1224          %66 = OpTypePointer Private %65
1225          %67 = OpVariable %66 Private
1226          %92 = OpConstant %6 4
1227          %94 = OpTypeVector %27 4
1228          %95 = OpTypeMatrix %94 2
1229          %96 = OpTypePointer Private %95
1230          %97 = OpVariable %96 Private
1231         %123 = OpTypeMatrix %28 3
1232         %124 = OpTypePointer Private %123
1233         %125 = OpVariable %124 Private
1234         %151 = OpTypeMatrix %64 3
1235         %152 = OpTypePointer Private %151
1236         %153 = OpVariable %152 Private
1237         %179 = OpTypeMatrix %94 3
1238         %180 = OpTypePointer Private %179
1239         %181 = OpVariable %180 Private
1240         %207 = OpTypeMatrix %28 4
1241         %208 = OpTypePointer Private %207
1242         %209 = OpVariable %208 Private
1243         %235 = OpTypeMatrix %64 4
1244         %236 = OpTypePointer Private %235
1245         %237 = OpVariable %236 Private
1246         %263 = OpTypeMatrix %94 4
1247         %264 = OpTypePointer Private %263
1248         %265 = OpVariable %264 Private
1249         %275 = OpTypeInt 32 0
1250         %276 = OpConstant %275 9
1251         %277 = OpTypeArray %27 %276
1252         %278 = OpTypePointer Function %277
1253         %280 = OpConstant %27 0
1254         %281 = OpTypePointer Function %27
1255         %311 = OpConstant %27 16
1256         %448 = OpConstant %6 5
1257         %482 = OpConstant %6 6
1258         %516 = OpConstant %6 7
1259         %550 = OpConstant %6 8
1260         %585 = OpTypePointer Input %94
1261         %586 = OpVariable %585 Input
1262         %587 = OpConstant %275 0
1263         %588 = OpTypePointer Input %27
1264         %591 = OpTypeStruct %28
1265         %592 = OpTypePointer Uniform %591
1266         %593 = OpVariable %592 Uniform
1267         %596 = OpConstant %27 3
1268         %601 = OpConstant %275 1
1269         %617 = OpConstant %6 9
1270         %622 = OpTypePointer Output %94
1271         %623 = OpVariable %622 Output
1272         %628 = OpConstant %27 1
1273         %634 = OpConstantComposite %94 %280 %280 %280 %628
1274         %635 = OpUndef %6
1275         %636 = OpUndef %17
1276         %637 = OpUndef %27
1277         %638 = OpUndef %64
1278         %639 = OpUndef %94
1279         %640 = OpConstantTrue %17
1280         %736 = OpConstantFalse %17
1281         %642 = OpVariable %37 Uniform
1282         %643 = OpVariable %40 Private
1283           %4 = OpFunction %2 None %3
1284           %5 = OpLabel
1285                OpBranch %164
1286         %164 = OpLabel
1287                OpLoopMerge %166 %167 None
1288                OpBranch %165
1289         %165 = OpLabel
1290                OpBranch %172
1291         %172 = OpLabel
1292                OpSelectionMerge %174 None
1293                OpBranchConditional %640 %174 %174
1294         %174 = OpLabel
1295         %785 = OpCopyObject %6 %43
1296                OpBranch %167
1297         %167 = OpLabel
1298         %190 = OpIAdd %6 %9 %785
1299                OpBranchConditional %640 %164 %166
1300         %166 = OpLabel
1301                OpBranch %196
1302         %196 = OpLabel
1303                OpBranch %194
1304         %194 = OpLabel
1305                OpReturn
1306                OpFunctionEnd
1307   )";
1308 
1309   const auto env = SPV_ENV_UNIVERSAL_1_3;
1310   const auto consumer = nullptr;
1311   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1312   spvtools::ValidatorOptions validator_options;
1313   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1314                                                kConsoleMessageConsumer));
1315   TransformationContext transformation_context(
1316       MakeUnique<FactManager>(context.get()), validator_options);
1317   // This transformation would shortcut the part of the loop body that defines
1318   // an id used in the continue target.
1319   auto bad_transformation = TransformationAddDeadContinue(165, false, {});
1320   ASSERT_FALSE(
1321       bad_transformation.IsApplicable(context.get(), transformation_context));
1322 }
1323 
TEST(TransformationAddDeadContinueTest,Miscellaneous2)1324 TEST(TransformationAddDeadContinueTest, Miscellaneous2) {
1325   // A miscellaneous test that exposed a bug in spirv-fuzz.
1326 
1327   std::string shader = R"(
1328                OpCapability Shader
1329           %1 = OpExtInstImport "GLSL.std.450"
1330                OpMemoryModel Logical GLSL450
1331                OpEntryPoint Fragment %4 "main"
1332                OpExecutionMode %4 OriginUpperLeft
1333                OpSource ESSL 310
1334           %2 = OpTypeVoid
1335           %3 = OpTypeFunction %2
1336          %51 = OpTypeBool
1337         %395 = OpConstantTrue %51
1338           %4 = OpFunction %2 None %3
1339           %5 = OpLabel
1340                OpBranch %389
1341         %389 = OpLabel
1342                OpLoopMerge %388 %391 None
1343                OpBranch %339
1344         %339 = OpLabel
1345                OpSelectionMerge %396 None
1346                OpBranchConditional %395 %388 %396
1347         %396 = OpLabel
1348                OpBranch %1552
1349        %1552 = OpLabel
1350                OpLoopMerge %1553 %1554 None
1351                OpBranch %1556
1352        %1556 = OpLabel
1353                OpLoopMerge %1557 %1570 None
1354                OpBranchConditional %395 %1562 %1557
1355        %1562 = OpLabel
1356                OpBranchConditional %395 %1571 %1570
1357        %1571 = OpLabel
1358                OpBranch %1557
1359        %1570 = OpLabel
1360                OpBranch %1556
1361        %1557 = OpLabel
1362                OpSelectionMerge %1586 None
1363                OpBranchConditional %395 %1553 %1586
1364        %1586 = OpLabel
1365                OpBranch %1553
1366        %1554 = OpLabel
1367                OpBranch %1552
1368        %1553 = OpLabel
1369                OpBranch %388
1370         %391 = OpLabel
1371                OpBranch %389
1372         %388 = OpLabel
1373                OpReturn
1374                OpFunctionEnd
1375   )";
1376 
1377   const auto env = SPV_ENV_UNIVERSAL_1_3;
1378   const auto consumer = nullptr;
1379   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1380   spvtools::ValidatorOptions validator_options;
1381   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1382                                                kConsoleMessageConsumer));
1383   TransformationContext transformation_context(
1384       MakeUnique<FactManager>(context.get()), validator_options);
1385   // This transformation would introduce a branch from a continue target to
1386   // itself.
1387   auto bad_transformation = TransformationAddDeadContinue(1554, true, {});
1388   ASSERT_FALSE(
1389       bad_transformation.IsApplicable(context.get(), transformation_context));
1390 }
1391 
TEST(TransformationAddDeadContinueTest,Miscellaneous3)1392 TEST(TransformationAddDeadContinueTest, Miscellaneous3) {
1393   // A miscellaneous test that exposed a bug in spirv-fuzz.
1394 
1395   std::string shader = R"(
1396                OpCapability Shader
1397           %1 = OpExtInstImport "GLSL.std.450"
1398                OpMemoryModel Logical GLSL450
1399                OpEntryPoint Fragment %4 "main"
1400                OpExecutionMode %4 OriginUpperLeft
1401                OpSource ESSL 310
1402           %2 = OpTypeVoid
1403           %3 = OpTypeFunction %2
1404          %85 = OpTypeBool
1405         %434 = OpConstantFalse %85
1406           %4 = OpFunction %2 None %3
1407           %5 = OpLabel
1408                OpBranch %234
1409         %234 = OpLabel
1410                OpLoopMerge %235 %236 None
1411                OpBranch %259
1412         %259 = OpLabel
1413                OpLoopMerge %260 %274 None
1414                OpBranchConditional %434 %265 %260
1415         %265 = OpLabel
1416                OpBranch %275
1417         %275 = OpLabel
1418                OpBranch %260
1419         %274 = OpLabel
1420                OpBranch %259
1421         %260 = OpLabel
1422                OpSelectionMerge %298 None
1423                OpBranchConditional %434 %299 %300
1424         %300 = OpLabel
1425                OpBranch %235
1426         %298 = OpLabel
1427                OpUnreachable
1428         %236 = OpLabel
1429                OpBranch %234
1430         %299 = OpLabel
1431                OpBranch %235
1432         %235 = OpLabel
1433                OpReturn
1434                OpFunctionEnd
1435   )";
1436 
1437   const auto env = SPV_ENV_UNIVERSAL_1_3;
1438   const auto consumer = nullptr;
1439   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1440   spvtools::ValidatorOptions validator_options;
1441   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1442                                                kConsoleMessageConsumer));
1443   TransformationContext transformation_context(
1444       MakeUnique<FactManager>(context.get()), validator_options);
1445   auto bad_transformation = TransformationAddDeadContinue(299, false, {});
1446 
1447   // The continue edge would connect %299 to the previously-unreachable %236,
1448   // making %299 dominate %236, and breaking the rule that block ordering must
1449   // respect dominance.
1450   ASSERT_FALSE(
1451       bad_transformation.IsApplicable(context.get(), transformation_context));
1452 }
1453 
TEST(TransformationAddDeadContinueTest,Miscellaneous4)1454 TEST(TransformationAddDeadContinueTest, Miscellaneous4) {
1455   // A miscellaneous test that exposed a bug in spirv-fuzz.
1456 
1457   std::string shader = R"(
1458                OpCapability Shader
1459           %1 = OpExtInstImport "GLSL.std.450"
1460                OpMemoryModel Logical GLSL450
1461                OpEntryPoint Fragment %4 "main"
1462                OpExecutionMode %4 OriginUpperLeft
1463                OpSource ESSL 310
1464                OpName %4 "main"
1465                OpName %8 "i"
1466           %2 = OpTypeVoid
1467           %3 = OpTypeFunction %2
1468           %6 = OpTypeInt 32 1
1469           %7 = OpTypePointer Function %6
1470           %9 = OpConstant %6 0
1471          %16 = OpConstant %6 100
1472          %17 = OpTypeBool
1473         %100 = OpConstantFalse %17
1474          %21 = OpConstant %6 1
1475           %4 = OpFunction %2 None %3
1476           %5 = OpLabel
1477           %8 = OpVariable %7 Function
1478                OpStore %8 %9
1479                OpBranch %10
1480          %13 = OpLabel
1481          %20 = OpLoad %6 %8
1482          %22 = OpIAdd %6 %20 %21
1483                OpStore %8 %22
1484                OpBranch %10
1485          %10 = OpLabel
1486                OpLoopMerge %12 %13 None
1487                OpBranch %14
1488          %14 = OpLabel
1489          %15 = OpLoad %6 %8
1490          %18 = OpSLessThan %17 %15 %16
1491                OpBranchConditional %18 %11 %12
1492          %11 = OpLabel
1493                OpBranch %12
1494          %12 = OpLabel
1495                OpReturn
1496                OpFunctionEnd
1497   )";
1498 
1499   const auto env = SPV_ENV_UNIVERSAL_1_3;
1500   const auto consumer = nullptr;
1501   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1502   spvtools::ValidatorOptions validator_options;
1503   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1504                                                kConsoleMessageConsumer));
1505   TransformationContext transformation_context(
1506       MakeUnique<FactManager>(context.get()), validator_options);
1507   auto bad_transformation = TransformationAddDeadContinue(10, false, {});
1508 
1509   // The continue edge would connect %10 to the previously-unreachable %13,
1510   // making %10 dominate %13, and breaking the rule that block ordering must
1511   // respect dominance.
1512   ASSERT_FALSE(
1513       bad_transformation.IsApplicable(context.get(), transformation_context));
1514 }
1515 
TEST(TransformationAddDeadContinueTest,Miscellaneous5)1516 TEST(TransformationAddDeadContinueTest, Miscellaneous5) {
1517   // A miscellaneous test that exposed a bug in spirv-fuzz.
1518 
1519   std::string shader = R"(
1520                OpCapability Shader
1521           %1 = OpExtInstImport "GLSL.std.450"
1522                OpMemoryModel Logical GLSL450
1523                OpEntryPoint Fragment %4 "main"
1524                OpExecutionMode %4 OriginUpperLeft
1525                OpSource ESSL 310
1526           %2 = OpTypeVoid
1527           %3 = OpTypeFunction %2
1528           %6 = OpTypeBool
1529           %7 = OpTypePointer Function %6
1530           %9 = OpConstantTrue %6
1531           %4 = OpFunction %2 None %3
1532           %5 = OpLabel
1533                OpBranch %98
1534          %98 = OpLabel
1535                OpLoopMerge %100 %101 None
1536                OpBranch %99
1537          %99 = OpLabel
1538                OpSelectionMerge %111 None
1539                OpBranchConditional %9 %110 %111
1540         %110 = OpLabel
1541                OpBranch %100
1542         %111 = OpLabel
1543         %200 = OpCopyObject %6 %9
1544                OpBranch %101
1545         %101 = OpLabel
1546         %201 = OpCopyObject %6 %200
1547                OpBranchConditional %9 %98 %100
1548         %100 = OpLabel
1549                OpReturn
1550                OpFunctionEnd
1551   )";
1552 
1553   const auto env = SPV_ENV_UNIVERSAL_1_3;
1554   const auto consumer = nullptr;
1555   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1556   spvtools::ValidatorOptions validator_options;
1557   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1558                                                kConsoleMessageConsumer));
1559   TransformationContext transformation_context(
1560       MakeUnique<FactManager>(context.get()), validator_options);
1561   auto bad_transformation = TransformationAddDeadContinue(110, true, {});
1562 
1563   // The continue edge would lead to the use of %200 in block %101 no longer
1564   // being dominated by its definition in block %111.
1565   ASSERT_FALSE(
1566       bad_transformation.IsApplicable(context.get(), transformation_context));
1567 }
1568 
TEST(TransformationAddDeadContinueTest,Miscellaneous6)1569 TEST(TransformationAddDeadContinueTest, Miscellaneous6) {
1570   // A miscellaneous test that exposed a bug in spirv-fuzz.
1571 
1572   std::string shader = R"(
1573                OpCapability Shader
1574           %1 = OpExtInstImport "GLSL.std.450"
1575                OpMemoryModel Logical GLSL450
1576                OpEntryPoint Fragment %4 "main"
1577                OpExecutionMode %4 OriginUpperLeft
1578                OpSource ESSL 310
1579           %2 = OpTypeVoid
1580           %3 = OpTypeFunction %2
1581           %6 = OpTypeBool
1582           %9 = OpConstantTrue %6
1583           %4 = OpFunction %2 None %3
1584           %5 = OpLabel
1585                OpBranch %10
1586          %10 = OpLabel
1587                OpLoopMerge %13 %12 None
1588                OpBranch %11
1589          %11 = OpLabel
1590          %20 = OpCopyObject %6 %9
1591                OpBranch %12
1592          %12 = OpLabel
1593                OpBranchConditional %9 %10 %13
1594          %13 = OpLabel
1595          %21 = OpCopyObject %6 %20
1596                OpReturn
1597                OpFunctionEnd
1598   )";
1599 
1600   const auto env = SPV_ENV_UNIVERSAL_1_3;
1601   const auto consumer = nullptr;
1602   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1603   spvtools::ValidatorOptions validator_options;
1604   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1605                                                kConsoleMessageConsumer));
1606   TransformationContext transformation_context(
1607       MakeUnique<FactManager>(context.get()), validator_options);
1608   auto bad_transformation = TransformationAddDeadContinue(10, true, {});
1609 
1610   ASSERT_FALSE(
1611       bad_transformation.IsApplicable(context.get(), transformation_context));
1612 }
1613 
1614 }  // namespace
1615 }  // namespace fuzz
1616 }  // namespace spvtools
1617