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