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