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_split_block.h"
16
17 #include "gtest/gtest.h"
18 #include "source/fuzz/fuzzer_util.h"
19 #include "source/fuzz/instruction_descriptor.h"
20 #include "test/fuzz/fuzz_test_util.h"
21
22 namespace spvtools {
23 namespace fuzz {
24 namespace {
25
TEST(TransformationSplitBlockTest,NotApplicable)26 TEST(TransformationSplitBlockTest, NotApplicable) {
27 // The SPIR-V in this test came from the following fragment shader, with
28 // local store elimination applied to get some OpPhi instructions.
29 //
30 // void main() {
31 // int x;
32 // int i;
33 // for (i = 0; i < 100; i++) {
34 // x += i;
35 // }
36 // }
37
38 std::string shader = R"(
39 OpCapability Shader
40 %1 = OpExtInstImport "GLSL.std.450"
41 OpMemoryModel Logical GLSL450
42 OpEntryPoint Fragment %4 "main"
43 OpExecutionMode %4 OriginUpperLeft
44 OpSource ESSL 310
45 OpName %4 "main"
46 OpName %8 "i"
47 OpName %19 "x"
48 OpDecorate %8 RelaxedPrecision
49 OpDecorate %19 RelaxedPrecision
50 OpDecorate %22 RelaxedPrecision
51 OpDecorate %25 RelaxedPrecision
52 OpDecorate %26 RelaxedPrecision
53 OpDecorate %27 RelaxedPrecision
54 %2 = OpTypeVoid
55 %3 = OpTypeFunction %2
56 %6 = OpTypeInt 32 1
57 %7 = OpTypePointer Function %6
58 %9 = OpConstant %6 0
59 %16 = OpConstant %6 100
60 %17 = OpTypeBool
61 %24 = OpConstant %6 1
62 %28 = OpUndef %6
63 %4 = OpFunction %2 None %3
64 %5 = OpLabel
65 %8 = OpVariable %7 Function
66 %19 = OpVariable %7 Function
67 OpStore %8 %9
68 OpBranch %10
69 %10 = OpLabel
70 %27 = OpPhi %6 %28 %5 %22 %13
71 %26 = OpPhi %6 %9 %5 %25 %13
72 OpLoopMerge %12 %13 None
73 OpBranch %14
74 %14 = OpLabel
75 %18 = OpSLessThan %17 %26 %16
76 OpBranchConditional %18 %11 %12
77 %11 = OpLabel
78 %22 = OpIAdd %6 %27 %26
79 OpStore %19 %22
80 OpBranch %13
81 %13 = OpLabel
82 %25 = OpIAdd %6 %26 %24
83 OpStore %8 %25
84 OpBranch %10
85 %12 = OpLabel
86 OpReturn
87 OpFunctionEnd
88 )";
89
90 const auto env = SPV_ENV_UNIVERSAL_1_3;
91 const auto consumer = nullptr;
92 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
93
94 spvtools::ValidatorOptions validator_options;
95 TransformationContext transformation_context(
96 MakeUnique<FactManager>(context.get()), validator_options);
97 // No split before OpVariable
98 ASSERT_FALSE(TransformationSplitBlock(
99 MakeInstructionDescriptor(8, SpvOpVariable, 0), 100)
100 .IsApplicable(context.get(), transformation_context));
101 ASSERT_FALSE(TransformationSplitBlock(
102 MakeInstructionDescriptor(8, SpvOpVariable, 1), 100)
103 .IsApplicable(context.get(), transformation_context));
104
105 // No split before OpLabel
106 ASSERT_FALSE(TransformationSplitBlock(
107 MakeInstructionDescriptor(14, SpvOpLabel, 0), 100)
108 .IsApplicable(context.get(), transformation_context));
109
110 // No split if base instruction is outside a function
111 ASSERT_FALSE(
112 TransformationSplitBlock(MakeInstructionDescriptor(1, SpvOpLabel, 0), 100)
113 .IsApplicable(context.get(), transformation_context));
114 ASSERT_FALSE(TransformationSplitBlock(
115 MakeInstructionDescriptor(1, SpvOpExecutionMode, 0), 100)
116 .IsApplicable(context.get(), transformation_context));
117
118 // No split if block is loop header
119 ASSERT_FALSE(
120 TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100)
121 .IsApplicable(context.get(), transformation_context));
122 ASSERT_FALSE(
123 TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100)
124 .IsApplicable(context.get(), transformation_context));
125
126 // No split if base instruction does not exist
127 ASSERT_FALSE(
128 TransformationSplitBlock(MakeInstructionDescriptor(88, SpvOpIAdd, 0), 100)
129 .IsApplicable(context.get(), transformation_context));
130 ASSERT_FALSE(TransformationSplitBlock(
131 MakeInstructionDescriptor(88, SpvOpIMul, 22), 100)
132 .IsApplicable(context.get(), transformation_context));
133
134 // No split if too many instructions with the desired opcode are skipped
135 ASSERT_FALSE(
136 TransformationSplitBlock(
137 MakeInstructionDescriptor(18, SpvOpBranchConditional, 1), 100)
138 .IsApplicable(context.get(), transformation_context));
139
140 // No split if id in use
141 ASSERT_FALSE(TransformationSplitBlock(
142 MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 27)
143 .IsApplicable(context.get(), transformation_context));
144 ASSERT_FALSE(TransformationSplitBlock(
145 MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 14)
146 .IsApplicable(context.get(), transformation_context));
147 }
148
TEST(TransformationSplitBlockTest,SplitBlockSeveralTimes)149 TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) {
150 // The SPIR-V in this test came from the following fragment shader:
151 //
152 // void main() {
153 // int a;
154 // int b;
155 // a = 1;
156 // b = a;
157 // a = b;
158 // b = 2;
159 // b++;
160 // }
161
162 std::string shader = R"(
163 OpCapability Shader
164 %1 = OpExtInstImport "GLSL.std.450"
165 OpMemoryModel Logical GLSL450
166 OpEntryPoint Fragment %4 "main"
167 OpExecutionMode %4 OriginUpperLeft
168 OpSource ESSL 310
169 OpName %4 "main"
170 OpName %8 "a"
171 OpName %10 "b"
172 OpDecorate %8 RelaxedPrecision
173 OpDecorate %10 RelaxedPrecision
174 OpDecorate %11 RelaxedPrecision
175 OpDecorate %12 RelaxedPrecision
176 OpDecorate %14 RelaxedPrecision
177 OpDecorate %15 RelaxedPrecision
178 %2 = OpTypeVoid
179 %3 = OpTypeFunction %2
180 %6 = OpTypeInt 32 1
181 %7 = OpTypePointer Function %6
182 %9 = OpConstant %6 1
183 %13 = OpConstant %6 2
184 %4 = OpFunction %2 None %3
185 %5 = OpLabel
186 %8 = OpVariable %7 Function
187 %10 = OpVariable %7 Function
188 OpStore %8 %9
189 %11 = OpLoad %6 %8
190 OpStore %10 %11
191 %12 = OpLoad %6 %10
192 OpStore %8 %12
193 OpStore %10 %13
194 %14 = OpLoad %6 %10
195 %15 = OpIAdd %6 %14 %9
196 OpStore %10 %15
197 OpReturn
198 OpFunctionEnd
199 )";
200
201 const auto env = SPV_ENV_UNIVERSAL_1_3;
202 const auto consumer = nullptr;
203 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
204
205 spvtools::ValidatorOptions validator_options;
206 TransformationContext transformation_context(
207 MakeUnique<FactManager>(context.get()), validator_options);
208 auto split_1 = TransformationSplitBlock(
209 MakeInstructionDescriptor(5, SpvOpStore, 0), 100);
210 ASSERT_TRUE(split_1.IsApplicable(context.get(), transformation_context));
211 ApplyAndCheckFreshIds(split_1, context.get(), &transformation_context);
212 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
213 kConsoleMessageConsumer));
214
215 std::string after_split_1 = R"(
216 OpCapability Shader
217 %1 = OpExtInstImport "GLSL.std.450"
218 OpMemoryModel Logical GLSL450
219 OpEntryPoint Fragment %4 "main"
220 OpExecutionMode %4 OriginUpperLeft
221 OpSource ESSL 310
222 OpName %4 "main"
223 OpName %8 "a"
224 OpName %10 "b"
225 OpDecorate %8 RelaxedPrecision
226 OpDecorate %10 RelaxedPrecision
227 OpDecorate %11 RelaxedPrecision
228 OpDecorate %12 RelaxedPrecision
229 OpDecorate %14 RelaxedPrecision
230 OpDecorate %15 RelaxedPrecision
231 %2 = OpTypeVoid
232 %3 = OpTypeFunction %2
233 %6 = OpTypeInt 32 1
234 %7 = OpTypePointer Function %6
235 %9 = OpConstant %6 1
236 %13 = OpConstant %6 2
237 %4 = OpFunction %2 None %3
238 %5 = OpLabel
239 %8 = OpVariable %7 Function
240 %10 = OpVariable %7 Function
241 OpBranch %100
242 %100 = OpLabel
243 OpStore %8 %9
244 %11 = OpLoad %6 %8
245 OpStore %10 %11
246 %12 = OpLoad %6 %10
247 OpStore %8 %12
248 OpStore %10 %13
249 %14 = OpLoad %6 %10
250 %15 = OpIAdd %6 %14 %9
251 OpStore %10 %15
252 OpReturn
253 OpFunctionEnd
254 )";
255 ASSERT_TRUE(IsEqual(env, after_split_1, context.get()));
256
257 auto split_2 = TransformationSplitBlock(
258 MakeInstructionDescriptor(11, SpvOpStore, 0), 101);
259 ASSERT_TRUE(split_2.IsApplicable(context.get(), transformation_context));
260 ApplyAndCheckFreshIds(split_2, context.get(), &transformation_context);
261 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
262 kConsoleMessageConsumer));
263
264 std::string after_split_2 = R"(
265 OpCapability Shader
266 %1 = OpExtInstImport "GLSL.std.450"
267 OpMemoryModel Logical GLSL450
268 OpEntryPoint Fragment %4 "main"
269 OpExecutionMode %4 OriginUpperLeft
270 OpSource ESSL 310
271 OpName %4 "main"
272 OpName %8 "a"
273 OpName %10 "b"
274 OpDecorate %8 RelaxedPrecision
275 OpDecorate %10 RelaxedPrecision
276 OpDecorate %11 RelaxedPrecision
277 OpDecorate %12 RelaxedPrecision
278 OpDecorate %14 RelaxedPrecision
279 OpDecorate %15 RelaxedPrecision
280 %2 = OpTypeVoid
281 %3 = OpTypeFunction %2
282 %6 = OpTypeInt 32 1
283 %7 = OpTypePointer Function %6
284 %9 = OpConstant %6 1
285 %13 = OpConstant %6 2
286 %4 = OpFunction %2 None %3
287 %5 = OpLabel
288 %8 = OpVariable %7 Function
289 %10 = OpVariable %7 Function
290 OpBranch %100
291 %100 = OpLabel
292 OpStore %8 %9
293 %11 = OpLoad %6 %8
294 OpBranch %101
295 %101 = OpLabel
296 OpStore %10 %11
297 %12 = OpLoad %6 %10
298 OpStore %8 %12
299 OpStore %10 %13
300 %14 = OpLoad %6 %10
301 %15 = OpIAdd %6 %14 %9
302 OpStore %10 %15
303 OpReturn
304 OpFunctionEnd
305 )";
306 ASSERT_TRUE(IsEqual(env, after_split_2, context.get()));
307
308 auto split_3 = TransformationSplitBlock(
309 MakeInstructionDescriptor(14, SpvOpLoad, 0), 102);
310 ASSERT_TRUE(split_3.IsApplicable(context.get(), transformation_context));
311 ApplyAndCheckFreshIds(split_3, context.get(), &transformation_context);
312 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
313 kConsoleMessageConsumer));
314
315 std::string after_split_3 = R"(
316 OpCapability Shader
317 %1 = OpExtInstImport "GLSL.std.450"
318 OpMemoryModel Logical GLSL450
319 OpEntryPoint Fragment %4 "main"
320 OpExecutionMode %4 OriginUpperLeft
321 OpSource ESSL 310
322 OpName %4 "main"
323 OpName %8 "a"
324 OpName %10 "b"
325 OpDecorate %8 RelaxedPrecision
326 OpDecorate %10 RelaxedPrecision
327 OpDecorate %11 RelaxedPrecision
328 OpDecorate %12 RelaxedPrecision
329 OpDecorate %14 RelaxedPrecision
330 OpDecorate %15 RelaxedPrecision
331 %2 = OpTypeVoid
332 %3 = OpTypeFunction %2
333 %6 = OpTypeInt 32 1
334 %7 = OpTypePointer Function %6
335 %9 = OpConstant %6 1
336 %13 = OpConstant %6 2
337 %4 = OpFunction %2 None %3
338 %5 = OpLabel
339 %8 = OpVariable %7 Function
340 %10 = OpVariable %7 Function
341 OpBranch %100
342 %100 = OpLabel
343 OpStore %8 %9
344 %11 = OpLoad %6 %8
345 OpBranch %101
346 %101 = OpLabel
347 OpStore %10 %11
348 %12 = OpLoad %6 %10
349 OpStore %8 %12
350 OpStore %10 %13
351 OpBranch %102
352 %102 = OpLabel
353 %14 = OpLoad %6 %10
354 %15 = OpIAdd %6 %14 %9
355 OpStore %10 %15
356 OpReturn
357 OpFunctionEnd
358 )";
359 ASSERT_TRUE(IsEqual(env, after_split_3, context.get()));
360 }
361
TEST(TransformationSplitBlockTest,SplitBlockBeforeSelectBranch)362 TEST(TransformationSplitBlockTest, SplitBlockBeforeSelectBranch) {
363 // The SPIR-V in this test came from the following fragment shader:
364 //
365 // void main() {
366 // int x, y;
367 // x = 2;
368 // if (x < y) {
369 // y = 3;
370 // } else {
371 // y = 4;
372 // }
373 // }
374
375 std::string shader = R"(
376 OpCapability Shader
377 %1 = OpExtInstImport "GLSL.std.450"
378 OpMemoryModel Logical GLSL450
379 OpEntryPoint Fragment %4 "main"
380 OpExecutionMode %4 OriginUpperLeft
381 OpSource ESSL 310
382 OpName %4 "main"
383 OpName %8 "x"
384 OpName %11 "y"
385 OpDecorate %8 RelaxedPrecision
386 OpDecorate %10 RelaxedPrecision
387 OpDecorate %11 RelaxedPrecision
388 OpDecorate %12 RelaxedPrecision
389 %2 = OpTypeVoid
390 %3 = OpTypeFunction %2
391 %6 = OpTypeInt 32 1
392 %7 = OpTypePointer Function %6
393 %9 = OpConstant %6 2
394 %13 = OpTypeBool
395 %17 = OpConstant %6 3
396 %19 = OpConstant %6 4
397 %4 = OpFunction %2 None %3
398 %5 = OpLabel
399 %8 = OpVariable %7 Function
400 %11 = OpVariable %7 Function
401 OpStore %8 %9
402 %10 = OpLoad %6 %8
403 %12 = OpLoad %6 %11
404 %14 = OpSLessThan %13 %10 %12
405 OpSelectionMerge %16 None
406 OpBranchConditional %14 %15 %18
407 %15 = OpLabel
408 OpStore %11 %17
409 OpBranch %16
410 %18 = OpLabel
411 OpStore %11 %19
412 OpBranch %16
413 %16 = OpLabel
414 OpReturn
415 OpFunctionEnd
416 )";
417
418 const auto env = SPV_ENV_UNIVERSAL_1_3;
419 const auto consumer = nullptr;
420 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
421
422 spvtools::ValidatorOptions validator_options;
423 TransformationContext transformation_context(
424 MakeUnique<FactManager>(context.get()), validator_options);
425 // Illegal to split between the merge and the conditional branch.
426 ASSERT_FALSE(
427 TransformationSplitBlock(
428 MakeInstructionDescriptor(14, SpvOpBranchConditional, 0), 100)
429 .IsApplicable(context.get(), transformation_context));
430 ASSERT_FALSE(
431 TransformationSplitBlock(
432 MakeInstructionDescriptor(12, SpvOpBranchConditional, 0), 100)
433 .IsApplicable(context.get(), transformation_context));
434
435 auto split = TransformationSplitBlock(
436 MakeInstructionDescriptor(14, SpvOpSelectionMerge, 0), 100);
437 ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
438 ApplyAndCheckFreshIds(split, context.get(), &transformation_context);
439 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
440 kConsoleMessageConsumer));
441
442 std::string after_split = R"(
443 OpCapability Shader
444 %1 = OpExtInstImport "GLSL.std.450"
445 OpMemoryModel Logical GLSL450
446 OpEntryPoint Fragment %4 "main"
447 OpExecutionMode %4 OriginUpperLeft
448 OpSource ESSL 310
449 OpName %4 "main"
450 OpName %8 "x"
451 OpName %11 "y"
452 OpDecorate %8 RelaxedPrecision
453 OpDecorate %10 RelaxedPrecision
454 OpDecorate %11 RelaxedPrecision
455 OpDecorate %12 RelaxedPrecision
456 %2 = OpTypeVoid
457 %3 = OpTypeFunction %2
458 %6 = OpTypeInt 32 1
459 %7 = OpTypePointer Function %6
460 %9 = OpConstant %6 2
461 %13 = OpTypeBool
462 %17 = OpConstant %6 3
463 %19 = OpConstant %6 4
464 %4 = OpFunction %2 None %3
465 %5 = OpLabel
466 %8 = OpVariable %7 Function
467 %11 = OpVariable %7 Function
468 OpStore %8 %9
469 %10 = OpLoad %6 %8
470 %12 = OpLoad %6 %11
471 %14 = OpSLessThan %13 %10 %12
472 OpBranch %100
473 %100 = OpLabel
474 OpSelectionMerge %16 None
475 OpBranchConditional %14 %15 %18
476 %15 = OpLabel
477 OpStore %11 %17
478 OpBranch %16
479 %18 = OpLabel
480 OpStore %11 %19
481 OpBranch %16
482 %16 = OpLabel
483 OpReturn
484 OpFunctionEnd
485 )";
486 ASSERT_TRUE(IsEqual(env, after_split, context.get()));
487 }
488
TEST(TransformationSplitBlockTest,SplitBlockBeforeSwitchBranch)489 TEST(TransformationSplitBlockTest, SplitBlockBeforeSwitchBranch) {
490 // The SPIR-V in this test came from the following fragment shader:
491 //
492 // void main() {
493 // int x, y;
494 // switch (y) {
495 // case 1:
496 // x = 2;
497 // case 2:
498 // break;
499 // case 3:
500 // x = 4;
501 // default:
502 // x = 6;
503 // }
504 // }
505
506 std::string shader = R"(
507 OpCapability Shader
508 %1 = OpExtInstImport "GLSL.std.450"
509 OpMemoryModel Logical GLSL450
510 OpEntryPoint Fragment %4 "main"
511 OpExecutionMode %4 OriginUpperLeft
512 OpSource ESSL 310
513 OpName %4 "main"
514 OpName %8 "y"
515 OpName %15 "x"
516 OpDecorate %8 RelaxedPrecision
517 OpDecorate %9 RelaxedPrecision
518 OpDecorate %15 RelaxedPrecision
519 %2 = OpTypeVoid
520 %3 = OpTypeFunction %2
521 %6 = OpTypeInt 32 1
522 %7 = OpTypePointer Function %6
523 %16 = OpConstant %6 2
524 %18 = OpConstant %6 4
525 %19 = OpConstant %6 6
526 %4 = OpFunction %2 None %3
527 %5 = OpLabel
528 %8 = OpVariable %7 Function
529 %15 = OpVariable %7 Function
530 %9 = OpLoad %6 %8
531 OpSelectionMerge %14 None
532 OpSwitch %9 %13 1 %10 2 %11 3 %12
533 %13 = OpLabel
534 OpStore %15 %19
535 OpBranch %14
536 %10 = OpLabel
537 OpStore %15 %16
538 OpBranch %11
539 %11 = OpLabel
540 OpBranch %14
541 %12 = OpLabel
542 OpStore %15 %18
543 OpBranch %13
544 %14 = OpLabel
545 OpReturn
546 OpFunctionEnd
547 )";
548
549 const auto env = SPV_ENV_UNIVERSAL_1_3;
550 const auto consumer = nullptr;
551 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
552
553 spvtools::ValidatorOptions validator_options;
554 TransformationContext transformation_context(
555 MakeUnique<FactManager>(context.get()), validator_options);
556 // Illegal to split between the merge and the conditional branch.
557 ASSERT_FALSE(TransformationSplitBlock(
558 MakeInstructionDescriptor(9, SpvOpSwitch, 0), 100)
559 .IsApplicable(context.get(), transformation_context));
560 ASSERT_FALSE(TransformationSplitBlock(
561 MakeInstructionDescriptor(15, SpvOpSwitch, 0), 100)
562 .IsApplicable(context.get(), transformation_context));
563
564 auto split = TransformationSplitBlock(
565 MakeInstructionDescriptor(9, SpvOpSelectionMerge, 0), 100);
566 ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
567 ApplyAndCheckFreshIds(split, context.get(), &transformation_context);
568 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
569 kConsoleMessageConsumer));
570
571 std::string after_split = R"(
572 OpCapability Shader
573 %1 = OpExtInstImport "GLSL.std.450"
574 OpMemoryModel Logical GLSL450
575 OpEntryPoint Fragment %4 "main"
576 OpExecutionMode %4 OriginUpperLeft
577 OpSource ESSL 310
578 OpName %4 "main"
579 OpName %8 "y"
580 OpName %15 "x"
581 OpDecorate %8 RelaxedPrecision
582 OpDecorate %9 RelaxedPrecision
583 OpDecorate %15 RelaxedPrecision
584 %2 = OpTypeVoid
585 %3 = OpTypeFunction %2
586 %6 = OpTypeInt 32 1
587 %7 = OpTypePointer Function %6
588 %16 = OpConstant %6 2
589 %18 = OpConstant %6 4
590 %19 = OpConstant %6 6
591 %4 = OpFunction %2 None %3
592 %5 = OpLabel
593 %8 = OpVariable %7 Function
594 %15 = OpVariable %7 Function
595 %9 = OpLoad %6 %8
596 OpBranch %100
597 %100 = OpLabel
598 OpSelectionMerge %14 None
599 OpSwitch %9 %13 1 %10 2 %11 3 %12
600 %13 = OpLabel
601 OpStore %15 %19
602 OpBranch %14
603 %10 = OpLabel
604 OpStore %15 %16
605 OpBranch %11
606 %11 = OpLabel
607 OpBranch %14
608 %12 = OpLabel
609 OpStore %15 %18
610 OpBranch %13
611 %14 = OpLabel
612 OpReturn
613 OpFunctionEnd
614 )";
615 ASSERT_TRUE(IsEqual(env, after_split, context.get()));
616 }
617
TEST(TransformationSplitBlockTest,NoSplitDuringOpPhis)618 TEST(TransformationSplitBlockTest, NoSplitDuringOpPhis) {
619 // The SPIR-V in this test came from the following fragment shader, with
620 // local store elimination applied to get some OpPhi instructions.
621 //
622 // void main() {
623 // int x;
624 // int i;
625 // for (i = 0; i < 100; i++) {
626 // x += i;
627 // }
628 // }
629
630 std::string shader = R"(
631 OpCapability Shader
632 %1 = OpExtInstImport "GLSL.std.450"
633 OpMemoryModel Logical GLSL450
634 OpEntryPoint Fragment %4 "main"
635 OpExecutionMode %4 OriginUpperLeft
636 OpSource ESSL 310
637 OpName %4 "main"
638 OpName %8 "i"
639 OpName %19 "x"
640 OpDecorate %8 RelaxedPrecision
641 OpDecorate %19 RelaxedPrecision
642 OpDecorate %22 RelaxedPrecision
643 OpDecorate %25 RelaxedPrecision
644 OpDecorate %26 RelaxedPrecision
645 OpDecorate %27 RelaxedPrecision
646 %2 = OpTypeVoid
647 %3 = OpTypeFunction %2
648 %6 = OpTypeInt 32 1
649 %7 = OpTypePointer Function %6
650 %9 = OpConstant %6 0
651 %16 = OpConstant %6 100
652 %17 = OpTypeBool
653 %24 = OpConstant %6 1
654 %28 = OpUndef %6
655 %4 = OpFunction %2 None %3
656 %5 = OpLabel
657 %8 = OpVariable %7 Function
658 %19 = OpVariable %7 Function
659 OpStore %8 %9
660 OpBranch %10
661 %10 = OpLabel
662 %27 = OpPhi %6 %28 %5 %22 %13
663 %26 = OpPhi %6 %9 %5 %25 %13
664 OpBranch %50
665 %50 = OpLabel
666 OpLoopMerge %12 %13 None
667 OpBranch %14
668 %14 = OpLabel
669 %18 = OpSLessThan %17 %26 %16
670 OpBranchConditional %18 %11 %12
671 %11 = OpLabel
672 %22 = OpIAdd %6 %27 %26
673 OpStore %19 %22
674 OpBranch %13
675 %13 = OpLabel
676 %25 = OpIAdd %6 %26 %24
677 OpStore %8 %25
678 OpBranch %50
679 %12 = OpLabel
680 OpReturn
681 OpFunctionEnd
682 )";
683
684 const auto env = SPV_ENV_UNIVERSAL_1_3;
685 const auto consumer = nullptr;
686 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
687
688 spvtools::ValidatorOptions validator_options;
689 TransformationContext transformation_context(
690 MakeUnique<FactManager>(context.get()), validator_options);
691 // We cannot split before OpPhi instructions, since the number of incoming
692 // blocks may not appropriately match after splitting.
693 ASSERT_FALSE(
694 TransformationSplitBlock(MakeInstructionDescriptor(26, SpvOpPhi, 0), 100)
695 .IsApplicable(context.get(), transformation_context));
696 ASSERT_FALSE(
697 TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100)
698 .IsApplicable(context.get(), transformation_context));
699 ASSERT_FALSE(
700 TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100)
701 .IsApplicable(context.get(), transformation_context));
702 }
703
TEST(TransformationSplitBlockTest,SplitOpPhiWithSinglePredecessor)704 TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) {
705 std::string shader = R"(
706 OpCapability Shader
707 %1 = OpExtInstImport "GLSL.std.450"
708 OpMemoryModel Logical GLSL450
709 OpEntryPoint Fragment %4 "main"
710 OpExecutionMode %4 OriginUpperLeft
711 OpSource ESSL 310
712 OpName %4 "main"
713 OpName %8 "x"
714 OpName %10 "y"
715 OpDecorate %8 RelaxedPrecision
716 OpDecorate %10 RelaxedPrecision
717 OpDecorate %11 RelaxedPrecision
718 %2 = OpTypeVoid
719 %3 = OpTypeFunction %2
720 %6 = OpTypeInt 32 1
721 %7 = OpTypePointer Function %6
722 %9 = OpConstant %6 1
723 %4 = OpFunction %2 None %3
724 %5 = OpLabel
725 %8 = OpVariable %7 Function
726 %10 = OpVariable %7 Function
727 OpStore %8 %9
728 %11 = OpLoad %6 %8
729 OpBranch %20
730 %20 = OpLabel
731 %21 = OpPhi %6 %11 %5
732 OpStore %10 %21
733 OpReturn
734 OpFunctionEnd
735 )";
736
737 const auto env = SPV_ENV_UNIVERSAL_1_3;
738 const auto consumer = nullptr;
739 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
740
741 spvtools::ValidatorOptions validator_options;
742 TransformationContext transformation_context(
743 MakeUnique<FactManager>(context.get()), validator_options);
744 ASSERT_TRUE(
745 TransformationSplitBlock(MakeInstructionDescriptor(21, SpvOpPhi, 0), 100)
746 .IsApplicable(context.get(), transformation_context));
747 // An equivalent transformation to the above, just described with respect to a
748 // different base instruction.
749 auto split =
750 TransformationSplitBlock(MakeInstructionDescriptor(20, SpvOpPhi, 0), 100);
751 ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
752 ApplyAndCheckFreshIds(split, context.get(), &transformation_context);
753 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
754 kConsoleMessageConsumer));
755
756 std::string after_split = R"(
757 OpCapability Shader
758 %1 = OpExtInstImport "GLSL.std.450"
759 OpMemoryModel Logical GLSL450
760 OpEntryPoint Fragment %4 "main"
761 OpExecutionMode %4 OriginUpperLeft
762 OpSource ESSL 310
763 OpName %4 "main"
764 OpName %8 "x"
765 OpName %10 "y"
766 OpDecorate %8 RelaxedPrecision
767 OpDecorate %10 RelaxedPrecision
768 OpDecorate %11 RelaxedPrecision
769 %2 = OpTypeVoid
770 %3 = OpTypeFunction %2
771 %6 = OpTypeInt 32 1
772 %7 = OpTypePointer Function %6
773 %9 = OpConstant %6 1
774 %4 = OpFunction %2 None %3
775 %5 = OpLabel
776 %8 = OpVariable %7 Function
777 %10 = OpVariable %7 Function
778 OpStore %8 %9
779 %11 = OpLoad %6 %8
780 OpBranch %20
781 %20 = OpLabel
782 OpBranch %100
783 %100 = OpLabel
784 %21 = OpPhi %6 %11 %20
785 OpStore %10 %21
786 OpReturn
787 OpFunctionEnd
788 )";
789 ASSERT_TRUE(IsEqual(env, after_split, context.get()));
790 }
791
TEST(TransformationSplitBlockTest,DeadBlockShouldSplitToTwoDeadBlocks)792 TEST(TransformationSplitBlockTest, DeadBlockShouldSplitToTwoDeadBlocks) {
793 // This checks that if a block B is marked as dead, it should split into a
794 // pair of dead blocks.
795 std::string shader = R"(
796 OpCapability Shader
797 %1 = OpExtInstImport "GLSL.std.450"
798 OpMemoryModel Logical GLSL450
799 OpEntryPoint Fragment %4 "main"
800 OpExecutionMode %4 OriginUpperLeft
801 OpSource ESSL 310
802 OpName %4 "main"
803 %2 = OpTypeVoid
804 %3 = OpTypeFunction %2
805 %6 = OpTypeBool
806 %7 = OpConstantFalse %6
807 %4 = OpFunction %2 None %3
808 %5 = OpLabel
809 OpSelectionMerge %9 None
810 OpBranchConditional %7 %8 %9
811 %8 = OpLabel
812 OpBranch %9
813 %9 = OpLabel
814 OpReturn
815 OpFunctionEnd
816 )";
817
818 const auto env = SPV_ENV_UNIVERSAL_1_3;
819 const auto consumer = nullptr;
820 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
821
822 spvtools::ValidatorOptions validator_options;
823 TransformationContext transformation_context(
824 MakeUnique<FactManager>(context.get()), validator_options);
825 // Record the fact that block 8 is dead.
826 transformation_context.GetFactManager()->AddFactBlockIsDead(8);
827
828 auto split = TransformationSplitBlock(
829 MakeInstructionDescriptor(8, SpvOpBranch, 0), 100);
830 ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
831 ApplyAndCheckFreshIds(split, context.get(), &transformation_context);
832 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
833 kConsoleMessageConsumer));
834
835 ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(8));
836 ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(100));
837
838 std::string after_split = R"(
839 OpCapability Shader
840 %1 = OpExtInstImport "GLSL.std.450"
841 OpMemoryModel Logical GLSL450
842 OpEntryPoint Fragment %4 "main"
843 OpExecutionMode %4 OriginUpperLeft
844 OpSource ESSL 310
845 OpName %4 "main"
846 %2 = OpTypeVoid
847 %3 = OpTypeFunction %2
848 %6 = OpTypeBool
849 %7 = OpConstantFalse %6
850 %4 = OpFunction %2 None %3
851 %5 = OpLabel
852 OpSelectionMerge %9 None
853 OpBranchConditional %7 %8 %9
854 %8 = OpLabel
855 OpBranch %100
856 %100 = OpLabel
857 OpBranch %9
858 %9 = OpLabel
859 OpReturn
860 OpFunctionEnd
861 )";
862 ASSERT_TRUE(IsEqual(env, after_split, context.get()));
863 }
864
TEST(TransformationSplitBlockTest,DoNotSplitUseOfOpSampledImage)865 TEST(TransformationSplitBlockTest, DoNotSplitUseOfOpSampledImage) {
866 // This checks that we cannot split the definition of an OpSampledImage
867 // from its use.
868 std::string shader = R"(
869 OpCapability Shader
870 OpCapability SampledBuffer
871 OpCapability ImageBuffer
872 %1 = OpExtInstImport "GLSL.std.450"
873 OpMemoryModel Logical GLSL450
874 OpEntryPoint Fragment %2 "main" %40 %41
875 OpExecutionMode %2 OriginUpperLeft
876 OpSource GLSL 450
877 OpDecorate %40 DescriptorSet 0
878 OpDecorate %40 Binding 69
879 OpDecorate %41 DescriptorSet 0
880 OpDecorate %41 Binding 1
881 %54 = OpTypeFloat 32
882 %76 = OpTypeVector %54 4
883 %55 = OpConstant %54 0
884 %56 = OpTypeVector %54 3
885 %94 = OpTypeVector %54 2
886 %112 = OpConstantComposite %94 %55 %55
887 %57 = OpConstantComposite %56 %55 %55 %55
888 %15 = OpTypeImage %54 2D 2 0 0 1 Unknown
889 %114 = OpTypePointer UniformConstant %15
890 %38 = OpTypeSampler
891 %125 = OpTypePointer UniformConstant %38
892 %132 = OpTypeVoid
893 %133 = OpTypeFunction %132
894 %45 = OpTypeSampledImage %15
895 %40 = OpVariable %114 UniformConstant
896 %41 = OpVariable %125 UniformConstant
897 %2 = OpFunction %132 None %133
898 %164 = OpLabel
899 %184 = OpLoad %15 %40
900 %213 = OpLoad %38 %41
901 %216 = OpSampledImage %45 %184 %213
902 %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55
903 OpReturn
904 OpFunctionEnd
905 )";
906
907 const auto env = SPV_ENV_UNIVERSAL_1_3;
908 const auto consumer = nullptr;
909 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
910
911 spvtools::ValidatorOptions validator_options;
912 TransformationContext transformation_context(
913 MakeUnique<FactManager>(context.get()), validator_options);
914 auto split = TransformationSplitBlock(
915 MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0), 500);
916 ASSERT_FALSE(split.IsApplicable(context.get(), transformation_context));
917 }
918
919 } // namespace
920 } // namespace fuzz
921 } // namespace spvtools
922