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