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