• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2020 Vasyl Teliman
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_propagate_instruction_down.h"
16 
17 #include "gtest/gtest.h"
18 #include "source/fuzz/counter_overflow_id_source.h"
19 #include "source/fuzz/fuzzer_util.h"
20 #include "test/fuzz/fuzz_test_util.h"
21 
22 namespace spvtools {
23 namespace fuzz {
24 namespace {
25 
TEST(TransformationPropagateInstructionDownTest,BasicTest)26 TEST(TransformationPropagateInstructionDownTest, BasicTest) {
27   std::string shader = R"(
28                OpCapability Shader
29           %1 = OpExtInstImport "GLSL.std.450"
30                OpMemoryModel Logical GLSL450
31                OpEntryPoint Fragment %4 "main"
32                OpExecutionMode %4 OriginUpperLeft
33                OpSource ESSL 310
34           %2 = OpTypeVoid
35           %3 = OpTypeFunction %2
36           %6 = OpTypeInt 32 1
37           %7 = OpConstant %6 1
38          %12 = OpTypeBool
39          %13 = OpConstantTrue %12
40           %9 = OpTypePointer Function %6
41           %4 = OpFunction %2 None %3
42 
43           ; Has no instruction to propagate
44           %5 = OpLabel
45          %10 = OpVariable %9 Function
46           %8 = OpCopyObject %6 %7
47                OpStore %10 %8
48                OpBranch %11
49 
50         ; Unreachable block
51         %100 = OpLabel
52         %101 = OpCopyObject %6 %7
53                OpBranch %11
54 
55          ; Selection header
56          ;
57          ; One of acceptable successors has an OpPhi that uses propagated
58          ; instruction's id
59          %11 = OpLabel
60          %19 = OpCopyObject %6 %7
61                OpSelectionMerge %18 None
62                OpBranchConditional %13 %14 %18
63 
64          ; %16 has no acceptable successors
65          %14 = OpLabel
66          %20 = OpPhi %6 %19 %11
67          %15 = OpCopyObject %6 %7 ; dependency
68                OpBranch %16
69          %16 = OpLabel
70          %17 = OpCopyObject %6 %15
71                OpBranch %18
72 
73          ; Can be applied
74          %18 = OpLabel
75          %21 = OpCopyObject %6 %7
76                OpSelectionMerge %24 None
77                OpBranchConditional %13 %22 %23
78          %22 = OpLabel
79          %29 = OpPhi %6 %7 %18
80                OpStore %10 %21
81                OpBranch %24
82          %23 = OpLabel
83                OpStore %10 %21
84                OpBranch %24
85          %24 = OpLabel
86                OpStore %10 %21
87                OpBranch %32
88 
89          ; Can't replace all uses of the propagated instruction: %30 is
90          ; propagated into %27.
91          %32 = OpLabel
92                OpLoopMerge %28 %27 None
93                OpBranchConditional %13 %26 %28
94          %26 = OpLabel
95          %25 = OpCopyObject %6 %7
96          %30 = OpCopyObject %6 %25
97                OpBranchConditional %13 %27 %28
98          %27 = OpLabel
99                OpBranch %32
100          %28 = OpLabel
101          %31 = OpPhi %6 %30 %26 %7 %32 ; Can't replace this use
102                OpReturn
103 
104                OpFunctionEnd
105   )";
106 
107   const auto env = SPV_ENV_UNIVERSAL_1_3;
108   const auto consumer = nullptr;
109   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
110   spvtools::ValidatorOptions validator_options;
111   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
112                                                kConsoleMessageConsumer));
113   TransformationContext transformation_context(
114       MakeUnique<FactManager>(context.get()), validator_options);
115 
116   // Invalid block id.
117   ASSERT_FALSE(TransformationPropagateInstructionDown(200, 200, {{}})
118                    .IsApplicable(context.get(), transformation_context));
119   ASSERT_FALSE(TransformationPropagateInstructionDown(101, 200, {{}})
120                    .IsApplicable(context.get(), transformation_context));
121 
122   // The block is unreachable.
123   ASSERT_FALSE(TransformationPropagateInstructionDown(100, 200, {{}})
124                    .IsApplicable(context.get(), transformation_context));
125 
126   // The block has no instruction to propagate.
127   ASSERT_FALSE(TransformationPropagateInstructionDown(5, 200, {{{11, 201}}})
128                    .IsApplicable(context.get(), transformation_context));
129 
130   // The block has no acceptable successors.
131   ASSERT_FALSE(TransformationPropagateInstructionDown(16, 200, {{{18, 201}}})
132                    .IsApplicable(context.get(), transformation_context));
133 
134   // One of acceptable successors has an OpPhi that uses propagated
135   // instruction's id.
136   ASSERT_FALSE(
137       TransformationPropagateInstructionDown(11, 200, {{{14, 201}, {18, 202}}})
138           .IsApplicable(context.get(), transformation_context));
139 
140 #ifndef NDEBUG
141   // Not all fresh ids are provided.
142   ASSERT_DEATH(
143       TransformationPropagateInstructionDown(18, 200, {{{22, 201}, {202, 203}}})
144           .IsApplicable(context.get(), transformation_context),
145       "Bad attempt to query whether overflow ids are available.");
146 #endif
147 
148   // Not all fresh ids are fresh.
149   ASSERT_FALSE(TransformationPropagateInstructionDown(
150                    18, 18, {{{22, 201}, {23, 202}, {202, 203}}})
151                    .IsApplicable(context.get(), transformation_context));
152   ASSERT_FALSE(TransformationPropagateInstructionDown(
153                    18, 200, {{{22, 22}, {23, 202}, {202, 203}}})
154                    .IsApplicable(context.get(), transformation_context));
155   ASSERT_FALSE(TransformationPropagateInstructionDown(
156                    18, 18, {{{22, 22}, {23, 202}, {202, 203}}})
157                    .IsApplicable(context.get(), transformation_context));
158 
159   // Not all fresh ids are unique.
160   ASSERT_FALSE(TransformationPropagateInstructionDown(
161                    18, 200, {{{22, 200}, {23, 202}, {202, 200}}})
162                    .IsApplicable(context.get(), transformation_context));
163   ASSERT_FALSE(TransformationPropagateInstructionDown(
164                    18, 200, {{{22, 201}, {23, 202}, {202, 200}}})
165                    .IsApplicable(context.get(), transformation_context));
166   ASSERT_FALSE(TransformationPropagateInstructionDown(
167                    18, 200, {{{22, 201}, {23, 201}, {202, 203}}})
168                    .IsApplicable(context.get(), transformation_context));
169 
170   // Can't replace all uses of the propagated instruction: %30 is propagated
171   // into %27.
172   ASSERT_FALSE(TransformationPropagateInstructionDown(26, 200, {{{27, 201}}})
173                    .IsApplicable(context.get(), transformation_context));
174 
175   {
176     TransformationPropagateInstructionDown transformation(
177         18, 200, {{{22, 201}, {23, 202}, {202, 203}}});
178     ASSERT_TRUE(
179         transformation.IsApplicable(context.get(), transformation_context));
180     ApplyAndCheckFreshIds(transformation, context.get(),
181                           &transformation_context);
182     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
183         context.get(), validator_options, kConsoleMessageConsumer));
184 
185     ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
186         MakeDataDescriptor(201, {}), MakeDataDescriptor(202, {})));
187     ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
188         MakeDataDescriptor(201, {}), MakeDataDescriptor(200, {})));
189   }
190 
191   std::string after_transformation = R"(
192                OpCapability Shader
193           %1 = OpExtInstImport "GLSL.std.450"
194                OpMemoryModel Logical GLSL450
195                OpEntryPoint Fragment %4 "main"
196                OpExecutionMode %4 OriginUpperLeft
197                OpSource ESSL 310
198           %2 = OpTypeVoid
199           %3 = OpTypeFunction %2
200           %6 = OpTypeInt 32 1
201           %7 = OpConstant %6 1
202          %12 = OpTypeBool
203          %13 = OpConstantTrue %12
204           %9 = OpTypePointer Function %6
205           %4 = OpFunction %2 None %3
206 
207           ; Has no instruction to propagate
208           %5 = OpLabel
209          %10 = OpVariable %9 Function
210           %8 = OpCopyObject %6 %7
211                OpStore %10 %8
212                OpBranch %11
213 
214         ; Unreachable block
215         %100 = OpLabel
216         %101 = OpCopyObject %6 %7
217                OpBranch %11
218 
219          ; Selection header
220          ;
221          ; One of acceptable successors has an OpPhi that uses propagated
222          ; instruction's id
223          %11 = OpLabel
224          %19 = OpCopyObject %6 %7
225                OpSelectionMerge %18 None
226                OpBranchConditional %13 %14 %18
227 
228          ; %16 has no acceptable successors
229          %14 = OpLabel
230          %20 = OpPhi %6 %19 %11
231          %15 = OpCopyObject %6 %7 ; dependency
232                OpBranch %16
233          %16 = OpLabel
234          %17 = OpCopyObject %6 %15
235                OpBranch %18
236 
237          ; Can be applied
238          %18 = OpLabel
239                OpSelectionMerge %24 None
240                OpBranchConditional %13 %22 %23
241          %22 = OpLabel
242          %29 = OpPhi %6 %7 %18
243         %201 = OpCopyObject %6 %7
244                OpStore %10 %201
245                OpBranch %24
246          %23 = OpLabel
247         %202 = OpCopyObject %6 %7
248                OpStore %10 %202
249                OpBranch %24
250          %24 = OpLabel
251         %200 = OpPhi %6 %201 %22 %202 %23
252                OpStore %10 %200
253                OpBranch %32
254 
255          ; Can't replace all uses of the propagated instruction: %30 is
256          ; propagated into %27.
257          %32 = OpLabel
258                OpLoopMerge %28 %27 None
259                OpBranchConditional %13 %26 %28
260          %26 = OpLabel
261          %25 = OpCopyObject %6 %7
262          %30 = OpCopyObject %6 %25
263                OpBranchConditional %13 %27 %28
264          %27 = OpLabel
265                OpBranch %32
266          %28 = OpLabel
267          %31 = OpPhi %6 %30 %26 %7 %32 ; Can't replace this use
268                OpReturn
269 
270                OpFunctionEnd
271   )";
272 
273   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
274 }
275 
TEST(TransformationPropagateInstructionDownTest,CantCreateOpPhiTest)276 TEST(TransformationPropagateInstructionDownTest, CantCreateOpPhiTest) {
277   std::string shader = R"(
278                OpCapability Shader
279           %1 = OpExtInstImport "GLSL.std.450"
280                OpMemoryModel Logical GLSL450
281                OpEntryPoint Fragment %4 "main"
282                OpExecutionMode %4 OriginUpperLeft
283                OpSource ESSL 310
284           %2 = OpTypeVoid
285           %3 = OpTypeFunction %2
286           %6 = OpTypeInt 32 1
287           %7 = OpConstant %6 1
288          %12 = OpTypeBool
289          %13 = OpConstantTrue %12
290           %4 = OpFunction %2 None %3
291 
292           ; %5 doesn't belong to any construct
293           %5 = OpLabel
294          %15 = OpCopyObject %6 %7
295                OpBranch %16
296 
297          ; The merge block (%19) is unreachable
298          %16 = OpLabel
299          %17 = OpCopyObject %6 %7
300                OpSelectionMerge %19 None
301                OpBranchConditional %13 %18 %18
302 
303          ; %21 doesn't dominate the merge block - %20
304          %18 = OpLabel
305                OpSelectionMerge %20 None
306                OpBranchConditional %13 %20 %21
307          %21 = OpLabel
308          %22 = OpCopyObject %6 %7
309                OpBranch %20
310 
311          ; The merge block (%24) is an acceptable successor of the propagated
312          ; instruction's block
313          %20 = OpLabel
314          %23 = OpCopyObject %6 %7
315                OpSelectionMerge %24 None
316                OpBranchConditional %13 %24 %30
317          %30 = OpLabel
318                OpBranch %24
319 
320          ; One of the predecessors of the merge block is not dominated by any
321          ; successor of the propagated instruction's block
322          %24 = OpLabel
323          %26 = OpCopyObject %6 %7
324                OpLoopMerge %29 %25 None
325                OpBranch %25
326          %25 = OpLabel
327                OpBranchConditional %13 %24 %29
328          %28 = OpLabel ; unreachable predecessor of %29
329                OpBranch %29
330          %29 = OpLabel
331                OpReturn
332 
333          %19 = OpLabel
334                OpReturn
335                OpFunctionEnd
336   )";
337 
338   const auto env = SPV_ENV_UNIVERSAL_1_3;
339   const auto consumer = nullptr;
340   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
341   spvtools::ValidatorOptions validator_options;
342   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
343                                                kConsoleMessageConsumer));
344   TransformationContext transformation_context(
345       MakeUnique<FactManager>(context.get()), validator_options);
346 
347   TransformationPropagateInstructionDown transformations[] = {
348       // %5 doesn't belong to any construct.
349       {5, 200, {{{16, 201}}}},
350 
351       // The merge block (%19) is unreachable.
352       {16, 200, {{{18, 202}}}},
353 
354       // %21 doesn't dominate the merge block - %20.
355       {21, 200, {{{20, 203}}}},
356 
357       // The merge block (%24) is an acceptable successor of the propagated
358       // instruction's block.
359       {20, 200, {{{24, 204}, {30, 205}}}},
360 
361       // One of the predecessors of the merge block is not dominated by any
362       // successor of the propagated instruction's block.
363       {24, 200, {{{25, 206}}}},
364   };
365 
366   for (const auto& transformation : transformations) {
367     ASSERT_TRUE(
368         transformation.IsApplicable(context.get(), transformation_context));
369     ApplyAndCheckFreshIds(transformation, context.get(),
370                           &transformation_context);
371     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
372         context.get(), validator_options, kConsoleMessageConsumer));
373   }
374 
375   // No transformation has introduced an OpPhi instruction.
376   ASSERT_FALSE(context->get_def_use_mgr()->GetDef(200));
377 
378   std::string after_transformation = R"(
379                OpCapability Shader
380           %1 = OpExtInstImport "GLSL.std.450"
381                OpMemoryModel Logical GLSL450
382                OpEntryPoint Fragment %4 "main"
383                OpExecutionMode %4 OriginUpperLeft
384                OpSource ESSL 310
385           %2 = OpTypeVoid
386           %3 = OpTypeFunction %2
387           %6 = OpTypeInt 32 1
388           %7 = OpConstant %6 1
389          %12 = OpTypeBool
390          %13 = OpConstantTrue %12
391           %4 = OpFunction %2 None %3
392 
393           ; %5 doesn't belong to any construct
394           %5 = OpLabel
395                OpBranch %16
396 
397          ; The merge block (%19) is unreachable
398          %16 = OpLabel
399         %201 = OpCopyObject %6 %7
400                OpSelectionMerge %19 None
401                OpBranchConditional %13 %18 %18
402 
403          ; %21 doesn't dominate the merge block - %20
404          %18 = OpLabel
405         %202 = OpCopyObject %6 %7
406                OpSelectionMerge %20 None
407                OpBranchConditional %13 %20 %21
408          %21 = OpLabel
409                OpBranch %20
410 
411          ; The merge block (%24) is an acceptable successor of the propagated
412          ; instruction's block
413          %20 = OpLabel
414         %203 = OpCopyObject %6 %7
415                OpSelectionMerge %24 None
416                OpBranchConditional %13 %24 %30
417          %30 = OpLabel
418         %205 = OpCopyObject %6 %7
419                OpBranch %24
420 
421          ; One of the predecessors of the merge block is not dominated by any
422          ; successor of the propagated instruction's block
423          %24 = OpLabel
424         %204 = OpCopyObject %6 %7
425                OpLoopMerge %29 %25 None
426                OpBranch %25
427          %25 = OpLabel
428         %206 = OpCopyObject %6 %7
429                OpBranchConditional %13 %24 %29
430          %28 = OpLabel ; unreachable predecessor of %29
431                OpBranch %29
432          %29 = OpLabel
433                OpReturn
434 
435          %19 = OpLabel
436                OpReturn
437                OpFunctionEnd
438   )";
439 
440   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
441 }
442 
TEST(TransformationPropagateInstructionDownTest,VariablePointersCapability)443 TEST(TransformationPropagateInstructionDownTest, VariablePointersCapability) {
444   std::string shader = R"(
445                OpCapability Shader
446           %1 = OpExtInstImport "GLSL.std.450"
447                OpMemoryModel Logical GLSL450
448                OpEntryPoint Fragment %4 "main"
449                OpExecutionMode %4 OriginUpperLeft
450                OpSource ESSL 310
451           %2 = OpTypeVoid
452           %3 = OpTypeFunction %2
453           %6 = OpTypeInt 32 1
454           %7 = OpConstant %6 1
455          %12 = OpTypeBool
456          %13 = OpConstantTrue %12
457          %10 = OpTypePointer Workgroup %6
458          %11 = OpVariable %10 Workgroup
459           %4 = OpFunction %2 None %3
460           %5 = OpLabel
461          %18 = OpCopyObject %10 %11
462          %14 = OpCopyObject %10 %11
463                OpSelectionMerge %17 None
464                OpBranchConditional %13 %15 %16
465          %15 = OpLabel
466                OpBranch %17
467          %16 = OpLabel
468                OpBranch %17
469          %17 = OpLabel
470                OpStore %18 %7
471                OpReturn
472                OpFunctionEnd
473   )";
474 
475   const auto env = SPV_ENV_UNIVERSAL_1_3;
476   const auto consumer = nullptr;
477   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
478   spvtools::ValidatorOptions validator_options;
479   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
480                                                kConsoleMessageConsumer));
481   TransformationContext transformation_context(
482       MakeUnique<FactManager>(context.get()), validator_options);
483 
484   {
485     // Can propagate a pointer only if we don't have to create an OpPhi.
486     TransformationPropagateInstructionDown transformation(
487         5, 200, {{{15, 201}, {16, 202}}});
488     ASSERT_TRUE(
489         transformation.IsApplicable(context.get(), transformation_context));
490     ApplyAndCheckFreshIds(transformation, context.get(),
491                           &transformation_context);
492     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
493         context.get(), validator_options, kConsoleMessageConsumer));
494     ASSERT_FALSE(context->get_def_use_mgr()->GetDef(200));
495   }
496   {
497     // Can't propagate a pointer if there is no VariablePointersStorageBuffer
498     // capability and we need to create an OpPhi.
499     TransformationPropagateInstructionDown transformation(
500         5, 200, {{{15, 203}, {16, 204}}});
501     ASSERT_FALSE(context->get_feature_mgr()->HasCapability(
502         SpvCapabilityVariablePointersStorageBuffer));
503     ASSERT_FALSE(
504         transformation.IsApplicable(context.get(), transformation_context));
505 
506     context->AddCapability(SpvCapabilityVariablePointers);
507     ASSERT_TRUE(context->get_feature_mgr()->HasCapability(
508         SpvCapabilityVariablePointersStorageBuffer));
509     ASSERT_TRUE(
510         transformation.IsApplicable(context.get(), transformation_context));
511     ApplyAndCheckFreshIds(transformation, context.get(),
512                           &transformation_context);
513     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
514         context.get(), validator_options, kConsoleMessageConsumer));
515   }
516 
517   std::string after_transformation = R"(
518                OpCapability Shader
519                OpCapability VariablePointers
520           %1 = OpExtInstImport "GLSL.std.450"
521                OpMemoryModel Logical GLSL450
522                OpEntryPoint Fragment %4 "main"
523                OpExecutionMode %4 OriginUpperLeft
524                OpSource ESSL 310
525           %2 = OpTypeVoid
526           %3 = OpTypeFunction %2
527           %6 = OpTypeInt 32 1
528           %7 = OpConstant %6 1
529          %12 = OpTypeBool
530          %13 = OpConstantTrue %12
531          %10 = OpTypePointer Workgroup %6
532          %11 = OpVariable %10 Workgroup
533           %4 = OpFunction %2 None %3
534           %5 = OpLabel
535                OpSelectionMerge %17 None
536                OpBranchConditional %13 %15 %16
537          %15 = OpLabel
538         %203 = OpCopyObject %10 %11
539         %201 = OpCopyObject %10 %11
540                OpBranch %17
541          %16 = OpLabel
542         %204 = OpCopyObject %10 %11
543         %202 = OpCopyObject %10 %11
544                OpBranch %17
545          %17 = OpLabel
546         %200 = OpPhi %10 %203 %15 %204 %16
547                OpStore %200 %7
548                OpReturn
549                OpFunctionEnd
550   )";
551 
552   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
553 }
554 
TEST(TransformationPropagateInstructionDownTest,UseOverflowIdsTest)555 TEST(TransformationPropagateInstructionDownTest, UseOverflowIdsTest) {
556   std::string shader = R"(
557                OpCapability Shader
558           %1 = OpExtInstImport "GLSL.std.450"
559                OpMemoryModel Logical GLSL450
560                OpEntryPoint Fragment %4 "main"
561                OpExecutionMode %4 OriginUpperLeft
562                OpSource ESSL 310
563           %2 = OpTypeVoid
564           %3 = OpTypeFunction %2
565           %6 = OpTypeInt 32 1
566           %7 = OpConstant %6 1
567          %12 = OpTypeBool
568          %13 = OpConstantTrue %12
569          %10 = OpTypePointer Private %6
570          %11 = OpVariable %10 Private
571           %4 = OpFunction %2 None %3
572           %5 = OpLabel
573          %20 = OpCopyObject %6 %7
574                OpSelectionMerge %23 None
575                OpBranchConditional %13 %21 %22
576          %21 = OpLabel
577                OpStore %11 %20
578                OpBranch %23
579          %22 = OpLabel
580                OpStore %11 %20
581                OpBranch %23
582          %23 = OpLabel
583                OpStore %11 %20
584                OpReturn
585                OpFunctionEnd
586   )";
587 
588   const auto env = SPV_ENV_UNIVERSAL_1_3;
589   const auto consumer = nullptr;
590   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
591   spvtools::ValidatorOptions validator_options;
592   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
593                                                kConsoleMessageConsumer));
594   TransformationContext transformation_context(
595       MakeUnique<FactManager>(context.get()), validator_options,
596       MakeUnique<CounterOverflowIdSource>(300));
597 
598   TransformationPropagateInstructionDown transformation(5, 200, {{{21, 201}}});
599   ASSERT_TRUE(
600       transformation.IsApplicable(context.get(), transformation_context));
601   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context,
602                         {300});
603   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
604                                                kConsoleMessageConsumer));
605 
606   std::string after_transformation = R"(
607                OpCapability Shader
608           %1 = OpExtInstImport "GLSL.std.450"
609                OpMemoryModel Logical GLSL450
610                OpEntryPoint Fragment %4 "main"
611                OpExecutionMode %4 OriginUpperLeft
612                OpSource ESSL 310
613           %2 = OpTypeVoid
614           %3 = OpTypeFunction %2
615           %6 = OpTypeInt 32 1
616           %7 = OpConstant %6 1
617          %12 = OpTypeBool
618          %13 = OpConstantTrue %12
619          %10 = OpTypePointer Private %6
620          %11 = OpVariable %10 Private
621           %4 = OpFunction %2 None %3
622           %5 = OpLabel
623                OpSelectionMerge %23 None
624                OpBranchConditional %13 %21 %22
625          %21 = OpLabel
626         %201 = OpCopyObject %6 %7
627                OpStore %11 %201
628                OpBranch %23
629          %22 = OpLabel
630         %300 = OpCopyObject %6 %7
631                OpStore %11 %300
632                OpBranch %23
633          %23 = OpLabel
634         %200 = OpPhi %6 %201 %21 %300 %22
635                OpStore %11 %200
636                OpReturn
637                OpFunctionEnd
638   )";
639 
640   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
641 }
642 
TEST(TransformationPropagateInstructionDownTest,TestCreatedFacts)643 TEST(TransformationPropagateInstructionDownTest, TestCreatedFacts) {
644   std::string shader = R"(
645                OpCapability VariablePointers
646           %1 = OpExtInstImport "GLSL.std.450"
647                OpMemoryModel Logical GLSL450
648                OpEntryPoint Fragment %4 "main"
649                OpExecutionMode %4 OriginUpperLeft
650                OpSource ESSL 310
651           %2 = OpTypeVoid
652           %3 = OpTypeFunction %2
653           %6 = OpTypeInt 32 1
654           %7 = OpConstant %6 1
655          %12 = OpTypeBool
656          %13 = OpConstantTrue %12
657          %10 = OpTypePointer Private %6
658          %11 = OpVariable %10 Private
659           %4 = OpFunction %2 None %3
660           %5 = OpLabel
661          %20 = OpCopyObject %6 %7
662          %24 = OpCopyObject %6 %7 ; Irrelevant id
663          %25 = OpCopyObject %10 %11 ; Pointee is irrelevant
664                OpSelectionMerge %23 None
665                OpBranchConditional %13 %21 %22
666          %21 = OpLabel
667                OpStore %25 %20
668                OpBranch %23
669          %22 = OpLabel ; Dead block
670                OpStore %25 %20
671                OpBranch %23
672          %23 = OpLabel
673                OpStore %25 %20
674                OpReturn
675                OpFunctionEnd
676   )";
677 
678   const auto env = SPV_ENV_UNIVERSAL_1_3;
679   const auto consumer = nullptr;
680   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
681   spvtools::ValidatorOptions validator_options;
682   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
683                                                kConsoleMessageConsumer));
684   TransformationContext transformation_context(
685       MakeUnique<FactManager>(context.get()), validator_options);
686 
687   transformation_context.GetFactManager()->AddFactBlockIsDead(22);
688   transformation_context.GetFactManager()->AddFactIdIsIrrelevant(24);
689   transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
690       25);
691 
692   {
693     // Propagate pointer with PointeeIsIrrelevant fact.
694     TransformationPropagateInstructionDown transformation(
695         5, 200, {{{21, 201}, {22, 202}}});
696     ASSERT_TRUE(
697         transformation.IsApplicable(context.get(), transformation_context));
698     ApplyAndCheckFreshIds(transformation, context.get(),
699                           &transformation_context);
700     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
701         context.get(), validator_options, kConsoleMessageConsumer));
702 
703     ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(201));
704     ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(202));
705     ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(200));
706 
707     ASSERT_TRUE(
708         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(201));
709     ASSERT_TRUE(
710         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(202));
711     ASSERT_TRUE(
712         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(200));
713 
714     ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
715         MakeDataDescriptor(201, {}), MakeDataDescriptor(202, {})));
716     ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
717         MakeDataDescriptor(201, {}), MakeDataDescriptor(200, {})));
718   }
719   {
720     // Propagate an irrelevant id.
721     TransformationPropagateInstructionDown transformation(
722         5, 203, {{{21, 204}, {22, 205}}});
723     ASSERT_TRUE(
724         transformation.IsApplicable(context.get(), transformation_context));
725     ApplyAndCheckFreshIds(transformation, context.get(),
726                           &transformation_context);
727     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
728         context.get(), validator_options, kConsoleMessageConsumer));
729 
730     ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(203));
731     ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(204));
732     ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(205));
733 
734     ASSERT_FALSE(
735         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(203));
736     ASSERT_FALSE(
737         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(204));
738     ASSERT_FALSE(
739         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(205));
740 
741     ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
742         MakeDataDescriptor(204, {}), MakeDataDescriptor(205, {})));
743     ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
744         MakeDataDescriptor(204, {}), MakeDataDescriptor(203, {})));
745   }
746   {
747     // Propagate a regular id.
748     TransformationPropagateInstructionDown transformation(
749         5, 206, {{{21, 207}, {22, 208}}});
750     ASSERT_TRUE(
751         transformation.IsApplicable(context.get(), transformation_context));
752     ApplyAndCheckFreshIds(transformation, context.get(),
753                           &transformation_context);
754     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
755         context.get(), validator_options, kConsoleMessageConsumer));
756 
757     ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(206));
758     ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(207));
759     ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(208));
760 
761     ASSERT_FALSE(
762         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(206));
763     ASSERT_FALSE(
764         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(207));
765     ASSERT_FALSE(
766         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(208));
767 
768     ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
769         MakeDataDescriptor(206, {}), MakeDataDescriptor(207, {})));
770     ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
771         MakeDataDescriptor(206, {}), MakeDataDescriptor(208, {})));
772   }
773 
774   std::string after_transformation = R"(
775                OpCapability VariablePointers
776           %1 = OpExtInstImport "GLSL.std.450"
777                OpMemoryModel Logical GLSL450
778                OpEntryPoint Fragment %4 "main"
779                OpExecutionMode %4 OriginUpperLeft
780                OpSource ESSL 310
781           %2 = OpTypeVoid
782           %3 = OpTypeFunction %2
783           %6 = OpTypeInt 32 1
784           %7 = OpConstant %6 1
785          %12 = OpTypeBool
786          %13 = OpConstantTrue %12
787          %10 = OpTypePointer Private %6
788          %11 = OpVariable %10 Private
789           %4 = OpFunction %2 None %3
790           %5 = OpLabel
791                OpSelectionMerge %23 None
792                OpBranchConditional %13 %21 %22
793          %21 = OpLabel
794         %207 = OpCopyObject %6 %7
795         %204 = OpCopyObject %6 %7 ; Irrelevant id
796         %201 = OpCopyObject %10 %11 ; Pointee is irrelevant
797                OpStore %201 %207
798                OpBranch %23
799          %22 = OpLabel ; Dead block
800         %208 = OpCopyObject %6 %7
801         %205 = OpCopyObject %6 %7 ; Irrelevant id
802         %202 = OpCopyObject %10 %11 ; Pointee is irrelevant
803                OpStore %202 %208
804                OpBranch %23
805          %23 = OpLabel
806         %206 = OpPhi %6 %207 %21 %208 %22
807         %203 = OpPhi %6 %204 %21 %205 %22 ; Irrelevant id
808         %200 = OpPhi %10 %201 %21 %202 %22 ; Pointee is irrelevant
809                OpStore %200 %206
810                OpReturn
811                OpFunctionEnd
812   )";
813 
814   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
815 }
816 
TEST(TransformationPropagateInstructionDownTest,TestLoops1)817 TEST(TransformationPropagateInstructionDownTest, TestLoops1) {
818   std::string shader = R"(
819                OpCapability Shader
820           %1 = OpExtInstImport "GLSL.std.450"
821                OpMemoryModel Logical GLSL450
822                OpEntryPoint Fragment %4 "main"
823                OpExecutionMode %4 OriginUpperLeft
824                OpSource ESSL 310
825           %2 = OpTypeVoid
826           %3 = OpTypeFunction %2
827           %6 = OpTypeInt 32 1
828           %7 = OpConstant %6 1
829          %12 = OpTypeBool
830          %13 = OpConstantTrue %12
831           %4 = OpFunction %2 None %3
832           %5 = OpLabel
833                OpBranch %20
834 
835          %20 = OpLabel
836                OpLoopMerge %26 %25 None
837                OpBranch %21
838 
839          %21 = OpLabel
840          %22 = OpCopyObject %6 %7
841          %31 = OpCopyObject %6 %7
842                OpSelectionMerge %35 None
843                OpBranchConditional %13 %23 %24
844 
845          %23 = OpLabel
846          %27 = OpCopyObject %6 %22
847          %32 = OpCopyObject %6 %31
848                OpBranch %26
849          %24 = OpLabel
850          %28 = OpCopyObject %6 %22
851          %33 = OpCopyObject %6 %31
852                OpBranchConditional %13 %26 %25
853 
854          %35 = OpLabel
855                OpBranch %25
856 
857          %25 = OpLabel
858          %29 = OpCopyObject %6 %22
859          %34 = OpCopyObject %6 %31
860                OpBranch %20
861          %26 = OpLabel
862          %30 = OpCopyObject %6 %22
863                OpReturn
864                OpFunctionEnd
865   )";
866 
867   const auto env = SPV_ENV_UNIVERSAL_1_3;
868   const auto consumer = nullptr;
869   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
870   spvtools::ValidatorOptions validator_options;
871   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
872                                                kConsoleMessageConsumer));
873   TransformationContext transformation_context(
874       MakeUnique<FactManager>(context.get()), validator_options);
875 
876   {
877     TransformationPropagateInstructionDown transformation(
878         21, 200, {{{23, 201}, {24, 202}}});
879     ASSERT_TRUE(
880         transformation.IsApplicable(context.get(), transformation_context));
881     ApplyAndCheckFreshIds(transformation, context.get(),
882                           &transformation_context);
883     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
884         context.get(), validator_options, kConsoleMessageConsumer));
885   }
886 
887   // Can't replace usage of %22 in %26.
888   ASSERT_FALSE(
889       TransformationPropagateInstructionDown(21, 200, {{{23, 201}, {24, 202}}})
890           .IsApplicable(context.get(), transformation_context));
891 
892   std::string after_transformation = R"(
893                OpCapability Shader
894           %1 = OpExtInstImport "GLSL.std.450"
895                OpMemoryModel Logical GLSL450
896                OpEntryPoint Fragment %4 "main"
897                OpExecutionMode %4 OriginUpperLeft
898                OpSource ESSL 310
899           %2 = OpTypeVoid
900           %3 = OpTypeFunction %2
901           %6 = OpTypeInt 32 1
902           %7 = OpConstant %6 1
903          %12 = OpTypeBool
904          %13 = OpConstantTrue %12
905           %4 = OpFunction %2 None %3
906           %5 = OpLabel
907                OpBranch %20
908 
909          %20 = OpLabel
910                OpLoopMerge %26 %25 None
911                OpBranch %21
912 
913          %21 = OpLabel
914          %22 = OpCopyObject %6 %7
915                OpSelectionMerge %35 None
916                OpBranchConditional %13 %23 %24
917          %23 = OpLabel
918         %201 = OpCopyObject %6 %7
919          %27 = OpCopyObject %6 %22
920          %32 = OpCopyObject %6 %201
921                OpBranch %26
922          %24 = OpLabel
923         %202 = OpCopyObject %6 %7
924          %28 = OpCopyObject %6 %22
925          %33 = OpCopyObject %6 %202
926                OpBranchConditional %13 %26 %25
927 
928          %35 = OpLabel
929                OpBranch %25
930 
931          %25 = OpLabel
932          %29 = OpCopyObject %6 %22
933          %34 = OpCopyObject %6 %202
934                OpBranch %20
935          %26 = OpLabel
936          %30 = OpCopyObject %6 %22
937                OpReturn
938                OpFunctionEnd
939   )";
940 
941   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
942 }
943 
TEST(TransformationPropagateInstructionDownTest,TestLoops2)944 TEST(TransformationPropagateInstructionDownTest, TestLoops2) {
945   std::string shader = R"(
946                OpCapability Shader
947           %1 = OpExtInstImport "GLSL.std.450"
948                OpMemoryModel Logical GLSL450
949                OpEntryPoint Fragment %4 "main"
950                OpExecutionMode %4 OriginUpperLeft
951                OpSource ESSL 310
952           %2 = OpTypeVoid
953           %3 = OpTypeFunction %2
954           %6 = OpTypeInt 32 1
955           %7 = OpConstant %6 1
956          %12 = OpTypeBool
957          %13 = OpConstantTrue %12
958           %4 = OpFunction %2 None %3
959           %5 = OpLabel
960                OpBranch %20
961 
962          %20 = OpLabel
963          %23 = OpPhi %6 %7 %5 %24 %21
964                OpLoopMerge %22 %21 None
965                OpBranch %21
966 
967          %21 = OpLabel
968          %24 = OpCopyObject %6 %23
969          %25 = OpCopyObject %6 %7
970                OpBranchConditional %13 %22 %20
971 
972          %22 = OpLabel
973                OpReturn
974                OpFunctionEnd
975   )";
976 
977   const auto env = SPV_ENV_UNIVERSAL_1_3;
978   const auto consumer = nullptr;
979   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
980   spvtools::ValidatorOptions validator_options;
981   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
982                                                kConsoleMessageConsumer));
983   TransformationContext transformation_context(
984       MakeUnique<FactManager>(context.get()), validator_options);
985 
986   {
987     // Can propagate %25 from %21 into %20.
988     TransformationPropagateInstructionDown transformation(
989         21, 200, {{{20, 201}, {22, 202}}});
990     ASSERT_TRUE(
991         transformation.IsApplicable(context.get(), transformation_context));
992     ApplyAndCheckFreshIds(transformation, context.get(),
993                           &transformation_context);
994     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
995         context.get(), validator_options, kConsoleMessageConsumer));
996   }
997   {
998     // Can propagate %201 from %20 into %21.
999     TransformationPropagateInstructionDown transformation(20, 200,
1000                                                           {{{21, 203}}});
1001     ASSERT_TRUE(
1002         transformation.IsApplicable(context.get(), transformation_context));
1003     ApplyAndCheckFreshIds(transformation, context.get(),
1004                           &transformation_context);
1005     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
1006         context.get(), validator_options, kConsoleMessageConsumer));
1007   }
1008 
1009   // Can't propagate %24 from %21 into %20.
1010   ASSERT_FALSE(
1011       TransformationPropagateInstructionDown(21, 200, {{{20, 204}, {22, 205}}})
1012           .IsApplicable(context.get(), transformation_context));
1013 
1014   std::string after_transformation = R"(
1015                OpCapability Shader
1016           %1 = OpExtInstImport "GLSL.std.450"
1017                OpMemoryModel Logical GLSL450
1018                OpEntryPoint Fragment %4 "main"
1019                OpExecutionMode %4 OriginUpperLeft
1020                OpSource ESSL 310
1021           %2 = OpTypeVoid
1022           %3 = OpTypeFunction %2
1023           %6 = OpTypeInt 32 1
1024           %7 = OpConstant %6 1
1025          %12 = OpTypeBool
1026          %13 = OpConstantTrue %12
1027           %4 = OpFunction %2 None %3
1028           %5 = OpLabel
1029                OpBranch %20
1030 
1031          %20 = OpLabel
1032          %23 = OpPhi %6 %7 %5 %24 %21
1033                OpLoopMerge %22 %21 None
1034                OpBranch %21
1035 
1036          %21 = OpLabel
1037         %203 = OpCopyObject %6 %7
1038          %24 = OpCopyObject %6 %23
1039                OpBranchConditional %13 %22 %20
1040 
1041          %22 = OpLabel
1042         %200 = OpPhi %6 %203 %21
1043         %202 = OpCopyObject %6 %7
1044                OpReturn
1045                OpFunctionEnd
1046   )";
1047 
1048   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1049 }
1050 
TEST(TransformationPropagateInstructionDownTest,TestLoops3)1051 TEST(TransformationPropagateInstructionDownTest, TestLoops3) {
1052   std::string shader = R"(
1053                OpCapability Shader
1054           %1 = OpExtInstImport "GLSL.std.450"
1055                OpMemoryModel Logical GLSL450
1056                OpEntryPoint Fragment %4 "main"
1057                OpExecutionMode %4 OriginUpperLeft
1058                OpSource ESSL 310
1059           %2 = OpTypeVoid
1060           %3 = OpTypeFunction %2
1061           %6 = OpTypeInt 32 1
1062           %7 = OpConstant %6 1
1063          %12 = OpTypeBool
1064          %13 = OpConstantTrue %12
1065           %4 = OpFunction %2 None %3
1066           %5 = OpLabel
1067                OpBranch %20
1068 
1069          %20 = OpLabel
1070          %27 = OpPhi %6 %7 %5 %26 %20
1071          %25 = OpCopyObject %6 %7
1072          %26 = OpCopyObject %6 %7
1073                OpLoopMerge %22 %20 None
1074                OpBranchConditional %13 %20 %22
1075 
1076          %22 = OpLabel
1077                OpReturn
1078                OpFunctionEnd
1079   )";
1080 
1081   const auto env = SPV_ENV_UNIVERSAL_1_3;
1082   const auto consumer = nullptr;
1083   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1084   spvtools::ValidatorOptions validator_options;
1085   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1086                                                kConsoleMessageConsumer));
1087   TransformationContext transformation_context(
1088       MakeUnique<FactManager>(context.get()), validator_options);
1089 
1090   {
1091     // Propagate %25 into %20 and %22. Not that we are skipping %26 since not
1092     // all of its users are in different blocks (%27).h
1093     TransformationPropagateInstructionDown transformation(
1094         20, 200, {{{20, 201}, {22, 202}}});
1095     ASSERT_TRUE(
1096         transformation.IsApplicable(context.get(), transformation_context));
1097     ApplyAndCheckFreshIds(transformation, context.get(),
1098                           &transformation_context);
1099     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
1100         context.get(), validator_options, kConsoleMessageConsumer));
1101   }
1102 
1103   std::string after_transformation = R"(
1104                OpCapability Shader
1105           %1 = OpExtInstImport "GLSL.std.450"
1106                OpMemoryModel Logical GLSL450
1107                OpEntryPoint Fragment %4 "main"
1108                OpExecutionMode %4 OriginUpperLeft
1109                OpSource ESSL 310
1110           %2 = OpTypeVoid
1111           %3 = OpTypeFunction %2
1112           %6 = OpTypeInt 32 1
1113           %7 = OpConstant %6 1
1114          %12 = OpTypeBool
1115          %13 = OpConstantTrue %12
1116           %4 = OpFunction %2 None %3
1117           %5 = OpLabel
1118                OpBranch %20
1119 
1120          %20 = OpLabel
1121          %27 = OpPhi %6 %7 %5 %26 %20
1122         %201 = OpCopyObject %6 %7
1123          %26 = OpCopyObject %6 %7
1124                OpLoopMerge %22 %20 None
1125                OpBranchConditional %13 %20 %22
1126 
1127          %22 = OpLabel
1128         %202 = OpCopyObject %6 %7
1129                OpReturn
1130                OpFunctionEnd
1131   )";
1132 
1133   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1134 }
1135 
1136 }  // namespace
1137 }  // namespace fuzz
1138 }  // namespace spvtools
1139