1 // Copyright (c) 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "source/fuzz/transformation_split_block.h"
16
17 #include "gtest/gtest.h"
18 #include "source/fuzz/fuzzer_util.h"
19 #include "source/fuzz/instruction_descriptor.h"
20 #include "test/fuzz/fuzz_test_util.h"
21
22 namespace spvtools {
23 namespace fuzz {
24 namespace {
25
TEST(TransformationSplitBlockTest,NotApplicable)26 TEST(TransformationSplitBlockTest, NotApplicable) {
27 // The SPIR-V in this test came from the following fragment shader, with
28 // local store elimination applied to get some OpPhi instructions.
29 //
30 // void main() {
31 // int x;
32 // int i;
33 // for (i = 0; i < 100; i++) {
34 // x += i;
35 // }
36 // }
37
38 std::string shader = R"(
39 OpCapability Shader
40 %1 = OpExtInstImport "GLSL.std.450"
41 OpMemoryModel Logical GLSL450
42 OpEntryPoint Fragment %4 "main"
43 OpExecutionMode %4 OriginUpperLeft
44 OpSource ESSL 310
45 OpName %4 "main"
46 OpName %8 "i"
47 OpName %19 "x"
48 OpDecorate %8 RelaxedPrecision
49 OpDecorate %19 RelaxedPrecision
50 OpDecorate %22 RelaxedPrecision
51 OpDecorate %25 RelaxedPrecision
52 OpDecorate %26 RelaxedPrecision
53 OpDecorate %27 RelaxedPrecision
54 %2 = OpTypeVoid
55 %3 = OpTypeFunction %2
56 %6 = OpTypeInt 32 1
57 %7 = OpTypePointer Function %6
58 %9 = OpConstant %6 0
59 %16 = OpConstant %6 100
60 %17 = OpTypeBool
61 %24 = OpConstant %6 1
62 %28 = OpUndef %6
63 %4 = OpFunction %2 None %3
64 %5 = OpLabel
65 %8 = OpVariable %7 Function
66 %19 = OpVariable %7 Function
67 OpStore %8 %9
68 OpBranch %10
69 %10 = OpLabel
70 %27 = OpPhi %6 %28 %5 %22 %13
71 %26 = OpPhi %6 %9 %5 %25 %13
72 OpLoopMerge %12 %13 None
73 OpBranch %14
74 %14 = OpLabel
75 %18 = OpSLessThan %17 %26 %16
76 OpBranchConditional %18 %11 %12
77 %11 = OpLabel
78 %22 = OpIAdd %6 %27 %26
79 OpStore %19 %22
80 OpBranch %13
81 %13 = OpLabel
82 %25 = OpIAdd %6 %26 %24
83 OpStore %8 %25
84 OpBranch %10
85 %12 = OpLabel
86 OpReturn
87 OpFunctionEnd
88 )";
89
90 const auto env = SPV_ENV_UNIVERSAL_1_3;
91 const auto consumer = nullptr;
92 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
93
94 spvtools::ValidatorOptions validator_options;
95 TransformationContext transformation_context(
96 MakeUnique<FactManager>(context.get()), validator_options);
97 // No split before OpVariable
98 ASSERT_FALSE(TransformationSplitBlock(
99 MakeInstructionDescriptor(8, spv::Op::OpVariable, 0), 100)
100 .IsApplicable(context.get(), transformation_context));
101 ASSERT_FALSE(TransformationSplitBlock(
102 MakeInstructionDescriptor(8, spv::Op::OpVariable, 1), 100)
103 .IsApplicable(context.get(), transformation_context));
104
105 // No split before OpLabel
106 ASSERT_FALSE(TransformationSplitBlock(
107 MakeInstructionDescriptor(14, spv::Op::OpLabel, 0), 100)
108 .IsApplicable(context.get(), transformation_context));
109
110 // No split if base instruction is outside a function
111 ASSERT_FALSE(TransformationSplitBlock(
112 MakeInstructionDescriptor(1, spv::Op::OpLabel, 0), 100)
113 .IsApplicable(context.get(), transformation_context));
114 ASSERT_FALSE(
115 TransformationSplitBlock(
116 MakeInstructionDescriptor(1, spv::Op::OpExecutionMode, 0), 100)
117 .IsApplicable(context.get(), transformation_context));
118
119 // No split if block is loop header
120 ASSERT_FALSE(TransformationSplitBlock(
121 MakeInstructionDescriptor(27, spv::Op::OpPhi, 0), 100)
122 .IsApplicable(context.get(), transformation_context));
123 ASSERT_FALSE(TransformationSplitBlock(
124 MakeInstructionDescriptor(27, spv::Op::OpPhi, 1), 100)
125 .IsApplicable(context.get(), transformation_context));
126
127 // No split if base instruction does not exist
128 ASSERT_FALSE(TransformationSplitBlock(
129 MakeInstructionDescriptor(88, spv::Op::OpIAdd, 0), 100)
130 .IsApplicable(context.get(), transformation_context));
131 ASSERT_FALSE(TransformationSplitBlock(
132 MakeInstructionDescriptor(88, spv::Op::OpIMul, 22), 100)
133 .IsApplicable(context.get(), transformation_context));
134
135 // No split if too many instructions with the desired opcode are skipped
136 ASSERT_FALSE(
137 TransformationSplitBlock(
138 MakeInstructionDescriptor(18, spv::Op::OpBranchConditional, 1), 100)
139 .IsApplicable(context.get(), transformation_context));
140
141 // No split if id in use
142 ASSERT_FALSE(TransformationSplitBlock(
143 MakeInstructionDescriptor(18, spv::Op::OpSLessThan, 0), 27)
144 .IsApplicable(context.get(), transformation_context));
145 ASSERT_FALSE(TransformationSplitBlock(
146 MakeInstructionDescriptor(18, spv::Op::OpSLessThan, 0), 14)
147 .IsApplicable(context.get(), transformation_context));
148 }
149
TEST(TransformationSplitBlockTest,SplitBlockSeveralTimes)150 TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) {
151 // The SPIR-V in this test came from the following fragment shader:
152 //
153 // void main() {
154 // int a;
155 // int b;
156 // a = 1;
157 // b = a;
158 // a = b;
159 // b = 2;
160 // b++;
161 // }
162
163 std::string shader = R"(
164 OpCapability Shader
165 %1 = OpExtInstImport "GLSL.std.450"
166 OpMemoryModel Logical GLSL450
167 OpEntryPoint Fragment %4 "main"
168 OpExecutionMode %4 OriginUpperLeft
169 OpSource ESSL 310
170 OpName %4 "main"
171 OpName %8 "a"
172 OpName %10 "b"
173 OpDecorate %8 RelaxedPrecision
174 OpDecorate %10 RelaxedPrecision
175 OpDecorate %11 RelaxedPrecision
176 OpDecorate %12 RelaxedPrecision
177 OpDecorate %14 RelaxedPrecision
178 OpDecorate %15 RelaxedPrecision
179 %2 = OpTypeVoid
180 %3 = OpTypeFunction %2
181 %6 = OpTypeInt 32 1
182 %7 = OpTypePointer Function %6
183 %9 = OpConstant %6 1
184 %13 = OpConstant %6 2
185 %4 = OpFunction %2 None %3
186 %5 = OpLabel
187 %8 = OpVariable %7 Function
188 %10 = OpVariable %7 Function
189 OpStore %8 %9
190 %11 = OpLoad %6 %8
191 OpStore %10 %11
192 %12 = OpLoad %6 %10
193 OpStore %8 %12
194 OpStore %10 %13
195 %14 = OpLoad %6 %10
196 %15 = OpIAdd %6 %14 %9
197 OpStore %10 %15
198 OpReturn
199 OpFunctionEnd
200 )";
201
202 const auto env = SPV_ENV_UNIVERSAL_1_3;
203 const auto consumer = nullptr;
204 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
205
206 spvtools::ValidatorOptions validator_options;
207 TransformationContext transformation_context(
208 MakeUnique<FactManager>(context.get()), validator_options);
209 auto split_1 = TransformationSplitBlock(
210 MakeInstructionDescriptor(5, spv::Op::OpStore, 0), 100);
211 ASSERT_TRUE(split_1.IsApplicable(context.get(), transformation_context));
212 ApplyAndCheckFreshIds(split_1, context.get(), &transformation_context);
213 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
214 kConsoleMessageConsumer));
215
216 std::string after_split_1 = R"(
217 OpCapability Shader
218 %1 = OpExtInstImport "GLSL.std.450"
219 OpMemoryModel Logical GLSL450
220 OpEntryPoint Fragment %4 "main"
221 OpExecutionMode %4 OriginUpperLeft
222 OpSource ESSL 310
223 OpName %4 "main"
224 OpName %8 "a"
225 OpName %10 "b"
226 OpDecorate %8 RelaxedPrecision
227 OpDecorate %10 RelaxedPrecision
228 OpDecorate %11 RelaxedPrecision
229 OpDecorate %12 RelaxedPrecision
230 OpDecorate %14 RelaxedPrecision
231 OpDecorate %15 RelaxedPrecision
232 %2 = OpTypeVoid
233 %3 = OpTypeFunction %2
234 %6 = OpTypeInt 32 1
235 %7 = OpTypePointer Function %6
236 %9 = OpConstant %6 1
237 %13 = OpConstant %6 2
238 %4 = OpFunction %2 None %3
239 %5 = OpLabel
240 %8 = OpVariable %7 Function
241 %10 = OpVariable %7 Function
242 OpBranch %100
243 %100 = OpLabel
244 OpStore %8 %9
245 %11 = OpLoad %6 %8
246 OpStore %10 %11
247 %12 = OpLoad %6 %10
248 OpStore %8 %12
249 OpStore %10 %13
250 %14 = OpLoad %6 %10
251 %15 = OpIAdd %6 %14 %9
252 OpStore %10 %15
253 OpReturn
254 OpFunctionEnd
255 )";
256 ASSERT_TRUE(IsEqual(env, after_split_1, context.get()));
257
258 auto split_2 = TransformationSplitBlock(
259 MakeInstructionDescriptor(11, spv::Op::OpStore, 0), 101);
260 ASSERT_TRUE(split_2.IsApplicable(context.get(), transformation_context));
261 ApplyAndCheckFreshIds(split_2, context.get(), &transformation_context);
262 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
263 kConsoleMessageConsumer));
264
265 std::string after_split_2 = R"(
266 OpCapability Shader
267 %1 = OpExtInstImport "GLSL.std.450"
268 OpMemoryModel Logical GLSL450
269 OpEntryPoint Fragment %4 "main"
270 OpExecutionMode %4 OriginUpperLeft
271 OpSource ESSL 310
272 OpName %4 "main"
273 OpName %8 "a"
274 OpName %10 "b"
275 OpDecorate %8 RelaxedPrecision
276 OpDecorate %10 RelaxedPrecision
277 OpDecorate %11 RelaxedPrecision
278 OpDecorate %12 RelaxedPrecision
279 OpDecorate %14 RelaxedPrecision
280 OpDecorate %15 RelaxedPrecision
281 %2 = OpTypeVoid
282 %3 = OpTypeFunction %2
283 %6 = OpTypeInt 32 1
284 %7 = OpTypePointer Function %6
285 %9 = OpConstant %6 1
286 %13 = OpConstant %6 2
287 %4 = OpFunction %2 None %3
288 %5 = OpLabel
289 %8 = OpVariable %7 Function
290 %10 = OpVariable %7 Function
291 OpBranch %100
292 %100 = OpLabel
293 OpStore %8 %9
294 %11 = OpLoad %6 %8
295 OpBranch %101
296 %101 = OpLabel
297 OpStore %10 %11
298 %12 = OpLoad %6 %10
299 OpStore %8 %12
300 OpStore %10 %13
301 %14 = OpLoad %6 %10
302 %15 = OpIAdd %6 %14 %9
303 OpStore %10 %15
304 OpReturn
305 OpFunctionEnd
306 )";
307 ASSERT_TRUE(IsEqual(env, after_split_2, context.get()));
308
309 auto split_3 = TransformationSplitBlock(
310 MakeInstructionDescriptor(14, spv::Op::OpLoad, 0), 102);
311 ASSERT_TRUE(split_3.IsApplicable(context.get(), transformation_context));
312 ApplyAndCheckFreshIds(split_3, context.get(), &transformation_context);
313 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
314 kConsoleMessageConsumer));
315
316 std::string after_split_3 = R"(
317 OpCapability Shader
318 %1 = OpExtInstImport "GLSL.std.450"
319 OpMemoryModel Logical GLSL450
320 OpEntryPoint Fragment %4 "main"
321 OpExecutionMode %4 OriginUpperLeft
322 OpSource ESSL 310
323 OpName %4 "main"
324 OpName %8 "a"
325 OpName %10 "b"
326 OpDecorate %8 RelaxedPrecision
327 OpDecorate %10 RelaxedPrecision
328 OpDecorate %11 RelaxedPrecision
329 OpDecorate %12 RelaxedPrecision
330 OpDecorate %14 RelaxedPrecision
331 OpDecorate %15 RelaxedPrecision
332 %2 = OpTypeVoid
333 %3 = OpTypeFunction %2
334 %6 = OpTypeInt 32 1
335 %7 = OpTypePointer Function %6
336 %9 = OpConstant %6 1
337 %13 = OpConstant %6 2
338 %4 = OpFunction %2 None %3
339 %5 = OpLabel
340 %8 = OpVariable %7 Function
341 %10 = OpVariable %7 Function
342 OpBranch %100
343 %100 = OpLabel
344 OpStore %8 %9
345 %11 = OpLoad %6 %8
346 OpBranch %101
347 %101 = OpLabel
348 OpStore %10 %11
349 %12 = OpLoad %6 %10
350 OpStore %8 %12
351 OpStore %10 %13
352 OpBranch %102
353 %102 = OpLabel
354 %14 = OpLoad %6 %10
355 %15 = OpIAdd %6 %14 %9
356 OpStore %10 %15
357 OpReturn
358 OpFunctionEnd
359 )";
360 ASSERT_TRUE(IsEqual(env, after_split_3, context.get()));
361 }
362
TEST(TransformationSplitBlockTest,SplitBlockBeforeSelectBranch)363 TEST(TransformationSplitBlockTest, SplitBlockBeforeSelectBranch) {
364 // The SPIR-V in this test came from the following fragment shader:
365 //
366 // void main() {
367 // int x, y;
368 // x = 2;
369 // if (x < y) {
370 // y = 3;
371 // } else {
372 // y = 4;
373 // }
374 // }
375
376 std::string shader = R"(
377 OpCapability Shader
378 %1 = OpExtInstImport "GLSL.std.450"
379 OpMemoryModel Logical GLSL450
380 OpEntryPoint Fragment %4 "main"
381 OpExecutionMode %4 OriginUpperLeft
382 OpSource ESSL 310
383 OpName %4 "main"
384 OpName %8 "x"
385 OpName %11 "y"
386 OpDecorate %8 RelaxedPrecision
387 OpDecorate %10 RelaxedPrecision
388 OpDecorate %11 RelaxedPrecision
389 OpDecorate %12 RelaxedPrecision
390 %2 = OpTypeVoid
391 %3 = OpTypeFunction %2
392 %6 = OpTypeInt 32 1
393 %7 = OpTypePointer Function %6
394 %9 = OpConstant %6 2
395 %13 = OpTypeBool
396 %17 = OpConstant %6 3
397 %19 = OpConstant %6 4
398 %4 = OpFunction %2 None %3
399 %5 = OpLabel
400 %8 = OpVariable %7 Function
401 %11 = OpVariable %7 Function
402 OpStore %8 %9
403 %10 = OpLoad %6 %8
404 %12 = OpLoad %6 %11
405 %14 = OpSLessThan %13 %10 %12
406 OpSelectionMerge %16 None
407 OpBranchConditional %14 %15 %18
408 %15 = OpLabel
409 OpStore %11 %17
410 OpBranch %16
411 %18 = OpLabel
412 OpStore %11 %19
413 OpBranch %16
414 %16 = OpLabel
415 OpReturn
416 OpFunctionEnd
417 )";
418
419 const auto env = SPV_ENV_UNIVERSAL_1_3;
420 const auto consumer = nullptr;
421 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
422
423 spvtools::ValidatorOptions validator_options;
424 TransformationContext transformation_context(
425 MakeUnique<FactManager>(context.get()), validator_options);
426 // Illegal to split between the merge and the conditional branch.
427 ASSERT_FALSE(
428 TransformationSplitBlock(
429 MakeInstructionDescriptor(14, spv::Op::OpBranchConditional, 0), 100)
430 .IsApplicable(context.get(), transformation_context));
431 ASSERT_FALSE(
432 TransformationSplitBlock(
433 MakeInstructionDescriptor(12, spv::Op::OpBranchConditional, 0), 100)
434 .IsApplicable(context.get(), transformation_context));
435
436 auto split = TransformationSplitBlock(
437 MakeInstructionDescriptor(14, spv::Op::OpSelectionMerge, 0), 100);
438 ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
439 ApplyAndCheckFreshIds(split, context.get(), &transformation_context);
440 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
441 kConsoleMessageConsumer));
442
443 std::string after_split = R"(
444 OpCapability Shader
445 %1 = OpExtInstImport "GLSL.std.450"
446 OpMemoryModel Logical GLSL450
447 OpEntryPoint Fragment %4 "main"
448 OpExecutionMode %4 OriginUpperLeft
449 OpSource ESSL 310
450 OpName %4 "main"
451 OpName %8 "x"
452 OpName %11 "y"
453 OpDecorate %8 RelaxedPrecision
454 OpDecorate %10 RelaxedPrecision
455 OpDecorate %11 RelaxedPrecision
456 OpDecorate %12 RelaxedPrecision
457 %2 = OpTypeVoid
458 %3 = OpTypeFunction %2
459 %6 = OpTypeInt 32 1
460 %7 = OpTypePointer Function %6
461 %9 = OpConstant %6 2
462 %13 = OpTypeBool
463 %17 = OpConstant %6 3
464 %19 = OpConstant %6 4
465 %4 = OpFunction %2 None %3
466 %5 = OpLabel
467 %8 = OpVariable %7 Function
468 %11 = OpVariable %7 Function
469 OpStore %8 %9
470 %10 = OpLoad %6 %8
471 %12 = OpLoad %6 %11
472 %14 = OpSLessThan %13 %10 %12
473 OpBranch %100
474 %100 = OpLabel
475 OpSelectionMerge %16 None
476 OpBranchConditional %14 %15 %18
477 %15 = OpLabel
478 OpStore %11 %17
479 OpBranch %16
480 %18 = OpLabel
481 OpStore %11 %19
482 OpBranch %16
483 %16 = OpLabel
484 OpReturn
485 OpFunctionEnd
486 )";
487 ASSERT_TRUE(IsEqual(env, after_split, context.get()));
488 }
489
TEST(TransformationSplitBlockTest,SplitBlockBeforeSwitchBranch)490 TEST(TransformationSplitBlockTest, SplitBlockBeforeSwitchBranch) {
491 // The SPIR-V in this test came from the following fragment shader:
492 //
493 // void main() {
494 // int x, y;
495 // switch (y) {
496 // case 1:
497 // x = 2;
498 // case 2:
499 // break;
500 // case 3:
501 // x = 4;
502 // default:
503 // x = 6;
504 // }
505 // }
506
507 std::string shader = R"(
508 OpCapability Shader
509 %1 = OpExtInstImport "GLSL.std.450"
510 OpMemoryModel Logical GLSL450
511 OpEntryPoint Fragment %4 "main"
512 OpExecutionMode %4 OriginUpperLeft
513 OpSource ESSL 310
514 OpName %4 "main"
515 OpName %8 "y"
516 OpName %15 "x"
517 OpDecorate %8 RelaxedPrecision
518 OpDecorate %9 RelaxedPrecision
519 OpDecorate %15 RelaxedPrecision
520 %2 = OpTypeVoid
521 %3 = OpTypeFunction %2
522 %6 = OpTypeInt 32 1
523 %7 = OpTypePointer Function %6
524 %16 = OpConstant %6 2
525 %18 = OpConstant %6 4
526 %19 = OpConstant %6 6
527 %4 = OpFunction %2 None %3
528 %5 = OpLabel
529 %8 = OpVariable %7 Function
530 %15 = OpVariable %7 Function
531 %9 = OpLoad %6 %8
532 OpSelectionMerge %14 None
533 OpSwitch %9 %13 1 %10 2 %11 3 %12
534 %13 = OpLabel
535 OpStore %15 %19
536 OpBranch %14
537 %10 = OpLabel
538 OpStore %15 %16
539 OpBranch %11
540 %11 = OpLabel
541 OpBranch %14
542 %12 = OpLabel
543 OpStore %15 %18
544 OpBranch %13
545 %14 = OpLabel
546 OpReturn
547 OpFunctionEnd
548 )";
549
550 const auto env = SPV_ENV_UNIVERSAL_1_3;
551 const auto consumer = nullptr;
552 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
553
554 spvtools::ValidatorOptions validator_options;
555 TransformationContext transformation_context(
556 MakeUnique<FactManager>(context.get()), validator_options);
557 // Illegal to split between the merge and the conditional branch.
558 ASSERT_FALSE(TransformationSplitBlock(
559 MakeInstructionDescriptor(9, spv::Op::OpSwitch, 0), 100)
560 .IsApplicable(context.get(), transformation_context));
561 ASSERT_FALSE(TransformationSplitBlock(
562 MakeInstructionDescriptor(15, spv::Op::OpSwitch, 0), 100)
563 .IsApplicable(context.get(), transformation_context));
564
565 auto split = TransformationSplitBlock(
566 MakeInstructionDescriptor(9, spv::Op::OpSelectionMerge, 0), 100);
567 ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
568 ApplyAndCheckFreshIds(split, context.get(), &transformation_context);
569 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
570 kConsoleMessageConsumer));
571
572 std::string after_split = R"(
573 OpCapability Shader
574 %1 = OpExtInstImport "GLSL.std.450"
575 OpMemoryModel Logical GLSL450
576 OpEntryPoint Fragment %4 "main"
577 OpExecutionMode %4 OriginUpperLeft
578 OpSource ESSL 310
579 OpName %4 "main"
580 OpName %8 "y"
581 OpName %15 "x"
582 OpDecorate %8 RelaxedPrecision
583 OpDecorate %9 RelaxedPrecision
584 OpDecorate %15 RelaxedPrecision
585 %2 = OpTypeVoid
586 %3 = OpTypeFunction %2
587 %6 = OpTypeInt 32 1
588 %7 = OpTypePointer Function %6
589 %16 = OpConstant %6 2
590 %18 = OpConstant %6 4
591 %19 = OpConstant %6 6
592 %4 = OpFunction %2 None %3
593 %5 = OpLabel
594 %8 = OpVariable %7 Function
595 %15 = OpVariable %7 Function
596 %9 = OpLoad %6 %8
597 OpBranch %100
598 %100 = OpLabel
599 OpSelectionMerge %14 None
600 OpSwitch %9 %13 1 %10 2 %11 3 %12
601 %13 = OpLabel
602 OpStore %15 %19
603 OpBranch %14
604 %10 = OpLabel
605 OpStore %15 %16
606 OpBranch %11
607 %11 = OpLabel
608 OpBranch %14
609 %12 = OpLabel
610 OpStore %15 %18
611 OpBranch %13
612 %14 = OpLabel
613 OpReturn
614 OpFunctionEnd
615 )";
616 ASSERT_TRUE(IsEqual(env, after_split, context.get()));
617 }
618
TEST(TransformationSplitBlockTest,NoSplitDuringOpPhis)619 TEST(TransformationSplitBlockTest, NoSplitDuringOpPhis) {
620 // The SPIR-V in this test came from the following fragment shader, with
621 // local store elimination applied to get some OpPhi instructions.
622 //
623 // void main() {
624 // int x;
625 // int i;
626 // for (i = 0; i < 100; i++) {
627 // x += i;
628 // }
629 // }
630
631 std::string shader = R"(
632 OpCapability Shader
633 %1 = OpExtInstImport "GLSL.std.450"
634 OpMemoryModel Logical GLSL450
635 OpEntryPoint Fragment %4 "main"
636 OpExecutionMode %4 OriginUpperLeft
637 OpSource ESSL 310
638 OpName %4 "main"
639 OpName %8 "i"
640 OpName %19 "x"
641 OpDecorate %8 RelaxedPrecision
642 OpDecorate %19 RelaxedPrecision
643 OpDecorate %22 RelaxedPrecision
644 OpDecorate %25 RelaxedPrecision
645 OpDecorate %26 RelaxedPrecision
646 OpDecorate %27 RelaxedPrecision
647 %2 = OpTypeVoid
648 %3 = OpTypeFunction %2
649 %6 = OpTypeInt 32 1
650 %7 = OpTypePointer Function %6
651 %9 = OpConstant %6 0
652 %16 = OpConstant %6 100
653 %17 = OpTypeBool
654 %24 = OpConstant %6 1
655 %28 = OpUndef %6
656 %4 = OpFunction %2 None %3
657 %5 = OpLabel
658 %8 = OpVariable %7 Function
659 %19 = OpVariable %7 Function
660 OpStore %8 %9
661 OpBranch %10
662 %10 = OpLabel
663 %27 = OpPhi %6 %28 %5 %22 %13
664 %26 = OpPhi %6 %9 %5 %25 %13
665 OpBranch %50
666 %50 = OpLabel
667 OpLoopMerge %12 %13 None
668 OpBranch %14
669 %14 = OpLabel
670 %18 = OpSLessThan %17 %26 %16
671 OpBranchConditional %18 %11 %12
672 %11 = OpLabel
673 %22 = OpIAdd %6 %27 %26
674 OpStore %19 %22
675 OpBranch %13
676 %13 = OpLabel
677 %25 = OpIAdd %6 %26 %24
678 OpStore %8 %25
679 OpBranch %50
680 %12 = OpLabel
681 OpReturn
682 OpFunctionEnd
683 )";
684
685 const auto env = SPV_ENV_UNIVERSAL_1_3;
686 const auto consumer = nullptr;
687 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
688
689 spvtools::ValidatorOptions validator_options;
690 TransformationContext transformation_context(
691 MakeUnique<FactManager>(context.get()), validator_options);
692 // We cannot split before OpPhi instructions, since the number of incoming
693 // blocks may not appropriately match after splitting.
694 ASSERT_FALSE(TransformationSplitBlock(
695 MakeInstructionDescriptor(26, spv::Op::OpPhi, 0), 100)
696 .IsApplicable(context.get(), transformation_context));
697 ASSERT_FALSE(TransformationSplitBlock(
698 MakeInstructionDescriptor(27, spv::Op::OpPhi, 0), 100)
699 .IsApplicable(context.get(), transformation_context));
700 ASSERT_FALSE(TransformationSplitBlock(
701 MakeInstructionDescriptor(27, spv::Op::OpPhi, 1), 100)
702 .IsApplicable(context.get(), transformation_context));
703 }
704
TEST(TransformationSplitBlockTest,SplitOpPhiWithSinglePredecessor)705 TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) {
706 std::string shader = R"(
707 OpCapability Shader
708 %1 = OpExtInstImport "GLSL.std.450"
709 OpMemoryModel Logical GLSL450
710 OpEntryPoint Fragment %4 "main"
711 OpExecutionMode %4 OriginUpperLeft
712 OpSource ESSL 310
713 OpName %4 "main"
714 OpName %8 "x"
715 OpName %10 "y"
716 OpDecorate %8 RelaxedPrecision
717 OpDecorate %10 RelaxedPrecision
718 OpDecorate %11 RelaxedPrecision
719 %2 = OpTypeVoid
720 %3 = OpTypeFunction %2
721 %6 = OpTypeInt 32 1
722 %7 = OpTypePointer Function %6
723 %9 = OpConstant %6 1
724 %4 = OpFunction %2 None %3
725 %5 = OpLabel
726 %8 = OpVariable %7 Function
727 %10 = OpVariable %7 Function
728 OpStore %8 %9
729 %11 = OpLoad %6 %8
730 OpBranch %20
731 %20 = OpLabel
732 %21 = OpPhi %6 %11 %5
733 OpStore %10 %21
734 OpReturn
735 OpFunctionEnd
736 )";
737
738 const auto env = SPV_ENV_UNIVERSAL_1_3;
739 const auto consumer = nullptr;
740 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
741
742 spvtools::ValidatorOptions validator_options;
743 TransformationContext transformation_context(
744 MakeUnique<FactManager>(context.get()), validator_options);
745 ASSERT_TRUE(TransformationSplitBlock(
746 MakeInstructionDescriptor(21, spv::Op::OpPhi, 0), 100)
747 .IsApplicable(context.get(), transformation_context));
748 // An equivalent transformation to the above, just described with respect to a
749 // different base instruction.
750 auto split = TransformationSplitBlock(
751 MakeInstructionDescriptor(20, spv::Op::OpPhi, 0), 100);
752 ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
753 ApplyAndCheckFreshIds(split, context.get(), &transformation_context);
754 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
755 kConsoleMessageConsumer));
756
757 std::string after_split = R"(
758 OpCapability Shader
759 %1 = OpExtInstImport "GLSL.std.450"
760 OpMemoryModel Logical GLSL450
761 OpEntryPoint Fragment %4 "main"
762 OpExecutionMode %4 OriginUpperLeft
763 OpSource ESSL 310
764 OpName %4 "main"
765 OpName %8 "x"
766 OpName %10 "y"
767 OpDecorate %8 RelaxedPrecision
768 OpDecorate %10 RelaxedPrecision
769 OpDecorate %11 RelaxedPrecision
770 %2 = OpTypeVoid
771 %3 = OpTypeFunction %2
772 %6 = OpTypeInt 32 1
773 %7 = OpTypePointer Function %6
774 %9 = OpConstant %6 1
775 %4 = OpFunction %2 None %3
776 %5 = OpLabel
777 %8 = OpVariable %7 Function
778 %10 = OpVariable %7 Function
779 OpStore %8 %9
780 %11 = OpLoad %6 %8
781 OpBranch %20
782 %20 = OpLabel
783 OpBranch %100
784 %100 = OpLabel
785 %21 = OpPhi %6 %11 %20
786 OpStore %10 %21
787 OpReturn
788 OpFunctionEnd
789 )";
790 ASSERT_TRUE(IsEqual(env, after_split, context.get()));
791 }
792
TEST(TransformationSplitBlockTest,DeadBlockShouldSplitToTwoDeadBlocks)793 TEST(TransformationSplitBlockTest, DeadBlockShouldSplitToTwoDeadBlocks) {
794 // This checks that if a block B is marked as dead, it should split into a
795 // pair of dead blocks.
796 std::string shader = R"(
797 OpCapability Shader
798 %1 = OpExtInstImport "GLSL.std.450"
799 OpMemoryModel Logical GLSL450
800 OpEntryPoint Fragment %4 "main"
801 OpExecutionMode %4 OriginUpperLeft
802 OpSource ESSL 310
803 OpName %4 "main"
804 %2 = OpTypeVoid
805 %3 = OpTypeFunction %2
806 %6 = OpTypeBool
807 %7 = OpConstantFalse %6
808 %4 = OpFunction %2 None %3
809 %5 = OpLabel
810 OpSelectionMerge %9 None
811 OpBranchConditional %7 %8 %9
812 %8 = OpLabel
813 OpBranch %9
814 %9 = OpLabel
815 OpReturn
816 OpFunctionEnd
817 )";
818
819 const auto env = SPV_ENV_UNIVERSAL_1_3;
820 const auto consumer = nullptr;
821 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
822
823 spvtools::ValidatorOptions validator_options;
824 TransformationContext transformation_context(
825 MakeUnique<FactManager>(context.get()), validator_options);
826 // Record the fact that block 8 is dead.
827 transformation_context.GetFactManager()->AddFactBlockIsDead(8);
828
829 auto split = TransformationSplitBlock(
830 MakeInstructionDescriptor(8, spv::Op::OpBranch, 0), 100);
831 ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
832 ApplyAndCheckFreshIds(split, context.get(), &transformation_context);
833 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
834 kConsoleMessageConsumer));
835
836 ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(8));
837 ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(100));
838
839 std::string after_split = R"(
840 OpCapability Shader
841 %1 = OpExtInstImport "GLSL.std.450"
842 OpMemoryModel Logical GLSL450
843 OpEntryPoint Fragment %4 "main"
844 OpExecutionMode %4 OriginUpperLeft
845 OpSource ESSL 310
846 OpName %4 "main"
847 %2 = OpTypeVoid
848 %3 = OpTypeFunction %2
849 %6 = OpTypeBool
850 %7 = OpConstantFalse %6
851 %4 = OpFunction %2 None %3
852 %5 = OpLabel
853 OpSelectionMerge %9 None
854 OpBranchConditional %7 %8 %9
855 %8 = OpLabel
856 OpBranch %100
857 %100 = OpLabel
858 OpBranch %9
859 %9 = OpLabel
860 OpReturn
861 OpFunctionEnd
862 )";
863 ASSERT_TRUE(IsEqual(env, after_split, context.get()));
864 }
865
TEST(TransformationSplitBlockTest,DoNotSplitUseOfOpSampledImage)866 TEST(TransformationSplitBlockTest, DoNotSplitUseOfOpSampledImage) {
867 // This checks that we cannot split the definition of an OpSampledImage
868 // from its use.
869 std::string shader = R"(
870 OpCapability Shader
871 OpCapability SampledBuffer
872 OpCapability ImageBuffer
873 %1 = OpExtInstImport "GLSL.std.450"
874 OpMemoryModel Logical GLSL450
875 OpEntryPoint Fragment %2 "main" %40 %41
876 OpExecutionMode %2 OriginUpperLeft
877 OpSource GLSL 450
878 OpDecorate %40 DescriptorSet 0
879 OpDecorate %40 Binding 69
880 OpDecorate %41 DescriptorSet 0
881 OpDecorate %41 Binding 1
882 %54 = OpTypeFloat 32
883 %76 = OpTypeVector %54 4
884 %55 = OpConstant %54 0
885 %56 = OpTypeVector %54 3
886 %94 = OpTypeVector %54 2
887 %112 = OpConstantComposite %94 %55 %55
888 %57 = OpConstantComposite %56 %55 %55 %55
889 %15 = OpTypeImage %54 2D 2 0 0 1 Unknown
890 %114 = OpTypePointer UniformConstant %15
891 %38 = OpTypeSampler
892 %125 = OpTypePointer UniformConstant %38
893 %132 = OpTypeVoid
894 %133 = OpTypeFunction %132
895 %45 = OpTypeSampledImage %15
896 %40 = OpVariable %114 UniformConstant
897 %41 = OpVariable %125 UniformConstant
898 %2 = OpFunction %132 None %133
899 %164 = OpLabel
900 %184 = OpLoad %15 %40
901 %213 = OpLoad %38 %41
902 %216 = OpSampledImage %45 %184 %213
903 %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55
904 OpReturn
905 OpFunctionEnd
906 )";
907
908 const auto env = SPV_ENV_UNIVERSAL_1_3;
909 const auto consumer = nullptr;
910 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
911
912 spvtools::ValidatorOptions validator_options;
913 TransformationContext transformation_context(
914 MakeUnique<FactManager>(context.get()), validator_options);
915 auto split = TransformationSplitBlock(
916 MakeInstructionDescriptor(217, spv::Op::OpImageSampleImplicitLod, 0),
917 500);
918 ASSERT_FALSE(split.IsApplicable(context.get(), transformation_context));
919 }
920
921 } // namespace
922 } // namespace fuzz
923 } // namespace spvtools
924