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