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(
70       TransformationAddGlobalVariable(4, 10, SpvStorageClassPrivate, 0, true)
71           .IsApplicable(context.get(), transformation_context));
72   // %1 is not a type
73   ASSERT_FALSE(
74       TransformationAddGlobalVariable(100, 1, SpvStorageClassPrivate, 0, false)
75           .IsApplicable(context.get(), transformation_context));
76 
77   // %7 is not a pointer type
78   ASSERT_FALSE(
79       TransformationAddGlobalVariable(100, 7, SpvStorageClassPrivate, 0, true)
80           .IsApplicable(context.get(), transformation_context));
81 
82   // %9 does not have Private storage class
83   ASSERT_FALSE(
84       TransformationAddGlobalVariable(100, 9, SpvStorageClassPrivate, 0, false)
85           .IsApplicable(context.get(), transformation_context));
86 
87   // %15 does not have Private storage class
88   ASSERT_FALSE(
89       TransformationAddGlobalVariable(100, 15, SpvStorageClassPrivate, 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(100, 10, SpvStorageClassPrivate,
94                                                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(
100       TransformationAddGlobalVariable(100, 10, SpvStorageClassPrivate, 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(100, 12, SpvStorageClassPrivate,
106                                                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(
112       TransformationAddGlobalVariable(104, 10, SpvStorageClassPrivate, 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(104, 17, SpvStorageClassPrivate,
118                                                14, false)
119                    .IsApplicable(context.get(), transformation_context));
120 
121   TransformationAddGlobalVariable transformations[] = {
122       // %100 = OpVariable %12 Private
123       TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16,
124                                       true),
125 
126       // %101 = OpVariable %10 Private
127       TransformationAddGlobalVariable(101, 10, SpvStorageClassPrivate, 40,
128                                       false),
129 
130       // %102 = OpVariable %13 Private
131       TransformationAddGlobalVariable(102, 13, SpvStorageClassPrivate, 41,
132                                       true),
133 
134       // %103 = OpVariable %12 Private %16
135       TransformationAddGlobalVariable(103, 12, SpvStorageClassPrivate, 16,
136                                       false),
137 
138       // %104 = OpVariable %19 Private %21
139       TransformationAddGlobalVariable(104, 19, SpvStorageClassPrivate, 21,
140                                       true),
141 
142       // %105 = OpVariable %19 Private %22
143       TransformationAddGlobalVariable(105, 19, SpvStorageClassPrivate, 22,
144                                       false)};
145 
146   for (auto& transformation : transformations) {
147     ASSERT_TRUE(
148         transformation.IsApplicable(context.get(), transformation_context));
149     ApplyAndCheckFreshIds(transformation, context.get(),
150                           &transformation_context);
151   }
152   ASSERT_TRUE(
153       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
154   ASSERT_TRUE(
155       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
156   ASSERT_TRUE(
157       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(104));
158   ASSERT_FALSE(
159       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
160   ASSERT_FALSE(
161       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(103));
162   ASSERT_FALSE(
163       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(105));
164 
165   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
166                                                kConsoleMessageConsumer));
167 
168   std::string after_transformation = R"(
169                OpCapability Shader
170           %1 = OpExtInstImport "GLSL.std.450"
171                OpMemoryModel Logical GLSL450
172                OpEntryPoint Fragment %4 "main"
173                OpExecutionMode %4 OriginUpperLeft
174                OpSource ESSL 310
175           %2 = OpTypeVoid
176           %3 = OpTypeFunction %2
177           %6 = OpTypeFloat 32
178          %40 = OpConstant %6 0
179           %7 = OpTypeInt 32 1
180           %8 = OpTypeVector %6 2
181          %41 = OpConstantComposite %8 %40 %40
182           %9 = OpTypePointer Function %6
183          %10 = OpTypePointer Private %6
184          %20 = OpTypePointer Uniform %6
185          %11 = OpTypePointer Function %7
186          %12 = OpTypePointer Private %7
187          %13 = OpTypePointer Private %8
188          %14 = OpVariable %10 Private
189          %15 = OpVariable %20 Uniform
190          %16 = OpConstant %7 1
191          %17 = OpTypePointer Private %10
192          %18 = OpTypeBool
193          %19 = OpTypePointer Private %18
194          %21 = OpConstantTrue %18
195          %22 = OpConstantFalse %18
196         %100 = OpVariable %12 Private %16
197         %101 = OpVariable %10 Private %40
198         %102 = OpVariable %13 Private %41
199         %103 = OpVariable %12 Private %16
200         %104 = OpVariable %19 Private %21
201         %105 = OpVariable %19 Private %22
202           %4 = OpFunction %2 None %3
203           %5 = OpLabel
204                OpReturn
205                OpFunctionEnd
206   )";
207   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
208 }
209 
TEST(TransformationAddGlobalVariableTest,TestEntryPointInterfaceEnlargement)210 TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) {
211   // This checks that when global variables are added to a SPIR-V 1.4+ module,
212   // they are also added to entry points of that module.
213   std::string shader = R"(
214                OpCapability Shader
215           %1 = OpExtInstImport "GLSL.std.450"
216                OpMemoryModel Logical GLSL450
217                OpEntryPoint Fragment %4 "m1"
218                OpEntryPoint Vertex %5 "m2"
219                OpExecutionMode %4 OriginUpperLeft
220                OpSource ESSL 310
221           %2 = OpTypeVoid
222           %3 = OpTypeFunction %2
223           %6 = OpTypeFloat 32
224           %7 = OpTypeInt 32 1
225           %8 = OpTypeVector %6 2
226           %9 = OpTypePointer Function %6
227          %10 = OpTypePointer Private %6
228          %20 = OpTypePointer Uniform %6
229          %11 = OpTypePointer Function %7
230          %12 = OpTypePointer Private %7
231          %13 = OpTypePointer Private %8
232          %14 = OpVariable %10 Private
233          %15 = OpVariable %20 Uniform
234          %16 = OpConstant %7 1
235          %17 = OpTypePointer Private %10
236          %18 = OpTypeBool
237          %19 = OpTypePointer Private %18
238          %21 = OpConstantTrue %18
239           %4 = OpFunction %2 None %3
240          %30 = OpLabel
241                OpReturn
242                OpFunctionEnd
243           %5 = OpFunction %2 None %3
244          %31 = OpLabel
245                OpReturn
246                OpFunctionEnd
247   )";
248 
249   const auto env = SPV_ENV_UNIVERSAL_1_4;
250   const auto consumer = nullptr;
251   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
252   spvtools::ValidatorOptions validator_options;
253   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
254                                                kConsoleMessageConsumer));
255   TransformationContext transformation_context(
256       MakeUnique<FactManager>(context.get()), validator_options);
257   TransformationAddGlobalVariable transformations[] = {
258       // %100 = OpVariable %12 Private
259       TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16,
260                                       true),
261 
262       // %101 = OpVariable %12 Private %16
263       TransformationAddGlobalVariable(101, 12, SpvStorageClassPrivate, 16,
264                                       false),
265 
266       // %102 = OpVariable %19 Private %21
267       TransformationAddGlobalVariable(102, 19, SpvStorageClassPrivate, 21,
268                                       true)};
269 
270   for (auto& transformation : transformations) {
271     ASSERT_TRUE(
272         transformation.IsApplicable(context.get(), transformation_context));
273     ApplyAndCheckFreshIds(transformation, context.get(),
274                           &transformation_context);
275   }
276   ASSERT_TRUE(
277       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
278   ASSERT_TRUE(
279       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
280   ASSERT_FALSE(
281       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
282   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
283                                                kConsoleMessageConsumer));
284 
285   std::string after_transformation = R"(
286                OpCapability Shader
287           %1 = OpExtInstImport "GLSL.std.450"
288                OpMemoryModel Logical GLSL450
289                OpEntryPoint Fragment %4 "m1" %100 %101 %102
290                OpEntryPoint Vertex %5 "m2" %100 %101 %102
291                OpExecutionMode %4 OriginUpperLeft
292                OpSource ESSL 310
293           %2 = OpTypeVoid
294           %3 = OpTypeFunction %2
295           %6 = OpTypeFloat 32
296           %7 = OpTypeInt 32 1
297           %8 = OpTypeVector %6 2
298           %9 = OpTypePointer Function %6
299          %10 = OpTypePointer Private %6
300          %20 = OpTypePointer Uniform %6
301          %11 = OpTypePointer Function %7
302          %12 = OpTypePointer Private %7
303          %13 = OpTypePointer Private %8
304          %14 = OpVariable %10 Private
305          %15 = OpVariable %20 Uniform
306          %16 = OpConstant %7 1
307          %17 = OpTypePointer Private %10
308          %18 = OpTypeBool
309          %19 = OpTypePointer Private %18
310          %21 = OpConstantTrue %18
311         %100 = OpVariable %12 Private %16
312         %101 = OpVariable %12 Private %16
313         %102 = OpVariable %19 Private %21
314           %4 = OpFunction %2 None %3
315          %30 = OpLabel
316                OpReturn
317                OpFunctionEnd
318           %5 = OpFunction %2 None %3
319          %31 = OpLabel
320                OpReturn
321                OpFunctionEnd
322   )";
323   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
324 }
325 
TEST(TransformationAddGlobalVariableTest,TestAddingWorkgroupGlobals)326 TEST(TransformationAddGlobalVariableTest, TestAddingWorkgroupGlobals) {
327   // This checks that workgroup globals can be added to a compute shader.
328   std::string shader = R"(
329                OpCapability Shader
330           %1 = OpExtInstImport "GLSL.std.450"
331                OpMemoryModel Logical GLSL450
332                OpEntryPoint GLCompute %4 "main"
333                OpExecutionMode %4 LocalSize 1 1 1
334                OpSource ESSL 310
335           %2 = OpTypeVoid
336           %3 = OpTypeFunction %2
337           %6 = OpTypeInt 32 1
338           %7 = OpTypePointer Workgroup %6
339          %50 = OpConstant %6 2
340           %4 = OpFunction %2 None %3
341           %5 = OpLabel
342                OpReturn
343                OpFunctionEnd
344   )";
345 
346   const auto env = SPV_ENV_UNIVERSAL_1_4;
347   const auto consumer = nullptr;
348   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
349   spvtools::ValidatorOptions validator_options;
350   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
351                                                kConsoleMessageConsumer));
352   TransformationContext transformation_context(
353       MakeUnique<FactManager>(context.get()), validator_options);
354 #ifndef NDEBUG
355   ASSERT_DEATH(
356       TransformationAddGlobalVariable(8, 7, SpvStorageClassWorkgroup, 50, true)
357           .IsApplicable(context.get(), transformation_context),
358       "By construction this transformation should not have an.*initializer "
359       "when Workgroup storage class is used");
360 #endif
361 
362   TransformationAddGlobalVariable transformations[] = {
363       // %8 = OpVariable %7 Workgroup
364       TransformationAddGlobalVariable(8, 7, SpvStorageClassWorkgroup, 0, true),
365 
366       // %10 = OpVariable %7 Workgroup
367       TransformationAddGlobalVariable(10, 7, SpvStorageClassWorkgroup, 0,
368                                       false)};
369 
370   for (auto& transformation : transformations) {
371     ASSERT_TRUE(
372         transformation.IsApplicable(context.get(), transformation_context));
373     ApplyAndCheckFreshIds(transformation, context.get(),
374                           &transformation_context);
375   }
376   ASSERT_TRUE(
377       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(8));
378   ASSERT_FALSE(
379       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(10));
380   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
381                                                kConsoleMessageConsumer));
382 
383   std::string after_transformation = R"(
384                OpCapability Shader
385           %1 = OpExtInstImport "GLSL.std.450"
386                OpMemoryModel Logical GLSL450
387                OpEntryPoint GLCompute %4 "main" %8 %10
388                OpExecutionMode %4 LocalSize 1 1 1
389                OpSource ESSL 310
390           %2 = OpTypeVoid
391           %3 = OpTypeFunction %2
392           %6 = OpTypeInt 32 1
393           %7 = OpTypePointer Workgroup %6
394          %50 = OpConstant %6 2
395           %8 = OpVariable %7 Workgroup
396          %10 = OpVariable %7 Workgroup
397           %4 = OpFunction %2 None %3
398           %5 = OpLabel
399                OpReturn
400                OpFunctionEnd
401   )";
402   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
403 }
404 
405 }  // namespace
406 }  // namespace fuzz
407 }  // namespace spvtools
408