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_propagate_instruction_down.h"
16
17 #include "gtest/gtest.h"
18 #include "source/fuzz/counter_overflow_id_source.h"
19 #include "source/fuzz/fuzzer_util.h"
20 #include "test/fuzz/fuzz_test_util.h"
21
22 namespace spvtools {
23 namespace fuzz {
24 namespace {
25
TEST(TransformationPropagateInstructionDownTest,BasicTest)26 TEST(TransformationPropagateInstructionDownTest, BasicTest) {
27 std::string shader = R"(
28 OpCapability Shader
29 %1 = OpExtInstImport "GLSL.std.450"
30 OpMemoryModel Logical GLSL450
31 OpEntryPoint Fragment %4 "main"
32 OpExecutionMode %4 OriginUpperLeft
33 OpSource ESSL 310
34 %2 = OpTypeVoid
35 %3 = OpTypeFunction %2
36 %6 = OpTypeInt 32 1
37 %7 = OpConstant %6 1
38 %12 = OpTypeBool
39 %13 = OpConstantTrue %12
40 %9 = OpTypePointer Function %6
41 %4 = OpFunction %2 None %3
42
43 ; Has no instruction to propagate
44 %5 = OpLabel
45 %10 = OpVariable %9 Function
46 %8 = OpCopyObject %6 %7
47 OpStore %10 %8
48 OpBranch %11
49
50 ; Unreachable block
51 %100 = OpLabel
52 %101 = OpCopyObject %6 %7
53 OpBranch %11
54
55 ; Selection header
56 ;
57 ; One of acceptable successors has an OpPhi that uses propagated
58 ; instruction's id
59 %11 = OpLabel
60 %19 = OpCopyObject %6 %7
61 OpSelectionMerge %18 None
62 OpBranchConditional %13 %14 %18
63
64 ; %16 has no acceptable successors
65 %14 = OpLabel
66 %20 = OpPhi %6 %19 %11
67 %15 = OpCopyObject %6 %7 ; dependency
68 OpBranch %16
69 %16 = OpLabel
70 %17 = OpCopyObject %6 %15
71 OpBranch %18
72
73 ; Can be applied
74 %18 = OpLabel
75 %21 = OpCopyObject %6 %7
76 OpSelectionMerge %24 None
77 OpBranchConditional %13 %22 %23
78 %22 = OpLabel
79 %29 = OpPhi %6 %7 %18
80 OpStore %10 %21
81 OpBranch %24
82 %23 = OpLabel
83 OpStore %10 %21
84 OpBranch %24
85 %24 = OpLabel
86 OpStore %10 %21
87 OpBranch %32
88
89 ; Can't replace all uses of the propagated instruction: %30 is
90 ; propagated into %27.
91 %32 = OpLabel
92 OpLoopMerge %28 %27 None
93 OpBranchConditional %13 %26 %28
94 %26 = OpLabel
95 %25 = OpCopyObject %6 %7
96 %30 = OpCopyObject %6 %25
97 OpBranchConditional %13 %27 %28
98 %27 = OpLabel
99 OpBranch %32
100 %28 = OpLabel
101 %31 = OpPhi %6 %30 %26 %7 %32 ; Can't replace this use
102 OpReturn
103
104 OpFunctionEnd
105 )";
106
107 const auto env = SPV_ENV_UNIVERSAL_1_3;
108 const auto consumer = nullptr;
109 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
110 spvtools::ValidatorOptions validator_options;
111 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
112 kConsoleMessageConsumer));
113 TransformationContext transformation_context(
114 MakeUnique<FactManager>(context.get()), validator_options);
115
116 // Invalid block id.
117 ASSERT_FALSE(TransformationPropagateInstructionDown(200, 200, {{}})
118 .IsApplicable(context.get(), transformation_context));
119 ASSERT_FALSE(TransformationPropagateInstructionDown(101, 200, {{}})
120 .IsApplicable(context.get(), transformation_context));
121
122 // The block is unreachable.
123 ASSERT_FALSE(TransformationPropagateInstructionDown(100, 200, {{}})
124 .IsApplicable(context.get(), transformation_context));
125
126 // The block has no instruction to propagate.
127 ASSERT_FALSE(TransformationPropagateInstructionDown(5, 200, {{{11, 201}}})
128 .IsApplicable(context.get(), transformation_context));
129
130 // The block has no acceptable successors.
131 ASSERT_FALSE(TransformationPropagateInstructionDown(16, 200, {{{18, 201}}})
132 .IsApplicable(context.get(), transformation_context));
133
134 // One of acceptable successors has an OpPhi that uses propagated
135 // instruction's id.
136 ASSERT_FALSE(
137 TransformationPropagateInstructionDown(11, 200, {{{14, 201}, {18, 202}}})
138 .IsApplicable(context.get(), transformation_context));
139
140 #ifndef NDEBUG
141 // Not all fresh ids are provided.
142 ASSERT_DEATH(
143 TransformationPropagateInstructionDown(18, 200, {{{22, 201}, {202, 203}}})
144 .IsApplicable(context.get(), transformation_context),
145 "Bad attempt to query whether overflow ids are available.");
146 #endif
147
148 // Not all fresh ids are fresh.
149 ASSERT_FALSE(TransformationPropagateInstructionDown(
150 18, 18, {{{22, 201}, {23, 202}, {202, 203}}})
151 .IsApplicable(context.get(), transformation_context));
152 ASSERT_FALSE(TransformationPropagateInstructionDown(
153 18, 200, {{{22, 22}, {23, 202}, {202, 203}}})
154 .IsApplicable(context.get(), transformation_context));
155 ASSERT_FALSE(TransformationPropagateInstructionDown(
156 18, 18, {{{22, 22}, {23, 202}, {202, 203}}})
157 .IsApplicable(context.get(), transformation_context));
158
159 // Not all fresh ids are unique.
160 ASSERT_FALSE(TransformationPropagateInstructionDown(
161 18, 200, {{{22, 200}, {23, 202}, {202, 200}}})
162 .IsApplicable(context.get(), transformation_context));
163 ASSERT_FALSE(TransformationPropagateInstructionDown(
164 18, 200, {{{22, 201}, {23, 202}, {202, 200}}})
165 .IsApplicable(context.get(), transformation_context));
166 ASSERT_FALSE(TransformationPropagateInstructionDown(
167 18, 200, {{{22, 201}, {23, 201}, {202, 203}}})
168 .IsApplicable(context.get(), transformation_context));
169
170 // Can't replace all uses of the propagated instruction: %30 is propagated
171 // into %27.
172 ASSERT_FALSE(TransformationPropagateInstructionDown(26, 200, {{{27, 201}}})
173 .IsApplicable(context.get(), transformation_context));
174
175 {
176 TransformationPropagateInstructionDown transformation(
177 18, 200, {{{22, 201}, {23, 202}, {202, 203}}});
178 ASSERT_TRUE(
179 transformation.IsApplicable(context.get(), transformation_context));
180 ApplyAndCheckFreshIds(transformation, context.get(),
181 &transformation_context);
182 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
183 context.get(), validator_options, kConsoleMessageConsumer));
184
185 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
186 MakeDataDescriptor(201, {}), MakeDataDescriptor(202, {})));
187 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
188 MakeDataDescriptor(201, {}), MakeDataDescriptor(200, {})));
189 }
190
191 std::string after_transformation = R"(
192 OpCapability Shader
193 %1 = OpExtInstImport "GLSL.std.450"
194 OpMemoryModel Logical GLSL450
195 OpEntryPoint Fragment %4 "main"
196 OpExecutionMode %4 OriginUpperLeft
197 OpSource ESSL 310
198 %2 = OpTypeVoid
199 %3 = OpTypeFunction %2
200 %6 = OpTypeInt 32 1
201 %7 = OpConstant %6 1
202 %12 = OpTypeBool
203 %13 = OpConstantTrue %12
204 %9 = OpTypePointer Function %6
205 %4 = OpFunction %2 None %3
206
207 ; Has no instruction to propagate
208 %5 = OpLabel
209 %10 = OpVariable %9 Function
210 %8 = OpCopyObject %6 %7
211 OpStore %10 %8
212 OpBranch %11
213
214 ; Unreachable block
215 %100 = OpLabel
216 %101 = OpCopyObject %6 %7
217 OpBranch %11
218
219 ; Selection header
220 ;
221 ; One of acceptable successors has an OpPhi that uses propagated
222 ; instruction's id
223 %11 = OpLabel
224 %19 = OpCopyObject %6 %7
225 OpSelectionMerge %18 None
226 OpBranchConditional %13 %14 %18
227
228 ; %16 has no acceptable successors
229 %14 = OpLabel
230 %20 = OpPhi %6 %19 %11
231 %15 = OpCopyObject %6 %7 ; dependency
232 OpBranch %16
233 %16 = OpLabel
234 %17 = OpCopyObject %6 %15
235 OpBranch %18
236
237 ; Can be applied
238 %18 = OpLabel
239 OpSelectionMerge %24 None
240 OpBranchConditional %13 %22 %23
241 %22 = OpLabel
242 %29 = OpPhi %6 %7 %18
243 %201 = OpCopyObject %6 %7
244 OpStore %10 %201
245 OpBranch %24
246 %23 = OpLabel
247 %202 = OpCopyObject %6 %7
248 OpStore %10 %202
249 OpBranch %24
250 %24 = OpLabel
251 %200 = OpPhi %6 %201 %22 %202 %23
252 OpStore %10 %200
253 OpBranch %32
254
255 ; Can't replace all uses of the propagated instruction: %30 is
256 ; propagated into %27.
257 %32 = OpLabel
258 OpLoopMerge %28 %27 None
259 OpBranchConditional %13 %26 %28
260 %26 = OpLabel
261 %25 = OpCopyObject %6 %7
262 %30 = OpCopyObject %6 %25
263 OpBranchConditional %13 %27 %28
264 %27 = OpLabel
265 OpBranch %32
266 %28 = OpLabel
267 %31 = OpPhi %6 %30 %26 %7 %32 ; Can't replace this use
268 OpReturn
269
270 OpFunctionEnd
271 )";
272
273 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
274 }
275
TEST(TransformationPropagateInstructionDownTest,CantCreateOpPhiTest)276 TEST(TransformationPropagateInstructionDownTest, CantCreateOpPhiTest) {
277 std::string shader = R"(
278 OpCapability Shader
279 %1 = OpExtInstImport "GLSL.std.450"
280 OpMemoryModel Logical GLSL450
281 OpEntryPoint Fragment %4 "main"
282 OpExecutionMode %4 OriginUpperLeft
283 OpSource ESSL 310
284 %2 = OpTypeVoid
285 %3 = OpTypeFunction %2
286 %6 = OpTypeInt 32 1
287 %7 = OpConstant %6 1
288 %12 = OpTypeBool
289 %13 = OpConstantTrue %12
290 %4 = OpFunction %2 None %3
291
292 ; %5 doesn't belong to any construct
293 %5 = OpLabel
294 %15 = OpCopyObject %6 %7
295 OpBranch %16
296
297 ; The merge block (%19) is unreachable
298 %16 = OpLabel
299 %17 = OpCopyObject %6 %7
300 OpSelectionMerge %19 None
301 OpBranchConditional %13 %18 %18
302
303 ; %21 doesn't dominate the merge block - %20
304 %18 = OpLabel
305 OpSelectionMerge %20 None
306 OpBranchConditional %13 %20 %21
307 %21 = OpLabel
308 %22 = OpCopyObject %6 %7
309 OpBranch %20
310
311 ; The merge block (%24) is an acceptable successor of the propagated
312 ; instruction's block
313 %20 = OpLabel
314 %23 = OpCopyObject %6 %7
315 OpSelectionMerge %24 None
316 OpBranchConditional %13 %24 %30
317 %30 = OpLabel
318 OpBranch %24
319
320 ; One of the predecessors of the merge block is not dominated by any
321 ; successor of the propagated instruction's block
322 %24 = OpLabel
323 %26 = OpCopyObject %6 %7
324 OpLoopMerge %29 %25 None
325 OpBranch %25
326 %25 = OpLabel
327 OpBranchConditional %13 %24 %29
328 %28 = OpLabel ; unreachable predecessor of %29
329 OpBranch %29
330 %29 = OpLabel
331 OpReturn
332
333 %19 = OpLabel
334 OpReturn
335 OpFunctionEnd
336 )";
337
338 const auto env = SPV_ENV_UNIVERSAL_1_3;
339 const auto consumer = nullptr;
340 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
341 spvtools::ValidatorOptions validator_options;
342 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
343 kConsoleMessageConsumer));
344 TransformationContext transformation_context(
345 MakeUnique<FactManager>(context.get()), validator_options);
346
347 TransformationPropagateInstructionDown transformations[] = {
348 // %5 doesn't belong to any construct.
349 {5, 200, {{{16, 201}}}},
350
351 // The merge block (%19) is unreachable.
352 {16, 200, {{{18, 202}}}},
353
354 // %21 doesn't dominate the merge block - %20.
355 {21, 200, {{{20, 203}}}},
356
357 // The merge block (%24) is an acceptable successor of the propagated
358 // instruction's block.
359 {20, 200, {{{24, 204}, {30, 205}}}},
360
361 // One of the predecessors of the merge block is not dominated by any
362 // successor of the propagated instruction's block.
363 {24, 200, {{{25, 206}}}},
364 };
365
366 for (const auto& transformation : transformations) {
367 ASSERT_TRUE(
368 transformation.IsApplicable(context.get(), transformation_context));
369 ApplyAndCheckFreshIds(transformation, context.get(),
370 &transformation_context);
371 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
372 context.get(), validator_options, kConsoleMessageConsumer));
373 }
374
375 // No transformation has introduced an OpPhi instruction.
376 ASSERT_FALSE(context->get_def_use_mgr()->GetDef(200));
377
378 std::string after_transformation = R"(
379 OpCapability Shader
380 %1 = OpExtInstImport "GLSL.std.450"
381 OpMemoryModel Logical GLSL450
382 OpEntryPoint Fragment %4 "main"
383 OpExecutionMode %4 OriginUpperLeft
384 OpSource ESSL 310
385 %2 = OpTypeVoid
386 %3 = OpTypeFunction %2
387 %6 = OpTypeInt 32 1
388 %7 = OpConstant %6 1
389 %12 = OpTypeBool
390 %13 = OpConstantTrue %12
391 %4 = OpFunction %2 None %3
392
393 ; %5 doesn't belong to any construct
394 %5 = OpLabel
395 OpBranch %16
396
397 ; The merge block (%19) is unreachable
398 %16 = OpLabel
399 %201 = OpCopyObject %6 %7
400 OpSelectionMerge %19 None
401 OpBranchConditional %13 %18 %18
402
403 ; %21 doesn't dominate the merge block - %20
404 %18 = OpLabel
405 %202 = OpCopyObject %6 %7
406 OpSelectionMerge %20 None
407 OpBranchConditional %13 %20 %21
408 %21 = OpLabel
409 OpBranch %20
410
411 ; The merge block (%24) is an acceptable successor of the propagated
412 ; instruction's block
413 %20 = OpLabel
414 %203 = OpCopyObject %6 %7
415 OpSelectionMerge %24 None
416 OpBranchConditional %13 %24 %30
417 %30 = OpLabel
418 %205 = OpCopyObject %6 %7
419 OpBranch %24
420
421 ; One of the predecessors of the merge block is not dominated by any
422 ; successor of the propagated instruction's block
423 %24 = OpLabel
424 %204 = OpCopyObject %6 %7
425 OpLoopMerge %29 %25 None
426 OpBranch %25
427 %25 = OpLabel
428 %206 = OpCopyObject %6 %7
429 OpBranchConditional %13 %24 %29
430 %28 = OpLabel ; unreachable predecessor of %29
431 OpBranch %29
432 %29 = OpLabel
433 OpReturn
434
435 %19 = OpLabel
436 OpReturn
437 OpFunctionEnd
438 )";
439
440 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
441 }
442
TEST(TransformationPropagateInstructionDownTest,VariablePointersCapability)443 TEST(TransformationPropagateInstructionDownTest, VariablePointersCapability) {
444 std::string shader = R"(
445 OpCapability Shader
446 %1 = OpExtInstImport "GLSL.std.450"
447 OpMemoryModel Logical GLSL450
448 OpEntryPoint Fragment %4 "main"
449 OpExecutionMode %4 OriginUpperLeft
450 OpSource ESSL 310
451 %2 = OpTypeVoid
452 %3 = OpTypeFunction %2
453 %6 = OpTypeInt 32 1
454 %7 = OpConstant %6 1
455 %12 = OpTypeBool
456 %13 = OpConstantTrue %12
457 %10 = OpTypePointer Workgroup %6
458 %11 = OpVariable %10 Workgroup
459 %4 = OpFunction %2 None %3
460 %5 = OpLabel
461 %18 = OpCopyObject %10 %11
462 %14 = OpCopyObject %10 %11
463 OpSelectionMerge %17 None
464 OpBranchConditional %13 %15 %16
465 %15 = OpLabel
466 OpBranch %17
467 %16 = OpLabel
468 OpBranch %17
469 %17 = OpLabel
470 OpStore %18 %7
471 OpReturn
472 OpFunctionEnd
473 )";
474
475 const auto env = SPV_ENV_UNIVERSAL_1_3;
476 const auto consumer = nullptr;
477 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
478 spvtools::ValidatorOptions validator_options;
479 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
480 kConsoleMessageConsumer));
481 TransformationContext transformation_context(
482 MakeUnique<FactManager>(context.get()), validator_options);
483
484 {
485 // Can propagate a pointer only if we don't have to create an OpPhi.
486 TransformationPropagateInstructionDown transformation(
487 5, 200, {{{15, 201}, {16, 202}}});
488 ASSERT_TRUE(
489 transformation.IsApplicable(context.get(), transformation_context));
490 ApplyAndCheckFreshIds(transformation, context.get(),
491 &transformation_context);
492 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
493 context.get(), validator_options, kConsoleMessageConsumer));
494 ASSERT_FALSE(context->get_def_use_mgr()->GetDef(200));
495 }
496 {
497 // Can't propagate a pointer if there is no VariablePointersStorageBuffer
498 // capability and we need to create an OpPhi.
499 TransformationPropagateInstructionDown transformation(
500 5, 200, {{{15, 203}, {16, 204}}});
501 ASSERT_FALSE(context->get_feature_mgr()->HasCapability(
502 SpvCapabilityVariablePointersStorageBuffer));
503 ASSERT_FALSE(
504 transformation.IsApplicable(context.get(), transformation_context));
505
506 context->AddCapability(SpvCapabilityVariablePointers);
507 ASSERT_TRUE(context->get_feature_mgr()->HasCapability(
508 SpvCapabilityVariablePointersStorageBuffer));
509 ASSERT_TRUE(
510 transformation.IsApplicable(context.get(), transformation_context));
511 ApplyAndCheckFreshIds(transformation, context.get(),
512 &transformation_context);
513 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
514 context.get(), validator_options, kConsoleMessageConsumer));
515 }
516
517 std::string after_transformation = R"(
518 OpCapability Shader
519 OpCapability VariablePointers
520 %1 = OpExtInstImport "GLSL.std.450"
521 OpMemoryModel Logical GLSL450
522 OpEntryPoint Fragment %4 "main"
523 OpExecutionMode %4 OriginUpperLeft
524 OpSource ESSL 310
525 %2 = OpTypeVoid
526 %3 = OpTypeFunction %2
527 %6 = OpTypeInt 32 1
528 %7 = OpConstant %6 1
529 %12 = OpTypeBool
530 %13 = OpConstantTrue %12
531 %10 = OpTypePointer Workgroup %6
532 %11 = OpVariable %10 Workgroup
533 %4 = OpFunction %2 None %3
534 %5 = OpLabel
535 OpSelectionMerge %17 None
536 OpBranchConditional %13 %15 %16
537 %15 = OpLabel
538 %203 = OpCopyObject %10 %11
539 %201 = OpCopyObject %10 %11
540 OpBranch %17
541 %16 = OpLabel
542 %204 = OpCopyObject %10 %11
543 %202 = OpCopyObject %10 %11
544 OpBranch %17
545 %17 = OpLabel
546 %200 = OpPhi %10 %203 %15 %204 %16
547 OpStore %200 %7
548 OpReturn
549 OpFunctionEnd
550 )";
551
552 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
553 }
554
TEST(TransformationPropagateInstructionDownTest,UseOverflowIdsTest)555 TEST(TransformationPropagateInstructionDownTest, UseOverflowIdsTest) {
556 std::string shader = R"(
557 OpCapability Shader
558 %1 = OpExtInstImport "GLSL.std.450"
559 OpMemoryModel Logical GLSL450
560 OpEntryPoint Fragment %4 "main"
561 OpExecutionMode %4 OriginUpperLeft
562 OpSource ESSL 310
563 %2 = OpTypeVoid
564 %3 = OpTypeFunction %2
565 %6 = OpTypeInt 32 1
566 %7 = OpConstant %6 1
567 %12 = OpTypeBool
568 %13 = OpConstantTrue %12
569 %10 = OpTypePointer Private %6
570 %11 = OpVariable %10 Private
571 %4 = OpFunction %2 None %3
572 %5 = OpLabel
573 %20 = OpCopyObject %6 %7
574 OpSelectionMerge %23 None
575 OpBranchConditional %13 %21 %22
576 %21 = OpLabel
577 OpStore %11 %20
578 OpBranch %23
579 %22 = OpLabel
580 OpStore %11 %20
581 OpBranch %23
582 %23 = OpLabel
583 OpStore %11 %20
584 OpReturn
585 OpFunctionEnd
586 )";
587
588 const auto env = SPV_ENV_UNIVERSAL_1_3;
589 const auto consumer = nullptr;
590 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
591 spvtools::ValidatorOptions validator_options;
592 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
593 kConsoleMessageConsumer));
594 TransformationContext transformation_context(
595 MakeUnique<FactManager>(context.get()), validator_options,
596 MakeUnique<CounterOverflowIdSource>(300));
597
598 TransformationPropagateInstructionDown transformation(5, 200, {{{21, 201}}});
599 ASSERT_TRUE(
600 transformation.IsApplicable(context.get(), transformation_context));
601 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context,
602 {300});
603 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
604 kConsoleMessageConsumer));
605
606 std::string after_transformation = R"(
607 OpCapability Shader
608 %1 = OpExtInstImport "GLSL.std.450"
609 OpMemoryModel Logical GLSL450
610 OpEntryPoint Fragment %4 "main"
611 OpExecutionMode %4 OriginUpperLeft
612 OpSource ESSL 310
613 %2 = OpTypeVoid
614 %3 = OpTypeFunction %2
615 %6 = OpTypeInt 32 1
616 %7 = OpConstant %6 1
617 %12 = OpTypeBool
618 %13 = OpConstantTrue %12
619 %10 = OpTypePointer Private %6
620 %11 = OpVariable %10 Private
621 %4 = OpFunction %2 None %3
622 %5 = OpLabel
623 OpSelectionMerge %23 None
624 OpBranchConditional %13 %21 %22
625 %21 = OpLabel
626 %201 = OpCopyObject %6 %7
627 OpStore %11 %201
628 OpBranch %23
629 %22 = OpLabel
630 %300 = OpCopyObject %6 %7
631 OpStore %11 %300
632 OpBranch %23
633 %23 = OpLabel
634 %200 = OpPhi %6 %201 %21 %300 %22
635 OpStore %11 %200
636 OpReturn
637 OpFunctionEnd
638 )";
639
640 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
641 }
642
TEST(TransformationPropagateInstructionDownTest,TestCreatedFacts)643 TEST(TransformationPropagateInstructionDownTest, TestCreatedFacts) {
644 std::string shader = R"(
645 OpCapability VariablePointers
646 %1 = OpExtInstImport "GLSL.std.450"
647 OpMemoryModel Logical GLSL450
648 OpEntryPoint Fragment %4 "main"
649 OpExecutionMode %4 OriginUpperLeft
650 OpSource ESSL 310
651 %2 = OpTypeVoid
652 %3 = OpTypeFunction %2
653 %6 = OpTypeInt 32 1
654 %7 = OpConstant %6 1
655 %12 = OpTypeBool
656 %13 = OpConstantTrue %12
657 %10 = OpTypePointer Private %6
658 %11 = OpVariable %10 Private
659 %4 = OpFunction %2 None %3
660 %5 = OpLabel
661 %20 = OpCopyObject %6 %7
662 %24 = OpCopyObject %6 %7 ; Irrelevant id
663 %25 = OpCopyObject %10 %11 ; Pointee is irrelevant
664 OpSelectionMerge %23 None
665 OpBranchConditional %13 %21 %22
666 %21 = OpLabel
667 OpStore %25 %20
668 OpBranch %23
669 %22 = OpLabel ; Dead block
670 OpStore %25 %20
671 OpBranch %23
672 %23 = OpLabel
673 OpStore %25 %20
674 OpReturn
675 OpFunctionEnd
676 )";
677
678 const auto env = SPV_ENV_UNIVERSAL_1_3;
679 const auto consumer = nullptr;
680 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
681 spvtools::ValidatorOptions validator_options;
682 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
683 kConsoleMessageConsumer));
684 TransformationContext transformation_context(
685 MakeUnique<FactManager>(context.get()), validator_options);
686
687 transformation_context.GetFactManager()->AddFactBlockIsDead(22);
688 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(24);
689 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
690 25);
691
692 {
693 // Propagate pointer with PointeeIsIrrelevant fact.
694 TransformationPropagateInstructionDown transformation(
695 5, 200, {{{21, 201}, {22, 202}}});
696 ASSERT_TRUE(
697 transformation.IsApplicable(context.get(), transformation_context));
698 ApplyAndCheckFreshIds(transformation, context.get(),
699 &transformation_context);
700 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
701 context.get(), validator_options, kConsoleMessageConsumer));
702
703 ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(201));
704 ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(202));
705 ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(200));
706
707 ASSERT_TRUE(
708 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(201));
709 ASSERT_TRUE(
710 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(202));
711 ASSERT_TRUE(
712 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(200));
713
714 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
715 MakeDataDescriptor(201, {}), MakeDataDescriptor(202, {})));
716 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
717 MakeDataDescriptor(201, {}), MakeDataDescriptor(200, {})));
718 }
719 {
720 // Propagate an irrelevant id.
721 TransformationPropagateInstructionDown transformation(
722 5, 203, {{{21, 204}, {22, 205}}});
723 ASSERT_TRUE(
724 transformation.IsApplicable(context.get(), transformation_context));
725 ApplyAndCheckFreshIds(transformation, context.get(),
726 &transformation_context);
727 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
728 context.get(), validator_options, kConsoleMessageConsumer));
729
730 ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(203));
731 ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(204));
732 ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(205));
733
734 ASSERT_FALSE(
735 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(203));
736 ASSERT_FALSE(
737 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(204));
738 ASSERT_FALSE(
739 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(205));
740
741 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
742 MakeDataDescriptor(204, {}), MakeDataDescriptor(205, {})));
743 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
744 MakeDataDescriptor(204, {}), MakeDataDescriptor(203, {})));
745 }
746 {
747 // Propagate a regular id.
748 TransformationPropagateInstructionDown transformation(
749 5, 206, {{{21, 207}, {22, 208}}});
750 ASSERT_TRUE(
751 transformation.IsApplicable(context.get(), transformation_context));
752 ApplyAndCheckFreshIds(transformation, context.get(),
753 &transformation_context);
754 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
755 context.get(), validator_options, kConsoleMessageConsumer));
756
757 ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(206));
758 ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(207));
759 ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(208));
760
761 ASSERT_FALSE(
762 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(206));
763 ASSERT_FALSE(
764 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(207));
765 ASSERT_FALSE(
766 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(208));
767
768 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
769 MakeDataDescriptor(206, {}), MakeDataDescriptor(207, {})));
770 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
771 MakeDataDescriptor(206, {}), MakeDataDescriptor(208, {})));
772 }
773
774 std::string after_transformation = R"(
775 OpCapability VariablePointers
776 %1 = OpExtInstImport "GLSL.std.450"
777 OpMemoryModel Logical GLSL450
778 OpEntryPoint Fragment %4 "main"
779 OpExecutionMode %4 OriginUpperLeft
780 OpSource ESSL 310
781 %2 = OpTypeVoid
782 %3 = OpTypeFunction %2
783 %6 = OpTypeInt 32 1
784 %7 = OpConstant %6 1
785 %12 = OpTypeBool
786 %13 = OpConstantTrue %12
787 %10 = OpTypePointer Private %6
788 %11 = OpVariable %10 Private
789 %4 = OpFunction %2 None %3
790 %5 = OpLabel
791 OpSelectionMerge %23 None
792 OpBranchConditional %13 %21 %22
793 %21 = OpLabel
794 %207 = OpCopyObject %6 %7
795 %204 = OpCopyObject %6 %7 ; Irrelevant id
796 %201 = OpCopyObject %10 %11 ; Pointee is irrelevant
797 OpStore %201 %207
798 OpBranch %23
799 %22 = OpLabel ; Dead block
800 %208 = OpCopyObject %6 %7
801 %205 = OpCopyObject %6 %7 ; Irrelevant id
802 %202 = OpCopyObject %10 %11 ; Pointee is irrelevant
803 OpStore %202 %208
804 OpBranch %23
805 %23 = OpLabel
806 %206 = OpPhi %6 %207 %21 %208 %22
807 %203 = OpPhi %6 %204 %21 %205 %22 ; Irrelevant id
808 %200 = OpPhi %10 %201 %21 %202 %22 ; Pointee is irrelevant
809 OpStore %200 %206
810 OpReturn
811 OpFunctionEnd
812 )";
813
814 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
815 }
816
TEST(TransformationPropagateInstructionDownTest,TestLoops1)817 TEST(TransformationPropagateInstructionDownTest, TestLoops1) {
818 std::string shader = R"(
819 OpCapability Shader
820 %1 = OpExtInstImport "GLSL.std.450"
821 OpMemoryModel Logical GLSL450
822 OpEntryPoint Fragment %4 "main"
823 OpExecutionMode %4 OriginUpperLeft
824 OpSource ESSL 310
825 %2 = OpTypeVoid
826 %3 = OpTypeFunction %2
827 %6 = OpTypeInt 32 1
828 %7 = OpConstant %6 1
829 %12 = OpTypeBool
830 %13 = OpConstantTrue %12
831 %4 = OpFunction %2 None %3
832 %5 = OpLabel
833 OpBranch %20
834
835 %20 = OpLabel
836 OpLoopMerge %26 %25 None
837 OpBranch %21
838
839 %21 = OpLabel
840 %22 = OpCopyObject %6 %7
841 %31 = OpCopyObject %6 %7
842 OpSelectionMerge %35 None
843 OpBranchConditional %13 %23 %24
844
845 %23 = OpLabel
846 %27 = OpCopyObject %6 %22
847 %32 = OpCopyObject %6 %31
848 OpBranch %26
849 %24 = OpLabel
850 %28 = OpCopyObject %6 %22
851 %33 = OpCopyObject %6 %31
852 OpBranchConditional %13 %26 %25
853
854 %35 = OpLabel
855 OpBranch %25
856
857 %25 = OpLabel
858 %29 = OpCopyObject %6 %22
859 %34 = OpCopyObject %6 %31
860 OpBranch %20
861 %26 = OpLabel
862 %30 = OpCopyObject %6 %22
863 OpReturn
864 OpFunctionEnd
865 )";
866
867 const auto env = SPV_ENV_UNIVERSAL_1_3;
868 const auto consumer = nullptr;
869 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
870 spvtools::ValidatorOptions validator_options;
871 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
872 kConsoleMessageConsumer));
873 TransformationContext transformation_context(
874 MakeUnique<FactManager>(context.get()), validator_options);
875
876 {
877 TransformationPropagateInstructionDown transformation(
878 21, 200, {{{23, 201}, {24, 202}}});
879 ASSERT_TRUE(
880 transformation.IsApplicable(context.get(), transformation_context));
881 ApplyAndCheckFreshIds(transformation, context.get(),
882 &transformation_context);
883 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
884 context.get(), validator_options, kConsoleMessageConsumer));
885 }
886
887 // Can't replace usage of %22 in %26.
888 ASSERT_FALSE(
889 TransformationPropagateInstructionDown(21, 200, {{{23, 201}, {24, 202}}})
890 .IsApplicable(context.get(), transformation_context));
891
892 std::string after_transformation = R"(
893 OpCapability Shader
894 %1 = OpExtInstImport "GLSL.std.450"
895 OpMemoryModel Logical GLSL450
896 OpEntryPoint Fragment %4 "main"
897 OpExecutionMode %4 OriginUpperLeft
898 OpSource ESSL 310
899 %2 = OpTypeVoid
900 %3 = OpTypeFunction %2
901 %6 = OpTypeInt 32 1
902 %7 = OpConstant %6 1
903 %12 = OpTypeBool
904 %13 = OpConstantTrue %12
905 %4 = OpFunction %2 None %3
906 %5 = OpLabel
907 OpBranch %20
908
909 %20 = OpLabel
910 OpLoopMerge %26 %25 None
911 OpBranch %21
912
913 %21 = OpLabel
914 %22 = OpCopyObject %6 %7
915 OpSelectionMerge %35 None
916 OpBranchConditional %13 %23 %24
917 %23 = OpLabel
918 %201 = OpCopyObject %6 %7
919 %27 = OpCopyObject %6 %22
920 %32 = OpCopyObject %6 %201
921 OpBranch %26
922 %24 = OpLabel
923 %202 = OpCopyObject %6 %7
924 %28 = OpCopyObject %6 %22
925 %33 = OpCopyObject %6 %202
926 OpBranchConditional %13 %26 %25
927
928 %35 = OpLabel
929 OpBranch %25
930
931 %25 = OpLabel
932 %29 = OpCopyObject %6 %22
933 %34 = OpCopyObject %6 %202
934 OpBranch %20
935 %26 = OpLabel
936 %30 = OpCopyObject %6 %22
937 OpReturn
938 OpFunctionEnd
939 )";
940
941 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
942 }
943
TEST(TransformationPropagateInstructionDownTest,TestLoops2)944 TEST(TransformationPropagateInstructionDownTest, TestLoops2) {
945 std::string shader = R"(
946 OpCapability Shader
947 %1 = OpExtInstImport "GLSL.std.450"
948 OpMemoryModel Logical GLSL450
949 OpEntryPoint Fragment %4 "main"
950 OpExecutionMode %4 OriginUpperLeft
951 OpSource ESSL 310
952 %2 = OpTypeVoid
953 %3 = OpTypeFunction %2
954 %6 = OpTypeInt 32 1
955 %7 = OpConstant %6 1
956 %12 = OpTypeBool
957 %13 = OpConstantTrue %12
958 %4 = OpFunction %2 None %3
959 %5 = OpLabel
960 OpBranch %20
961
962 %20 = OpLabel
963 %23 = OpPhi %6 %7 %5 %24 %21
964 OpLoopMerge %22 %21 None
965 OpBranch %21
966
967 %21 = OpLabel
968 %24 = OpCopyObject %6 %23
969 %25 = OpCopyObject %6 %7
970 OpBranchConditional %13 %22 %20
971
972 %22 = OpLabel
973 OpReturn
974 OpFunctionEnd
975 )";
976
977 const auto env = SPV_ENV_UNIVERSAL_1_3;
978 const auto consumer = nullptr;
979 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
980 spvtools::ValidatorOptions validator_options;
981 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
982 kConsoleMessageConsumer));
983 TransformationContext transformation_context(
984 MakeUnique<FactManager>(context.get()), validator_options);
985
986 {
987 // Can propagate %25 from %21 into %20.
988 TransformationPropagateInstructionDown transformation(
989 21, 200, {{{20, 201}, {22, 202}}});
990 ASSERT_TRUE(
991 transformation.IsApplicable(context.get(), transformation_context));
992 ApplyAndCheckFreshIds(transformation, context.get(),
993 &transformation_context);
994 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
995 context.get(), validator_options, kConsoleMessageConsumer));
996 }
997 {
998 // Can propagate %201 from %20 into %21.
999 TransformationPropagateInstructionDown transformation(20, 200,
1000 {{{21, 203}}});
1001 ASSERT_TRUE(
1002 transformation.IsApplicable(context.get(), transformation_context));
1003 ApplyAndCheckFreshIds(transformation, context.get(),
1004 &transformation_context);
1005 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
1006 context.get(), validator_options, kConsoleMessageConsumer));
1007 }
1008
1009 // Can't propagate %24 from %21 into %20.
1010 ASSERT_FALSE(
1011 TransformationPropagateInstructionDown(21, 200, {{{20, 204}, {22, 205}}})
1012 .IsApplicable(context.get(), transformation_context));
1013
1014 std::string after_transformation = R"(
1015 OpCapability Shader
1016 %1 = OpExtInstImport "GLSL.std.450"
1017 OpMemoryModel Logical GLSL450
1018 OpEntryPoint Fragment %4 "main"
1019 OpExecutionMode %4 OriginUpperLeft
1020 OpSource ESSL 310
1021 %2 = OpTypeVoid
1022 %3 = OpTypeFunction %2
1023 %6 = OpTypeInt 32 1
1024 %7 = OpConstant %6 1
1025 %12 = OpTypeBool
1026 %13 = OpConstantTrue %12
1027 %4 = OpFunction %2 None %3
1028 %5 = OpLabel
1029 OpBranch %20
1030
1031 %20 = OpLabel
1032 %23 = OpPhi %6 %7 %5 %24 %21
1033 OpLoopMerge %22 %21 None
1034 OpBranch %21
1035
1036 %21 = OpLabel
1037 %203 = OpCopyObject %6 %7
1038 %24 = OpCopyObject %6 %23
1039 OpBranchConditional %13 %22 %20
1040
1041 %22 = OpLabel
1042 %200 = OpPhi %6 %203 %21
1043 %202 = OpCopyObject %6 %7
1044 OpReturn
1045 OpFunctionEnd
1046 )";
1047
1048 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1049 }
1050
TEST(TransformationPropagateInstructionDownTest,TestLoops3)1051 TEST(TransformationPropagateInstructionDownTest, TestLoops3) {
1052 std::string shader = R"(
1053 OpCapability Shader
1054 %1 = OpExtInstImport "GLSL.std.450"
1055 OpMemoryModel Logical GLSL450
1056 OpEntryPoint Fragment %4 "main"
1057 OpExecutionMode %4 OriginUpperLeft
1058 OpSource ESSL 310
1059 %2 = OpTypeVoid
1060 %3 = OpTypeFunction %2
1061 %6 = OpTypeInt 32 1
1062 %7 = OpConstant %6 1
1063 %12 = OpTypeBool
1064 %13 = OpConstantTrue %12
1065 %4 = OpFunction %2 None %3
1066 %5 = OpLabel
1067 OpBranch %20
1068
1069 %20 = OpLabel
1070 %27 = OpPhi %6 %7 %5 %26 %20
1071 %25 = OpCopyObject %6 %7
1072 %26 = OpCopyObject %6 %7
1073 OpLoopMerge %22 %20 None
1074 OpBranchConditional %13 %20 %22
1075
1076 %22 = OpLabel
1077 OpReturn
1078 OpFunctionEnd
1079 )";
1080
1081 const auto env = SPV_ENV_UNIVERSAL_1_3;
1082 const auto consumer = nullptr;
1083 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1084 spvtools::ValidatorOptions validator_options;
1085 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1086 kConsoleMessageConsumer));
1087 TransformationContext transformation_context(
1088 MakeUnique<FactManager>(context.get()), validator_options);
1089
1090 {
1091 // Propagate %25 into %20 and %22. Not that we are skipping %26 since not
1092 // all of its users are in different blocks (%27).h
1093 TransformationPropagateInstructionDown transformation(
1094 20, 200, {{{20, 201}, {22, 202}}});
1095 ASSERT_TRUE(
1096 transformation.IsApplicable(context.get(), transformation_context));
1097 ApplyAndCheckFreshIds(transformation, context.get(),
1098 &transformation_context);
1099 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
1100 context.get(), validator_options, kConsoleMessageConsumer));
1101 }
1102
1103 std::string after_transformation = R"(
1104 OpCapability Shader
1105 %1 = OpExtInstImport "GLSL.std.450"
1106 OpMemoryModel Logical GLSL450
1107 OpEntryPoint Fragment %4 "main"
1108 OpExecutionMode %4 OriginUpperLeft
1109 OpSource ESSL 310
1110 %2 = OpTypeVoid
1111 %3 = OpTypeFunction %2
1112 %6 = OpTypeInt 32 1
1113 %7 = OpConstant %6 1
1114 %12 = OpTypeBool
1115 %13 = OpConstantTrue %12
1116 %4 = OpFunction %2 None %3
1117 %5 = OpLabel
1118 OpBranch %20
1119
1120 %20 = OpLabel
1121 %27 = OpPhi %6 %7 %5 %26 %20
1122 %201 = OpCopyObject %6 %7
1123 %26 = OpCopyObject %6 %7
1124 OpLoopMerge %22 %20 None
1125 OpBranchConditional %13 %20 %22
1126
1127 %22 = OpLabel
1128 %202 = OpCopyObject %6 %7
1129 OpReturn
1130 OpFunctionEnd
1131 )";
1132
1133 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1134 }
1135
1136 } // namespace
1137 } // namespace fuzz
1138 } // namespace spvtools
1139