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