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