1 // Copyright (c) 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "source/fuzz/transformation_merge_function_returns.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 
MakeReturnMergingInfo(uint32_t merge_block_id,uint32_t is_returning_id,uint32_t maybe_return_val_id,const std::map<uint32_t,uint32_t> & opphi_to_suitable_id)26 protobufs::ReturnMergingInfo MakeReturnMergingInfo(
27     uint32_t merge_block_id, uint32_t is_returning_id,
28     uint32_t maybe_return_val_id,
29     const std::map<uint32_t, uint32_t>& opphi_to_suitable_id) {
30   protobufs::ReturnMergingInfo result;
31   result.set_merge_block_id(merge_block_id);
32   result.set_is_returning_id(is_returning_id);
33   result.set_maybe_return_val_id(maybe_return_val_id);
34   *result.mutable_opphi_to_suitable_id() =
35       fuzzerutil::MapToRepeatedUInt32Pair(opphi_to_suitable_id);
36   return result;
37 }
38 
TEST(TransformationMergeFunctionReturnsTest,SimpleInapplicable)39 TEST(TransformationMergeFunctionReturnsTest, SimpleInapplicable) {
40   std::string shader = R"(
41                OpCapability Shader
42           %1 = OpExtInstImport "GLSL.std.450"
43                OpMemoryModel Logical GLSL450
44                OpEntryPoint Fragment %2 "main"
45                OpExecutionMode %2 OriginUpperLeft
46                OpSource ESSL 310
47           %3 = OpTypeVoid
48           %4 = OpTypeFunction %3
49           %5 = OpTypeInt 32 1
50           %6 = OpTypeFunction %5
51           %7 = OpTypeFloat 32
52           %8 = OpTypeFunction %7
53           %9 = OpTypeBool
54          %10 = OpConstantTrue %9
55          %11 = OpConstantFalse %9
56          %12 = OpConstant %5 0
57          %13 = OpConstant %5 1
58           %2 = OpFunction %3 None %4
59          %14 = OpLabel
60          %15 = OpFunctionCall %3 %16
61          %17 = OpFunctionCall %3 %18
62          %19 = OpFunctionCall %3 %20
63          %21 = OpFunctionCall %7 %22
64                OpReturn
65                OpFunctionEnd
66          %16 = OpFunction %3 None %4
67          %23 = OpLabel
68                OpSelectionMerge %24 None
69                OpBranchConditional %10 %25 %26
70          %25 = OpLabel
71                OpReturn
72          %26 = OpLabel
73                OpReturn
74          %24 = OpLabel
75                OpUnreachable
76                OpFunctionEnd
77          %18 = OpFunction %3 None %4
78          %27 = OpLabel
79                OpBranch %28
80          %28 = OpLabel
81                OpLoopMerge %29 %30 None
82                OpBranch %31
83          %31 = OpLabel
84                OpBranchConditional %10 %32 %29
85          %32 = OpLabel
86                OpReturn
87          %30 = OpLabel
88                OpBranch %28
89          %29 = OpLabel
90                OpReturn
91                OpFunctionEnd
92          %20 = OpFunction %3 None %4
93          %33 = OpLabel
94                OpBranch %34
95          %34 = OpLabel
96                OpLoopMerge %35 %36 None
97                OpBranch %37
98          %37 = OpLabel
99                OpBranchConditional %10 %38 %35
100          %38 = OpLabel
101                OpReturn
102          %36 = OpLabel
103                OpBranch %34
104          %35 = OpLabel
105          %39 = OpFunctionCall %3 %18
106                OpReturn
107                OpFunctionEnd
108          %22 = OpFunction %7 None %8
109          %40 = OpLabel
110                OpBranch %51
111          %51 = OpLabel
112                OpLoopMerge %41 %53 None
113                OpBranchConditional %10 %42 %41
114          %42 = OpLabel
115          %43 = OpConvertSToF %7 %12
116                OpReturnValue %43
117          %41 = OpLabel
118          %44 = OpConvertSToF %7 %13
119                OpReturnValue %44
120          %53 = OpLabel
121                OpBranch %51
122                OpFunctionEnd
123          %45 = OpFunction %5 None %6
124          %46 = OpLabel
125                OpBranch %52
126          %52 = OpLabel
127          %47 = OpConvertSToF %7 %13
128                OpLoopMerge %48 %54 None
129                OpBranchConditional %10 %49 %48
130          %49 = OpLabel
131                OpReturnValue %12
132          %48 = OpLabel
133          %50 = OpCopyObject %5 %12
134                OpReturnValue %13
135          %54 = OpLabel
136                OpBranch %52
137                OpFunctionEnd
138 )";
139 
140   const auto env = SPV_ENV_UNIVERSAL_1_5;
141   const auto consumer = nullptr;
142   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
143   spvtools::ValidatorOptions validator_options;
144   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
145                                                kConsoleMessageConsumer));
146   TransformationContext transformation_context(
147       MakeUnique<FactManager>(context.get()), validator_options);
148 
149   // Function %1 does not exist.
150   ASSERT_FALSE(TransformationMergeFunctionReturns(1, 100, 101, 0, 0, {{}})
151                    .IsApplicable(context.get(), transformation_context));
152 
153   // The entry block (%22) of function %15 does not branch unconditionally to
154   // the following block.
155   ASSERT_FALSE(TransformationMergeFunctionReturns(16, 100, 101, 0, 0, {{}})
156                    .IsApplicable(context.get(), transformation_context));
157 
158   // Block %28 is the merge block of a loop containing a return instruction, but
159   // it contains an OpReturn instruction (so, it contains instructions that are
160   // not OpLabel, OpPhi or OpBranch).
161   ASSERT_FALSE(
162       TransformationMergeFunctionReturns(
163           18, 100, 101, 0, 0, {{MakeReturnMergingInfo(29, 102, 0, {{}})}})
164           .IsApplicable(context.get(), transformation_context));
165 
166   // Block %34 is the merge block of a loop containing a return instruction, but
167   // it contains an OpFunctionCall instruction (so, it contains instructions
168   // that are not OpLabel, OpPhi or OpBranch).
169   ASSERT_FALSE(
170       TransformationMergeFunctionReturns(
171           20, 100, 101, 0, 0, {{MakeReturnMergingInfo(35, 102, 0, {{}})}})
172           .IsApplicable(context.get(), transformation_context));
173 
174   // Id %1000 cannot be found in the module and there is no id of the correct
175   // type (float) available at the end of the entry block of function %21.
176   ASSERT_FALSE(TransformationMergeFunctionReturns(22, 100, 101, 102, 1000, {{}})
177                    .IsApplicable(context.get(), transformation_context));
178 
179   // Id %47 is of type float, while function %45 has return type int.
180   ASSERT_FALSE(TransformationMergeFunctionReturns(45, 100, 101, 102, 47, {{}})
181                    .IsApplicable(context.get(), transformation_context));
182 
183   // Id %50 is not available at the end of the entry block of function %45.
184   ASSERT_FALSE(TransformationMergeFunctionReturns(45, 100, 101, 102, 50, {{}})
185                    .IsApplicable(context.get(), transformation_context));
186 }
187 
TEST(TransformationMergeFunctionReturnsTest,MissingBooleans)188 TEST(TransformationMergeFunctionReturnsTest, MissingBooleans) {
189   {
190     // OpConstantTrue is missing.
191     std::string shader = R"(
192                OpCapability Shader
193           %1 = OpExtInstImport "GLSL.std.450"
194                OpMemoryModel Logical GLSL450
195                OpEntryPoint Fragment %2 "main"
196                OpExecutionMode %2 OriginUpperLeft
197                OpSource ESSL 310
198                OpName %2 "main"
199                OpName %3 "A("
200                OpDecorate %3 RelaxedPrecision
201                OpDecorate %4 RelaxedPrecision
202           %5 = OpTypeVoid
203           %6 = OpTypeFunction %5
204           %7 = OpTypeInt 32 1
205           %8 = OpTypeFunction %7
206           %9 = OpTypeBool
207          %10 = OpConstantFalse %9
208          %11 = OpConstant %7 1
209          %12 = OpConstant %7 2
210           %2 = OpFunction %5 None %6
211          %13 = OpLabel
212           %4 = OpFunctionCall %7 %3
213                OpReturn
214                OpFunctionEnd
215           %3 = OpFunction %7 None %8
216          %14 = OpLabel
217                OpBranch %15
218          %15 = OpLabel
219                OpSelectionMerge %16 None
220                OpBranchConditional %10 %17 %16
221          %17 = OpLabel
222                OpReturnValue %11
223          %16 = OpLabel
224                OpReturnValue %12
225                OpFunctionEnd
226 )";
227 
228     const auto env = SPV_ENV_UNIVERSAL_1_5;
229     const auto consumer = nullptr;
230     const auto context =
231         BuildModule(env, consumer, shader, kFuzzAssembleOption);
232     spvtools::ValidatorOptions validator_options;
233     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
234         context.get(), validator_options, kConsoleMessageConsumer));
235     TransformationContext transformation_context(
236         MakeUnique<FactManager>(context.get()), validator_options);
237 
238     ASSERT_FALSE(TransformationMergeFunctionReturns(3, 100, 101, 0, 0, {{}})
239                      .IsApplicable(context.get(), transformation_context));
240   }
241   {
242     // OpConstantFalse is missing.
243     std::string shader = R"(
244                OpCapability Shader
245           %1 = OpExtInstImport "GLSL.std.450"
246                OpMemoryModel Logical GLSL450
247                OpEntryPoint Fragment %2 "main"
248                OpExecutionMode %2 OriginUpperLeft
249                OpSource ESSL 310
250                OpName %2 "main"
251                OpName %3 "A("
252                OpDecorate %3 RelaxedPrecision
253                OpDecorate %4 RelaxedPrecision
254           %5 = OpTypeVoid
255           %6 = OpTypeFunction %5
256           %7 = OpTypeInt 32 1
257           %8 = OpTypeFunction %7
258           %9 = OpTypeBool
259          %10 = OpConstantTrue %9
260          %11 = OpConstant %7 1
261          %12 = OpConstant %7 2
262           %2 = OpFunction %5 None %6
263          %13 = OpLabel
264           %4 = OpFunctionCall %7 %3
265                OpReturn
266                OpFunctionEnd
267           %3 = OpFunction %7 None %8
268          %14 = OpLabel
269                OpBranch %15
270          %15 = OpLabel
271                OpSelectionMerge %16 None
272                OpBranchConditional %10 %17 %16
273          %17 = OpLabel
274                OpReturnValue %11
275          %16 = OpLabel
276                OpReturnValue %12
277                OpFunctionEnd
278 )";
279 
280     const auto env = SPV_ENV_UNIVERSAL_1_5;
281     const auto consumer = nullptr;
282     const auto context =
283         BuildModule(env, consumer, shader, kFuzzAssembleOption);
284     spvtools::ValidatorOptions validator_options;
285     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
286         context.get(), validator_options, kConsoleMessageConsumer));
287     TransformationContext transformation_context(
288         MakeUnique<FactManager>(context.get()), validator_options);
289 
290     ASSERT_FALSE(TransformationMergeFunctionReturns(3, 100, 101, 0, 0, {{}})
291                      .IsApplicable(context.get(), transformation_context));
292   }
293 }
294 
TEST(TransformationMergeFunctionReturnsTest,InvalidIds)295 TEST(TransformationMergeFunctionReturnsTest, InvalidIds) {
296   std::string shader = R"(
297                OpCapability Shader
298           %1 = OpExtInstImport "GLSL.std.450"
299                OpMemoryModel Logical GLSL450
300                OpEntryPoint Fragment %2 "main"
301                OpExecutionMode %2 OriginUpperLeft
302                OpSource ESSL 310
303           %3 = OpTypeVoid
304           %4 = OpTypeFunction %3
305           %5 = OpTypeInt 32 1
306           %6 = OpTypeFunction %5
307          %42 = OpTypeFloat 32
308           %7 = OpTypeBool
309           %8 = OpConstantTrue %7
310           %9 = OpConstantFalse %7
311          %10 = OpConstant %5 0
312          %11 = OpConstant %5 1
313           %2 = OpFunction %3 None %4
314          %12 = OpLabel
315          %13 = OpFunctionCall %5 %14
316          %15 = OpFunctionCall %3 %16
317                OpReturn
318                OpFunctionEnd
319          %17 = OpFunction %3 None %4
320          %18 = OpLabel
321                OpBranch %19
322          %19 = OpLabel
323                OpLoopMerge %20 %21 None
324                OpBranch %22
325          %22 = OpLabel
326                OpBranchConditional %8 %23 %20
327          %23 = OpLabel
328                OpReturn
329          %21 = OpLabel
330                OpBranch %19
331          %20 = OpLabel
332                OpBranch %24
333          %24 = OpLabel
334                OpReturn
335                OpFunctionEnd
336          %14 = OpFunction %5 None %6
337          %25 = OpLabel
338                OpBranch %26
339          %26 = OpLabel
340                OpLoopMerge %27 %28 None
341                OpBranch %29
342          %29 = OpLabel
343                OpBranchConditional %8 %30 %27
344          %30 = OpLabel
345                OpReturnValue %10
346          %28 = OpLabel
347                OpBranch %26
348          %27 = OpLabel
349                OpBranch %33
350          %33 = OpLabel
351                OpReturnValue %11
352                OpFunctionEnd
353          %16 = OpFunction %3 None %4
354          %34 = OpLabel
355                OpBranch %35
356          %35 = OpLabel
357                OpLoopMerge %36 %37 None
358                OpBranch %38
359          %38 = OpLabel
360          %43 = OpConvertSToF %42 %10
361                OpBranchConditional %8 %39 %36
362          %39 = OpLabel
363                OpReturn
364          %37 = OpLabel
365          %44 = OpConvertSToF %42 %10
366                OpBranch %35
367          %36 = OpLabel
368          %31 = OpPhi %42 %43 %38
369          %32 = OpPhi %5 %11 %38
370                OpBranch %40
371          %40 = OpLabel
372          %41 = OpFunctionCall %3 %17
373                OpReturn
374                OpFunctionEnd
375 )";
376 
377   const auto env = SPV_ENV_UNIVERSAL_1_5;
378   const auto consumer = nullptr;
379   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
380   spvtools::ValidatorOptions validator_options;
381   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
382                                                kConsoleMessageConsumer));
383   TransformationContext transformation_context(
384       MakeUnique<FactManager>(context.get()), validator_options);
385 
386   // Fresh id %100 is used twice.
387   ASSERT_FALSE(
388       TransformationMergeFunctionReturns(
389           17, 100, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
390           .IsApplicable(context.get(), transformation_context));
391 
392   // Fresh id %100 is used twice.
393   ASSERT_FALSE(
394       TransformationMergeFunctionReturns(
395           17, 100, 101, 0, 0, {{MakeReturnMergingInfo(20, 100, 0, {{}})}})
396           .IsApplicable(context.get(), transformation_context));
397 
398   // %0 cannot be a fresh id for the new merge block.
399   ASSERT_FALSE(
400       TransformationMergeFunctionReturns(
401           17, 100, 0, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
402           .IsApplicable(context.get(), transformation_context));
403 
404   // %0 cannot be a fresh id for the new header block.
405   ASSERT_FALSE(
406       TransformationMergeFunctionReturns(
407           17, 0, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
408           .IsApplicable(context.get(), transformation_context));
409 
410   // %0 cannot be a fresh id for the new |is_returning| instruction in an
411   // existing merge block.
412   ASSERT_FALSE(
413       TransformationMergeFunctionReturns(
414           17, 100, 101, 0, 0, {{MakeReturnMergingInfo(20, 0, 0, {{}})}})
415           .IsApplicable(context.get(), transformation_context));
416 
417   // %0 cannot be a fresh id for the new |return_val| instruction in the new
418   // return block.
419   ASSERT_FALSE(
420       TransformationMergeFunctionReturns(
421           14, 100, 101, 0, 10, {{MakeReturnMergingInfo(27, 102, 103, {{}})}})
422           .IsApplicable(context.get(), transformation_context));
423 
424   // %0 cannot be a fresh id for the new |maybe_return_val| instruction in an
425   // existing merge block, inside a non-void function.
426   ASSERT_FALSE(
427       TransformationMergeFunctionReturns(
428           14, 100, 101, 102, 10, {{MakeReturnMergingInfo(27, 103, 0, {{}})}})
429           .IsApplicable(context.get(), transformation_context));
430 
431   // Fresh id %102 is repeated.
432   ASSERT_FALSE(
433       TransformationMergeFunctionReturns(
434           14, 100, 101, 102, 10, {{MakeReturnMergingInfo(27, 102, 104, {{}})}})
435           .IsApplicable(context.get(), transformation_context));
436 
437   // Id %11 (type int) does not have the correct type (float) for OpPhi
438   // instruction %31.
439   ASSERT_FALSE(
440       TransformationMergeFunctionReturns(
441           16, 100, 101, 0, 0,
442           {{MakeReturnMergingInfo(36, 103, 104, {{{31, 11}, {32, 11}}})}})
443           .IsApplicable(context.get(), transformation_context));
444 
445   // Id %11 (type int) does not have the correct type (float) for OpPhi
446   // instruction %31.
447   ASSERT_FALSE(
448       TransformationMergeFunctionReturns(
449           16, 100, 101, 0, 0,
450           {{MakeReturnMergingInfo(36, 102, 0, {{{31, 11}, {32, 11}}})}})
451           .IsApplicable(context.get(), transformation_context));
452 
453   // Id %43 is not available at the end of the entry block.
454   ASSERT_FALSE(
455       TransformationMergeFunctionReturns(
456           16, 100, 101, 0, 0,
457           {{MakeReturnMergingInfo(36, 102, 0, {{{31, 44}, {32, 11}}})}})
458           .IsApplicable(context.get(), transformation_context));
459 
460   // There is not a mapping for id %31 (float OpPhi instruction in a loop merge
461   // block) and no suitable id is available at the end of the entry block.
462   ASSERT_FALSE(TransformationMergeFunctionReturns(
463                    16, 100, 101, 0, 0,
464                    {{MakeReturnMergingInfo(36, 102, 0, {{{32, 11}}})}})
465                    .IsApplicable(context.get(), transformation_context));
466 
467   // Id %1000 cannot be found in the module and no suitable id for OpPhi %31 is
468   // available at the end of the entry block.
469   ASSERT_FALSE(
470       TransformationMergeFunctionReturns(
471           16, 100, 101, 0, 0,
472           {{MakeReturnMergingInfo(36, 102, 0, {{{31, 1000}, {32, 11}}})}})
473           .IsApplicable(context.get(), transformation_context));
474 }
475 
TEST(TransformationMergeFunctionReturnsTest,Simple)476 TEST(TransformationMergeFunctionReturnsTest, Simple) {
477   std::string shader = R"(
478                OpCapability Shader
479           %1 = OpExtInstImport "GLSL.std.450"
480                OpMemoryModel Logical GLSL450
481                OpEntryPoint Fragment %2 "main"
482                OpExecutionMode %2 OriginUpperLeft
483                OpSource ESSL 310
484           %3 = OpTypeVoid
485           %4 = OpTypeFunction %3
486           %5 = OpTypeInt 32 1
487           %6 = OpTypeFunction %5
488           %7 = OpTypeFloat 32
489           %8 = OpTypeFunction %7 %7
490           %9 = OpTypeFunction %7
491          %10 = OpTypeBool
492          %11 = OpConstantTrue %10
493          %40 = OpConstantFalse %10
494          %12 = OpConstant %5 1
495           %2 = OpFunction %3 None %4
496          %13 = OpLabel
497                OpReturn
498                OpFunctionEnd
499          %14 = OpFunction %3 None %4
500          %15 = OpLabel
501                OpBranch %16
502          %16 = OpLabel
503                OpSelectionMerge %17 None
504                OpBranchConditional %11 %18 %17
505          %18 = OpLabel
506                OpReturn
507          %17 = OpLabel
508                OpReturn
509                OpFunctionEnd
510          %19 = OpFunction %5 None %6
511          %20 = OpLabel
512                OpBranch %21
513          %21 = OpLabel
514                OpSelectionMerge %22 None
515                OpBranchConditional %11 %23 %24
516          %23 = OpLabel
517                OpReturnValue %12
518          %24 = OpLabel
519          %25 = OpIAdd %5 %12 %12
520                OpReturnValue %25
521          %22 = OpLabel
522                OpUnreachable
523                OpFunctionEnd
524          %26 = OpFunction %7 None %8
525          %27 = OpFunctionParameter %7
526          %28 = OpLabel
527                OpBranch %29
528          %29 = OpLabel
529                OpSelectionMerge %30 None
530                OpBranchConditional %11 %31 %30
531          %31 = OpLabel
532          %32 = OpFAdd %7 %27 %27
533                OpReturnValue %32
534          %30 = OpLabel
535                OpReturnValue %27
536                OpFunctionEnd
537          %33 = OpFunction %7 None %9
538          %34 = OpLabel
539          %35 = OpConvertSToF %7 %12
540                OpBranch %36
541          %36 = OpLabel
542                OpSelectionMerge %37 None
543                OpBranchConditional %11 %38 %37
544          %38 = OpLabel
545          %39 = OpFAdd %7 %35 %35
546                OpReturnValue %39
547          %37 = OpLabel
548                OpReturnValue %35
549                OpFunctionEnd
550 )";
551 
552   const auto env = SPV_ENV_UNIVERSAL_1_5;
553   const auto consumer = nullptr;
554   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
555   spvtools::ValidatorOptions validator_options;
556   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
557                                                kConsoleMessageConsumer));
558   TransformationContext transformation_context(
559       MakeUnique<FactManager>(context.get()), validator_options);
560 
561   // The 0s are allowed because the function's return type is void.
562   auto transformation1 =
563       TransformationMergeFunctionReturns(14, 100, 101, 0, 0, {{}});
564   ASSERT_TRUE(
565       transformation1.IsApplicable(context.get(), transformation_context));
566   ApplyAndCheckFreshIds(transformation1, context.get(),
567                         &transformation_context);
568   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
569                                                kConsoleMessageConsumer));
570 
571   // %12 is available at the end of the entry block of %19 (it is a global
572   // variable).
573   ASSERT_TRUE(TransformationMergeFunctionReturns(19, 110, 111, 112, 12, {{}})
574                   .IsApplicable(context.get(), transformation_context));
575 
576   // %1000 cannot be found in the module, but there is a suitable id available
577   // at the end of the entry block (%12).
578   auto transformation2 =
579       TransformationMergeFunctionReturns(19, 110, 111, 112, 1000, {{}});
580   ASSERT_TRUE(
581       transformation2.IsApplicable(context.get(), transformation_context));
582   ApplyAndCheckFreshIds(transformation2, context.get(),
583                         &transformation_context);
584   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
585                                                kConsoleMessageConsumer));
586 
587   // %27 is available at the end of the entry block of %26 (it is a function
588   // parameter).
589   ASSERT_TRUE(TransformationMergeFunctionReturns(26, 120, 121, 122, 27, {{}})
590                   .IsApplicable(context.get(), transformation_context));
591 
592   // %1000 cannot be found in the module, but there is a suitable id available
593   // at the end of the entry block (%27).
594   auto transformation3 =
595       TransformationMergeFunctionReturns(26, 120, 121, 122, 1000, {{}});
596   ASSERT_TRUE(
597       transformation3.IsApplicable(context.get(), transformation_context));
598   ApplyAndCheckFreshIds(transformation3, context.get(),
599                         &transformation_context);
600   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
601                                                kConsoleMessageConsumer));
602 
603   // %35 is available at the end of the entry block of %33 (it is in the entry
604   // block).
605   ASSERT_TRUE(TransformationMergeFunctionReturns(26, 130, 131, 132, 27, {{}})
606                   .IsApplicable(context.get(), transformation_context));
607 
608   // %1000 cannot be found in the module, but there is a suitable id available
609   // at the end of the entry block (%35).
610   auto transformation4 =
611       TransformationMergeFunctionReturns(33, 130, 131, 132, 1000, {{}});
612   ASSERT_TRUE(
613       transformation4.IsApplicable(context.get(), transformation_context));
614   ApplyAndCheckFreshIds(transformation4, context.get(),
615                         &transformation_context);
616   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
617                                                kConsoleMessageConsumer));
618 
619   std::string after_transformations = R"(
620                OpCapability Shader
621           %1 = OpExtInstImport "GLSL.std.450"
622                OpMemoryModel Logical GLSL450
623                OpEntryPoint Fragment %2 "main"
624                OpExecutionMode %2 OriginUpperLeft
625                OpSource ESSL 310
626           %3 = OpTypeVoid
627           %4 = OpTypeFunction %3
628           %5 = OpTypeInt 32 1
629           %6 = OpTypeFunction %5
630           %7 = OpTypeFloat 32
631           %8 = OpTypeFunction %7 %7
632           %9 = OpTypeFunction %7
633          %10 = OpTypeBool
634          %11 = OpConstantTrue %10
635          %40 = OpConstantFalse %10
636          %12 = OpConstant %5 1
637           %2 = OpFunction %3 None %4
638          %13 = OpLabel
639                OpReturn
640                OpFunctionEnd
641          %14 = OpFunction %3 None %4
642          %15 = OpLabel
643                OpBranch %100
644         %100 = OpLabel
645                OpLoopMerge %101 %100 None
646                OpBranchConditional %11 %16 %100
647          %16 = OpLabel
648                OpSelectionMerge %17 None
649                OpBranchConditional %11 %18 %17
650          %18 = OpLabel
651                OpBranch %101
652          %17 = OpLabel
653                OpBranch %101
654         %101 = OpLabel
655                OpReturn
656                OpFunctionEnd
657          %19 = OpFunction %5 None %6
658          %20 = OpLabel
659                OpBranch %110
660         %110 = OpLabel
661                OpLoopMerge %111 %110 None
662                OpBranchConditional %11 %21 %110
663          %21 = OpLabel
664                OpSelectionMerge %22 None
665                OpBranchConditional %11 %23 %24
666          %23 = OpLabel
667                OpBranch %111
668          %24 = OpLabel
669          %25 = OpIAdd %5 %12 %12
670                OpBranch %111
671          %22 = OpLabel
672                OpUnreachable
673         %111 = OpLabel
674         %112 = OpPhi %5 %12 %23 %25 %24
675                OpReturnValue %112
676                OpFunctionEnd
677          %26 = OpFunction %7 None %8
678          %27 = OpFunctionParameter %7
679          %28 = OpLabel
680                OpBranch %120
681         %120 = OpLabel
682                OpLoopMerge %121 %120 None
683                OpBranchConditional %11 %29 %120
684          %29 = OpLabel
685                OpSelectionMerge %30 None
686                OpBranchConditional %11 %31 %30
687          %31 = OpLabel
688          %32 = OpFAdd %7 %27 %27
689                OpBranch %121
690          %30 = OpLabel
691                OpBranch %121
692         %121 = OpLabel
693         %122 = OpPhi %7 %27 %30 %32 %31
694                OpReturnValue %122
695                OpFunctionEnd
696          %33 = OpFunction %7 None %9
697          %34 = OpLabel
698          %35 = OpConvertSToF %7 %12
699                OpBranch %130
700         %130 = OpLabel
701                OpLoopMerge %131 %130 None
702                OpBranchConditional %11 %36 %130
703          %36 = OpLabel
704                OpSelectionMerge %37 None
705                OpBranchConditional %11 %38 %37
706          %38 = OpLabel
707          %39 = OpFAdd %7 %35 %35
708                OpBranch %131
709          %37 = OpLabel
710                OpBranch %131
711         %131 = OpLabel
712         %132 = OpPhi %7 %35 %37 %39 %38
713                OpReturnValue %132
714                OpFunctionEnd
715 )";
716 
717   ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
718 }
719 
TEST(TransformationMergeFunctionReturnsTest,NestedLoops)720 TEST(TransformationMergeFunctionReturnsTest, NestedLoops) {
721   std::string shader = R"(
722                OpCapability Shader
723           %1 = OpExtInstImport "GLSL.std.450"
724                OpMemoryModel Logical GLSL450
725                OpEntryPoint Fragment %2 "main"
726                OpExecutionMode %2 OriginUpperLeft
727                OpSource ESSL 310
728           %3 = OpTypeVoid
729           %4 = OpTypeFunction %3
730           %5 = OpTypeInt 32 1
731           %6 = OpTypeFunction %5
732           %7 = OpTypeBool
733           %8 = OpConstantTrue %7
734           %9 = OpConstantFalse %7
735          %10 = OpConstant %5 2
736          %11 = OpConstant %5 1
737          %12 = OpConstant %5 3
738           %2 = OpFunction %3 None %4
739          %13 = OpLabel
740                OpReturn
741                OpFunctionEnd
742          %14 = OpFunction %5 None %6
743          %15 = OpLabel
744                OpBranch %16
745          %16 = OpLabel
746                OpLoopMerge %17 %16 None
747                OpBranchConditional %8 %18 %16
748          %18 = OpLabel
749                OpLoopMerge %19 %20 None
750                OpBranchConditional %8 %19 %21
751          %19 = OpLabel
752                OpBranch %17
753          %21 = OpLabel
754                OpReturnValue %12
755          %17 = OpLabel
756                OpBranch %22
757          %20 = OpLabel
758                OpBranch %18
759          %22 = OpLabel
760                OpLoopMerge %23 %24 None
761                OpBranch %25
762          %25 = OpLabel
763                OpBranchConditional %8 %26 %23
764          %26 = OpLabel
765                OpSelectionMerge %27 None
766                OpBranchConditional %9 %28 %27
767          %28 = OpLabel
768                OpBranch %29
769          %29 = OpLabel
770                OpLoopMerge %30 %29 None
771                OpBranchConditional %8 %30 %29
772          %30 = OpLabel
773                OpLoopMerge %31 %32 None
774                OpBranch %33
775          %33 = OpLabel
776                OpBranchConditional %9 %34 %31
777          %34 = OpLabel
778                OpReturnValue %10
779          %32 = OpLabel
780                OpBranch %30
781          %31 = OpLabel
782          %35 = OpPhi %5 %11 %33
783          %36 = OpPhi %5 %10 %33
784                OpBranch %37
785          %37 = OpLabel
786                OpReturnValue %35
787          %27 = OpLabel
788                OpBranch %24
789          %24 = OpLabel
790                OpBranch %22
791          %23 = OpLabel
792                OpBranch %38
793          %38 = OpLabel
794                OpReturnValue %12
795                OpFunctionEnd
796 )";
797 
798   const auto env = SPV_ENV_UNIVERSAL_1_5;
799   const auto consumer = nullptr;
800   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
801   spvtools::ValidatorOptions validator_options;
802   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
803                                                kConsoleMessageConsumer));
804   TransformationContext transformation_context(
805       MakeUnique<FactManager>(context.get()), validator_options);
806 
807   auto transformation = TransformationMergeFunctionReturns(
808       14, 100, 101, 102, 11,
809       {{MakeReturnMergingInfo(19, 103, 104, {{}}),
810         MakeReturnMergingInfo(17, 105, 106, {{}}),
811         MakeReturnMergingInfo(31, 107, 108, {{{35, 10}, {36, 12}}}),
812         MakeReturnMergingInfo(23, 109, 110, {})}});
813   ASSERT_TRUE(
814       transformation.IsApplicable(context.get(), transformation_context));
815   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
816   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
817                                                kConsoleMessageConsumer));
818 
819   std::string after_transformation = R"(
820                OpCapability Shader
821           %1 = OpExtInstImport "GLSL.std.450"
822                OpMemoryModel Logical GLSL450
823                OpEntryPoint Fragment %2 "main"
824                OpExecutionMode %2 OriginUpperLeft
825                OpSource ESSL 310
826           %3 = OpTypeVoid
827           %4 = OpTypeFunction %3
828           %5 = OpTypeInt 32 1
829           %6 = OpTypeFunction %5
830           %7 = OpTypeBool
831           %8 = OpConstantTrue %7
832           %9 = OpConstantFalse %7
833          %10 = OpConstant %5 2
834          %11 = OpConstant %5 1
835          %12 = OpConstant %5 3
836           %2 = OpFunction %3 None %4
837          %13 = OpLabel
838                OpReturn
839                OpFunctionEnd
840          %14 = OpFunction %5 None %6
841          %15 = OpLabel
842                OpBranch %100
843         %100 = OpLabel
844                OpLoopMerge %101 %100 None
845                OpBranchConditional %8 %16 %100
846          %16 = OpLabel
847                OpLoopMerge %17 %16 None
848                OpBranchConditional %8 %18 %16
849          %18 = OpLabel
850                OpLoopMerge %19 %20 None
851                OpBranchConditional %8 %19 %21
852          %19 = OpLabel
853         %103 = OpPhi %7 %8 %21 %9 %18
854         %104 = OpPhi %5 %12 %21 %11 %18
855                OpBranch %17
856          %21 = OpLabel
857                OpBranch %19
858          %17 = OpLabel
859         %105 = OpPhi %7 %103 %19
860         %106 = OpPhi %5 %104 %19
861                OpBranchConditional %105 %101 %22
862          %20 = OpLabel
863                OpBranch %18
864          %22 = OpLabel
865                OpLoopMerge %23 %24 None
866                OpBranch %25
867          %25 = OpLabel
868                OpBranchConditional %8 %26 %23
869          %26 = OpLabel
870                OpSelectionMerge %27 None
871                OpBranchConditional %9 %28 %27
872          %28 = OpLabel
873                OpBranch %29
874          %29 = OpLabel
875                OpLoopMerge %30 %29 None
876                OpBranchConditional %8 %30 %29
877          %30 = OpLabel
878                OpLoopMerge %31 %32 None
879                OpBranch %33
880          %33 = OpLabel
881                OpBranchConditional %9 %34 %31
882          %34 = OpLabel
883                OpBranch %31
884          %32 = OpLabel
885                OpBranch %30
886          %31 = OpLabel
887         %107 = OpPhi %7 %8 %34 %9 %33
888         %108 = OpPhi %5 %10 %34 %11 %33
889          %35 = OpPhi %5 %11 %33 %10 %34
890          %36 = OpPhi %5 %10 %33 %12 %34
891                OpBranchConditional %107 %23 %37
892          %37 = OpLabel
893                OpBranch %23
894          %27 = OpLabel
895                OpBranch %24
896          %24 = OpLabel
897                OpBranch %22
898          %23 = OpLabel
899         %109 = OpPhi %7 %107 %31 %8 %37 %9 %25
900         %110 = OpPhi %5 %108 %31 %35 %37 %11 %25
901                OpBranchConditional %109 %101 %38
902          %38 = OpLabel
903                OpBranch %101
904         %101 = OpLabel
905         %102 = OpPhi %5 %106 %17 %110 %23 %12 %38
906                OpReturnValue %102
907                OpFunctionEnd
908 )";
909 
910   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
911 }
912 
TEST(TransformationMergeFunctionReturnsTest,OverflowIds)913 TEST(TransformationMergeFunctionReturnsTest, OverflowIds) {
914   std::string shader = R"(
915                OpCapability Shader
916           %1 = OpExtInstImport "GLSL.std.450"
917                OpMemoryModel Logical GLSL450
918                OpEntryPoint Fragment %2 "main"
919                OpExecutionMode %2 OriginUpperLeft
920                OpSource ESSL 310
921           %3 = OpTypeVoid
922           %4 = OpTypeFunction %3
923           %5 = OpTypeInt 32 1
924           %6 = OpTypeFunction %5
925           %7 = OpTypeBool
926           %8 = OpConstantTrue %7
927           %9 = OpConstantFalse %7
928          %10 = OpConstant %5 1
929           %2 = OpFunction %3 None %4
930          %11 = OpLabel
931                OpReturn
932                OpFunctionEnd
933          %12 = OpFunction %5 None %6
934          %13 = OpLabel
935                OpBranch %14
936          %14 = OpLabel
937          %15 = OpIAdd %5 %10 %10
938                OpLoopMerge %16 %17 None
939                OpBranch %18
940          %18 = OpLabel
941                OpBranchConditional %8 %19 %16
942          %19 = OpLabel
943                OpSelectionMerge %20 None
944                OpBranchConditional %9 %21 %20
945          %21 = OpLabel
946                OpReturnValue %10
947          %20 = OpLabel
948                OpBranch %17
949          %17 = OpLabel
950                OpBranchConditional %8 %14 %16
951          %16 = OpLabel
952          %22 = OpPhi %5 %15 %17 %10 %18
953                OpBranch %23
954          %23 = OpLabel
955                OpReturnValue %22
956                OpFunctionEnd
957          %24 = OpFunction %3 None %4
958          %25 = OpLabel
959                OpBranch %26
960          %26 = OpLabel
961                OpLoopMerge %27 %28 None
962                OpBranch %29
963          %29 = OpLabel
964                OpBranchConditional %8 %30 %27
965          %30 = OpLabel
966                OpSelectionMerge %31 None
967                OpBranchConditional %9 %32 %31
968          %32 = OpLabel
969                OpReturn
970          %31 = OpLabel
971                OpBranch %28
972          %28 = OpLabel
973                OpBranch %26
974          %27 = OpLabel
975          %33 = OpPhi %5 %10 %29
976                OpBranch %34
977          %34 = OpLabel
978                OpReturn
979                OpFunctionEnd
980 )";
981 
982   const auto env = SPV_ENV_UNIVERSAL_1_5;
983   const auto consumer = nullptr;
984   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
985   spvtools::ValidatorOptions validator_options;
986   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
987                                                kConsoleMessageConsumer));
988   TransformationContext transformation_context(
989       MakeUnique<FactManager>(context.get()), validator_options);
990 
991   auto overflow_ids_unique_ptr = MakeUnique<CounterOverflowIdSource>(1000);
992   auto overflow_ids_ptr = overflow_ids_unique_ptr.get();
993   TransformationContext transformation_context_with_overflow_ids(
994       MakeUnique<FactManager>(context.get()), validator_options,
995       std::move(overflow_ids_unique_ptr));
996 
997   // No mapping from merge block %16 to fresh ids is given, so overflow ids are
998   // needed.
999   auto transformation1 =
1000       TransformationMergeFunctionReturns(12, 100, 101, 102, 10, {{}});
1001 
1002 #ifndef NDEBUG
1003   ASSERT_DEATH(
1004       transformation1.IsApplicable(context.get(), transformation_context),
1005       "Bad attempt to query whether overflow ids are available.");
1006 #endif
1007 
1008   ASSERT_TRUE(transformation1.IsApplicable(
1009       context.get(), transformation_context_with_overflow_ids));
1010   ApplyAndCheckFreshIds(transformation1, context.get(),
1011                         &transformation_context_with_overflow_ids,
1012                         overflow_ids_ptr->GetIssuedOverflowIds());
1013   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1014                                                kConsoleMessageConsumer));
1015 
1016   // No mapping from merge block %27 to fresh ids is given, so overflow ids are
1017   // needed.
1018   auto transformation2 =
1019       TransformationMergeFunctionReturns(24, 110, 111, 0, 0, {{}});
1020 
1021 #ifndef NDEBUG
1022   ASSERT_DEATH(
1023       transformation2.IsApplicable(context.get(), transformation_context),
1024       "Bad attempt to query whether overflow ids are available.");
1025 #endif
1026 
1027   ASSERT_TRUE(transformation2.IsApplicable(
1028       context.get(), transformation_context_with_overflow_ids));
1029   ApplyAndCheckFreshIds(transformation2, context.get(),
1030                         &transformation_context_with_overflow_ids, {1002});
1031   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1032                                                kConsoleMessageConsumer));
1033 
1034   std::string after_transformations = R"(
1035                OpCapability Shader
1036           %1 = OpExtInstImport "GLSL.std.450"
1037                OpMemoryModel Logical GLSL450
1038                OpEntryPoint Fragment %2 "main"
1039                OpExecutionMode %2 OriginUpperLeft
1040                OpSource ESSL 310
1041           %3 = OpTypeVoid
1042           %4 = OpTypeFunction %3
1043           %5 = OpTypeInt 32 1
1044           %6 = OpTypeFunction %5
1045           %7 = OpTypeBool
1046           %8 = OpConstantTrue %7
1047           %9 = OpConstantFalse %7
1048          %10 = OpConstant %5 1
1049           %2 = OpFunction %3 None %4
1050          %11 = OpLabel
1051                OpReturn
1052                OpFunctionEnd
1053          %12 = OpFunction %5 None %6
1054          %13 = OpLabel
1055                OpBranch %100
1056         %100 = OpLabel
1057                OpLoopMerge %101 %100 None
1058                OpBranchConditional %8 %14 %100
1059          %14 = OpLabel
1060          %15 = OpIAdd %5 %10 %10
1061                OpLoopMerge %16 %17 None
1062                OpBranch %18
1063          %18 = OpLabel
1064                OpBranchConditional %8 %19 %16
1065          %19 = OpLabel
1066                OpSelectionMerge %20 None
1067                OpBranchConditional %9 %21 %20
1068          %21 = OpLabel
1069                OpBranch %16
1070          %20 = OpLabel
1071                OpBranch %17
1072          %17 = OpLabel
1073                OpBranchConditional %8 %14 %16
1074          %16 = OpLabel
1075        %1000 = OpPhi %7 %8 %21 %9 %17 %9 %18
1076        %1001 = OpPhi %5 %10 %21 %10 %17 %10 %18
1077          %22 = OpPhi %5 %15 %17 %10 %18 %10 %21
1078                OpBranchConditional %1000 %101 %23
1079          %23 = OpLabel
1080                OpBranch %101
1081         %101 = OpLabel
1082         %102 = OpPhi %5 %1001 %16 %22 %23
1083                OpReturnValue %102
1084                OpFunctionEnd
1085          %24 = OpFunction %3 None %4
1086          %25 = OpLabel
1087                OpBranch %110
1088         %110 = OpLabel
1089                OpLoopMerge %111 %110 None
1090                OpBranchConditional %8 %26 %110
1091          %26 = OpLabel
1092                OpLoopMerge %27 %28 None
1093                OpBranch %29
1094          %29 = OpLabel
1095                OpBranchConditional %8 %30 %27
1096          %30 = OpLabel
1097                OpSelectionMerge %31 None
1098                OpBranchConditional %9 %32 %31
1099          %32 = OpLabel
1100                OpBranch %27
1101          %31 = OpLabel
1102                OpBranch %28
1103          %28 = OpLabel
1104                OpBranch %26
1105          %27 = OpLabel
1106        %1002 = OpPhi %7 %8 %32 %9 %29
1107          %33 = OpPhi %5 %10 %29 %10 %32
1108                OpBranchConditional %1002 %111 %34
1109          %34 = OpLabel
1110                OpBranch %111
1111         %111 = OpLabel
1112                OpReturn
1113                OpFunctionEnd
1114 )";
1115 
1116   ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
1117 }
1118 
TEST(TransformationMergeFunctionReturnsTest,MissingIdsForOpPhi)1119 TEST(TransformationMergeFunctionReturnsTest, MissingIdsForOpPhi) {
1120   std::string shader = R"(
1121                OpCapability Shader
1122           %1 = OpExtInstImport "GLSL.std.450"
1123                OpMemoryModel Logical GLSL450
1124                OpEntryPoint Fragment %2 "main"
1125                OpExecutionMode %2 OriginUpperLeft
1126                OpSource ESSL 310
1127           %3 = OpTypeVoid
1128           %4 = OpTypeFunction %3
1129           %5 = OpTypeBool
1130           %6 = OpConstantTrue %5
1131           %7 = OpConstantFalse %5
1132           %8 = OpTypeInt 32 1
1133           %9 = OpTypeFunction %3 %8
1134          %10 = OpTypeFloat 32
1135           %2 = OpFunction %3 None %4
1136          %11 = OpLabel
1137                OpReturn
1138                OpFunctionEnd
1139          %12 = OpFunction %3 None %9
1140          %13 = OpFunctionParameter %8
1141          %14 = OpLabel
1142          %15 = OpConvertSToF %10 %13
1143                OpBranch %16
1144          %16 = OpLabel
1145                OpLoopMerge %17 %18 None
1146                OpBranch %19
1147          %19 = OpLabel
1148                OpBranchConditional %6 %20 %17
1149          %20 = OpLabel
1150                OpSelectionMerge %21 None
1151                OpBranchConditional %7 %22 %21
1152          %22 = OpLabel
1153                OpReturn
1154          %21 = OpLabel
1155                OpBranch %18
1156          %18 = OpLabel
1157                OpBranch %16
1158          %17 = OpLabel
1159          %23 = OpPhi %8 %13 %19
1160          %24 = OpPhi %10 %15 %19
1161          %25 = OpPhi %5 %6 %19
1162                OpBranch %26
1163          %26 = OpLabel
1164                OpReturn
1165                OpFunctionEnd
1166 )";
1167 
1168   const auto env = SPV_ENV_UNIVERSAL_1_5;
1169   const auto consumer = nullptr;
1170   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1171   spvtools::ValidatorOptions validator_options;
1172   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1173                                                kConsoleMessageConsumer));
1174   TransformationContext transformation_context(
1175       MakeUnique<FactManager>(context.get()), validator_options);
1176 
1177   // This test checks whether the transformation is able to find suitable ids
1178   // to use in existing OpPhi instructions if they are not provided in the
1179   // corresponding mapping.
1180 
1181   auto transformation = TransformationMergeFunctionReturns(
1182       12, 101, 102, 0, 0,
1183       {{MakeReturnMergingInfo(17, 103, 0, {{{25, 7}, {35, 8}}})}});
1184   ASSERT_TRUE(
1185       transformation.IsApplicable(context.get(), transformation_context));
1186   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
1187   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1188                                                kConsoleMessageConsumer));
1189 
1190   std::string after_transformation = R"(
1191                OpCapability Shader
1192           %1 = OpExtInstImport "GLSL.std.450"
1193                OpMemoryModel Logical GLSL450
1194                OpEntryPoint Fragment %2 "main"
1195                OpExecutionMode %2 OriginUpperLeft
1196                OpSource ESSL 310
1197           %3 = OpTypeVoid
1198           %4 = OpTypeFunction %3
1199           %5 = OpTypeBool
1200           %6 = OpConstantTrue %5
1201           %7 = OpConstantFalse %5
1202           %8 = OpTypeInt 32 1
1203           %9 = OpTypeFunction %3 %8
1204          %10 = OpTypeFloat 32
1205           %2 = OpFunction %3 None %4
1206          %11 = OpLabel
1207                OpReturn
1208                OpFunctionEnd
1209          %12 = OpFunction %3 None %9
1210          %13 = OpFunctionParameter %8
1211          %14 = OpLabel
1212          %15 = OpConvertSToF %10 %13
1213                OpBranch %101
1214         %101 = OpLabel
1215                OpLoopMerge %102 %101 None
1216                OpBranchConditional %6 %16 %101
1217          %16 = OpLabel
1218                OpLoopMerge %17 %18 None
1219                OpBranch %19
1220          %19 = OpLabel
1221                OpBranchConditional %6 %20 %17
1222          %20 = OpLabel
1223                OpSelectionMerge %21 None
1224                OpBranchConditional %7 %22 %21
1225          %22 = OpLabel
1226                OpBranch %17
1227          %21 = OpLabel
1228                OpBranch %18
1229          %18 = OpLabel
1230                OpBranch %16
1231          %17 = OpLabel
1232         %103 = OpPhi %5 %6 %22 %7 %19
1233          %23 = OpPhi %8 %13 %19 %13 %22
1234          %24 = OpPhi %10 %15 %19 %15 %22
1235          %25 = OpPhi %5 %6 %19 %7 %22
1236                OpBranchConditional %103 %102 %26
1237          %26 = OpLabel
1238                OpBranch %102
1239         %102 = OpLabel
1240                OpReturn
1241                OpFunctionEnd
1242 )";
1243 
1244   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1245 }
1246 
TEST(TransformationMergeFunctionReturnsTest,RespectDominanceRules1)1247 TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules1) {
1248   // An id defined in a loop is used in the corresponding merge block. After the
1249   // transformation, the id will not dominate the merge block anymore. This is
1250   // only OK if the use is inside an OpPhi instruction. (Note that there is also
1251   // another condition for this transformation that forbids non-OpPhi
1252   // instructions in relevant merge blocks, but that case is also considered
1253   // here for completeness).
1254 
1255   std::string shader = R"(
1256                OpCapability Shader
1257           %1 = OpExtInstImport "GLSL.std.450"
1258                OpMemoryModel Logical GLSL450
1259                OpEntryPoint Fragment %2 "main"
1260                OpExecutionMode %2 OriginUpperLeft
1261                OpSource ESSL 310
1262           %3 = OpTypeVoid
1263           %4 = OpTypeFunction %3
1264           %5 = OpTypeBool
1265           %6 = OpConstantTrue %5
1266           %7 = OpConstantFalse %5
1267           %2 = OpFunction %3 None %4
1268           %8 = OpLabel
1269                OpBranch %9
1270           %9 = OpLabel
1271                OpLoopMerge %10 %11 None
1272                OpBranch %12
1273          %12 = OpLabel
1274                OpSelectionMerge %13 None
1275                OpBranchConditional %7 %13 %14
1276          %14 = OpLabel
1277                OpReturn
1278          %13 = OpLabel
1279          %15 = OpCopyObject %5 %7
1280                OpBranch %11
1281          %11 = OpLabel
1282                OpBranchConditional %7 %9 %10
1283          %10 = OpLabel
1284          %16 = OpCopyObject %5 %15
1285                OpBranch %17
1286          %17 = OpLabel
1287                OpReturn
1288                OpFunctionEnd
1289          %18 = OpFunction %3 None %4
1290          %19 = OpLabel
1291                OpBranch %20
1292          %20 = OpLabel
1293                OpLoopMerge %21 %22 None
1294                OpBranch %23
1295          %23 = OpLabel
1296                OpSelectionMerge %24 None
1297                OpBranchConditional %7 %24 %25
1298          %25 = OpLabel
1299                OpReturn
1300          %24 = OpLabel
1301          %26 = OpCopyObject %5 %7
1302                OpBranch %22
1303          %22 = OpLabel
1304                OpBranchConditional %7 %20 %21
1305          %21 = OpLabel
1306          %27 = OpPhi %5 %26 %22
1307                OpBranch %28
1308          %28 = OpLabel
1309                OpReturn
1310                OpFunctionEnd
1311 )";
1312 
1313   const auto env = SPV_ENV_UNIVERSAL_1_5;
1314   const auto consumer = nullptr;
1315   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1316   spvtools::ValidatorOptions validator_options;
1317   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1318                                                kConsoleMessageConsumer));
1319   TransformationContext transformation_context(
1320       MakeUnique<FactManager>(context.get()), validator_options);
1321 
1322   // In function %2, the definition of id %15 will not dominate its use in
1323   // instruction %16 (inside merge block %10) after a new branch from return
1324   // block %14 is added.
1325   ASSERT_FALSE(
1326       TransformationMergeFunctionReturns(
1327           2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}})
1328           .IsApplicable(context.get(), transformation_context));
1329 
1330   // In function %18, The definition of id %26 will still dominate its use in
1331   // instruction %27 (inside merge block %21), because %27 is an OpPhi
1332   // instruction.
1333   auto transformation = TransformationMergeFunctionReturns(
1334       18, 100, 101, 0, 0, {{MakeReturnMergingInfo(21, 102, 103, {{}})}});
1335   ASSERT_TRUE(
1336       transformation.IsApplicable(context.get(), transformation_context));
1337   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
1338   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1339                                                kConsoleMessageConsumer));
1340 
1341   std::string after_transformation = R"(
1342                OpCapability Shader
1343           %1 = OpExtInstImport "GLSL.std.450"
1344                OpMemoryModel Logical GLSL450
1345                OpEntryPoint Fragment %2 "main"
1346                OpExecutionMode %2 OriginUpperLeft
1347                OpSource ESSL 310
1348           %3 = OpTypeVoid
1349           %4 = OpTypeFunction %3
1350           %5 = OpTypeBool
1351           %6 = OpConstantTrue %5
1352           %7 = OpConstantFalse %5
1353           %2 = OpFunction %3 None %4
1354           %8 = OpLabel
1355                OpBranch %9
1356           %9 = OpLabel
1357                OpLoopMerge %10 %11 None
1358                OpBranch %12
1359          %12 = OpLabel
1360                OpSelectionMerge %13 None
1361                OpBranchConditional %7 %13 %14
1362          %14 = OpLabel
1363                OpReturn
1364          %13 = OpLabel
1365          %15 = OpCopyObject %5 %7
1366                OpBranch %11
1367          %11 = OpLabel
1368                OpBranchConditional %7 %9 %10
1369          %10 = OpLabel
1370          %16 = OpCopyObject %5 %15
1371                OpBranch %17
1372          %17 = OpLabel
1373                OpReturn
1374                OpFunctionEnd
1375          %18 = OpFunction %3 None %4
1376          %19 = OpLabel
1377                OpBranch %100
1378         %100 = OpLabel
1379                OpLoopMerge %101 %100 None
1380                OpBranchConditional %6 %20 %100
1381          %20 = OpLabel
1382                OpLoopMerge %21 %22 None
1383                OpBranch %23
1384          %23 = OpLabel
1385                OpSelectionMerge %24 None
1386                OpBranchConditional %7 %24 %25
1387          %25 = OpLabel
1388                OpBranch %21
1389          %24 = OpLabel
1390          %26 = OpCopyObject %5 %7
1391                OpBranch %22
1392          %22 = OpLabel
1393                OpBranchConditional %7 %20 %21
1394          %21 = OpLabel
1395         %102 = OpPhi %5 %6 %25 %7 %22
1396          %27 = OpPhi %5 %26 %22 %6 %25
1397                OpBranchConditional %102 %101 %28
1398          %28 = OpLabel
1399                OpBranch %101
1400         %101 = OpLabel
1401                OpReturn
1402                OpFunctionEnd
1403 )";
1404 
1405   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1406 }
1407 
TEST(TransformationMergeFunctionReturnsTest,RespectDominanceRules2)1408 TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules2) {
1409   // An id defined in a loop is used after the corresponding merge block. After
1410   // the transformation, the id will not dominate its use anymore, regardless of
1411   // the kind of instruction in which it is used.
1412 
1413   std::string shader = R"(
1414                OpCapability Shader
1415           %1 = OpExtInstImport "GLSL.std.450"
1416                OpMemoryModel Logical GLSL450
1417                OpEntryPoint Fragment %2 "main"
1418                OpExecutionMode %2 OriginUpperLeft
1419                OpSource ESSL 310
1420           %3 = OpTypeVoid
1421           %4 = OpTypeFunction %3
1422           %5 = OpTypeBool
1423           %6 = OpConstantTrue %5
1424           %7 = OpConstantFalse %5
1425           %2 = OpFunction %3 None %4
1426           %8 = OpLabel
1427                OpBranch %9
1428           %9 = OpLabel
1429                OpLoopMerge %10 %11 None
1430                OpBranch %12
1431          %12 = OpLabel
1432                OpSelectionMerge %13 None
1433                OpBranchConditional %7 %13 %14
1434          %14 = OpLabel
1435                OpReturn
1436          %13 = OpLabel
1437          %15 = OpCopyObject %5 %7
1438                OpBranch %11
1439          %11 = OpLabel
1440                OpBranchConditional %7 %9 %10
1441          %10 = OpLabel
1442                OpBranch %16
1443          %16 = OpLabel
1444          %17 = OpCopyObject %5 %15
1445                OpReturn
1446                OpFunctionEnd
1447          %18 = OpFunction %3 None %4
1448          %19 = OpLabel
1449                OpBranch %20
1450          %20 = OpLabel
1451                OpLoopMerge %21 %22 None
1452                OpBranch %23
1453          %23 = OpLabel
1454                OpSelectionMerge %24 None
1455                OpBranchConditional %7 %24 %25
1456          %25 = OpLabel
1457                OpReturn
1458          %24 = OpLabel
1459          %26 = OpCopyObject %5 %7
1460                OpBranch %22
1461          %22 = OpLabel
1462                OpBranchConditional %7 %20 %21
1463          %21 = OpLabel
1464                OpBranch %27
1465          %27 = OpLabel
1466          %28 = OpPhi %5 %26 %21
1467                OpReturn
1468                OpFunctionEnd
1469 )";
1470 
1471   const auto env = SPV_ENV_UNIVERSAL_1_5;
1472   const auto consumer = nullptr;
1473   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1474   spvtools::ValidatorOptions validator_options;
1475   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1476                                                kConsoleMessageConsumer));
1477   TransformationContext transformation_context(
1478       MakeUnique<FactManager>(context.get()), validator_options);
1479 
1480   // In function %2, the definition of id %15 will not dominate its use in
1481   // instruction %17 (inside block %16) after a new branch from return
1482   // block %14 to merge block %10 is added.
1483   ASSERT_FALSE(
1484       TransformationMergeFunctionReturns(
1485           2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}})
1486           .IsApplicable(context.get(), transformation_context));
1487 
1488   // In function %18, the definition of id %26 will not dominate its use in
1489   // instruction %28 (inside block %27) after a new branch from return
1490   // block %25 to merge block %21 is added.
1491   ASSERT_FALSE(TransformationMergeFunctionReturns(
1492                    2, 100, 101, 0, 0,
1493                    {{MakeReturnMergingInfo(10, 102, 0, {{}}),
1494                      MakeReturnMergingInfo(21, 103, 0, {{}})}})
1495                    .IsApplicable(context.get(), transformation_context));
1496 }
1497 
TEST(TransformationMergeFunctionReturnsTest,RespectDominanceRules3)1498 TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules3) {
1499   // An id defined in a loop is used inside the loop.
1500   // Changes to the predecessors of the merge block do not affect the validity
1501   // of the uses of such id.
1502 
1503   std::string shader = R"(
1504                OpCapability Shader
1505           %1 = OpExtInstImport "GLSL.std.450"
1506                OpMemoryModel Logical GLSL450
1507                OpEntryPoint Fragment %2 "main"
1508                OpExecutionMode %2 OriginUpperLeft
1509                OpSource ESSL 310
1510           %3 = OpTypeVoid
1511           %4 = OpTypeFunction %3
1512           %5 = OpTypeBool
1513           %6 = OpConstantTrue %5
1514           %7 = OpConstantFalse %5
1515           %2 = OpFunction %3 None %4
1516           %8 = OpLabel
1517                OpBranch %9
1518           %9 = OpLabel
1519                OpLoopMerge %10 %11 None
1520                OpBranch %12
1521          %12 = OpLabel
1522                OpSelectionMerge %13 None
1523                OpBranchConditional %7 %13 %14
1524          %14 = OpLabel
1525                OpReturn
1526          %13 = OpLabel
1527          %15 = OpCopyObject %5 %7
1528                OpSelectionMerge %16 None
1529                OpBranchConditional %7 %16 %17
1530          %17 = OpLabel
1531          %18 = OpPhi %5 %15 %13
1532          %19 = OpCopyObject %5 %15
1533                OpBranch %16
1534          %16 = OpLabel
1535                OpBranch %11
1536          %11 = OpLabel
1537                OpBranchConditional %7 %9 %10
1538          %10 = OpLabel
1539                OpBranch %20
1540          %20 = OpLabel
1541                OpReturn
1542                OpFunctionEnd
1543 )";
1544 
1545   const auto env = SPV_ENV_UNIVERSAL_1_5;
1546   const auto consumer = nullptr;
1547   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1548   spvtools::ValidatorOptions validator_options;
1549   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1550                                                kConsoleMessageConsumer));
1551   TransformationContext transformation_context(
1552       MakeUnique<FactManager>(context.get()), validator_options);
1553 
1554   // In function %2, the definition of id %15 will still dominate its use in
1555   // instructions %18 and %19 after the transformation is applied, because the
1556   // fact that the id definition dominates the uses does not depend on it
1557   // dominating the merge block.
1558   auto transformation = TransformationMergeFunctionReturns(
1559       2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}});
1560   ASSERT_TRUE(
1561       transformation.IsApplicable(context.get(), transformation_context));
1562   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
1563   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1564                                                kConsoleMessageConsumer));
1565 
1566   std::string after_transformation = R"(
1567                OpCapability Shader
1568           %1 = OpExtInstImport "GLSL.std.450"
1569                OpMemoryModel Logical GLSL450
1570                OpEntryPoint Fragment %2 "main"
1571                OpExecutionMode %2 OriginUpperLeft
1572                OpSource ESSL 310
1573           %3 = OpTypeVoid
1574           %4 = OpTypeFunction %3
1575           %5 = OpTypeBool
1576           %6 = OpConstantTrue %5
1577           %7 = OpConstantFalse %5
1578           %2 = OpFunction %3 None %4
1579           %8 = OpLabel
1580                OpBranch %100
1581         %100 = OpLabel
1582                OpLoopMerge %101 %100 None
1583                OpBranchConditional %6 %9 %100
1584           %9 = OpLabel
1585                OpLoopMerge %10 %11 None
1586                OpBranch %12
1587          %12 = OpLabel
1588                OpSelectionMerge %13 None
1589                OpBranchConditional %7 %13 %14
1590          %14 = OpLabel
1591                OpBranch %10
1592          %13 = OpLabel
1593          %15 = OpCopyObject %5 %7
1594                OpSelectionMerge %16 None
1595                OpBranchConditional %7 %16 %17
1596          %17 = OpLabel
1597          %18 = OpPhi %5 %15 %13
1598          %19 = OpCopyObject %5 %15
1599                OpBranch %16
1600          %16 = OpLabel
1601                OpBranch %11
1602          %11 = OpLabel
1603                OpBranchConditional %7 %9 %10
1604          %10 = OpLabel
1605         %102 = OpPhi %5 %6 %14 %7 %11
1606                OpBranchConditional %102 %101 %20
1607          %20 = OpLabel
1608                OpBranch %101
1609         %101 = OpLabel
1610                OpReturn
1611                OpFunctionEnd
1612 )";
1613 
1614   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1615 }
1616 
TEST(TransformationMergeFunctionReturnsTest,RespectDominanceRules4)1617 TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules4) {
1618   // An id defined in a loop, which contain 2 return statements, is used after
1619   // the loop. We can only apply the transformation if the id dominates all of
1620   // the return blocks.
1621 
1622   std::string shader = R"(
1623                OpCapability Shader
1624           %1 = OpExtInstImport "GLSL.std.450"
1625                OpMemoryModel Logical GLSL450
1626                OpEntryPoint Fragment %2 "main"
1627                OpExecutionMode %2 OriginUpperLeft
1628                OpSource ESSL 310
1629           %3 = OpTypeVoid
1630           %4 = OpTypeFunction %3
1631           %5 = OpTypeBool
1632           %6 = OpConstantTrue %5
1633           %7 = OpConstantFalse %5
1634           %2 = OpFunction %3 None %4
1635           %8 = OpLabel
1636                OpBranch %9
1637           %9 = OpLabel
1638                OpLoopMerge %10 %11 None
1639                OpBranch %12
1640          %12 = OpLabel
1641          %13 = OpCopyObject %5 %7
1642                OpSelectionMerge %14 None
1643                OpBranchConditional %7 %14 %15
1644          %15 = OpLabel
1645                OpReturn
1646          %14 = OpLabel
1647                OpSelectionMerge %16 None
1648                OpBranchConditional %7 %16 %17
1649          %17 = OpLabel
1650                OpReturn
1651          %16 = OpLabel
1652                OpBranch %11
1653          %11 = OpLabel
1654                OpBranchConditional %7 %9 %10
1655          %10 = OpLabel
1656                OpBranch %18
1657          %18 = OpLabel
1658          %19 = OpCopyObject %5 %13
1659                OpReturn
1660                OpFunctionEnd
1661          %20 = OpFunction %3 None %4
1662          %21 = OpLabel
1663                OpBranch %22
1664          %22 = OpLabel
1665                OpLoopMerge %23 %24 None
1666                OpBranch %25
1667          %25 = OpLabel
1668                OpSelectionMerge %26 None
1669                OpBranchConditional %7 %26 %27
1670          %27 = OpLabel
1671                OpReturn
1672          %26 = OpLabel
1673          %28 = OpCopyObject %5 %7
1674                OpSelectionMerge %29 None
1675                OpBranchConditional %7 %29 %30
1676          %30 = OpLabel
1677                OpReturn
1678          %29 = OpLabel
1679                OpBranch %24
1680          %24 = OpLabel
1681                OpBranchConditional %7 %22 %23
1682          %23 = OpLabel
1683                OpBranch %31
1684          %31 = OpLabel
1685          %32 = OpCopyObject %5 %28
1686                OpReturn
1687                OpFunctionEnd
1688 )";
1689 
1690   const auto env = SPV_ENV_UNIVERSAL_1_5;
1691   const auto consumer = nullptr;
1692   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1693   spvtools::ValidatorOptions validator_options;
1694   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1695                                                kConsoleMessageConsumer));
1696   TransformationContext transformation_context(
1697       MakeUnique<FactManager>(context.get()), validator_options);
1698 
1699   // In function %2, the definition of id %13 will still dominate its use in
1700   // instruction %19 after the transformation is applied, because %13 dominates
1701   // all of the return blocks.
1702   auto transformation = TransformationMergeFunctionReturns(
1703       2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}});
1704   ASSERT_TRUE(
1705       transformation.IsApplicable(context.get(), transformation_context));
1706   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
1707   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1708                                                kConsoleMessageConsumer));
1709 
1710   // In function %20, the definition of id %28 will not dominate its use in
1711   // instruction %32 after the transformation is applied, because %28 dominates
1712   // only one of the return blocks.
1713   ASSERT_FALSE(
1714       TransformationMergeFunctionReturns(
1715           20, 100, 101, 0, 0, {{MakeReturnMergingInfo(23, 102, 103, {{}})}})
1716           .IsApplicable(context.get(), transformation_context));
1717 
1718   std::string after_transformation = R"(
1719                OpCapability Shader
1720           %1 = OpExtInstImport "GLSL.std.450"
1721                OpMemoryModel Logical GLSL450
1722                OpEntryPoint Fragment %2 "main"
1723                OpExecutionMode %2 OriginUpperLeft
1724                OpSource ESSL 310
1725           %3 = OpTypeVoid
1726           %4 = OpTypeFunction %3
1727           %5 = OpTypeBool
1728           %6 = OpConstantTrue %5
1729           %7 = OpConstantFalse %5
1730           %2 = OpFunction %3 None %4
1731           %8 = OpLabel
1732                OpBranch %100
1733         %100 = OpLabel
1734                OpLoopMerge %101 %100 None
1735                OpBranchConditional %6 %9 %100
1736           %9 = OpLabel
1737                OpLoopMerge %10 %11 None
1738                OpBranch %12
1739          %12 = OpLabel
1740          %13 = OpCopyObject %5 %7
1741                OpSelectionMerge %14 None
1742                OpBranchConditional %7 %14 %15
1743          %15 = OpLabel
1744                OpBranch %10
1745          %14 = OpLabel
1746                OpSelectionMerge %16 None
1747                OpBranchConditional %7 %16 %17
1748          %17 = OpLabel
1749                OpBranch %10
1750          %16 = OpLabel
1751                OpBranch %11
1752          %11 = OpLabel
1753                OpBranchConditional %7 %9 %10
1754          %10 = OpLabel
1755         %102 = OpPhi %5 %6 %15 %6 %17 %7 %11
1756                OpBranchConditional %102 %101 %18
1757          %18 = OpLabel
1758          %19 = OpCopyObject %5 %13
1759                OpBranch %101
1760         %101 = OpLabel
1761                OpReturn
1762                OpFunctionEnd
1763          %20 = OpFunction %3 None %4
1764          %21 = OpLabel
1765                OpBranch %22
1766          %22 = OpLabel
1767                OpLoopMerge %23 %24 None
1768                OpBranch %25
1769          %25 = OpLabel
1770                OpSelectionMerge %26 None
1771                OpBranchConditional %7 %26 %27
1772          %27 = OpLabel
1773                OpReturn
1774          %26 = OpLabel
1775          %28 = OpCopyObject %5 %7
1776                OpSelectionMerge %29 None
1777                OpBranchConditional %7 %29 %30
1778          %30 = OpLabel
1779                OpReturn
1780          %29 = OpLabel
1781                OpBranch %24
1782          %24 = OpLabel
1783                OpBranchConditional %7 %22 %23
1784          %23 = OpLabel
1785                OpBranch %31
1786          %31 = OpLabel
1787          %32 = OpCopyObject %5 %28
1788                OpReturn
1789                OpFunctionEnd
1790 )";
1791 
1792   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1793 }
1794 
TEST(TransformationMergeFunctionReturnsTest,OpPhiAfterFirstBlock)1795 TEST(TransformationMergeFunctionReturnsTest, OpPhiAfterFirstBlock) {
1796   std::string shader = R"(
1797                OpCapability Shader
1798           %1 = OpExtInstImport "GLSL.std.450"
1799                OpMemoryModel Logical GLSL450
1800                OpEntryPoint Fragment %2 "main"
1801                OpExecutionMode %2 OriginUpperLeft
1802                OpSource ESSL 310
1803           %3 = OpTypeVoid
1804           %4 = OpTypeFunction %3
1805           %5 = OpTypeBool
1806           %6 = OpConstantTrue %5
1807           %7 = OpConstantFalse %5
1808           %2 = OpFunction %3 None %4
1809           %8 = OpLabel
1810                OpBranch %9
1811           %9 = OpLabel
1812          %10 = OpPhi %5 %6 %8
1813                OpSelectionMerge %11 None
1814                OpBranchConditional %6 %12 %11
1815          %12 = OpLabel
1816                OpReturn
1817          %11 = OpLabel
1818                OpReturn
1819                OpFunctionEnd
1820 )";
1821 
1822   const auto env = SPV_ENV_UNIVERSAL_1_5;
1823   const auto consumer = nullptr;
1824   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1825   spvtools::ValidatorOptions validator_options;
1826   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1827                                                kConsoleMessageConsumer));
1828 
1829   TransformationContext transformation_context(
1830       MakeUnique<FactManager>(context.get()), validator_options);
1831 
1832   auto transformation =
1833       TransformationMergeFunctionReturns(2, 100, 101, 0, 0, {});
1834   ASSERT_TRUE(
1835       transformation.IsApplicable(context.get(), transformation_context));
1836   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
1837   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1838                                                kConsoleMessageConsumer));
1839 
1840   // Ensure that all input operands of OpBranchConditional instructions have
1841   // the right operand type.
1842   context->module()->ForEachInst([](opt::Instruction* inst) {
1843     if (inst->opcode() == SpvOpBranchConditional) {
1844       ASSERT_EQ(inst->GetInOperand(0).type, SPV_OPERAND_TYPE_ID);
1845       ASSERT_EQ(inst->GetInOperand(1).type, SPV_OPERAND_TYPE_ID);
1846       ASSERT_EQ(inst->GetInOperand(2).type, SPV_OPERAND_TYPE_ID);
1847     }
1848   });
1849 
1850   std::string after_transformation = R"(
1851                OpCapability Shader
1852           %1 = OpExtInstImport "GLSL.std.450"
1853                OpMemoryModel Logical GLSL450
1854                OpEntryPoint Fragment %2 "main"
1855                OpExecutionMode %2 OriginUpperLeft
1856                OpSource ESSL 310
1857           %3 = OpTypeVoid
1858           %4 = OpTypeFunction %3
1859           %5 = OpTypeBool
1860           %6 = OpConstantTrue %5
1861           %7 = OpConstantFalse %5
1862           %2 = OpFunction %3 None %4
1863           %8 = OpLabel
1864                OpBranch %100
1865         %100 = OpLabel
1866                OpLoopMerge %101 %100 None
1867                OpBranchConditional %6 %9 %100
1868           %9 = OpLabel
1869          %10 = OpPhi %5 %6 %100
1870                OpSelectionMerge %11 None
1871                OpBranchConditional %6 %12 %11
1872          %12 = OpLabel
1873                OpBranch %101
1874          %11 = OpLabel
1875                OpBranch %101
1876         %101 = OpLabel
1877                OpReturn
1878                OpFunctionEnd
1879 )";
1880 
1881   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1882 }
1883 
1884 }  // namespace
1885 }  // namespace fuzz
1886 }  // namespace spvtools