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