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 #include "source/fuzz/instruction_descriptor.h"
17 #include "test/fuzz/fuzz_test_util.h"
18
19 namespace spvtools {
20 namespace fuzz {
21 namespace {
22
TEST(TransformationSplitBlockTest,NotApplicable)23 TEST(TransformationSplitBlockTest, NotApplicable) {
24 // The SPIR-V in this test came from the following fragment shader, with
25 // local store elimination applied to get some OpPhi instructions.
26 //
27 // void main() {
28 // int x;
29 // int i;
30 // for (i = 0; i < 100; i++) {
31 // x += i;
32 // }
33 // }
34
35 std::string shader = R"(
36 OpCapability Shader
37 %1 = OpExtInstImport "GLSL.std.450"
38 OpMemoryModel Logical GLSL450
39 OpEntryPoint Fragment %4 "main"
40 OpExecutionMode %4 OriginUpperLeft
41 OpSource ESSL 310
42 OpName %4 "main"
43 OpName %8 "i"
44 OpName %19 "x"
45 OpDecorate %8 RelaxedPrecision
46 OpDecorate %19 RelaxedPrecision
47 OpDecorate %22 RelaxedPrecision
48 OpDecorate %25 RelaxedPrecision
49 OpDecorate %26 RelaxedPrecision
50 OpDecorate %27 RelaxedPrecision
51 %2 = OpTypeVoid
52 %3 = OpTypeFunction %2
53 %6 = OpTypeInt 32 1
54 %7 = OpTypePointer Function %6
55 %9 = OpConstant %6 0
56 %16 = OpConstant %6 100
57 %17 = OpTypeBool
58 %24 = OpConstant %6 1
59 %28 = OpUndef %6
60 %4 = OpFunction %2 None %3
61 %5 = OpLabel
62 %8 = OpVariable %7 Function
63 %19 = OpVariable %7 Function
64 OpStore %8 %9
65 OpBranch %10
66 %10 = OpLabel
67 %27 = OpPhi %6 %28 %5 %22 %13
68 %26 = OpPhi %6 %9 %5 %25 %13
69 OpLoopMerge %12 %13 None
70 OpBranch %14
71 %14 = OpLabel
72 %18 = OpSLessThan %17 %26 %16
73 OpBranchConditional %18 %11 %12
74 %11 = OpLabel
75 %22 = OpIAdd %6 %27 %26
76 OpStore %19 %22
77 OpBranch %13
78 %13 = OpLabel
79 %25 = OpIAdd %6 %26 %24
80 OpStore %8 %25
81 OpBranch %10
82 %12 = OpLabel
83 OpReturn
84 OpFunctionEnd
85 )";
86
87 const auto env = SPV_ENV_UNIVERSAL_1_3;
88 const auto consumer = nullptr;
89 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
90
91 FactManager fact_manager;
92 spvtools::ValidatorOptions validator_options;
93 TransformationContext transformation_context(&fact_manager,
94 validator_options);
95
96 // No split before OpVariable
97 ASSERT_FALSE(TransformationSplitBlock(
98 MakeInstructionDescriptor(8, SpvOpVariable, 0), 100)
99 .IsApplicable(context.get(), transformation_context));
100 ASSERT_FALSE(TransformationSplitBlock(
101 MakeInstructionDescriptor(8, SpvOpVariable, 1), 100)
102 .IsApplicable(context.get(), transformation_context));
103
104 // No split before OpLabel
105 ASSERT_FALSE(TransformationSplitBlock(
106 MakeInstructionDescriptor(14, SpvOpLabel, 0), 100)
107 .IsApplicable(context.get(), transformation_context));
108
109 // No split if base instruction is outside a function
110 ASSERT_FALSE(
111 TransformationSplitBlock(MakeInstructionDescriptor(1, SpvOpLabel, 0), 100)
112 .IsApplicable(context.get(), transformation_context));
113 ASSERT_FALSE(TransformationSplitBlock(
114 MakeInstructionDescriptor(1, SpvOpExecutionMode, 0), 100)
115 .IsApplicable(context.get(), transformation_context));
116
117 // No split if block is loop header
118 ASSERT_FALSE(
119 TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100)
120 .IsApplicable(context.get(), transformation_context));
121 ASSERT_FALSE(
122 TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100)
123 .IsApplicable(context.get(), transformation_context));
124
125 // No split if base instruction does not exist
126 ASSERT_FALSE(
127 TransformationSplitBlock(MakeInstructionDescriptor(88, SpvOpIAdd, 0), 100)
128 .IsApplicable(context.get(), transformation_context));
129 ASSERT_FALSE(TransformationSplitBlock(
130 MakeInstructionDescriptor(88, SpvOpIMul, 22), 100)
131 .IsApplicable(context.get(), transformation_context));
132
133 // No split if too many instructions with the desired opcode are skipped
134 ASSERT_FALSE(
135 TransformationSplitBlock(
136 MakeInstructionDescriptor(18, SpvOpBranchConditional, 1), 100)
137 .IsApplicable(context.get(), transformation_context));
138
139 // No split if id in use
140 ASSERT_FALSE(TransformationSplitBlock(
141 MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 27)
142 .IsApplicable(context.get(), transformation_context));
143 ASSERT_FALSE(TransformationSplitBlock(
144 MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 14)
145 .IsApplicable(context.get(), transformation_context));
146 }
147
TEST(TransformationSplitBlockTest,SplitBlockSeveralTimes)148 TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) {
149 // The SPIR-V in this test came from the following fragment shader:
150 //
151 // void main() {
152 // int a;
153 // int b;
154 // a = 1;
155 // b = a;
156 // a = b;
157 // b = 2;
158 // b++;
159 // }
160
161 std::string shader = R"(
162 OpCapability Shader
163 %1 = OpExtInstImport "GLSL.std.450"
164 OpMemoryModel Logical GLSL450
165 OpEntryPoint Fragment %4 "main"
166 OpExecutionMode %4 OriginUpperLeft
167 OpSource ESSL 310
168 OpName %4 "main"
169 OpName %8 "a"
170 OpName %10 "b"
171 OpDecorate %8 RelaxedPrecision
172 OpDecorate %10 RelaxedPrecision
173 OpDecorate %11 RelaxedPrecision
174 OpDecorate %12 RelaxedPrecision
175 OpDecorate %14 RelaxedPrecision
176 OpDecorate %15 RelaxedPrecision
177 %2 = OpTypeVoid
178 %3 = OpTypeFunction %2
179 %6 = OpTypeInt 32 1
180 %7 = OpTypePointer Function %6
181 %9 = OpConstant %6 1
182 %13 = OpConstant %6 2
183 %4 = OpFunction %2 None %3
184 %5 = OpLabel
185 %8 = OpVariable %7 Function
186 %10 = OpVariable %7 Function
187 OpStore %8 %9
188 %11 = OpLoad %6 %8
189 OpStore %10 %11
190 %12 = OpLoad %6 %10
191 OpStore %8 %12
192 OpStore %10 %13
193 %14 = OpLoad %6 %10
194 %15 = OpIAdd %6 %14 %9
195 OpStore %10 %15
196 OpReturn
197 OpFunctionEnd
198 )";
199
200 const auto env = SPV_ENV_UNIVERSAL_1_3;
201 const auto consumer = nullptr;
202 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
203
204 FactManager fact_manager;
205 spvtools::ValidatorOptions validator_options;
206 TransformationContext transformation_context(&fact_manager,
207 validator_options);
208
209 auto split_1 = TransformationSplitBlock(
210 MakeInstructionDescriptor(5, SpvOpStore, 0), 100);
211 ASSERT_TRUE(split_1.IsApplicable(context.get(), transformation_context));
212 split_1.Apply(context.get(), &transformation_context);
213 ASSERT_TRUE(IsValid(env, context.get()));
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 split_2.Apply(context.get(), &transformation_context);
261 ASSERT_TRUE(IsValid(env, context.get()));
262
263 std::string after_split_2 = R"(
264 OpCapability Shader
265 %1 = OpExtInstImport "GLSL.std.450"
266 OpMemoryModel Logical GLSL450
267 OpEntryPoint Fragment %4 "main"
268 OpExecutionMode %4 OriginUpperLeft
269 OpSource ESSL 310
270 OpName %4 "main"
271 OpName %8 "a"
272 OpName %10 "b"
273 OpDecorate %8 RelaxedPrecision
274 OpDecorate %10 RelaxedPrecision
275 OpDecorate %11 RelaxedPrecision
276 OpDecorate %12 RelaxedPrecision
277 OpDecorate %14 RelaxedPrecision
278 OpDecorate %15 RelaxedPrecision
279 %2 = OpTypeVoid
280 %3 = OpTypeFunction %2
281 %6 = OpTypeInt 32 1
282 %7 = OpTypePointer Function %6
283 %9 = OpConstant %6 1
284 %13 = OpConstant %6 2
285 %4 = OpFunction %2 None %3
286 %5 = OpLabel
287 %8 = OpVariable %7 Function
288 %10 = OpVariable %7 Function
289 OpBranch %100
290 %100 = OpLabel
291 OpStore %8 %9
292 %11 = OpLoad %6 %8
293 OpBranch %101
294 %101 = OpLabel
295 OpStore %10 %11
296 %12 = OpLoad %6 %10
297 OpStore %8 %12
298 OpStore %10 %13
299 %14 = OpLoad %6 %10
300 %15 = OpIAdd %6 %14 %9
301 OpStore %10 %15
302 OpReturn
303 OpFunctionEnd
304 )";
305 ASSERT_TRUE(IsEqual(env, after_split_2, context.get()));
306
307 auto split_3 = TransformationSplitBlock(
308 MakeInstructionDescriptor(14, SpvOpLoad, 0), 102);
309 ASSERT_TRUE(split_3.IsApplicable(context.get(), transformation_context));
310 split_3.Apply(context.get(), &transformation_context);
311 ASSERT_TRUE(IsValid(env, context.get()));
312
313 std::string after_split_3 = R"(
314 OpCapability Shader
315 %1 = OpExtInstImport "GLSL.std.450"
316 OpMemoryModel Logical GLSL450
317 OpEntryPoint Fragment %4 "main"
318 OpExecutionMode %4 OriginUpperLeft
319 OpSource ESSL 310
320 OpName %4 "main"
321 OpName %8 "a"
322 OpName %10 "b"
323 OpDecorate %8 RelaxedPrecision
324 OpDecorate %10 RelaxedPrecision
325 OpDecorate %11 RelaxedPrecision
326 OpDecorate %12 RelaxedPrecision
327 OpDecorate %14 RelaxedPrecision
328 OpDecorate %15 RelaxedPrecision
329 %2 = OpTypeVoid
330 %3 = OpTypeFunction %2
331 %6 = OpTypeInt 32 1
332 %7 = OpTypePointer Function %6
333 %9 = OpConstant %6 1
334 %13 = OpConstant %6 2
335 %4 = OpFunction %2 None %3
336 %5 = OpLabel
337 %8 = OpVariable %7 Function
338 %10 = OpVariable %7 Function
339 OpBranch %100
340 %100 = OpLabel
341 OpStore %8 %9
342 %11 = OpLoad %6 %8
343 OpBranch %101
344 %101 = OpLabel
345 OpStore %10 %11
346 %12 = OpLoad %6 %10
347 OpStore %8 %12
348 OpStore %10 %13
349 OpBranch %102
350 %102 = OpLabel
351 %14 = OpLoad %6 %10
352 %15 = OpIAdd %6 %14 %9
353 OpStore %10 %15
354 OpReturn
355 OpFunctionEnd
356 )";
357 ASSERT_TRUE(IsEqual(env, after_split_3, context.get()));
358 }
359
TEST(TransformationSplitBlockTest,SplitBlockBeforeSelectBranch)360 TEST(TransformationSplitBlockTest, SplitBlockBeforeSelectBranch) {
361 // The SPIR-V in this test came from the following fragment shader:
362 //
363 // void main() {
364 // int x, y;
365 // x = 2;
366 // if (x < y) {
367 // y = 3;
368 // } else {
369 // y = 4;
370 // }
371 // }
372
373 std::string shader = R"(
374 OpCapability Shader
375 %1 = OpExtInstImport "GLSL.std.450"
376 OpMemoryModel Logical GLSL450
377 OpEntryPoint Fragment %4 "main"
378 OpExecutionMode %4 OriginUpperLeft
379 OpSource ESSL 310
380 OpName %4 "main"
381 OpName %8 "x"
382 OpName %11 "y"
383 OpDecorate %8 RelaxedPrecision
384 OpDecorate %10 RelaxedPrecision
385 OpDecorate %11 RelaxedPrecision
386 OpDecorate %12 RelaxedPrecision
387 %2 = OpTypeVoid
388 %3 = OpTypeFunction %2
389 %6 = OpTypeInt 32 1
390 %7 = OpTypePointer Function %6
391 %9 = OpConstant %6 2
392 %13 = OpTypeBool
393 %17 = OpConstant %6 3
394 %19 = OpConstant %6 4
395 %4 = OpFunction %2 None %3
396 %5 = OpLabel
397 %8 = OpVariable %7 Function
398 %11 = OpVariable %7 Function
399 OpStore %8 %9
400 %10 = OpLoad %6 %8
401 %12 = OpLoad %6 %11
402 %14 = OpSLessThan %13 %10 %12
403 OpSelectionMerge %16 None
404 OpBranchConditional %14 %15 %18
405 %15 = OpLabel
406 OpStore %11 %17
407 OpBranch %16
408 %18 = OpLabel
409 OpStore %11 %19
410 OpBranch %16
411 %16 = OpLabel
412 OpReturn
413 OpFunctionEnd
414 )";
415
416 const auto env = SPV_ENV_UNIVERSAL_1_3;
417 const auto consumer = nullptr;
418 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
419
420 FactManager fact_manager;
421 spvtools::ValidatorOptions validator_options;
422 TransformationContext transformation_context(&fact_manager,
423 validator_options);
424
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 split.Apply(context.get(), &transformation_context);
439 ASSERT_TRUE(IsValid(env, context.get()));
440
441 std::string after_split = R"(
442 OpCapability Shader
443 %1 = OpExtInstImport "GLSL.std.450"
444 OpMemoryModel Logical GLSL450
445 OpEntryPoint Fragment %4 "main"
446 OpExecutionMode %4 OriginUpperLeft
447 OpSource ESSL 310
448 OpName %4 "main"
449 OpName %8 "x"
450 OpName %11 "y"
451 OpDecorate %8 RelaxedPrecision
452 OpDecorate %10 RelaxedPrecision
453 OpDecorate %11 RelaxedPrecision
454 OpDecorate %12 RelaxedPrecision
455 %2 = OpTypeVoid
456 %3 = OpTypeFunction %2
457 %6 = OpTypeInt 32 1
458 %7 = OpTypePointer Function %6
459 %9 = OpConstant %6 2
460 %13 = OpTypeBool
461 %17 = OpConstant %6 3
462 %19 = OpConstant %6 4
463 %4 = OpFunction %2 None %3
464 %5 = OpLabel
465 %8 = OpVariable %7 Function
466 %11 = OpVariable %7 Function
467 OpStore %8 %9
468 %10 = OpLoad %6 %8
469 %12 = OpLoad %6 %11
470 %14 = OpSLessThan %13 %10 %12
471 OpBranch %100
472 %100 = OpLabel
473 OpSelectionMerge %16 None
474 OpBranchConditional %14 %15 %18
475 %15 = OpLabel
476 OpStore %11 %17
477 OpBranch %16
478 %18 = OpLabel
479 OpStore %11 %19
480 OpBranch %16
481 %16 = OpLabel
482 OpReturn
483 OpFunctionEnd
484 )";
485 ASSERT_TRUE(IsEqual(env, after_split, context.get()));
486 }
487
TEST(TransformationSplitBlockTest,SplitBlockBeforeSwitchBranch)488 TEST(TransformationSplitBlockTest, SplitBlockBeforeSwitchBranch) {
489 // The SPIR-V in this test came from the following fragment shader:
490 //
491 // void main() {
492 // int x, y;
493 // switch (y) {
494 // case 1:
495 // x = 2;
496 // case 2:
497 // break;
498 // case 3:
499 // x = 4;
500 // default:
501 // x = 6;
502 // }
503 // }
504
505 std::string shader = R"(
506 OpCapability Shader
507 %1 = OpExtInstImport "GLSL.std.450"
508 OpMemoryModel Logical GLSL450
509 OpEntryPoint Fragment %4 "main"
510 OpExecutionMode %4 OriginUpperLeft
511 OpSource ESSL 310
512 OpName %4 "main"
513 OpName %8 "y"
514 OpName %15 "x"
515 OpDecorate %8 RelaxedPrecision
516 OpDecorate %9 RelaxedPrecision
517 OpDecorate %15 RelaxedPrecision
518 %2 = OpTypeVoid
519 %3 = OpTypeFunction %2
520 %6 = OpTypeInt 32 1
521 %7 = OpTypePointer Function %6
522 %16 = OpConstant %6 2
523 %18 = OpConstant %6 4
524 %19 = OpConstant %6 6
525 %4 = OpFunction %2 None %3
526 %5 = OpLabel
527 %8 = OpVariable %7 Function
528 %15 = OpVariable %7 Function
529 %9 = OpLoad %6 %8
530 OpSelectionMerge %14 None
531 OpSwitch %9 %13 1 %10 2 %11 3 %12
532 %13 = OpLabel
533 OpStore %15 %19
534 OpBranch %14
535 %10 = OpLabel
536 OpStore %15 %16
537 OpBranch %11
538 %11 = OpLabel
539 OpBranch %14
540 %12 = OpLabel
541 OpStore %15 %18
542 OpBranch %13
543 %14 = OpLabel
544 OpReturn
545 OpFunctionEnd
546 )";
547
548 const auto env = SPV_ENV_UNIVERSAL_1_3;
549 const auto consumer = nullptr;
550 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
551
552 FactManager fact_manager;
553 spvtools::ValidatorOptions validator_options;
554 TransformationContext transformation_context(&fact_manager,
555 validator_options);
556
557 // Illegal to split between the merge and the conditional branch.
558 ASSERT_FALSE(TransformationSplitBlock(
559 MakeInstructionDescriptor(9, SpvOpSwitch, 0), 100)
560 .IsApplicable(context.get(), transformation_context));
561 ASSERT_FALSE(TransformationSplitBlock(
562 MakeInstructionDescriptor(15, SpvOpSwitch, 0), 100)
563 .IsApplicable(context.get(), transformation_context));
564
565 auto split = TransformationSplitBlock(
566 MakeInstructionDescriptor(9, SpvOpSelectionMerge, 0), 100);
567 ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
568 split.Apply(context.get(), &transformation_context);
569 ASSERT_TRUE(IsValid(env, context.get()));
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 FactManager fact_manager;
689 spvtools::ValidatorOptions validator_options;
690 TransformationContext transformation_context(&fact_manager,
691 validator_options);
692
693 // We cannot split before OpPhi instructions, since the number of incoming
694 // blocks may not appropriately match after splitting.
695 ASSERT_FALSE(
696 TransformationSplitBlock(MakeInstructionDescriptor(26, SpvOpPhi, 0), 100)
697 .IsApplicable(context.get(), transformation_context));
698 ASSERT_FALSE(
699 TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100)
700 .IsApplicable(context.get(), transformation_context));
701 ASSERT_FALSE(
702 TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100)
703 .IsApplicable(context.get(), transformation_context));
704 }
705
TEST(TransformationSplitBlockTest,SplitOpPhiWithSinglePredecessor)706 TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) {
707 std::string shader = R"(
708 OpCapability Shader
709 %1 = OpExtInstImport "GLSL.std.450"
710 OpMemoryModel Logical GLSL450
711 OpEntryPoint Fragment %4 "main"
712 OpExecutionMode %4 OriginUpperLeft
713 OpSource ESSL 310
714 OpName %4 "main"
715 OpName %8 "x"
716 OpName %10 "y"
717 OpDecorate %8 RelaxedPrecision
718 OpDecorate %10 RelaxedPrecision
719 OpDecorate %11 RelaxedPrecision
720 %2 = OpTypeVoid
721 %3 = OpTypeFunction %2
722 %6 = OpTypeInt 32 1
723 %7 = OpTypePointer Function %6
724 %9 = OpConstant %6 1
725 %4 = OpFunction %2 None %3
726 %5 = OpLabel
727 %8 = OpVariable %7 Function
728 %10 = OpVariable %7 Function
729 OpStore %8 %9
730 %11 = OpLoad %6 %8
731 OpBranch %20
732 %20 = OpLabel
733 %21 = OpPhi %6 %11 %5
734 OpStore %10 %21
735 OpReturn
736 OpFunctionEnd
737 )";
738
739 const auto env = SPV_ENV_UNIVERSAL_1_3;
740 const auto consumer = nullptr;
741 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
742
743 FactManager fact_manager;
744 spvtools::ValidatorOptions validator_options;
745 TransformationContext transformation_context(&fact_manager,
746 validator_options);
747
748 ASSERT_TRUE(
749 TransformationSplitBlock(MakeInstructionDescriptor(21, SpvOpPhi, 0), 100)
750 .IsApplicable(context.get(), transformation_context));
751 // An equivalent transformation to the above, just described with respect to a
752 // different base instruction.
753 auto split =
754 TransformationSplitBlock(MakeInstructionDescriptor(20, SpvOpPhi, 0), 100);
755 ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
756 split.Apply(context.get(), &transformation_context);
757 ASSERT_TRUE(IsValid(env, context.get()));
758
759 std::string after_split = R"(
760 OpCapability Shader
761 %1 = OpExtInstImport "GLSL.std.450"
762 OpMemoryModel Logical GLSL450
763 OpEntryPoint Fragment %4 "main"
764 OpExecutionMode %4 OriginUpperLeft
765 OpSource ESSL 310
766 OpName %4 "main"
767 OpName %8 "x"
768 OpName %10 "y"
769 OpDecorate %8 RelaxedPrecision
770 OpDecorate %10 RelaxedPrecision
771 OpDecorate %11 RelaxedPrecision
772 %2 = OpTypeVoid
773 %3 = OpTypeFunction %2
774 %6 = OpTypeInt 32 1
775 %7 = OpTypePointer Function %6
776 %9 = OpConstant %6 1
777 %4 = OpFunction %2 None %3
778 %5 = OpLabel
779 %8 = OpVariable %7 Function
780 %10 = OpVariable %7 Function
781 OpStore %8 %9
782 %11 = OpLoad %6 %8
783 OpBranch %20
784 %20 = OpLabel
785 OpBranch %100
786 %100 = OpLabel
787 %21 = OpPhi %6 %11 %20
788 OpStore %10 %21
789 OpReturn
790 OpFunctionEnd
791 )";
792 ASSERT_TRUE(IsEqual(env, after_split, context.get()));
793 }
794
TEST(TransformationSplitBlockTest,DeadBlockShouldSplitToTwoDeadBlocks)795 TEST(TransformationSplitBlockTest, DeadBlockShouldSplitToTwoDeadBlocks) {
796 // This checks that if a block B is marked as dead, it should split into a
797 // pair of dead blocks.
798 std::string shader = R"(
799 OpCapability Shader
800 %1 = OpExtInstImport "GLSL.std.450"
801 OpMemoryModel Logical GLSL450
802 OpEntryPoint Fragment %4 "main"
803 OpExecutionMode %4 OriginUpperLeft
804 OpSource ESSL 310
805 OpName %4 "main"
806 %2 = OpTypeVoid
807 %3 = OpTypeFunction %2
808 %6 = OpTypeBool
809 %7 = OpConstantFalse %6
810 %4 = OpFunction %2 None %3
811 %5 = OpLabel
812 OpSelectionMerge %9 None
813 OpBranchConditional %7 %8 %9
814 %8 = OpLabel
815 OpBranch %9
816 %9 = OpLabel
817 OpReturn
818 OpFunctionEnd
819 )";
820
821 const auto env = SPV_ENV_UNIVERSAL_1_3;
822 const auto consumer = nullptr;
823 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
824
825 FactManager fact_manager;
826 spvtools::ValidatorOptions validator_options;
827 TransformationContext transformation_context(&fact_manager,
828 validator_options);
829
830 // Record the fact that block 8 is dead.
831 transformation_context.GetFactManager()->AddFactBlockIsDead(8);
832
833 auto split = TransformationSplitBlock(
834 MakeInstructionDescriptor(8, SpvOpBranch, 0), 100);
835 ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
836 split.Apply(context.get(), &transformation_context);
837 ASSERT_TRUE(IsValid(env, context.get()));
838
839 ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(8));
840 ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(100));
841
842 std::string after_split = R"(
843 OpCapability Shader
844 %1 = OpExtInstImport "GLSL.std.450"
845 OpMemoryModel Logical GLSL450
846 OpEntryPoint Fragment %4 "main"
847 OpExecutionMode %4 OriginUpperLeft
848 OpSource ESSL 310
849 OpName %4 "main"
850 %2 = OpTypeVoid
851 %3 = OpTypeFunction %2
852 %6 = OpTypeBool
853 %7 = OpConstantFalse %6
854 %4 = OpFunction %2 None %3
855 %5 = OpLabel
856 OpSelectionMerge %9 None
857 OpBranchConditional %7 %8 %9
858 %8 = OpLabel
859 OpBranch %100
860 %100 = OpLabel
861 OpBranch %9
862 %9 = OpLabel
863 OpReturn
864 OpFunctionEnd
865 )";
866 ASSERT_TRUE(IsEqual(env, after_split, context.get()));
867 }
868
869 } // namespace
870 } // namespace fuzz
871 } // namespace spvtools
872