1 // Copyright (c) 2020 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_add_opphi_synonym.h"
16 
17 #include "gtest/gtest.h"
18 #include "source/fuzz/fuzzer_util.h"
19 #include "test/fuzz/fuzz_test_util.h"
20 
21 namespace spvtools {
22 namespace fuzz {
23 namespace {
24 
MakeSynonymFact(uint32_t first,uint32_t second)25 protobufs::Fact MakeSynonymFact(uint32_t first, uint32_t second) {
26   protobufs::FactDataSynonym data_synonym_fact;
27   *data_synonym_fact.mutable_data1() = MakeDataDescriptor(first, {});
28   *data_synonym_fact.mutable_data2() = MakeDataDescriptor(second, {});
29   protobufs::Fact result;
30   *result.mutable_data_synonym_fact() = data_synonym_fact;
31   return result;
32 }
33 
34 // Adds synonym facts to the fact manager.
SetUpIdSynonyms(FactManager * fact_manager)35 void SetUpIdSynonyms(FactManager* fact_manager) {
36   fact_manager->MaybeAddFact(MakeSynonymFact(11, 9));
37   fact_manager->MaybeAddFact(MakeSynonymFact(13, 9));
38   fact_manager->MaybeAddFact(MakeSynonymFact(14, 9));
39   fact_manager->MaybeAddFact(MakeSynonymFact(19, 9));
40   fact_manager->MaybeAddFact(MakeSynonymFact(20, 9));
41   fact_manager->MaybeAddFact(MakeSynonymFact(10, 21));
42 }
43 
TEST(TransformationAddOpPhiSynonymTest,Inapplicable)44 TEST(TransformationAddOpPhiSynonymTest, Inapplicable) {
45   std::string shader = R"(
46                OpCapability Shader
47           %1 = OpExtInstImport "GLSL.std.450"
48                OpMemoryModel Logical GLSL450
49                OpEntryPoint Fragment %2 "main"
50                OpExecutionMode %2 OriginUpperLeft
51                OpSource ESSL 310
52                OpName %2 "main"
53           %3 = OpTypeVoid
54           %4 = OpTypeFunction %3
55           %5 = OpTypeBool
56           %6 = OpConstantTrue %5
57           %7 = OpTypeInt 32 1
58           %8 = OpTypeInt 32 0
59          %22 = OpTypePointer Function %7
60           %9 = OpConstant %7 1
61          %10 = OpConstant %7 2
62          %11 = OpConstant %8 1
63           %2 = OpFunction %3 None %4
64          %12 = OpLabel
65          %23 = OpVariable %22 Function
66          %13 = OpCopyObject %7 %9
67          %14 = OpCopyObject %8 %11
68                OpBranch %15
69          %15 = OpLabel
70                OpSelectionMerge %16 None
71                OpBranchConditional %6 %17 %18
72          %17 = OpLabel
73          %19 = OpCopyObject %7 %13
74          %20 = OpCopyObject %8 %14
75          %21 = OpCopyObject %7 %10
76                OpBranch %16
77          %18 = OpLabel
78          %24 = OpCopyObject %22 %23
79                OpBranch %16
80          %16 = OpLabel
81                OpReturn
82                OpFunctionEnd
83 )";
84 
85   const auto env = SPV_ENV_UNIVERSAL_1_5;
86   const auto consumer = nullptr;
87   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
88   spvtools::ValidatorOptions validator_options;
89   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
90                                                kConsoleMessageConsumer));
91   TransformationContext transformation_context(
92       MakeUnique<FactManager>(context.get()), validator_options);
93   SetUpIdSynonyms(transformation_context.GetFactManager());
94   transformation_context.GetFactManager()->MaybeAddFact(
95       MakeSynonymFact(23, 24));
96 
97   // %13 is not a block label.
98   ASSERT_FALSE(TransformationAddOpPhiSynonym(13, {{}}, 100)
99                    .IsApplicable(context.get(), transformation_context));
100 
101   // Block %12 does not have a predecessor.
102   ASSERT_FALSE(TransformationAddOpPhiSynonym(12, {{}}, 100)
103                    .IsApplicable(context.get(), transformation_context));
104 
105   // Not all predecessors of %16 (%17 and %18) are considered in the map.
106   ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 19}}}, 100)
107                    .IsApplicable(context.get(), transformation_context));
108 
109   // %30 does not exist in the module.
110   ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{30, 19}}}, 100)
111                    .IsApplicable(context.get(), transformation_context));
112 
113   // %20 is not a block label.
114   ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{20, 19}}}, 100)
115                    .IsApplicable(context.get(), transformation_context));
116 
117   // %15 is not the id of one of the predecessors of the block.
118   ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{15, 19}}}, 100)
119                    .IsApplicable(context.get(), transformation_context));
120 
121   // %30 does not exist in the module.
122   ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 30}, {18, 13}}}, 100)
123                    .IsApplicable(context.get(), transformation_context));
124 
125   // %19 and %10 are not synonymous.
126   ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 19}, {18, 10}}}, 100)
127                    .IsApplicable(context.get(), transformation_context));
128 
129   // %19 and %14 do not have the same type.
130   ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 19}, {18, 14}}}, 100)
131                    .IsApplicable(context.get(), transformation_context));
132 
133   // %19 is not available at the end of %18.
134   ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 9}, {18, 19}}}, 100)
135                    .IsApplicable(context.get(), transformation_context));
136 
137   // %21 is not a fresh id.
138   ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 9}, {18, 9}}}, 21)
139                    .IsApplicable(context.get(), transformation_context));
140 
141   // %23 and %24 have pointer id.
142   ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 23}, {18, 24}}}, 100)
143                    .IsApplicable(context.get(), transformation_context));
144 }
145 
TEST(TransformationAddOpPhiSynonymTest,Apply)146 TEST(TransformationAddOpPhiSynonymTest, Apply) {
147   std::string shader = R"(
148                OpCapability Shader
149           %1 = OpExtInstImport "GLSL.std.450"
150                OpMemoryModel Logical GLSL450
151                OpEntryPoint Fragment %2 "main"
152                OpExecutionMode %2 OriginUpperLeft
153                OpSource ESSL 310
154                OpName %2 "main"
155           %3 = OpTypeVoid
156           %4 = OpTypeFunction %3
157           %5 = OpTypeBool
158           %6 = OpConstantTrue %5
159           %7 = OpTypeInt 32 1
160           %8 = OpTypeInt 32 0
161           %9 = OpConstant %7 1
162          %10 = OpConstant %7 2
163          %11 = OpConstant %8 1
164           %2 = OpFunction %3 None %4
165          %12 = OpLabel
166          %13 = OpCopyObject %7 %9
167          %14 = OpCopyObject %8 %11
168                OpBranch %15
169          %15 = OpLabel
170                OpSelectionMerge %16 None
171                OpBranchConditional %6 %17 %18
172          %17 = OpLabel
173          %19 = OpCopyObject %7 %13
174          %20 = OpCopyObject %8 %14
175          %21 = OpCopyObject %7 %10
176                OpBranch %16
177          %18 = OpLabel
178                OpBranch %16
179          %16 = OpLabel
180                OpBranch %22
181          %22 = OpLabel
182                OpLoopMerge %23 %24 None
183                OpBranchConditional %6 %25 %23
184          %25 = OpLabel
185                OpSelectionMerge %26 None
186                OpBranchConditional %6 %27 %26
187          %27 = OpLabel
188          %28 = OpCopyObject %7 %13
189                OpBranch %23
190          %26 = OpLabel
191                OpSelectionMerge %29 None
192                OpBranchConditional %6 %29 %24
193          %29 = OpLabel
194          %30 = OpCopyObject %7 %13
195                OpBranch %23
196          %24 = OpLabel
197                OpBranch %22
198          %23 = OpLabel
199                OpSelectionMerge %31 None
200                OpBranchConditional %6 %31 %31
201          %31 = OpLabel
202                OpReturn
203                OpFunctionEnd
204 )";
205 
206   const auto env = SPV_ENV_UNIVERSAL_1_5;
207   const auto consumer = nullptr;
208   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
209   spvtools::ValidatorOptions validator_options;
210   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
211                                                kConsoleMessageConsumer));
212   TransformationContext transformation_context(
213       MakeUnique<FactManager>(context.get()), validator_options);
214   SetUpIdSynonyms(transformation_context.GetFactManager());
215 
216   // Add some further synonym facts.
217   transformation_context.GetFactManager()->MaybeAddFact(MakeSynonymFact(28, 9));
218   transformation_context.GetFactManager()->MaybeAddFact(MakeSynonymFact(30, 9));
219 
220   auto transformation1 = TransformationAddOpPhiSynonym(17, {{{15, 13}}}, 100);
221   ASSERT_TRUE(
222       transformation1.IsApplicable(context.get(), transformation_context));
223   ApplyAndCheckFreshIds(transformation1, context.get(),
224                         &transformation_context);
225   ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
226       MakeDataDescriptor(100, {}), MakeDataDescriptor(9, {})));
227 
228   auto transformation2 =
229       TransformationAddOpPhiSynonym(16, {{{17, 19}, {18, 13}}}, 101);
230   ASSERT_TRUE(
231       transformation2.IsApplicable(context.get(), transformation_context));
232   ApplyAndCheckFreshIds(transformation2, context.get(),
233                         &transformation_context);
234   ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
235       MakeDataDescriptor(101, {}), MakeDataDescriptor(9, {})));
236 
237   auto transformation3 =
238       TransformationAddOpPhiSynonym(23, {{{22, 13}, {27, 28}, {29, 30}}}, 102);
239   ASSERT_TRUE(
240       transformation3.IsApplicable(context.get(), transformation_context));
241   ApplyAndCheckFreshIds(transformation3, context.get(),
242                         &transformation_context);
243   ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
244       MakeDataDescriptor(102, {}), MakeDataDescriptor(9, {})));
245 
246   auto transformation4 = TransformationAddOpPhiSynonym(31, {{{23, 13}}}, 103);
247   ASSERT_TRUE(
248       transformation4.IsApplicable(context.get(), transformation_context));
249   ApplyAndCheckFreshIds(transformation4, context.get(),
250                         &transformation_context);
251   ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
252       MakeDataDescriptor(103, {}), MakeDataDescriptor(9, {})));
253 
254   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
255                                                kConsoleMessageConsumer));
256 
257   std::string after_transformations = R"(
258                OpCapability Shader
259           %1 = OpExtInstImport "GLSL.std.450"
260                OpMemoryModel Logical GLSL450
261                OpEntryPoint Fragment %2 "main"
262                OpExecutionMode %2 OriginUpperLeft
263                OpSource ESSL 310
264                OpName %2 "main"
265           %3 = OpTypeVoid
266           %4 = OpTypeFunction %3
267           %5 = OpTypeBool
268           %6 = OpConstantTrue %5
269           %7 = OpTypeInt 32 1
270           %8 = OpTypeInt 32 0
271           %9 = OpConstant %7 1
272          %10 = OpConstant %7 2
273          %11 = OpConstant %8 1
274           %2 = OpFunction %3 None %4
275          %12 = OpLabel
276          %13 = OpCopyObject %7 %9
277          %14 = OpCopyObject %8 %11
278                OpBranch %15
279          %15 = OpLabel
280                OpSelectionMerge %16 None
281                OpBranchConditional %6 %17 %18
282          %17 = OpLabel
283         %100 = OpPhi %7 %13 %15
284          %19 = OpCopyObject %7 %13
285          %20 = OpCopyObject %8 %14
286          %21 = OpCopyObject %7 %10
287                OpBranch %16
288          %18 = OpLabel
289                OpBranch %16
290          %16 = OpLabel
291         %101 = OpPhi %7 %19 %17 %13 %18
292                OpBranch %22
293          %22 = OpLabel
294                OpLoopMerge %23 %24 None
295                OpBranchConditional %6 %25 %23
296          %25 = OpLabel
297                OpSelectionMerge %26 None
298                OpBranchConditional %6 %27 %26
299          %27 = OpLabel
300          %28 = OpCopyObject %7 %13
301                OpBranch %23
302          %26 = OpLabel
303                OpSelectionMerge %29 None
304                OpBranchConditional %6 %29 %24
305          %29 = OpLabel
306          %30 = OpCopyObject %7 %13
307                OpBranch %23
308          %24 = OpLabel
309                OpBranch %22
310          %23 = OpLabel
311         %102 = OpPhi %7 %13 %22 %28 %27 %30 %29
312                OpSelectionMerge %31 None
313                OpBranchConditional %6 %31 %31
314          %31 = OpLabel
315         %103 = OpPhi %7 %13 %23
316                OpReturn
317                OpFunctionEnd
318 )";
319 
320   ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
321 }
322 
TEST(TransformationAddOpPhiSynonymTest,VariablePointers)323 TEST(TransformationAddOpPhiSynonymTest, VariablePointers) {
324   std::string shader = R"(
325                OpCapability Shader
326                OpCapability VariablePointers
327           %1 = OpExtInstImport "GLSL.std.450"
328                OpMemoryModel Logical GLSL450
329                OpEntryPoint Fragment %2 "main" %3
330                OpExecutionMode %2 OriginUpperLeft
331                OpSource ESSL 310
332           %4 = OpTypeVoid
333           %5 = OpTypeFunction %4
334           %6 = OpTypeBool
335           %7 = OpConstantTrue %6
336           %8 = OpTypeInt 32 1
337           %9 = OpTypePointer Function %8
338          %10 = OpTypePointer Workgroup %8
339           %3 = OpVariable %10 Workgroup
340           %2 = OpFunction %4 None %5
341          %11 = OpLabel
342          %12 = OpVariable %9 Function
343                OpSelectionMerge %13 None
344                OpBranchConditional %7 %14 %13
345          %14 = OpLabel
346          %15 = OpCopyObject %10 %3
347          %16 = OpCopyObject %9 %12
348                OpBranch %13
349          %13 = OpLabel
350                OpReturn
351                OpFunctionEnd
352 )";
353 
354   const auto env = SPV_ENV_UNIVERSAL_1_5;
355   const auto consumer = nullptr;
356   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
357   spvtools::ValidatorOptions validator_options;
358   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
359                                                kConsoleMessageConsumer));
360   TransformationContext transformation_context(
361       MakeUnique<FactManager>(context.get()), validator_options);
362   // Declare synonyms
363   transformation_context.GetFactManager()->MaybeAddFact(MakeSynonymFact(3, 15));
364   transformation_context.GetFactManager()->MaybeAddFact(
365       MakeSynonymFact(12, 16));
366 
367   // Remove the VariablePointers capability.
368   context.get()->get_feature_mgr()->RemoveCapability(
369       SpvCapabilityVariablePointers);
370 
371   // The VariablePointers capability is required to add an OpPhi instruction of
372   // pointer type.
373   ASSERT_FALSE(TransformationAddOpPhiSynonym(13, {{{11, 3}, {14, 15}}}, 100)
374                    .IsApplicable(context.get(), transformation_context));
375 
376   // Add the VariablePointers capability back.
377   context.get()->get_feature_mgr()->AddCapability(
378       SpvCapabilityVariablePointers);
379 
380   // If the ids have pointer type, the storage class must be Workgroup or
381   // StorageBuffer, but it is Function in this case.
382   ASSERT_FALSE(TransformationAddOpPhiSynonym(13, {{{11, 12}, {14, 16}}}, 100)
383                    .IsApplicable(context.get(), transformation_context));
384 
385   auto transformation =
386       TransformationAddOpPhiSynonym(13, {{{11, 3}, {14, 15}}}, 100);
387   ASSERT_TRUE(
388       transformation.IsApplicable(context.get(), transformation_context));
389   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
390 
391   std::string after_transformation = R"(
392                OpCapability Shader
393                OpCapability VariablePointers
394           %1 = OpExtInstImport "GLSL.std.450"
395                OpMemoryModel Logical GLSL450
396                OpEntryPoint Fragment %2 "main" %3
397                OpExecutionMode %2 OriginUpperLeft
398                OpSource ESSL 310
399           %4 = OpTypeVoid
400           %5 = OpTypeFunction %4
401           %6 = OpTypeBool
402           %7 = OpConstantTrue %6
403           %8 = OpTypeInt 32 1
404           %9 = OpTypePointer Function %8
405          %10 = OpTypePointer Workgroup %8
406           %3 = OpVariable %10 Workgroup
407           %2 = OpFunction %4 None %5
408          %11 = OpLabel
409          %12 = OpVariable %9 Function
410                OpSelectionMerge %13 None
411                OpBranchConditional %7 %14 %13
412          %14 = OpLabel
413          %15 = OpCopyObject %10 %3
414          %16 = OpCopyObject %9 %12
415                OpBranch %13
416          %13 = OpLabel
417         %100 = OpPhi %10 %3 %11 %15 %14
418                OpReturn
419                OpFunctionEnd
420 )";
421 
422   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
423 }
424 
TEST(TransformationAddOpPhiSynonymTest,DeadBlock)425 TEST(TransformationAddOpPhiSynonymTest, DeadBlock) {
426   std::string shader = R"(
427                OpCapability Shader
428           %1 = OpExtInstImport "GLSL.std.450"
429                OpMemoryModel Logical GLSL450
430                OpEntryPoint Fragment %4 "main"
431                OpExecutionMode %4 OriginUpperLeft
432                OpSource ESSL 320
433           %2 = OpTypeVoid
434           %3 = OpTypeFunction %2
435           %6 = OpTypeInt 32 1
436           %7 = OpTypePointer Function %6
437           %9 = OpConstant %6 2
438          %10 = OpTypeBool
439          %11 = OpConstantFalse %10
440          %15 = OpConstant %6 0
441          %50 = OpConstant %6 0
442           %4 = OpFunction %2 None %3
443           %5 = OpLabel
444           %8 = OpVariable %7 Function
445                OpStore %8 %9
446                OpSelectionMerge %13 None
447                OpBranchConditional %11 %12 %13
448          %12 = OpLabel
449          %14 = OpLoad %6 %8
450          %16 = OpIEqual %10 %14 %15
451                OpSelectionMerge %18 None
452                OpBranchConditional %16 %17 %40
453          %17 = OpLabel
454                OpBranch %18
455          %40 = OpLabel
456                OpBranch %18
457          %18 = OpLabel
458                OpBranch %13
459          %13 = OpLabel
460                OpReturn
461                OpFunctionEnd
462   )";
463 
464   const auto env = SPV_ENV_UNIVERSAL_1_5;
465   const auto consumer = nullptr;
466   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
467   spvtools::ValidatorOptions validator_options;
468   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
469                                                kConsoleMessageConsumer));
470   TransformationContext transformation_context(
471       MakeUnique<FactManager>(context.get()), validator_options);
472   // Dead blocks
473   transformation_context.GetFactManager()->AddFactBlockIsDead(12);
474   transformation_context.GetFactManager()->AddFactBlockIsDead(17);
475   transformation_context.GetFactManager()->AddFactBlockIsDead(18);
476 
477   // Declare synonym
478   ASSERT_TRUE(transformation_context.GetFactManager()->MaybeAddFact(
479       MakeSynonymFact(15, 50)));
480 
481   // Bad because the block 18 is dead.
482   ASSERT_FALSE(TransformationAddOpPhiSynonym(18, {{{17, 15}, {40, 50}}}, 100)
483                    .IsApplicable(context.get(), transformation_context));
484 }
485 
486 }  // namespace
487 }  // namespace fuzz
488 }  // namespace spvtools
489