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