• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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