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_outline_function.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(TransformationOutlineFunctionTest,TrivialOutline)26 TEST(TransformationOutlineFunctionTest, TrivialOutline) {
27 // This tests outlining of a single, empty basic block.
28
29 std::string shader = R"(
30 OpCapability Shader
31 %1 = OpExtInstImport "GLSL.std.450"
32 OpMemoryModel Logical GLSL450
33 OpEntryPoint Fragment %4 "main"
34 OpExecutionMode %4 OriginUpperLeft
35 OpSource ESSL 310
36 OpName %4 "main"
37 %2 = OpTypeVoid
38 %3 = OpTypeFunction %2
39 %4 = OpFunction %2 None %3
40 %5 = OpLabel
41 OpReturn
42 OpFunctionEnd
43 )";
44
45 const auto env = SPV_ENV_UNIVERSAL_1_4;
46 const auto consumer = nullptr;
47 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
48 spvtools::ValidatorOptions validator_options;
49 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
50 kConsoleMessageConsumer));
51 TransformationContext transformation_context(
52 MakeUnique<FactManager>(context.get()), validator_options);
53 TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200,
54 100, 101, 102, 103,
55 /* not relevant */ 201, {}, {});
56 ASSERT_TRUE(
57 transformation.IsApplicable(context.get(), transformation_context));
58 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
59 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
60 kConsoleMessageConsumer));
61
62 std::string after_transformation = R"(
63 OpCapability Shader
64 %1 = OpExtInstImport "GLSL.std.450"
65 OpMemoryModel Logical GLSL450
66 OpEntryPoint Fragment %4 "main"
67 OpExecutionMode %4 OriginUpperLeft
68 OpSource ESSL 310
69 OpName %4 "main"
70 %2 = OpTypeVoid
71 %3 = OpTypeFunction %2
72 %4 = OpFunction %2 None %3
73 %5 = OpLabel
74 %103 = OpFunctionCall %2 %101
75 OpReturn
76 OpFunctionEnd
77 %101 = OpFunction %2 None %3
78 %102 = OpLabel
79 OpReturn
80 OpFunctionEnd
81 )";
82 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
83 }
84
TEST(TransformationOutlineFunctionTest,DoNotOutlineIfRegionStartsWithOpVariable)85 TEST(TransformationOutlineFunctionTest,
86 DoNotOutlineIfRegionStartsWithOpVariable) {
87 // This checks that we do not outline the first block of a function if it
88 // contains OpVariable.
89
90 std::string shader = R"(
91 OpCapability Shader
92 %1 = OpExtInstImport "GLSL.std.450"
93 OpMemoryModel Logical GLSL450
94 OpEntryPoint Fragment %4 "main"
95 OpExecutionMode %4 OriginUpperLeft
96 OpSource ESSL 310
97 OpName %4 "main"
98 %2 = OpTypeVoid
99 %3 = OpTypeFunction %2
100 %7 = OpTypeBool
101 %8 = OpTypePointer Function %7
102 %4 = OpFunction %2 None %3
103 %5 = OpLabel
104 %6 = OpVariable %8 Function
105 OpReturn
106 OpFunctionEnd
107 )";
108
109 const auto env = SPV_ENV_UNIVERSAL_1_4;
110 const auto consumer = nullptr;
111 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
112 spvtools::ValidatorOptions validator_options;
113 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
114 kConsoleMessageConsumer));
115 TransformationContext transformation_context(
116 MakeUnique<FactManager>(context.get()), validator_options);
117 TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200,
118 100, 101, 102, 103,
119 /* not relevant */ 201, {}, {});
120 ASSERT_FALSE(
121 transformation.IsApplicable(context.get(), transformation_context));
122 }
123
TEST(TransformationOutlineFunctionTest,OutlineInterestingControlFlowNoState)124 TEST(TransformationOutlineFunctionTest, OutlineInterestingControlFlowNoState) {
125 // This tests outlining of some non-trivial control flow, but such that the
126 // basic blocks in the control flow do not actually do anything.
127
128 std::string shader = R"(
129 OpCapability Shader
130 %1 = OpExtInstImport "GLSL.std.450"
131 OpMemoryModel Logical GLSL450
132 OpEntryPoint Fragment %4 "main"
133 OpExecutionMode %4 OriginUpperLeft
134 OpSource ESSL 310
135 OpName %4 "main"
136 %2 = OpTypeVoid
137 %20 = OpTypeBool
138 %21 = OpConstantTrue %20
139 %3 = OpTypeFunction %2
140 %4 = OpFunction %2 None %3
141 %5 = OpLabel
142 OpBranch %6
143 %6 = OpLabel
144 OpBranch %7
145 %7 = OpLabel
146 OpSelectionMerge %9 None
147 OpBranchConditional %21 %8 %9
148 %8 = OpLabel
149 OpBranch %9
150 %9 = OpLabel
151 OpLoopMerge %12 %11 None
152 OpBranch %10
153 %10 = OpLabel
154 OpBranchConditional %21 %11 %12
155 %11 = OpLabel
156 OpBranch %9
157 %12 = OpLabel
158 OpBranch %13
159 %13 = OpLabel
160 OpReturn
161 OpFunctionEnd
162 )";
163
164 const auto env = SPV_ENV_UNIVERSAL_1_4;
165 const auto consumer = nullptr;
166 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
167 spvtools::ValidatorOptions validator_options;
168 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
169 kConsoleMessageConsumer));
170 TransformationContext transformation_context(
171 MakeUnique<FactManager>(context.get()), validator_options);
172 TransformationOutlineFunction transformation(6, 13, /* not relevant */
173 200, 100, 101, 102, 103,
174 /* not relevant */ 201, {}, {});
175 ASSERT_TRUE(
176 transformation.IsApplicable(context.get(), transformation_context));
177 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
178 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
179 kConsoleMessageConsumer));
180
181 std::string after_transformation = R"(
182 OpCapability Shader
183 %1 = OpExtInstImport "GLSL.std.450"
184 OpMemoryModel Logical GLSL450
185 OpEntryPoint Fragment %4 "main"
186 OpExecutionMode %4 OriginUpperLeft
187 OpSource ESSL 310
188 OpName %4 "main"
189 %2 = OpTypeVoid
190 %20 = OpTypeBool
191 %21 = OpConstantTrue %20
192 %3 = OpTypeFunction %2
193 %4 = OpFunction %2 None %3
194 %5 = OpLabel
195 OpBranch %6
196 %6 = OpLabel
197 %103 = OpFunctionCall %2 %101
198 OpReturn
199 OpFunctionEnd
200 %101 = OpFunction %2 None %3
201 %102 = OpLabel
202 OpBranch %7
203 %7 = OpLabel
204 OpSelectionMerge %9 None
205 OpBranchConditional %21 %8 %9
206 %8 = OpLabel
207 OpBranch %9
208 %9 = OpLabel
209 OpLoopMerge %12 %11 None
210 OpBranch %10
211 %10 = OpLabel
212 OpBranchConditional %21 %11 %12
213 %11 = OpLabel
214 OpBranch %9
215 %12 = OpLabel
216 OpBranch %13
217 %13 = OpLabel
218 OpReturn
219 OpFunctionEnd
220 )";
221 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
222 }
223
TEST(TransformationOutlineFunctionTest,OutlineCodeThatGeneratesUnusedIds)224 TEST(TransformationOutlineFunctionTest, OutlineCodeThatGeneratesUnusedIds) {
225 // This tests outlining of a single basic block that does some computation,
226 // but that does not use nor generate ids required outside of the outlined
227 // region.
228
229 std::string shader = R"(
230 OpCapability Shader
231 %1 = OpExtInstImport "GLSL.std.450"
232 OpMemoryModel Logical GLSL450
233 OpEntryPoint Fragment %4 "main"
234 OpExecutionMode %4 OriginUpperLeft
235 OpSource ESSL 310
236 OpName %4 "main"
237 %2 = OpTypeVoid
238 %20 = OpTypeInt 32 1
239 %21 = OpConstant %20 5
240 %3 = OpTypeFunction %2
241 %4 = OpFunction %2 None %3
242 %5 = OpLabel
243 OpBranch %6
244 %6 = OpLabel
245 %7 = OpCopyObject %20 %21
246 %8 = OpCopyObject %20 %21
247 %9 = OpIAdd %20 %7 %8
248 OpReturn
249 OpFunctionEnd
250 )";
251
252 const auto env = SPV_ENV_UNIVERSAL_1_4;
253 const auto consumer = nullptr;
254 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
255 spvtools::ValidatorOptions validator_options;
256 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
257 kConsoleMessageConsumer));
258 TransformationContext transformation_context(
259 MakeUnique<FactManager>(context.get()), validator_options);
260 TransformationOutlineFunction transformation(6, 6, /* not relevant */ 200,
261 100, 101, 102, 103,
262 /* not relevant */ 201, {}, {});
263 ASSERT_TRUE(
264 transformation.IsApplicable(context.get(), transformation_context));
265 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
266 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
267 kConsoleMessageConsumer));
268
269 std::string after_transformation = R"(
270 OpCapability Shader
271 %1 = OpExtInstImport "GLSL.std.450"
272 OpMemoryModel Logical GLSL450
273 OpEntryPoint Fragment %4 "main"
274 OpExecutionMode %4 OriginUpperLeft
275 OpSource ESSL 310
276 OpName %4 "main"
277 %2 = OpTypeVoid
278 %20 = OpTypeInt 32 1
279 %21 = OpConstant %20 5
280 %3 = OpTypeFunction %2
281 %4 = OpFunction %2 None %3
282 %5 = OpLabel
283 OpBranch %6
284 %6 = OpLabel
285 %103 = OpFunctionCall %2 %101
286 OpReturn
287 OpFunctionEnd
288 %101 = OpFunction %2 None %3
289 %102 = OpLabel
290 %7 = OpCopyObject %20 %21
291 %8 = OpCopyObject %20 %21
292 %9 = OpIAdd %20 %7 %8
293 OpReturn
294 OpFunctionEnd
295 )";
296 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
297 }
298
TEST(TransformationOutlineFunctionTest,OutlineCodeThatGeneratesSingleUsedId)299 TEST(TransformationOutlineFunctionTest, OutlineCodeThatGeneratesSingleUsedId) {
300 // This tests outlining of a block that generates an id that is used in a
301 // later block.
302
303 std::string shader = R"(
304 OpCapability Shader
305 %1 = OpExtInstImport "GLSL.std.450"
306 OpMemoryModel Logical GLSL450
307 OpEntryPoint Fragment %4 "main"
308 OpExecutionMode %4 OriginUpperLeft
309 OpSource ESSL 310
310 OpName %4 "main"
311 %2 = OpTypeVoid
312 %20 = OpTypeInt 32 1
313 %21 = OpConstant %20 5
314 %3 = OpTypeFunction %2
315 %4 = OpFunction %2 None %3
316 %5 = OpLabel
317 OpBranch %6
318 %6 = OpLabel
319 %7 = OpCopyObject %20 %21
320 %8 = OpCopyObject %20 %21
321 %9 = OpIAdd %20 %7 %8
322 OpBranch %10
323 %10 = OpLabel
324 %11 = OpCopyObject %20 %9
325 OpReturn
326 OpFunctionEnd
327 )";
328
329 const auto env = SPV_ENV_UNIVERSAL_1_4;
330 const auto consumer = nullptr;
331 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
332 spvtools::ValidatorOptions validator_options;
333 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
334 kConsoleMessageConsumer));
335 TransformationContext transformation_context(
336 MakeUnique<FactManager>(context.get()), validator_options);
337 TransformationOutlineFunction transformation(6, 6, 99, 100, 101, 102, 103,
338 105, {}, {{9, 104}});
339 ASSERT_TRUE(
340 transformation.IsApplicable(context.get(), transformation_context));
341 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
342 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
343 kConsoleMessageConsumer));
344
345 std::string after_transformation = R"(
346 OpCapability Shader
347 %1 = OpExtInstImport "GLSL.std.450"
348 OpMemoryModel Logical GLSL450
349 OpEntryPoint Fragment %4 "main"
350 OpExecutionMode %4 OriginUpperLeft
351 OpSource ESSL 310
352 OpName %4 "main"
353 %2 = OpTypeVoid
354 %20 = OpTypeInt 32 1
355 %21 = OpConstant %20 5
356 %3 = OpTypeFunction %2
357 %99 = OpTypeStruct %20
358 %100 = OpTypeFunction %99
359 %4 = OpFunction %2 None %3
360 %5 = OpLabel
361 OpBranch %6
362 %6 = OpLabel
363 %103 = OpFunctionCall %99 %101
364 %9 = OpCompositeExtract %20 %103 0
365 OpBranch %10
366 %10 = OpLabel
367 %11 = OpCopyObject %20 %9
368 OpReturn
369 OpFunctionEnd
370 %101 = OpFunction %99 None %100
371 %102 = OpLabel
372 %7 = OpCopyObject %20 %21
373 %8 = OpCopyObject %20 %21
374 %104 = OpIAdd %20 %7 %8
375 %105 = OpCompositeConstruct %99 %104
376 OpReturnValue %105
377 OpFunctionEnd
378 )";
379 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
380 }
381
TEST(TransformationOutlineFunctionTest,OutlineDiamondThatGeneratesSeveralIds)382 TEST(TransformationOutlineFunctionTest, OutlineDiamondThatGeneratesSeveralIds) {
383 // This tests outlining of several blocks that generate a number of ids that
384 // are used in later blocks.
385
386 std::string shader = R"(
387 OpCapability Shader
388 %1 = OpExtInstImport "GLSL.std.450"
389 OpMemoryModel Logical GLSL450
390 OpEntryPoint Fragment %4 "main"
391 OpExecutionMode %4 OriginUpperLeft
392 OpSource ESSL 310
393 OpName %4 "main"
394 %2 = OpTypeVoid
395 %20 = OpTypeInt 32 1
396 %21 = OpConstant %20 5
397 %22 = OpTypeBool
398 %3 = OpTypeFunction %2
399 %4 = OpFunction %2 None %3
400 %5 = OpLabel
401 OpBranch %6
402 %6 = OpLabel
403 %7 = OpCopyObject %20 %21
404 %8 = OpCopyObject %20 %21
405 %9 = OpSLessThan %22 %7 %8
406 OpSelectionMerge %12 None
407 OpBranchConditional %9 %10 %11
408 %10 = OpLabel
409 %13 = OpIAdd %20 %7 %8
410 OpBranch %12
411 %11 = OpLabel
412 %14 = OpIAdd %20 %7 %7
413 OpBranch %12
414 %12 = OpLabel
415 %15 = OpPhi %20 %13 %10 %14 %11
416 OpBranch %80
417 %80 = OpLabel
418 OpBranch %16
419 %16 = OpLabel
420 %17 = OpCopyObject %20 %15
421 %18 = OpCopyObject %22 %9
422 %19 = OpIAdd %20 %7 %8
423 OpReturn
424 OpFunctionEnd
425 )";
426
427 const auto env = SPV_ENV_UNIVERSAL_1_4;
428 const auto consumer = nullptr;
429 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
430 spvtools::ValidatorOptions validator_options;
431 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
432 kConsoleMessageConsumer));
433 TransformationContext transformation_context(
434 MakeUnique<FactManager>(context.get()), validator_options);
435 TransformationOutlineFunction transformation(
436 6, 80, 100, 101, 102, 103, 104, 105, {},
437 {{15, 106}, {9, 107}, {7, 108}, {8, 109}});
438 ASSERT_TRUE(
439 transformation.IsApplicable(context.get(), transformation_context));
440 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
441 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
442 kConsoleMessageConsumer));
443
444 std::string after_transformation = 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 OpName %4 "main"
452 %2 = OpTypeVoid
453 %20 = OpTypeInt 32 1
454 %21 = OpConstant %20 5
455 %22 = OpTypeBool
456 %3 = OpTypeFunction %2
457 %100 = OpTypeStruct %20 %20 %22 %20
458 %101 = OpTypeFunction %100
459 %4 = OpFunction %2 None %3
460 %5 = OpLabel
461 OpBranch %6
462 %6 = OpLabel
463 %104 = OpFunctionCall %100 %102
464 %7 = OpCompositeExtract %20 %104 0
465 %8 = OpCompositeExtract %20 %104 1
466 %9 = OpCompositeExtract %22 %104 2
467 %15 = OpCompositeExtract %20 %104 3
468 OpBranch %16
469 %16 = OpLabel
470 %17 = OpCopyObject %20 %15
471 %18 = OpCopyObject %22 %9
472 %19 = OpIAdd %20 %7 %8
473 OpReturn
474 OpFunctionEnd
475 %102 = OpFunction %100 None %101
476 %103 = OpLabel
477 %108 = OpCopyObject %20 %21
478 %109 = OpCopyObject %20 %21
479 %107 = OpSLessThan %22 %108 %109
480 OpSelectionMerge %12 None
481 OpBranchConditional %107 %10 %11
482 %10 = OpLabel
483 %13 = OpIAdd %20 %108 %109
484 OpBranch %12
485 %11 = OpLabel
486 %14 = OpIAdd %20 %108 %108
487 OpBranch %12
488 %12 = OpLabel
489 %106 = OpPhi %20 %13 %10 %14 %11
490 OpBranch %80
491 %80 = OpLabel
492 %105 = OpCompositeConstruct %100 %108 %109 %107 %106
493 OpReturnValue %105
494 OpFunctionEnd
495 )";
496 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
497 }
498
TEST(TransformationOutlineFunctionTest,OutlineCodeThatUsesASingleId)499 TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesASingleId) {
500 // This tests outlining of a block that uses an id defined earlier.
501
502 std::string shader = R"(
503 OpCapability Shader
504 %1 = OpExtInstImport "GLSL.std.450"
505 OpMemoryModel Logical GLSL450
506 OpEntryPoint Fragment %4 "main"
507 OpExecutionMode %4 OriginUpperLeft
508 OpSource ESSL 310
509 OpName %4 "main"
510 %2 = OpTypeVoid
511 %20 = OpTypeInt 32 1
512 %21 = OpConstant %20 5
513 %3 = OpTypeFunction %2
514 %4 = OpFunction %2 None %3
515 %5 = OpLabel
516 %7 = OpCopyObject %20 %21
517 OpBranch %6
518 %6 = OpLabel
519 %8 = OpCopyObject %20 %7
520 OpBranch %10
521 %10 = OpLabel
522 OpReturn
523 OpFunctionEnd
524 )";
525
526 const auto env = SPV_ENV_UNIVERSAL_1_4;
527 const auto consumer = nullptr;
528 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
529 spvtools::ValidatorOptions validator_options;
530 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
531 kConsoleMessageConsumer));
532 TransformationContext transformation_context(
533 MakeUnique<FactManager>(context.get()), validator_options);
534 TransformationOutlineFunction transformation(6, 6, 100, 101, 102, 103, 104,
535 105, {{7, 106}}, {});
536 ASSERT_TRUE(
537 transformation.IsApplicable(context.get(), transformation_context));
538 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
539 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
540 kConsoleMessageConsumer));
541
542 std::string after_transformation = R"(
543 OpCapability Shader
544 %1 = OpExtInstImport "GLSL.std.450"
545 OpMemoryModel Logical GLSL450
546 OpEntryPoint Fragment %4 "main"
547 OpExecutionMode %4 OriginUpperLeft
548 OpSource ESSL 310
549 OpName %4 "main"
550 %2 = OpTypeVoid
551 %20 = OpTypeInt 32 1
552 %21 = OpConstant %20 5
553 %3 = OpTypeFunction %2
554 %101 = OpTypeFunction %2 %20
555 %4 = OpFunction %2 None %3
556 %5 = OpLabel
557 %7 = OpCopyObject %20 %21
558 OpBranch %6
559 %6 = OpLabel
560 %104 = OpFunctionCall %2 %102 %7
561 OpBranch %10
562 %10 = OpLabel
563 OpReturn
564 OpFunctionEnd
565 %102 = OpFunction %2 None %101
566 %106 = OpFunctionParameter %20
567 %103 = OpLabel
568 %8 = OpCopyObject %20 %106
569 OpReturn
570 OpFunctionEnd
571 )";
572 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
573 }
574
TEST(TransformationOutlineFunctionTest,OutlineCodeThatUsesAVariable)575 TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesAVariable) {
576 // This tests outlining of a block that uses a variable.
577
578 std::string shader = R"(
579 OpCapability Shader
580 %1 = OpExtInstImport "GLSL.std.450"
581 OpMemoryModel Logical GLSL450
582 OpEntryPoint Fragment %4 "main"
583 OpExecutionMode %4 OriginUpperLeft
584 OpSource ESSL 310
585 OpName %4 "main"
586 %2 = OpTypeVoid
587 %20 = OpTypeInt 32 1
588 %21 = OpConstant %20 5
589 %3 = OpTypeFunction %2
590 %12 = OpTypePointer Function %20
591 %4 = OpFunction %2 None %3
592 %5 = OpLabel
593 %13 = OpVariable %12 Function
594 OpBranch %6
595 %6 = OpLabel
596 %8 = OpLoad %20 %13
597 OpBranch %10
598 %10 = OpLabel
599 OpReturn
600 OpFunctionEnd
601 )";
602
603 const auto env = SPV_ENV_UNIVERSAL_1_4;
604 const auto consumer = nullptr;
605 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
606 spvtools::ValidatorOptions validator_options;
607 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
608 kConsoleMessageConsumer));
609 TransformationContext transformation_context(
610 MakeUnique<FactManager>(context.get()), validator_options);
611 TransformationOutlineFunction transformation(6, 6, 100, 101, 102, 103, 104,
612 105, {{13, 106}}, {});
613 ASSERT_TRUE(
614 transformation.IsApplicable(context.get(), transformation_context));
615 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
616 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
617 kConsoleMessageConsumer));
618
619 std::string after_transformation = R"(
620 OpCapability Shader
621 %1 = OpExtInstImport "GLSL.std.450"
622 OpMemoryModel Logical GLSL450
623 OpEntryPoint Fragment %4 "main"
624 OpExecutionMode %4 OriginUpperLeft
625 OpSource ESSL 310
626 OpName %4 "main"
627 %2 = OpTypeVoid
628 %20 = OpTypeInt 32 1
629 %21 = OpConstant %20 5
630 %3 = OpTypeFunction %2
631 %12 = OpTypePointer Function %20
632 %101 = OpTypeFunction %2 %12
633 %4 = OpFunction %2 None %3
634 %5 = OpLabel
635 %13 = OpVariable %12 Function
636 OpBranch %6
637 %6 = OpLabel
638 %104 = OpFunctionCall %2 %102 %13
639 OpBranch %10
640 %10 = OpLabel
641 OpReturn
642 OpFunctionEnd
643 %102 = OpFunction %2 None %101
644 %106 = OpFunctionParameter %12
645 %103 = OpLabel
646 %8 = OpLoad %20 %106
647 OpReturn
648 OpFunctionEnd
649 )";
650 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
651 }
652
TEST(TransformationOutlineFunctionTest,OutlineCodeThatUsesAParameter)653 TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesAParameter) {
654 // This tests outlining of a block that uses a function parameter.
655
656 std::string shader = R"(
657 OpCapability Shader
658 %1 = OpExtInstImport "GLSL.std.450"
659 OpMemoryModel Logical GLSL450
660 OpEntryPoint Fragment %4 "main"
661 OpExecutionMode %4 OriginUpperLeft
662 OpSource ESSL 310
663 OpName %4 "main"
664 OpName %10 "foo(i1;"
665 OpName %9 "x"
666 OpName %18 "param"
667 %2 = OpTypeVoid
668 %3 = OpTypeFunction %2
669 %6 = OpTypeInt 32 1
670 %7 = OpTypePointer Function %6
671 %8 = OpTypeFunction %6 %7
672 %13 = OpConstant %6 1
673 %17 = OpConstant %6 3
674 %4 = OpFunction %2 None %3
675 %5 = OpLabel
676 %18 = OpVariable %7 Function
677 OpStore %18 %17
678 %19 = OpFunctionCall %6 %10 %18
679 OpReturn
680 OpFunctionEnd
681 %10 = OpFunction %6 None %8
682 %9 = OpFunctionParameter %7
683 %11 = OpLabel
684 %12 = OpLoad %6 %9
685 %14 = OpIAdd %6 %12 %13
686 OpReturnValue %14
687 OpFunctionEnd
688 )";
689
690 const auto env = SPV_ENV_UNIVERSAL_1_4;
691 const auto consumer = nullptr;
692 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
693 spvtools::ValidatorOptions validator_options;
694 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
695 kConsoleMessageConsumer));
696 TransformationContext transformation_context(
697 MakeUnique<FactManager>(context.get()), validator_options);
698 TransformationOutlineFunction transformation(11, 11, 100, 101, 102, 103, 104,
699 105, {{9, 106}}, {{14, 107}});
700 ASSERT_TRUE(
701 transformation.IsApplicable(context.get(), transformation_context));
702 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
703 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
704 kConsoleMessageConsumer));
705
706 std::string after_transformation = 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 %10 "foo(i1;"
715 OpName %9 "x"
716 OpName %18 "param"
717 %2 = OpTypeVoid
718 %3 = OpTypeFunction %2
719 %6 = OpTypeInt 32 1
720 %7 = OpTypePointer Function %6
721 %8 = OpTypeFunction %6 %7
722 %13 = OpConstant %6 1
723 %17 = OpConstant %6 3
724 %100 = OpTypeStruct %6
725 %101 = OpTypeFunction %100 %7
726 %4 = OpFunction %2 None %3
727 %5 = OpLabel
728 %18 = OpVariable %7 Function
729 OpStore %18 %17
730 %19 = OpFunctionCall %6 %10 %18
731 OpReturn
732 OpFunctionEnd
733 %10 = OpFunction %6 None %8
734 %9 = OpFunctionParameter %7
735 %11 = OpLabel
736 %104 = OpFunctionCall %100 %102 %9
737 %14 = OpCompositeExtract %6 %104 0
738 OpReturnValue %14
739 OpFunctionEnd
740 %102 = OpFunction %100 None %101
741 %106 = OpFunctionParameter %7
742 %103 = OpLabel
743 %12 = OpLoad %6 %106
744 %107 = OpIAdd %6 %12 %13
745 %105 = OpCompositeConstruct %100 %107
746 OpReturnValue %105
747 OpFunctionEnd
748 )";
749 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
750 }
751
TEST(TransformationOutlineFunctionTest,DoNotOutlineIfLoopMergeIsOutsideRegion)752 TEST(TransformationOutlineFunctionTest,
753 DoNotOutlineIfLoopMergeIsOutsideRegion) {
754 std::string shader = R"(
755 OpCapability Shader
756 %1 = OpExtInstImport "GLSL.std.450"
757 OpMemoryModel Logical GLSL450
758 OpEntryPoint Fragment %4 "main"
759 OpExecutionMode %4 OriginUpperLeft
760 OpSource ESSL 310
761 OpName %4 "main"
762 %2 = OpTypeVoid
763 %3 = OpTypeFunction %2
764 %9 = OpTypeBool
765 %10 = OpConstantTrue %9
766 %4 = OpFunction %2 None %3
767 %5 = OpLabel
768 OpBranch %6
769 %6 = OpLabel
770 OpLoopMerge %7 %8 None
771 OpBranch %8
772 %8 = OpLabel
773 OpBranchConditional %10 %6 %7
774 %7 = OpLabel
775 OpReturn
776 OpFunctionEnd
777 )";
778
779 const auto env = SPV_ENV_UNIVERSAL_1_4;
780 const auto consumer = nullptr;
781 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
782 spvtools::ValidatorOptions validator_options;
783 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
784 kConsoleMessageConsumer));
785 TransformationContext transformation_context(
786 MakeUnique<FactManager>(context.get()), validator_options);
787 TransformationOutlineFunction transformation(6, 8, 100, 101, 102, 103, 104,
788 105, {}, {});
789 ASSERT_FALSE(
790 transformation.IsApplicable(context.get(), transformation_context));
791 }
792
TEST(TransformationOutlineFunctionTest,DoNotOutlineIfRegionInvolvesReturn)793 TEST(TransformationOutlineFunctionTest, DoNotOutlineIfRegionInvolvesReturn) {
794 std::string shader = R"(
795 OpCapability Shader
796 %1 = OpExtInstImport "GLSL.std.450"
797 OpMemoryModel Logical GLSL450
798 OpEntryPoint Fragment %4 "main"
799 OpExecutionMode %4 OriginUpperLeft
800 OpSource ESSL 310
801 OpName %4 "main"
802 %2 = OpTypeVoid
803 %20 = OpTypeBool
804 %21 = OpConstantTrue %20
805 %3 = OpTypeFunction %2
806 %4 = OpFunction %2 None %3
807 %5 = OpLabel
808 OpBranch %6
809 %6 = OpLabel
810 OpBranch %7
811 %7 = OpLabel
812 OpSelectionMerge %10 None
813 OpBranchConditional %21 %8 %9
814 %8 = OpLabel
815 OpReturn
816 %9 = OpLabel
817 OpBranch %10
818 %10 = OpLabel
819 OpBranch %11
820 %11 = OpLabel
821 OpBranch %12
822 %12 = OpLabel
823 OpReturn
824 OpFunctionEnd
825 )";
826
827 const auto env = SPV_ENV_UNIVERSAL_1_4;
828 const auto consumer = nullptr;
829 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
830 spvtools::ValidatorOptions validator_options;
831 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
832 kConsoleMessageConsumer));
833 TransformationContext transformation_context(
834 MakeUnique<FactManager>(context.get()), validator_options);
835 TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200,
836 100, 101, 102, 103,
837 /* not relevant */ 201, {}, {});
838 ASSERT_FALSE(
839 transformation.IsApplicable(context.get(), transformation_context));
840 }
841
TEST(TransformationOutlineFunctionTest,DoNotOutlineIfRegionInvolvesKill)842 TEST(TransformationOutlineFunctionTest, DoNotOutlineIfRegionInvolvesKill) {
843 std::string shader = R"(
844 OpCapability Shader
845 %1 = OpExtInstImport "GLSL.std.450"
846 OpMemoryModel Logical GLSL450
847 OpEntryPoint Fragment %4 "main"
848 OpExecutionMode %4 OriginUpperLeft
849 OpSource ESSL 310
850 OpName %4 "main"
851 %2 = OpTypeVoid
852 %20 = OpTypeBool
853 %21 = OpConstantTrue %20
854 %3 = OpTypeFunction %2
855 %4 = OpFunction %2 None %3
856 %5 = OpLabel
857 OpBranch %6
858 %6 = OpLabel
859 OpBranch %7
860 %7 = OpLabel
861 OpSelectionMerge %10 None
862 OpBranchConditional %21 %8 %9
863 %8 = OpLabel
864 OpKill
865 %9 = OpLabel
866 OpBranch %10
867 %10 = OpLabel
868 OpBranch %11
869 %11 = OpLabel
870 OpBranch %12
871 %12 = OpLabel
872 OpReturn
873 OpFunctionEnd
874 )";
875
876 const auto env = SPV_ENV_UNIVERSAL_1_4;
877 const auto consumer = nullptr;
878 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
879 spvtools::ValidatorOptions validator_options;
880 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
881 kConsoleMessageConsumer));
882 TransformationContext transformation_context(
883 MakeUnique<FactManager>(context.get()), validator_options);
884 TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200,
885 100, 101, 102, 103,
886 /* not relevant */ 201, {}, {});
887 ASSERT_FALSE(
888 transformation.IsApplicable(context.get(), transformation_context));
889 }
890
TEST(TransformationOutlineFunctionTest,DoNotOutlineIfRegionInvolvesUnreachable)891 TEST(TransformationOutlineFunctionTest,
892 DoNotOutlineIfRegionInvolvesUnreachable) {
893 std::string shader = R"(
894 OpCapability Shader
895 %1 = OpExtInstImport "GLSL.std.450"
896 OpMemoryModel Logical GLSL450
897 OpEntryPoint Fragment %4 "main"
898 OpExecutionMode %4 OriginUpperLeft
899 OpSource ESSL 310
900 OpName %4 "main"
901 %2 = OpTypeVoid
902 %20 = OpTypeBool
903 %21 = OpConstantTrue %20
904 %3 = OpTypeFunction %2
905 %4 = OpFunction %2 None %3
906 %5 = OpLabel
907 OpBranch %6
908 %6 = OpLabel
909 OpBranch %7
910 %7 = OpLabel
911 OpSelectionMerge %10 None
912 OpBranchConditional %21 %8 %9
913 %8 = OpLabel
914 OpBranch %10
915 %9 = OpLabel
916 OpUnreachable
917 %10 = OpLabel
918 OpBranch %11
919 %11 = OpLabel
920 OpBranch %12
921 %12 = OpLabel
922 OpReturn
923 OpFunctionEnd
924 )";
925
926 const auto env = SPV_ENV_UNIVERSAL_1_4;
927 const auto consumer = nullptr;
928 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
929 spvtools::ValidatorOptions validator_options;
930 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
931 kConsoleMessageConsumer));
932 TransformationContext transformation_context(
933 MakeUnique<FactManager>(context.get()), validator_options);
934 TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200,
935 100, 101, 102, 103,
936 /* not relevant */ 201, {}, {});
937 ASSERT_FALSE(
938 transformation.IsApplicable(context.get(), transformation_context));
939 }
940
TEST(TransformationOutlineFunctionTest,DoNotOutlineIfSelectionMergeIsOutsideRegion)941 TEST(TransformationOutlineFunctionTest,
942 DoNotOutlineIfSelectionMergeIsOutsideRegion) {
943 std::string shader = R"(
944 OpCapability Shader
945 %1 = OpExtInstImport "GLSL.std.450"
946 OpMemoryModel Logical GLSL450
947 OpEntryPoint Fragment %4 "main"
948 OpExecutionMode %4 OriginUpperLeft
949 OpSource ESSL 310
950 OpName %4 "main"
951 %2 = OpTypeVoid
952 %3 = OpTypeFunction %2
953 %9 = OpTypeBool
954 %10 = OpConstantTrue %9
955 %4 = OpFunction %2 None %3
956 %5 = OpLabel
957 OpBranch %6
958 %6 = OpLabel
959 OpSelectionMerge %7 None
960 OpBranchConditional %10 %8 %7
961 %8 = OpLabel
962 OpBranch %7
963 %7 = OpLabel
964 OpReturn
965 OpFunctionEnd
966 )";
967
968 const auto env = SPV_ENV_UNIVERSAL_1_4;
969 const auto consumer = nullptr;
970 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
971 spvtools::ValidatorOptions validator_options;
972 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
973 kConsoleMessageConsumer));
974 TransformationContext transformation_context(
975 MakeUnique<FactManager>(context.get()), validator_options);
976 TransformationOutlineFunction transformation(6, 8, 100, 101, 102, 103, 104,
977 105, {}, {});
978 ASSERT_FALSE(
979 transformation.IsApplicable(context.get(), transformation_context));
980 }
981
TEST(TransformationOutlineFunctionTest,DoNotOutlineIfLoopHeadIsOutsideRegion)982 TEST(TransformationOutlineFunctionTest, DoNotOutlineIfLoopHeadIsOutsideRegion) {
983 std::string shader = R"(
984 OpCapability Shader
985 %1 = OpExtInstImport "GLSL.std.450"
986 OpMemoryModel Logical GLSL450
987 OpEntryPoint Fragment %4 "main"
988 OpExecutionMode %4 OriginUpperLeft
989 OpSource ESSL 310
990 OpName %4 "main"
991 %2 = OpTypeVoid
992 %3 = OpTypeFunction %2
993 %9 = OpTypeBool
994 %10 = OpConstantTrue %9
995 %4 = OpFunction %2 None %3
996 %5 = OpLabel
997 OpBranch %6
998 %6 = OpLabel
999 OpLoopMerge %8 %11 None
1000 OpBranch %7
1001 %7 = OpLabel
1002 OpBranchConditional %10 %11 %8
1003 %11 = OpLabel
1004 OpBranch %6
1005 %8 = OpLabel
1006 OpReturn
1007 OpFunctionEnd
1008 )";
1009
1010 const auto env = SPV_ENV_UNIVERSAL_1_4;
1011 const auto consumer = nullptr;
1012 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1013 spvtools::ValidatorOptions validator_options;
1014 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1015 kConsoleMessageConsumer));
1016 TransformationContext transformation_context(
1017 MakeUnique<FactManager>(context.get()), validator_options);
1018 TransformationOutlineFunction transformation(7, 8, 100, 101, 102, 103, 104,
1019 105, {}, {});
1020 ASSERT_FALSE(
1021 transformation.IsApplicable(context.get(), transformation_context));
1022 }
1023
TEST(TransformationOutlineFunctionTest,DoNotOutlineIfLoopContinueIsOutsideRegion)1024 TEST(TransformationOutlineFunctionTest,
1025 DoNotOutlineIfLoopContinueIsOutsideRegion) {
1026 std::string shader = R"(
1027 OpCapability Shader
1028 %1 = OpExtInstImport "GLSL.std.450"
1029 OpMemoryModel Logical GLSL450
1030 OpEntryPoint Fragment %4 "main"
1031 OpExecutionMode %4 OriginUpperLeft
1032 OpSource ESSL 310
1033 OpName %4 "main"
1034 %2 = OpTypeVoid
1035 %3 = OpTypeFunction %2
1036 %9 = OpTypeBool
1037 %10 = OpConstantTrue %9
1038 %4 = OpFunction %2 None %3
1039 %5 = OpLabel
1040 OpBranch %6
1041 %6 = OpLabel
1042 OpLoopMerge %7 %8 None
1043 OpBranch %7
1044 %8 = OpLabel
1045 OpBranch %6
1046 %7 = OpLabel
1047 OpReturn
1048 OpFunctionEnd
1049 )";
1050
1051 const auto env = SPV_ENV_UNIVERSAL_1_4;
1052 const auto consumer = nullptr;
1053 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1054 spvtools::ValidatorOptions validator_options;
1055 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1056 kConsoleMessageConsumer));
1057 TransformationContext transformation_context(
1058 MakeUnique<FactManager>(context.get()), validator_options);
1059 TransformationOutlineFunction transformation(6, 7, 100, 101, 102, 103, 104,
1060 105, {}, {});
1061 ASSERT_FALSE(
1062 transformation.IsApplicable(context.get(), transformation_context));
1063 }
1064
TEST(TransformationOutlineFunctionTest,DoNotOutlineWithLoopCarriedPhiDependence)1065 TEST(TransformationOutlineFunctionTest,
1066 DoNotOutlineWithLoopCarriedPhiDependence) {
1067 std::string shader = R"(
1068 OpCapability Shader
1069 %1 = OpExtInstImport "GLSL.std.450"
1070 OpMemoryModel Logical GLSL450
1071 OpEntryPoint Fragment %4 "main"
1072 OpExecutionMode %4 OriginUpperLeft
1073 OpSource ESSL 310
1074 OpName %4 "main"
1075 %2 = OpTypeVoid
1076 %3 = OpTypeFunction %2
1077 %9 = OpTypeBool
1078 %10 = OpConstantTrue %9
1079 %4 = OpFunction %2 None %3
1080 %5 = OpLabel
1081 OpBranch %6
1082 %6 = OpLabel
1083 %12 = OpPhi %9 %10 %5 %13 %8
1084 OpLoopMerge %7 %8 None
1085 OpBranch %8
1086 %8 = OpLabel
1087 %13 = OpCopyObject %9 %10
1088 OpBranchConditional %10 %6 %7
1089 %7 = OpLabel
1090 OpReturn
1091 OpFunctionEnd
1092 )";
1093
1094 const auto env = SPV_ENV_UNIVERSAL_1_4;
1095 const auto consumer = nullptr;
1096 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1097 spvtools::ValidatorOptions validator_options;
1098 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1099 kConsoleMessageConsumer));
1100 TransformationContext transformation_context(
1101 MakeUnique<FactManager>(context.get()), validator_options);
1102 TransformationOutlineFunction transformation(6, 7, 100, 101, 102, 103, 104,
1103 105, {}, {});
1104 ASSERT_FALSE(
1105 transformation.IsApplicable(context.get(), transformation_context));
1106 }
1107
TEST(TransformationOutlineFunctionTest,DoNotOutlineSelectionHeaderNotInRegion)1108 TEST(TransformationOutlineFunctionTest,
1109 DoNotOutlineSelectionHeaderNotInRegion) {
1110 std::string shader = R"(
1111 OpCapability Shader
1112 %1 = OpExtInstImport "GLSL.std.450"
1113 OpMemoryModel Logical GLSL450
1114 OpEntryPoint Fragment %4 "main"
1115 OpExecutionMode %4 OriginUpperLeft
1116 OpSource ESSL 310
1117 OpName %4 "main"
1118 %2 = OpTypeVoid
1119 %3 = OpTypeFunction %2
1120 %6 = OpTypeBool
1121 %7 = OpConstantTrue %6
1122 %4 = OpFunction %2 None %3
1123 %5 = OpLabel
1124 OpSelectionMerge %10 None
1125 OpBranchConditional %7 %8 %8
1126 %8 = OpLabel
1127 OpBranch %9
1128 %9 = OpLabel
1129 OpBranch %10
1130 %10 = OpLabel
1131 OpBranch %11
1132 %11 = OpLabel
1133 OpReturn
1134 OpFunctionEnd
1135 )";
1136
1137 const auto env = SPV_ENV_UNIVERSAL_1_4;
1138 const auto consumer = nullptr;
1139 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1140 spvtools::ValidatorOptions validator_options;
1141 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1142 kConsoleMessageConsumer));
1143 TransformationContext transformation_context(
1144 MakeUnique<FactManager>(context.get()), validator_options);
1145 TransformationOutlineFunction transformation(8, 11, 100, 101, 102, 103, 104,
1146 105, {}, {});
1147 ASSERT_FALSE(
1148 transformation.IsApplicable(context.get(), transformation_context));
1149 }
1150
TEST(TransformationOutlineFunctionTest,OutlineRegionEndingWithReturnVoid)1151 TEST(TransformationOutlineFunctionTest, OutlineRegionEndingWithReturnVoid) {
1152 std::string shader = R"(
1153 OpCapability Shader
1154 %1 = OpExtInstImport "GLSL.std.450"
1155 OpMemoryModel Logical GLSL450
1156 OpEntryPoint Fragment %4 "main"
1157 OpExecutionMode %4 OriginUpperLeft
1158 OpSource ESSL 310
1159 %20 = OpTypeInt 32 0
1160 %21 = OpConstant %20 1
1161 %2 = OpTypeVoid
1162 %3 = OpTypeFunction %2
1163 %4 = OpFunction %2 None %3
1164 %5 = OpLabel
1165 %22 = OpCopyObject %20 %21
1166 OpBranch %54
1167 %54 = OpLabel
1168 OpBranch %57
1169 %57 = OpLabel
1170 %23 = OpCopyObject %20 %22
1171 OpBranch %58
1172 %58 = OpLabel
1173 OpReturn
1174 OpFunctionEnd
1175 )";
1176
1177 const auto env = SPV_ENV_UNIVERSAL_1_5;
1178 const auto consumer = nullptr;
1179 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1180 spvtools::ValidatorOptions validator_options;
1181 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1182 kConsoleMessageConsumer));
1183 TransformationContext transformation_context(
1184 MakeUnique<FactManager>(context.get()), validator_options);
1185 TransformationOutlineFunction transformation(
1186 /*entry_block*/ 54,
1187 /*exit_block*/ 58,
1188 /*new_function_struct_return_type_id*/ 200,
1189 /*new_function_type_id*/ 201,
1190 /*new_function_id*/ 202,
1191 /*new_function_region_entry_block*/ 203,
1192 /*new_caller_result_id*/ 204,
1193 /*new_callee_result_id*/ 205,
1194 /*input_id_to_fresh_id*/ {{22, 206}},
1195 /*output_id_to_fresh_id*/ {});
1196
1197 ASSERT_TRUE(
1198 transformation.IsApplicable(context.get(), transformation_context));
1199 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
1200 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1201 kConsoleMessageConsumer));
1202
1203 std::string after_transformation = R"(
1204 OpCapability Shader
1205 %1 = OpExtInstImport "GLSL.std.450"
1206 OpMemoryModel Logical GLSL450
1207 OpEntryPoint Fragment %4 "main"
1208 OpExecutionMode %4 OriginUpperLeft
1209 OpSource ESSL 310
1210 %20 = OpTypeInt 32 0
1211 %21 = OpConstant %20 1
1212 %2 = OpTypeVoid
1213 %3 = OpTypeFunction %2
1214 %201 = OpTypeFunction %2 %20
1215 %4 = OpFunction %2 None %3
1216 %5 = OpLabel
1217 %22 = OpCopyObject %20 %21
1218 OpBranch %54
1219 %54 = OpLabel
1220 %204 = OpFunctionCall %2 %202 %22
1221 OpReturn
1222 OpFunctionEnd
1223 %202 = OpFunction %2 None %201
1224 %206 = OpFunctionParameter %20
1225 %203 = OpLabel
1226 OpBranch %57
1227 %57 = OpLabel
1228 %23 = OpCopyObject %20 %206
1229 OpBranch %58
1230 %58 = OpLabel
1231 OpReturn
1232 OpFunctionEnd
1233 )";
1234 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1235 }
1236
TEST(TransformationOutlineFunctionTest,OutlineRegionEndingWithReturnValue)1237 TEST(TransformationOutlineFunctionTest, OutlineRegionEndingWithReturnValue) {
1238 std::string shader = R"(
1239 OpCapability Shader
1240 %1 = OpExtInstImport "GLSL.std.450"
1241 OpMemoryModel Logical GLSL450
1242 OpEntryPoint Fragment %4 "main"
1243 OpExecutionMode %4 OriginUpperLeft
1244 OpSource ESSL 310
1245 %20 = OpTypeInt 32 0
1246 %21 = OpConstant %20 1
1247 %2 = OpTypeVoid
1248 %3 = OpTypeFunction %2
1249 %30 = OpTypeFunction %20
1250 %4 = OpFunction %2 None %3
1251 %5 = OpLabel
1252 %6 = OpFunctionCall %20 %100
1253 OpReturn
1254 OpFunctionEnd
1255 %100 = OpFunction %20 None %30
1256 %8 = OpLabel
1257 %31 = OpCopyObject %20 %21
1258 OpBranch %9
1259 %9 = OpLabel
1260 %32 = OpCopyObject %20 %31
1261 OpBranch %10
1262 %10 = OpLabel
1263 OpReturnValue %32
1264 OpFunctionEnd
1265 )";
1266
1267 const auto env = SPV_ENV_UNIVERSAL_1_5;
1268 const auto consumer = nullptr;
1269 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1270 spvtools::ValidatorOptions validator_options;
1271 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1272 kConsoleMessageConsumer));
1273 TransformationContext transformation_context(
1274 MakeUnique<FactManager>(context.get()), validator_options);
1275 TransformationOutlineFunction transformation(
1276 /*entry_block*/ 9,
1277 /*exit_block*/ 10,
1278 /*new_function_struct_return_type_id*/ 200,
1279 /*new_function_type_id*/ 201,
1280 /*new_function_id*/ 202,
1281 /*new_function_region_entry_block*/ 203,
1282 /*new_caller_result_id*/ 204,
1283 /*new_callee_result_id*/ 205,
1284 /*input_id_to_fresh_id*/ {{31, 206}},
1285 /*output_id_to_fresh_id*/ {{32, 207}});
1286
1287 ASSERT_TRUE(
1288 transformation.IsApplicable(context.get(), transformation_context));
1289 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
1290 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1291 kConsoleMessageConsumer));
1292
1293 std::string after_transformation = R"(
1294 OpCapability Shader
1295 %1 = OpExtInstImport "GLSL.std.450"
1296 OpMemoryModel Logical GLSL450
1297 OpEntryPoint Fragment %4 "main"
1298 OpExecutionMode %4 OriginUpperLeft
1299 OpSource ESSL 310
1300 %20 = OpTypeInt 32 0
1301 %21 = OpConstant %20 1
1302 %2 = OpTypeVoid
1303 %3 = OpTypeFunction %2
1304 %30 = OpTypeFunction %20
1305 %200 = OpTypeStruct %20
1306 %201 = OpTypeFunction %200 %20
1307 %4 = OpFunction %2 None %3
1308 %5 = OpLabel
1309 %6 = OpFunctionCall %20 %100
1310 OpReturn
1311 OpFunctionEnd
1312 %100 = OpFunction %20 None %30
1313 %8 = OpLabel
1314 %31 = OpCopyObject %20 %21
1315 OpBranch %9
1316 %9 = OpLabel
1317 %204 = OpFunctionCall %200 %202 %31
1318 %32 = OpCompositeExtract %20 %204 0
1319 OpReturnValue %32
1320 OpFunctionEnd
1321 %202 = OpFunction %200 None %201
1322 %206 = OpFunctionParameter %20
1323 %203 = OpLabel
1324 %207 = OpCopyObject %20 %206
1325 OpBranch %10
1326 %10 = OpLabel
1327 %205 = OpCompositeConstruct %200 %207
1328 OpReturnValue %205
1329 OpFunctionEnd
1330 )";
1331 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1332 }
1333
TEST(TransformationOutlineFunctionTest,OutlineRegionEndingWithConditionalBranch)1334 TEST(TransformationOutlineFunctionTest,
1335 OutlineRegionEndingWithConditionalBranch) {
1336 std::string shader = R"(
1337 OpCapability Shader
1338 %1 = OpExtInstImport "GLSL.std.450"
1339 OpMemoryModel Logical GLSL450
1340 OpEntryPoint Fragment %4 "main"
1341 OpExecutionMode %4 OriginUpperLeft
1342 OpSource ESSL 310
1343 %20 = OpTypeBool
1344 %21 = OpConstantTrue %20
1345 %2 = OpTypeVoid
1346 %3 = OpTypeFunction %2
1347 %4 = OpFunction %2 None %3
1348 %5 = OpLabel
1349 OpBranch %54
1350 %54 = OpLabel
1351 %6 = OpCopyObject %20 %21
1352 OpSelectionMerge %8 None
1353 OpBranchConditional %6 %7 %8
1354 %7 = OpLabel
1355 OpBranch %8
1356 %8 = OpLabel
1357 OpReturn
1358 OpFunctionEnd
1359 )";
1360
1361 const auto env = SPV_ENV_UNIVERSAL_1_5;
1362 const auto consumer = nullptr;
1363 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1364 spvtools::ValidatorOptions validator_options;
1365 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1366 kConsoleMessageConsumer));
1367 TransformationContext transformation_context(
1368 MakeUnique<FactManager>(context.get()), validator_options);
1369 TransformationOutlineFunction transformation(
1370 /*entry_block*/ 54,
1371 /*exit_block*/ 54,
1372 /*new_function_struct_return_type_id*/ 200,
1373 /*new_function_type_id*/ 201,
1374 /*new_function_id*/ 202,
1375 /*new_function_region_entry_block*/ 203,
1376 /*new_caller_result_id*/ 204,
1377 /*new_callee_result_id*/ 205,
1378 /*input_id_to_fresh_id*/ {{}},
1379 /*output_id_to_fresh_id*/ {{6, 206}});
1380
1381 ASSERT_TRUE(
1382 transformation.IsApplicable(context.get(), transformation_context));
1383 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
1384 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1385 kConsoleMessageConsumer));
1386
1387 std::string after_transformation = R"(
1388 OpCapability Shader
1389 %1 = OpExtInstImport "GLSL.std.450"
1390 OpMemoryModel Logical GLSL450
1391 OpEntryPoint Fragment %4 "main"
1392 OpExecutionMode %4 OriginUpperLeft
1393 OpSource ESSL 310
1394 %20 = OpTypeBool
1395 %21 = OpConstantTrue %20
1396 %2 = OpTypeVoid
1397 %3 = OpTypeFunction %2
1398 %200 = OpTypeStruct %20
1399 %201 = OpTypeFunction %200
1400 %4 = OpFunction %2 None %3
1401 %5 = OpLabel
1402 OpBranch %54
1403 %54 = OpLabel
1404 %204 = OpFunctionCall %200 %202
1405 %6 = OpCompositeExtract %20 %204 0
1406 OpSelectionMerge %8 None
1407 OpBranchConditional %6 %7 %8
1408 %7 = OpLabel
1409 OpBranch %8
1410 %8 = OpLabel
1411 OpReturn
1412 OpFunctionEnd
1413 %202 = OpFunction %200 None %201
1414 %203 = OpLabel
1415 %206 = OpCopyObject %20 %21
1416 %205 = OpCompositeConstruct %200 %206
1417 OpReturnValue %205
1418 OpFunctionEnd
1419 )";
1420 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1421 }
1422
TEST(TransformationOutlineFunctionTest,OutlineRegionEndingWithConditionalBranch2)1423 TEST(TransformationOutlineFunctionTest,
1424 OutlineRegionEndingWithConditionalBranch2) {
1425 std::string shader = R"(
1426 OpCapability Shader
1427 %1 = OpExtInstImport "GLSL.std.450"
1428 OpMemoryModel Logical GLSL450
1429 OpEntryPoint Fragment %4 "main"
1430 OpExecutionMode %4 OriginUpperLeft
1431 OpSource ESSL 310
1432 %20 = OpTypeBool
1433 %21 = OpConstantTrue %20
1434 %2 = OpTypeVoid
1435 %3 = OpTypeFunction %2
1436 %4 = OpFunction %2 None %3
1437 %5 = OpLabel
1438 %6 = OpCopyObject %20 %21
1439 OpBranch %54
1440 %54 = OpLabel
1441 OpSelectionMerge %8 None
1442 OpBranchConditional %6 %7 %8
1443 %7 = OpLabel
1444 OpBranch %8
1445 %8 = OpLabel
1446 OpReturn
1447 OpFunctionEnd
1448 )";
1449
1450 const auto env = SPV_ENV_UNIVERSAL_1_5;
1451 const auto consumer = nullptr;
1452 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1453 spvtools::ValidatorOptions validator_options;
1454 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1455 kConsoleMessageConsumer));
1456 TransformationContext transformation_context(
1457 MakeUnique<FactManager>(context.get()), validator_options);
1458 TransformationOutlineFunction transformation(
1459 /*entry_block*/ 54,
1460 /*exit_block*/ 54,
1461 /*new_function_struct_return_type_id*/ 200,
1462 /*new_function_type_id*/ 201,
1463 /*new_function_id*/ 202,
1464 /*new_function_region_entry_block*/ 203,
1465 /*new_caller_result_id*/ 204,
1466 /*new_callee_result_id*/ 205,
1467 /*input_id_to_fresh_id*/ {},
1468 /*output_id_to_fresh_id*/ {});
1469
1470 ASSERT_TRUE(
1471 transformation.IsApplicable(context.get(), transformation_context));
1472 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
1473 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1474 kConsoleMessageConsumer));
1475
1476 std::string after_transformation = R"(
1477 OpCapability Shader
1478 %1 = OpExtInstImport "GLSL.std.450"
1479 OpMemoryModel Logical GLSL450
1480 OpEntryPoint Fragment %4 "main"
1481 OpExecutionMode %4 OriginUpperLeft
1482 OpSource ESSL 310
1483 %20 = OpTypeBool
1484 %21 = OpConstantTrue %20
1485 %2 = OpTypeVoid
1486 %3 = OpTypeFunction %2
1487 %4 = OpFunction %2 None %3
1488 %5 = OpLabel
1489 %6 = OpCopyObject %20 %21
1490 OpBranch %54
1491 %54 = OpLabel
1492 %204 = OpFunctionCall %2 %202
1493 OpSelectionMerge %8 None
1494 OpBranchConditional %6 %7 %8
1495 %7 = OpLabel
1496 OpBranch %8
1497 %8 = OpLabel
1498 OpReturn
1499 OpFunctionEnd
1500 %202 = OpFunction %2 None %3
1501 %203 = OpLabel
1502 OpReturn
1503 OpFunctionEnd
1504 )";
1505 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1506 }
1507
TEST(TransformationOutlineFunctionTest,DoNotOutlineRegionThatStartsWithOpPhi)1508 TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatStartsWithOpPhi) {
1509 std::string shader = R"(
1510 OpCapability Shader
1511 %1 = OpExtInstImport "GLSL.std.450"
1512 OpMemoryModel Logical GLSL450
1513 OpEntryPoint Fragment %4 "main"
1514 OpExecutionMode %4 OriginUpperLeft
1515 OpSource ESSL 310
1516 OpName %4 "main"
1517 %2 = OpTypeVoid
1518 %3 = OpTypeFunction %2
1519 %6 = OpTypeBool
1520 %7 = OpConstantTrue %6
1521 %4 = OpFunction %2 None %3
1522 %5 = OpLabel
1523 OpBranch %21
1524 %21 = OpLabel
1525 %22 = OpPhi %6 %7 %5
1526 %23 = OpCopyObject %6 %22
1527 OpBranch %24
1528 %24 = OpLabel
1529 %25 = OpCopyObject %6 %23
1530 %26 = OpCopyObject %6 %22
1531 OpReturn
1532 OpFunctionEnd
1533 )";
1534
1535 const auto env = SPV_ENV_UNIVERSAL_1_5;
1536 const auto consumer = nullptr;
1537 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1538 spvtools::ValidatorOptions validator_options;
1539 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1540 kConsoleMessageConsumer));
1541 TransformationContext transformation_context(
1542 MakeUnique<FactManager>(context.get()), validator_options);
1543 TransformationOutlineFunction transformation(
1544 /*entry_block*/ 21,
1545 /*exit_block*/ 21,
1546 /*new_function_struct_return_type_id*/ 200,
1547 /*new_function_type_id*/ 201,
1548 /*new_function_id*/ 202,
1549 /*new_function_region_entry_block*/ 204,
1550 /*new_caller_result_id*/ 205,
1551 /*new_callee_result_id*/ 206,
1552 /*input_id_to_fresh_id*/ {{22, 207}},
1553 /*output_id_to_fresh_id*/ {{23, 208}});
1554
1555 ASSERT_FALSE(
1556 transformation.IsApplicable(context.get(), transformation_context));
1557 }
1558
TEST(TransformationOutlineFunctionTest,DoNotOutlineRegionThatStartsWithLoopHeader)1559 TEST(TransformationOutlineFunctionTest,
1560 DoNotOutlineRegionThatStartsWithLoopHeader) {
1561 std::string shader = R"(
1562 OpCapability Shader
1563 %1 = OpExtInstImport "GLSL.std.450"
1564 OpMemoryModel Logical GLSL450
1565 OpEntryPoint Fragment %4 "main"
1566 OpExecutionMode %4 OriginUpperLeft
1567 OpSource ESSL 310
1568 OpName %4 "main"
1569 %2 = OpTypeVoid
1570 %3 = OpTypeFunction %2
1571 %6 = OpTypeBool
1572 %7 = OpConstantTrue %6
1573 %4 = OpFunction %2 None %3
1574 %5 = OpLabel
1575 OpBranch %21
1576 %21 = OpLabel
1577 OpLoopMerge %22 %23 None
1578 OpBranch %24
1579 %24 = OpLabel
1580 OpBranchConditional %7 %22 %23
1581 %23 = OpLabel
1582 OpBranch %21
1583 %22 = OpLabel
1584 OpBranch %25
1585 %25 = OpLabel
1586 OpReturn
1587 OpFunctionEnd
1588 )";
1589
1590 const auto env = SPV_ENV_UNIVERSAL_1_5;
1591 const auto consumer = nullptr;
1592 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1593 spvtools::ValidatorOptions validator_options;
1594 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1595 kConsoleMessageConsumer));
1596 TransformationContext transformation_context(
1597 MakeUnique<FactManager>(context.get()), validator_options);
1598 TransformationOutlineFunction transformation(
1599 /*entry_block*/ 21,
1600 /*exit_block*/ 24,
1601 /*new_function_struct_return_type_id*/ 200,
1602 /*new_function_type_id*/ 201,
1603 /*new_function_id*/ 202,
1604 /*new_function_region_entry_block*/ 204,
1605 /*new_caller_result_id*/ 205,
1606 /*new_callee_result_id*/ 206,
1607 /*input_id_to_fresh_id*/ {},
1608 /*output_id_to_fresh_id*/ {});
1609
1610 ASSERT_FALSE(
1611 transformation.IsApplicable(context.get(), transformation_context));
1612 }
1613
TEST(TransformationOutlineFunctionTest,DoNotOutlineRegionThatEndsWithLoopMerge)1614 TEST(TransformationOutlineFunctionTest,
1615 DoNotOutlineRegionThatEndsWithLoopMerge) {
1616 std::string shader = R"(
1617 OpCapability Shader
1618 %1 = OpExtInstImport "GLSL.std.450"
1619 OpMemoryModel Logical GLSL450
1620 OpEntryPoint Fragment %4 "main"
1621 OpExecutionMode %4 OriginUpperLeft
1622 OpSource ESSL 310
1623 OpName %4 "main"
1624 %2 = OpTypeVoid
1625 %3 = OpTypeFunction %2
1626 %6 = OpTypeBool
1627 %7 = OpConstantTrue %6
1628 %4 = OpFunction %2 None %3
1629 %5 = OpLabel
1630 OpBranch %21
1631 %21 = OpLabel
1632 OpLoopMerge %22 %23 None
1633 OpBranch %24
1634 %24 = OpLabel
1635 OpBranchConditional %7 %22 %23
1636 %23 = OpLabel
1637 OpBranch %21
1638 %22 = OpLabel
1639 OpBranch %25
1640 %25 = OpLabel
1641 OpReturn
1642 OpFunctionEnd
1643 )";
1644
1645 const auto env = SPV_ENV_UNIVERSAL_1_5;
1646 const auto consumer = nullptr;
1647 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1648 spvtools::ValidatorOptions validator_options;
1649 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1650 kConsoleMessageConsumer));
1651 TransformationContext transformation_context(
1652 MakeUnique<FactManager>(context.get()), validator_options);
1653 TransformationOutlineFunction transformation(
1654 /*entry_block*/ 5,
1655 /*exit_block*/ 22,
1656 /*new_function_struct_return_type_id*/ 200,
1657 /*new_function_type_id*/ 201,
1658 /*new_function_id*/ 202,
1659 /*new_function_region_entry_block*/ 204,
1660 /*new_caller_result_id*/ 205,
1661 /*new_callee_result_id*/ 206,
1662 /*input_id_to_fresh_id*/ {},
1663 /*output_id_to_fresh_id*/ {});
1664
1665 ASSERT_FALSE(
1666 transformation.IsApplicable(context.get(), transformation_context));
1667 }
1668
TEST(TransformationOutlineFunctionTest,DoNotOutlineRegionThatUsesAccessChain)1669 TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatUsesAccessChain) {
1670 // An access chain result is a pointer, but it cannot be passed as a function
1671 // parameter, as it is not a memory object.
1672 std::string shader = R"(
1673 OpCapability Shader
1674 %1 = OpExtInstImport "GLSL.std.450"
1675 OpMemoryModel Logical GLSL450
1676 OpEntryPoint Fragment %4 "main"
1677 OpExecutionMode %4 OriginUpperLeft
1678 OpSource ESSL 310
1679 OpName %4 "main"
1680 %2 = OpTypeVoid
1681 %3 = OpTypeFunction %2
1682 %6 = OpTypeFloat 32
1683 %7 = OpTypeVector %6 4
1684 %8 = OpTypePointer Function %7
1685 %9 = OpTypePointer Function %6
1686 %18 = OpTypeInt 32 0
1687 %19 = OpConstant %18 0
1688 %4 = OpFunction %2 None %3
1689 %5 = OpLabel
1690 %10 = OpVariable %8 Function
1691 OpBranch %11
1692 %11 = OpLabel
1693 %12 = OpAccessChain %9 %10 %19
1694 OpBranch %13
1695 %13 = OpLabel
1696 %14 = OpLoad %6 %12
1697 OpBranch %15
1698 %15 = OpLabel
1699 OpReturn
1700 OpFunctionEnd
1701 )";
1702
1703 const auto env = SPV_ENV_UNIVERSAL_1_5;
1704 const auto consumer = nullptr;
1705 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1706 spvtools::ValidatorOptions validator_options;
1707 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1708 kConsoleMessageConsumer));
1709 TransformationContext transformation_context(
1710 MakeUnique<FactManager>(context.get()), validator_options);
1711 TransformationOutlineFunction transformation(
1712 /*entry_block*/ 13,
1713 /*exit_block*/ 15,
1714 /*new_function_struct_return_type_id*/ 200,
1715 /*new_function_type_id*/ 201,
1716 /*new_function_id*/ 202,
1717 /*new_function_region_entry_block*/ 204,
1718 /*new_caller_result_id*/ 205,
1719 /*new_callee_result_id*/ 206,
1720 /*input_id_to_fresh_id*/ {{12, 207}},
1721 /*output_id_to_fresh_id*/ {});
1722
1723 ASSERT_FALSE(
1724 transformation.IsApplicable(context.get(), transformation_context));
1725 }
1726
TEST(TransformationOutlineFunctionTest,DoNotOutlineRegionThatUsesCopiedObject)1727 TEST(TransformationOutlineFunctionTest,
1728 DoNotOutlineRegionThatUsesCopiedObject) {
1729 // Copying a variable leads to a pointer, but one that cannot be passed as a
1730 // function parameter, as it is not a memory object.
1731 std::string shader = R"(
1732 OpCapability Shader
1733 %1 = OpExtInstImport "GLSL.std.450"
1734 OpMemoryModel Logical GLSL450
1735 OpEntryPoint Fragment %4 "main"
1736 OpExecutionMode %4 OriginUpperLeft
1737 OpSource ESSL 310
1738 OpName %4 "main"
1739 %2 = OpTypeVoid
1740 %3 = OpTypeFunction %2
1741 %6 = OpTypeFloat 32
1742 %7 = OpTypeVector %6 4
1743 %8 = OpTypePointer Function %7
1744 %9 = OpTypePointer Function %6
1745 %18 = OpTypeInt 32 0
1746 %19 = OpConstant %18 0
1747 %4 = OpFunction %2 None %3
1748 %5 = OpLabel
1749 %10 = OpVariable %8 Function
1750 OpBranch %11
1751 %11 = OpLabel
1752 %20 = OpCopyObject %8 %10
1753 OpBranch %13
1754 %13 = OpLabel
1755 %12 = OpAccessChain %9 %20 %19
1756 %14 = OpLoad %6 %12
1757 OpBranch %15
1758 %15 = OpLabel
1759 OpReturn
1760 OpFunctionEnd
1761 )";
1762
1763 const auto env = SPV_ENV_UNIVERSAL_1_5;
1764 const auto consumer = nullptr;
1765 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1766 spvtools::ValidatorOptions validator_options;
1767 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1768 kConsoleMessageConsumer));
1769 TransformationContext transformation_context(
1770 MakeUnique<FactManager>(context.get()), validator_options);
1771 TransformationOutlineFunction transformation(
1772 /*entry_block*/ 13,
1773 /*exit_block*/ 15,
1774 /*new_function_struct_return_type_id*/ 200,
1775 /*new_function_type_id*/ 201,
1776 /*new_function_id*/ 202,
1777 /*new_function_region_entry_block*/ 204,
1778 /*new_caller_result_id*/ 205,
1779 /*new_callee_result_id*/ 206,
1780 /*input_id_to_fresh_id*/ {{20, 207}},
1781 /*output_id_to_fresh_id*/ {});
1782
1783 ASSERT_FALSE(
1784 transformation.IsApplicable(context.get(), transformation_context));
1785 }
1786
TEST(TransformationOutlineFunctionTest,DoOutlineRegionThatUsesPointerParameter)1787 TEST(TransformationOutlineFunctionTest,
1788 DoOutlineRegionThatUsesPointerParameter) {
1789 // The region being outlined reads from a function parameter of pointer type.
1790 // This is OK: the function parameter can itself be passed on as a function
1791 // parameter.
1792 std::string shader = R"(
1793 OpCapability Shader
1794 %1 = OpExtInstImport "GLSL.std.450"
1795 OpMemoryModel Logical GLSL450
1796 OpEntryPoint Fragment %4 "main"
1797 OpExecutionMode %4 OriginUpperLeft
1798 OpSource ESSL 310
1799 %2 = OpTypeVoid
1800 %3 = OpTypeFunction %2
1801 %6 = OpTypeInt 32 1
1802 %7 = OpTypePointer Function %6
1803 %8 = OpTypeFunction %2 %7
1804 %13 = OpConstant %6 2
1805 %4 = OpFunction %2 None %3
1806 %5 = OpLabel
1807 %15 = OpVariable %7 Function
1808 %16 = OpVariable %7 Function
1809 %17 = OpLoad %6 %15
1810 OpStore %16 %17
1811 %18 = OpFunctionCall %2 %10 %16
1812 %19 = OpLoad %6 %16
1813 OpStore %15 %19
1814 OpReturn
1815 OpFunctionEnd
1816 %10 = OpFunction %2 None %8
1817 %9 = OpFunctionParameter %7
1818 %11 = OpLabel
1819 %12 = OpLoad %6 %9
1820 %14 = OpIAdd %6 %12 %13
1821 OpBranch %20
1822 %20 = OpLabel
1823 OpStore %9 %14
1824 OpReturn
1825 OpFunctionEnd
1826 )";
1827
1828 const auto env = SPV_ENV_UNIVERSAL_1_5;
1829 const auto consumer = nullptr;
1830 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1831 spvtools::ValidatorOptions validator_options;
1832 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1833 kConsoleMessageConsumer));
1834 TransformationContext transformation_context(
1835 MakeUnique<FactManager>(context.get()), validator_options);
1836 TransformationOutlineFunction transformation(
1837 /*entry_block*/ 11,
1838 /*exit_block*/ 11,
1839 /*new_function_struct_return_type_id*/ 200,
1840 /*new_function_type_id*/ 201,
1841 /*new_function_id*/ 202,
1842 /*new_function_region_entry_block*/ 204,
1843 /*new_caller_result_id*/ 205,
1844 /*new_callee_result_id*/ 206,
1845 /*input_id_to_fresh_id*/ {{9, 207}},
1846 /*output_id_to_fresh_id*/ {{14, 208}});
1847
1848 ASSERT_TRUE(
1849 transformation.IsApplicable(context.get(), transformation_context));
1850 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
1851 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1852 kConsoleMessageConsumer));
1853
1854 std::string after_transformation = R"(
1855 OpCapability Shader
1856 %1 = OpExtInstImport "GLSL.std.450"
1857 OpMemoryModel Logical GLSL450
1858 OpEntryPoint Fragment %4 "main"
1859 OpExecutionMode %4 OriginUpperLeft
1860 OpSource ESSL 310
1861 %2 = OpTypeVoid
1862 %3 = OpTypeFunction %2
1863 %6 = OpTypeInt 32 1
1864 %7 = OpTypePointer Function %6
1865 %8 = OpTypeFunction %2 %7
1866 %13 = OpConstant %6 2
1867 %200 = OpTypeStruct %6
1868 %201 = OpTypeFunction %200 %7
1869 %4 = OpFunction %2 None %3
1870 %5 = OpLabel
1871 %15 = OpVariable %7 Function
1872 %16 = OpVariable %7 Function
1873 %17 = OpLoad %6 %15
1874 OpStore %16 %17
1875 %18 = OpFunctionCall %2 %10 %16
1876 %19 = OpLoad %6 %16
1877 OpStore %15 %19
1878 OpReturn
1879 OpFunctionEnd
1880 %10 = OpFunction %2 None %8
1881 %9 = OpFunctionParameter %7
1882 %11 = OpLabel
1883 %205 = OpFunctionCall %200 %202 %9
1884 %14 = OpCompositeExtract %6 %205 0
1885 OpBranch %20
1886 %20 = OpLabel
1887 OpStore %9 %14
1888 OpReturn
1889 OpFunctionEnd
1890 %202 = OpFunction %200 None %201
1891 %207 = OpFunctionParameter %7
1892 %204 = OpLabel
1893 %12 = OpLoad %6 %207
1894 %208 = OpIAdd %6 %12 %13
1895 %206 = OpCompositeConstruct %200 %208
1896 OpReturnValue %206
1897 OpFunctionEnd
1898 )";
1899 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1900 }
1901
TEST(TransformationOutlineFunctionTest,OutlineLivesafe)1902 TEST(TransformationOutlineFunctionTest, OutlineLivesafe) {
1903 // In the following, %30 is a livesafe function, with irrelevant parameter
1904 // %200 and irrelevant local variable %201. Variable %100 is a loop limiter,
1905 // which is not irrelevant. The test checks that the outlined function is
1906 // livesafe, and that the parameters corresponding to %200 and %201 have the
1907 // irrelevant fact associated with them.
1908 std::string shader = R"(
1909 OpCapability Shader
1910 %1 = OpExtInstImport "GLSL.std.450"
1911 OpMemoryModel Logical GLSL450
1912 OpEntryPoint Fragment %4 "main"
1913 OpExecutionMode %4 OriginUpperLeft
1914 OpSource ESSL 310
1915 %2 = OpTypeVoid
1916 %3 = OpTypeFunction %2
1917 %6 = OpTypeInt 32 0
1918 %7 = OpTypePointer Function %6
1919 %199 = OpTypeFunction %2 %7
1920 %8 = OpConstant %6 0
1921 %9 = OpConstant %6 1
1922 %10 = OpConstant %6 5
1923 %11 = OpTypeBool
1924 %12 = OpConstantTrue %11
1925 %4 = OpFunction %2 None %3
1926 %5 = OpLabel
1927 OpReturn
1928 OpFunctionEnd
1929 %30 = OpFunction %2 None %199
1930 %200 = OpFunctionParameter %7
1931 %31 = OpLabel
1932 %100 = OpVariable %7 Function %8
1933 %201 = OpVariable %7 Function %8
1934 OpBranch %198
1935 %198 = OpLabel
1936 OpBranch %20
1937 %20 = OpLabel
1938 %101 = OpLoad %6 %100
1939 %102 = OpIAdd %6 %101 %9
1940 %202 = OpLoad %6 %200
1941 OpStore %201 %202
1942 OpStore %100 %102
1943 %103 = OpUGreaterThanEqual %11 %101 %10
1944 OpLoopMerge %21 %22 None
1945 OpBranchConditional %103 %21 %104
1946 %104 = OpLabel
1947 OpBranchConditional %12 %23 %21
1948 %23 = OpLabel
1949 %105 = OpLoad %6 %100
1950 %106 = OpIAdd %6 %105 %9
1951 OpStore %100 %106
1952 %107 = OpUGreaterThanEqual %11 %105 %10
1953 OpLoopMerge %25 %26 None
1954 OpBranchConditional %107 %25 %108
1955 %108 = OpLabel
1956 OpBranch %28
1957 %28 = OpLabel
1958 OpBranchConditional %12 %26 %25
1959 %26 = OpLabel
1960 OpBranch %23
1961 %25 = OpLabel
1962 %109 = OpLoad %6 %100
1963 %110 = OpIAdd %6 %109 %9
1964 OpStore %100 %110
1965 %111 = OpUGreaterThanEqual %11 %109 %10
1966 OpLoopMerge %24 %27 None
1967 OpBranchConditional %111 %24 %112
1968 %112 = OpLabel
1969 OpBranchConditional %12 %24 %27
1970 %27 = OpLabel
1971 OpBranch %25
1972 %24 = OpLabel
1973 OpBranch %22
1974 %22 = OpLabel
1975 OpBranch %20
1976 %21 = OpLabel
1977 OpBranch %197
1978 %197 = OpLabel
1979 OpReturn
1980 OpFunctionEnd
1981 )";
1982
1983 const auto env = SPV_ENV_UNIVERSAL_1_5;
1984 const auto consumer = nullptr;
1985 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1986 spvtools::ValidatorOptions validator_options;
1987 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1988 kConsoleMessageConsumer));
1989 TransformationContext transformation_context(
1990 MakeUnique<FactManager>(context.get()), validator_options);
1991 transformation_context.GetFactManager()->AddFactFunctionIsLivesafe(30);
1992 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
1993 200);
1994 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
1995 201);
1996
1997 TransformationOutlineFunction transformation(
1998 /*entry_block*/ 198,
1999 /*exit_block*/ 197,
2000 /*new_function_struct_return_type_id*/ 400,
2001 /*new_function_type_id*/ 401,
2002 /*new_function_id*/ 402,
2003 /*new_function_region_entry_block*/ 404,
2004 /*new_caller_result_id*/ 405,
2005 /*new_callee_result_id*/ 406,
2006 /*input_id_to_fresh_id*/ {{100, 407}, {200, 408}, {201, 409}},
2007 /*output_id_to_fresh_id*/ {});
2008
2009 ASSERT_TRUE(
2010 transformation.IsApplicable(context.get(), transformation_context));
2011 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
2012 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2013 kConsoleMessageConsumer));
2014
2015 // The original function should still be livesafe.
2016 ASSERT_TRUE(transformation_context.GetFactManager()->FunctionIsLivesafe(30));
2017 // The outlined function should be livesafe.
2018 ASSERT_TRUE(transformation_context.GetFactManager()->FunctionIsLivesafe(402));
2019 // The variable and parameter that were originally irrelevant should still be.
2020 ASSERT_TRUE(
2021 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(200));
2022 ASSERT_TRUE(
2023 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(201));
2024 // The loop limiter should still be non-irrelevant.
2025 ASSERT_FALSE(
2026 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
2027 // The parameters for the original irrelevant variables should be irrelevant.
2028 ASSERT_TRUE(
2029 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(408));
2030 ASSERT_TRUE(
2031 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(409));
2032 // The parameter for the loop limiter should not be irrelevant.
2033 ASSERT_FALSE(
2034 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(407));
2035
2036 std::string after_transformation = R"(
2037 OpCapability Shader
2038 %1 = OpExtInstImport "GLSL.std.450"
2039 OpMemoryModel Logical GLSL450
2040 OpEntryPoint Fragment %4 "main"
2041 OpExecutionMode %4 OriginUpperLeft
2042 OpSource ESSL 310
2043 %2 = OpTypeVoid
2044 %3 = OpTypeFunction %2
2045 %6 = OpTypeInt 32 0
2046 %7 = OpTypePointer Function %6
2047 %199 = OpTypeFunction %2 %7
2048 %8 = OpConstant %6 0
2049 %9 = OpConstant %6 1
2050 %10 = OpConstant %6 5
2051 %11 = OpTypeBool
2052 %12 = OpConstantTrue %11
2053 %401 = OpTypeFunction %2 %7 %7 %7
2054 %4 = OpFunction %2 None %3
2055 %5 = OpLabel
2056 OpReturn
2057 OpFunctionEnd
2058 %30 = OpFunction %2 None %199
2059 %200 = OpFunctionParameter %7
2060 %31 = OpLabel
2061 %100 = OpVariable %7 Function %8
2062 %201 = OpVariable %7 Function %8
2063 OpBranch %198
2064 %198 = OpLabel
2065 %405 = OpFunctionCall %2 %402 %200 %100 %201
2066 OpReturn
2067 OpFunctionEnd
2068 %402 = OpFunction %2 None %401
2069 %408 = OpFunctionParameter %7
2070 %407 = OpFunctionParameter %7
2071 %409 = OpFunctionParameter %7
2072 %404 = OpLabel
2073 OpBranch %20
2074 %20 = OpLabel
2075 %101 = OpLoad %6 %407
2076 %102 = OpIAdd %6 %101 %9
2077 %202 = OpLoad %6 %408
2078 OpStore %409 %202
2079 OpStore %407 %102
2080 %103 = OpUGreaterThanEqual %11 %101 %10
2081 OpLoopMerge %21 %22 None
2082 OpBranchConditional %103 %21 %104
2083 %104 = OpLabel
2084 OpBranchConditional %12 %23 %21
2085 %23 = OpLabel
2086 %105 = OpLoad %6 %407
2087 %106 = OpIAdd %6 %105 %9
2088 OpStore %407 %106
2089 %107 = OpUGreaterThanEqual %11 %105 %10
2090 OpLoopMerge %25 %26 None
2091 OpBranchConditional %107 %25 %108
2092 %108 = OpLabel
2093 OpBranch %28
2094 %28 = OpLabel
2095 OpBranchConditional %12 %26 %25
2096 %26 = OpLabel
2097 OpBranch %23
2098 %25 = OpLabel
2099 %109 = OpLoad %6 %407
2100 %110 = OpIAdd %6 %109 %9
2101 OpStore %407 %110
2102 %111 = OpUGreaterThanEqual %11 %109 %10
2103 OpLoopMerge %24 %27 None
2104 OpBranchConditional %111 %24 %112
2105 %112 = OpLabel
2106 OpBranchConditional %12 %24 %27
2107 %27 = OpLabel
2108 OpBranch %25
2109 %24 = OpLabel
2110 OpBranch %22
2111 %22 = OpLabel
2112 OpBranch %20
2113 %21 = OpLabel
2114 OpBranch %197
2115 %197 = OpLabel
2116 OpReturn
2117 OpFunctionEnd
2118 )";
2119 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
2120 }
2121
TEST(TransformationOutlineFunctionTest,OutlineWithDeadBlocks1)2122 TEST(TransformationOutlineFunctionTest, OutlineWithDeadBlocks1) {
2123 // This checks that if all blocks in the region being outlined were dead, all
2124 // blocks in the outlined function will be dead.
2125 std::string shader = R"(
2126 OpCapability Shader
2127 %1 = OpExtInstImport "GLSL.std.450"
2128 OpMemoryModel Logical GLSL450
2129 OpEntryPoint Fragment %4 "main"
2130 OpExecutionMode %4 OriginUpperLeft
2131 OpSource ESSL 310
2132 OpName %4 "main"
2133 OpName %10 "foo(i1;"
2134 OpName %9 "x"
2135 OpName %12 "y"
2136 OpName %21 "i"
2137 OpName %46 "param"
2138 %2 = OpTypeVoid
2139 %3 = OpTypeFunction %2
2140 %6 = OpTypeInt 32 1
2141 %7 = OpTypePointer Function %6
2142 %8 = OpTypeFunction %2 %7
2143 %13 = OpConstant %6 2
2144 %14 = OpTypeBool
2145 %15 = OpConstantFalse %14
2146 %22 = OpConstant %6 0
2147 %29 = OpConstant %6 10
2148 %41 = OpConstant %6 1
2149 %4 = OpFunction %2 None %3
2150 %5 = OpLabel
2151 %46 = OpVariable %7 Function
2152 OpStore %46 %13
2153 %47 = OpFunctionCall %2 %10 %46
2154 OpReturn
2155 OpFunctionEnd
2156 %10 = OpFunction %2 None %8
2157 %9 = OpFunctionParameter %7
2158 %11 = OpLabel
2159 %12 = OpVariable %7 Function
2160 %21 = OpVariable %7 Function
2161 OpStore %12 %13
2162 OpSelectionMerge %17 None
2163 OpBranchConditional %15 %16 %17
2164 %16 = OpLabel
2165 %18 = OpLoad %6 %9
2166 OpStore %12 %18
2167 %19 = OpLoad %6 %9
2168 %20 = OpIAdd %6 %19 %13
2169 OpStore %9 %20
2170 OpStore %21 %22
2171 OpBranch %23
2172 %23 = OpLabel
2173 OpLoopMerge %25 %26 None
2174 OpBranch %27
2175 %27 = OpLabel
2176 %28 = OpLoad %6 %21
2177 %30 = OpSLessThan %14 %28 %29
2178 OpBranchConditional %30 %24 %25
2179 %24 = OpLabel
2180 %31 = OpLoad %6 %9
2181 %32 = OpLoad %6 %21
2182 %33 = OpSGreaterThan %14 %31 %32
2183 OpSelectionMerge %35 None
2184 OpBranchConditional %33 %34 %35
2185 %34 = OpLabel
2186 OpBranch %26
2187 %35 = OpLabel
2188 %37 = OpLoad %6 %9
2189 %38 = OpLoad %6 %12
2190 %39 = OpIAdd %6 %38 %37
2191 OpStore %12 %39
2192 OpBranch %26
2193 %26 = OpLabel
2194 %40 = OpLoad %6 %21
2195 %42 = OpIAdd %6 %40 %41
2196 OpStore %21 %42
2197 OpBranch %23
2198 %25 = OpLabel
2199 OpBranch %50
2200 %50 = OpLabel
2201 OpBranch %17
2202 %17 = OpLabel
2203 %43 = OpLoad %6 %9
2204 %44 = OpLoad %6 %12
2205 %45 = OpIAdd %6 %44 %43
2206 OpStore %12 %45
2207 OpReturn
2208 OpFunctionEnd
2209 )";
2210
2211 const auto env = SPV_ENV_UNIVERSAL_1_5;
2212 const auto consumer = nullptr;
2213 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2214 spvtools::ValidatorOptions validator_options;
2215 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2216 kConsoleMessageConsumer));
2217 TransformationContext transformation_context(
2218 MakeUnique<FactManager>(context.get()), validator_options);
2219 for (uint32_t block_id : {16u, 23u, 24u, 26u, 27u, 34u, 35u, 50u}) {
2220 transformation_context.GetFactManager()->AddFactBlockIsDead(block_id);
2221 }
2222
2223 TransformationOutlineFunction transformation(
2224 /*entry_block*/ 16,
2225 /*exit_block*/ 50,
2226 /*new_function_struct_return_type_id*/ 200,
2227 /*new_function_type_id*/ 201,
2228 /*new_function_id*/ 202,
2229 /*new_function_region_entry_block*/ 203,
2230 /*new_caller_result_id*/ 204,
2231 /*new_callee_result_id*/ 205,
2232 /*input_id_to_fresh_id*/ {{9, 206}, {12, 207}, {21, 208}},
2233 /*output_id_to_fresh_id*/ {});
2234
2235 ASSERT_TRUE(
2236 transformation.IsApplicable(context.get(), transformation_context));
2237 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
2238 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2239 kConsoleMessageConsumer));
2240 // All the original blocks, plus the new function entry block, should be dead.
2241 for (uint32_t block_id : {16u, 23u, 24u, 26u, 27u, 34u, 35u, 50u, 203u}) {
2242 ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(block_id));
2243 }
2244 }
2245
TEST(TransformationOutlineFunctionTest,OutlineWithDeadBlocks2)2246 TEST(TransformationOutlineFunctionTest, OutlineWithDeadBlocks2) {
2247 // This checks that if some, but not all, blocks in the outlined region are
2248 // dead, those (but not others) will be dead in the outlined function.
2249 std::string shader = R"(
2250 OpCapability Shader
2251 %1 = OpExtInstImport "GLSL.std.450"
2252 OpMemoryModel Logical GLSL450
2253 OpEntryPoint Fragment %4 "main" %8
2254 OpExecutionMode %4 OriginUpperLeft
2255 OpSource ESSL 310
2256 %2 = OpTypeVoid
2257 %3 = OpTypeFunction %2
2258 %6 = OpTypeBool
2259 %7 = OpTypePointer Private %6
2260 %8 = OpVariable %7 Private
2261 %9 = OpConstantFalse %6
2262 %10 = OpTypePointer Function %6
2263 %12 = OpConstantTrue %6
2264 %4 = OpFunction %2 None %3
2265 %5 = OpLabel
2266 %11 = OpVariable %10 Function
2267 OpBranch %30
2268 %30 = OpLabel
2269 OpStore %8 %9
2270 OpBranch %31
2271 %31 = OpLabel
2272 OpStore %11 %12
2273 OpSelectionMerge %36 None
2274 OpBranchConditional %9 %32 %33
2275 %32 = OpLabel
2276 OpBranch %34
2277 %33 = OpLabel
2278 OpBranch %36
2279 %34 = OpLabel
2280 OpBranch %35
2281 %35 = OpLabel
2282 OpBranch %36
2283 %36 = OpLabel
2284 OpBranch %37
2285 %37 = OpLabel
2286 %13 = OpLoad %6 %8
2287 OpStore %11 %13
2288 %14 = OpLoad %6 %11
2289 OpStore %8 %14
2290 OpReturn
2291 OpFunctionEnd
2292 )";
2293
2294 const auto env = SPV_ENV_UNIVERSAL_1_5;
2295 const auto consumer = nullptr;
2296 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2297 spvtools::ValidatorOptions validator_options;
2298 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2299 kConsoleMessageConsumer));
2300 TransformationContext transformation_context(
2301 MakeUnique<FactManager>(context.get()), validator_options);
2302 for (uint32_t block_id : {32u, 34u, 35u}) {
2303 transformation_context.GetFactManager()->AddFactBlockIsDead(block_id);
2304 }
2305
2306 TransformationOutlineFunction transformation(
2307 /*entry_block*/ 30,
2308 /*exit_block*/ 37,
2309 /*new_function_struct_return_type_id*/ 200,
2310 /*new_function_type_id*/ 201,
2311 /*new_function_id*/ 202,
2312 /*new_function_region_entry_block*/ 203,
2313 /*new_caller_result_id*/ 204,
2314 /*new_callee_result_id*/ 205,
2315 /*input_id_to_fresh_id*/ {{11, 206}},
2316 /*output_id_to_fresh_id*/ {});
2317
2318 ASSERT_TRUE(
2319 transformation.IsApplicable(context.get(), transformation_context));
2320 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
2321 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2322 kConsoleMessageConsumer));
2323 // The blocks that were originally dead, but not others, should be dead.
2324 for (uint32_t block_id : {32u, 34u, 35u}) {
2325 ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(block_id));
2326 }
2327 for (uint32_t block_id : {5u, 30u, 31u, 33u, 36u, 37u, 203u}) {
2328 ASSERT_FALSE(
2329 transformation_context.GetFactManager()->BlockIsDead(block_id));
2330 }
2331 }
2332
TEST(TransformationOutlineFunctionTest,OutlineWithIrrelevantVariablesAndParameters)2333 TEST(TransformationOutlineFunctionTest,
2334 OutlineWithIrrelevantVariablesAndParameters) {
2335 // This checks that if the outlined region uses a mixture of irrelevant and
2336 // non-irrelevant variables and parameters, these properties are preserved
2337 // during outlining.
2338 std::string shader = R"(
2339 OpCapability Shader
2340 %1 = OpExtInstImport "GLSL.std.450"
2341 OpMemoryModel Logical GLSL450
2342 OpEntryPoint Fragment %4 "main"
2343 OpExecutionMode %4 OriginUpperLeft
2344 OpSource ESSL 310
2345 %2 = OpTypeVoid
2346 %3 = OpTypeFunction %2
2347 %6 = OpTypeInt 32 1
2348 %7 = OpTypePointer Function %6
2349 %8 = OpTypeFunction %2 %7 %7
2350 %13 = OpConstant %6 2
2351 %15 = OpConstant %6 3
2352 %4 = OpFunction %2 None %3
2353 %5 = OpLabel
2354 OpReturn
2355 OpFunctionEnd
2356 %11 = OpFunction %2 None %8
2357 %9 = OpFunctionParameter %7
2358 %10 = OpFunctionParameter %7
2359 %12 = OpLabel
2360 %14 = OpVariable %7 Function
2361 %20 = OpVariable %7 Function
2362 OpBranch %50
2363 %50 = OpLabel
2364 OpStore %9 %13
2365 OpStore %14 %15
2366 %16 = OpLoad %6 %14
2367 OpStore %10 %16
2368 %17 = OpLoad %6 %9
2369 %18 = OpLoad %6 %10
2370 %19 = OpIAdd %6 %17 %18
2371 OpStore %14 %19
2372 %21 = OpLoad %6 %9
2373 OpStore %20 %21
2374 OpReturn
2375 OpFunctionEnd
2376 )";
2377
2378 const auto env = SPV_ENV_UNIVERSAL_1_5;
2379 const auto consumer = nullptr;
2380 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2381 spvtools::ValidatorOptions validator_options;
2382 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2383 kConsoleMessageConsumer));
2384 TransformationContext transformation_context(
2385 MakeUnique<FactManager>(context.get()), validator_options);
2386 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(9);
2387 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
2388 14);
2389
2390 TransformationOutlineFunction transformation(
2391 /*entry_block*/ 50,
2392 /*exit_block*/ 50,
2393 /*new_function_struct_return_type_id*/ 200,
2394 /*new_function_type_id*/ 201,
2395 /*new_function_id*/ 202,
2396 /*new_function_region_entry_block*/ 203,
2397 /*new_caller_result_id*/ 204,
2398 /*new_callee_result_id*/ 205,
2399 /*input_id_to_fresh_id*/ {{9, 206}, {10, 207}, {14, 208}, {20, 209}},
2400 /*output_id_to_fresh_id*/ {});
2401
2402 ASSERT_TRUE(
2403 transformation.IsApplicable(context.get(), transformation_context));
2404 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
2405 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2406 kConsoleMessageConsumer));
2407 // The variables that were originally irrelevant, plus input parameters
2408 // corresponding to them, should be irrelevant. The rest should not be.
2409 for (uint32_t variable_id : {9u, 14u, 206u, 208u}) {
2410 ASSERT_TRUE(
2411 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(
2412 variable_id));
2413 }
2414 for (uint32_t variable_id : {10u, 20u, 207u, 209u}) {
2415 ASSERT_FALSE(
2416 transformation_context.GetFactManager()->BlockIsDead(variable_id));
2417 }
2418 }
2419
TEST(TransformationOutlineFunctionTest,DoNotOutlineCodeThatProducesUsedPointer)2420 TEST(TransformationOutlineFunctionTest,
2421 DoNotOutlineCodeThatProducesUsedPointer) {
2422 // This checks that we cannot outline a region of code if it produces a
2423 // pointer result id that gets used outside the region. This avoids creating
2424 // a struct with a pointer member.
2425 std::string shader = R"(
2426 OpCapability Shader
2427 %1 = OpExtInstImport "GLSL.std.450"
2428 OpMemoryModel Logical GLSL450
2429 OpEntryPoint Fragment %6 "main"
2430 OpExecutionMode %6 OriginUpperLeft
2431 OpSource ESSL 310
2432 %2 = OpTypeVoid
2433 %3 = OpTypeFunction %2
2434 %21 = OpTypeBool
2435 %100 = OpTypeInt 32 0
2436 %99 = OpConstant %100 0
2437 %101 = OpTypeVector %100 2
2438 %102 = OpTypePointer Function %100
2439 %103 = OpTypePointer Function %101
2440 %6 = OpFunction %2 None %3
2441 %7 = OpLabel
2442 %104 = OpVariable %103 Function
2443 OpBranch %80
2444 %80 = OpLabel
2445 %105 = OpAccessChain %102 %104 %99
2446 OpBranch %106
2447 %106 = OpLabel
2448 OpStore %105 %99
2449 OpReturn
2450 OpFunctionEnd
2451 )";
2452
2453 const auto env = SPV_ENV_UNIVERSAL_1_5;
2454 const auto consumer = nullptr;
2455 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2456 spvtools::ValidatorOptions validator_options;
2457 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2458 kConsoleMessageConsumer));
2459 TransformationContext transformation_context(
2460 MakeUnique<FactManager>(context.get()), validator_options);
2461 TransformationOutlineFunction transformation(
2462 /*entry_block*/ 80,
2463 /*exit_block*/ 80,
2464 /*new_function_struct_return_type_id*/ 300,
2465 /*new_function_type_id*/ 301,
2466 /*new_function_id*/ 302,
2467 /*new_function_region_entry_block*/ 304,
2468 /*new_caller_result_id*/ 305,
2469 /*new_callee_result_id*/ 306,
2470 /*input_id_to_fresh_id*/ {{104, 307}},
2471 /*output_id_to_fresh_id*/ {{105, 308}});
2472
2473 ASSERT_FALSE(
2474 transformation.IsApplicable(context.get(), transformation_context));
2475 }
2476
TEST(TransformationOutlineFunctionTest,ExitBlockHeadsLoop)2477 TEST(TransformationOutlineFunctionTest, ExitBlockHeadsLoop) {
2478 // This checks that it is not possible outline a region that ends in a loop
2479 // head.
2480 std::string shader = R"(
2481 OpCapability Shader
2482 %1 = OpExtInstImport "GLSL.std.450"
2483 OpMemoryModel Logical GLSL450
2484 OpEntryPoint Fragment %4 "main"
2485 OpExecutionMode %4 OriginUpperLeft
2486 OpSource ESSL 310
2487 %2 = OpTypeVoid
2488 %3 = OpTypeFunction %2
2489 %15 = OpTypeInt 32 1
2490 %35 = OpTypeBool
2491 %39 = OpConstant %15 1
2492 %40 = OpConstantTrue %35
2493 %4 = OpFunction %2 None %3
2494 %5 = OpLabel
2495 OpBranch %22
2496 %22 = OpLabel
2497 OpBranch %23
2498 %23 = OpLabel
2499 %24 = OpPhi %15 %39 %22 %39 %25
2500 OpLoopMerge %26 %25 None
2501 OpBranchConditional %40 %25 %26
2502 %25 = OpLabel
2503 OpBranch %23
2504 %26 = OpLabel
2505 OpReturn
2506 OpFunctionEnd
2507 )";
2508
2509 const auto env = SPV_ENV_UNIVERSAL_1_5;
2510 const auto consumer = nullptr;
2511 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2512 spvtools::ValidatorOptions validator_options;
2513 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2514 kConsoleMessageConsumer));
2515 TransformationContext transformation_context(
2516 MakeUnique<FactManager>(context.get()), validator_options);
2517 TransformationOutlineFunction transformation(
2518 /*entry_block*/ 22,
2519 /*exit_block*/ 23,
2520 /*new_function_struct_return_type_id*/ 200,
2521 /*new_function_type_id*/ 201,
2522 /*new_function_id*/ 202,
2523 /*new_function_region_entry_block*/ 203,
2524 /*new_caller_result_id*/ 204,
2525 /*new_callee_result_id*/ 205,
2526 /*input_id_to_fresh_id*/ {},
2527 /*output_id_to_fresh_id*/ {});
2528
2529 ASSERT_FALSE(
2530 transformation.IsApplicable(context.get(), transformation_context));
2531 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
2532 }
2533
TEST(TransformationOutlineFunctionTest,Miscellaneous1)2534 TEST(TransformationOutlineFunctionTest, Miscellaneous1) {
2535 // This tests outlining of some non-trivial code, and also tests the way
2536 // overflow ids are used by the transformation.
2537
2538 std::string reference_shader = R"(
2539 OpCapability Shader
2540 %1 = OpExtInstImport "GLSL.std.450"
2541 OpMemoryModel Logical GLSL450
2542 OpEntryPoint Fragment %4 "main" %85
2543 OpExecutionMode %4 OriginUpperLeft
2544 OpSource ESSL 310
2545 OpName %4 "main"
2546 OpName %28 "buf"
2547 OpMemberName %28 0 "u1"
2548 OpMemberName %28 1 "u2"
2549 OpName %30 ""
2550 OpName %85 "color"
2551 OpMemberDecorate %28 0 Offset 0
2552 OpMemberDecorate %28 1 Offset 4
2553 OpDecorate %28 Block
2554 OpDecorate %30 DescriptorSet 0
2555 OpDecorate %30 Binding 0
2556 OpDecorate %85 Location 0
2557 %2 = OpTypeVoid
2558 %3 = OpTypeFunction %2
2559 %6 = OpTypeFloat 32
2560 %7 = OpTypeVector %6 4
2561 %10 = OpConstant %6 1
2562 %11 = OpConstant %6 2
2563 %12 = OpConstant %6 3
2564 %13 = OpConstant %6 4
2565 %14 = OpConstantComposite %7 %10 %11 %12 %13
2566 %15 = OpTypeInt 32 1
2567 %18 = OpConstant %15 0
2568 %28 = OpTypeStruct %6 %6
2569 %29 = OpTypePointer Uniform %28
2570 %30 = OpVariable %29 Uniform
2571 %31 = OpTypePointer Uniform %6
2572 %35 = OpTypeBool
2573 %39 = OpConstant %15 1
2574 %84 = OpTypePointer Output %7
2575 %85 = OpVariable %84 Output
2576 %114 = OpConstant %15 8
2577 %4 = OpFunction %2 None %3
2578 %5 = OpLabel
2579 OpBranch %22
2580 %22 = OpLabel
2581 %103 = OpPhi %15 %18 %5 %106 %43
2582 %102 = OpPhi %7 %14 %5 %107 %43
2583 %101 = OpPhi %15 %18 %5 %40 %43
2584 %32 = OpAccessChain %31 %30 %18
2585 %33 = OpLoad %6 %32
2586 %34 = OpConvertFToS %15 %33
2587 %36 = OpSLessThan %35 %101 %34
2588 OpLoopMerge %24 %43 None
2589 OpBranchConditional %36 %23 %24
2590 %23 = OpLabel
2591 %40 = OpIAdd %15 %101 %39
2592 OpBranch %150
2593 %150 = OpLabel
2594 OpBranch %41
2595 %41 = OpLabel
2596 %107 = OpPhi %7 %102 %150 %111 %65
2597 %106 = OpPhi %15 %103 %150 %110 %65
2598 %104 = OpPhi %15 %40 %150 %81 %65
2599 %47 = OpAccessChain %31 %30 %39
2600 %48 = OpLoad %6 %47
2601 %49 = OpConvertFToS %15 %48
2602 %50 = OpSLessThan %35 %104 %49
2603 OpLoopMerge %1000 %65 None
2604 OpBranchConditional %50 %42 %1000
2605 %42 = OpLabel
2606 %60 = OpIAdd %15 %106 %114
2607 %63 = OpSGreaterThan %35 %104 %60
2608 OpBranchConditional %63 %64 %65
2609 %64 = OpLabel
2610 %71 = OpCompositeExtract %6 %107 0
2611 %72 = OpFAdd %6 %71 %11
2612 %97 = OpCompositeInsert %7 %72 %107 0
2613 %76 = OpCompositeExtract %6 %107 3
2614 %77 = OpConvertFToS %15 %76
2615 %79 = OpIAdd %15 %60 %77
2616 OpBranch %65
2617 %65 = OpLabel
2618 %111 = OpPhi %7 %107 %42 %97 %64
2619 %110 = OpPhi %15 %60 %42 %79 %64
2620 %81 = OpIAdd %15 %104 %39
2621 OpBranch %41
2622 %1000 = OpLabel
2623 OpBranch %1001
2624 %1001 = OpLabel
2625 OpBranch %43
2626 %43 = OpLabel
2627 OpBranch %22
2628 %24 = OpLabel
2629 %87 = OpCompositeExtract %6 %102 0
2630 %91 = OpConvertSToF %6 %103
2631 %92 = OpCompositeConstruct %7 %87 %11 %91 %10
2632 OpStore %85 %92
2633 OpReturn
2634 OpFunctionEnd
2635 )";
2636
2637 const auto env = SPV_ENV_UNIVERSAL_1_3;
2638 const auto consumer = nullptr;
2639 spvtools::ValidatorOptions validator_options;
2640
2641 TransformationOutlineFunction transformation(
2642 /*entry_block*/ 150,
2643 /*exit_block*/ 1001,
2644 /*new_function_struct_return_type_id*/ 200,
2645 /*new_function_type_id*/ 201,
2646 /*new_function_id*/ 202,
2647 /*new_function_region_entry_block*/ 203,
2648 /*new_caller_result_id*/ 204,
2649 /*new_callee_result_id*/ 205,
2650 /*input_id_to_fresh_id*/ {{102, 300}, {103, 301}, {40, 302}},
2651 /*output_id_to_fresh_id*/ {{106, 400}, {107, 401}});
2652
2653 TransformationOutlineFunction transformation_with_missing_input_id(
2654 /*entry_block*/ 150,
2655 /*exit_block*/ 1001,
2656 /*new_function_struct_return_type_id*/ 200,
2657 /*new_function_type_id*/ 201,
2658 /*new_function_id*/ 202,
2659 /*new_function_region_entry_block*/ 203,
2660 /*new_caller_result_id*/ 204,
2661 /*new_callee_result_id*/ 205,
2662 /*input_id_to_fresh_id*/ {{102, 300}, {40, 302}},
2663 /*output_id_to_fresh_id*/ {{106, 400}, {107, 401}});
2664
2665 TransformationOutlineFunction transformation_with_missing_output_id(
2666 /*entry_block*/ 150,
2667 /*exit_block*/ 1001,
2668 /*new_function_struct_return_type_id*/ 200,
2669 /*new_function_type_id*/ 201,
2670 /*new_function_id*/ 202,
2671 /*new_function_region_entry_block*/ 203,
2672 /*new_caller_result_id*/ 204,
2673 /*new_callee_result_id*/ 205,
2674 /*input_id_to_fresh_id*/ {{102, 300}, {103, 301}, {40, 302}},
2675 /*output_id_to_fresh_id*/ {{106, 400}});
2676
2677 TransformationOutlineFunction
2678 transformation_with_missing_input_and_output_ids(
2679 /*entry_block*/ 150,
2680 /*exit_block*/ 1001,
2681 /*new_function_struct_return_type_id*/ 200,
2682 /*new_function_type_id*/ 201,
2683 /*new_function_id*/ 202,
2684 /*new_function_region_entry_block*/ 203,
2685 /*new_caller_result_id*/ 204,
2686 /*new_callee_result_id*/ 205,
2687 /*input_id_to_fresh_id*/ {{102, 300}, {40, 302}},
2688 /*output_id_to_fresh_id*/ {{106, 400}});
2689
2690 {
2691 const auto context =
2692 BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
2693 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
2694 context.get(), validator_options, kConsoleMessageConsumer));
2695
2696 TransformationContext transformation_context(
2697 MakeUnique<FactManager>(context.get()), validator_options);
2698
2699 #ifndef NDEBUG
2700 // We expect the following applicability checks to lead to assertion
2701 // failures since the transformations are missing input or output ids, and
2702 // the transformation context does not have a source of overflow ids.
2703 ASSERT_DEATH(transformation_with_missing_input_id.IsApplicable(
2704 context.get(), transformation_context),
2705 "Bad attempt to query whether overflow ids are available.");
2706 ASSERT_DEATH(transformation_with_missing_output_id.IsApplicable(
2707 context.get(), transformation_context),
2708 "Bad attempt to query whether overflow ids are available.");
2709 ASSERT_DEATH(transformation_with_missing_input_and_output_ids.IsApplicable(
2710 context.get(), transformation_context),
2711 "Bad attempt to query whether overflow ids are available.");
2712 #endif
2713
2714 ASSERT_TRUE(
2715 transformation.IsApplicable(context.get(), transformation_context));
2716 ApplyAndCheckFreshIds(transformation, context.get(),
2717 &transformation_context);
2718 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
2719 context.get(), validator_options, kConsoleMessageConsumer));
2720
2721 std::string variant_shader = R"(
2722 OpCapability Shader
2723 %1 = OpExtInstImport "GLSL.std.450"
2724 OpMemoryModel Logical GLSL450
2725 OpEntryPoint Fragment %4 "main" %85
2726 OpExecutionMode %4 OriginUpperLeft
2727 OpSource ESSL 310
2728 OpName %4 "main"
2729 OpName %28 "buf"
2730 OpMemberName %28 0 "u1"
2731 OpMemberName %28 1 "u2"
2732 OpName %30 ""
2733 OpName %85 "color"
2734 OpMemberDecorate %28 0 Offset 0
2735 OpMemberDecorate %28 1 Offset 4
2736 OpDecorate %28 Block
2737 OpDecorate %30 DescriptorSet 0
2738 OpDecorate %30 Binding 0
2739 OpDecorate %85 Location 0
2740 %2 = OpTypeVoid
2741 %3 = OpTypeFunction %2
2742 %6 = OpTypeFloat 32
2743 %7 = OpTypeVector %6 4
2744 %10 = OpConstant %6 1
2745 %11 = OpConstant %6 2
2746 %12 = OpConstant %6 3
2747 %13 = OpConstant %6 4
2748 %14 = OpConstantComposite %7 %10 %11 %12 %13
2749 %15 = OpTypeInt 32 1
2750 %18 = OpConstant %15 0
2751 %28 = OpTypeStruct %6 %6
2752 %29 = OpTypePointer Uniform %28
2753 %30 = OpVariable %29 Uniform
2754 %31 = OpTypePointer Uniform %6
2755 %35 = OpTypeBool
2756 %39 = OpConstant %15 1
2757 %84 = OpTypePointer Output %7
2758 %85 = OpVariable %84 Output
2759 %114 = OpConstant %15 8
2760 %200 = OpTypeStruct %7 %15
2761 %201 = OpTypeFunction %200 %15 %7 %15
2762 %4 = OpFunction %2 None %3
2763 %5 = OpLabel
2764 OpBranch %22
2765 %22 = OpLabel
2766 %103 = OpPhi %15 %18 %5 %106 %43
2767 %102 = OpPhi %7 %14 %5 %107 %43
2768 %101 = OpPhi %15 %18 %5 %40 %43
2769 %32 = OpAccessChain %31 %30 %18
2770 %33 = OpLoad %6 %32
2771 %34 = OpConvertFToS %15 %33
2772 %36 = OpSLessThan %35 %101 %34
2773 OpLoopMerge %24 %43 None
2774 OpBranchConditional %36 %23 %24
2775 %23 = OpLabel
2776 %40 = OpIAdd %15 %101 %39
2777 OpBranch %150
2778 %150 = OpLabel
2779 %204 = OpFunctionCall %200 %202 %103 %102 %40
2780 %107 = OpCompositeExtract %7 %204 0
2781 %106 = OpCompositeExtract %15 %204 1
2782 OpBranch %43
2783 %43 = OpLabel
2784 OpBranch %22
2785 %24 = OpLabel
2786 %87 = OpCompositeExtract %6 %102 0
2787 %91 = OpConvertSToF %6 %103
2788 %92 = OpCompositeConstruct %7 %87 %11 %91 %10
2789 OpStore %85 %92
2790 OpReturn
2791 OpFunctionEnd
2792 %202 = OpFunction %200 None %201
2793 %301 = OpFunctionParameter %15
2794 %300 = OpFunctionParameter %7
2795 %302 = OpFunctionParameter %15
2796 %203 = OpLabel
2797 OpBranch %41
2798 %41 = OpLabel
2799 %401 = OpPhi %7 %300 %203 %111 %65
2800 %400 = OpPhi %15 %301 %203 %110 %65
2801 %104 = OpPhi %15 %302 %203 %81 %65
2802 %47 = OpAccessChain %31 %30 %39
2803 %48 = OpLoad %6 %47
2804 %49 = OpConvertFToS %15 %48
2805 %50 = OpSLessThan %35 %104 %49
2806 OpLoopMerge %1000 %65 None
2807 OpBranchConditional %50 %42 %1000
2808 %42 = OpLabel
2809 %60 = OpIAdd %15 %400 %114
2810 %63 = OpSGreaterThan %35 %104 %60
2811 OpBranchConditional %63 %64 %65
2812 %64 = OpLabel
2813 %71 = OpCompositeExtract %6 %401 0
2814 %72 = OpFAdd %6 %71 %11
2815 %97 = OpCompositeInsert %7 %72 %401 0
2816 %76 = OpCompositeExtract %6 %401 3
2817 %77 = OpConvertFToS %15 %76
2818 %79 = OpIAdd %15 %60 %77
2819 OpBranch %65
2820 %65 = OpLabel
2821 %111 = OpPhi %7 %401 %42 %97 %64
2822 %110 = OpPhi %15 %60 %42 %79 %64
2823 %81 = OpIAdd %15 %104 %39
2824 OpBranch %41
2825 %1000 = OpLabel
2826 OpBranch %1001
2827 %1001 = OpLabel
2828 %205 = OpCompositeConstruct %200 %401 %400
2829 OpReturnValue %205
2830 OpFunctionEnd
2831 )";
2832 ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
2833 }
2834
2835 {
2836 const auto context =
2837 BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
2838 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
2839 context.get(), validator_options, kConsoleMessageConsumer));
2840 auto overflow_ids_unique_ptr = MakeUnique<CounterOverflowIdSource>(2000);
2841 auto overflow_ids_ptr = overflow_ids_unique_ptr.get();
2842 TransformationContext new_transformation_context(
2843 MakeUnique<FactManager>(context.get()), validator_options,
2844 std::move(overflow_ids_unique_ptr));
2845 ASSERT_TRUE(transformation_with_missing_input_id.IsApplicable(
2846 context.get(), new_transformation_context));
2847 ASSERT_TRUE(transformation_with_missing_output_id.IsApplicable(
2848 context.get(), new_transformation_context));
2849 ASSERT_TRUE(transformation_with_missing_input_and_output_ids.IsApplicable(
2850 context.get(), new_transformation_context));
2851 ApplyAndCheckFreshIds(transformation_with_missing_input_and_output_ids,
2852 context.get(), &new_transformation_context,
2853 overflow_ids_ptr->GetIssuedOverflowIds());
2854 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
2855 context.get(), validator_options, kConsoleMessageConsumer));
2856
2857 std::string variant_shader = R"(
2858 OpCapability Shader
2859 %1 = OpExtInstImport "GLSL.std.450"
2860 OpMemoryModel Logical GLSL450
2861 OpEntryPoint Fragment %4 "main" %85
2862 OpExecutionMode %4 OriginUpperLeft
2863 OpSource ESSL 310
2864 OpName %4 "main"
2865 OpName %28 "buf"
2866 OpMemberName %28 0 "u1"
2867 OpMemberName %28 1 "u2"
2868 OpName %30 ""
2869 OpName %85 "color"
2870 OpMemberDecorate %28 0 Offset 0
2871 OpMemberDecorate %28 1 Offset 4
2872 OpDecorate %28 Block
2873 OpDecorate %30 DescriptorSet 0
2874 OpDecorate %30 Binding 0
2875 OpDecorate %85 Location 0
2876 %2 = OpTypeVoid
2877 %3 = OpTypeFunction %2
2878 %6 = OpTypeFloat 32
2879 %7 = OpTypeVector %6 4
2880 %10 = OpConstant %6 1
2881 %11 = OpConstant %6 2
2882 %12 = OpConstant %6 3
2883 %13 = OpConstant %6 4
2884 %14 = OpConstantComposite %7 %10 %11 %12 %13
2885 %15 = OpTypeInt 32 1
2886 %18 = OpConstant %15 0
2887 %28 = OpTypeStruct %6 %6
2888 %29 = OpTypePointer Uniform %28
2889 %30 = OpVariable %29 Uniform
2890 %31 = OpTypePointer Uniform %6
2891 %35 = OpTypeBool
2892 %39 = OpConstant %15 1
2893 %84 = OpTypePointer Output %7
2894 %85 = OpVariable %84 Output
2895 %114 = OpConstant %15 8
2896 %200 = OpTypeStruct %7 %15
2897 %201 = OpTypeFunction %200 %15 %7 %15
2898 %4 = OpFunction %2 None %3
2899 %5 = OpLabel
2900 OpBranch %22
2901 %22 = OpLabel
2902 %103 = OpPhi %15 %18 %5 %106 %43
2903 %102 = OpPhi %7 %14 %5 %107 %43
2904 %101 = OpPhi %15 %18 %5 %40 %43
2905 %32 = OpAccessChain %31 %30 %18
2906 %33 = OpLoad %6 %32
2907 %34 = OpConvertFToS %15 %33
2908 %36 = OpSLessThan %35 %101 %34
2909 OpLoopMerge %24 %43 None
2910 OpBranchConditional %36 %23 %24
2911 %23 = OpLabel
2912 %40 = OpIAdd %15 %101 %39
2913 OpBranch %150
2914 %150 = OpLabel
2915 %204 = OpFunctionCall %200 %202 %103 %102 %40
2916 %107 = OpCompositeExtract %7 %204 0
2917 %106 = OpCompositeExtract %15 %204 1
2918 OpBranch %43
2919 %43 = OpLabel
2920 OpBranch %22
2921 %24 = OpLabel
2922 %87 = OpCompositeExtract %6 %102 0
2923 %91 = OpConvertSToF %6 %103
2924 %92 = OpCompositeConstruct %7 %87 %11 %91 %10
2925 OpStore %85 %92
2926 OpReturn
2927 OpFunctionEnd
2928 %202 = OpFunction %200 None %201
2929 %2000 = OpFunctionParameter %15
2930 %300 = OpFunctionParameter %7
2931 %302 = OpFunctionParameter %15
2932 %203 = OpLabel
2933 OpBranch %41
2934 %41 = OpLabel
2935 %2001 = OpPhi %7 %300 %203 %111 %65
2936 %400 = OpPhi %15 %2000 %203 %110 %65
2937 %104 = OpPhi %15 %302 %203 %81 %65
2938 %47 = OpAccessChain %31 %30 %39
2939 %48 = OpLoad %6 %47
2940 %49 = OpConvertFToS %15 %48
2941 %50 = OpSLessThan %35 %104 %49
2942 OpLoopMerge %1000 %65 None
2943 OpBranchConditional %50 %42 %1000
2944 %42 = OpLabel
2945 %60 = OpIAdd %15 %400 %114
2946 %63 = OpSGreaterThan %35 %104 %60
2947 OpBranchConditional %63 %64 %65
2948 %64 = OpLabel
2949 %71 = OpCompositeExtract %6 %2001 0
2950 %72 = OpFAdd %6 %71 %11
2951 %97 = OpCompositeInsert %7 %72 %2001 0
2952 %76 = OpCompositeExtract %6 %2001 3
2953 %77 = OpConvertFToS %15 %76
2954 %79 = OpIAdd %15 %60 %77
2955 OpBranch %65
2956 %65 = OpLabel
2957 %111 = OpPhi %7 %2001 %42 %97 %64
2958 %110 = OpPhi %15 %60 %42 %79 %64
2959 %81 = OpIAdd %15 %104 %39
2960 OpBranch %41
2961 %1000 = OpLabel
2962 OpBranch %1001
2963 %1001 = OpLabel
2964 %205 = OpCompositeConstruct %200 %2001 %400
2965 OpReturnValue %205
2966 OpFunctionEnd
2967 )";
2968 ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
2969 }
2970 }
2971
TEST(TransformationOutlineFunctionTest,Miscellaneous2)2972 TEST(TransformationOutlineFunctionTest, Miscellaneous2) {
2973 std::string shader = R"(
2974 OpCapability Shader
2975 %1 = OpExtInstImport "GLSL.std.450"
2976 OpMemoryModel Logical GLSL450
2977 OpEntryPoint Fragment %4 "main"
2978 OpExecutionMode %4 OriginUpperLeft
2979 OpSource ESSL 310
2980 %2 = OpTypeVoid
2981 %3 = OpTypeFunction %2
2982 %21 = OpTypeBool
2983 %167 = OpConstantTrue %21
2984 %168 = OpConstantFalse %21
2985 %4 = OpFunction %2 None %3
2986 %5 = OpLabel
2987 OpBranch %34
2988 %34 = OpLabel
2989 OpLoopMerge %36 %37 None
2990 OpBranchConditional %168 %37 %38
2991 %38 = OpLabel
2992 OpBranchConditional %168 %37 %36
2993 %37 = OpLabel
2994 OpBranch %34
2995 %36 = OpLabel
2996 OpReturn
2997 OpFunctionEnd
2998 )";
2999
3000 const auto env = SPV_ENV_UNIVERSAL_1_5;
3001 const auto consumer = nullptr;
3002 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
3003 spvtools::ValidatorOptions validator_options;
3004 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
3005 kConsoleMessageConsumer));
3006 TransformationContext transformation_context(
3007 MakeUnique<FactManager>(context.get()), validator_options);
3008 TransformationOutlineFunction transformation(
3009 /*entry_block*/ 38,
3010 /*exit_block*/ 36,
3011 /*new_function_struct_return_type_id*/ 200,
3012 /*new_function_type_id*/ 201,
3013 /*new_function_id*/ 202,
3014 /*new_function_region_entry_block*/ 203,
3015 /*new_caller_result_id*/ 204,
3016 /*new_callee_result_id*/ 205,
3017 /*input_id_to_fresh_id*/ {},
3018 /*output_id_to_fresh_id*/ {});
3019
3020 ASSERT_FALSE(
3021 transformation.IsApplicable(context.get(), transformation_context));
3022 }
3023
TEST(TransformationOutlineFunctionTest,Miscellaneous3)3024 TEST(TransformationOutlineFunctionTest, Miscellaneous3) {
3025 std::string shader = R"(
3026 OpCapability Shader
3027 %1 = OpExtInstImport "GLSL.std.450"
3028 OpMemoryModel Logical GLSL450
3029 OpEntryPoint Fragment %6 "main"
3030 OpExecutionMode %6 OriginUpperLeft
3031 OpSource ESSL 310
3032 %2 = OpTypeVoid
3033 %3 = OpTypeFunction %2
3034 %21 = OpTypeBool
3035 %167 = OpConstantTrue %21
3036 %6 = OpFunction %2 None %3
3037 %7 = OpLabel
3038 OpBranch %80
3039 %80 = OpLabel
3040 OpBranch %14
3041 %14 = OpLabel
3042 OpLoopMerge %16 %17 None
3043 OpBranch %18
3044 %18 = OpLabel
3045 OpBranchConditional %167 %15 %16
3046 %15 = OpLabel
3047 OpBranch %17
3048 %16 = OpLabel
3049 OpBranch %81
3050 %81 = OpLabel
3051 OpReturn
3052 %17 = OpLabel
3053 OpBranch %14
3054 OpFunctionEnd
3055 )";
3056
3057 const auto env = SPV_ENV_UNIVERSAL_1_5;
3058 const auto consumer = nullptr;
3059 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
3060 spvtools::ValidatorOptions validator_options;
3061 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
3062 kConsoleMessageConsumer));
3063 TransformationContext transformation_context(
3064 MakeUnique<FactManager>(context.get()), validator_options);
3065 TransformationOutlineFunction transformation(
3066 /*entry_block*/ 80,
3067 /*exit_block*/ 81,
3068 /*new_function_struct_return_type_id*/ 300,
3069 /*new_function_type_id*/ 301,
3070 /*new_function_id*/ 302,
3071 /*new_function_region_entry_block*/ 304,
3072 /*new_caller_result_id*/ 305,
3073 /*new_callee_result_id*/ 306,
3074 /*input_id_to_fresh_id*/ {},
3075 /*output_id_to_fresh_id*/ {});
3076
3077 ASSERT_TRUE(
3078 transformation.IsApplicable(context.get(), transformation_context));
3079 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
3080 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
3081 kConsoleMessageConsumer));
3082
3083 std::string after_transformation = R"(
3084 OpCapability Shader
3085 %1 = OpExtInstImport "GLSL.std.450"
3086 OpMemoryModel Logical GLSL450
3087 OpEntryPoint Fragment %6 "main"
3088 OpExecutionMode %6 OriginUpperLeft
3089 OpSource ESSL 310
3090 %2 = OpTypeVoid
3091 %3 = OpTypeFunction %2
3092 %21 = OpTypeBool
3093 %167 = OpConstantTrue %21
3094 %6 = OpFunction %2 None %3
3095 %7 = OpLabel
3096 OpBranch %80
3097 %80 = OpLabel
3098 %305 = OpFunctionCall %2 %302
3099 OpReturn
3100 OpFunctionEnd
3101 %302 = OpFunction %2 None %3
3102 %304 = OpLabel
3103 OpBranch %14
3104 %14 = OpLabel
3105 OpLoopMerge %16 %17 None
3106 OpBranch %18
3107 %18 = OpLabel
3108 OpBranchConditional %167 %15 %16
3109 %15 = OpLabel
3110 OpBranch %17
3111 %16 = OpLabel
3112 OpBranch %81
3113 %81 = OpLabel
3114 OpReturn
3115 %17 = OpLabel
3116 OpBranch %14
3117 OpFunctionEnd
3118 )";
3119 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
3120 }
3121
TEST(TransformationOutlineFunctionTest,Miscellaneous4)3122 TEST(TransformationOutlineFunctionTest, Miscellaneous4) {
3123 std::string shader = R"(
3124 OpCapability Shader
3125 %1 = OpExtInstImport "GLSL.std.450"
3126 OpMemoryModel Logical GLSL450
3127 OpEntryPoint Fragment %6 "main"
3128 OpExecutionMode %6 OriginUpperLeft
3129 OpSource ESSL 310
3130 %2 = OpTypeVoid
3131 %3 = OpTypeFunction %2
3132 %21 = OpTypeBool
3133 %100 = OpTypeInt 32 0
3134 %101 = OpTypePointer Function %100
3135 %102 = OpTypePointer Function %100
3136 %103 = OpTypeFunction %2 %101
3137 %6 = OpFunction %2 None %3
3138 %7 = OpLabel
3139 %104 = OpVariable %102 Function
3140 OpBranch %80
3141 %80 = OpLabel
3142 %105 = OpLoad %100 %104
3143 OpBranch %106
3144 %106 = OpLabel
3145 OpReturn
3146 OpFunctionEnd
3147 )";
3148
3149 const auto env = SPV_ENV_UNIVERSAL_1_5;
3150 const auto consumer = nullptr;
3151 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
3152 spvtools::ValidatorOptions validator_options;
3153 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
3154 kConsoleMessageConsumer));
3155 TransformationContext transformation_context(
3156 MakeUnique<FactManager>(context.get()), validator_options);
3157 TransformationOutlineFunction transformation(
3158 /*entry_block*/ 80,
3159 /*exit_block*/ 106,
3160 /*new_function_struct_return_type_id*/ 300,
3161 /*new_function_type_id*/ 301,
3162 /*new_function_id*/ 302,
3163 /*new_function_region_entry_block*/ 304,
3164 /*new_caller_result_id*/ 305,
3165 /*new_callee_result_id*/ 306,
3166 /*input_id_to_fresh_id*/ {{104, 307}},
3167 /*output_id_to_fresh_id*/ {});
3168
3169 ASSERT_TRUE(
3170 transformation.IsApplicable(context.get(), transformation_context));
3171 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
3172 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
3173 kConsoleMessageConsumer));
3174
3175 std::string after_transformation = R"(
3176 OpCapability Shader
3177 %1 = OpExtInstImport "GLSL.std.450"
3178 OpMemoryModel Logical GLSL450
3179 OpEntryPoint Fragment %6 "main"
3180 OpExecutionMode %6 OriginUpperLeft
3181 OpSource ESSL 310
3182 %2 = OpTypeVoid
3183 %3 = OpTypeFunction %2
3184 %21 = OpTypeBool
3185 %100 = OpTypeInt 32 0
3186 %101 = OpTypePointer Function %100
3187 %102 = OpTypePointer Function %100
3188 %103 = OpTypeFunction %2 %101
3189 %301 = OpTypeFunction %2 %102
3190 %6 = OpFunction %2 None %3
3191 %7 = OpLabel
3192 %104 = OpVariable %102 Function
3193 OpBranch %80
3194 %80 = OpLabel
3195 %305 = OpFunctionCall %2 %302 %104
3196 OpReturn
3197 OpFunctionEnd
3198 %302 = OpFunction %2 None %301
3199 %307 = OpFunctionParameter %102
3200 %304 = OpLabel
3201 %105 = OpLoad %100 %307
3202 OpBranch %106
3203 %106 = OpLabel
3204 OpReturn
3205 OpFunctionEnd
3206 )";
3207 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
3208 }
3209
TEST(TransformationOutlineFunctionTest,NoOutlineWithUnreachableBlocks)3210 TEST(TransformationOutlineFunctionTest, NoOutlineWithUnreachableBlocks) {
3211 // This checks that outlining will not be performed if a node in the region
3212 // has an unreachable predecessor.
3213
3214 std::string shader = R"(
3215 OpCapability Shader
3216 %1 = OpExtInstImport "GLSL.std.450"
3217 OpMemoryModel Logical GLSL450
3218 OpEntryPoint Fragment %4 "main"
3219 OpExecutionMode %4 OriginUpperLeft
3220 OpSource ESSL 310
3221 OpName %4 "main"
3222 %2 = OpTypeVoid
3223 %3 = OpTypeFunction %2
3224 %4 = OpFunction %2 None %3
3225 %7 = OpLabel
3226 OpBranch %5
3227 %5 = OpLabel
3228 OpReturn
3229 %6 = OpLabel
3230 OpBranch %5
3231 OpFunctionEnd
3232 )";
3233
3234 const auto env = SPV_ENV_UNIVERSAL_1_4;
3235 const auto consumer = nullptr;
3236 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
3237 spvtools::ValidatorOptions validator_options;
3238 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
3239 kConsoleMessageConsumer));
3240 TransformationContext transformation_context(
3241 MakeUnique<FactManager>(context.get()), validator_options);
3242 TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200,
3243 100, 101, 102, 103,
3244 /* not relevant */ 201, {}, {});
3245 ASSERT_FALSE(
3246 transformation.IsApplicable(context.get(), transformation_context));
3247 }
3248
3249 } // namespace
3250 } // namespace fuzz
3251 } // namespace spvtools
3252