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