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