1 // Copyright (c) 2020 Vasyl Teliman
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_replace_params_with_struct.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(TransformationReplaceParamsWithStructTest,BasicTest)25 TEST(TransformationReplaceParamsWithStructTest, 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 OpMemberDecorate %13 0 RelaxedPrecision
34 OpDecorate %16 RelaxedPrecision
35 %2 = OpTypeVoid
36 %6 = OpTypeInt 32 1
37 %3 = OpTypeFunction %2
38 %7 = OpTypePointer Private %6
39 %8 = OpTypeFloat 32
40 %9 = OpTypePointer Private %8
41 %10 = OpTypeVector %8 2
42 %11 = OpTypePointer Private %10
43 %12 = OpTypeBool
44 %51 = OpTypeFunction %2 %12
45 %64 = OpTypeStruct %6
46 %63 = OpTypeFunction %2 %64
47 %65 = OpTypeFunction %2 %6
48 %75 = OpTypeStruct %8
49 %76 = OpTypeFunction %2 %75
50 %77 = OpTypeFunction %2 %8
51 %40 = OpTypePointer Function %12
52 %13 = OpTypeStruct %6 %8
53 %45 = OpTypeStruct %6 %10 %13
54 %46 = OpTypeStruct %12
55 %47 = OpTypeStruct %8 %45 %46
56 %14 = OpTypePointer Private %13
57 %15 = OpTypeFunction %2 %6 %8 %10 %13 %40 %12
58 %22 = OpConstant %6 0
59 %23 = OpConstant %8 0
60 %26 = OpConstantComposite %10 %23 %23
61 %27 = OpConstantTrue %12
62 %28 = OpConstantComposite %13 %22 %23
63 %4 = OpFunction %2 None %3
64 %5 = OpLabel
65 %41 = OpVariable %40 Function %27
66 %33 = OpFunctionCall %2 %20 %22 %23 %26 %28 %41 %27
67 OpReturn
68 OpFunctionEnd
69
70 ; adjust type of the function in-place
71 %20 = OpFunction %2 None %15
72 %16 = OpFunctionParameter %6
73 %17 = OpFunctionParameter %8
74 %18 = OpFunctionParameter %10
75 %19 = OpFunctionParameter %13
76 %42 = OpFunctionParameter %40
77 %43 = OpFunctionParameter %12
78 %21 = OpLabel
79 OpReturn
80 OpFunctionEnd
81
82 ; create a new function type
83 %50 = OpFunction %2 None %51
84 %52 = OpFunctionParameter %12
85 %53 = OpLabel
86 OpReturn
87 OpFunctionEnd
88 %54 = OpFunction %2 None %51
89 %55 = OpFunctionParameter %12
90 %56 = OpLabel
91 OpReturn
92 OpFunctionEnd
93
94 ; reuse an existing function type
95 %57 = OpFunction %2 None %63
96 %58 = OpFunctionParameter %64
97 %59 = OpLabel
98 OpReturn
99 OpFunctionEnd
100 %60 = OpFunction %2 None %65
101 %61 = OpFunctionParameter %6
102 %62 = OpLabel
103 OpReturn
104 OpFunctionEnd
105 %66 = OpFunction %2 None %65
106 %67 = OpFunctionParameter %6
107 %68 = OpLabel
108 OpReturn
109 OpFunctionEnd
110
111 ; don't adjust the type of the function if it creates a duplicate
112 %69 = OpFunction %2 None %76
113 %70 = OpFunctionParameter %75
114 %71 = OpLabel
115 OpReturn
116 OpFunctionEnd
117 %72 = OpFunction %2 None %77
118 %73 = OpFunctionParameter %8
119 %74 = OpLabel
120 OpReturn
121 OpFunctionEnd
122 )";
123
124 const auto env = SPV_ENV_UNIVERSAL_1_3;
125 const auto consumer = nullptr;
126 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
127 spvtools::ValidatorOptions validator_options;
128 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
129 kConsoleMessageConsumer));
130 TransformationContext transformation_context(
131 MakeUnique<FactManager>(context.get()), validator_options);
132 // |parameter_id| is empty.
133 ASSERT_FALSE(
134 TransformationReplaceParamsWithStruct({}, 90, 91, {{33, 92}, {90, 93}})
135 .IsApplicable(context.get(), transformation_context));
136
137 // |parameter_id| has duplicates.
138 ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 16, 17}, 90, 91,
139 {{33, 92}, {90, 93}})
140 .IsApplicable(context.get(), transformation_context));
141
142 // |parameter_id| has invalid values.
143 ASSERT_FALSE(TransformationReplaceParamsWithStruct({21, 16, 17}, 90, 91,
144 {{33, 92}, {90, 93}})
145 .IsApplicable(context.get(), transformation_context));
146 ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 90, 17}, 90, 91,
147 {{33, 92}, {90, 93}})
148 .IsApplicable(context.get(), transformation_context));
149
150 // Parameter's belong to different functions.
151 ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17, 52}, 90, 91,
152 {{33, 92}, {90, 93}})
153 .IsApplicable(context.get(), transformation_context));
154
155 // Parameter has unsupported type.
156 ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17, 42, 43}, 90, 91,
157 {{33, 92}, {90, 93}})
158 .IsApplicable(context.get(), transformation_context));
159
160 // OpTypeStruct does not exist in the module.
161 ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 43}, 90, 91,
162 {{33, 92}, {90, 93}})
163 .IsApplicable(context.get(), transformation_context));
164
165 // |caller_id_to_fresh_composite_id| misses values.
166 ASSERT_FALSE(
167 TransformationReplaceParamsWithStruct({16, 17}, 90, 91, {{90, 93}})
168 .IsApplicable(context.get(), transformation_context));
169
170 // All fresh ids must be unique.
171 ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 90,
172 {{33, 92}, {90, 93}})
173 .IsApplicable(context.get(), transformation_context));
174 ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
175 {{33, 90}, {90, 93}})
176 .IsApplicable(context.get(), transformation_context));
177 ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
178 {{33, 92}, {90, 92}})
179 .IsApplicable(context.get(), transformation_context));
180
181 // All 'fresh' ids must be fresh.
182 ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
183 {{33, 33}, {90, 93}})
184 .IsApplicable(context.get(), transformation_context));
185 ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 33, 91,
186 {{33, 92}, {90, 93}})
187 .IsApplicable(context.get(), transformation_context));
188 ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 33,
189 {{33, 92}, {90, 93}})
190 .IsApplicable(context.get(), transformation_context));
191
192 {
193 TransformationReplaceParamsWithStruct transformation({16, 18, 19}, 90, 91,
194 {{33, 92}, {90, 93}});
195 ASSERT_TRUE(
196 transformation.IsApplicable(context.get(), transformation_context));
197 ApplyAndCheckFreshIds(transformation, context.get(),
198 &transformation_context);
199 }
200 {
201 TransformationReplaceParamsWithStruct transformation({43}, 93, 94,
202 {{33, 95}});
203 ASSERT_TRUE(
204 transformation.IsApplicable(context.get(), transformation_context));
205 ApplyAndCheckFreshIds(transformation, context.get(),
206 &transformation_context);
207 }
208 {
209 TransformationReplaceParamsWithStruct transformation({17, 91, 94}, 96, 97,
210 {{33, 98}});
211 ASSERT_TRUE(
212 transformation.IsApplicable(context.get(), transformation_context));
213 ApplyAndCheckFreshIds(transformation, context.get(),
214 &transformation_context);
215 }
216 {
217 TransformationReplaceParamsWithStruct transformation({55}, 99, 100, {{}});
218 ASSERT_TRUE(
219 transformation.IsApplicable(context.get(), transformation_context));
220 ApplyAndCheckFreshIds(transformation, context.get(),
221 &transformation_context);
222 }
223 {
224 TransformationReplaceParamsWithStruct transformation({61}, 101, 102, {{}});
225 ASSERT_TRUE(
226 transformation.IsApplicable(context.get(), transformation_context));
227 ApplyAndCheckFreshIds(transformation, context.get(),
228 &transformation_context);
229 }
230 {
231 TransformationReplaceParamsWithStruct transformation({73}, 103, 104, {{}});
232 ASSERT_TRUE(
233 transformation.IsApplicable(context.get(), transformation_context));
234 ApplyAndCheckFreshIds(transformation, context.get(),
235 &transformation_context);
236 }
237
238 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
239 kConsoleMessageConsumer));
240
241 std::string expected_shader = R"(
242 OpCapability Shader
243 %1 = OpExtInstImport "GLSL.std.450"
244 OpMemoryModel Logical GLSL450
245 OpEntryPoint Fragment %4 "main"
246 OpExecutionMode %4 OriginUpperLeft
247 OpSource ESSL 310
248 OpMemberDecorate %13 0 RelaxedPrecision
249 OpDecorate %16 RelaxedPrecision
250 %2 = OpTypeVoid
251 %6 = OpTypeInt 32 1
252 %3 = OpTypeFunction %2
253 %7 = OpTypePointer Private %6
254 %8 = OpTypeFloat 32
255 %9 = OpTypePointer Private %8
256 %10 = OpTypeVector %8 2
257 %11 = OpTypePointer Private %10
258 %12 = OpTypeBool
259 %51 = OpTypeFunction %2 %12
260 %64 = OpTypeStruct %6
261 %63 = OpTypeFunction %2 %64
262 %65 = OpTypeFunction %2 %6
263 %75 = OpTypeStruct %8
264 %76 = OpTypeFunction %2 %75
265 %40 = OpTypePointer Function %12
266 %13 = OpTypeStruct %6 %8
267 %45 = OpTypeStruct %6 %10 %13
268 %46 = OpTypeStruct %12
269 %47 = OpTypeStruct %8 %45 %46
270 %14 = OpTypePointer Private %13
271 %22 = OpConstant %6 0
272 %23 = OpConstant %8 0
273 %26 = OpConstantComposite %10 %23 %23
274 %27 = OpConstantTrue %12
275 %28 = OpConstantComposite %13 %22 %23
276 %15 = OpTypeFunction %2 %40 %47
277 %99 = OpTypeFunction %2 %46
278 %4 = OpFunction %2 None %3
279 %5 = OpLabel
280 %41 = OpVariable %40 Function %27
281 %92 = OpCompositeConstruct %45 %22 %26 %28
282 %95 = OpCompositeConstruct %46 %27
283 %98 = OpCompositeConstruct %47 %23 %92 %95
284 %33 = OpFunctionCall %2 %20 %41 %98
285 OpReturn
286 OpFunctionEnd
287 %20 = OpFunction %2 None %15
288 %42 = OpFunctionParameter %40
289 %97 = OpFunctionParameter %47
290 %21 = OpLabel
291 %94 = OpCompositeExtract %46 %97 2
292 %91 = OpCompositeExtract %45 %97 1
293 %17 = OpCompositeExtract %8 %97 0
294 %43 = OpCompositeExtract %12 %94 0
295 %19 = OpCompositeExtract %13 %91 2
296 %18 = OpCompositeExtract %10 %91 1
297 %16 = OpCompositeExtract %6 %91 0
298 OpReturn
299 OpFunctionEnd
300 %50 = OpFunction %2 None %51
301 %52 = OpFunctionParameter %12
302 %53 = OpLabel
303 OpReturn
304 OpFunctionEnd
305 %54 = OpFunction %2 None %99
306 %100 = OpFunctionParameter %46
307 %56 = OpLabel
308 %55 = OpCompositeExtract %12 %100 0
309 OpReturn
310 OpFunctionEnd
311 %57 = OpFunction %2 None %63
312 %58 = OpFunctionParameter %64
313 %59 = OpLabel
314 OpReturn
315 OpFunctionEnd
316 %60 = OpFunction %2 None %63
317 %102 = OpFunctionParameter %64
318 %62 = OpLabel
319 %61 = OpCompositeExtract %6 %102 0
320 OpReturn
321 OpFunctionEnd
322 %66 = OpFunction %2 None %65
323 %67 = OpFunctionParameter %6
324 %68 = OpLabel
325 OpReturn
326 OpFunctionEnd
327 %69 = OpFunction %2 None %76
328 %70 = OpFunctionParameter %75
329 %71 = OpLabel
330 OpReturn
331 OpFunctionEnd
332 %72 = OpFunction %2 None %76
333 %104 = OpFunctionParameter %75
334 %74 = OpLabel
335 %73 = OpCompositeExtract %8 %104 0
336 OpReturn
337 OpFunctionEnd
338 )";
339
340 ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
341 }
342
TEST(TransformationReplaceParamsWithStructTest,ParametersRemainValid)343 TEST(TransformationReplaceParamsWithStructTest, ParametersRemainValid) {
344 std::string shader = R"(
345 OpCapability Shader
346 %1 = OpExtInstImport "GLSL.std.450"
347 OpMemoryModel Logical GLSL450
348 OpEntryPoint Fragment %4 "main"
349 OpExecutionMode %4 OriginUpperLeft
350 OpSource ESSL 310
351 %2 = OpTypeVoid
352 %6 = OpTypeInt 32 1
353 %3 = OpTypeFunction %2
354 %8 = OpTypeFloat 32
355 %10 = OpTypeVector %8 2
356 %12 = OpTypeBool
357 %40 = OpTypePointer Function %12
358 %13 = OpTypeStruct %6 %8
359 %45 = OpTypeStruct %6 %8 %13
360 %47 = OpTypeStruct %45 %12 %10
361 %15 = OpTypeFunction %2 %6 %8 %10 %13 %40 %12
362 %4 = OpFunction %2 None %3
363 %5 = OpLabel
364 OpReturn
365 OpFunctionEnd
366 %20 = OpFunction %2 None %15
367 %16 = OpFunctionParameter %6
368 %17 = OpFunctionParameter %8
369 %18 = OpFunctionParameter %10
370 %19 = OpFunctionParameter %13
371 %42 = OpFunctionParameter %40
372 %43 = OpFunctionParameter %12
373 %21 = OpLabel
374 OpReturn
375 OpFunctionEnd
376 )";
377
378 const auto env = SPV_ENV_UNIVERSAL_1_3;
379 const auto consumer = nullptr;
380 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
381 spvtools::ValidatorOptions validator_options;
382 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
383 kConsoleMessageConsumer));
384 TransformationContext transformation_context(
385 MakeUnique<FactManager>(context.get()), validator_options);
386 {
387 // Try to replace parameters in "increasing" order of their declaration.
388 TransformationReplaceParamsWithStruct transformation({16, 17, 19}, 70, 71,
389 {{}});
390 ASSERT_TRUE(
391 transformation.IsApplicable(context.get(), transformation_context));
392 ApplyAndCheckFreshIds(transformation, context.get(),
393 &transformation_context);
394 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
395 context.get(), validator_options, kConsoleMessageConsumer));
396 }
397
398 std::string after_transformation = R"(
399 OpCapability Shader
400 %1 = OpExtInstImport "GLSL.std.450"
401 OpMemoryModel Logical GLSL450
402 OpEntryPoint Fragment %4 "main"
403 OpExecutionMode %4 OriginUpperLeft
404 OpSource ESSL 310
405 %2 = OpTypeVoid
406 %6 = OpTypeInt 32 1
407 %3 = OpTypeFunction %2
408 %8 = OpTypeFloat 32
409 %10 = OpTypeVector %8 2
410 %12 = OpTypeBool
411 %40 = OpTypePointer Function %12
412 %13 = OpTypeStruct %6 %8
413 %45 = OpTypeStruct %6 %8 %13
414 %47 = OpTypeStruct %45 %12 %10
415 %15 = OpTypeFunction %2 %10 %40 %12 %45
416 %4 = OpFunction %2 None %3
417 %5 = OpLabel
418 OpReturn
419 OpFunctionEnd
420 %20 = OpFunction %2 None %15
421 %18 = OpFunctionParameter %10
422 %42 = OpFunctionParameter %40
423 %43 = OpFunctionParameter %12
424 %71 = OpFunctionParameter %45
425 %21 = OpLabel
426 %19 = OpCompositeExtract %13 %71 2
427 %17 = OpCompositeExtract %8 %71 1
428 %16 = OpCompositeExtract %6 %71 0
429 OpReturn
430 OpFunctionEnd
431 )";
432
433 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
434
435 {
436 // Try to replace parameters in "decreasing" order of their declaration.
437 TransformationReplaceParamsWithStruct transformation({71, 43, 18}, 72, 73,
438 {{}});
439 ASSERT_TRUE(
440 transformation.IsApplicable(context.get(), transformation_context));
441 ApplyAndCheckFreshIds(transformation, context.get(),
442 &transformation_context);
443 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
444 context.get(), validator_options, kConsoleMessageConsumer));
445 }
446
447 after_transformation = R"(
448 OpCapability Shader
449 %1 = OpExtInstImport "GLSL.std.450"
450 OpMemoryModel Logical GLSL450
451 OpEntryPoint Fragment %4 "main"
452 OpExecutionMode %4 OriginUpperLeft
453 OpSource ESSL 310
454 %2 = OpTypeVoid
455 %6 = OpTypeInt 32 1
456 %3 = OpTypeFunction %2
457 %8 = OpTypeFloat 32
458 %10 = OpTypeVector %8 2
459 %12 = OpTypeBool
460 %40 = OpTypePointer Function %12
461 %13 = OpTypeStruct %6 %8
462 %45 = OpTypeStruct %6 %8 %13
463 %47 = OpTypeStruct %45 %12 %10
464 %15 = OpTypeFunction %2 %40 %47
465 %4 = OpFunction %2 None %3
466 %5 = OpLabel
467 OpReturn
468 OpFunctionEnd
469 %20 = OpFunction %2 None %15
470 %42 = OpFunctionParameter %40
471 %73 = OpFunctionParameter %47
472 %21 = OpLabel
473 %18 = OpCompositeExtract %10 %73 2
474 %43 = OpCompositeExtract %12 %73 1
475 %71 = OpCompositeExtract %45 %73 0
476 %19 = OpCompositeExtract %13 %71 2
477 %17 = OpCompositeExtract %8 %71 1
478 %16 = OpCompositeExtract %6 %71 0
479 OpReturn
480 OpFunctionEnd
481 )";
482
483 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
484 }
485
TEST(TransformationReplaceParamsWithStructTest,IsomorphicStructs)486 TEST(TransformationReplaceParamsWithStructTest, IsomorphicStructs) {
487 std::string shader = R"(
488 OpCapability Shader
489 %1 = OpExtInstImport "GLSL.std.450"
490 OpMemoryModel Logical GLSL450
491 OpEntryPoint Fragment %16 "main"
492 OpExecutionMode %16 OriginUpperLeft
493 OpSource ESSL 310
494 %2 = OpTypeVoid
495 %6 = OpTypeInt 32 1
496 %7 = OpTypeStruct %6
497 %8 = OpTypeStruct %6
498 %9 = OpTypeStruct %8
499 %10 = OpTypeFunction %2 %7
500 %15 = OpTypeFunction %2
501 %16 = OpFunction %2 None %15
502 %17 = OpLabel
503 OpReturn
504 OpFunctionEnd
505 %11 = OpFunction %2 None %10
506 %12 = OpFunctionParameter %7
507 %13 = OpLabel
508 OpReturn
509 OpFunctionEnd
510 )";
511
512 const auto env = SPV_ENV_UNIVERSAL_1_3;
513 const auto consumer = nullptr;
514 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
515 spvtools::ValidatorOptions validator_options;
516 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
517 kConsoleMessageConsumer));
518 TransformationContext transformation_context(
519 MakeUnique<FactManager>(context.get()), validator_options);
520
521 ASSERT_FALSE(TransformationReplaceParamsWithStruct({12}, 100, 101, {{}})
522 .IsApplicable(context.get(), transformation_context));
523 }
524
525 } // namespace
526 } // namespace fuzz
527 } // namespace spvtools
528