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_add_global_variable.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 
TEST(TransformationAddGlobalVariableTest, BasicTest)25 TEST(TransformationAddGlobalVariableTest, BasicTest) {
26   std::string shader = R"(
27                OpCapability Shader
28           %1 = OpExtInstImport "GLSL.std.450"
29                OpMemoryModel Logical GLSL450
30                OpEntryPoint Fragment %4 "main"
31                OpExecutionMode %4 OriginUpperLeft
32                OpSource ESSL 310
33           %2 = OpTypeVoid
34           %3 = OpTypeFunction %2
35           %6 = OpTypeFloat 32
36          %40 = OpConstant %6 0
37           %7 = OpTypeInt 32 1
38           %8 = OpTypeVector %6 2
39          %41 = OpConstantComposite %8 %40 %40
40           %9 = OpTypePointer Function %6
41          %10 = OpTypePointer Private %6
42          %20 = OpTypePointer Uniform %6
43          %11 = OpTypePointer Function %7
44          %12 = OpTypePointer Private %7
45          %13 = OpTypePointer Private %8
46          %14 = OpVariable %10 Private
47          %15 = OpVariable %20 Uniform
48          %16 = OpConstant %7 1
49          %17 = OpTypePointer Private %10
50          %18 = OpTypeBool
51          %19 = OpTypePointer Private %18
52          %21 = OpConstantTrue %18
53          %22 = OpConstantFalse %18
54           %4 = OpFunction %2 None %3
55           %5 = OpLabel
56                OpReturn
57                OpFunctionEnd
58   )";
59 
60   const auto env = SPV_ENV_UNIVERSAL_1_3;
61   const auto consumer = nullptr;
62   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
63   spvtools::ValidatorOptions validator_options;
64   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
65                                                kConsoleMessageConsumer));
66   TransformationContext transformation_context(
67       MakeUnique<FactManager>(context.get()), validator_options);
68   // Id already in use
69   ASSERT_FALSE(TransformationAddGlobalVariable(
70                    4, 10, spv::StorageClass::Private, 0, true)
71                    .IsApplicable(context.get(), transformation_context));
72   // %1 is not a type
73   ASSERT_FALSE(TransformationAddGlobalVariable(
74                    100, 1, spv::StorageClass::Private, 0, false)
75                    .IsApplicable(context.get(), transformation_context));
76 
77   // %7 is not a pointer type
78   ASSERT_FALSE(TransformationAddGlobalVariable(
79                    100, 7, spv::StorageClass::Private, 0, true)
80                    .IsApplicable(context.get(), transformation_context));
81 
82   // %9 does not have Private storage class
83   ASSERT_FALSE(TransformationAddGlobalVariable(
84                    100, 9, spv::StorageClass::Private, 0, false)
85                    .IsApplicable(context.get(), transformation_context));
86 
87   // %15 does not have Private storage class
88   ASSERT_FALSE(TransformationAddGlobalVariable(
89                    100, 15, spv::StorageClass::Private, 0, true)
90                    .IsApplicable(context.get(), transformation_context));
91 
92   // %10 is a pointer to float, while %16 is an int constant
93   ASSERT_FALSE(TransformationAddGlobalVariable(
94                    100, 10, spv::StorageClass::Private, 16, false)
95                    .IsApplicable(context.get(), transformation_context));
96 
97   // %10 is a Private pointer to float, while %15 is a variable with type
98   // Uniform float pointer
99   ASSERT_FALSE(TransformationAddGlobalVariable(
100                    100, 10, spv::StorageClass::Private, 15, true)
101                    .IsApplicable(context.get(), transformation_context));
102 
103   // %12 is a Private pointer to int, while %10 is a variable with type
104   // Private float pointer
105   ASSERT_FALSE(TransformationAddGlobalVariable(
106                    100, 12, spv::StorageClass::Private, 10, false)
107                    .IsApplicable(context.get(), transformation_context));
108 
109   // %10 is pointer-to-float, and %14 has type pointer-to-float; that's not OK
110   // since the initializer's type should be the *pointee* type.
111   ASSERT_FALSE(TransformationAddGlobalVariable(
112                    104, 10, spv::StorageClass::Private, 14, true)
113                    .IsApplicable(context.get(), transformation_context));
114 
115   // This would work in principle, but logical addressing does not allow
116   // a pointer to a pointer.
117   ASSERT_FALSE(TransformationAddGlobalVariable(
118                    104, 17, spv::StorageClass::Private, 14, false)
119                    .IsApplicable(context.get(), transformation_context));
120 
121   {
122     // %100 = OpVariable %12 Private
123     ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100));
124     TransformationAddGlobalVariable transformation(
125         100, 12, spv::StorageClass::Private, 16, true);
126     ASSERT_TRUE(
127         transformation.IsApplicable(context.get(), transformation_context));
128     ApplyAndCheckFreshIds(transformation, context.get(),
129                           &transformation_context);
130     ASSERT_EQ(spv::Op::OpVariable,
131               context->get_def_use_mgr()->GetDef(100)->opcode());
132     ASSERT_EQ(
133         spv::StorageClass::Private,
134         static_cast<spv::StorageClass>(
135             context->get_def_use_mgr()->GetDef(100)->GetSingleWordInOperand(
136                 0)));
137   }
138 
139   TransformationAddGlobalVariable transformations[] = {
140       // %101 = OpVariable %10 Private
141       TransformationAddGlobalVariable(101, 10, spv::StorageClass::Private, 40,
142                                       false),
143 
144       // %102 = OpVariable %13 Private
145       TransformationAddGlobalVariable(102, 13, spv::StorageClass::Private, 41,
146                                       true),
147 
148       // %103 = OpVariable %12 Private %16
149       TransformationAddGlobalVariable(103, 12, spv::StorageClass::Private, 16,
150                                       false),
151 
152       // %104 = OpVariable %19 Private %21
153       TransformationAddGlobalVariable(104, 19, spv::StorageClass::Private, 21,
154                                       true),
155 
156       // %105 = OpVariable %19 Private %22
157       TransformationAddGlobalVariable(105, 19, spv::StorageClass::Private, 22,
158                                       false)};
159 
160   for (auto& transformation : transformations) {
161     ASSERT_TRUE(
162         transformation.IsApplicable(context.get(), transformation_context));
163     ApplyAndCheckFreshIds(transformation, context.get(),
164                           &transformation_context);
165   }
166   ASSERT_TRUE(
167       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
168   ASSERT_TRUE(
169       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
170   ASSERT_TRUE(
171       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(104));
172   ASSERT_FALSE(
173       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
174   ASSERT_FALSE(
175       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(103));
176   ASSERT_FALSE(
177       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(105));
178 
179   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
180                                                kConsoleMessageConsumer));
181 
182   std::string after_transformation = R"(
183                OpCapability Shader
184           %1 = OpExtInstImport "GLSL.std.450"
185                OpMemoryModel Logical GLSL450
186                OpEntryPoint Fragment %4 "main"
187                OpExecutionMode %4 OriginUpperLeft
188                OpSource ESSL 310
189           %2 = OpTypeVoid
190           %3 = OpTypeFunction %2
191           %6 = OpTypeFloat 32
192          %40 = OpConstant %6 0
193           %7 = OpTypeInt 32 1
194           %8 = OpTypeVector %6 2
195          %41 = OpConstantComposite %8 %40 %40
196           %9 = OpTypePointer Function %6
197          %10 = OpTypePointer Private %6
198          %20 = OpTypePointer Uniform %6
199          %11 = OpTypePointer Function %7
200          %12 = OpTypePointer Private %7
201          %13 = OpTypePointer Private %8
202          %14 = OpVariable %10 Private
203          %15 = OpVariable %20 Uniform
204          %16 = OpConstant %7 1
205          %17 = OpTypePointer Private %10
206          %18 = OpTypeBool
207          %19 = OpTypePointer Private %18
208          %21 = OpConstantTrue %18
209          %22 = OpConstantFalse %18
210         %100 = OpVariable %12 Private %16
211         %101 = OpVariable %10 Private %40
212         %102 = OpVariable %13 Private %41
213         %103 = OpVariable %12 Private %16
214         %104 = OpVariable %19 Private %21
215         %105 = OpVariable %19 Private %22
216           %4 = OpFunction %2 None %3
217           %5 = OpLabel
218                OpReturn
219                OpFunctionEnd
220   )";
221   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
222 }
223 
TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement)224 TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) {
225   // This checks that when global variables are added to a SPIR-V 1.4+ module,
226   // they are also added to entry points of that module.
227   std::string shader = R"(
228                OpCapability Shader
229           %1 = OpExtInstImport "GLSL.std.450"
230                OpMemoryModel Logical GLSL450
231                OpEntryPoint Fragment %4 "m1"
232                OpEntryPoint Vertex %5 "m2"
233                OpExecutionMode %4 OriginUpperLeft
234                OpSource ESSL 310
235           %2 = OpTypeVoid
236           %3 = OpTypeFunction %2
237           %6 = OpTypeFloat 32
238           %7 = OpTypeInt 32 1
239           %8 = OpTypeVector %6 2
240           %9 = OpTypePointer Function %6
241          %10 = OpTypePointer Private %6
242          %11 = OpTypePointer Function %7
243          %12 = OpTypePointer Private %7
244          %13 = OpTypePointer Private %8
245          %14 = OpVariable %10 Private
246          %16 = OpConstant %7 1
247          %17 = OpTypePointer Private %10
248          %18 = OpTypeBool
249          %19 = OpTypePointer Private %18
250          %21 = OpConstantTrue %18
251           %4 = OpFunction %2 None %3
252          %30 = OpLabel
253                OpReturn
254                OpFunctionEnd
255           %5 = OpFunction %2 None %3
256          %31 = OpLabel
257                OpReturn
258                OpFunctionEnd
259   )";
260 
261   for (auto env : {SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5,
262                    SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_2}) {
263     const auto consumer = nullptr;
264     const auto context =
265         BuildModule(env, consumer, shader, kFuzzAssembleOption);
266     spvtools::ValidatorOptions validator_options;
267     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
268         context.get(), validator_options, kConsoleMessageConsumer));
269     TransformationContext transformation_context(
270         MakeUnique<FactManager>(context.get()), validator_options);
271     TransformationAddGlobalVariable transformations[] = {
272         // %100 = OpVariable %12 Private
273         TransformationAddGlobalVariable(100, 12, spv::StorageClass::Private, 16,
274                                         true),
275 
276         // %101 = OpVariable %12 Private %16
277         TransformationAddGlobalVariable(101, 12, spv::StorageClass::Private, 16,
278                                         false),
279 
280         // %102 = OpVariable %19 Private %21
281         TransformationAddGlobalVariable(102, 19, spv::StorageClass::Private, 21,
282                                         true)};
283 
284     for (auto& transformation : transformations) {
285       ASSERT_TRUE(
286           transformation.IsApplicable(context.get(), transformation_context));
287       ApplyAndCheckFreshIds(transformation, context.get(),
288                             &transformation_context);
289     }
290     ASSERT_TRUE(
291         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
292     ASSERT_TRUE(
293         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
294     ASSERT_FALSE(
295         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
296     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
297         context.get(), validator_options, kConsoleMessageConsumer));
298 
299     std::string after_transformation_enlarged_interface = R"(
300                  OpCapability Shader
301             %1 = OpExtInstImport "GLSL.std.450"
302                  OpMemoryModel Logical GLSL450
303                  OpEntryPoint Fragment %4 "m1" %100 %101 %102
304                  OpEntryPoint Vertex %5 "m2" %100 %101 %102
305                  OpExecutionMode %4 OriginUpperLeft
306                  OpSource ESSL 310
307             %2 = OpTypeVoid
308             %3 = OpTypeFunction %2
309             %6 = OpTypeFloat 32
310             %7 = OpTypeInt 32 1
311             %8 = OpTypeVector %6 2
312             %9 = OpTypePointer Function %6
313            %10 = OpTypePointer Private %6
314            %11 = OpTypePointer Function %7
315            %12 = OpTypePointer Private %7
316            %13 = OpTypePointer Private %8
317            %14 = OpVariable %10 Private
318            %16 = OpConstant %7 1
319            %17 = OpTypePointer Private %10
320            %18 = OpTypeBool
321            %19 = OpTypePointer Private %18
322            %21 = OpConstantTrue %18
323           %100 = OpVariable %12 Private %16
324           %101 = OpVariable %12 Private %16
325           %102 = OpVariable %19 Private %21
326             %4 = OpFunction %2 None %3
327            %30 = OpLabel
328                  OpReturn
329                  OpFunctionEnd
330             %5 = OpFunction %2 None %3
331            %31 = OpLabel
332                  OpReturn
333                  OpFunctionEnd
334     )";
335 
336     ASSERT_TRUE(
337         IsEqual(env, after_transformation_enlarged_interface, context.get()));
338   }
339 }
340 
TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceNoEnlargement)341 TEST(TransformationAddGlobalVariableTest,
342      TestEntryPointInterfaceNoEnlargement) {
343   // This checks that when global variables are added to a SPIR-V 1.3- module,
344   // they are not added to entry points of that module.
345   std::string shader = R"(
346                OpCapability Shader
347           %1 = OpExtInstImport "GLSL.std.450"
348                OpMemoryModel Logical GLSL450
349                OpEntryPoint Fragment %4 "m1"
350                OpEntryPoint Vertex %5 "m2"
351                OpExecutionMode %4 OriginUpperLeft
352                OpSource ESSL 310
353           %2 = OpTypeVoid
354           %3 = OpTypeFunction %2
355           %6 = OpTypeFloat 32
356           %7 = OpTypeInt 32 1
357           %8 = OpTypeVector %6 2
358           %9 = OpTypePointer Function %6
359          %10 = OpTypePointer Private %6
360          %11 = OpTypePointer Function %7
361          %12 = OpTypePointer Private %7
362          %13 = OpTypePointer Private %8
363          %14 = OpVariable %10 Private
364          %16 = OpConstant %7 1
365          %17 = OpTypePointer Private %10
366          %18 = OpTypeBool
367          %19 = OpTypePointer Private %18
368          %21 = OpConstantTrue %18
369           %4 = OpFunction %2 None %3
370          %30 = OpLabel
371                OpReturn
372                OpFunctionEnd
373           %5 = OpFunction %2 None %3
374          %31 = OpLabel
375                OpReturn
376                OpFunctionEnd
377   )";
378 
379   for (auto env :
380        {SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2,
381         SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1}) {
382     const auto consumer = nullptr;
383     const auto context =
384         BuildModule(env, consumer, shader, kFuzzAssembleOption);
385     spvtools::ValidatorOptions validator_options;
386     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
387         context.get(), validator_options, kConsoleMessageConsumer));
388     TransformationContext transformation_context(
389         MakeUnique<FactManager>(context.get()), validator_options);
390     TransformationAddGlobalVariable transformations[] = {
391         // %100 = OpVariable %12 Private
392         TransformationAddGlobalVariable(100, 12, spv::StorageClass::Private, 16,
393                                         true),
394 
395         // %101 = OpVariable %12 Private %16
396         TransformationAddGlobalVariable(101, 12, spv::StorageClass::Private, 16,
397                                         false),
398 
399         // %102 = OpVariable %19 Private %21
400         TransformationAddGlobalVariable(102, 19, spv::StorageClass::Private, 21,
401                                         true)};
402 
403     for (auto& transformation : transformations) {
404       ASSERT_TRUE(
405           transformation.IsApplicable(context.get(), transformation_context));
406       ApplyAndCheckFreshIds(transformation, context.get(),
407                             &transformation_context);
408     }
409     ASSERT_TRUE(
410         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
411     ASSERT_TRUE(
412         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
413     ASSERT_FALSE(
414         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
415     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
416         context.get(), validator_options, kConsoleMessageConsumer));
417 
418     std::string after_transformation_fixed_interface = R"(
419                  OpCapability Shader
420             %1 = OpExtInstImport "GLSL.std.450"
421                  OpMemoryModel Logical GLSL450
422                  OpEntryPoint Fragment %4 "m1"
423                  OpEntryPoint Vertex %5 "m2"
424                  OpExecutionMode %4 OriginUpperLeft
425                  OpSource ESSL 310
426             %2 = OpTypeVoid
427             %3 = OpTypeFunction %2
428             %6 = OpTypeFloat 32
429             %7 = OpTypeInt 32 1
430             %8 = OpTypeVector %6 2
431             %9 = OpTypePointer Function %6
432            %10 = OpTypePointer Private %6
433            %11 = OpTypePointer Function %7
434            %12 = OpTypePointer Private %7
435            %13 = OpTypePointer Private %8
436            %14 = OpVariable %10 Private
437            %16 = OpConstant %7 1
438            %17 = OpTypePointer Private %10
439            %18 = OpTypeBool
440            %19 = OpTypePointer Private %18
441            %21 = OpConstantTrue %18
442           %100 = OpVariable %12 Private %16
443           %101 = OpVariable %12 Private %16
444           %102 = OpVariable %19 Private %21
445             %4 = OpFunction %2 None %3
446            %30 = OpLabel
447                  OpReturn
448                  OpFunctionEnd
449             %5 = OpFunction %2 None %3
450            %31 = OpLabel
451                  OpReturn
452                  OpFunctionEnd
453     )";
454 
455     ASSERT_TRUE(
456         IsEqual(env, after_transformation_fixed_interface, context.get()));
457   }
458 }
459 
TEST(TransformationAddGlobalVariableTest, TestAddingWorkgroupGlobals)460 TEST(TransformationAddGlobalVariableTest, TestAddingWorkgroupGlobals) {
461   // This checks that workgroup globals can be added to a compute shader.
462   std::string shader = R"(
463                OpCapability Shader
464           %1 = OpExtInstImport "GLSL.std.450"
465                OpMemoryModel Logical GLSL450
466                OpEntryPoint GLCompute %4 "main"
467                OpExecutionMode %4 LocalSize 1 1 1
468                OpSource ESSL 310
469           %2 = OpTypeVoid
470           %3 = OpTypeFunction %2
471           %6 = OpTypeInt 32 1
472           %7 = OpTypePointer Workgroup %6
473          %50 = OpConstant %6 2
474           %4 = OpFunction %2 None %3
475           %5 = OpLabel
476                OpReturn
477                OpFunctionEnd
478   )";
479 
480   const auto env = SPV_ENV_UNIVERSAL_1_4;
481   const auto consumer = nullptr;
482   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
483   spvtools::ValidatorOptions validator_options;
484   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
485                                                kConsoleMessageConsumer));
486   TransformationContext transformation_context(
487       MakeUnique<FactManager>(context.get()), validator_options);
488 #ifndef NDEBUG
489   ASSERT_DEATH(
490       TransformationAddGlobalVariable(8, 7, spv::StorageClass::Workgroup, 50,
491                                       true)
492           .IsApplicable(context.get(), transformation_context),
493       "By construction this transformation should not have an.*initializer "
494       "when Workgroup storage class is used");
495 #endif
496 
497   TransformationAddGlobalVariable transformations[] = {
498       // %8 = OpVariable %7 Workgroup
499       TransformationAddGlobalVariable(8, 7, spv::StorageClass::Workgroup, 0,
500                                       true),
501 
502       // %10 = OpVariable %7 Workgroup
503       TransformationAddGlobalVariable(10, 7, spv::StorageClass::Workgroup, 0,
504                                       false)};
505 
506   for (auto& transformation : transformations) {
507     ASSERT_TRUE(
508         transformation.IsApplicable(context.get(), transformation_context));
509     ApplyAndCheckFreshIds(transformation, context.get(),
510                           &transformation_context);
511   }
512   ASSERT_TRUE(
513       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(8));
514   ASSERT_FALSE(
515       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(10));
516   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
517                                                kConsoleMessageConsumer));
518 
519   std::string after_transformation = R"(
520                OpCapability Shader
521           %1 = OpExtInstImport "GLSL.std.450"
522                OpMemoryModel Logical GLSL450
523                OpEntryPoint GLCompute %4 "main" %8 %10
524                OpExecutionMode %4 LocalSize 1 1 1
525                OpSource ESSL 310
526           %2 = OpTypeVoid
527           %3 = OpTypeFunction %2
528           %6 = OpTypeInt 32 1
529           %7 = OpTypePointer Workgroup %6
530          %50 = OpConstant %6 2
531           %8 = OpVariable %7 Workgroup
532          %10 = OpVariable %7 Workgroup
533           %4 = OpFunction %2 None %3
534           %5 = OpLabel
535                OpReturn
536                OpFunctionEnd
537   )";
538   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
539 }
540 
541 }  // namespace
542 }  // namespace fuzz
543 }  // namespace spvtools
544