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_duplicate_region_with_selection.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(TransformationDuplicateRegionWithSelectionTest, BasicUseTest)26 TEST(TransformationDuplicateRegionWithSelectionTest, BasicUseTest) {
27   // This test handles a case where the ids from the original region are used in
28   // subsequent block.
29 
30   std::string shader = R"(
31                OpCapability Shader
32                OpCapability VariablePointers
33           %1 = OpExtInstImport "GLSL.std.450"
34                OpMemoryModel Logical GLSL450
35                OpEntryPoint Fragment %4 "main"
36                OpExecutionMode %4 OriginUpperLeft
37                OpSource ESSL 310
38                OpName %4 "main"
39                OpName %10 "fun(i1;"
40                OpName %9 "a"
41                OpName %12 "b"
42                OpName %18 "c"
43                OpName %20 "param"
44           %2 = OpTypeVoid
45           %3 = OpTypeFunction %2
46           %6 = OpTypeInt 32 1
47           %7 = OpTypePointer Function %6
48           %8 = OpTypeFunction %2 %7
49          %14 = OpConstant %6 2
50          %16 = OpTypeBool
51          %17 = OpTypePointer Function %16
52          %19 = OpConstantTrue %16
53           %4 = OpFunction %2 None %3
54           %5 = OpLabel
55          %18 = OpVariable %17 Function
56          %20 = OpVariable %7 Function
57                OpStore %18 %19
58                OpStore %20 %14
59          %21 = OpFunctionCall %2 %10 %20
60                OpReturn
61                OpFunctionEnd
62          %10 = OpFunction %2 None %8
63           %9 = OpFunctionParameter %7
64          %11 = OpLabel
65          %12 = OpVariable %7 Function
66                OpBranch %800
67         %800 = OpLabel
68          %13 = OpLoad %6 %9
69          %15 = OpIAdd %6 %13 %14
70                OpStore %12 %15
71                OpBranch %900
72          %900 = OpLabel
73          %901 = OpIAdd %6 %15 %13
74          %902 = OpISub %6 %13 %15
75                OpReturn
76                OpFunctionEnd
77     )";
78 
79   const auto env = SPV_ENV_UNIVERSAL_1_4;
80   const auto consumer = nullptr;
81   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
82   spvtools::ValidatorOptions validator_options;
83   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
84                                                kConsoleMessageConsumer));
85   TransformationContext transformation_context(
86       MakeUnique<FactManager>(context.get()), validator_options);
87   TransformationDuplicateRegionWithSelection transformation_good_1 =
88       TransformationDuplicateRegionWithSelection(
89           500, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
90           {{13, 301}, {15, 302}});
91 
92   ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
93                                                  transformation_context));
94   ApplyAndCheckFreshIds(transformation_good_1, context.get(),
95                         &transformation_context);
96 
97   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
98                                                kConsoleMessageConsumer));
99   std::string expected_shader = R"(
100                OpCapability Shader
101                OpCapability VariablePointers
102           %1 = OpExtInstImport "GLSL.std.450"
103                OpMemoryModel Logical GLSL450
104                OpEntryPoint Fragment %4 "main"
105                OpExecutionMode %4 OriginUpperLeft
106                OpSource ESSL 310
107                OpName %4 "main"
108                OpName %10 "fun(i1;"
109                OpName %9 "a"
110                OpName %12 "b"
111                OpName %18 "c"
112                OpName %20 "param"
113           %2 = OpTypeVoid
114           %3 = OpTypeFunction %2
115           %6 = OpTypeInt 32 1
116           %7 = OpTypePointer Function %6
117           %8 = OpTypeFunction %2 %7
118          %14 = OpConstant %6 2
119          %16 = OpTypeBool
120          %17 = OpTypePointer Function %16
121          %19 = OpConstantTrue %16
122           %4 = OpFunction %2 None %3
123           %5 = OpLabel
124          %18 = OpVariable %17 Function
125          %20 = OpVariable %7 Function
126                OpStore %18 %19
127                OpStore %20 %14
128          %21 = OpFunctionCall %2 %10 %20
129                OpReturn
130                OpFunctionEnd
131          %10 = OpFunction %2 None %8
132           %9 = OpFunctionParameter %7
133          %11 = OpLabel
134          %12 = OpVariable %7 Function
135                OpBranch %500
136         %500 = OpLabel
137                OpSelectionMerge %501 None
138                OpBranchConditional %19 %800 %100
139         %800 = OpLabel
140          %13 = OpLoad %6 %9
141          %15 = OpIAdd %6 %13 %14
142                OpStore %12 %15
143                OpBranch %501
144         %100 = OpLabel
145         %201 = OpLoad %6 %9
146         %202 = OpIAdd %6 %201 %14
147                OpStore %12 %202
148                OpBranch %501
149         %501 = OpLabel
150         %301 = OpPhi %6 %13 %800 %201 %100
151         %302 = OpPhi %6 %15 %800 %202 %100
152                OpBranch %900
153         %900 = OpLabel
154         %901 = OpIAdd %6 %302 %301
155         %902 = OpISub %6 %301 %302
156                OpReturn
157                OpFunctionEnd
158   )";
159   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
160 }
161 
162 TEST(TransformationDuplicateRegionWithSelectionTest, BasicExitBlockTest) {
163   // This test handles a case where the exit block of the region is the exit
164   // block of the containing function.
165 
166   std::string shader = R"(
167                OpCapability Shader
168                OpCapability VariablePointers
169           %1 = OpExtInstImport "GLSL.std.450"
170                OpMemoryModel Logical GLSL450
171                OpEntryPoint Fragment %4 "main"
172                OpExecutionMode %4 OriginUpperLeft
173                OpSource ESSL 310
174                OpName %4 "main"
175                OpName %10 "fun(i1;"
176                OpName %9 "a"
177                OpName %12 "b"
178                OpName %18 "c"
179                OpName %20 "param"
180           %2 = OpTypeVoid
181           %3 = OpTypeFunction %2
182           %6 = OpTypeInt 32 1
183           %7 = OpTypePointer Function %6
184           %8 = OpTypeFunction %2 %7
185          %14 = OpConstant %6 2
186          %16 = OpTypeBool
187          %17 = OpTypePointer Function %16
188          %19 = OpConstantTrue %16
189           %4 = OpFunction %2 None %3
190           %5 = OpLabel
191          %18 = OpVariable %17 Function
192          %20 = OpVariable %7 Function
193                OpStore %18 %19
194                OpStore %20 %14
195          %21 = OpFunctionCall %2 %10 %20
196                OpReturn
197                OpFunctionEnd
198          %10 = OpFunction %2 None %8
199           %9 = OpFunctionParameter %7
200          %11 = OpLabel
201          %12 = OpVariable %7 Function
202                OpBranch %800
203         %800 = OpLabel
204          %13 = OpLoad %6 %9
205          %15 = OpIAdd %6 %13 %14
206                OpStore %12 %15
207                OpReturn
208                OpFunctionEnd
209     )";
210 
211   const auto env = SPV_ENV_UNIVERSAL_1_4;
212   const auto consumer = nullptr;
213   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
214   spvtools::ValidatorOptions validator_options;
215   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
216                                                kConsoleMessageConsumer));
217   TransformationContext transformation_context(
218       MakeUnique<FactManager>(context.get()), validator_options);
219   TransformationDuplicateRegionWithSelection transformation_good_1 =
220       TransformationDuplicateRegionWithSelection(
221           500, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
222           {{13, 301}, {15, 302}});
223 
224   ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
225                                                  transformation_context));
226   ApplyAndCheckFreshIds(transformation_good_1, context.get(),
227                         &transformation_context);
228 
229   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
230                                                kConsoleMessageConsumer));
231 
232   std::string expected_shader = R"(
233    OpCapability Shader
234                OpCapability VariablePointers
235           %1 = OpExtInstImport "GLSL.std.450"
236                OpMemoryModel Logical GLSL450
237                OpEntryPoint Fragment %4 "main"
238                OpExecutionMode %4 OriginUpperLeft
239                OpSource ESSL 310
240                OpName %4 "main"
241                OpName %10 "fun(i1;"
242                OpName %9 "a"
243                OpName %12 "b"
244                OpName %18 "c"
245                OpName %20 "param"
246           %2 = OpTypeVoid
247           %3 = OpTypeFunction %2
248           %6 = OpTypeInt 32 1
249           %7 = OpTypePointer Function %6
250           %8 = OpTypeFunction %2 %7
251          %14 = OpConstant %6 2
252          %16 = OpTypeBool
253          %17 = OpTypePointer Function %16
254          %19 = OpConstantTrue %16
255           %4 = OpFunction %2 None %3
256           %5 = OpLabel
257          %18 = OpVariable %17 Function
258          %20 = OpVariable %7 Function
259                OpStore %18 %19
260                OpStore %20 %14
261          %21 = OpFunctionCall %2 %10 %20
262                OpReturn
263                OpFunctionEnd
264          %10 = OpFunction %2 None %8
265           %9 = OpFunctionParameter %7
266          %11 = OpLabel
267          %12 = OpVariable %7 Function
268                OpBranch %500
269         %500 = OpLabel
270                OpSelectionMerge %501 None
271                OpBranchConditional %19 %800 %100
272         %800 = OpLabel
273          %13 = OpLoad %6 %9
274          %15 = OpIAdd %6 %13 %14
275                OpStore %12 %15
276                OpBranch %501
277         %100 = OpLabel
278         %201 = OpLoad %6 %9
279         %202 = OpIAdd %6 %201 %14
280                OpStore %12 %202
281                OpBranch %501
282         %501 = OpLabel
283         %301 = OpPhi %6 %13 %800 %201 %100
284         %302 = OpPhi %6 %15 %800 %202 %100
285                OpReturn
286                OpFunctionEnd
287 
288   )";
289   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
290 }
291 
292 TEST(TransformationDuplicateRegionWithSelectionTest, NotApplicableCFGTest) {
293   // This test handles few cases where the transformation is not applicable
294   // because of the control flow graph or layout of the blocks.
295 
296   std::string shader = R"(
297                OpCapability Shader
298           %1 = OpExtInstImport "GLSL.std.450"
299                OpMemoryModel Logical GLSL450
300                OpEntryPoint Fragment %4 "main"
301                OpExecutionMode %4 OriginUpperLeft
302                OpSource ESSL 310
303                OpName %4 "main"
304                OpName %10 "fun(i1;"
305                OpName %9 "a"
306                OpName %18 "b"
307                OpName %25 "c"
308                OpName %27 "param"
309           %2 = OpTypeVoid
310           %3 = OpTypeFunction %2
311           %6 = OpTypeInt 32 1
312           %7 = OpTypePointer Function %6
313           %8 = OpTypeFunction %2 %7
314          %13 = OpConstant %6 2
315          %14 = OpTypeBool
316          %24 = OpTypePointer Function %14
317          %26 = OpConstantTrue %14
318           %4 = OpFunction %2 None %3
319           %5 = OpLabel
320          %25 = OpVariable %24 Function
321          %27 = OpVariable %7 Function
322                OpStore %25 %26
323                OpStore %27 %13
324          %28 = OpFunctionCall %2 %10 %27
325                OpReturn
326                OpFunctionEnd
327          %10 = OpFunction %2 None %8
328           %9 = OpFunctionParameter %7
329          %11 = OpLabel
330          %18 = OpVariable %7 Function
331          %12 = OpLoad %6 %9
332          %15 = OpSLessThan %14 %12 %13
333                OpSelectionMerge %17 None
334                OpBranchConditional %15 %16 %21
335          %16 = OpLabel
336          %19 = OpLoad %6 %9
337          %20 = OpIAdd %6 %19 %13
338                OpStore %18 %20
339                OpBranch %17
340          %21 = OpLabel
341          %22 = OpLoad %6 %9
342          %23 = OpISub %6 %22 %13
343                OpStore %18 %23
344                OpBranch %17
345          %17 = OpLabel
346                OpReturn
347                OpFunctionEnd
348     )";
349 
350   const auto env = SPV_ENV_UNIVERSAL_1_4;
351   const auto consumer = nullptr;
352   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
353   spvtools::ValidatorOptions validator_options;
354   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
355                                                kConsoleMessageConsumer));
356   TransformationContext transformation_context(
357       MakeUnique<FactManager>(context.get()), validator_options);
358   // Bad: |entry_block_id| refers to the entry block of the function (this
359   // transformation currently avoids such cases).
360   TransformationDuplicateRegionWithSelection transformation_bad_1 =
361       TransformationDuplicateRegionWithSelection(
362           500, 26, 501, 11, 11, {{11, 100}}, {{18, 201}, {12, 202}, {15, 203}},
363           {{18, 301}, {12, 302}, {15, 303}});
364   ASSERT_FALSE(
365       transformation_bad_1.IsApplicable(context.get(), transformation_context));
366 
367   // Bad: The block with id 16 does not dominate the block with id 21.
368   TransformationDuplicateRegionWithSelection transformation_bad_2 =
369       TransformationDuplicateRegionWithSelection(
370           500, 26, 501, 16, 21, {{16, 100}, {21, 101}},
371           {{19, 201}, {20, 202}, {22, 203}, {23, 204}},
372           {{19, 301}, {20, 302}, {22, 303}, {23, 304}});
373   ASSERT_FALSE(
374       transformation_bad_2.IsApplicable(context.get(), transformation_context));
375 
376   // Bad: The block with id 21 does not post-dominate the block with id 11.
377   TransformationDuplicateRegionWithSelection transformation_bad_3 =
378       TransformationDuplicateRegionWithSelection(
379           500, 26, 501, 11, 21, {{11, 100}, {21, 101}},
380           {{18, 201}, {12, 202}, {15, 203}, {22, 204}, {23, 205}},
381           {{18, 301}, {12, 302}, {15, 303}, {22, 304}, {23, 305}});
382   ASSERT_FALSE(
383       transformation_bad_3.IsApplicable(context.get(), transformation_context));
384 
385   // Bad: The block with id 5 is contained in a different function than the
386   // block with id 11.
387   TransformationDuplicateRegionWithSelection transformation_bad_4 =
388       TransformationDuplicateRegionWithSelection(
389           500, 26, 501, 5, 11, {{5, 100}, {11, 101}},
390           {{25, 201}, {27, 202}, {28, 203}, {18, 204}, {12, 205}, {15, 206}},
391           {{25, 301}, {27, 302}, {28, 303}, {18, 304}, {12, 305}, {15, 306}});
392   ASSERT_FALSE(
393       transformation_bad_4.IsApplicable(context.get(), transformation_context));
394 }
395 
396 TEST(TransformationDuplicateRegionWithSelectionTest, NotApplicableIdTest) {
397   // This test handles a case where the supplied ids are either not fresh, not
398   // distinct, not valid in their context or do not refer to the existing
399   // instructions.
400 
401   std::string shader = R"(
402                OpCapability Shader
403                OpCapability VariablePointers
404           %1 = OpExtInstImport "GLSL.std.450"
405                OpMemoryModel Logical GLSL450
406                OpEntryPoint Fragment %4 "main"
407                OpExecutionMode %4 OriginUpperLeft
408                OpSource ESSL 310
409                OpName %4 "main"
410                OpName %10 "fun(i1;"
411                OpName %9 "a"
412                OpName %12 "b"
413                OpName %18 "c"
414                OpName %20 "param"
415           %2 = OpTypeVoid
416           %3 = OpTypeFunction %2
417           %6 = OpTypeInt 32 1
418           %7 = OpTypePointer Function %6
419           %8 = OpTypeFunction %2 %7
420          %14 = OpConstant %6 2
421          %16 = OpTypeBool
422          %17 = OpTypePointer Function %16
423          %19 = OpConstantTrue %16
424           %4 = OpFunction %2 None %3
425           %5 = OpLabel
426          %18 = OpVariable %17 Function
427          %20 = OpVariable %7 Function
428                OpStore %18 %19
429                OpStore %20 %14
430          %21 = OpFunctionCall %2 %10 %20
431                OpReturn
432                OpFunctionEnd
433          %10 = OpFunction %2 None %8
434           %9 = OpFunctionParameter %7
435          %11 = OpLabel
436          %12 = OpVariable %7 Function
437                OpBranch %800
438         %800 = OpLabel
439          %13 = OpLoad %6 %9
440          %15 = OpIAdd %6 %13 %14
441                OpStore %12 %15
442                OpReturn
443                OpFunctionEnd
444     )";
445 
446   const auto env = SPV_ENV_UNIVERSAL_1_4;
447   const auto consumer = nullptr;
448   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
449   spvtools::ValidatorOptions validator_options;
450   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
451                                                kConsoleMessageConsumer));
452   TransformationContext transformation_context(
453       MakeUnique<FactManager>(context.get()), validator_options);
454   // Bad: A value in the |original_label_to_duplicate_label| is not a fresh id.
455   TransformationDuplicateRegionWithSelection transformation_bad_1 =
456       TransformationDuplicateRegionWithSelection(
457           500, 19, 501, 800, 800, {{800, 21}}, {{13, 201}, {15, 202}},
458           {{13, 301}, {15, 302}});
459 
460   ASSERT_FALSE(
461       transformation_bad_1.IsApplicable(context.get(), transformation_context));
462 
463   // Bad: Values in the |original_id_to_duplicate_id| are not distinct.
464   TransformationDuplicateRegionWithSelection transformation_bad_2 =
465       TransformationDuplicateRegionWithSelection(
466           500, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 201}},
467           {{13, 301}, {15, 302}});
468   ASSERT_FALSE(
469       transformation_bad_2.IsApplicable(context.get(), transformation_context));
470 
471   // Bad: Values in the |original_id_to_phi_id| are not fresh and are not
472   // distinct with previous values.
473   TransformationDuplicateRegionWithSelection transformation_bad_3 =
474       TransformationDuplicateRegionWithSelection(
475           500, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
476           {{13, 18}, {15, 202}});
477   ASSERT_FALSE(
478       transformation_bad_3.IsApplicable(context.get(), transformation_context));
479 
480   // Bad: |entry_block_id| does not refer to an existing instruction.
481   TransformationDuplicateRegionWithSelection transformation_bad_4 =
482       TransformationDuplicateRegionWithSelection(
483           500, 19, 501, 802, 800, {{800, 100}}, {{13, 201}, {15, 202}},
484           {{13, 301}, {15, 302}});
485   ASSERT_FALSE(
486       transformation_bad_4.IsApplicable(context.get(), transformation_context));
487 
488   // Bad: |exit_block_id| does not refer to a block.
489   TransformationDuplicateRegionWithSelection transformation_bad_5 =
490       TransformationDuplicateRegionWithSelection(
491           500, 19, 501, 800, 9, {{800, 100}}, {{13, 201}, {15, 202}},
492           {{13, 301}, {15, 302}});
493   ASSERT_FALSE(
494       transformation_bad_5.IsApplicable(context.get(), transformation_context));
495 
496   // Bad: |new_entry_fresh_id| is not fresh.
497   TransformationDuplicateRegionWithSelection transformation_bad_6 =
498       TransformationDuplicateRegionWithSelection(
499           20, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
500           {{13, 301}, {15, 302}});
501   ASSERT_FALSE(
502       transformation_bad_6.IsApplicable(context.get(), transformation_context));
503 
504   // Bad: |merge_label_fresh_id| is not fresh.
505   TransformationDuplicateRegionWithSelection transformation_bad_7 =
506       TransformationDuplicateRegionWithSelection(
507           500, 19, 20, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
508           {{13, 301}, {15, 302}});
509   ASSERT_FALSE(
510       transformation_bad_7.IsApplicable(context.get(), transformation_context));
511 
512 #ifndef NDEBUG
513   // Bad: Instruction with id 15 is from the original region and is available
514   // at the end of the region but it is not present in the
515   // |original_id_to_phi_id|.
516   TransformationDuplicateRegionWithSelection transformation_bad_8 =
517       TransformationDuplicateRegionWithSelection(
518           500, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
519           {{13, 301}});
520   ASSERT_DEATH(
521       transformation_bad_8.IsApplicable(context.get(), transformation_context),
522       "Bad attempt to query whether overflow ids are available.");
523 
524   // Bad: Instruction with id 15 is from the original region but it is
525   // not present in the |original_id_to_duplicate_id|.
526   TransformationDuplicateRegionWithSelection transformation_bad_9 =
527       TransformationDuplicateRegionWithSelection(500, 19, 501, 800, 800,
528                                                  {{800, 100}}, {{13, 201}},
529                                                  {{13, 301}, {15, 302}});
530   ASSERT_DEATH(
531       transformation_bad_9.IsApplicable(context.get(), transformation_context),
532       "Bad attempt to query whether overflow ids are available.");
533 #endif
534 
535   // Bad: |condition_id| does not refer to the valid instruction.
536   TransformationDuplicateRegionWithSelection transformation_bad_10 =
537       TransformationDuplicateRegionWithSelection(
538           500, 200, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
539           {{13, 301}, {15, 302}});
540 
541   ASSERT_FALSE(transformation_bad_10.IsApplicable(context.get(),
542                                                   transformation_context));
543 
544   // Bad: |condition_id| does not refer to the instruction of type OpTypeBool
545   TransformationDuplicateRegionWithSelection transformation_bad_11 =
546       TransformationDuplicateRegionWithSelection(
547           500, 14, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
548           {{13, 301}, {15, 302}});
549 
550   ASSERT_FALSE(transformation_bad_11.IsApplicable(context.get(),
551                                                   transformation_context));
552 }
553 
554 TEST(TransformationDuplicateRegionWithSelectionTest, NotApplicableCFGTest2) {
555   // This test handles few cases where the transformation is not applicable
556   // because of the control flow graph or the layout of the blocks.
557 
558   std::string shader = R"(
559                OpCapability Shader
560           %1 = OpExtInstImport "GLSL.std.450"
561                OpMemoryModel Logical GLSL450
562                OpEntryPoint Fragment %4 "main"
563                OpExecutionMode %4 OriginUpperLeft
564                OpSource ESSL 310
565                OpName %4 "main"
566                OpName %6 "fun("
567                OpName %10 "s"
568                OpName %12 "i"
569                OpName %29 "b"
570           %2 = OpTypeVoid
571           %3 = OpTypeFunction %2
572           %8 = OpTypeInt 32 1
573           %9 = OpTypePointer Function %8
574          %11 = OpConstant %8 0
575          %19 = OpConstant %8 10
576          %20 = OpTypeBool
577          %26 = OpConstant %8 1
578          %28 = OpTypePointer Function %20
579          %30 = OpConstantTrue %20
580           %4 = OpFunction %2 None %3
581           %5 = OpLabel
582          %29 = OpVariable %28 Function
583                OpStore %29 %30
584          %31 = OpFunctionCall %2 %6
585                OpReturn
586                OpFunctionEnd
587           %6 = OpFunction %2 None %3
588           %7 = OpLabel
589          %10 = OpVariable %9 Function
590          %12 = OpVariable %9 Function
591                OpStore %10 %11
592                OpStore %12 %11
593                OpBranch %13
594          %13 = OpLabel
595                OpLoopMerge %15 %16 None
596                OpBranch %17
597          %17 = OpLabel
598          %18 = OpLoad %8 %12
599          %21 = OpSLessThan %20 %18 %19
600                OpBranchConditional %21 %14 %15
601          %14 = OpLabel
602          %22 = OpLoad %8 %10
603          %23 = OpLoad %8 %12
604          %24 = OpIAdd %8 %22 %23
605                OpStore %10 %24
606                OpBranch %16
607          %16 = OpLabel
608                OpBranch %50
609          %50 = OpLabel
610          %25 = OpLoad %8 %12
611          %27 = OpIAdd %8 %25 %26
612                OpStore %12 %27
613                OpBranch %13
614          %15 = OpLabel
615                OpReturn
616                OpFunctionEnd
617     )";
618 
619   const auto env = SPV_ENV_UNIVERSAL_1_4;
620   const auto consumer = nullptr;
621   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
622   spvtools::ValidatorOptions validator_options;
623   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
624                                                kConsoleMessageConsumer));
625   TransformationContext transformation_context(
626       MakeUnique<FactManager>(context.get()), validator_options);
627   // Bad: The exit block cannot be a header of a loop, because the region won't
628   // be a single-entry, single-exit region.
629   TransformationDuplicateRegionWithSelection transformation_bad_1 =
630       TransformationDuplicateRegionWithSelection(500, 30, 501, 13, 13,
631                                                  {{13, 100}}, {{}}, {{}});
632   ASSERT_FALSE(
633       transformation_bad_1.IsApplicable(context.get(), transformation_context));
634 
635   // Bad: The block with id 13, the loop header, is in the region. The block
636   // with id 15, the loop merge block, is not in the region.
637   TransformationDuplicateRegionWithSelection transformation_bad_2 =
638       TransformationDuplicateRegionWithSelection(
639           500, 30, 501, 13, 17, {{13, 100}, {17, 101}}, {{18, 201}, {21, 202}},
640           {{18, 301}, {21, 302}});
641   ASSERT_FALSE(
642       transformation_bad_2.IsApplicable(context.get(), transformation_context));
643 
644   // Bad: The block with id 13, the loop header, is not in the region. The block
645   // with id 16, the loop continue target, is in the region.
646   TransformationDuplicateRegionWithSelection transformation_bad_3 =
647       TransformationDuplicateRegionWithSelection(
648           500, 30, 501, 16, 50, {{16, 100}, {50, 101}}, {{25, 201}, {27, 202}},
649           {{25, 301}, {27, 302}});
650   ASSERT_FALSE(
651       transformation_bad_3.IsApplicable(context.get(), transformation_context));
652 }
653 
654 TEST(TransformationDuplicateRegionWithSelectionTest, NotApplicableCFGTest3) {
655   // This test handles a case where for the block which is not the exit block,
656   // not all successors are in the region.
657 
658   std::string shader = R"(
659                OpCapability Shader
660           %1 = OpExtInstImport "GLSL.std.450"
661                OpMemoryModel Logical GLSL450
662                OpEntryPoint Fragment %4 "main"
663                OpExecutionMode %4 OriginUpperLeft
664                OpSource ESSL 310
665                OpName %4 "main"
666                OpName %6 "fun("
667                OpName %14 "a"
668                OpName %19 "b"
669           %2 = OpTypeVoid
670           %3 = OpTypeFunction %2
671           %8 = OpTypeBool
672           %9 = OpConstantTrue %8
673          %12 = OpTypeInt 32 1
674          %13 = OpTypePointer Function %12
675          %15 = OpConstant %12 2
676          %17 = OpConstant %12 3
677          %18 = OpTypePointer Function %8
678           %4 = OpFunction %2 None %3
679           %5 = OpLabel
680          %19 = OpVariable %18 Function
681                OpStore %19 %9
682          %20 = OpFunctionCall %2 %6
683                OpReturn
684                OpFunctionEnd
685           %6 = OpFunction %2 None %3
686           %7 = OpLabel
687          %14 = OpVariable %13 Function
688                OpSelectionMerge %11 None
689                OpBranchConditional %9 %10 %16
690          %10 = OpLabel
691                OpStore %14 %15
692                OpBranch %11
693          %16 = OpLabel
694                OpStore %14 %17
695                OpBranch %11
696          %11 = OpLabel
697                OpReturn
698                OpFunctionEnd
699     )";
700 
701   const auto env = SPV_ENV_UNIVERSAL_1_4;
702   const auto consumer = nullptr;
703   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
704   spvtools::ValidatorOptions validator_options;
705   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
706                                                kConsoleMessageConsumer));
707   TransformationContext transformation_context(
708       MakeUnique<FactManager>(context.get()), validator_options);
709   // Bad: The block with id 7, which is not an exit block, has two successors:
710   // the block with id 10 and the block with id 16. The block with id 16 is not
711   // in the region.
712   TransformationDuplicateRegionWithSelection transformation_bad_1 =
713       TransformationDuplicateRegionWithSelection(
714           500, 30, 501, 7, 10, {{13, 100}}, {{14, 201}}, {{14, 301}});
715   ASSERT_FALSE(
716       transformation_bad_1.IsApplicable(context.get(), transformation_context));
717 }
718 
719 TEST(TransformationDuplicateRegionWithSelectionTest, MultipleBlocksLoopTest) {
720   // This test handles a case where the region consists of multiple blocks
721   // (they form a loop). The transformation is applicable and the region is
722   // duplicated.
723 
724   std::string shader = R"(
725                OpCapability Shader
726           %1 = OpExtInstImport "GLSL.std.450"
727                OpMemoryModel Logical GLSL450
728                OpEntryPoint Fragment %4 "main"
729                OpExecutionMode %4 OriginUpperLeft
730                OpSource ESSL 310
731                OpName %4 "main"
732                OpName %6 "fun("
733                OpName %10 "s"
734                OpName %12 "i"
735                OpName %29 "b"
736           %2 = OpTypeVoid
737           %3 = OpTypeFunction %2
738           %8 = OpTypeInt 32 1
739           %9 = OpTypePointer Function %8
740          %11 = OpConstant %8 0
741          %19 = OpConstant %8 10
742          %20 = OpTypeBool
743          %26 = OpConstant %8 1
744          %28 = OpTypePointer Function %20
745          %30 = OpConstantTrue %20
746           %4 = OpFunction %2 None %3
747           %5 = OpLabel
748          %29 = OpVariable %28 Function
749                OpStore %29 %30
750          %31 = OpFunctionCall %2 %6
751                OpReturn
752                OpFunctionEnd
753           %6 = OpFunction %2 None %3
754           %7 = OpLabel
755          %10 = OpVariable %9 Function
756          %12 = OpVariable %9 Function
757                OpStore %10 %11
758                OpStore %12 %11
759                OpBranch %50
760          %50 = OpLabel
761                OpBranch %13
762          %13 = OpLabel
763                OpLoopMerge %15 %16 None
764                OpBranch %17
765          %17 = OpLabel
766          %18 = OpLoad %8 %12
767          %21 = OpSLessThan %20 %18 %19
768                OpBranchConditional %21 %14 %15
769          %14 = OpLabel
770          %22 = OpLoad %8 %10
771          %23 = OpLoad %8 %12
772          %24 = OpIAdd %8 %22 %23
773                OpStore %10 %24
774                OpBranch %16
775          %16 = OpLabel
776          %25 = OpLoad %8 %12
777          %27 = OpIAdd %8 %25 %26
778                OpStore %12 %27
779                OpBranch %13
780          %15 = OpLabel
781                OpReturn
782                OpFunctionEnd
783     )";
784 
785   const auto env = SPV_ENV_UNIVERSAL_1_4;
786   const auto consumer = nullptr;
787   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
788   spvtools::ValidatorOptions validator_options;
789   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
790                                                kConsoleMessageConsumer));
791   TransformationContext transformation_context(
792       MakeUnique<FactManager>(context.get()), validator_options);
793   TransformationDuplicateRegionWithSelection transformation_good_1 =
794       TransformationDuplicateRegionWithSelection(
795           500, 30, 501, 50, 15,
796           {{50, 100}, {13, 101}, {14, 102}, {15, 103}, {16, 104}, {17, 105}},
797           {{22, 201},
798            {23, 202},
799            {24, 203},
800            {25, 204},
801            {27, 205},
802            {18, 206},
803            {21, 207}},
804           {{22, 301},
805            {23, 302},
806            {24, 303},
807            {25, 304},
808            {27, 305},
809            {18, 306},
810            {21, 307}});
811   ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
812                                                  transformation_context));
813   ApplyAndCheckFreshIds(transformation_good_1, context.get(),
814                         &transformation_context);
815   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
816                                                kConsoleMessageConsumer));
817 
818   std::string expected_shader = R"(
819                  OpCapability Shader
820           %1 = OpExtInstImport "GLSL.std.450"
821                OpMemoryModel Logical GLSL450
822                OpEntryPoint Fragment %4 "main"
823                OpExecutionMode %4 OriginUpperLeft
824                OpSource ESSL 310
825                OpName %4 "main"
826                OpName %6 "fun("
827                OpName %10 "s"
828                OpName %12 "i"
829                OpName %29 "b"
830           %2 = OpTypeVoid
831           %3 = OpTypeFunction %2
832           %8 = OpTypeInt 32 1
833           %9 = OpTypePointer Function %8
834          %11 = OpConstant %8 0
835          %19 = OpConstant %8 10
836          %20 = OpTypeBool
837          %26 = OpConstant %8 1
838          %28 = OpTypePointer Function %20
839          %30 = OpConstantTrue %20
840           %4 = OpFunction %2 None %3
841           %5 = OpLabel
842          %29 = OpVariable %28 Function
843                OpStore %29 %30
844          %31 = OpFunctionCall %2 %6
845                OpReturn
846                OpFunctionEnd
847           %6 = OpFunction %2 None %3
848           %7 = OpLabel
849          %10 = OpVariable %9 Function
850          %12 = OpVariable %9 Function
851                OpStore %10 %11
852                OpStore %12 %11
853                OpBranch %500
854         %500 = OpLabel
855                OpSelectionMerge %501 None
856                OpBranchConditional %30 %50 %100
857          %50 = OpLabel
858                OpBranch %13
859          %13 = OpLabel
860                OpLoopMerge %15 %16 None
861                OpBranch %17
862          %17 = OpLabel
863          %18 = OpLoad %8 %12
864          %21 = OpSLessThan %20 %18 %19
865                OpBranchConditional %21 %14 %15
866          %14 = OpLabel
867          %22 = OpLoad %8 %10
868          %23 = OpLoad %8 %12
869          %24 = OpIAdd %8 %22 %23
870                OpStore %10 %24
871                OpBranch %16
872          %16 = OpLabel
873          %25 = OpLoad %8 %12
874          %27 = OpIAdd %8 %25 %26
875                OpStore %12 %27
876                OpBranch %13
877          %15 = OpLabel
878                OpBranch %501
879         %100 = OpLabel
880                OpBranch %101
881         %101 = OpLabel
882                OpLoopMerge %103 %104 None
883                OpBranch %105
884         %105 = OpLabel
885         %206 = OpLoad %8 %12
886         %207 = OpSLessThan %20 %206 %19
887                OpBranchConditional %207 %102 %103
888         %102 = OpLabel
889         %201 = OpLoad %8 %10
890         %202 = OpLoad %8 %12
891         %203 = OpIAdd %8 %201 %202
892                OpStore %10 %203
893                OpBranch %104
894         %104 = OpLabel
895         %204 = OpLoad %8 %12
896         %205 = OpIAdd %8 %204 %26
897                OpStore %12 %205
898                OpBranch %101
899         %103 = OpLabel
900                OpBranch %501
901         %501 = OpLabel
902         %306 = OpPhi %8 %18 %15 %206 %103
903         %307 = OpPhi %20 %21 %15 %207 %103
904                OpReturn
905                OpFunctionEnd
906     )";
907   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
908 }
909 
910 TEST(TransformationDuplicateRegionWithSelectionTest,
911      ResolvingOpPhiExitBlockTest) {
912   // This test handles a case where the region under the transformation is
913   // referenced in OpPhi instructions. Since the new merge block becomes the
914   // exit of the region, these OpPhi instructions need to be updated.
915 
916   std::string shader = R"(
917                OpCapability Shader
918           %1 = OpExtInstImport "GLSL.std.450"
919                OpMemoryModel Logical GLSL450
920                OpEntryPoint Fragment %4 "main"
921                OpExecutionMode %4 OriginUpperLeft
922                OpSource ESSL 310
923                OpName %4 "main"
924                OpName %10 "fun(i1;"
925                OpName %9 "a"
926                OpName %12 "s"
927                OpName %26 "b"
928                OpName %29 "param"
929           %2 = OpTypeVoid
930           %3 = OpTypeFunction %2
931           %6 = OpTypeInt 32 1
932           %7 = OpTypePointer Function %6
933           %8 = OpTypeFunction %2 %7
934          %13 = OpConstant %6 0
935          %15 = OpConstant %6 2
936          %16 = OpTypeBool
937          %25 = OpTypePointer Function %16
938          %27 = OpConstantTrue %16
939          %28 = OpConstant %6 3
940           %4 = OpFunction %2 None %3
941           %5 = OpLabel
942          %26 = OpVariable %25 Function
943          %29 = OpVariable %7 Function
944                OpStore %26 %27
945                OpStore %29 %28
946          %30 = OpFunctionCall %2 %10 %29
947                OpReturn
948                OpFunctionEnd
949          %10 = OpFunction %2 None %8
950           %9 = OpFunctionParameter %7
951          %11 = OpLabel
952          %12 = OpVariable %7 Function
953                OpStore %12 %13
954          %14 = OpLoad %6 %9
955          %17 = OpSLessThan %16 %14 %15
956                OpSelectionMerge %19 None
957                OpBranchConditional %17 %18 %22
958          %18 = OpLabel
959          %20 = OpLoad %6 %9
960          %21 = OpIAdd %6 %20 %15
961                OpStore %12 %21
962                OpBranch %19
963          %22 = OpLabel
964          %23 = OpLoad %6 %9
965          %24 = OpIMul %6 %23 %15
966                OpStore %12 %24
967                OpBranch %19
968          %19 = OpLabel
969          %40 = OpPhi %6 %21 %18 %24 %22
970                OpReturn
971                OpFunctionEnd
972     )";
973 
974   const auto env = SPV_ENV_UNIVERSAL_1_4;
975   const auto consumer = nullptr;
976   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
977   spvtools::ValidatorOptions validator_options;
978   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
979                                                kConsoleMessageConsumer));
980   TransformationContext transformation_context(
981       MakeUnique<FactManager>(context.get()), validator_options);
982 
983   TransformationDuplicateRegionWithSelection transformation_good_1 =
984       TransformationDuplicateRegionWithSelection(
985           500, 27, 501, 22, 22, {{22, 100}}, {{23, 201}, {24, 202}},
986           {{23, 301}, {24, 302}});
987 
988   ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
989                                                  transformation_context));
990   ApplyAndCheckFreshIds(transformation_good_1, context.get(),
991                         &transformation_context);
992   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
993                                                kConsoleMessageConsumer));
994 
995   std::string expected_shader = R"(
996                OpCapability Shader
997           %1 = OpExtInstImport "GLSL.std.450"
998                OpMemoryModel Logical GLSL450
999                OpEntryPoint Fragment %4 "main"
1000                OpExecutionMode %4 OriginUpperLeft
1001                OpSource ESSL 310
1002                OpName %4 "main"
1003                OpName %10 "fun(i1;"
1004                OpName %9 "a"
1005                OpName %12 "s"
1006                OpName %26 "b"
1007                OpName %29 "param"
1008           %2 = OpTypeVoid
1009           %3 = OpTypeFunction %2
1010           %6 = OpTypeInt 32 1
1011           %7 = OpTypePointer Function %6
1012           %8 = OpTypeFunction %2 %7
1013          %13 = OpConstant %6 0
1014          %15 = OpConstant %6 2
1015          %16 = OpTypeBool
1016          %25 = OpTypePointer Function %16
1017          %27 = OpConstantTrue %16
1018          %28 = OpConstant %6 3
1019           %4 = OpFunction %2 None %3
1020           %5 = OpLabel
1021          %26 = OpVariable %25 Function
1022          %29 = OpVariable %7 Function
1023                OpStore %26 %27
1024                OpStore %29 %28
1025          %30 = OpFunctionCall %2 %10 %29
1026                OpReturn
1027                OpFunctionEnd
1028          %10 = OpFunction %2 None %8
1029           %9 = OpFunctionParameter %7
1030          %11 = OpLabel
1031          %12 = OpVariable %7 Function
1032                OpStore %12 %13
1033          %14 = OpLoad %6 %9
1034          %17 = OpSLessThan %16 %14 %15
1035                OpSelectionMerge %19 None
1036                OpBranchConditional %17 %18 %500
1037          %18 = OpLabel
1038          %20 = OpLoad %6 %9
1039          %21 = OpIAdd %6 %20 %15
1040                OpStore %12 %21
1041                OpBranch %19
1042         %500 = OpLabel
1043                OpSelectionMerge %501 None
1044                OpBranchConditional %27 %22 %100
1045          %22 = OpLabel
1046          %23 = OpLoad %6 %9
1047          %24 = OpIMul %6 %23 %15
1048                OpStore %12 %24
1049                OpBranch %501
1050         %100 = OpLabel
1051         %201 = OpLoad %6 %9
1052         %202 = OpIMul %6 %201 %15
1053                OpStore %12 %202
1054                OpBranch %501
1055         %501 = OpLabel
1056         %301 = OpPhi %6 %23 %22 %201 %100
1057         %302 = OpPhi %6 %24 %22 %202 %100
1058                OpBranch %19
1059          %19 = OpLabel
1060          %40 = OpPhi %6 %21 %18 %302 %501
1061                OpReturn
1062                OpFunctionEnd
1063     )";
1064   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
1065 }
1066 
1067 TEST(TransformationDuplicateRegionWithSelectionTest, NotApplicableEarlyReturn) {
1068   // This test handles a case where one of the blocks has successor outside of
1069   // the region, which has an early return from the function, so that the
1070   // transformation is not applicable.
1071 
1072   std::string shader = R"(
1073                OpCapability Shader
1074           %1 = OpExtInstImport "GLSL.std.450"
1075                OpMemoryModel Logical GLSL450
1076                OpEntryPoint Fragment %4 "main"
1077                OpExecutionMode %4 OriginUpperLeft
1078                OpSource ESSL 310
1079                OpName %4 "main"
1080                OpName %10 "fun(i1;"
1081                OpName %9 "a"
1082                OpName %12 "s"
1083                OpName %27 "b"
1084                OpName %30 "param"
1085           %2 = OpTypeVoid
1086           %3 = OpTypeFunction %2
1087           %6 = OpTypeInt 32 1
1088           %7 = OpTypePointer Function %6
1089           %8 = OpTypeFunction %2 %7
1090          %13 = OpConstant %6 0
1091          %15 = OpConstant %6 2
1092          %16 = OpTypeBool
1093          %26 = OpTypePointer Function %16
1094          %28 = OpConstantTrue %16
1095          %29 = OpConstant %6 3
1096           %4 = OpFunction %2 None %3
1097           %5 = OpLabel
1098          %27 = OpVariable %26 Function
1099          %30 = OpVariable %7 Function
1100                OpStore %27 %28
1101                OpStore %30 %29
1102          %31 = OpFunctionCall %2 %10 %30
1103                OpReturn
1104                OpFunctionEnd
1105          %10 = OpFunction %2 None %8
1106           %9 = OpFunctionParameter %7
1107          %11 = OpLabel
1108          %12 = OpVariable %7 Function
1109                OpBranch %50
1110          %50 = OpLabel
1111                OpStore %12 %13
1112          %14 = OpLoad %6 %9
1113          %17 = OpSLessThan %16 %14 %15
1114                OpSelectionMerge %19 None
1115                OpBranchConditional %17 %18 %22
1116          %18 = OpLabel
1117          %20 = OpLoad %6 %9
1118          %21 = OpIAdd %6 %20 %15
1119                OpStore %12 %21
1120                OpBranch %19
1121          %22 = OpLabel
1122                OpReturn
1123          %19 = OpLabel
1124                OpReturn
1125                OpFunctionEnd
1126     )";
1127 
1128   const auto env = SPV_ENV_UNIVERSAL_1_4;
1129   const auto consumer = nullptr;
1130   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1131   spvtools::ValidatorOptions validator_options;
1132   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1133                                                kConsoleMessageConsumer));
1134   TransformationContext transformation_context(
1135       MakeUnique<FactManager>(context.get()), validator_options);
1136   // Bad: The block with id 50, which is the entry block, has two successors:
1137   // the block with id 18 and the block with id 22. The block 22 has an early
1138   // return from the function, so that the entry block is not post-dominated by
1139   // the exit block.
1140   TransformationDuplicateRegionWithSelection transformation_bad_1 =
1141       TransformationDuplicateRegionWithSelection(
1142           500, 28, 501, 50, 19, {{50, 100}, {18, 101}, {22, 102}, {19, 103}},
1143           {{14, 202}, {17, 203}, {20, 204}, {21, 205}},
1144           {{14, 302}, {17, 303}, {20, 304}, {21, 305}});
1145   ASSERT_FALSE(
1146       transformation_bad_1.IsApplicable(context.get(), transformation_context));
1147 }
1148 
1149 TEST(TransformationDuplicateRegionWithSelectionTest,
1150      ResolvingOpPhiEntryBlockOnePredecessor) {
1151   // This test handles a case where the entry block has an OpPhi instruction
1152   // referring to its predecessor. After transformation, this instruction needs
1153   // to be updated.
1154 
1155   std::string shader = R"(
1156                OpCapability Shader
1157           %1 = OpExtInstImport "GLSL.std.450"
1158                OpMemoryModel Logical GLSL450
1159                OpEntryPoint Fragment %4 "main"
1160                OpExecutionMode %4 OriginUpperLeft
1161                OpSource ESSL 310
1162                OpName %4 "main"
1163                OpName %10 "fun(i1;"
1164                OpName %9 "a"
1165                OpName %12 "s"
1166                OpName %14 "t"
1167                OpName %20 "b"
1168                OpName %23 "param"
1169           %2 = OpTypeVoid
1170           %3 = OpTypeFunction %2
1171           %6 = OpTypeInt 32 1
1172           %7 = OpTypePointer Function %6
1173           %8 = OpTypeFunction %2 %7
1174          %13 = OpConstant %6 0
1175          %15 = OpConstant %6 2
1176          %18 = OpTypeBool
1177          %19 = OpTypePointer Function %18
1178          %21 = OpConstantTrue %18
1179          %22 = OpConstant %6 3
1180           %4 = OpFunction %2 None %3
1181           %5 = OpLabel
1182          %20 = OpVariable %19 Function
1183          %23 = OpVariable %7 Function
1184                OpStore %20 %21
1185                OpStore %23 %22
1186          %24 = OpFunctionCall %2 %10 %23
1187                OpReturn
1188                OpFunctionEnd
1189          %10 = OpFunction %2 None %8
1190           %9 = OpFunctionParameter %7
1191          %11 = OpLabel
1192          %12 = OpVariable %7 Function
1193          %14 = OpVariable %7 Function
1194                OpStore %12 %13
1195          %16 = OpLoad %6 %12
1196          %17 = OpIMul %6 %15 %16
1197                OpStore %14 %17
1198                OpBranch %50
1199          %50 = OpLabel
1200          %51 = OpPhi %6 %17 %11
1201                OpReturn
1202                OpFunctionEnd
1203     )";
1204 
1205   const auto env = SPV_ENV_UNIVERSAL_1_4;
1206   const auto consumer = nullptr;
1207   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1208   spvtools::ValidatorOptions validator_options;
1209   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1210                                                kConsoleMessageConsumer));
1211   TransformationContext transformation_context(
1212       MakeUnique<FactManager>(context.get()), validator_options);
1213 
1214   TransformationDuplicateRegionWithSelection transformation_good_1 =
1215       TransformationDuplicateRegionWithSelection(
1216           500, 21, 501, 50, 50, {{50, 100}}, {{51, 201}}, {{51, 301}});
1217   ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
1218                                                  transformation_context));
1219   ApplyAndCheckFreshIds(transformation_good_1, context.get(),
1220                         &transformation_context);
1221   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1222                                                kConsoleMessageConsumer));
1223 
1224   std::string expected_shader = R"(
1225                OpCapability Shader
1226           %1 = OpExtInstImport "GLSL.std.450"
1227                OpMemoryModel Logical GLSL450
1228                OpEntryPoint Fragment %4 "main"
1229                OpExecutionMode %4 OriginUpperLeft
1230                OpSource ESSL 310
1231                OpName %4 "main"
1232                OpName %10 "fun(i1;"
1233                OpName %9 "a"
1234                OpName %12 "s"
1235                OpName %14 "t"
1236                OpName %20 "b"
1237                OpName %23 "param"
1238           %2 = OpTypeVoid
1239           %3 = OpTypeFunction %2
1240           %6 = OpTypeInt 32 1
1241           %7 = OpTypePointer Function %6
1242           %8 = OpTypeFunction %2 %7
1243          %13 = OpConstant %6 0
1244          %15 = OpConstant %6 2
1245          %18 = OpTypeBool
1246          %19 = OpTypePointer Function %18
1247          %21 = OpConstantTrue %18
1248          %22 = OpConstant %6 3
1249           %4 = OpFunction %2 None %3
1250           %5 = OpLabel
1251          %20 = OpVariable %19 Function
1252          %23 = OpVariable %7 Function
1253                OpStore %20 %21
1254                OpStore %23 %22
1255          %24 = OpFunctionCall %2 %10 %23
1256                OpReturn
1257                OpFunctionEnd
1258          %10 = OpFunction %2 None %8
1259           %9 = OpFunctionParameter %7
1260          %11 = OpLabel
1261          %12 = OpVariable %7 Function
1262          %14 = OpVariable %7 Function
1263                OpStore %12 %13
1264          %16 = OpLoad %6 %12
1265          %17 = OpIMul %6 %15 %16
1266                OpStore %14 %17
1267                OpBranch %500
1268         %500 = OpLabel
1269                OpSelectionMerge %501 None
1270                OpBranchConditional %21 %50 %100
1271          %50 = OpLabel
1272          %51 = OpPhi %6 %17 %500
1273                OpBranch %501
1274         %100 = OpLabel
1275         %201 = OpPhi %6 %17 %500
1276                OpBranch %501
1277         %501 = OpLabel
1278         %301 = OpPhi %6 %51 %50 %201 %100
1279                OpReturn
1280                OpFunctionEnd
1281     )";
1282   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
1283 }
1284 
1285 TEST(TransformationDuplicateRegionWithSelectionTest,
1286      NotApplicableNoVariablePointerCapability) {
1287   // This test handles a case where the transformation would create an OpPhi
1288   // instruction with pointer operands, however there is no cab
1289   // CapabilityVariablePointers. Hence, the transformation is not applicable.
1290 
1291   std::string shader = R"(
1292                OpCapability Shader
1293           %1 = OpExtInstImport "GLSL.std.450"
1294                OpMemoryModel Logical GLSL450
1295                OpEntryPoint Fragment %4 "main"
1296                OpExecutionMode %4 OriginUpperLeft
1297                OpSource ESSL 310
1298                OpName %4 "main"
1299                OpName %10 "fun(i1;"
1300                OpName %9 "a"
1301                OpName %12 "s"
1302                OpName %14 "t"
1303                OpName %20 "b"
1304                OpName %23 "param"
1305           %2 = OpTypeVoid
1306           %3 = OpTypeFunction %2
1307           %6 = OpTypeInt 32 1
1308           %7 = OpTypePointer Function %6
1309           %8 = OpTypeFunction %2 %7
1310          %13 = OpConstant %6 0
1311          %15 = OpConstant %6 2
1312          %18 = OpTypeBool
1313          %19 = OpTypePointer Function %18
1314          %21 = OpConstantTrue %18
1315          %22 = OpConstant %6 3
1316           %4 = OpFunction %2 None %3
1317           %5 = OpLabel
1318          %20 = OpVariable %19 Function
1319          %23 = OpVariable %7 Function
1320                OpStore %20 %21
1321                OpStore %23 %22
1322          %24 = OpFunctionCall %2 %10 %23
1323                OpReturn
1324                OpFunctionEnd
1325          %10 = OpFunction %2 None %8
1326           %9 = OpFunctionParameter %7
1327          %11 = OpLabel
1328          %12 = OpVariable %7 Function
1329          %14 = OpVariable %7 Function
1330                OpStore %12 %13
1331          %16 = OpLoad %6 %12
1332          %17 = OpIMul %6 %15 %16
1333                OpStore %14 %17
1334                OpBranch %50
1335          %50 = OpLabel
1336          %51 = OpCopyObject %7 %12
1337                OpBranch %52
1338          %52 = OpLabel
1339          %53 = OpCopyObject %7 %51
1340                OpReturn
1341                OpFunctionEnd
1342     )";
1343 
1344   const auto env = SPV_ENV_UNIVERSAL_1_4;
1345   const auto consumer = nullptr;
1346   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1347   spvtools::ValidatorOptions validator_options;
1348   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1349                                                kConsoleMessageConsumer));
1350   TransformationContext transformation_context(
1351       MakeUnique<FactManager>(context.get()), validator_options);
1352   // Bad: There is no required capability CapabilityVariablePointers
1353   TransformationDuplicateRegionWithSelection transformation_bad_1 =
1354       TransformationDuplicateRegionWithSelection(
1355           500, 21, 501, 50, 50, {{50, 100}}, {{51, 201}}, {{51, 301}});
1356   ASSERT_FALSE(
1357       transformation_bad_1.IsApplicable(context.get(), transformation_context));
1358 }
1359 
1360 TEST(TransformationDuplicateRegionWithSelectionTest,
1361      ExitBlockTerminatorOpUnreachable) {
1362   // This test handles a case where the exit block ends with OpUnreachable.
1363 
1364   std::string shader = R"(
1365                OpCapability Shader
1366           %1 = OpExtInstImport "GLSL.std.450"
1367                OpMemoryModel Logical GLSL450
1368                OpEntryPoint Fragment %4 "main"
1369                OpExecutionMode %4 OriginUpperLeft
1370                OpSource ESSL 310
1371                OpName %4 "main"
1372                OpName %6 "fun("
1373                OpName %10 "s"
1374                OpName %17 "b"
1375           %2 = OpTypeVoid
1376           %3 = OpTypeFunction %2
1377           %8 = OpTypeInt 32 1
1378           %9 = OpTypePointer Function %8
1379          %11 = OpConstant %8 0
1380          %13 = OpConstant %8 2
1381          %15 = OpTypeBool
1382          %16 = OpTypePointer Function %15
1383          %18 = OpConstantTrue %15
1384           %4 = OpFunction %2 None %3
1385           %5 = OpLabel
1386          %17 = OpVariable %16 Function
1387                OpStore %17 %18
1388          %19 = OpFunctionCall %2 %6
1389                OpReturn
1390                OpFunctionEnd
1391           %6 = OpFunction %2 None %3
1392           %7 = OpLabel
1393          %10 = OpVariable %9 Function
1394                OpBranch %50
1395          %50 = OpLabel
1396                OpStore %10 %11
1397          %12 = OpLoad %8 %10
1398          %14 = OpIAdd %8 %12 %13
1399                OpStore %10 %14
1400                OpUnreachable
1401                OpFunctionEnd
1402     )";
1403 
1404   const auto env = SPV_ENV_UNIVERSAL_1_4;
1405   const auto consumer = nullptr;
1406   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1407   spvtools::ValidatorOptions validator_options;
1408   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1409                                                kConsoleMessageConsumer));
1410   TransformationContext transformation_context(
1411       MakeUnique<FactManager>(context.get()), validator_options);
1412 
1413   TransformationDuplicateRegionWithSelection transformation_good_1 =
1414       TransformationDuplicateRegionWithSelection(
1415           500, 18, 501, 50, 50, {{50, 100}}, {{12, 201}, {14, 202}},
1416           {{12, 301}, {14, 302}});
1417   ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
1418                                                  transformation_context));
1419   ApplyAndCheckFreshIds(transformation_good_1, context.get(),
1420                         &transformation_context);
1421   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1422                                                kConsoleMessageConsumer));
1423 
1424   std::string expected_shader = R"(
1425                OpCapability Shader
1426           %1 = OpExtInstImport "GLSL.std.450"
1427                OpMemoryModel Logical GLSL450
1428                OpEntryPoint Fragment %4 "main"
1429                OpExecutionMode %4 OriginUpperLeft
1430                OpSource ESSL 310
1431                OpName %4 "main"
1432                OpName %6 "fun("
1433                OpName %10 "s"
1434                OpName %17 "b"
1435           %2 = OpTypeVoid
1436           %3 = OpTypeFunction %2
1437           %8 = OpTypeInt 32 1
1438           %9 = OpTypePointer Function %8
1439          %11 = OpConstant %8 0
1440          %13 = OpConstant %8 2
1441          %15 = OpTypeBool
1442          %16 = OpTypePointer Function %15
1443          %18 = OpConstantTrue %15
1444           %4 = OpFunction %2 None %3
1445           %5 = OpLabel
1446          %17 = OpVariable %16 Function
1447                OpStore %17 %18
1448          %19 = OpFunctionCall %2 %6
1449                OpReturn
1450                OpFunctionEnd
1451           %6 = OpFunction %2 None %3
1452           %7 = OpLabel
1453          %10 = OpVariable %9 Function
1454                OpBranch %500
1455         %500 = OpLabel
1456                OpSelectionMerge %501 None
1457                OpBranchConditional %18 %50 %100
1458          %50 = OpLabel
1459                OpStore %10 %11
1460          %12 = OpLoad %8 %10
1461          %14 = OpIAdd %8 %12 %13
1462                OpStore %10 %14
1463                OpBranch %501
1464         %100 = OpLabel
1465                OpStore %10 %11
1466         %201 = OpLoad %8 %10
1467         %202 = OpIAdd %8 %201 %13
1468                OpStore %10 %202
1469                OpBranch %501
1470         %501 = OpLabel
1471         %301 = OpPhi %8 %12 %50 %201 %100
1472         %302 = OpPhi %8 %14 %50 %202 %100
1473                OpUnreachable
1474                OpFunctionEnd
1475         )";
1476   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
1477 }
1478 
1479 TEST(TransformationDuplicateRegionWithSelectionTest,
1480      ExitBlockTerminatorOpKill) {
1481   // This test handles a case where the exit block ends with OpKill.
1482 
1483   std::string shader = R"(
1484                OpCapability Shader
1485           %1 = OpExtInstImport "GLSL.std.450"
1486                OpMemoryModel Logical GLSL450
1487                OpEntryPoint Fragment %4 "main"
1488                OpExecutionMode %4 OriginUpperLeft
1489                OpSource ESSL 310
1490                OpName %4 "main"
1491                OpName %6 "fun("
1492                OpName %10 "s"
1493                OpName %17 "b"
1494           %2 = OpTypeVoid
1495           %3 = OpTypeFunction %2
1496           %8 = OpTypeInt 32 1
1497           %9 = OpTypePointer Function %8
1498          %11 = OpConstant %8 0
1499          %13 = OpConstant %8 2
1500          %15 = OpTypeBool
1501          %16 = OpTypePointer Function %15
1502          %18 = OpConstantTrue %15
1503           %4 = OpFunction %2 None %3
1504           %5 = OpLabel
1505          %17 = OpVariable %16 Function
1506                OpStore %17 %18
1507          %19 = OpFunctionCall %2 %6
1508                OpReturn
1509                OpFunctionEnd
1510           %6 = OpFunction %2 None %3
1511           %7 = OpLabel
1512          %10 = OpVariable %9 Function
1513                OpBranch %50
1514          %50 = OpLabel
1515                OpStore %10 %11
1516          %12 = OpLoad %8 %10
1517          %14 = OpIAdd %8 %12 %13
1518                OpStore %10 %14
1519                OpKill
1520                OpFunctionEnd
1521          )";
1522 
1523   const auto env = SPV_ENV_UNIVERSAL_1_4;
1524   const auto consumer = nullptr;
1525   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1526   spvtools::ValidatorOptions validator_options;
1527   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1528                                                kConsoleMessageConsumer));
1529   TransformationContext transformation_context(
1530       MakeUnique<FactManager>(context.get()), validator_options);
1531 
1532   TransformationDuplicateRegionWithSelection transformation_good_1 =
1533       TransformationDuplicateRegionWithSelection(
1534           500, 18, 501, 50, 50, {{50, 100}}, {{12, 201}, {14, 202}},
1535           {{12, 301}, {14, 302}});
1536   ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
1537                                                  transformation_context));
1538   ApplyAndCheckFreshIds(transformation_good_1, context.get(),
1539                         &transformation_context);
1540   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1541                                                kConsoleMessageConsumer));
1542 
1543   std::string expected_shader = R"(
1544                OpCapability Shader
1545           %1 = OpExtInstImport "GLSL.std.450"
1546                OpMemoryModel Logical GLSL450
1547                OpEntryPoint Fragment %4 "main"
1548                OpExecutionMode %4 OriginUpperLeft
1549                OpSource ESSL 310
1550                OpName %4 "main"
1551                OpName %6 "fun("
1552                OpName %10 "s"
1553                OpName %17 "b"
1554           %2 = OpTypeVoid
1555           %3 = OpTypeFunction %2
1556           %8 = OpTypeInt 32 1
1557           %9 = OpTypePointer Function %8
1558          %11 = OpConstant %8 0
1559          %13 = OpConstant %8 2
1560          %15 = OpTypeBool
1561          %16 = OpTypePointer Function %15
1562          %18 = OpConstantTrue %15
1563           %4 = OpFunction %2 None %3
1564           %5 = OpLabel
1565          %17 = OpVariable %16 Function
1566                OpStore %17 %18
1567          %19 = OpFunctionCall %2 %6
1568                OpReturn
1569                OpFunctionEnd
1570           %6 = OpFunction %2 None %3
1571           %7 = OpLabel
1572          %10 = OpVariable %9 Function
1573                OpBranch %500
1574         %500 = OpLabel
1575                OpSelectionMerge %501 None
1576                OpBranchConditional %18 %50 %100
1577          %50 = OpLabel
1578                OpStore %10 %11
1579          %12 = OpLoad %8 %10
1580          %14 = OpIAdd %8 %12 %13
1581                OpStore %10 %14
1582                OpBranch %501
1583         %100 = OpLabel
1584                OpStore %10 %11
1585         %201 = OpLoad %8 %10
1586         %202 = OpIAdd %8 %201 %13
1587                OpStore %10 %202
1588                OpBranch %501
1589         %501 = OpLabel
1590         %301 = OpPhi %8 %12 %50 %201 %100
1591         %302 = OpPhi %8 %14 %50 %202 %100
1592                OpKill
1593                OpFunctionEnd
1594         )";
1595 
1596   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
1597 }
1598 
1599 TEST(TransformationDuplicateRegionWithSelectionTest,
1600      ContinueExitBlockNotApplicable) {
1601   // This test handles a case where the exit block is the continue target and
1602   // the transformation is not applicable.
1603 
1604   std::string shader = R"(
1605                OpCapability Shader
1606           %1 = OpExtInstImport "GLSL.std.450"
1607                OpMemoryModel Logical GLSL450
1608                OpEntryPoint Fragment %4 "main"
1609                OpExecutionMode %4 OriginUpperLeft
1610                OpSource ESSL 310
1611                OpName %4 "main"
1612                OpName %8 "s"
1613                OpName %10 "i"
1614           %2 = OpTypeVoid
1615           %3 = OpTypeFunction %2
1616           %6 = OpTypeInt 32 1
1617           %7 = OpTypePointer Function %6
1618           %9 = OpConstant %6 0
1619          %17 = OpConstant %6 10
1620          %18 = OpTypeBool
1621          %24 = OpConstant %6 5
1622          %30 = OpConstant %6 1
1623          %50 = OpConstantTrue %18
1624           %4 = OpFunction %2 None %3
1625           %5 = OpLabel
1626           %8 = OpVariable %7 Function
1627          %10 = OpVariable %7 Function
1628                OpStore %8 %9
1629                OpStore %10 %9
1630                OpBranch %11
1631          %11 = OpLabel
1632                OpLoopMerge %13 %14 None
1633                OpBranch %15
1634          %15 = OpLabel
1635          %16 = OpLoad %6 %10
1636          %19 = OpSLessThan %18 %16 %17
1637                OpBranchConditional %19 %12 %13
1638          %12 = OpLabel
1639          %20 = OpLoad %6 %10
1640          %21 = OpLoad %6 %8
1641          %22 = OpIAdd %6 %21 %20
1642                OpStore %8 %22
1643          %23 = OpLoad %6 %10
1644          %25 = OpIEqual %18 %23 %24
1645                OpSelectionMerge %27 None
1646                OpBranchConditional %25 %26 %27
1647          %26 = OpLabel
1648                OpBranch %13
1649          %27 = OpLabel
1650                OpBranch %14
1651          %14 = OpLabel
1652          %29 = OpLoad %6 %10
1653          %31 = OpIAdd %6 %29 %30
1654                OpStore %10 %31
1655                OpBranch %11
1656          %13 = OpLabel
1657                OpReturn
1658                OpFunctionEnd
1659     )";
1660 
1661   const auto env = SPV_ENV_UNIVERSAL_1_4;
1662   const auto consumer = nullptr;
1663   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1664   spvtools::ValidatorOptions validator_options;
1665   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1666                                                kConsoleMessageConsumer));
1667   TransformationContext transformation_context(
1668       MakeUnique<FactManager>(context.get()), validator_options);
1669   TransformationDuplicateRegionWithSelection transformation_bad =
1670       TransformationDuplicateRegionWithSelection(
1671           500, 50, 501, 27, 14, {{27, 101}, {14, 102}}, {{29, 201}, {31, 202}},
1672           {{29, 301}, {31, 302}});
1673 
1674   ASSERT_FALSE(
1675       transformation_bad.IsApplicable(context.get(), transformation_context));
1676 }
1677 
1678 TEST(TransformationDuplicateRegionWithSelectionTest,
1679      MultiplePredecessorsNotApplicableTest) {
1680   // This test handles a case where the entry block has multiple predecessors
1681   // and the transformation is not applicable.
1682 
1683   std::string shader = R"(
1684                OpCapability Shader
1685           %1 = OpExtInstImport "GLSL.std.450"
1686                OpMemoryModel Logical GLSL450
1687                OpEntryPoint Fragment %4 "main"
1688                OpExecutionMode %4 OriginUpperLeft
1689                OpSource ESSL 310
1690                OpName %4 "main"
1691                OpName %10 "fun1(i1;"
1692                OpName %9 "a"
1693                OpName %18 "b"
1694                OpName %24 "b"
1695                OpName %27 "param"
1696           %2 = OpTypeVoid
1697           %3 = OpTypeFunction %2
1698           %6 = OpTypeInt 32 1
1699           %7 = OpTypePointer Function %6
1700           %8 = OpTypeFunction %2 %7
1701          %13 = OpConstant %6 2
1702          %14 = OpTypeBool
1703          %23 = OpTypePointer Function %14
1704          %25 = OpConstantTrue %14
1705          %26 = OpConstant %6 1
1706           %4 = OpFunction %2 None %3
1707           %5 = OpLabel
1708          %24 = OpVariable %23 Function
1709          %27 = OpVariable %7 Function
1710                OpStore %24 %25
1711                OpStore %27 %26
1712          %28 = OpFunctionCall %2 %10 %27
1713                OpReturn
1714                OpFunctionEnd
1715          %10 = OpFunction %2 None %8
1716           %9 = OpFunctionParameter %7
1717          %11 = OpLabel
1718          %18 = OpVariable %7 Function
1719          %12 = OpLoad %6 %9
1720          %15 = OpSLessThan %14 %12 %13
1721                OpSelectionMerge %17 None
1722                OpBranchConditional %15 %16 %20
1723          %16 = OpLabel
1724          %19 = OpLoad %6 %9
1725                OpStore %18 %19
1726                OpBranch %60
1727          %20 = OpLabel
1728          %21 = OpLoad %6 %9
1729          %22 = OpIAdd %6 %21 %13
1730                OpStore %18 %22
1731                OpBranch %60
1732          %60 = OpLabel
1733                OpBranch %17
1734          %17 = OpLabel
1735                OpReturn
1736                OpFunctionEnd
1737     )";
1738 
1739   const auto env = SPV_ENV_UNIVERSAL_1_4;
1740   const auto consumer = nullptr;
1741   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1742   spvtools::ValidatorOptions validator_options;
1743   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1744                                                kConsoleMessageConsumer));
1745   TransformationContext transformation_context(
1746       MakeUnique<FactManager>(context.get()), validator_options);
1747 
1748   TransformationDuplicateRegionWithSelection transformation_bad =
1749       TransformationDuplicateRegionWithSelection(500, 25, 501, 60, 60,
1750                                                  {{60, 101}}, {{}}, {{}});
1751 
1752   ASSERT_FALSE(
1753       transformation_bad.IsApplicable(context.get(), transformation_context));
1754 }
1755 
1756 TEST(TransformationDuplicateRegionWithSelectionTest, OverflowIds) {
1757   // This test checks that the transformation correctly uses overflow ids, when
1758   // they are both needed and provided.
1759 
1760   std::string shader = R"(
1761                OpCapability Shader
1762           %1 = OpExtInstImport "GLSL.std.450"
1763                OpMemoryModel Logical GLSL450
1764                OpEntryPoint Fragment %4 "main"
1765                OpExecutionMode %4 OriginUpperLeft
1766                OpSource ESSL 310
1767                OpName %4 "main"
1768                OpName %6 "fun("
1769                OpName %10 "s"
1770                OpName %17 "b"
1771           %2 = OpTypeVoid
1772           %3 = OpTypeFunction %2
1773           %8 = OpTypeInt 32 1
1774           %9 = OpTypePointer Function %8
1775          %11 = OpConstant %8 0
1776          %13 = OpConstant %8 2
1777          %15 = OpTypeBool
1778          %16 = OpTypePointer Function %15
1779          %18 = OpConstantTrue %15
1780           %4 = OpFunction %2 None %3
1781           %5 = OpLabel
1782          %17 = OpVariable %16 Function
1783                OpStore %17 %18
1784          %19 = OpFunctionCall %2 %6
1785                OpReturn
1786                OpFunctionEnd
1787           %6 = OpFunction %2 None %3
1788           %7 = OpLabel
1789          %10 = OpVariable %9 Function
1790                OpBranch %50
1791          %50 = OpLabel
1792                OpStore %10 %11
1793          %12 = OpLoad %8 %10
1794          %14 = OpIAdd %8 %12 %13
1795                OpStore %10 %14
1796                OpKill
1797                OpFunctionEnd
1798          )";
1799 
1800   const auto env = SPV_ENV_UNIVERSAL_1_4;
1801   const auto consumer = nullptr;
1802   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1803   spvtools::ValidatorOptions validator_options;
1804   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1805                                                kConsoleMessageConsumer));
1806   auto overflow_ids_unique_ptr = MakeUnique<CounterOverflowIdSource>(1000);
1807   auto overflow_ids_ptr = overflow_ids_unique_ptr.get();
1808   TransformationContext transformation_context(
1809       MakeUnique<FactManager>(context.get()), validator_options,
1810       std::move(overflow_ids_unique_ptr));
1811 
1812   // The mappings do not provide sufficient ids, thus overflow ids are required.
1813   TransformationDuplicateRegionWithSelection transformation_good_1 =
1814       TransformationDuplicateRegionWithSelection(500, 18, 501, 50, 50, {},
1815                                                  {{12, 201}}, {{14, 302}});
1816   ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
1817                                                  transformation_context));
1818   ApplyAndCheckFreshIds(transformation_good_1, context.get(),
1819                         &transformation_context,
1820                         overflow_ids_ptr->GetIssuedOverflowIds());
1821   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1822                                                kConsoleMessageConsumer));
1823 
1824   std::string expected_shader = R"(
1825                OpCapability Shader
1826           %1 = OpExtInstImport "GLSL.std.450"
1827                OpMemoryModel Logical GLSL450
1828                OpEntryPoint Fragment %4 "main"
1829                OpExecutionMode %4 OriginUpperLeft
1830                OpSource ESSL 310
1831                OpName %4 "main"
1832                OpName %6 "fun("
1833                OpName %10 "s"
1834                OpName %17 "b"
1835           %2 = OpTypeVoid
1836           %3 = OpTypeFunction %2
1837           %8 = OpTypeInt 32 1
1838           %9 = OpTypePointer Function %8
1839          %11 = OpConstant %8 0
1840          %13 = OpConstant %8 2
1841          %15 = OpTypeBool
1842          %16 = OpTypePointer Function %15
1843          %18 = OpConstantTrue %15
1844           %4 = OpFunction %2 None %3
1845           %5 = OpLabel
1846          %17 = OpVariable %16 Function
1847                OpStore %17 %18
1848          %19 = OpFunctionCall %2 %6
1849                OpReturn
1850                OpFunctionEnd
1851           %6 = OpFunction %2 None %3
1852           %7 = OpLabel
1853          %10 = OpVariable %9 Function
1854                OpBranch %500
1855         %500 = OpLabel
1856                OpSelectionMerge %501 None
1857                OpBranchConditional %18 %50 %1000
1858          %50 = OpLabel
1859                OpStore %10 %11
1860          %12 = OpLoad %8 %10
1861          %14 = OpIAdd %8 %12 %13
1862                OpStore %10 %14
1863                OpBranch %501
1864        %1000 = OpLabel
1865                OpStore %10 %11
1866         %201 = OpLoad %8 %10
1867        %1002 = OpIAdd %8 %201 %13
1868                OpStore %10 %1002
1869                OpBranch %501
1870         %501 = OpLabel
1871        %1001 = OpPhi %8 %12 %50 %201 %1000
1872         %302 = OpPhi %8 %14 %50 %1002 %1000
1873                OpKill
1874                OpFunctionEnd
1875         )";
1876 
1877   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
1878 }
1879 
1880 TEST(TransformationDuplicateRegionWithSelectionTest,
1881      RegionExitIsOpBranchConditional) {
1882   // Checks the case where the exit block of a region ends with
1883   // OpBranchConditional (but is not a header).
1884   std::string shader = R"(
1885                OpCapability Shader
1886           %1 = OpExtInstImport "GLSL.std.450"
1887                OpMemoryModel Logical GLSL450
1888                OpEntryPoint Fragment %4 "main"
1889                OpExecutionMode %4 OriginUpperLeft
1890                OpSource ESSL 320
1891                OpName %4 "main"
1892                OpName %8 "i"
1893           %2 = OpTypeVoid
1894           %3 = OpTypeFunction %2
1895           %6 = OpTypeInt 32 1
1896           %7 = OpTypePointer Function %6
1897           %9 = OpConstant %6 0
1898          %16 = OpConstant %6 100
1899          %17 = OpTypeBool
1900          %50 = OpConstantTrue %17
1901          %20 = OpConstant %6 1
1902           %4 = OpFunction %2 None %3
1903           %5 = OpLabel
1904           %8 = OpVariable %7 Function
1905                OpStore %8 %9
1906                OpBranch %10
1907          %10 = OpLabel
1908                OpLoopMerge %12 %13 None
1909                OpBranch %14
1910          %14 = OpLabel
1911          %15 = OpLoad %6 %8
1912          %18 = OpSLessThan %17 %15 %16
1913                OpBranchConditional %18 %11 %12
1914          %11 = OpLabel
1915          %19 = OpLoad %6 %8
1916          %21 = OpIAdd %6 %19 %20
1917                OpStore %8 %21
1918                OpBranchConditional %50 %13 %12
1919          %13 = OpLabel
1920          %22 = OpLoad %6 %8
1921          %23 = OpIAdd %6 %22 %20
1922                OpStore %8 %23
1923                OpBranch %10
1924          %12 = OpLabel
1925                OpReturn
1926                OpFunctionEnd
1927          )";
1928 
1929   const auto env = SPV_ENV_UNIVERSAL_1_4;
1930   const auto consumer = nullptr;
1931   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1932   spvtools::ValidatorOptions validator_options;
1933   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1934                                                kConsoleMessageConsumer));
1935   TransformationContext transformation_context(
1936       MakeUnique<FactManager>(context.get()), validator_options);
1937 
1938   TransformationDuplicateRegionWithSelection transformation_good_1 =
1939       TransformationDuplicateRegionWithSelection(
1940           600, 50, 601, 11, 11, {{11, 602}}, {{19, 603}, {21, 604}},
1941           {{19, 605}, {21, 606}});
1942   ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
1943                                                  transformation_context));
1944   ApplyAndCheckFreshIds(transformation_good_1, context.get(),
1945                         &transformation_context);
1946   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1947                                                kConsoleMessageConsumer));
1948 
1949   std::string expected_shader = R"(
1950                OpCapability Shader
1951           %1 = OpExtInstImport "GLSL.std.450"
1952                OpMemoryModel Logical GLSL450
1953                OpEntryPoint Fragment %4 "main"
1954                OpExecutionMode %4 OriginUpperLeft
1955                OpSource ESSL 320
1956                OpName %4 "main"
1957                OpName %8 "i"
1958           %2 = OpTypeVoid
1959           %3 = OpTypeFunction %2
1960           %6 = OpTypeInt 32 1
1961           %7 = OpTypePointer Function %6
1962           %9 = OpConstant %6 0
1963          %16 = OpConstant %6 100
1964          %17 = OpTypeBool
1965          %50 = OpConstantTrue %17
1966          %20 = OpConstant %6 1
1967           %4 = OpFunction %2 None %3
1968           %5 = OpLabel
1969           %8 = OpVariable %7 Function
1970                OpStore %8 %9
1971                OpBranch %10
1972          %10 = OpLabel
1973                OpLoopMerge %12 %13 None
1974                OpBranch %14
1975          %14 = OpLabel
1976          %15 = OpLoad %6 %8
1977          %18 = OpSLessThan %17 %15 %16
1978                OpBranchConditional %18 %600 %12
1979         %600 = OpLabel
1980                OpSelectionMerge %601 None
1981                OpBranchConditional %50 %11 %602
1982          %11 = OpLabel
1983          %19 = OpLoad %6 %8
1984          %21 = OpIAdd %6 %19 %20
1985                OpStore %8 %21
1986                OpBranch %601
1987         %602 = OpLabel
1988         %603 = OpLoad %6 %8
1989         %604 = OpIAdd %6 %603 %20
1990                OpStore %8 %604
1991                OpBranch %601
1992         %601 = OpLabel
1993         %605 = OpPhi %6 %19 %11 %603 %602
1994         %606 = OpPhi %6 %21 %11 %604 %602
1995                OpBranchConditional %50 %13 %12
1996          %13 = OpLabel
1997          %22 = OpLoad %6 %8
1998          %23 = OpIAdd %6 %22 %20
1999                OpStore %8 %23
2000                OpBranch %10
2001          %12 = OpLabel
2002                OpReturn
2003                OpFunctionEnd
2004         )";
2005 
2006   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
2007 }
2008 
2009 TEST(TransformationDuplicateRegionWithSelectionTest,
2010      RegionExitIsOpBranchConditionalUsingBooleanDefinedInBlock) {
2011   std::string shader = R"(
2012                OpCapability Shader
2013           %1 = OpExtInstImport "GLSL.std.450"
2014                OpMemoryModel Logical GLSL450
2015                OpEntryPoint Fragment %4 "main"
2016                OpExecutionMode %4 OriginUpperLeft
2017                OpSource ESSL 320
2018                OpName %4 "main"
2019                OpName %8 "i"
2020           %2 = OpTypeVoid
2021           %3 = OpTypeFunction %2
2022           %6 = OpTypeInt 32 1
2023           %7 = OpTypePointer Function %6
2024           %9 = OpConstant %6 0
2025          %16 = OpConstant %6 100
2026          %17 = OpTypeBool
2027          %50 = OpConstantTrue %17
2028          %20 = OpConstant %6 1
2029           %4 = OpFunction %2 None %3
2030           %5 = OpLabel
2031           %8 = OpVariable %7 Function
2032                OpStore %8 %9
2033                OpBranch %10
2034          %10 = OpLabel
2035                OpLoopMerge %12 %13 None
2036                OpBranch %14
2037          %14 = OpLabel
2038          %15 = OpLoad %6 %8
2039          %18 = OpSLessThan %17 %15 %16
2040                OpBranchConditional %18 %11 %12
2041          %11 = OpLabel
2042          %19 = OpLoad %6 %8
2043          %21 = OpIAdd %6 %19 %20
2044          %70 = OpCopyObject %17 %50
2045                OpStore %8 %21
2046                OpBranchConditional %70 %13 %12
2047          %13 = OpLabel
2048          %22 = OpLoad %6 %8
2049          %23 = OpIAdd %6 %22 %20
2050                OpStore %8 %23
2051                OpBranch %10
2052          %12 = OpLabel
2053                OpReturn
2054                OpFunctionEnd
2055          )";
2056 
2057   const auto env = SPV_ENV_UNIVERSAL_1_4;
2058   const auto consumer = nullptr;
2059   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2060   spvtools::ValidatorOptions validator_options;
2061   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2062                                                kConsoleMessageConsumer));
2063   TransformationContext transformation_context(
2064       MakeUnique<FactManager>(context.get()), validator_options);
2065 
2066   TransformationDuplicateRegionWithSelection transformation_good_1 =
2067       TransformationDuplicateRegionWithSelection(
2068           600, 50, 601, 11, 11, {{11, 602}}, {{19, 603}, {21, 604}, {70, 608}},
2069           {{19, 605}, {21, 606}, {70, 607}});
2070   ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
2071                                                  transformation_context));
2072   ApplyAndCheckFreshIds(transformation_good_1, context.get(),
2073                         &transformation_context);
2074   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2075                                                kConsoleMessageConsumer));
2076 
2077   std::string expected_shader = R"(
2078                OpCapability Shader
2079           %1 = OpExtInstImport "GLSL.std.450"
2080                OpMemoryModel Logical GLSL450
2081                OpEntryPoint Fragment %4 "main"
2082                OpExecutionMode %4 OriginUpperLeft
2083                OpSource ESSL 320
2084                OpName %4 "main"
2085                OpName %8 "i"
2086           %2 = OpTypeVoid
2087           %3 = OpTypeFunction %2
2088           %6 = OpTypeInt 32 1
2089           %7 = OpTypePointer Function %6
2090           %9 = OpConstant %6 0
2091          %16 = OpConstant %6 100
2092          %17 = OpTypeBool
2093          %50 = OpConstantTrue %17
2094          %20 = OpConstant %6 1
2095           %4 = OpFunction %2 None %3
2096           %5 = OpLabel
2097           %8 = OpVariable %7 Function
2098                OpStore %8 %9
2099                OpBranch %10
2100          %10 = OpLabel
2101                OpLoopMerge %12 %13 None
2102                OpBranch %14
2103          %14 = OpLabel
2104          %15 = OpLoad %6 %8
2105          %18 = OpSLessThan %17 %15 %16
2106                OpBranchConditional %18 %600 %12
2107         %600 = OpLabel
2108                OpSelectionMerge %601 None
2109                OpBranchConditional %50 %11 %602
2110          %11 = OpLabel
2111          %19 = OpLoad %6 %8
2112          %21 = OpIAdd %6 %19 %20
2113          %70 = OpCopyObject %17 %50
2114                OpStore %8 %21
2115                OpBranch %601
2116         %602 = OpLabel
2117         %603 = OpLoad %6 %8
2118         %604 = OpIAdd %6 %603 %20
2119         %608 = OpCopyObject %17 %50
2120                OpStore %8 %604
2121                OpBranch %601
2122         %601 = OpLabel
2123         %605 = OpPhi %6 %19 %11 %603 %602
2124         %606 = OpPhi %6 %21 %11 %604 %602
2125         %607 = OpPhi %17 %70 %11 %608 %602
2126                OpBranchConditional %607 %13 %12
2127          %13 = OpLabel
2128          %22 = OpLoad %6 %8
2129          %23 = OpIAdd %6 %22 %20
2130                OpStore %8 %23
2131                OpBranch %10
2132          %12 = OpLabel
2133                OpReturn
2134                OpFunctionEnd
2135         )";
2136 
2137   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
2138 }
2139 
2140 TEST(TransformationDuplicateRegionWithSelectionTest,
2141      RegionExitUsesOpReturnValueWithIdDefinedInRegion) {
2142   std::string shader = R"(
2143                OpCapability Shader
2144           %1 = OpExtInstImport "GLSL.std.450"
2145                OpMemoryModel Logical GLSL450
2146                OpEntryPoint Fragment %4 "main"
2147                OpExecutionMode %4 OriginUpperLeft
2148                OpSource ESSL 320
2149           %2 = OpTypeVoid
2150           %3 = OpTypeFunction %2
2151           %6 = OpTypeInt 32 1
2152           %7 = OpTypeFunction %6
2153          %10 = OpConstant %6 2
2154          %30 = OpTypeBool
2155          %31 = OpConstantTrue %30
2156           %4 = OpFunction %2 None %3
2157           %5 = OpLabel
2158          %13 = OpFunctionCall %6 %8
2159                OpReturn
2160                OpFunctionEnd
2161           %8 = OpFunction %6 None %7
2162           %9 = OpLabel
2163                OpBranch %20
2164          %20 = OpLabel
2165          %21 = OpCopyObject %6 %10
2166                OpReturnValue %21
2167                OpFunctionEnd
2168          )";
2169 
2170   const auto env = SPV_ENV_UNIVERSAL_1_4;
2171   const auto consumer = nullptr;
2172   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2173   spvtools::ValidatorOptions validator_options;
2174   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2175                                                kConsoleMessageConsumer));
2176   TransformationContext transformation_context(
2177       MakeUnique<FactManager>(context.get()), validator_options);
2178 
2179   TransformationDuplicateRegionWithSelection transformation_good_1 =
2180       TransformationDuplicateRegionWithSelection(
2181           600, 31, 601, 20, 20, {{20, 602}}, {{21, 603}}, {{21, 605}});
2182   ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
2183                                                  transformation_context));
2184   ApplyAndCheckFreshIds(transformation_good_1, context.get(),
2185                         &transformation_context);
2186   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2187                                                kConsoleMessageConsumer));
2188 
2189   std::string expected_shader = R"(
2190                OpCapability Shader
2191           %1 = OpExtInstImport "GLSL.std.450"
2192                OpMemoryModel Logical GLSL450
2193                OpEntryPoint Fragment %4 "main"
2194                OpExecutionMode %4 OriginUpperLeft
2195                OpSource ESSL 320
2196           %2 = OpTypeVoid
2197           %3 = OpTypeFunction %2
2198           %6 = OpTypeInt 32 1
2199           %7 = OpTypeFunction %6
2200          %10 = OpConstant %6 2
2201          %30 = OpTypeBool
2202          %31 = OpConstantTrue %30
2203           %4 = OpFunction %2 None %3
2204           %5 = OpLabel
2205          %13 = OpFunctionCall %6 %8
2206                OpReturn
2207                OpFunctionEnd
2208           %8 = OpFunction %6 None %7
2209           %9 = OpLabel
2210                OpBranch %600
2211         %600 = OpLabel
2212                OpSelectionMerge %601 None
2213                OpBranchConditional %31 %20 %602
2214          %20 = OpLabel
2215          %21 = OpCopyObject %6 %10
2216                OpBranch %601
2217         %602 = OpLabel
2218         %603 = OpCopyObject %6 %10
2219                OpBranch %601
2220         %601 = OpLabel
2221         %605 = OpPhi %6 %21 %20 %603 %602
2222                OpReturnValue %605
2223                OpFunctionEnd
2224         )";
2225 
2226   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
2227 }
2228 
2229 TEST(TransformationDuplicateRegionWithSelectionTest,
2230      InapplicableDueToOpTypeSampledImage) {
2231   std::string reference_shader = R"(
2232                OpCapability Shader
2233           %1 = OpExtInstImport "GLSL.std.450"
2234                OpMemoryModel Logical GLSL450
2235                OpEntryPoint Fragment %4 "main" %10
2236                OpExecutionMode %4 OriginUpperLeft
2237                OpSource ESSL 320
2238                OpDecorate %10 RelaxedPrecision
2239                OpDecorate %10 DescriptorSet 0
2240                OpDecorate %10 Binding 0
2241           %2 = OpTypeVoid
2242           %3 = OpTypeFunction %2
2243           %6 = OpTypeFloat 32
2244           %7 = OpTypeImage %6 2D 0 0 0 1 Unknown
2245           %8 = OpTypeSampledImage %7
2246           %9 = OpTypePointer UniformConstant %8
2247          %10 = OpVariable %9 UniformConstant
2248          %12 = OpTypeVector %6 2
2249          %13 = OpConstant %6 0
2250          %14 = OpConstantComposite %12 %13 %13
2251          %15 = OpTypeVector %6 4
2252          %30 = OpTypeBool
2253          %31 = OpConstantTrue %30
2254           %4 = OpFunction %2 None %3
2255           %5 = OpLabel
2256                OpBranch %20
2257          %20 = OpLabel
2258          %11 = OpLoad %8 %10
2259                OpBranch %21
2260          %21 = OpLabel
2261          %16 = OpImageSampleImplicitLod %15 %11 %14
2262                OpReturn
2263                OpFunctionEnd
2264   )";
2265 
2266   const auto env = SPV_ENV_UNIVERSAL_1_4;
2267   const auto consumer = nullptr;
2268   const auto context =
2269       BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
2270   spvtools::ValidatorOptions validator_options;
2271   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2272                                                kConsoleMessageConsumer));
2273   TransformationContext transformation_context(
2274       MakeUnique<FactManager>(context.get()), validator_options);
2275 
2276   ASSERT_FALSE(TransformationDuplicateRegionWithSelection(
2277                    600, 31, 601, 20, 20, {{20, 602}}, {{11, 603}}, {{11, 605}})
2278                    .IsApplicable(context.get(), transformation_context));
2279 }
2280 
2281 TEST(TransformationDuplicateRegionWithSelectionTest,
2282      DoNotProduceOpPhiWithVoidType) {
2283   std::string reference_shader = R"(
2284                OpCapability Shader
2285           %1 = OpExtInstImport "GLSL.std.450"
2286                OpMemoryModel Logical GLSL450
2287                OpEntryPoint Fragment %4 "main"
2288                OpExecutionMode %4 OriginUpperLeft
2289                OpSource ESSL 320
2290           %2 = OpTypeVoid
2291           %3 = OpTypeFunction %2
2292          %10 = OpTypeBool
2293          %11 = OpConstantTrue %10
2294           %4 = OpFunction %2 None %3
2295          %12 = OpLabel
2296                OpBranch %5
2297           %5 = OpLabel
2298           %8 = OpFunctionCall %2 %6
2299                OpReturn
2300                OpFunctionEnd
2301           %6 = OpFunction %2 None %3
2302           %7 = OpLabel
2303                OpReturn
2304                OpFunctionEnd
2305   )";
2306 
2307   const auto env = SPV_ENV_UNIVERSAL_1_4;
2308   const auto consumer = nullptr;
2309   const auto context =
2310       BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
2311   spvtools::ValidatorOptions validator_options;
2312   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2313                                                kConsoleMessageConsumer));
2314   TransformationContext transformation_context(
2315       MakeUnique<FactManager>(context.get()), validator_options);
2316 
2317   TransformationDuplicateRegionWithSelection transformation(
2318       100, 11, 101, 5, 5, {{5, 102}}, {{8, 103}}, {});
2319   ASSERT_TRUE(
2320       transformation.IsApplicable(context.get(), transformation_context));
2321   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
2322   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2323                                                kConsoleMessageConsumer));
2324 
2325   std::string expected_shader = R"(
2326                OpCapability Shader
2327           %1 = OpExtInstImport "GLSL.std.450"
2328                OpMemoryModel Logical GLSL450
2329                OpEntryPoint Fragment %4 "main"
2330                OpExecutionMode %4 OriginUpperLeft
2331                OpSource ESSL 320
2332           %2 = OpTypeVoid
2333           %3 = OpTypeFunction %2
2334          %10 = OpTypeBool
2335          %11 = OpConstantTrue %10
2336           %4 = OpFunction %2 None %3
2337          %12 = OpLabel
2338                OpBranch %100
2339         %100 = OpLabel
2340                OpSelectionMerge %101 None
2341                OpBranchConditional %11 %5 %102
2342           %5 = OpLabel
2343           %8 = OpFunctionCall %2 %6
2344                OpBranch %101
2345         %102 = OpLabel
2346         %103 = OpFunctionCall %2 %6
2347                OpBranch %101
2348         %101 = OpLabel
2349                OpReturn
2350                OpFunctionEnd
2351           %6 = OpFunction %2 None %3
2352           %7 = OpLabel
2353                OpReturn
2354                OpFunctionEnd
2355         )";
2356 
2357   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
2358 }
2359 
2360 TEST(TransformationDuplicateRegionWithSelectionTest,
2361      DoNotProduceOpPhiWithDisallowedType) {
2362   std::string reference_shader = R"(
2363                OpCapability Shader
2364           %1 = OpExtInstImport "GLSL.std.450"
2365                OpMemoryModel Logical GLSL450
2366                OpEntryPoint Fragment %4 "main"
2367                OpExecutionMode %4 OriginUpperLeft
2368                OpSource ESSL 320
2369                OpDecorate %13 DescriptorSet 0
2370                OpDecorate %13 Binding 0
2371           %2 = OpTypeVoid
2372           %3 = OpTypeFunction %2
2373           %6 = OpTypeFloat 32
2374           %7 = OpTypeVector %6 2
2375           %8 = OpTypePointer Function %7
2376          %10 = OpTypeImage %6 2D 0 0 0 1 Unknown
2377          %11 = OpTypeSampledImage %10
2378          %12 = OpTypePointer UniformConstant %11
2379          %13 = OpVariable %12 UniformConstant
2380          %15 = OpConstant %6 1
2381          %16 = OpConstantComposite %7 %15 %15
2382          %17 = OpTypeVector %6 4
2383          %19 = OpTypeInt 32 0
2384          %20 = OpConstant %19 0
2385          %22 = OpTypePointer Function %6
2386          %90 = OpTypeBool
2387          %91 = OpConstantTrue %90
2388           %4 = OpFunction %2 None %3
2389           %5 = OpLabel
2390           %9 = OpVariable %8 Function
2391                OpBranch %81
2392          %81 = OpLabel
2393          %14 = OpLoad %11 %13
2394          %18 = OpImageSampleImplicitLod %17 %14 %16
2395          %21 = OpCompositeExtract %6 %18 0
2396          %23 = OpAccessChain %22 %9 %20
2397                OpStore %23 %21
2398                OpBranch %80
2399          %80 = OpLabel
2400                OpReturn
2401                OpFunctionEnd
2402   )";
2403 
2404   const auto env = SPV_ENV_UNIVERSAL_1_3;
2405   const auto consumer = nullptr;
2406   const auto context =
2407       BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
2408   spvtools::ValidatorOptions validator_options;
2409   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2410                                                kConsoleMessageConsumer));
2411   TransformationContext transformation_context(
2412       MakeUnique<FactManager>(context.get()), validator_options);
2413 
2414   TransformationDuplicateRegionWithSelection transformation(
2415       100, 91, 101, 81, 81, {{81, 102}},
2416       {{14, 103}, {18, 104}, {21, 105}, {23, 106}}, {{18, 107}, {21, 108}});
2417   ASSERT_TRUE(
2418       transformation.IsApplicable(context.get(), transformation_context));
2419   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
2420   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2421                                                kConsoleMessageConsumer));
2422 
2423   std::string expected_shader = R"(
2424                OpCapability Shader
2425           %1 = OpExtInstImport "GLSL.std.450"
2426                OpMemoryModel Logical GLSL450
2427                OpEntryPoint Fragment %4 "main"
2428                OpExecutionMode %4 OriginUpperLeft
2429                OpSource ESSL 320
2430                OpDecorate %13 DescriptorSet 0
2431                OpDecorate %13 Binding 0
2432           %2 = OpTypeVoid
2433           %3 = OpTypeFunction %2
2434           %6 = OpTypeFloat 32
2435           %7 = OpTypeVector %6 2
2436           %8 = OpTypePointer Function %7
2437          %10 = OpTypeImage %6 2D 0 0 0 1 Unknown
2438          %11 = OpTypeSampledImage %10
2439          %12 = OpTypePointer UniformConstant %11
2440          %13 = OpVariable %12 UniformConstant
2441          %15 = OpConstant %6 1
2442          %16 = OpConstantComposite %7 %15 %15
2443          %17 = OpTypeVector %6 4
2444          %19 = OpTypeInt 32 0
2445          %20 = OpConstant %19 0
2446          %22 = OpTypePointer Function %6
2447          %90 = OpTypeBool
2448          %91 = OpConstantTrue %90
2449           %4 = OpFunction %2 None %3
2450           %5 = OpLabel
2451           %9 = OpVariable %8 Function
2452                OpBranch %100
2453         %100 = OpLabel
2454                OpSelectionMerge %101 None
2455                OpBranchConditional %91 %81 %102
2456          %81 = OpLabel
2457          %14 = OpLoad %11 %13
2458          %18 = OpImageSampleImplicitLod %17 %14 %16
2459          %21 = OpCompositeExtract %6 %18 0
2460          %23 = OpAccessChain %22 %9 %20
2461                OpStore %23 %21
2462                OpBranch %101
2463         %102 = OpLabel
2464         %103 = OpLoad %11 %13
2465         %104 = OpImageSampleImplicitLod %17 %103 %16
2466         %105 = OpCompositeExtract %6 %104 0
2467         %106 = OpAccessChain %22 %9 %20
2468                OpStore %106 %105
2469                OpBranch %101
2470         %101 = OpLabel
2471         %107 = OpPhi %17 %18 %81 %104 %102
2472         %108 = OpPhi %6 %21 %81 %105 %102
2473                OpBranch %80
2474          %80 = OpLabel
2475                OpReturn
2476                OpFunctionEnd
2477         )";
2478 
2479   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
2480 }
2481 
2482 }  // namespace
2483 }  // namespace fuzz
2484 }  // namespace spvtools
2485