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_break.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(TransformationAddDeadBreakTest,BreaksOutOfSimpleIf)25 TEST(TransformationAddDeadBreakTest, BreaksOutOfSimpleIf) {
26   // For a simple if-then-else, checks that some dead break scenarios are
27   // possible, and that some invalid scenarios are indeed not allowed.
28 
29   // The SPIR-V for this test is adapted from the following GLSL, by separating
30   // some assignments into their own basic blocks, and adding constants for true
31   // and false:
32   //
33   // void main() {
34   //   int x;
35   //   int y;
36   //   x = 1;
37   //   if (x < y) {
38   //     x = 2;
39   //     x = 3;
40   //   } else {
41   //     y = 2;
42   //     y = 3;
43   //   }
44   //   x = y;
45   // }
46 
47   std::string shader = R"(
48                OpCapability Shader
49           %1 = OpExtInstImport "GLSL.std.450"
50                OpMemoryModel Logical GLSL450
51                OpEntryPoint Fragment %4 "main"
52                OpExecutionMode %4 OriginUpperLeft
53                OpSource ESSL 310
54                OpName %4 "main"
55                OpName %8 "x"
56                OpName %11 "y"
57           %2 = OpTypeVoid
58           %3 = OpTypeFunction %2
59           %6 = OpTypeInt 32 1
60           %7 = OpTypePointer Function %6
61           %9 = OpConstant %6 1
62          %13 = OpTypeBool
63          %17 = OpConstant %6 2
64          %18 = OpConstant %6 3
65          %25 = OpConstantTrue %13
66          %26 = OpConstantFalse %13
67           %4 = OpFunction %2 None %3
68           %5 = OpLabel
69           %8 = OpVariable %7 Function
70          %11 = OpVariable %7 Function
71                OpStore %8 %9
72          %10 = OpLoad %6 %8
73          %12 = OpLoad %6 %11
74          %14 = OpSLessThan %13 %10 %12
75                OpSelectionMerge %16 None
76                OpBranchConditional %14 %15 %19
77          %15 = OpLabel
78                OpStore %8 %17
79                OpBranch %21
80          %21 = OpLabel
81                OpStore %8 %18
82                OpBranch %22
83          %22 = OpLabel
84                OpBranch %16
85          %19 = OpLabel
86                OpStore %11 %17
87                OpBranch %23
88          %23 = OpLabel
89                OpStore %11 %18
90                OpBranch %24
91          %24 = OpLabel
92                OpBranch %16
93          %16 = OpLabel
94          %20 = OpLoad %6 %11
95                OpStore %8 %20
96                OpReturn
97                OpFunctionEnd
98   )";
99 
100   const auto env = SPV_ENV_UNIVERSAL_1_3;
101   const auto consumer = nullptr;
102   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
103   spvtools::ValidatorOptions validator_options;
104   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
105                                                kConsoleMessageConsumer));
106   TransformationContext transformation_context(
107       MakeUnique<FactManager>(context.get()), validator_options);
108   const uint32_t merge_block = 16;
109 
110   // These are all possibilities.
111   ASSERT_TRUE(TransformationAddDeadBreak(15, merge_block, true, {})
112                   .IsApplicable(context.get(), transformation_context));
113   ASSERT_TRUE(TransformationAddDeadBreak(15, merge_block, false, {})
114                   .IsApplicable(context.get(), transformation_context));
115   ASSERT_TRUE(TransformationAddDeadBreak(21, merge_block, true, {})
116                   .IsApplicable(context.get(), transformation_context));
117   ASSERT_TRUE(TransformationAddDeadBreak(21, merge_block, false, {})
118                   .IsApplicable(context.get(), transformation_context));
119   ASSERT_TRUE(TransformationAddDeadBreak(22, merge_block, true, {})
120                   .IsApplicable(context.get(), transformation_context));
121   ASSERT_TRUE(TransformationAddDeadBreak(22, merge_block, false, {})
122                   .IsApplicable(context.get(), transformation_context));
123   ASSERT_TRUE(TransformationAddDeadBreak(19, merge_block, true, {})
124                   .IsApplicable(context.get(), transformation_context));
125   ASSERT_TRUE(TransformationAddDeadBreak(19, merge_block, false, {})
126                   .IsApplicable(context.get(), transformation_context));
127   ASSERT_TRUE(TransformationAddDeadBreak(23, merge_block, true, {})
128                   .IsApplicable(context.get(), transformation_context));
129   ASSERT_TRUE(TransformationAddDeadBreak(23, merge_block, false, {})
130                   .IsApplicable(context.get(), transformation_context));
131   ASSERT_TRUE(TransformationAddDeadBreak(24, merge_block, true, {})
132                   .IsApplicable(context.get(), transformation_context));
133   ASSERT_TRUE(TransformationAddDeadBreak(24, merge_block, false, {})
134                   .IsApplicable(context.get(), transformation_context));
135 
136   // Inapplicable: 100 is not a block id.
137   ASSERT_FALSE(TransformationAddDeadBreak(100, merge_block, true, {})
138                    .IsApplicable(context.get(), transformation_context));
139   ASSERT_FALSE(TransformationAddDeadBreak(15, 100, true, {})
140                    .IsApplicable(context.get(), transformation_context));
141 
142   // Inapplicable: 24 is not a merge block.
143   ASSERT_FALSE(TransformationAddDeadBreak(15, 24, true, {})
144                    .IsApplicable(context.get(), transformation_context));
145 
146   // These are the transformations we will apply.
147   auto transformation1 = TransformationAddDeadBreak(15, merge_block, true, {});
148   auto transformation2 = TransformationAddDeadBreak(21, merge_block, false, {});
149   auto transformation3 = TransformationAddDeadBreak(22, merge_block, true, {});
150   auto transformation4 = TransformationAddDeadBreak(19, merge_block, false, {});
151   auto transformation5 = TransformationAddDeadBreak(23, merge_block, true, {});
152   auto transformation6 = TransformationAddDeadBreak(24, merge_block, false, {});
153 
154   ASSERT_TRUE(
155       transformation1.IsApplicable(context.get(), transformation_context));
156   ApplyAndCheckFreshIds(transformation1, context.get(),
157                         &transformation_context);
158   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
159                                                kConsoleMessageConsumer));
160 
161   ASSERT_TRUE(
162       transformation2.IsApplicable(context.get(), transformation_context));
163   ApplyAndCheckFreshIds(transformation2, context.get(),
164                         &transformation_context);
165   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
166                                                kConsoleMessageConsumer));
167 
168   ASSERT_TRUE(
169       transformation3.IsApplicable(context.get(), transformation_context));
170   ApplyAndCheckFreshIds(transformation3, context.get(),
171                         &transformation_context);
172   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
173                                                kConsoleMessageConsumer));
174 
175   ASSERT_TRUE(
176       transformation4.IsApplicable(context.get(), transformation_context));
177   ApplyAndCheckFreshIds(transformation4, context.get(),
178                         &transformation_context);
179   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
180                                                kConsoleMessageConsumer));
181 
182   ASSERT_TRUE(
183       transformation5.IsApplicable(context.get(), transformation_context));
184   ApplyAndCheckFreshIds(transformation5, context.get(),
185                         &transformation_context);
186   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
187                                                kConsoleMessageConsumer));
188 
189   ASSERT_TRUE(
190       transformation6.IsApplicable(context.get(), transformation_context));
191   ApplyAndCheckFreshIds(transformation6, context.get(),
192                         &transformation_context);
193   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
194                                                kConsoleMessageConsumer));
195 
196   std::string after_transformation = R"(
197                OpCapability Shader
198           %1 = OpExtInstImport "GLSL.std.450"
199                OpMemoryModel Logical GLSL450
200                OpEntryPoint Fragment %4 "main"
201                OpExecutionMode %4 OriginUpperLeft
202                OpSource ESSL 310
203                OpName %4 "main"
204                OpName %8 "x"
205                OpName %11 "y"
206           %2 = OpTypeVoid
207           %3 = OpTypeFunction %2
208           %6 = OpTypeInt 32 1
209           %7 = OpTypePointer Function %6
210           %9 = OpConstant %6 1
211          %13 = OpTypeBool
212          %17 = OpConstant %6 2
213          %18 = OpConstant %6 3
214          %25 = OpConstantTrue %13
215          %26 = OpConstantFalse %13
216           %4 = OpFunction %2 None %3
217           %5 = OpLabel
218           %8 = OpVariable %7 Function
219          %11 = OpVariable %7 Function
220                OpStore %8 %9
221          %10 = OpLoad %6 %8
222          %12 = OpLoad %6 %11
223          %14 = OpSLessThan %13 %10 %12
224                OpSelectionMerge %16 None
225                OpBranchConditional %14 %15 %19
226          %15 = OpLabel
227                OpStore %8 %17
228                OpBranchConditional %25 %21 %16
229          %21 = OpLabel
230                OpStore %8 %18
231                OpBranchConditional %26 %16 %22
232          %22 = OpLabel
233                OpBranchConditional %25 %16 %16
234          %19 = OpLabel
235                OpStore %11 %17
236                OpBranchConditional %26 %16 %23
237          %23 = OpLabel
238                OpStore %11 %18
239                OpBranchConditional %25 %24 %16
240          %24 = OpLabel
241                OpBranchConditional %26 %16 %16
242          %16 = OpLabel
243          %20 = OpLoad %6 %11
244                OpStore %8 %20
245                OpReturn
246                OpFunctionEnd
247   )";
248 
249   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
250 }
251 
TEST(TransformationAddDeadBreakTest,BreakOutOfNestedIfs)252 TEST(TransformationAddDeadBreakTest, BreakOutOfNestedIfs) {
253   // Checks some allowed and disallowed scenarios for nests of ifs.
254 
255   // The SPIR-V for this test is adapted from the following GLSL:
256   //
257   // void main() {
258   //   int x;
259   //   int y;
260   //   x = 1;
261   //   if (x < y) {
262   //     x = 2;
263   //     x = 3;
264   //     if (x == y) {
265   //       y = 3;
266   //     }
267   //   } else {
268   //     y = 2;
269   //     y = 3;
270   //   }
271   //   if (x == y) {
272   //     x = 2;
273   //   }
274   //   x = y;
275   // }
276 
277   std::string shader = R"(
278                OpCapability Shader
279           %1 = OpExtInstImport "GLSL.std.450"
280                OpMemoryModel Logical GLSL450
281                OpEntryPoint Fragment %4 "main"
282                OpExecutionMode %4 OriginUpperLeft
283                OpSource ESSL 310
284                OpName %4 "main"
285                OpName %8 "x"
286                OpName %11 "y"
287           %2 = OpTypeVoid
288           %3 = OpTypeFunction %2
289           %6 = OpTypeInt 32 1
290           %7 = OpTypePointer Function %6
291           %9 = OpConstant %6 1
292          %13 = OpTypeBool
293          %17 = OpConstant %6 2
294          %18 = OpConstant %6 3
295          %31 = OpConstantTrue %13
296          %32 = OpConstantFalse %13
297           %4 = OpFunction %2 None %3
298           %5 = OpLabel
299           %8 = OpVariable %7 Function
300          %11 = OpVariable %7 Function
301                OpStore %8 %9
302          %10 = OpLoad %6 %8
303          %12 = OpLoad %6 %11
304          %14 = OpSLessThan %13 %10 %12
305                OpSelectionMerge %16 None
306                OpBranchConditional %14 %15 %24
307          %15 = OpLabel
308                OpStore %8 %17
309                OpBranch %33
310          %33 = OpLabel
311                OpStore %8 %18
312          %19 = OpLoad %6 %8
313                OpBranch %34
314          %34 = OpLabel
315          %20 = OpLoad %6 %11
316          %21 = OpIEqual %13 %19 %20
317                OpSelectionMerge %23 None
318                OpBranchConditional %21 %22 %23
319          %22 = OpLabel
320                OpStore %11 %18
321                OpBranch %35
322          %35 = OpLabel
323                OpBranch %23
324          %23 = OpLabel
325                OpBranch %16
326          %24 = OpLabel
327                OpStore %11 %17
328                OpBranch %36
329          %36 = OpLabel
330                OpStore %11 %18
331                OpBranch %16
332          %16 = OpLabel
333          %25 = OpLoad %6 %8
334                OpBranch %37
335          %37 = OpLabel
336          %26 = OpLoad %6 %11
337          %27 = OpIEqual %13 %25 %26
338                OpSelectionMerge %29 None
339                OpBranchConditional %27 %28 %29
340          %28 = OpLabel
341                OpStore %8 %17
342                OpBranch %38
343          %38 = OpLabel
344                OpBranch %29
345          %29 = OpLabel
346          %30 = OpLoad %6 %11
347                OpStore %8 %30
348                OpReturn
349                OpFunctionEnd
350   )";
351 
352   const auto env = SPV_ENV_UNIVERSAL_1_3;
353   const auto consumer = nullptr;
354   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
355   spvtools::ValidatorOptions validator_options;
356   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
357                                                kConsoleMessageConsumer));
358   TransformationContext transformation_context(
359       MakeUnique<FactManager>(context.get()), validator_options);
360   // The header and merge blocks
361   const uint32_t header_inner = 34;
362   const uint32_t merge_inner = 23;
363   const uint32_t header_outer = 5;
364   const uint32_t merge_outer = 16;
365   const uint32_t header_after = 37;
366   const uint32_t merge_after = 29;
367 
368   // The non-merge-nor-header blocks in each construct
369   const uint32_t inner_block_1 = 22;
370   const uint32_t inner_block_2 = 35;
371   const uint32_t outer_block_1 = 15;
372   const uint32_t outer_block_2 = 33;
373   const uint32_t outer_block_3 = 24;
374   const uint32_t outer_block_4 = 36;
375   const uint32_t after_block_1 = 28;
376   const uint32_t after_block_2 = 38;
377 
378   // Fine to break from a construct to its merge
379   ASSERT_TRUE(TransformationAddDeadBreak(inner_block_1, merge_inner, true, {})
380                   .IsApplicable(context.get(), transformation_context));
381   ASSERT_TRUE(TransformationAddDeadBreak(inner_block_2, merge_inner, false, {})
382                   .IsApplicable(context.get(), transformation_context));
383   ASSERT_TRUE(TransformationAddDeadBreak(outer_block_1, merge_outer, true, {})
384                   .IsApplicable(context.get(), transformation_context));
385   ASSERT_TRUE(TransformationAddDeadBreak(outer_block_2, merge_outer, false, {})
386                   .IsApplicable(context.get(), transformation_context));
387   ASSERT_TRUE(TransformationAddDeadBreak(outer_block_3, merge_outer, true, {})
388                   .IsApplicable(context.get(), transformation_context));
389   ASSERT_TRUE(TransformationAddDeadBreak(outer_block_4, merge_outer, false, {})
390                   .IsApplicable(context.get(), transformation_context));
391   ASSERT_TRUE(TransformationAddDeadBreak(after_block_1, merge_after, true, {})
392                   .IsApplicable(context.get(), transformation_context));
393   ASSERT_TRUE(TransformationAddDeadBreak(after_block_2, merge_after, false, {})
394                   .IsApplicable(context.get(), transformation_context));
395 
396   // Not OK to break to the wrong merge (whether enclosing or not)
397   ASSERT_FALSE(TransformationAddDeadBreak(inner_block_1, merge_outer, true, {})
398                    .IsApplicable(context.get(), transformation_context));
399   ASSERT_FALSE(TransformationAddDeadBreak(inner_block_2, merge_after, false, {})
400                    .IsApplicable(context.get(), transformation_context));
401   ASSERT_FALSE(TransformationAddDeadBreak(outer_block_1, merge_inner, true, {})
402                    .IsApplicable(context.get(), transformation_context));
403   ASSERT_FALSE(TransformationAddDeadBreak(outer_block_2, merge_after, false, {})
404                    .IsApplicable(context.get(), transformation_context));
405   ASSERT_FALSE(TransformationAddDeadBreak(after_block_1, merge_inner, true, {})
406                    .IsApplicable(context.get(), transformation_context));
407   ASSERT_FALSE(TransformationAddDeadBreak(after_block_2, merge_outer, false, {})
408                    .IsApplicable(context.get(), transformation_context));
409 
410   // Not OK to break from header (as it does not branch unconditionally)
411   ASSERT_FALSE(TransformationAddDeadBreak(header_inner, merge_inner, true, {})
412                    .IsApplicable(context.get(), transformation_context));
413   ASSERT_FALSE(TransformationAddDeadBreak(header_outer, merge_outer, false, {})
414                    .IsApplicable(context.get(), transformation_context));
415   ASSERT_FALSE(TransformationAddDeadBreak(header_after, merge_after, true, {})
416                    .IsApplicable(context.get(), transformation_context));
417 
418   // Not OK to break to non-merge
419   ASSERT_FALSE(
420       TransformationAddDeadBreak(inner_block_1, inner_block_2, true, {})
421           .IsApplicable(context.get(), transformation_context));
422   ASSERT_FALSE(
423       TransformationAddDeadBreak(outer_block_2, after_block_1, false, {})
424           .IsApplicable(context.get(), transformation_context));
425   ASSERT_FALSE(TransformationAddDeadBreak(outer_block_1, header_after, true, {})
426                    .IsApplicable(context.get(), transformation_context));
427 
428   auto transformation1 =
429       TransformationAddDeadBreak(inner_block_1, merge_inner, true, {});
430   auto transformation2 =
431       TransformationAddDeadBreak(inner_block_2, merge_inner, false, {});
432   auto transformation3 =
433       TransformationAddDeadBreak(outer_block_1, merge_outer, true, {});
434   auto transformation4 =
435       TransformationAddDeadBreak(outer_block_2, merge_outer, false, {});
436   auto transformation5 =
437       TransformationAddDeadBreak(outer_block_3, merge_outer, true, {});
438   auto transformation6 =
439       TransformationAddDeadBreak(outer_block_4, merge_outer, false, {});
440   auto transformation7 =
441       TransformationAddDeadBreak(after_block_1, merge_after, true, {});
442   auto transformation8 =
443       TransformationAddDeadBreak(after_block_2, merge_after, false, {});
444 
445   ASSERT_TRUE(
446       transformation1.IsApplicable(context.get(), transformation_context));
447   ApplyAndCheckFreshIds(transformation1, context.get(),
448                         &transformation_context);
449   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
450                                                kConsoleMessageConsumer));
451 
452   ASSERT_TRUE(
453       transformation2.IsApplicable(context.get(), transformation_context));
454   ApplyAndCheckFreshIds(transformation2, context.get(),
455                         &transformation_context);
456   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
457                                                kConsoleMessageConsumer));
458 
459   ASSERT_TRUE(
460       transformation3.IsApplicable(context.get(), transformation_context));
461   ApplyAndCheckFreshIds(transformation3, context.get(),
462                         &transformation_context);
463   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
464                                                kConsoleMessageConsumer));
465 
466   ASSERT_TRUE(
467       transformation4.IsApplicable(context.get(), transformation_context));
468   ApplyAndCheckFreshIds(transformation4, context.get(),
469                         &transformation_context);
470   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
471                                                kConsoleMessageConsumer));
472 
473   ASSERT_TRUE(
474       transformation5.IsApplicable(context.get(), transformation_context));
475   ApplyAndCheckFreshIds(transformation5, context.get(),
476                         &transformation_context);
477   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
478                                                kConsoleMessageConsumer));
479 
480   ASSERT_TRUE(
481       transformation6.IsApplicable(context.get(), transformation_context));
482   ApplyAndCheckFreshIds(transformation6, context.get(),
483                         &transformation_context);
484   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
485                                                kConsoleMessageConsumer));
486 
487   ASSERT_TRUE(
488       transformation7.IsApplicable(context.get(), transformation_context));
489   ApplyAndCheckFreshIds(transformation7, context.get(),
490                         &transformation_context);
491   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
492                                                kConsoleMessageConsumer));
493 
494   ASSERT_TRUE(
495       transformation8.IsApplicable(context.get(), transformation_context));
496   ApplyAndCheckFreshIds(transformation8, context.get(),
497                         &transformation_context);
498   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
499                                                kConsoleMessageConsumer));
500 
501   std::string after_transformation = R"(
502                OpCapability Shader
503           %1 = OpExtInstImport "GLSL.std.450"
504                OpMemoryModel Logical GLSL450
505                OpEntryPoint Fragment %4 "main"
506                OpExecutionMode %4 OriginUpperLeft
507                OpSource ESSL 310
508                OpName %4 "main"
509                OpName %8 "x"
510                OpName %11 "y"
511           %2 = OpTypeVoid
512           %3 = OpTypeFunction %2
513           %6 = OpTypeInt 32 1
514           %7 = OpTypePointer Function %6
515           %9 = OpConstant %6 1
516          %13 = OpTypeBool
517          %17 = OpConstant %6 2
518          %18 = OpConstant %6 3
519          %31 = OpConstantTrue %13
520          %32 = OpConstantFalse %13
521           %4 = OpFunction %2 None %3
522           %5 = OpLabel
523           %8 = OpVariable %7 Function
524          %11 = OpVariable %7 Function
525                OpStore %8 %9
526          %10 = OpLoad %6 %8
527          %12 = OpLoad %6 %11
528          %14 = OpSLessThan %13 %10 %12
529                OpSelectionMerge %16 None
530                OpBranchConditional %14 %15 %24
531          %15 = OpLabel
532                OpStore %8 %17
533                OpBranchConditional %31 %33 %16
534          %33 = OpLabel
535                OpStore %8 %18
536          %19 = OpLoad %6 %8
537                OpBranchConditional %32 %16 %34
538          %34 = OpLabel
539          %20 = OpLoad %6 %11
540          %21 = OpIEqual %13 %19 %20
541                OpSelectionMerge %23 None
542                OpBranchConditional %21 %22 %23
543          %22 = OpLabel
544                OpStore %11 %18
545                OpBranchConditional %31 %35 %23
546          %35 = OpLabel
547                OpBranchConditional %32 %23 %23
548          %23 = OpLabel
549                OpBranch %16
550          %24 = OpLabel
551                OpStore %11 %17
552                OpBranchConditional %31 %36 %16
553          %36 = OpLabel
554                OpStore %11 %18
555                OpBranchConditional %32 %16 %16
556          %16 = OpLabel
557          %25 = OpLoad %6 %8
558                OpBranch %37
559          %37 = OpLabel
560          %26 = OpLoad %6 %11
561          %27 = OpIEqual %13 %25 %26
562                OpSelectionMerge %29 None
563                OpBranchConditional %27 %28 %29
564          %28 = OpLabel
565                OpStore %8 %17
566                OpBranchConditional %31 %38 %29
567          %38 = OpLabel
568                OpBranchConditional %32 %29 %29
569          %29 = OpLabel
570          %30 = OpLoad %6 %11
571                OpStore %8 %30
572                OpReturn
573                OpFunctionEnd
574   )";
575 
576   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
577 }
578 
TEST(TransformationAddDeadBreakTest,BreakOutOfNestedSwitches)579 TEST(TransformationAddDeadBreakTest, BreakOutOfNestedSwitches) {
580   // Checks some allowed and disallowed scenarios for nests of switches.
581 
582   // The SPIR-V for this test is adapted from the following GLSL:
583   //
584   // void main() {
585   //   int x;
586   //   int y;
587   //   x = 1;
588   //   if (x < y) {
589   //     switch (x) {
590   //       case 0:
591   //       case 1:
592   //         if (x == y) {
593   //         }
594   //         x = 2;
595   //         break;
596   //       case 3:
597   //         if (y == 4) {
598   //           y = 2;
599   //           x = 3;
600   //         }
601   //       case 10:
602   //         break;
603   //       default:
604   //         switch (y) {
605   //           case 1:
606   //             break;
607   //           case 2:
608   //             x = 4;
609   //             y = 2;
610   //           default:
611   //             x = 3;
612   //             break;
613   //         }
614   //     }
615   //   } else {
616   //     switch (y) {
617   //       case 1:
618   //         x = 4;
619   //       case 2:
620   //         y = 3;
621   //       default:
622   //         x = y;
623   //         break;
624   //     }
625   //   }
626   // }
627 
628   std::string shader = R"(
629                OpCapability Shader
630           %1 = OpExtInstImport "GLSL.std.450"
631                OpMemoryModel Logical GLSL450
632                OpEntryPoint Fragment %4 "main"
633                OpExecutionMode %4 OriginUpperLeft
634                OpSource ESSL 310
635                OpName %4 "main"
636                OpName %8 "x"
637                OpName %11 "y"
638           %2 = OpTypeVoid
639           %3 = OpTypeFunction %2
640           %6 = OpTypeInt 32 1
641           %7 = OpTypePointer Function %6
642           %9 = OpConstant %6 1
643          %13 = OpTypeBool
644          %29 = OpConstant %6 2
645          %32 = OpConstant %6 4
646          %36 = OpConstant %6 3
647          %60 = OpConstantTrue %13
648          %61 = OpConstantFalse %13
649           %4 = OpFunction %2 None %3
650           %5 = OpLabel
651           %8 = OpVariable %7 Function
652          %11 = OpVariable %7 Function
653                OpStore %8 %9
654          %10 = OpLoad %6 %8
655          %12 = OpLoad %6 %11
656          %14 = OpSLessThan %13 %10 %12
657                OpSelectionMerge %16 None
658                OpBranchConditional %14 %15 %47
659          %15 = OpLabel
660          %17 = OpLoad %6 %8
661                OpSelectionMerge %22 None
662                OpSwitch %17 %21 0 %18 1 %18 3 %19 10 %20
663          %21 = OpLabel
664          %38 = OpLoad %6 %11
665                OpSelectionMerge %42 None
666                OpSwitch %38 %41 1 %39 2 %40
667          %41 = OpLabel
668                OpStore %8 %36
669                OpBranch %42
670          %39 = OpLabel
671                OpBranch %42
672          %40 = OpLabel
673                OpStore %8 %32
674                OpStore %11 %29
675                OpBranch %41
676          %42 = OpLabel
677                OpBranch %22
678          %18 = OpLabel
679          %23 = OpLoad %6 %8
680                OpBranch %63
681          %63 = OpLabel
682          %24 = OpLoad %6 %11
683          %25 = OpIEqual %13 %23 %24
684                OpSelectionMerge %27 None
685                OpBranchConditional %25 %26 %27
686          %26 = OpLabel
687                OpBranch %27
688          %27 = OpLabel
689                OpStore %8 %29
690                OpBranch %22
691          %19 = OpLabel
692          %31 = OpLoad %6 %11
693          %33 = OpIEqual %13 %31 %32
694                OpSelectionMerge %35 None
695                OpBranchConditional %33 %34 %35
696          %34 = OpLabel
697                OpStore %11 %29
698                OpBranch %62
699          %62 = OpLabel
700                OpStore %8 %36
701                OpBranch %35
702          %35 = OpLabel
703                OpBranch %20
704          %20 = OpLabel
705                OpBranch %22
706          %22 = OpLabel
707                OpBranch %16
708          %47 = OpLabel
709          %48 = OpLoad %6 %11
710                OpSelectionMerge %52 None
711                OpSwitch %48 %51 1 %49 2 %50
712          %51 = OpLabel
713          %53 = OpLoad %6 %11
714                OpStore %8 %53
715                OpBranch %52
716          %49 = OpLabel
717                OpStore %8 %32
718                OpBranch %50
719          %50 = OpLabel
720                OpStore %11 %36
721                OpBranch %51
722          %52 = OpLabel
723                OpBranch %16
724          %16 = OpLabel
725                OpReturn
726                OpFunctionEnd
727   )";
728 
729   const auto env = SPV_ENV_UNIVERSAL_1_3;
730   const auto consumer = nullptr;
731   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
732   spvtools::ValidatorOptions validator_options;
733   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
734                                                kConsoleMessageConsumer));
735   TransformationContext transformation_context(
736       MakeUnique<FactManager>(context.get()), validator_options);
737   // The header and merge blocks
738   const uint32_t header_outer_if = 5;
739   const uint32_t merge_outer_if = 16;
740   const uint32_t header_then_outer_switch = 15;
741   const uint32_t merge_then_outer_switch = 22;
742   const uint32_t header_then_inner_switch = 21;
743   const uint32_t merge_then_inner_switch = 42;
744   const uint32_t header_else_switch = 47;
745   const uint32_t merge_else_switch = 52;
746   const uint32_t header_inner_if_1 = 19;
747   const uint32_t merge_inner_if_1 = 35;
748   const uint32_t header_inner_if_2 = 63;
749   const uint32_t merge_inner_if_2 = 27;
750 
751   // The non-merge-nor-header blocks in each construct
752   const uint32_t then_outer_switch_block_1 = 18;
753   const uint32_t then_inner_switch_block_1 = 39;
754   const uint32_t then_inner_switch_block_2 = 40;
755   const uint32_t then_inner_switch_block_3 = 41;
756   const uint32_t else_switch_block_1 = 49;
757   const uint32_t else_switch_block_2 = 50;
758   const uint32_t else_switch_block_3 = 51;
759   const uint32_t inner_if_1_block_1 = 34;
760   const uint32_t inner_if_1_block_2 = 62;
761   const uint32_t inner_if_2_block_1 = 26;
762 
763   // Fine to branch straight to direct merge block for a construct
764   ASSERT_TRUE(TransformationAddDeadBreak(then_outer_switch_block_1,
765                                          merge_then_outer_switch, true, {})
766                   .IsApplicable(context.get(), transformation_context));
767   ASSERT_TRUE(TransformationAddDeadBreak(then_inner_switch_block_1,
768                                          merge_then_inner_switch, false, {})
769                   .IsApplicable(context.get(), transformation_context));
770   ASSERT_TRUE(TransformationAddDeadBreak(then_inner_switch_block_2,
771                                          merge_then_inner_switch, true, {})
772                   .IsApplicable(context.get(), transformation_context));
773   ASSERT_TRUE(TransformationAddDeadBreak(then_inner_switch_block_3,
774                                          merge_then_inner_switch, true, {})
775                   .IsApplicable(context.get(), transformation_context));
776   ASSERT_TRUE(TransformationAddDeadBreak(else_switch_block_1, merge_else_switch,
777                                          false, {})
778                   .IsApplicable(context.get(), transformation_context));
779   ASSERT_TRUE(TransformationAddDeadBreak(else_switch_block_2, merge_else_switch,
780                                          true, {})
781                   .IsApplicable(context.get(), transformation_context));
782   ASSERT_TRUE(TransformationAddDeadBreak(else_switch_block_3, merge_else_switch,
783                                          false, {})
784                   .IsApplicable(context.get(), transformation_context));
785   ASSERT_TRUE(
786       TransformationAddDeadBreak(inner_if_1_block_1, merge_inner_if_1, true, {})
787           .IsApplicable(context.get(), transformation_context));
788   ASSERT_TRUE(TransformationAddDeadBreak(inner_if_1_block_2, merge_inner_if_1,
789                                          false, {})
790                   .IsApplicable(context.get(), transformation_context));
791   ASSERT_TRUE(
792       TransformationAddDeadBreak(inner_if_2_block_1, merge_inner_if_2, true, {})
793           .IsApplicable(context.get(), transformation_context));
794 
795   // Not OK to break out of a switch from a selection construct inside the
796   // switch.
797   ASSERT_FALSE(TransformationAddDeadBreak(inner_if_1_block_1,
798                                           merge_then_outer_switch, true, {})
799                    .IsApplicable(context.get(), transformation_context));
800   ASSERT_FALSE(TransformationAddDeadBreak(inner_if_1_block_2,
801                                           merge_then_outer_switch, false, {})
802                    .IsApplicable(context.get(), transformation_context));
803   ASSERT_FALSE(TransformationAddDeadBreak(inner_if_2_block_1,
804                                           merge_then_outer_switch, true, {})
805                    .IsApplicable(context.get(), transformation_context));
806 
807   // Some miscellaneous inapplicable cases.
808   ASSERT_FALSE(
809       TransformationAddDeadBreak(header_outer_if, merge_outer_if, true, {})
810           .IsApplicable(context.get(), transformation_context));
811   ASSERT_FALSE(TransformationAddDeadBreak(header_inner_if_1, inner_if_1_block_2,
812                                           false, {})
813                    .IsApplicable(context.get(), transformation_context));
814   ASSERT_FALSE(TransformationAddDeadBreak(header_then_inner_switch,
815                                           header_then_outer_switch, false, {})
816                    .IsApplicable(context.get(), transformation_context));
817   ASSERT_FALSE(TransformationAddDeadBreak(header_else_switch,
818                                           then_inner_switch_block_3, false, {})
819                    .IsApplicable(context.get(), transformation_context));
820   ASSERT_FALSE(TransformationAddDeadBreak(header_inner_if_2, header_inner_if_2,
821                                           false, {})
822                    .IsApplicable(context.get(), transformation_context));
823 
824   auto transformation1 = TransformationAddDeadBreak(
825       then_outer_switch_block_1, merge_then_outer_switch, true, {});
826   auto transformation2 = TransformationAddDeadBreak(
827       then_inner_switch_block_1, merge_then_inner_switch, false, {});
828   auto transformation3 = TransformationAddDeadBreak(
829       then_inner_switch_block_2, merge_then_inner_switch, true, {});
830   auto transformation4 = TransformationAddDeadBreak(
831       then_inner_switch_block_3, merge_then_inner_switch, true, {});
832   auto transformation5 = TransformationAddDeadBreak(
833       else_switch_block_1, merge_else_switch, false, {});
834   auto transformation6 = TransformationAddDeadBreak(
835       else_switch_block_2, merge_else_switch, true, {});
836   auto transformation7 = TransformationAddDeadBreak(
837       else_switch_block_3, merge_else_switch, false, {});
838   auto transformation8 = TransformationAddDeadBreak(inner_if_1_block_1,
839                                                     merge_inner_if_1, true, {});
840   auto transformation9 = TransformationAddDeadBreak(
841       inner_if_1_block_2, merge_inner_if_1, false, {});
842   auto transformation10 = TransformationAddDeadBreak(
843       inner_if_2_block_1, merge_inner_if_2, true, {});
844 
845   ASSERT_TRUE(
846       transformation1.IsApplicable(context.get(), transformation_context));
847   ApplyAndCheckFreshIds(transformation1, context.get(),
848                         &transformation_context);
849   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
850                                                kConsoleMessageConsumer));
851 
852   ASSERT_TRUE(
853       transformation2.IsApplicable(context.get(), transformation_context));
854   ApplyAndCheckFreshIds(transformation2, context.get(),
855                         &transformation_context);
856   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
857                                                kConsoleMessageConsumer));
858 
859   ASSERT_TRUE(
860       transformation3.IsApplicable(context.get(), transformation_context));
861   ApplyAndCheckFreshIds(transformation3, context.get(),
862                         &transformation_context);
863   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
864                                                kConsoleMessageConsumer));
865 
866   ASSERT_TRUE(
867       transformation4.IsApplicable(context.get(), transformation_context));
868   ApplyAndCheckFreshIds(transformation4, context.get(),
869                         &transformation_context);
870   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
871                                                kConsoleMessageConsumer));
872 
873   ASSERT_TRUE(
874       transformation5.IsApplicable(context.get(), transformation_context));
875   ApplyAndCheckFreshIds(transformation5, context.get(),
876                         &transformation_context);
877   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
878                                                kConsoleMessageConsumer));
879 
880   ASSERT_TRUE(
881       transformation6.IsApplicable(context.get(), transformation_context));
882   ApplyAndCheckFreshIds(transformation6, context.get(),
883                         &transformation_context);
884   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
885                                                kConsoleMessageConsumer));
886 
887   ASSERT_TRUE(
888       transformation7.IsApplicable(context.get(), transformation_context));
889   ApplyAndCheckFreshIds(transformation7, context.get(),
890                         &transformation_context);
891   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
892                                                kConsoleMessageConsumer));
893 
894   ASSERT_TRUE(
895       transformation8.IsApplicable(context.get(), transformation_context));
896   ApplyAndCheckFreshIds(transformation8, context.get(),
897                         &transformation_context);
898   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
899                                                kConsoleMessageConsumer));
900 
901   ASSERT_TRUE(
902       transformation9.IsApplicable(context.get(), transformation_context));
903   ApplyAndCheckFreshIds(transformation9, context.get(),
904                         &transformation_context);
905   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
906                                                kConsoleMessageConsumer));
907 
908   ASSERT_TRUE(
909       transformation10.IsApplicable(context.get(), transformation_context));
910   ApplyAndCheckFreshIds(transformation10, context.get(),
911                         &transformation_context);
912   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
913                                                kConsoleMessageConsumer));
914 
915   std::string after_transformation = R"(
916                OpCapability Shader
917           %1 = OpExtInstImport "GLSL.std.450"
918                OpMemoryModel Logical GLSL450
919                OpEntryPoint Fragment %4 "main"
920                OpExecutionMode %4 OriginUpperLeft
921                OpSource ESSL 310
922                OpName %4 "main"
923                OpName %8 "x"
924                OpName %11 "y"
925           %2 = OpTypeVoid
926           %3 = OpTypeFunction %2
927           %6 = OpTypeInt 32 1
928           %7 = OpTypePointer Function %6
929           %9 = OpConstant %6 1
930          %13 = OpTypeBool
931          %29 = OpConstant %6 2
932          %32 = OpConstant %6 4
933          %36 = OpConstant %6 3
934          %60 = OpConstantTrue %13
935          %61 = OpConstantFalse %13
936           %4 = OpFunction %2 None %3
937           %5 = OpLabel
938           %8 = OpVariable %7 Function
939          %11 = OpVariable %7 Function
940                OpStore %8 %9
941          %10 = OpLoad %6 %8
942          %12 = OpLoad %6 %11
943          %14 = OpSLessThan %13 %10 %12
944                OpSelectionMerge %16 None
945                OpBranchConditional %14 %15 %47
946          %15 = OpLabel
947          %17 = OpLoad %6 %8
948                OpSelectionMerge %22 None
949                OpSwitch %17 %21 0 %18 1 %18 3 %19 10 %20
950          %21 = OpLabel
951          %38 = OpLoad %6 %11
952                OpSelectionMerge %42 None
953                OpSwitch %38 %41 1 %39 2 %40
954          %41 = OpLabel
955                OpStore %8 %36
956                OpBranchConditional %60 %42 %42
957          %39 = OpLabel
958                OpBranchConditional %61 %42 %42
959          %40 = OpLabel
960                OpStore %8 %32
961                OpStore %11 %29
962                OpBranchConditional %60 %41 %42
963          %42 = OpLabel
964                OpBranch %22
965          %18 = OpLabel
966          %23 = OpLoad %6 %8
967                OpBranchConditional %60 %63 %22
968          %63 = OpLabel
969          %24 = OpLoad %6 %11
970          %25 = OpIEqual %13 %23 %24
971                OpSelectionMerge %27 None
972                OpBranchConditional %25 %26 %27
973          %26 = OpLabel
974                OpBranchConditional %60 %27 %27
975          %27 = OpLabel
976                OpStore %8 %29
977                OpBranch %22
978          %19 = OpLabel
979          %31 = OpLoad %6 %11
980          %33 = OpIEqual %13 %31 %32
981                OpSelectionMerge %35 None
982                OpBranchConditional %33 %34 %35
983          %34 = OpLabel
984                OpStore %11 %29
985                OpBranchConditional %60 %62 %35
986          %62 = OpLabel
987                OpStore %8 %36
988                OpBranchConditional %61 %35 %35
989          %35 = OpLabel
990                OpBranch %20
991          %20 = OpLabel
992                OpBranch %22
993          %22 = OpLabel
994                OpBranch %16
995          %47 = OpLabel
996          %48 = OpLoad %6 %11
997                OpSelectionMerge %52 None
998                OpSwitch %48 %51 1 %49 2 %50
999          %51 = OpLabel
1000          %53 = OpLoad %6 %11
1001                OpStore %8 %53
1002                OpBranchConditional %61 %52 %52
1003          %49 = OpLabel
1004                OpStore %8 %32
1005                OpBranchConditional %61 %52 %50
1006          %50 = OpLabel
1007                OpStore %11 %36
1008                OpBranchConditional %60 %51 %52
1009          %52 = OpLabel
1010                OpBranch %16
1011          %16 = OpLabel
1012                OpReturn
1013                OpFunctionEnd
1014   )";
1015 
1016   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1017 }
1018 
TEST(TransformationAddDeadBreakTest,BreakOutOfLoopNest)1019 TEST(TransformationAddDeadBreakTest, BreakOutOfLoopNest) {
1020   // Checks some allowed and disallowed scenarios for a nest of loops, including
1021   // breaking from an if or switch right out of a loop.
1022 
1023   // The SPIR-V for this test is adapted from the following GLSL:
1024   //
1025   // void main() {
1026   //   int x, y;
1027   //   do {
1028   //     x++;
1029   //     for (int j = 0; j < 100; j++) {
1030   //       y++;
1031   //       if (x == y) {
1032   //         x++;
1033   //         if (x == 2) {
1034   //           y++;
1035   //         }
1036   //         switch (x) {
1037   //           case 0:
1038   //             x = 2;
1039   //           default:
1040   //             break;
1041   //         }
1042   //       }
1043   //     }
1044   //   } while (x > y);
1045   //
1046   //   for (int i = 0; i < 100; i++) {
1047   //     x++;
1048   //   }
1049   // }
1050 
1051   std::string shader = R"(
1052                OpCapability Shader
1053           %1 = OpExtInstImport "GLSL.std.450"
1054                OpMemoryModel Logical GLSL450
1055                OpEntryPoint Fragment %4 "main"
1056                OpExecutionMode %4 OriginUpperLeft
1057                OpSource ESSL 310
1058                OpName %4 "main"
1059                OpName %12 "x"
1060                OpName %16 "j"
1061                OpName %27 "y"
1062                OpName %55 "i"
1063           %2 = OpTypeVoid
1064           %3 = OpTypeFunction %2
1065          %10 = OpTypeInt 32 1
1066          %11 = OpTypePointer Function %10
1067          %14 = OpConstant %10 1
1068          %17 = OpConstant %10 0
1069          %24 = OpConstant %10 100
1070          %25 = OpTypeBool
1071          %38 = OpConstant %10 2
1072          %67 = OpConstantTrue %25
1073          %68 = OpConstantFalse %25
1074           %4 = OpFunction %2 None %3
1075           %5 = OpLabel
1076          %12 = OpVariable %11 Function
1077          %16 = OpVariable %11 Function
1078          %27 = OpVariable %11 Function
1079          %55 = OpVariable %11 Function
1080                OpBranch %6
1081           %6 = OpLabel
1082                OpLoopMerge %8 %9 None
1083                OpBranch %7
1084           %7 = OpLabel
1085          %13 = OpLoad %10 %12
1086          %15 = OpIAdd %10 %13 %14
1087                OpStore %12 %15
1088                OpStore %16 %17
1089                OpBranch %18
1090          %18 = OpLabel
1091                OpLoopMerge %20 %21 None
1092                OpBranch %22
1093          %22 = OpLabel
1094          %23 = OpLoad %10 %16
1095          %26 = OpSLessThan %25 %23 %24
1096                OpBranchConditional %26 %19 %20
1097          %19 = OpLabel
1098          %28 = OpLoad %10 %27
1099          %29 = OpIAdd %10 %28 %14
1100                OpStore %27 %29
1101          %30 = OpLoad %10 %12
1102          %31 = OpLoad %10 %27
1103          %32 = OpIEqual %25 %30 %31
1104                OpSelectionMerge %34 None
1105                OpBranchConditional %32 %33 %34
1106          %33 = OpLabel
1107          %35 = OpLoad %10 %12
1108          %36 = OpIAdd %10 %35 %14
1109                OpStore %12 %36
1110          %37 = OpLoad %10 %12
1111          %39 = OpIEqual %25 %37 %38
1112                OpSelectionMerge %41 None
1113                OpBranchConditional %39 %40 %41
1114          %40 = OpLabel
1115          %42 = OpLoad %10 %27
1116          %43 = OpIAdd %10 %42 %14
1117                OpStore %27 %43
1118                OpBranch %41
1119          %41 = OpLabel
1120          %44 = OpLoad %10 %12
1121                OpSelectionMerge %47 None
1122                OpSwitch %44 %46 0 %45
1123          %46 = OpLabel
1124                OpBranch %47
1125          %45 = OpLabel
1126                OpStore %12 %38
1127                OpBranch %46
1128          %47 = OpLabel
1129                OpBranch %34
1130          %34 = OpLabel
1131                OpBranch %21
1132          %21 = OpLabel
1133          %50 = OpLoad %10 %16
1134          %51 = OpIAdd %10 %50 %14
1135                OpStore %16 %51
1136                OpBranch %18
1137          %20 = OpLabel
1138                OpBranch %9
1139           %9 = OpLabel
1140          %52 = OpLoad %10 %12
1141          %53 = OpLoad %10 %27
1142          %54 = OpSGreaterThan %25 %52 %53
1143                OpBranchConditional %54 %6 %8
1144           %8 = OpLabel
1145                OpStore %55 %17
1146                OpBranch %56
1147          %56 = OpLabel
1148                OpLoopMerge %58 %59 None
1149                OpBranch %60
1150          %60 = OpLabel
1151          %61 = OpLoad %10 %55
1152          %62 = OpSLessThan %25 %61 %24
1153                OpBranchConditional %62 %57 %58
1154          %57 = OpLabel
1155          %63 = OpLoad %10 %12
1156          %64 = OpIAdd %10 %63 %14
1157                OpStore %12 %64
1158                OpBranch %59
1159          %59 = OpLabel
1160          %65 = OpLoad %10 %55
1161          %66 = OpIAdd %10 %65 %14
1162                OpStore %55 %66
1163                OpBranch %56
1164          %58 = OpLabel
1165                OpReturn
1166                OpFunctionEnd
1167   )";
1168 
1169   const auto env = SPV_ENV_UNIVERSAL_1_3;
1170   const auto consumer = nullptr;
1171   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1172   spvtools::ValidatorOptions validator_options;
1173   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1174                                                kConsoleMessageConsumer));
1175   TransformationContext transformation_context(
1176       MakeUnique<FactManager>(context.get()), validator_options);
1177   // The header and merge blocks
1178   const uint32_t header_do_while = 6;
1179   const uint32_t merge_do_while = 8;
1180   const uint32_t header_for_j = 18;
1181   const uint32_t merge_for_j = 20;
1182   const uint32_t header_for_i = 56;
1183   const uint32_t merge_for_i = 58;
1184   const uint32_t header_switch = 41;
1185   const uint32_t merge_switch = 47;
1186   const uint32_t header_if_x_eq_y = 19;
1187   const uint32_t merge_if_x_eq_y = 34;
1188   const uint32_t header_if_x_eq_2 = 33;
1189   const uint32_t merge_if_x_eq_2 = 41;
1190 
1191   // Loop continue targets
1192   const uint32_t continue_do_while = 9;
1193   const uint32_t continue_for_j = 21;
1194   const uint32_t continue_for_i = 59;
1195 
1196   // Some blocks in these constructs
1197   const uint32_t block_in_inner_if = 40;
1198   const uint32_t block_switch_case = 46;
1199   const uint32_t block_switch_default = 45;
1200   const uint32_t block_in_for_i_loop = 57;
1201 
1202   // Fine to break from any loop header to its merge
1203   ASSERT_TRUE(
1204       TransformationAddDeadBreak(header_do_while, merge_do_while, true, {})
1205           .IsApplicable(context.get(), transformation_context));
1206   ASSERT_TRUE(TransformationAddDeadBreak(header_for_i, merge_for_i, false, {})
1207                   .IsApplicable(context.get(), transformation_context));
1208   ASSERT_TRUE(TransformationAddDeadBreak(header_for_j, merge_for_j, true, {})
1209                   .IsApplicable(context.get(), transformation_context));
1210 
1211   // Fine to break from any of the blocks in constructs in the "for j" loop to
1212   // that loop's merge
1213   ASSERT_TRUE(
1214       TransformationAddDeadBreak(block_in_inner_if, merge_for_j, false, {})
1215           .IsApplicable(context.get(), transformation_context));
1216   ASSERT_TRUE(
1217       TransformationAddDeadBreak(block_switch_case, merge_for_j, true, {})
1218           .IsApplicable(context.get(), transformation_context));
1219   ASSERT_TRUE(
1220       TransformationAddDeadBreak(block_switch_default, merge_for_j, false, {})
1221           .IsApplicable(context.get(), transformation_context));
1222 
1223   // Fine to break from the body of the "for i" loop to that loop's merge
1224   ASSERT_TRUE(
1225       TransformationAddDeadBreak(block_in_for_i_loop, merge_for_i, true, {})
1226           .IsApplicable(context.get(), transformation_context));
1227 
1228   // Not OK to break from multiple loops
1229   ASSERT_FALSE(
1230       TransformationAddDeadBreak(block_in_inner_if, merge_do_while, false, {})
1231           .IsApplicable(context.get(), transformation_context));
1232   ASSERT_FALSE(
1233       TransformationAddDeadBreak(block_switch_case, merge_do_while, true, {})
1234           .IsApplicable(context.get(), transformation_context));
1235   ASSERT_FALSE(TransformationAddDeadBreak(block_switch_default, merge_do_while,
1236                                           false, {})
1237                    .IsApplicable(context.get(), transformation_context));
1238   ASSERT_FALSE(
1239       TransformationAddDeadBreak(header_for_j, merge_do_while, true, {})
1240           .IsApplicable(context.get(), transformation_context));
1241 
1242   // Not OK to break loop from its continue construct, except from the back-edge
1243   // block.
1244   ASSERT_FALSE(
1245       TransformationAddDeadBreak(continue_do_while, merge_do_while, true, {})
1246           .IsApplicable(context.get(), transformation_context));
1247   ASSERT_TRUE(TransformationAddDeadBreak(continue_for_j, merge_for_j, false, {})
1248                   .IsApplicable(context.get(), transformation_context));
1249   ASSERT_TRUE(TransformationAddDeadBreak(continue_for_i, merge_for_i, true, {})
1250                   .IsApplicable(context.get(), transformation_context));
1251 
1252   // Not OK to break out of multiple non-loop constructs if not breaking to a
1253   // loop merge
1254   ASSERT_FALSE(
1255       TransformationAddDeadBreak(block_in_inner_if, merge_if_x_eq_y, false, {})
1256           .IsApplicable(context.get(), transformation_context));
1257   ASSERT_FALSE(
1258       TransformationAddDeadBreak(block_switch_case, merge_if_x_eq_y, true, {})
1259           .IsApplicable(context.get(), transformation_context));
1260   ASSERT_FALSE(TransformationAddDeadBreak(block_switch_default, merge_if_x_eq_y,
1261                                           false, {})
1262                    .IsApplicable(context.get(), transformation_context));
1263 
1264   // Some miscellaneous inapplicable transformations
1265   ASSERT_FALSE(
1266       TransformationAddDeadBreak(header_if_x_eq_2, header_if_x_eq_y, false, {})
1267           .IsApplicable(context.get(), transformation_context));
1268   ASSERT_FALSE(
1269       TransformationAddDeadBreak(merge_if_x_eq_2, merge_switch, false, {})
1270           .IsApplicable(context.get(), transformation_context));
1271   ASSERT_FALSE(
1272       TransformationAddDeadBreak(header_switch, header_switch, false, {})
1273           .IsApplicable(context.get(), transformation_context));
1274 
1275   auto transformation1 =
1276       TransformationAddDeadBreak(header_do_while, merge_do_while, true, {});
1277   auto transformation2 =
1278       TransformationAddDeadBreak(header_for_i, merge_for_i, false, {});
1279   auto transformation3 =
1280       TransformationAddDeadBreak(header_for_j, merge_for_j, true, {});
1281   auto transformation4 =
1282       TransformationAddDeadBreak(block_in_inner_if, merge_for_j, false, {});
1283   auto transformation5 =
1284       TransformationAddDeadBreak(block_switch_case, merge_for_j, true, {});
1285   auto transformation6 =
1286       TransformationAddDeadBreak(block_switch_default, merge_for_j, false, {});
1287   auto transformation7 =
1288       TransformationAddDeadBreak(block_in_for_i_loop, merge_for_i, true, {});
1289 
1290   ASSERT_TRUE(
1291       transformation1.IsApplicable(context.get(), transformation_context));
1292   ApplyAndCheckFreshIds(transformation1, context.get(),
1293                         &transformation_context);
1294   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1295                                                kConsoleMessageConsumer));
1296 
1297   ASSERT_TRUE(
1298       transformation2.IsApplicable(context.get(), transformation_context));
1299   ApplyAndCheckFreshIds(transformation2, context.get(),
1300                         &transformation_context);
1301   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1302                                                kConsoleMessageConsumer));
1303 
1304   ASSERT_TRUE(
1305       transformation3.IsApplicable(context.get(), transformation_context));
1306   ApplyAndCheckFreshIds(transformation3, context.get(),
1307                         &transformation_context);
1308   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1309                                                kConsoleMessageConsumer));
1310 
1311   ASSERT_TRUE(
1312       transformation4.IsApplicable(context.get(), transformation_context));
1313   ApplyAndCheckFreshIds(transformation4, context.get(),
1314                         &transformation_context);
1315   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1316                                                kConsoleMessageConsumer));
1317 
1318   ASSERT_TRUE(
1319       transformation5.IsApplicable(context.get(), transformation_context));
1320   ApplyAndCheckFreshIds(transformation5, context.get(),
1321                         &transformation_context);
1322   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1323                                                kConsoleMessageConsumer));
1324 
1325   ASSERT_TRUE(
1326       transformation6.IsApplicable(context.get(), transformation_context));
1327   ApplyAndCheckFreshIds(transformation6, context.get(),
1328                         &transformation_context);
1329   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1330                                                kConsoleMessageConsumer));
1331 
1332   ASSERT_TRUE(
1333       transformation7.IsApplicable(context.get(), transformation_context));
1334   ApplyAndCheckFreshIds(transformation7, context.get(),
1335                         &transformation_context);
1336   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1337                                                kConsoleMessageConsumer));
1338 
1339   std::string after_transformation = R"(
1340                OpCapability Shader
1341           %1 = OpExtInstImport "GLSL.std.450"
1342                OpMemoryModel Logical GLSL450
1343                OpEntryPoint Fragment %4 "main"
1344                OpExecutionMode %4 OriginUpperLeft
1345                OpSource ESSL 310
1346                OpName %4 "main"
1347                OpName %12 "x"
1348                OpName %16 "j"
1349                OpName %27 "y"
1350                OpName %55 "i"
1351           %2 = OpTypeVoid
1352           %3 = OpTypeFunction %2
1353          %10 = OpTypeInt 32 1
1354          %11 = OpTypePointer Function %10
1355          %14 = OpConstant %10 1
1356          %17 = OpConstant %10 0
1357          %24 = OpConstant %10 100
1358          %25 = OpTypeBool
1359          %38 = OpConstant %10 2
1360          %67 = OpConstantTrue %25
1361          %68 = OpConstantFalse %25
1362           %4 = OpFunction %2 None %3
1363           %5 = OpLabel
1364          %12 = OpVariable %11 Function
1365          %16 = OpVariable %11 Function
1366          %27 = OpVariable %11 Function
1367          %55 = OpVariable %11 Function
1368                OpBranch %6
1369           %6 = OpLabel
1370                OpLoopMerge %8 %9 None
1371                OpBranchConditional %67 %7 %8
1372           %7 = OpLabel
1373          %13 = OpLoad %10 %12
1374          %15 = OpIAdd %10 %13 %14
1375                OpStore %12 %15
1376                OpStore %16 %17
1377                OpBranch %18
1378          %18 = OpLabel
1379                OpLoopMerge %20 %21 None
1380                OpBranchConditional %67 %22 %20
1381          %22 = OpLabel
1382          %23 = OpLoad %10 %16
1383          %26 = OpSLessThan %25 %23 %24
1384                OpBranchConditional %26 %19 %20
1385          %19 = OpLabel
1386          %28 = OpLoad %10 %27
1387          %29 = OpIAdd %10 %28 %14
1388                OpStore %27 %29
1389          %30 = OpLoad %10 %12
1390          %31 = OpLoad %10 %27
1391          %32 = OpIEqual %25 %30 %31
1392                OpSelectionMerge %34 None
1393                OpBranchConditional %32 %33 %34
1394          %33 = OpLabel
1395          %35 = OpLoad %10 %12
1396          %36 = OpIAdd %10 %35 %14
1397                OpStore %12 %36
1398          %37 = OpLoad %10 %12
1399          %39 = OpIEqual %25 %37 %38
1400                OpSelectionMerge %41 None
1401                OpBranchConditional %39 %40 %41
1402          %40 = OpLabel
1403          %42 = OpLoad %10 %27
1404          %43 = OpIAdd %10 %42 %14
1405                OpStore %27 %43
1406                OpBranchConditional %68 %20 %41
1407          %41 = OpLabel
1408          %44 = OpLoad %10 %12
1409                OpSelectionMerge %47 None
1410                OpSwitch %44 %46 0 %45
1411          %46 = OpLabel
1412                OpBranchConditional %67 %47 %20
1413          %45 = OpLabel
1414                OpStore %12 %38
1415                OpBranchConditional %68 %20 %46
1416          %47 = OpLabel
1417                OpBranch %34
1418          %34 = OpLabel
1419                OpBranch %21
1420          %21 = OpLabel
1421          %50 = OpLoad %10 %16
1422          %51 = OpIAdd %10 %50 %14
1423                OpStore %16 %51
1424                OpBranch %18
1425          %20 = OpLabel
1426                OpBranch %9
1427           %9 = OpLabel
1428          %52 = OpLoad %10 %12
1429          %53 = OpLoad %10 %27
1430          %54 = OpSGreaterThan %25 %52 %53
1431                OpBranchConditional %54 %6 %8
1432           %8 = OpLabel
1433                OpStore %55 %17
1434                OpBranch %56
1435          %56 = OpLabel
1436                OpLoopMerge %58 %59 None
1437                OpBranchConditional %68 %58 %60
1438          %60 = OpLabel
1439          %61 = OpLoad %10 %55
1440          %62 = OpSLessThan %25 %61 %24
1441                OpBranchConditional %62 %57 %58
1442          %57 = OpLabel
1443          %63 = OpLoad %10 %12
1444          %64 = OpIAdd %10 %63 %14
1445                OpStore %12 %64
1446                OpBranchConditional %67 %59 %58
1447          %59 = OpLabel
1448          %65 = OpLoad %10 %55
1449          %66 = OpIAdd %10 %65 %14
1450                OpStore %55 %66
1451                OpBranch %56
1452          %58 = OpLabel
1453                OpReturn
1454                OpFunctionEnd
1455   )";
1456 
1457   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1458 }
1459 
TEST(TransformationAddDeadBreakTest,NoBreakFromContinueConstruct)1460 TEST(TransformationAddDeadBreakTest, NoBreakFromContinueConstruct) {
1461   // Checks that it is illegal to break straight from a continue construct.
1462 
1463   // The SPIR-V for this test is adapted from the following GLSL:
1464   //
1465   // void main() {
1466   //   for (int i = 0; i < 100; i++) {
1467   //   }
1468   // }
1469 
1470   std::string shader = R"(
1471                OpCapability Shader
1472           %1 = OpExtInstImport "GLSL.std.450"
1473                OpMemoryModel Logical GLSL450
1474                OpEntryPoint Fragment %4 "main"
1475                OpExecutionMode %4 OriginUpperLeft
1476                OpSource ESSL 310
1477                OpName %4 "main"
1478                OpName %8 "i"
1479                OpDecorate %8 RelaxedPrecision
1480                OpDecorate %15 RelaxedPrecision
1481                OpDecorate %19 RelaxedPrecision
1482                OpDecorate %21 RelaxedPrecision
1483           %2 = OpTypeVoid
1484           %3 = OpTypeFunction %2
1485           %6 = OpTypeInt 32 1
1486           %7 = OpTypePointer Function %6
1487           %9 = OpConstant %6 0
1488          %16 = OpConstant %6 100
1489          %17 = OpTypeBool
1490          %22 = OpConstantTrue %17
1491          %20 = OpConstant %6 1
1492           %4 = OpFunction %2 None %3
1493           %5 = OpLabel
1494           %8 = OpVariable %7 Function
1495                OpStore %8 %9
1496                OpBranch %10
1497          %10 = OpLabel
1498                OpLoopMerge %12 %13 None
1499                OpBranch %14
1500          %14 = OpLabel
1501          %15 = OpLoad %6 %8
1502          %18 = OpSLessThan %17 %15 %16
1503                OpBranchConditional %18 %11 %12
1504          %11 = OpLabel
1505                OpBranch %13
1506          %13 = OpLabel
1507          %19 = OpLoad %6 %8
1508          %21 = OpIAdd %6 %19 %20
1509                OpBranch %23
1510          %23 = OpLabel
1511                OpStore %8 %21
1512                OpBranch %10
1513          %12 = OpLabel
1514                OpReturn
1515                OpFunctionEnd
1516   )";
1517 
1518   const auto env = SPV_ENV_UNIVERSAL_1_3;
1519   const auto consumer = nullptr;
1520   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1521   spvtools::ValidatorOptions validator_options;
1522   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1523                                                kConsoleMessageConsumer));
1524   TransformationContext transformation_context(
1525       MakeUnique<FactManager>(context.get()), validator_options);
1526   // Not OK to break loop from its continue construct, except from the back-edge
1527   // block.
1528   ASSERT_FALSE(TransformationAddDeadBreak(13, 12, true, {})
1529                    .IsApplicable(context.get(), transformation_context));
1530   ASSERT_TRUE(TransformationAddDeadBreak(23, 12, true, {})
1531                   .IsApplicable(context.get(), transformation_context));
1532 }
1533 
TEST(TransformationAddDeadBreakTest,BreakFromBackEdgeBlock)1534 TEST(TransformationAddDeadBreakTest, BreakFromBackEdgeBlock) {
1535   std::string reference_shader = R"(
1536                OpCapability Shader
1537           %1 = OpExtInstImport "GLSL.std.450"
1538                OpMemoryModel Logical GLSL450
1539                OpEntryPoint Vertex %10 "main"
1540 
1541 ; Types
1542           %2 = OpTypeVoid
1543           %3 = OpTypeFunction %2
1544           %4 = OpTypeInt 32 0
1545           %5 = OpTypeBool
1546           %6 = OpTypePointer Function %4
1547 
1548 ; Constants
1549           %7 = OpConstant %4 0
1550           %8 = OpConstant %4 1
1551           %9 = OpConstantTrue %5
1552 
1553 ; main function
1554          %10 = OpFunction %2 None %3
1555          %11 = OpLabel
1556          %12 = OpVariable %6 Function
1557                OpStore %12 %7
1558                OpBranch %13
1559          %13 = OpLabel
1560                OpLoopMerge %21 %18 None ; structured loop
1561                OpBranch %14
1562          %14 = OpLabel
1563          %15 = OpLoad %4 %12
1564          %16 = OpULessThan %5 %15 %8 ; i < 1 ?
1565                OpBranchConditional %16 %17 %21 ; body or break
1566          %17 = OpLabel ; body
1567                OpBranch %18
1568          %18 = OpLabel ; continue target does not strictly dominates the back-edge block
1569          %19 = OpLoad %4 %12
1570          %20 = OpIAdd %4 %19 %8 ; ++i
1571                OpStore %12 %20
1572                OpBranch %13
1573          %21 = OpLabel
1574                OpReturn
1575                OpFunctionEnd
1576   )";
1577 
1578   const auto env = SPV_ENV_UNIVERSAL_1_5;
1579   const auto consumer = nullptr;
1580   const auto context =
1581       BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
1582   spvtools::ValidatorOptions validator_options;
1583   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1584                                                kConsoleMessageConsumer));
1585   TransformationContext transformation_context(
1586       MakeUnique<FactManager>(context.get()), validator_options);
1587   auto transformation = TransformationAddDeadBreak(18, 21, true, {});
1588   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
1589 
1590   std::string variant_shader = R"(
1591                OpCapability Shader
1592           %1 = OpExtInstImport "GLSL.std.450"
1593                OpMemoryModel Logical GLSL450
1594                OpEntryPoint Vertex %10 "main"
1595 
1596 ; Types
1597           %2 = OpTypeVoid
1598           %3 = OpTypeFunction %2
1599           %4 = OpTypeInt 32 0
1600           %5 = OpTypeBool
1601           %6 = OpTypePointer Function %4
1602 
1603 ; Constants
1604           %7 = OpConstant %4 0
1605           %8 = OpConstant %4 1
1606           %9 = OpConstantTrue %5
1607 
1608 ; main function
1609          %10 = OpFunction %2 None %3
1610          %11 = OpLabel
1611          %12 = OpVariable %6 Function
1612                OpStore %12 %7
1613                OpBranch %13
1614          %13 = OpLabel
1615                OpLoopMerge %21 %18 None ; structured loop
1616                OpBranch %14
1617          %14 = OpLabel
1618          %15 = OpLoad %4 %12
1619          %16 = OpULessThan %5 %15 %8 ; i < 1 ?
1620                OpBranchConditional %16 %17 %21 ; body or break
1621          %17 = OpLabel ; body
1622                OpBranch %18
1623          %18 = OpLabel ; continue target does not strictly dominates the back-edge block
1624          %19 = OpLoad %4 %12
1625          %20 = OpIAdd %4 %19 %8 ; ++i
1626                OpStore %12 %20
1627                OpBranchConditional %9 %13 %21
1628          %21 = OpLabel
1629                OpReturn
1630                OpFunctionEnd
1631   )";
1632 
1633   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1634                                                kConsoleMessageConsumer));
1635   ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
1636 }
1637 
TEST(TransformationAddDeadBreakTest,SelectionInContinueConstruct)1638 TEST(TransformationAddDeadBreakTest, SelectionInContinueConstruct) {
1639   // Considers some scenarios where there is a selection construct in a loop's
1640   // continue construct.
1641 
1642   // The SPIR-V for this test is adapted from the following GLSL:
1643   //
1644   // void main() {
1645   //   for (int i = 0; i < 100; i = (i < 50 ? i + 2 : i + 1)) {
1646   //   }
1647   // }
1648 
1649   std::string shader = R"(
1650                OpCapability Shader
1651           %1 = OpExtInstImport "GLSL.std.450"
1652                OpMemoryModel Logical GLSL450
1653                OpEntryPoint Fragment %4 "main"
1654                OpExecutionMode %4 OriginUpperLeft
1655                OpSource ESSL 310
1656                OpName %4 "main"
1657                OpName %8 "i"
1658           %2 = OpTypeVoid
1659           %3 = OpTypeFunction %2
1660           %6 = OpTypeInt 32 1
1661           %7 = OpTypePointer Function %6
1662           %9 = OpConstant %6 0
1663          %16 = OpConstant %6 100
1664          %17 = OpTypeBool
1665          %99 = OpConstantTrue %17
1666          %20 = OpConstant %6 50
1667          %26 = OpConstant %6 2
1668          %30 = OpConstant %6 1
1669           %4 = OpFunction %2 None %3
1670           %5 = OpLabel
1671           %8 = OpVariable %7 Function
1672          %22 = OpVariable %7 Function
1673                OpStore %8 %9
1674                OpBranch %10
1675          %10 = OpLabel
1676                OpLoopMerge %12 %13 None
1677                OpBranch %14
1678          %14 = OpLabel
1679          %15 = OpLoad %6 %8
1680          %18 = OpSLessThan %17 %15 %16
1681                OpBranchConditional %18 %11 %12
1682          %11 = OpLabel
1683                OpBranch %13
1684          %13 = OpLabel
1685          %19 = OpLoad %6 %8
1686          %21 = OpSLessThan %17 %19 %20
1687                OpSelectionMerge %24 None
1688                OpBranchConditional %21 %23 %28
1689          %23 = OpLabel
1690          %25 = OpLoad %6 %8
1691                OpBranch %100
1692         %100 = OpLabel
1693          %27 = OpIAdd %6 %25 %26
1694                OpStore %22 %27
1695                OpBranch %24
1696          %28 = OpLabel
1697          %29 = OpLoad %6 %8
1698                OpBranch %101
1699         %101 = OpLabel
1700          %31 = OpIAdd %6 %29 %30
1701                OpStore %22 %31
1702                OpBranch %24
1703          %24 = OpLabel
1704          %32 = OpLoad %6 %22
1705                OpStore %8 %32
1706                OpBranch %10
1707          %12 = OpLabel
1708                OpReturn
1709                OpFunctionEnd
1710   )";
1711 
1712   const auto env = SPV_ENV_UNIVERSAL_1_3;
1713   const auto consumer = nullptr;
1714   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1715   spvtools::ValidatorOptions validator_options;
1716   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1717                                                kConsoleMessageConsumer));
1718   TransformationContext transformation_context(
1719       MakeUnique<FactManager>(context.get()), validator_options);
1720   const uint32_t loop_merge = 12;
1721   const uint32_t selection_merge = 24;
1722   const uint32_t in_selection_1 = 23;
1723   const uint32_t in_selection_2 = 100;
1724   const uint32_t in_selection_3 = 28;
1725   const uint32_t in_selection_4 = 101;
1726 
1727   // Not OK to jump from the selection to the loop merge, as this would break
1728   // from the loop's continue construct.
1729   ASSERT_FALSE(TransformationAddDeadBreak(in_selection_1, loop_merge, true, {})
1730                    .IsApplicable(context.get(), transformation_context));
1731   ASSERT_FALSE(TransformationAddDeadBreak(in_selection_2, loop_merge, true, {})
1732                    .IsApplicable(context.get(), transformation_context));
1733   ASSERT_FALSE(TransformationAddDeadBreak(in_selection_3, loop_merge, true, {})
1734                    .IsApplicable(context.get(), transformation_context));
1735   ASSERT_FALSE(TransformationAddDeadBreak(in_selection_4, loop_merge, true, {})
1736                    .IsApplicable(context.get(), transformation_context));
1737 
1738   // But fine to jump from the selection to its merge.
1739 
1740   auto transformation1 =
1741       TransformationAddDeadBreak(in_selection_1, selection_merge, true, {});
1742   auto transformation2 =
1743       TransformationAddDeadBreak(in_selection_2, selection_merge, true, {});
1744   auto transformation3 =
1745       TransformationAddDeadBreak(in_selection_3, selection_merge, true, {});
1746   auto transformation4 =
1747       TransformationAddDeadBreak(in_selection_4, selection_merge, true, {});
1748 
1749   ASSERT_TRUE(
1750       transformation1.IsApplicable(context.get(), transformation_context));
1751   ApplyAndCheckFreshIds(transformation1, context.get(),
1752                         &transformation_context);
1753   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1754                                                kConsoleMessageConsumer));
1755 
1756   ASSERT_TRUE(
1757       transformation2.IsApplicable(context.get(), transformation_context));
1758   ApplyAndCheckFreshIds(transformation2, context.get(),
1759                         &transformation_context);
1760   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1761                                                kConsoleMessageConsumer));
1762 
1763   ASSERT_TRUE(
1764       transformation3.IsApplicable(context.get(), transformation_context));
1765   ApplyAndCheckFreshIds(transformation3, context.get(),
1766                         &transformation_context);
1767   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1768                                                kConsoleMessageConsumer));
1769 
1770   ASSERT_TRUE(
1771       transformation4.IsApplicable(context.get(), transformation_context));
1772   ApplyAndCheckFreshIds(transformation4, context.get(),
1773                         &transformation_context);
1774   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1775                                                kConsoleMessageConsumer));
1776 
1777   std::string after_transformation = R"(
1778                OpCapability Shader
1779           %1 = OpExtInstImport "GLSL.std.450"
1780                OpMemoryModel Logical GLSL450
1781                OpEntryPoint Fragment %4 "main"
1782                OpExecutionMode %4 OriginUpperLeft
1783                OpSource ESSL 310
1784                OpName %4 "main"
1785                OpName %8 "i"
1786           %2 = OpTypeVoid
1787           %3 = OpTypeFunction %2
1788           %6 = OpTypeInt 32 1
1789           %7 = OpTypePointer Function %6
1790           %9 = OpConstant %6 0
1791          %16 = OpConstant %6 100
1792          %17 = OpTypeBool
1793          %99 = OpConstantTrue %17
1794          %20 = OpConstant %6 50
1795          %26 = OpConstant %6 2
1796          %30 = OpConstant %6 1
1797           %4 = OpFunction %2 None %3
1798           %5 = OpLabel
1799           %8 = OpVariable %7 Function
1800          %22 = OpVariable %7 Function
1801                OpStore %8 %9
1802                OpBranch %10
1803          %10 = OpLabel
1804                OpLoopMerge %12 %13 None
1805                OpBranch %14
1806          %14 = OpLabel
1807          %15 = OpLoad %6 %8
1808          %18 = OpSLessThan %17 %15 %16
1809                OpBranchConditional %18 %11 %12
1810          %11 = OpLabel
1811                OpBranch %13
1812          %13 = OpLabel
1813          %19 = OpLoad %6 %8
1814          %21 = OpSLessThan %17 %19 %20
1815                OpSelectionMerge %24 None
1816                OpBranchConditional %21 %23 %28
1817          %23 = OpLabel
1818          %25 = OpLoad %6 %8
1819                OpBranchConditional %99 %100 %24
1820         %100 = OpLabel
1821          %27 = OpIAdd %6 %25 %26
1822                OpStore %22 %27
1823                OpBranchConditional %99 %24 %24
1824          %28 = OpLabel
1825          %29 = OpLoad %6 %8
1826                OpBranchConditional %99 %101 %24
1827         %101 = OpLabel
1828          %31 = OpIAdd %6 %29 %30
1829                OpStore %22 %31
1830                OpBranchConditional %99 %24 %24
1831          %24 = OpLabel
1832          %32 = OpLoad %6 %22
1833                OpStore %8 %32
1834                OpBranch %10
1835          %12 = OpLabel
1836                OpReturn
1837                OpFunctionEnd
1838   )";
1839 
1840   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1841 }
1842 
TEST(TransformationAddDeadBreakTest,LoopInContinueConstruct)1843 TEST(TransformationAddDeadBreakTest, LoopInContinueConstruct) {
1844   // Considers some scenarios where there is a loop in a loop's continue
1845   // construct.
1846 
1847   // The SPIR-V for this test is adapted from the following GLSL, with inlining
1848   // applied so that the loop from foo is in the main loop's continue construct:
1849   //
1850   // int foo() {
1851   //   int result = 0;
1852   //   for (int j = 0; j < 10; j++) {
1853   //     result++;
1854   //   }
1855   //   return result;
1856   // }
1857   //
1858   // void main() {
1859   //   for (int i = 0; i < 100; i += foo()) {
1860   //   }
1861   // }
1862 
1863   std::string shader = R"(
1864                OpCapability Shader
1865           %1 = OpExtInstImport "GLSL.std.450"
1866                OpMemoryModel Logical GLSL450
1867                OpEntryPoint Fragment %4 "main"
1868                OpExecutionMode %4 OriginUpperLeft
1869                OpSource ESSL 310
1870                OpName %4 "main"
1871                OpName %31 "i"
1872           %2 = OpTypeVoid
1873           %3 = OpTypeFunction %2
1874           %6 = OpTypeInt 32 1
1875           %7 = OpTypeFunction %6
1876          %10 = OpTypePointer Function %6
1877          %12 = OpConstant %6 0
1878          %20 = OpConstant %6 10
1879          %21 = OpTypeBool
1880         %100 = OpConstantTrue %21
1881          %24 = OpConstant %6 1
1882          %38 = OpConstant %6 100
1883           %4 = OpFunction %2 None %3
1884           %5 = OpLabel
1885          %43 = OpVariable %10 Function
1886          %44 = OpVariable %10 Function
1887          %45 = OpVariable %10 Function
1888          %31 = OpVariable %10 Function
1889                OpStore %31 %12
1890                OpBranch %32
1891          %32 = OpLabel
1892                OpLoopMerge %34 %35 None
1893                OpBranch %36
1894          %36 = OpLabel
1895          %37 = OpLoad %6 %31
1896          %39 = OpSLessThan %21 %37 %38
1897                OpBranchConditional %39 %33 %34
1898          %33 = OpLabel
1899                OpBranch %35
1900          %35 = OpLabel
1901                OpStore %43 %12
1902                OpStore %44 %12
1903                OpBranch %46
1904          %46 = OpLabel
1905                OpLoopMerge %47 %48 None
1906                OpBranch %49
1907          %49 = OpLabel
1908          %50 = OpLoad %6 %44
1909          %51 = OpSLessThan %21 %50 %20
1910                OpBranchConditional %51 %52 %47
1911          %52 = OpLabel
1912          %53 = OpLoad %6 %43
1913                OpBranch %101
1914         %101 = OpLabel
1915          %54 = OpIAdd %6 %53 %24
1916                OpStore %43 %54
1917                OpBranch %48
1918          %48 = OpLabel
1919          %55 = OpLoad %6 %44
1920          %56 = OpIAdd %6 %55 %24
1921                OpStore %44 %56
1922                OpBranch %46
1923          %47 = OpLabel
1924          %57 = OpLoad %6 %43
1925                OpStore %45 %57
1926          %40 = OpLoad %6 %45
1927          %41 = OpLoad %6 %31
1928          %42 = OpIAdd %6 %41 %40
1929                OpStore %31 %42
1930                OpBranch %32
1931          %34 = OpLabel
1932                OpReturn
1933                OpFunctionEnd
1934   )";
1935 
1936   const auto env = SPV_ENV_UNIVERSAL_1_3;
1937   const auto consumer = nullptr;
1938   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1939   spvtools::ValidatorOptions validator_options;
1940   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1941                                                kConsoleMessageConsumer));
1942   TransformationContext transformation_context(
1943       MakeUnique<FactManager>(context.get()), validator_options);
1944   const uint32_t outer_loop_merge = 34;
1945   const uint32_t outer_loop_block = 33;
1946   const uint32_t inner_loop_merge = 47;
1947   const uint32_t inner_loop_block = 52;
1948 
1949   // Some inapplicable cases
1950   ASSERT_FALSE(
1951       TransformationAddDeadBreak(inner_loop_block, outer_loop_merge, true, {})
1952           .IsApplicable(context.get(), transformation_context));
1953   ASSERT_FALSE(
1954       TransformationAddDeadBreak(outer_loop_block, inner_loop_merge, true, {})
1955           .IsApplicable(context.get(), transformation_context));
1956 
1957   auto transformation1 =
1958       TransformationAddDeadBreak(inner_loop_block, inner_loop_merge, true, {});
1959   auto transformation2 =
1960       TransformationAddDeadBreak(outer_loop_block, outer_loop_merge, true, {});
1961 
1962   ASSERT_TRUE(
1963       transformation1.IsApplicable(context.get(), transformation_context));
1964   ApplyAndCheckFreshIds(transformation1, context.get(),
1965                         &transformation_context);
1966   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1967                                                kConsoleMessageConsumer));
1968 
1969   ASSERT_TRUE(
1970       transformation2.IsApplicable(context.get(), transformation_context));
1971   ApplyAndCheckFreshIds(transformation2, context.get(),
1972                         &transformation_context);
1973   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1974                                                kConsoleMessageConsumer));
1975 
1976   std::string after_transformation = R"(
1977                OpCapability Shader
1978           %1 = OpExtInstImport "GLSL.std.450"
1979                OpMemoryModel Logical GLSL450
1980                OpEntryPoint Fragment %4 "main"
1981                OpExecutionMode %4 OriginUpperLeft
1982                OpSource ESSL 310
1983                OpName %4 "main"
1984                OpName %31 "i"
1985           %2 = OpTypeVoid
1986           %3 = OpTypeFunction %2
1987           %6 = OpTypeInt 32 1
1988           %7 = OpTypeFunction %6
1989          %10 = OpTypePointer Function %6
1990          %12 = OpConstant %6 0
1991          %20 = OpConstant %6 10
1992          %21 = OpTypeBool
1993         %100 = OpConstantTrue %21
1994          %24 = OpConstant %6 1
1995          %38 = OpConstant %6 100
1996           %4 = OpFunction %2 None %3
1997           %5 = OpLabel
1998          %43 = OpVariable %10 Function
1999          %44 = OpVariable %10 Function
2000          %45 = OpVariable %10 Function
2001          %31 = OpVariable %10 Function
2002                OpStore %31 %12
2003                OpBranch %32
2004          %32 = OpLabel
2005                OpLoopMerge %34 %35 None
2006                OpBranch %36
2007          %36 = OpLabel
2008          %37 = OpLoad %6 %31
2009          %39 = OpSLessThan %21 %37 %38
2010                OpBranchConditional %39 %33 %34
2011          %33 = OpLabel
2012                OpBranchConditional %100 %35 %34
2013          %35 = OpLabel
2014                OpStore %43 %12
2015                OpStore %44 %12
2016                OpBranch %46
2017          %46 = OpLabel
2018                OpLoopMerge %47 %48 None
2019                OpBranch %49
2020          %49 = OpLabel
2021          %50 = OpLoad %6 %44
2022          %51 = OpSLessThan %21 %50 %20
2023                OpBranchConditional %51 %52 %47
2024          %52 = OpLabel
2025          %53 = OpLoad %6 %43
2026                OpBranchConditional %100 %101 %47
2027         %101 = OpLabel
2028          %54 = OpIAdd %6 %53 %24
2029                OpStore %43 %54
2030                OpBranch %48
2031          %48 = OpLabel
2032          %55 = OpLoad %6 %44
2033          %56 = OpIAdd %6 %55 %24
2034                OpStore %44 %56
2035                OpBranch %46
2036          %47 = OpLabel
2037          %57 = OpLoad %6 %43
2038                OpStore %45 %57
2039          %40 = OpLoad %6 %45
2040          %41 = OpLoad %6 %31
2041          %42 = OpIAdd %6 %41 %40
2042                OpStore %31 %42
2043                OpBranch %32
2044          %34 = OpLabel
2045                OpReturn
2046                OpFunctionEnd
2047   )";
2048 
2049   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
2050 }
2051 
TEST(TransformationAddDeadBreakTest,PhiInstructions)2052 TEST(TransformationAddDeadBreakTest, PhiInstructions) {
2053   // Checks that the transformation works in the presence of phi instructions.
2054 
2055   // The SPIR-V for this test is adapted from the following GLSL, with a bit of
2056   // extra and artificial work to get some interesting uses of OpPhi:
2057   //
2058   // void main() {
2059   //   int x; int y;
2060   //   float f;
2061   //   x = 2;
2062   //   f = 3.0;
2063   //   if (x > y) {
2064   //     x = 3;
2065   //     f = 4.0;
2066   //   } else {
2067   //     x = x + 2;
2068   //     f = f + 10.0;
2069   //   }
2070   //   while (x < y) {
2071   //     x = x + 1;
2072   //     f = f + 1.0;
2073   //   }
2074   //   y = x;
2075   //   f = f + 3.0;
2076   // }
2077 
2078   std::string shader = R"(
2079                OpCapability Shader
2080           %1 = OpExtInstImport "GLSL.std.450"
2081                OpMemoryModel Logical GLSL450
2082                OpEntryPoint Fragment %4 "main"
2083                OpExecutionMode %4 OriginUpperLeft
2084                OpSource ESSL 310
2085                OpName %4 "main"
2086                OpName %8 "x"
2087                OpName %12 "f"
2088                OpName %15 "y"
2089           %2 = OpTypeVoid
2090           %3 = OpTypeFunction %2
2091           %6 = OpTypeInt 32 1
2092           %7 = OpTypePointer Function %6
2093           %9 = OpConstant %6 2
2094          %10 = OpTypeFloat 32
2095          %11 = OpTypePointer Function %10
2096          %13 = OpConstant %10 3
2097          %17 = OpTypeBool
2098          %80 = OpConstantTrue %17
2099          %21 = OpConstant %6 3
2100          %22 = OpConstant %10 4
2101          %27 = OpConstant %10 10
2102          %38 = OpConstant %6 1
2103          %41 = OpConstant %10 1
2104          %46 = OpUndef %6
2105           %4 = OpFunction %2 None %3
2106           %5 = OpLabel
2107           %8 = OpVariable %7 Function
2108          %12 = OpVariable %11 Function
2109          %15 = OpVariable %7 Function
2110                OpStore %8 %9
2111                OpStore %12 %13
2112          %18 = OpSGreaterThan %17 %9 %46
2113                OpSelectionMerge %20 None
2114                OpBranchConditional %18 %19 %23
2115          %19 = OpLabel
2116                OpStore %8 %21
2117                OpStore %12 %22
2118                OpBranch %20
2119          %23 = OpLabel
2120          %25 = OpIAdd %6 %9 %9
2121                OpStore %8 %25
2122                OpBranch %70
2123          %70 = OpLabel
2124          %28 = OpFAdd %10 %13 %27
2125                OpStore %12 %28
2126                OpBranch %20
2127          %20 = OpLabel
2128          %52 = OpPhi %10 %22 %19 %28 %70
2129          %48 = OpPhi %6 %21 %19 %25 %70
2130                OpBranch %29
2131          %29 = OpLabel
2132          %51 = OpPhi %10 %52 %20 %42 %32
2133          %47 = OpPhi %6 %48 %20 %39 %32
2134                OpLoopMerge %31 %32 None
2135                OpBranch %33
2136          %33 = OpLabel
2137          %36 = OpSLessThan %17 %47 %46
2138                OpBranchConditional %36 %30 %31
2139          %30 = OpLabel
2140          %39 = OpIAdd %6 %47 %38
2141                OpStore %8 %39
2142                OpBranch %75
2143          %75 = OpLabel
2144          %42 = OpFAdd %10 %51 %41
2145                OpStore %12 %42
2146                OpBranch %32
2147          %32 = OpLabel
2148                OpBranch %29
2149          %31 = OpLabel
2150          %71 = OpPhi %6 %47 %33
2151          %72 = OpPhi %10 %51 %33
2152                OpStore %15 %71
2153          %45 = OpFAdd %10 %72 %13
2154                OpStore %12 %45
2155                OpReturn
2156                OpFunctionEnd
2157   )";
2158 
2159   const auto env = SPV_ENV_UNIVERSAL_1_3;
2160   const auto consumer = nullptr;
2161   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2162   spvtools::ValidatorOptions validator_options;
2163   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2164                                                kConsoleMessageConsumer));
2165   TransformationContext transformation_context(
2166       MakeUnique<FactManager>(context.get()), validator_options);
2167   // Some inapplicable transformations
2168   // Not applicable because there is already an edge 19->20, so the OpPhis at 20
2169   // do not need to be updated
2170   ASSERT_FALSE(TransformationAddDeadBreak(19, 20, true, {13, 21})
2171                    .IsApplicable(context.get(), transformation_context));
2172   // Not applicable because two OpPhis (not zero) need to be updated at 20
2173   ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {})
2174                    .IsApplicable(context.get(), transformation_context));
2175   // Not applicable because two OpPhis (not just one) need to be updated at 20
2176   ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {13})
2177                    .IsApplicable(context.get(), transformation_context));
2178   // Not applicable because the given ids do not have types that match the
2179   // OpPhis at 20, in order
2180   ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {21, 13})
2181                    .IsApplicable(context.get(), transformation_context));
2182   // Not applicable because id 23 is a label
2183   ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {21, 23})
2184                    .IsApplicable(context.get(), transformation_context));
2185   // Not applicable because 101 is not an id
2186   ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {21, 101})
2187                    .IsApplicable(context.get(), transformation_context));
2188   // Not applicable because ids 51 and 47 are not available at the end of block
2189   // 23
2190   ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {51, 47})
2191                    .IsApplicable(context.get(), transformation_context));
2192 
2193   // Not applicable because OpConstantFalse is not present in the module
2194   ASSERT_FALSE(TransformationAddDeadBreak(19, 20, false, {})
2195                    .IsApplicable(context.get(), transformation_context));
2196 
2197   auto transformation1 = TransformationAddDeadBreak(19, 20, true, {});
2198   auto transformation2 = TransformationAddDeadBreak(23, 20, true, {13, 21});
2199   auto transformation3 = TransformationAddDeadBreak(70, 20, true, {});
2200   auto transformation4 = TransformationAddDeadBreak(30, 31, true, {21, 13});
2201   auto transformation5 = TransformationAddDeadBreak(75, 31, true, {47, 51});
2202 
2203   ASSERT_TRUE(
2204       transformation1.IsApplicable(context.get(), transformation_context));
2205   ApplyAndCheckFreshIds(transformation1, context.get(),
2206                         &transformation_context);
2207   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2208                                                kConsoleMessageConsumer));
2209 
2210   ASSERT_TRUE(
2211       transformation2.IsApplicable(context.get(), transformation_context));
2212   ApplyAndCheckFreshIds(transformation2, context.get(),
2213                         &transformation_context);
2214   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2215                                                kConsoleMessageConsumer));
2216 
2217   ASSERT_TRUE(
2218       transformation3.IsApplicable(context.get(), transformation_context));
2219   ApplyAndCheckFreshIds(transformation3, context.get(),
2220                         &transformation_context);
2221   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2222                                                kConsoleMessageConsumer));
2223 
2224   ASSERT_TRUE(
2225       transformation4.IsApplicable(context.get(), transformation_context));
2226   ApplyAndCheckFreshIds(transformation4, context.get(),
2227                         &transformation_context);
2228   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2229                                                kConsoleMessageConsumer));
2230 
2231   ASSERT_TRUE(
2232       transformation5.IsApplicable(context.get(), transformation_context));
2233   ApplyAndCheckFreshIds(transformation5, context.get(),
2234                         &transformation_context);
2235   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2236                                                kConsoleMessageConsumer));
2237 
2238   std::string after_transformation = R"(
2239                OpCapability Shader
2240           %1 = OpExtInstImport "GLSL.std.450"
2241                OpMemoryModel Logical GLSL450
2242                OpEntryPoint Fragment %4 "main"
2243                OpExecutionMode %4 OriginUpperLeft
2244                OpSource ESSL 310
2245                OpName %4 "main"
2246                OpName %8 "x"
2247                OpName %12 "f"
2248                OpName %15 "y"
2249           %2 = OpTypeVoid
2250           %3 = OpTypeFunction %2
2251           %6 = OpTypeInt 32 1
2252           %7 = OpTypePointer Function %6
2253           %9 = OpConstant %6 2
2254          %10 = OpTypeFloat 32
2255          %11 = OpTypePointer Function %10
2256          %13 = OpConstant %10 3
2257          %17 = OpTypeBool
2258          %80 = OpConstantTrue %17
2259          %21 = OpConstant %6 3
2260          %22 = OpConstant %10 4
2261          %27 = OpConstant %10 10
2262          %38 = OpConstant %6 1
2263          %41 = OpConstant %10 1
2264          %46 = OpUndef %6
2265           %4 = OpFunction %2 None %3
2266           %5 = OpLabel
2267           %8 = OpVariable %7 Function
2268          %12 = OpVariable %11 Function
2269          %15 = OpVariable %7 Function
2270                OpStore %8 %9
2271                OpStore %12 %13
2272          %18 = OpSGreaterThan %17 %9 %46
2273                OpSelectionMerge %20 None
2274                OpBranchConditional %18 %19 %23
2275          %19 = OpLabel
2276                OpStore %8 %21
2277                OpStore %12 %22
2278                OpBranchConditional %80 %20 %20
2279          %23 = OpLabel
2280          %25 = OpIAdd %6 %9 %9
2281                OpStore %8 %25
2282                OpBranchConditional %80 %70 %20
2283          %70 = OpLabel
2284          %28 = OpFAdd %10 %13 %27
2285                OpStore %12 %28
2286                OpBranchConditional %80 %20 %20
2287          %20 = OpLabel
2288          %52 = OpPhi %10 %22 %19 %28 %70 %13 %23
2289          %48 = OpPhi %6 %21 %19 %25 %70 %21 %23
2290                OpBranch %29
2291          %29 = OpLabel
2292          %51 = OpPhi %10 %52 %20 %42 %32
2293          %47 = OpPhi %6 %48 %20 %39 %32
2294                OpLoopMerge %31 %32 None
2295                OpBranch %33
2296          %33 = OpLabel
2297          %36 = OpSLessThan %17 %47 %46
2298                OpBranchConditional %36 %30 %31
2299          %30 = OpLabel
2300          %39 = OpIAdd %6 %47 %38
2301                OpStore %8 %39
2302                OpBranchConditional %80 %75 %31
2303          %75 = OpLabel
2304          %42 = OpFAdd %10 %51 %41
2305                OpStore %12 %42
2306                OpBranchConditional %80 %32 %31
2307          %32 = OpLabel
2308                OpBranch %29
2309          %31 = OpLabel
2310          %71 = OpPhi %6 %47 %33 %21 %30 %47 %75
2311          %72 = OpPhi %10 %51 %33 %13 %30 %51 %75
2312                OpStore %15 %71
2313          %45 = OpFAdd %10 %72 %13
2314                OpStore %12 %45
2315                OpReturn
2316                OpFunctionEnd
2317   )";
2318 
2319   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
2320 }
2321 
TEST(TransformationAddDeadBreakTest,RespectDominanceRules1)2322 TEST(TransformationAddDeadBreakTest, RespectDominanceRules1) {
2323   // Right after the loop, an OpCopyObject defined by the loop is used.  Adding
2324   // a dead break would prevent that use from being dominated by its definition,
2325   // so is not allowed.
2326 
2327   std::string shader = R"(
2328                OpCapability Shader
2329           %1 = OpExtInstImport "GLSL.std.450"
2330                OpMemoryModel Logical GLSL450
2331                OpEntryPoint Fragment %4 "main"
2332                OpExecutionMode %4 OriginUpperLeft
2333                OpSource ESSL 310
2334                OpName %4 "main"
2335           %2 = OpTypeVoid
2336           %3 = OpTypeFunction %2
2337          %10 = OpTypeBool
2338          %11 = OpConstantFalse %10
2339           %4 = OpFunction %2 None %3
2340           %5 = OpLabel
2341                OpBranch %100
2342         %100 = OpLabel
2343                OpLoopMerge %101 %102 None
2344                OpBranch %103
2345         %103 = OpLabel
2346         %200 = OpCopyObject %10 %11
2347                OpBranch %104
2348         %104 = OpLabel
2349                OpBranch %102
2350         %102 = OpLabel
2351                OpBranchConditional %11 %100 %101
2352         %101 = OpLabel
2353         %201 = OpCopyObject %10 %200
2354                OpReturn
2355                OpFunctionEnd
2356   )";
2357 
2358   const auto env = SPV_ENV_UNIVERSAL_1_3;
2359   const auto consumer = nullptr;
2360   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2361   spvtools::ValidatorOptions validator_options;
2362   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2363                                                kConsoleMessageConsumer));
2364   TransformationContext transformation_context(
2365       MakeUnique<FactManager>(context.get()), validator_options);
2366   auto bad_transformation = TransformationAddDeadBreak(100, 101, false, {});
2367   ASSERT_FALSE(
2368       bad_transformation.IsApplicable(context.get(), transformation_context));
2369 }
2370 
TEST(TransformationAddDeadBreakTest,RespectDominanceRules2)2371 TEST(TransformationAddDeadBreakTest, RespectDominanceRules2) {
2372   // This example captures the following idiom:
2373   //
2374   //   if {
2375   // L1:
2376   //   }
2377   //   definition;
2378   // L2:
2379   //   use;
2380   //
2381   // Adding a dead jump from L1 to L2 would lead to 'definition' no longer
2382   // dominating 'use', and so is not allowed.
2383 
2384   std::string shader = R"(
2385                OpCapability Shader
2386           %1 = OpExtInstImport "GLSL.std.450"
2387                OpMemoryModel Logical GLSL450
2388                OpEntryPoint Fragment %4 "main"
2389                OpExecutionMode %4 OriginUpperLeft
2390                OpSource ESSL 310
2391                OpName %4 "main"
2392           %2 = OpTypeVoid
2393           %3 = OpTypeFunction %2
2394          %10 = OpTypeBool
2395          %11 = OpConstantFalse %10
2396           %4 = OpFunction %2 None %3
2397           %5 = OpLabel
2398                OpBranch %100
2399         %100 = OpLabel
2400                OpSelectionMerge %101 None
2401                OpBranchConditional %11 %102 %103
2402         %102 = OpLabel
2403                OpBranch %103
2404         %103 = OpLabel
2405         %200 = OpCopyObject %10 %11
2406                OpBranch %101
2407         %101 = OpLabel
2408         %201 = OpCopyObject %10 %200
2409                OpReturn
2410                OpFunctionEnd
2411   )";
2412 
2413   const auto env = SPV_ENV_UNIVERSAL_1_3;
2414   const auto consumer = nullptr;
2415   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2416   spvtools::ValidatorOptions validator_options;
2417   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2418                                                kConsoleMessageConsumer));
2419   TransformationContext transformation_context(
2420       MakeUnique<FactManager>(context.get()), validator_options);
2421   auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {});
2422   ASSERT_FALSE(
2423       bad_transformation.IsApplicable(context.get(), transformation_context));
2424 }
2425 
TEST(TransformationAddDeadBreakTest,RespectDominanceRules3)2426 TEST(TransformationAddDeadBreakTest, RespectDominanceRules3) {
2427   // Right after the loop, an OpCopyObject defined by the loop is used in an
2428   // OpPhi. Adding a dead break is OK in this case, due to the use being in an
2429   // OpPhi.
2430 
2431   std::string shader = R"(
2432                OpCapability Shader
2433           %1 = OpExtInstImport "GLSL.std.450"
2434                OpMemoryModel Logical GLSL450
2435                OpEntryPoint Fragment %4 "main"
2436                OpExecutionMode %4 OriginUpperLeft
2437                OpSource ESSL 310
2438                OpName %4 "main"
2439           %2 = OpTypeVoid
2440           %3 = OpTypeFunction %2
2441          %10 = OpTypeBool
2442          %11 = OpConstantFalse %10
2443           %4 = OpFunction %2 None %3
2444           %5 = OpLabel
2445                OpBranch %100
2446         %100 = OpLabel
2447                OpLoopMerge %101 %102 None
2448                OpBranch %103
2449         %103 = OpLabel
2450         %200 = OpCopyObject %10 %11
2451                OpBranch %104
2452         %104 = OpLabel
2453                OpBranch %102
2454         %102 = OpLabel
2455                OpBranchConditional %11 %100 %101
2456         %101 = OpLabel
2457         %201 = OpPhi %10 %200 %102
2458                OpReturn
2459                OpFunctionEnd
2460   )";
2461 
2462   const auto env = SPV_ENV_UNIVERSAL_1_3;
2463   const auto consumer = nullptr;
2464   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2465   spvtools::ValidatorOptions validator_options;
2466   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2467                                                kConsoleMessageConsumer));
2468   TransformationContext transformation_context(
2469       MakeUnique<FactManager>(context.get()), validator_options);
2470   auto good_transformation = TransformationAddDeadBreak(100, 101, false, {11});
2471   ASSERT_TRUE(
2472       good_transformation.IsApplicable(context.get(), transformation_context));
2473 
2474   ApplyAndCheckFreshIds(good_transformation, context.get(),
2475                         &transformation_context);
2476   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2477                                                kConsoleMessageConsumer));
2478 
2479   std::string after_transformation = R"(
2480                OpCapability Shader
2481           %1 = OpExtInstImport "GLSL.std.450"
2482                OpMemoryModel Logical GLSL450
2483                OpEntryPoint Fragment %4 "main"
2484                OpExecutionMode %4 OriginUpperLeft
2485                OpSource ESSL 310
2486                OpName %4 "main"
2487           %2 = OpTypeVoid
2488           %3 = OpTypeFunction %2
2489          %10 = OpTypeBool
2490          %11 = OpConstantFalse %10
2491           %4 = OpFunction %2 None %3
2492           %5 = OpLabel
2493                OpBranch %100
2494         %100 = OpLabel
2495                OpLoopMerge %101 %102 None
2496                OpBranchConditional %11 %101 %103
2497         %103 = OpLabel
2498         %200 = OpCopyObject %10 %11
2499                OpBranch %104
2500         %104 = OpLabel
2501                OpBranch %102
2502         %102 = OpLabel
2503                OpBranchConditional %11 %100 %101
2504         %101 = OpLabel
2505         %201 = OpPhi %10 %200 %102 %11 %100
2506                OpReturn
2507                OpFunctionEnd
2508   )";
2509 
2510   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
2511 }
2512 
TEST(TransformationAddDeadBreakTest,RespectDominanceRules4)2513 TEST(TransformationAddDeadBreakTest, RespectDominanceRules4) {
2514   // This example captures the following idiom:
2515   //
2516   //   if {
2517   // L1:
2518   //   }
2519   //   definition;
2520   // L2:
2521   //   use in OpPhi;
2522   //
2523   // Adding a dead jump from L1 to L2 is OK, due to 'use' being in an OpPhi.
2524 
2525   std::string shader = R"(
2526                OpCapability Shader
2527           %1 = OpExtInstImport "GLSL.std.450"
2528                OpMemoryModel Logical GLSL450
2529                OpEntryPoint Fragment %4 "main"
2530                OpExecutionMode %4 OriginUpperLeft
2531                OpSource ESSL 310
2532                OpName %4 "main"
2533           %2 = OpTypeVoid
2534           %3 = OpTypeFunction %2
2535          %10 = OpTypeBool
2536          %11 = OpConstantFalse %10
2537           %4 = OpFunction %2 None %3
2538           %5 = OpLabel
2539                OpBranch %100
2540         %100 = OpLabel
2541                OpSelectionMerge %101 None
2542                OpBranchConditional %11 %102 %103
2543         %102 = OpLabel
2544                OpBranch %103
2545         %103 = OpLabel
2546         %200 = OpCopyObject %10 %11
2547                OpBranch %101
2548         %101 = OpLabel
2549         %201 = OpPhi %10 %200 %103
2550                OpReturn
2551                OpFunctionEnd
2552   )";
2553 
2554   const auto env = SPV_ENV_UNIVERSAL_1_3;
2555   const auto consumer = nullptr;
2556   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2557   spvtools::ValidatorOptions validator_options;
2558   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2559                                                kConsoleMessageConsumer));
2560   TransformationContext transformation_context(
2561       MakeUnique<FactManager>(context.get()), validator_options);
2562   auto good_transformation = TransformationAddDeadBreak(102, 101, false, {11});
2563   ASSERT_TRUE(
2564       good_transformation.IsApplicable(context.get(), transformation_context));
2565 
2566   ApplyAndCheckFreshIds(good_transformation, context.get(),
2567                         &transformation_context);
2568   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2569                                                kConsoleMessageConsumer));
2570 
2571   std::string after_transformation = R"(
2572                OpCapability Shader
2573           %1 = OpExtInstImport "GLSL.std.450"
2574                OpMemoryModel Logical GLSL450
2575                OpEntryPoint Fragment %4 "main"
2576                OpExecutionMode %4 OriginUpperLeft
2577                OpSource ESSL 310
2578                OpName %4 "main"
2579           %2 = OpTypeVoid
2580           %3 = OpTypeFunction %2
2581          %10 = OpTypeBool
2582          %11 = OpConstantFalse %10
2583           %4 = OpFunction %2 None %3
2584           %5 = OpLabel
2585                OpBranch %100
2586         %100 = OpLabel
2587                OpSelectionMerge %101 None
2588                OpBranchConditional %11 %102 %103
2589         %102 = OpLabel
2590                OpBranchConditional %11 %101 %103
2591         %103 = OpLabel
2592         %200 = OpCopyObject %10 %11
2593                OpBranch %101
2594         %101 = OpLabel
2595         %201 = OpPhi %10 %200 %103 %11 %102
2596                OpReturn
2597                OpFunctionEnd
2598   )";
2599 
2600   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
2601 }
2602 
TEST(TransformationAddDeadBreakTest,RespectDominanceRules5)2603 TEST(TransformationAddDeadBreakTest, RespectDominanceRules5) {
2604   // After, but not right after, the loop, an OpCopyObject defined by the loop
2605   // is used in an OpPhi. Adding a dead break is not OK in this case.
2606 
2607   std::string shader = R"(
2608                OpCapability Shader
2609           %1 = OpExtInstImport "GLSL.std.450"
2610                OpMemoryModel Logical GLSL450
2611                OpEntryPoint Fragment %4 "main"
2612                OpExecutionMode %4 OriginUpperLeft
2613                OpSource ESSL 310
2614                OpName %4 "main"
2615           %2 = OpTypeVoid
2616           %3 = OpTypeFunction %2
2617          %10 = OpTypeBool
2618          %11 = OpConstantFalse %10
2619           %4 = OpFunction %2 None %3
2620           %5 = OpLabel
2621                OpBranch %100
2622         %100 = OpLabel
2623                OpLoopMerge %101 %102 None
2624                OpBranch %103
2625         %103 = OpLabel
2626         %200 = OpCopyObject %10 %11
2627                OpBranch %104
2628         %104 = OpLabel
2629                OpBranch %102
2630         %102 = OpLabel
2631                OpBranchConditional %11 %100 %101
2632         %101 = OpLabel
2633                OpBranch %105
2634         %105 = OpLabel
2635         %201 = OpPhi %10 %200 %101
2636                OpReturn
2637                OpFunctionEnd
2638   )";
2639 
2640   const auto env = SPV_ENV_UNIVERSAL_1_3;
2641   const auto consumer = nullptr;
2642   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2643   spvtools::ValidatorOptions validator_options;
2644   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2645                                                kConsoleMessageConsumer));
2646   TransformationContext transformation_context(
2647       MakeUnique<FactManager>(context.get()), validator_options);
2648   auto bad_transformation = TransformationAddDeadBreak(100, 101, false, {});
2649   ASSERT_FALSE(
2650       bad_transformation.IsApplicable(context.get(), transformation_context));
2651 }
2652 
TEST(TransformationAddDeadBreakTest,RespectDominanceRules6)2653 TEST(TransformationAddDeadBreakTest, RespectDominanceRules6) {
2654   // This example captures the following idiom:
2655   //
2656   //   if {
2657   // L1:
2658   //   }
2659   //   definition;
2660   // L2:
2661   //   goto L3;
2662   // L3:
2663   //   use in OpPhi;
2664   //
2665   // Adding a dead jump from L1 to L2 not OK, due to the use in an OpPhi being
2666   // in L3.
2667 
2668   std::string shader = R"(
2669                OpCapability Shader
2670           %1 = OpExtInstImport "GLSL.std.450"
2671                OpMemoryModel Logical GLSL450
2672                OpEntryPoint Fragment %4 "main"
2673                OpExecutionMode %4 OriginUpperLeft
2674                OpSource ESSL 310
2675                OpName %4 "main"
2676           %2 = OpTypeVoid
2677           %3 = OpTypeFunction %2
2678          %10 = OpTypeBool
2679          %11 = OpConstantFalse %10
2680           %4 = OpFunction %2 None %3
2681           %5 = OpLabel
2682                OpBranch %100
2683         %100 = OpLabel
2684                OpSelectionMerge %101 None
2685                OpBranchConditional %11 %102 %103
2686         %102 = OpLabel
2687                OpBranch %103
2688         %103 = OpLabel
2689         %200 = OpCopyObject %10 %11
2690                OpBranch %101
2691         %101 = OpLabel
2692                OpBranch %150
2693         %150 = OpLabel
2694         %201 = OpPhi %10 %200 %101
2695                OpReturn
2696                OpFunctionEnd
2697   )";
2698 
2699   const auto env = SPV_ENV_UNIVERSAL_1_3;
2700   const auto consumer = nullptr;
2701   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2702   spvtools::ValidatorOptions validator_options;
2703   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2704                                                kConsoleMessageConsumer));
2705   TransformationContext transformation_context(
2706       MakeUnique<FactManager>(context.get()), validator_options);
2707   auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {});
2708   ASSERT_FALSE(
2709       bad_transformation.IsApplicable(context.get(), transformation_context));
2710 }
2711 
TEST(TransformationAddDeadBreakTest,RespectDominanceRules7)2712 TEST(TransformationAddDeadBreakTest, RespectDominanceRules7) {
2713   // This example - a variation on an earlier test - captures the following
2714   // idiom:
2715   //
2716   //   loop {
2717   // L1:
2718   //   }
2719   //   definition;
2720   // L2:
2721   //   use;
2722   //
2723   // Adding a dead jump from L1 to L2 would lead to 'definition' no longer
2724   // dominating 'use', and so is not allowed.
2725   //
2726   // This version of the test captures the case where L1 appears after the
2727   // loop merge (which SPIR-V dominance rules allow).
2728 
2729   std::string shader = R"(
2730                OpCapability Shader
2731           %1 = OpExtInstImport "GLSL.std.450"
2732                OpMemoryModel Logical GLSL450
2733                OpEntryPoint Fragment %4 "main"
2734                OpExecutionMode %4 OriginUpperLeft
2735                OpSource ESSL 310
2736                OpName %4 "main"
2737           %2 = OpTypeVoid
2738           %3 = OpTypeFunction %2
2739          %10 = OpTypeBool
2740          %11 = OpConstantFalse %10
2741           %4 = OpFunction %2 None %3
2742           %5 = OpLabel
2743                OpBranch %100
2744         %100 = OpLabel
2745                OpLoopMerge %101 %104 None
2746                OpBranchConditional %11 %102 %103
2747         %103 = OpLabel
2748         %200 = OpCopyObject %10 %11
2749                OpBranch %101
2750         %101 = OpLabel
2751         %201 = OpCopyObject %10 %200
2752                OpReturn
2753         %102 = OpLabel
2754                OpBranch %103
2755         %104 = OpLabel
2756                OpBranch %100
2757                OpFunctionEnd
2758   )";
2759 
2760   const auto env = SPV_ENV_UNIVERSAL_1_3;
2761   const auto consumer = nullptr;
2762   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2763   spvtools::ValidatorOptions validator_options;
2764   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2765                                                kConsoleMessageConsumer));
2766   TransformationContext transformation_context(
2767       MakeUnique<FactManager>(context.get()), validator_options);
2768   auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {});
2769   ASSERT_FALSE(
2770       bad_transformation.IsApplicable(context.get(), transformation_context));
2771 }
2772 
TEST(TransformationAddDeadBreakTest,RespectDominanceRules8)2773 TEST(TransformationAddDeadBreakTest, RespectDominanceRules8) {
2774   // A variation of RespectDominanceRules8 where the defining block appears
2775   // in the loop, but after the definition of interest.
2776 
2777   std::string shader = R"(
2778                OpCapability Shader
2779           %1 = OpExtInstImport "GLSL.std.450"
2780                OpMemoryModel Logical GLSL450
2781                OpEntryPoint Fragment %4 "main"
2782                OpExecutionMode %4 OriginUpperLeft
2783                OpSource ESSL 310
2784                OpName %4 "main"
2785           %2 = OpTypeVoid
2786           %3 = OpTypeFunction %2
2787          %10 = OpTypeBool
2788          %11 = OpConstantFalse %10
2789           %4 = OpFunction %2 None %3
2790           %5 = OpLabel
2791                OpBranch %100
2792         %100 = OpLabel
2793                OpLoopMerge %101 %104 None
2794                OpBranchConditional %11 %102 %103
2795         %103 = OpLabel
2796         %200 = OpCopyObject %10 %11
2797                OpBranch %101
2798         %102 = OpLabel
2799                OpBranch %103
2800         %101 = OpLabel
2801         %201 = OpCopyObject %10 %200
2802                OpReturn
2803         %104 = OpLabel
2804                OpBranch %100
2805                OpFunctionEnd
2806   )";
2807 
2808   const auto env = SPV_ENV_UNIVERSAL_1_3;
2809   const auto consumer = nullptr;
2810   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2811   spvtools::ValidatorOptions validator_options;
2812   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2813                                                kConsoleMessageConsumer));
2814   TransformationContext transformation_context(
2815       MakeUnique<FactManager>(context.get()), validator_options);
2816   auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {});
2817   ASSERT_FALSE(
2818       bad_transformation.IsApplicable(context.get(), transformation_context));
2819 }
2820 
TEST(TransformationAddDeadBreakTest,BreakWouldDisobeyDominanceBlockOrderingRules)2821 TEST(TransformationAddDeadBreakTest,
2822      BreakWouldDisobeyDominanceBlockOrderingRules) {
2823   std::string shader = R"(
2824                OpCapability Shader
2825           %1 = OpExtInstImport "GLSL.std.450"
2826                OpMemoryModel Logical GLSL450
2827                OpEntryPoint Fragment %4 "main"
2828                OpExecutionMode %4 OriginUpperLeft
2829                OpSource ESSL 310
2830           %2 = OpTypeVoid
2831           %3 = OpTypeFunction %2
2832           %6 = OpTypeBool
2833           %9 = OpConstantTrue %6
2834           %4 = OpFunction %2 None %3
2835           %5 = OpLabel
2836                OpBranch %10
2837          %10 = OpLabel
2838                OpLoopMerge %16 %15 None
2839                OpBranch %11
2840          %11 = OpLabel
2841                OpSelectionMerge %14 None
2842                OpBranchConditional %9 %12 %13
2843          %14 = OpLabel
2844                OpBranch %15
2845          %12 = OpLabel
2846                OpBranch %16
2847          %13 = OpLabel
2848                OpBranch %16
2849          %15 = OpLabel
2850                OpBranch %10
2851          %16 = OpLabel
2852                OpReturn
2853                OpFunctionEnd
2854   )";
2855 
2856   const auto env = SPV_ENV_UNIVERSAL_1_3;
2857   const auto consumer = nullptr;
2858   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2859   spvtools::ValidatorOptions validator_options;
2860   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2861                                                kConsoleMessageConsumer));
2862   TransformationContext transformation_context(
2863       MakeUnique<FactManager>(context.get()), validator_options);
2864   // Bad because 14 comes before 12 in the module, and 14 has no predecessors.
2865   // This means that an edge from 12 to 14 will lead to 12 dominating 14, which
2866   // is illegal if 12 appears after 14.
2867   auto bad_transformation = TransformationAddDeadBreak(12, 14, true, {});
2868   ASSERT_FALSE(
2869       bad_transformation.IsApplicable(context.get(), transformation_context));
2870 }
2871 
2872 }  // namespace
2873 }  // namespace fuzz
2874 }  // namespace spvtools
2875