1 // Copyright (c) 2020 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_composite_insert.h"
16
17 #include "gtest/gtest.h"
18 #include "source/fuzz/data_descriptor.h"
19 #include "source/fuzz/fuzzer_util.h"
20 #include "source/fuzz/instruction_descriptor.h"
21 #include "test/fuzz/fuzz_test_util.h"
22
23 namespace spvtools {
24 namespace fuzz {
25 namespace {
26
TEST(TransformationCompositeInsertTest,NotApplicableScenarios)27 TEST(TransformationCompositeInsertTest, NotApplicableScenarios) {
28 // This test handles cases where IsApplicable() returns false.
29
30 std::string shader = R"(
31 OpCapability Shader
32 %1 = OpExtInstImport "GLSL.std.450"
33 OpMemoryModel Logical GLSL450
34 OpEntryPoint Fragment %4 "main"
35 OpExecutionMode %4 OriginUpperLeft
36 OpSource ESSL 310
37 OpName %4 "main"
38 OpName %8 "i1"
39 OpName %10 "i2"
40 OpName %12 "base"
41 OpMemberName %12 0 "a1"
42 OpMemberName %12 1 "a2"
43 OpName %14 "b"
44 OpName %18 "level_1"
45 OpMemberName %18 0 "b1"
46 OpMemberName %18 1 "b2"
47 OpName %20 "l1"
48 OpName %24 "level_2"
49 OpMemberName %24 0 "c1"
50 OpMemberName %24 1 "c2"
51 OpName %26 "l2"
52 %2 = OpTypeVoid
53 %3 = OpTypeFunction %2
54 %6 = OpTypeInt 32 1
55 %7 = OpTypePointer Function %6
56 %9 = OpConstant %6 1
57 %11 = OpConstant %6 2
58 %12 = OpTypeStruct %6 %6
59 %13 = OpTypePointer Function %12
60 %18 = OpTypeStruct %12 %12
61 %19 = OpTypePointer Function %18
62 %24 = OpTypeStruct %18 %18
63 %25 = OpTypePointer Function %24
64 %30 = OpTypeBool
65 %31 = OpConstantTrue %30
66 %4 = OpFunction %2 None %3
67 %5 = OpLabel
68 %8 = OpVariable %7 Function
69 %10 = OpVariable %7 Function
70 %14 = OpVariable %13 Function
71 %20 = OpVariable %19 Function
72 %26 = OpVariable %25 Function
73 OpStore %8 %9
74 OpStore %10 %11
75 %15 = OpLoad %6 %8
76 %16 = OpLoad %6 %10
77 %17 = OpCompositeConstruct %12 %15 %16
78 OpStore %14 %17
79 %21 = OpLoad %12 %14
80 %22 = OpLoad %12 %14
81 %23 = OpCompositeConstruct %18 %21 %22
82 OpStore %20 %23
83 %27 = OpLoad %18 %20
84 %28 = OpLoad %18 %20
85 %29 = OpCompositeConstruct %24 %27 %28
86 OpStore %26 %29
87 OpSelectionMerge %33 None
88 OpBranchConditional %31 %32 %33
89 %32 = OpLabel
90 OpBranch %33
91 %33 = OpLabel
92 OpReturn
93 OpFunctionEnd
94 )";
95
96 const auto env = SPV_ENV_UNIVERSAL_1_4;
97 const auto consumer = nullptr;
98 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
99 spvtools::ValidatorOptions validator_options;
100 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
101 kConsoleMessageConsumer));
102 TransformationContext transformation_context(
103 MakeUnique<FactManager>(context.get()), validator_options);
104 // Bad: |fresh_id| is not fresh.
105 auto transformation_bad_1 = TransformationCompositeInsert(
106 MakeInstructionDescriptor(29, SpvOpStore, 0), 20, 29, 11, {1, 0, 0});
107 ASSERT_FALSE(
108 transformation_bad_1.IsApplicable(context.get(), transformation_context));
109
110 // Bad: |composite_id| does not refer to a existing instruction.
111 auto transformation_bad_2 = TransformationCompositeInsert(
112 MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 40, 11, {1, 0, 0});
113 ASSERT_FALSE(
114 transformation_bad_2.IsApplicable(context.get(), transformation_context));
115
116 // Bad: |composite_id| does not refer to a composite value.
117 auto transformation_bad_3 = TransformationCompositeInsert(
118 MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 9, 11, {1, 0, 0});
119 ASSERT_FALSE(
120 transformation_bad_3.IsApplicable(context.get(), transformation_context));
121
122 // Bad: |object_id| does not refer to a defined instruction.
123 auto transformation_bad_4 = TransformationCompositeInsert(
124 MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 40, {1, 0, 0});
125 ASSERT_FALSE(
126 transformation_bad_4.IsApplicable(context.get(), transformation_context));
127
128 // Bad: |object_id| cannot refer to a pointer.
129 auto transformation_bad_5 = TransformationCompositeInsert(
130 MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 8, {1, 0, 0});
131 ASSERT_FALSE(
132 transformation_bad_5.IsApplicable(context.get(), transformation_context));
133
134 // Bad: |index| is not a correct index.
135 auto transformation_bad_6 = TransformationCompositeInsert(
136 MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 11, {2, 0, 0});
137 ASSERT_FALSE(
138 transformation_bad_6.IsApplicable(context.get(), transformation_context));
139
140 // Bad: Type id of the object to be inserted and the type id of the
141 // component at |index| are not the same.
142 auto transformation_bad_7 = TransformationCompositeInsert(
143 MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 11, {1, 0});
144 ASSERT_FALSE(
145 transformation_bad_7.IsApplicable(context.get(), transformation_context));
146
147 // Bad: |instruction_to_insert_before| does not refer to a defined
148 // instruction.
149 auto transformation_bad_8 = TransformationCompositeInsert(
150 MakeInstructionDescriptor(29, SpvOpIMul, 0), 50, 29, 11, {1, 0, 0});
151 ASSERT_FALSE(
152 transformation_bad_8.IsApplicable(context.get(), transformation_context));
153
154 // Bad: OpCompositeInsert cannot be inserted before OpBranchConditional with
155 // OpSelectionMerge above it.
156 auto transformation_bad_9 = TransformationCompositeInsert(
157 MakeInstructionDescriptor(29, SpvOpBranchConditional, 0), 50, 29, 11,
158 {1, 0, 0});
159 ASSERT_FALSE(
160 transformation_bad_9.IsApplicable(context.get(), transformation_context));
161
162 // Bad: |composite_id| does not have a type_id.
163 auto transformation_bad_10 = TransformationCompositeInsert(
164 MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 1, 11, {1, 0, 0});
165 ASSERT_FALSE(transformation_bad_10.IsApplicable(context.get(),
166 transformation_context));
167 }
168
TEST(TransformationCompositeInsertTest,EmptyCompositeScenarios)169 TEST(TransformationCompositeInsertTest, EmptyCompositeScenarios) {
170 // This test handles cases where either the composite is empty or the
171 // composite contains an empty composite.
172
173 std::string shader = R"(
174 OpCapability Shader
175 %1 = OpExtInstImport "GLSL.std.450"
176 OpMemoryModel Logical GLSL450
177 OpEntryPoint Fragment %4 "main"
178 OpExecutionMode %4 OriginUpperLeft
179 OpSource ESSL 310
180 OpName %4 "main"
181 OpName %8 "i1"
182 OpName %10 "i2"
183 OpName %12 "base"
184 OpMemberName %12 0 "a1"
185 OpMemberName %12 1 "a2"
186 OpName %14 "b"
187 %2 = OpTypeVoid
188 %60 = OpTypeStruct
189 %3 = OpTypeFunction %2
190 %6 = OpTypeInt 32 1
191 %7 = OpTypePointer Function %6
192 %9 = OpConstant %6 1
193 %11 = OpConstant %6 2
194 %61 = OpConstantComposite %60
195 %62 = OpConstantComposite %60
196 %12 = OpTypeStruct %6 %6
197 %63 = OpTypeStruct %6 %60
198 %13 = OpTypePointer Function %12
199 %4 = OpFunction %2 None %3
200 %5 = OpLabel
201 %8 = OpVariable %7 Function
202 %10 = OpVariable %7 Function
203 %14 = OpVariable %13 Function
204 OpStore %8 %9
205 OpStore %10 %11
206 %15 = OpLoad %6 %8
207 %16 = OpLoad %6 %10
208 %17 = OpCompositeConstruct %12 %15 %16
209 %64 = OpCompositeConstruct %63 %15 %61
210 OpStore %14 %17
211 OpReturn
212 OpFunctionEnd
213 )";
214
215 const auto env = SPV_ENV_UNIVERSAL_1_4;
216 const auto consumer = nullptr;
217 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
218 spvtools::ValidatorOptions validator_options;
219 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
220 kConsoleMessageConsumer));
221 TransformationContext transformation_context(
222 MakeUnique<FactManager>(context.get()), validator_options);
223 // Bad: The composite with |composite_id| cannot be empty.
224 auto transformation_bad_1 = TransformationCompositeInsert(
225 MakeInstructionDescriptor(64, SpvOpStore, 0), 50, 61, 62, {1});
226 ASSERT_FALSE(
227 transformation_bad_1.IsApplicable(context.get(), transformation_context));
228
229 // Good: It is possible to insert into a composite an element which is an
230 // empty composite.
231 auto transformation_good_1 = TransformationCompositeInsert(
232 MakeInstructionDescriptor(64, SpvOpStore, 0), 50, 64, 62, {1});
233 ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
234 transformation_context));
235 ApplyAndCheckFreshIds(transformation_good_1, context.get(),
236 &transformation_context);
237 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
238 kConsoleMessageConsumer));
239
240 std::string after_transformations = R"(
241 OpCapability Shader
242 %1 = OpExtInstImport "GLSL.std.450"
243 OpMemoryModel Logical GLSL450
244 OpEntryPoint Fragment %4 "main"
245 OpExecutionMode %4 OriginUpperLeft
246 OpSource ESSL 310
247 OpName %4 "main"
248 OpName %8 "i1"
249 OpName %10 "i2"
250 OpName %12 "base"
251 OpMemberName %12 0 "a1"
252 OpMemberName %12 1 "a2"
253 OpName %14 "b"
254 %2 = OpTypeVoid
255 %60 = OpTypeStruct
256 %3 = OpTypeFunction %2
257 %6 = OpTypeInt 32 1
258 %7 = OpTypePointer Function %6
259 %9 = OpConstant %6 1
260 %11 = OpConstant %6 2
261 %61 = OpConstantComposite %60
262 %62 = OpConstantComposite %60
263 %12 = OpTypeStruct %6 %6
264 %63 = OpTypeStruct %6 %60
265 %13 = OpTypePointer Function %12
266 %4 = OpFunction %2 None %3
267 %5 = OpLabel
268 %8 = OpVariable %7 Function
269 %10 = OpVariable %7 Function
270 %14 = OpVariable %13 Function
271 OpStore %8 %9
272 OpStore %10 %11
273 %15 = OpLoad %6 %8
274 %16 = OpLoad %6 %10
275 %17 = OpCompositeConstruct %12 %15 %16
276 %64 = OpCompositeConstruct %63 %15 %61
277 %50 = OpCompositeInsert %63 %62 %64 1
278 OpStore %14 %17
279 OpReturn
280 OpFunctionEnd
281 )";
282 ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
283 }
284
TEST(TransformationCompositeInsertTest,IrrelevantCompositeNoSynonyms)285 TEST(TransformationCompositeInsertTest, IrrelevantCompositeNoSynonyms) {
286 // This test handles cases where either |composite| is irrelevant.
287 // The transformation shouldn't create any synonyms.
288 // The member composite has a different number of elements than the parent
289 // composite.
290
291 std::string shader = R"(
292 OpCapability Shader
293 %1 = OpExtInstImport "GLSL.std.450"
294 OpMemoryModel Logical GLSL450
295 OpEntryPoint Fragment %4 "main"
296 OpExecutionMode %4 OriginUpperLeft
297 OpSource ESSL 310
298 OpName %4 "main"
299 OpName %8 "i1"
300 OpName %10 "i2"
301 OpName %12 "base"
302 OpMemberName %12 0 "a1"
303 OpMemberName %12 1 "a2"
304 OpName %14 "b"
305 OpName %18 "level_1"
306 OpMemberName %18 0 "b1"
307 OpMemberName %18 1 "b2"
308 OpMemberName %18 2 "b3"
309 OpName %20 "l1"
310 OpName %25 "level_2"
311 OpMemberName %25 0 "c1"
312 OpMemberName %25 1 "c2"
313 OpName %27 "l2"
314 %2 = OpTypeVoid
315 %3 = OpTypeFunction %2
316 %6 = OpTypeInt 32 1
317 %7 = OpTypePointer Function %6
318 %9 = OpConstant %6 1
319 %11 = OpConstant %6 2
320 %12 = OpTypeStruct %6 %6
321 %13 = OpTypePointer Function %12
322 %18 = OpTypeStruct %12 %12 %12
323 %19 = OpTypePointer Function %18
324 %25 = OpTypeStruct %18 %18
325 %26 = OpTypePointer Function %25
326 %31 = OpTypeBool
327 %32 = OpConstantTrue %31
328 %4 = OpFunction %2 None %3
329 %5 = OpLabel
330 %8 = OpVariable %7 Function
331 %10 = OpVariable %7 Function
332 %14 = OpVariable %13 Function
333 %20 = OpVariable %19 Function
334 %27 = OpVariable %26 Function
335 OpStore %8 %9
336 OpStore %10 %11
337 %15 = OpLoad %6 %8
338 %16 = OpLoad %6 %10
339 %17 = OpCompositeConstruct %12 %15 %16
340 OpStore %14 %17
341 %21 = OpLoad %12 %14
342 %22 = OpLoad %12 %14
343 %23 = OpLoad %12 %14
344 %24 = OpCompositeConstruct %18 %21 %22 %23
345 OpStore %20 %24
346 %28 = OpLoad %18 %20
347 %29 = OpLoad %18 %20
348 %30 = OpCompositeConstruct %25 %28 %29
349 OpStore %27 %30
350 OpSelectionMerge %34 None
351 OpBranchConditional %32 %33 %34
352 %33 = OpLabel
353 OpBranch %34
354 %34 = OpLabel
355 OpReturn
356 OpFunctionEnd
357 )";
358
359 const auto env = SPV_ENV_UNIVERSAL_1_4;
360 const auto consumer = nullptr;
361 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
362 spvtools::ValidatorOptions validator_options;
363 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
364 kConsoleMessageConsumer));
365 TransformationContext transformation_context(
366 MakeUnique<FactManager>(context.get()), validator_options);
367 // Add fact that the composite is irrelevant.
368 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(30);
369
370 auto transformation_good_1 = TransformationCompositeInsert(
371 MakeInstructionDescriptor(30, SpvOpStore, 0), 50, 30, 11, {1, 0, 0});
372 ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
373 transformation_context));
374 ApplyAndCheckFreshIds(transformation_good_1, context.get(),
375 &transformation_context);
376 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
377 kConsoleMessageConsumer));
378
379 // No synonyms that involve the original object - %30 - should have been
380 // added.
381 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
382 MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0})));
383 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
384 MakeDataDescriptor(30, {1, 1}), MakeDataDescriptor(50, {1, 1})));
385 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
386 MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2})));
387 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
388 MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1})));
389 // We *should* have a synonym between %11 and the component of %50 into which
390 // it has been inserted.
391 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
392 MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {})));
393 }
394
TEST(TransformationCompositeInsertTest,IrrelevantObjectNoSynonyms)395 TEST(TransformationCompositeInsertTest, IrrelevantObjectNoSynonyms) {
396 std::string shader = R"(
397 OpCapability Shader
398 %1 = OpExtInstImport "GLSL.std.450"
399 OpMemoryModel Logical GLSL450
400 OpEntryPoint Fragment %4 "main"
401 OpExecutionMode %4 OriginUpperLeft
402 OpSource ESSL 310
403 OpName %4 "main"
404 OpName %8 "i1"
405 OpName %10 "i2"
406 OpName %12 "base"
407 OpMemberName %12 0 "a1"
408 OpMemberName %12 1 "a2"
409 OpName %14 "b"
410 OpName %18 "level_1"
411 OpMemberName %18 0 "b1"
412 OpMemberName %18 1 "b2"
413 OpMemberName %18 2 "b3"
414 OpName %20 "l1"
415 OpName %25 "level_2"
416 OpMemberName %25 0 "c1"
417 OpMemberName %25 1 "c2"
418 OpName %27 "l2"
419 %2 = OpTypeVoid
420 %3 = OpTypeFunction %2
421 %6 = OpTypeInt 32 1
422 %7 = OpTypePointer Function %6
423 %9 = OpConstant %6 1
424 %11 = OpConstant %6 2
425 %12 = OpTypeStruct %6 %6
426 %13 = OpTypePointer Function %12
427 %18 = OpTypeStruct %12 %12 %12
428 %19 = OpTypePointer Function %18
429 %25 = OpTypeStruct %18 %18
430 %26 = OpTypePointer Function %25
431 %31 = OpTypeBool
432 %32 = OpConstantTrue %31
433 %4 = OpFunction %2 None %3
434 %5 = OpLabel
435 %8 = OpVariable %7 Function
436 %10 = OpVariable %7 Function
437 %14 = OpVariable %13 Function
438 %20 = OpVariable %19 Function
439 %27 = OpVariable %26 Function
440 OpStore %8 %9
441 OpStore %10 %11
442 %15 = OpLoad %6 %8
443 %16 = OpLoad %6 %10
444 %17 = OpCompositeConstruct %12 %15 %16
445 OpStore %14 %17
446 %21 = OpLoad %12 %14
447 %22 = OpLoad %12 %14
448 %23 = OpLoad %12 %14
449 %24 = OpCompositeConstruct %18 %21 %22 %23
450 OpStore %20 %24
451 %28 = OpLoad %18 %20
452 %29 = OpLoad %18 %20
453 %30 = OpCompositeConstruct %25 %28 %29
454 OpStore %27 %30
455 OpSelectionMerge %34 None
456 OpBranchConditional %32 %33 %34
457 %33 = OpLabel
458 OpBranch %34
459 %34 = OpLabel
460 OpReturn
461 OpFunctionEnd
462 )";
463
464 const auto env = SPV_ENV_UNIVERSAL_1_4;
465 const auto consumer = nullptr;
466 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
467 spvtools::ValidatorOptions validator_options;
468 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
469 kConsoleMessageConsumer));
470 TransformationContext transformation_context(
471 MakeUnique<FactManager>(context.get()), validator_options);
472 // Add fact that the object is irrelevant.
473 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(11);
474
475 auto transformation_good_1 = TransformationCompositeInsert(
476 MakeInstructionDescriptor(30, SpvOpStore, 0), 50, 30, 11, {1, 0, 0});
477 ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
478 transformation_context));
479 ApplyAndCheckFreshIds(transformation_good_1, context.get(),
480 &transformation_context);
481 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
482 kConsoleMessageConsumer));
483
484 // Since %30 and %50 are not irrelevant, they should be synonymous at all
485 // indices unaffected by the insertion.
486 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
487 MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0})));
488 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
489 MakeDataDescriptor(30, {1, 1}), MakeDataDescriptor(50, {1, 1})));
490 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
491 MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2})));
492 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
493 MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1})));
494 // Since %11 is irrelevant it should not be synonymous with the component into
495 // which it has been inserted.
496 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
497 MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {})));
498 }
499
TEST(TransformationCompositeInsertTest,ApplicableCreatedSynonyms)500 TEST(TransformationCompositeInsertTest, ApplicableCreatedSynonyms) {
501 // This test handles cases where neither |composite| nor |object| is
502 // irrelevant. The transformation should create synonyms.
503 // The member composite has a different number of elements than the parent
504 // composite.
505
506 std::string shader = R"(
507 OpCapability Shader
508 %1 = OpExtInstImport "GLSL.std.450"
509 OpMemoryModel Logical GLSL450
510 OpEntryPoint Fragment %4 "main"
511 OpExecutionMode %4 OriginUpperLeft
512 OpSource ESSL 310
513 OpName %4 "main"
514 OpName %8 "i1"
515 OpName %10 "i2"
516 OpName %12 "base"
517 OpMemberName %12 0 "a1"
518 OpMemberName %12 1 "a2"
519 OpName %14 "b"
520 OpName %18 "level_1"
521 OpMemberName %18 0 "b1"
522 OpMemberName %18 1 "b2"
523 OpMemberName %18 2 "b3"
524 OpName %20 "l1"
525 OpName %25 "level_2"
526 OpMemberName %25 0 "c1"
527 OpMemberName %25 1 "c2"
528 OpName %27 "l2"
529 %2 = OpTypeVoid
530 %3 = OpTypeFunction %2
531 %6 = OpTypeInt 32 1
532 %7 = OpTypePointer Function %6
533 %9 = OpConstant %6 1
534 %11 = OpConstant %6 2
535 %12 = OpTypeStruct %6 %6
536 %13 = OpTypePointer Function %12
537 %18 = OpTypeStruct %12 %12 %12
538 %19 = OpTypePointer Function %18
539 %25 = OpTypeStruct %18 %18
540 %26 = OpTypePointer Function %25
541 %31 = OpTypeBool
542 %32 = OpConstantTrue %31
543 %4 = OpFunction %2 None %3
544 %5 = OpLabel
545 %8 = OpVariable %7 Function
546 %10 = OpVariable %7 Function
547 %14 = OpVariable %13 Function
548 %20 = OpVariable %19 Function
549 %27 = OpVariable %26 Function
550 OpStore %8 %9
551 OpStore %10 %11
552 %15 = OpLoad %6 %8
553 %16 = OpLoad %6 %10
554 %17 = OpCompositeConstruct %12 %15 %16
555 OpStore %14 %17
556 %21 = OpLoad %12 %14
557 %22 = OpLoad %12 %14
558 %23 = OpLoad %12 %14
559 %24 = OpCompositeConstruct %18 %21 %22 %23
560 OpStore %20 %24
561 %28 = OpLoad %18 %20
562 %29 = OpLoad %18 %20
563 %30 = OpCompositeConstruct %25 %28 %29
564 OpStore %27 %30
565 OpSelectionMerge %34 None
566 OpBranchConditional %32 %33 %34
567 %33 = OpLabel
568 OpBranch %34
569 %34 = OpLabel
570 OpReturn
571 OpFunctionEnd
572 )";
573
574 const auto env = SPV_ENV_UNIVERSAL_1_4;
575 const auto consumer = nullptr;
576 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
577 spvtools::ValidatorOptions validator_options;
578 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
579 kConsoleMessageConsumer));
580 TransformationContext transformation_context(
581 MakeUnique<FactManager>(context.get()), validator_options);
582 auto transformation_good_1 = TransformationCompositeInsert(
583 MakeInstructionDescriptor(30, SpvOpStore, 0), 50, 30, 11, {1, 0, 0});
584 ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
585 transformation_context));
586 ApplyAndCheckFreshIds(transformation_good_1, context.get(),
587 &transformation_context);
588 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
589 kConsoleMessageConsumer));
590
591 // These synonyms should have been added.
592 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
593 MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0})));
594 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
595 MakeDataDescriptor(30, {1, 1}), MakeDataDescriptor(50, {1, 1})));
596 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
597 MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2})));
598 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
599 MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1})));
600 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
601 MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {})));
602
603 // These synonyms should not have been added.
604 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
605 MakeDataDescriptor(30, {1}), MakeDataDescriptor(50, {1})));
606 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
607 MakeDataDescriptor(30, {1, 0}), MakeDataDescriptor(50, {1, 0})));
608 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
609 MakeDataDescriptor(30, {1, 0, 0}), MakeDataDescriptor(50, {1, 0, 0})));
610
611 auto transformation_good_2 = TransformationCompositeInsert(
612 MakeInstructionDescriptor(50, SpvOpStore, 0), 51, 50, 11, {0, 1, 1});
613 ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(),
614 transformation_context));
615 ApplyAndCheckFreshIds(transformation_good_2, context.get(),
616 &transformation_context);
617 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
618 kConsoleMessageConsumer));
619
620 // These synonyms should have been added.
621 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
622 MakeDataDescriptor(50, {1}), MakeDataDescriptor(51, {1})));
623 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
624 MakeDataDescriptor(50, {0, 0}), MakeDataDescriptor(51, {0, 0})));
625 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
626 MakeDataDescriptor(50, {0, 2}), MakeDataDescriptor(51, {0, 2})));
627 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
628 MakeDataDescriptor(50, {0, 1, 0}), MakeDataDescriptor(51, {0, 1, 0})));
629 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
630 MakeDataDescriptor(51, {0, 1, 1}), MakeDataDescriptor(11, {})));
631
632 // These synonyms should not have been added.
633 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
634 MakeDataDescriptor(50, {0}), MakeDataDescriptor(51, {0})));
635 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
636 MakeDataDescriptor(50, {0, 1}), MakeDataDescriptor(51, {0, 1})));
637 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
638 MakeDataDescriptor(50, {0, 1, 1}), MakeDataDescriptor(51, {0, 1, 1})));
639
640 std::string after_transformations = R"(
641 OpCapability Shader
642 %1 = OpExtInstImport "GLSL.std.450"
643 OpMemoryModel Logical GLSL450
644 OpEntryPoint Fragment %4 "main"
645 OpExecutionMode %4 OriginUpperLeft
646 OpSource ESSL 310
647 OpName %4 "main"
648 OpName %8 "i1"
649 OpName %10 "i2"
650 OpName %12 "base"
651 OpMemberName %12 0 "a1"
652 OpMemberName %12 1 "a2"
653 OpName %14 "b"
654 OpName %18 "level_1"
655 OpMemberName %18 0 "b1"
656 OpMemberName %18 1 "b2"
657 OpMemberName %18 2 "b3"
658 OpName %20 "l1"
659 OpName %25 "level_2"
660 OpMemberName %25 0 "c1"
661 OpMemberName %25 1 "c2"
662 OpName %27 "l2"
663 %2 = OpTypeVoid
664 %3 = OpTypeFunction %2
665 %6 = OpTypeInt 32 1
666 %7 = OpTypePointer Function %6
667 %9 = OpConstant %6 1
668 %11 = OpConstant %6 2
669 %12 = OpTypeStruct %6 %6
670 %13 = OpTypePointer Function %12
671 %18 = OpTypeStruct %12 %12 %12
672 %19 = OpTypePointer Function %18
673 %25 = OpTypeStruct %18 %18
674 %26 = OpTypePointer Function %25
675 %31 = OpTypeBool
676 %32 = OpConstantTrue %31
677 %4 = OpFunction %2 None %3
678 %5 = OpLabel
679 %8 = OpVariable %7 Function
680 %10 = OpVariable %7 Function
681 %14 = OpVariable %13 Function
682 %20 = OpVariable %19 Function
683 %27 = OpVariable %26 Function
684 OpStore %8 %9
685 OpStore %10 %11
686 %15 = OpLoad %6 %8
687 %16 = OpLoad %6 %10
688 %17 = OpCompositeConstruct %12 %15 %16
689 OpStore %14 %17
690 %21 = OpLoad %12 %14
691 %22 = OpLoad %12 %14
692 %23 = OpLoad %12 %14
693 %24 = OpCompositeConstruct %18 %21 %22 %23
694 OpStore %20 %24
695 %28 = OpLoad %18 %20
696 %29 = OpLoad %18 %20
697 %30 = OpCompositeConstruct %25 %28 %29
698 %50 = OpCompositeInsert %25 %11 %30 1 0 0
699 %51 = OpCompositeInsert %25 %11 %50 0 1 1
700 OpStore %27 %30
701 OpSelectionMerge %34 None
702 OpBranchConditional %32 %33 %34
703 %33 = OpLabel
704 OpBranch %34
705 %34 = OpLabel
706 OpReturn
707 OpFunctionEnd
708 )";
709
710 ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
711 }
712
TEST(TransformationCompositeInsertTest,IdNotAvailableScenarios)713 TEST(TransformationCompositeInsertTest, IdNotAvailableScenarios) {
714 // This test handles cases where either the composite or the object is not
715 // available before the |instruction_to_insert_before|.
716
717 std::string shader = R"(
718 OpCapability Shader
719 %1 = OpExtInstImport "GLSL.std.450"
720 OpMemoryModel Logical GLSL450
721 OpEntryPoint Fragment %4 "main"
722 OpExecutionMode %4 OriginUpperLeft
723 OpSource ESSL 310
724 OpName %4 "main"
725 OpName %8 "i1"
726 OpName %10 "i2"
727 OpName %12 "base"
728 OpMemberName %12 0 "a1"
729 OpMemberName %12 1 "a2"
730 OpName %14 "b1"
731 OpName %18 "b2"
732 OpName %22 "lvl1"
733 OpMemberName %22 0 "b1"
734 OpMemberName %22 1 "b2"
735 OpName %24 "l1"
736 OpName %28 "i3"
737 %2 = OpTypeVoid
738 %3 = OpTypeFunction %2
739 %6 = OpTypeInt 32 1
740 %7 = OpTypePointer Function %6
741 %9 = OpConstant %6 1
742 %11 = OpConstant %6 2
743 %12 = OpTypeStruct %6 %6
744 %13 = OpTypePointer Function %12
745 %22 = OpTypeStruct %12 %12
746 %23 = OpTypePointer Function %22
747 %4 = OpFunction %2 None %3
748 %5 = OpLabel
749 %8 = OpVariable %7 Function
750 %10 = OpVariable %7 Function
751 %14 = OpVariable %13 Function
752 %18 = OpVariable %13 Function
753 %24 = OpVariable %23 Function
754 %28 = OpVariable %7 Function
755 OpStore %8 %9
756 OpStore %10 %11
757 %15 = OpLoad %6 %8
758 %16 = OpLoad %6 %10
759 %17 = OpCompositeConstruct %12 %15 %16
760 OpStore %14 %17
761 %19 = OpLoad %6 %10
762 %20 = OpLoad %6 %8
763 %21 = OpCompositeConstruct %12 %19 %20
764 OpStore %18 %21
765 %25 = OpLoad %12 %14
766 %26 = OpLoad %12 %18
767 %27 = OpCompositeConstruct %22 %25 %26
768 OpStore %24 %27
769 %29 = OpLoad %6 %8
770 %30 = OpLoad %6 %10
771 %31 = OpIMul %6 %29 %30
772 OpStore %28 %31
773 %60 = OpCompositeConstruct %12 %20 %19
774 %61 = OpCompositeConstruct %22 %26 %25
775 OpReturn
776 OpFunctionEnd
777 )";
778
779 const auto env = SPV_ENV_UNIVERSAL_1_4;
780 const auto consumer = nullptr;
781 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
782 spvtools::ValidatorOptions validator_options;
783 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
784 kConsoleMessageConsumer));
785 TransformationContext transformation_context(
786 MakeUnique<FactManager>(context.get()), validator_options);
787 // Bad: The object with |object_id| is not available at
788 // |instruction_to_insert_before|.
789 auto transformation_bad_1 = TransformationCompositeInsert(
790 MakeInstructionDescriptor(31, SpvOpIMul, 0), 50, 27, 60, {1});
791 ASSERT_FALSE(
792 transformation_bad_1.IsApplicable(context.get(), transformation_context));
793
794 // Bad: The composite with |composite_id| is not available at
795 // |instruction_to_insert_before|.
796 auto transformation_bad_2 = TransformationCompositeInsert(
797 MakeInstructionDescriptor(31, SpvOpIMul, 0), 50, 61, 21, {1});
798 ASSERT_FALSE(
799 transformation_bad_2.IsApplicable(context.get(), transformation_context));
800
801 // Bad: The |instruction_to_insert_before| is the composite itself and is
802 // available.
803 auto transformation_bad_3 = TransformationCompositeInsert(
804 MakeInstructionDescriptor(61, SpvOpCompositeConstruct, 0), 50, 61, 21,
805 {1});
806 ASSERT_FALSE(
807 transformation_bad_3.IsApplicable(context.get(), transformation_context));
808
809 // Bad: The |instruction_to_insert_before| is the object itself and is not
810 // available.
811 auto transformation_bad_4 = TransformationCompositeInsert(
812 MakeInstructionDescriptor(60, SpvOpCompositeConstruct, 0), 50, 27, 60,
813 {1});
814 ASSERT_FALSE(
815 transformation_bad_4.IsApplicable(context.get(), transformation_context));
816 }
817
TEST(TransformationCompositeInsertTest,CompositeInsertionWithIrrelevantIds)818 TEST(TransformationCompositeInsertTest, CompositeInsertionWithIrrelevantIds) {
819 // This checks that we do *not* get data synonym facts when we do composite
820 // insertion using irrelevant ids or in dead blocks.
821
822 std::string shader = R"(
823 OpCapability Shader
824 %1 = OpExtInstImport "GLSL.std.450"
825 OpMemoryModel Logical GLSL450
826 OpEntryPoint Fragment %12 "main"
827 OpExecutionMode %12 OriginUpperLeft
828 OpSource ESSL 310
829 %2 = OpTypeVoid
830 %3 = OpTypeFunction %2
831 %6 = OpTypeInt 32 1
832 %7 = OpTypeVector %6 2
833 %8 = OpConstant %6 0
834 %9 = OpConstantComposite %7 %8 %8
835 %10 = OpTypeBool
836 %11 = OpConstantFalse %10
837 %16 = OpConstant %6 0
838 %17 = OpConstant %6 1
839 %18 = OpConstantComposite %7 %8 %8
840 %12 = OpFunction %2 None %3
841 %13 = OpLabel
842 OpSelectionMerge %15 None
843 OpBranchConditional %11 %14 %15
844 %14 = OpLabel
845 OpBranch %15
846 %15 = OpLabel
847 OpReturn
848 OpFunctionEnd
849 )";
850
851 const auto env = SPV_ENV_UNIVERSAL_1_3;
852 const auto consumer = nullptr;
853 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
854 spvtools::ValidatorOptions validator_options;
855 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
856 kConsoleMessageConsumer));
857 TransformationContext transformation_context(
858 MakeUnique<FactManager>(context.get()), validator_options);
859
860 transformation_context.GetFactManager()->AddFactBlockIsDead(14);
861 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(16);
862 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(18);
863
864 // Leads to synonyms - nothing is irrelevant.
865 auto transformation1 = TransformationCompositeInsert(
866 MakeInstructionDescriptor(13, SpvOpSelectionMerge, 0), 100, 9, 17, {0});
867 ASSERT_TRUE(
868 transformation1.IsApplicable(context.get(), transformation_context));
869 ApplyAndCheckFreshIds(transformation1, context.get(),
870 &transformation_context);
871 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
872 MakeDataDescriptor(100, {0}), MakeDataDescriptor(17, {})));
873 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
874 MakeDataDescriptor(100, {1}), MakeDataDescriptor(9, {1})));
875
876 // Because %16 is irrelevant, we don't get a synonym with the component to
877 // which it has been inserted (but we do for the other component).
878 auto transformation2 = TransformationCompositeInsert(
879 MakeInstructionDescriptor(13, SpvOpSelectionMerge, 0), 101, 9, 16, {0});
880 ASSERT_TRUE(
881 transformation2.IsApplicable(context.get(), transformation_context));
882 ApplyAndCheckFreshIds(transformation2, context.get(),
883 &transformation_context);
884 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
885 MakeDataDescriptor(101, {0}), MakeDataDescriptor(16, {})));
886 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
887 MakeDataDescriptor(101, {1}), MakeDataDescriptor(9, {1})));
888
889 // Because %18 is irrelevant we only get a synonym for the component into
890 // which insertion has taken place.
891 auto transformation3 = TransformationCompositeInsert(
892 MakeInstructionDescriptor(13, SpvOpSelectionMerge, 0), 102, 18, 17, {0});
893 ASSERT_TRUE(
894 transformation3.IsApplicable(context.get(), transformation_context));
895 ApplyAndCheckFreshIds(transformation3, context.get(),
896 &transformation_context);
897 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
898 MakeDataDescriptor(102, {0}), MakeDataDescriptor(17, {})));
899 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
900 MakeDataDescriptor(102, {1}), MakeDataDescriptor(18, {1})));
901
902 // Does not lead to synonyms as block %14 is dead.
903 auto transformation4 = TransformationCompositeInsert(
904 MakeInstructionDescriptor(14, SpvOpBranch, 0), 103, 9, 17, {0});
905 ASSERT_TRUE(
906 transformation4.IsApplicable(context.get(), transformation_context));
907 ApplyAndCheckFreshIds(transformation4, context.get(),
908 &transformation_context);
909 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
910 MakeDataDescriptor(103, {0}), MakeDataDescriptor(17, {})));
911 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
912 MakeDataDescriptor(103, {1}), MakeDataDescriptor(9, {1})));
913
914 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
915 kConsoleMessageConsumer));
916 }
917
918 } // namespace
919 } // namespace fuzz
920 } // namespace spvtools
921