• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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