1 // Copyright (c) 2018 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 <memory>
16 #include <vector>
17 
18 #include "gmock/gmock.h"
19 #include "source/opt/loop_descriptor.h"
20 #include "source/opt/loop_fusion.h"
21 #include "test/opt/pass_fixture.h"
22 
23 namespace spvtools {
24 namespace opt {
25 namespace {
26 
27 using FusionCompatibilityTest = PassTest<::testing::Test>;
28 
29 /*
30 Generated from the following GLSL + --eliminate-local-multi-store
31 
32 #version 440 core
33 void main() {
34   int i = 0; // Can't fuse, i=0 in first & i=10 in second
35   for (; i < 10; i++) {}
36   for (; i < 10; i++) {}
37 }
38 */
TEST_F(FusionCompatibilityTest,SameInductionVariableDifferentBounds)39 TEST_F(FusionCompatibilityTest, SameInductionVariableDifferentBounds) {
40   const std::string text = R"(
41                OpCapability Shader
42           %1 = OpExtInstImport "GLSL.std.450"
43                OpMemoryModel Logical GLSL450
44                OpEntryPoint Fragment %4 "main"
45                OpExecutionMode %4 OriginUpperLeft
46                OpSource GLSL 440
47                OpName %4 "main"
48                OpName %8 "i"
49           %2 = OpTypeVoid
50           %3 = OpTypeFunction %2
51           %6 = OpTypeInt 32 1
52           %7 = OpTypePointer Function %6
53           %9 = OpConstant %6 0
54          %16 = OpConstant %6 10
55          %17 = OpTypeBool
56          %20 = OpConstant %6 1
57           %4 = OpFunction %2 None %3
58           %5 = OpLabel
59           %8 = OpVariable %7 Function
60                OpStore %8 %9
61                OpBranch %10
62          %10 = OpLabel
63          %31 = OpPhi %6 %9 %5 %21 %13
64                OpLoopMerge %12 %13 None
65                OpBranch %14
66          %14 = OpLabel
67          %18 = OpSLessThan %17 %31 %16
68                OpBranchConditional %18 %11 %12
69          %11 = OpLabel
70                OpBranch %13
71          %13 = OpLabel
72          %21 = OpIAdd %6 %31 %20
73                OpStore %8 %21
74                OpBranch %10
75          %12 = OpLabel
76                OpBranch %22
77          %22 = OpLabel
78          %32 = OpPhi %6 %31 %12 %30 %25
79                OpLoopMerge %24 %25 None
80                OpBranch %26
81          %26 = OpLabel
82          %28 = OpSLessThan %17 %32 %16
83                OpBranchConditional %28 %23 %24
84          %23 = OpLabel
85                OpBranch %25
86          %25 = OpLabel
87          %30 = OpIAdd %6 %32 %20
88                OpStore %8 %30
89                OpBranch %22
90          %24 = OpLabel
91                OpReturn
92                OpFunctionEnd
93   )";
94 
95   std::unique_ptr<IRContext> context =
96       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
97                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
98   Module* module = context->module();
99   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
100                              << text << std::endl;
101   Function& f = *module->begin();
102   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
103   EXPECT_EQ(ld.NumLoops(), 2u);
104 
105   auto loops = ld.GetLoopsInBinaryLayoutOrder();
106 
107   LoopFusion fusion(context.get(), loops[0], loops[1]);
108   EXPECT_FALSE(fusion.AreCompatible());
109 }
110 
111 /*
112 Generated from the following GLSL + --eliminate-local-multi-store
113 
114 // 1
115 #version 440 core
116 void main() {
117   for (int i = 0; i < 10; i++) {}
118   for (int i = 0; i < 10; i++) {}
119 }
120 */
TEST_F(FusionCompatibilityTest,Compatible)121 TEST_F(FusionCompatibilityTest, Compatible) {
122   const std::string text = R"(
123                OpCapability Shader
124           %1 = OpExtInstImport "GLSL.std.450"
125                OpMemoryModel Logical GLSL450
126                OpEntryPoint Fragment %4 "main"
127                OpExecutionMode %4 OriginUpperLeft
128                OpSource GLSL 440
129                OpName %4 "main"
130                OpName %8 "i"
131                OpName %22 "i"
132           %2 = OpTypeVoid
133           %3 = OpTypeFunction %2
134           %6 = OpTypeInt 32 1
135           %7 = OpTypePointer Function %6
136           %9 = OpConstant %6 0
137          %16 = OpConstant %6 10
138          %17 = OpTypeBool
139          %20 = OpConstant %6 1
140           %4 = OpFunction %2 None %3
141           %5 = OpLabel
142           %8 = OpVariable %7 Function
143          %22 = OpVariable %7 Function
144                OpStore %8 %9
145                OpBranch %10
146          %10 = OpLabel
147          %32 = OpPhi %6 %9 %5 %21 %13
148                OpLoopMerge %12 %13 None
149                OpBranch %14
150          %14 = OpLabel
151          %18 = OpSLessThan %17 %32 %16
152                OpBranchConditional %18 %11 %12
153          %11 = OpLabel
154                OpBranch %13
155          %13 = OpLabel
156          %21 = OpIAdd %6 %32 %20
157                OpStore %8 %21
158                OpBranch %10
159          %12 = OpLabel
160                OpStore %22 %9
161                OpBranch %23
162          %23 = OpLabel
163          %33 = OpPhi %6 %9 %12 %31 %26
164                OpLoopMerge %25 %26 None
165                OpBranch %27
166          %27 = OpLabel
167          %29 = OpSLessThan %17 %33 %16
168                OpBranchConditional %29 %24 %25
169          %24 = OpLabel
170                OpBranch %26
171          %26 = OpLabel
172          %31 = OpIAdd %6 %33 %20
173                OpStore %22 %31
174                OpBranch %23
175          %25 = OpLabel
176                OpReturn
177                OpFunctionEnd
178   )";
179 
180   std::unique_ptr<IRContext> context =
181       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
182                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
183   Module* module = context->module();
184   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
185                              << text << std::endl;
186   Function& f = *module->begin();
187   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
188   EXPECT_EQ(ld.NumLoops(), 2u);
189 
190   auto loops = ld.GetLoopsInBinaryLayoutOrder();
191 
192   LoopFusion fusion(context.get(), loops[0], loops[1]);
193   EXPECT_TRUE(fusion.AreCompatible());
194 }
195 
196 /*
197 Generated from the following GLSL + --eliminate-local-multi-store
198 
199 // 2
200 #version 440 core
201 void main() {
202   for (int i = 0; i < 10; i++) {}
203   for (int j = 0; j < 10; j++) {}
204 }
205 
206 */
TEST_F(FusionCompatibilityTest,DifferentName)207 TEST_F(FusionCompatibilityTest, DifferentName) {
208   const std::string text = R"(
209                OpCapability Shader
210           %1 = OpExtInstImport "GLSL.std.450"
211                OpMemoryModel Logical GLSL450
212                OpEntryPoint Fragment %4 "main"
213                OpExecutionMode %4 OriginUpperLeft
214                OpSource GLSL 440
215                OpName %4 "main"
216                OpName %8 "i"
217                OpName %22 "j"
218           %2 = OpTypeVoid
219           %3 = OpTypeFunction %2
220           %6 = OpTypeInt 32 1
221           %7 = OpTypePointer Function %6
222           %9 = OpConstant %6 0
223          %16 = OpConstant %6 10
224          %17 = OpTypeBool
225          %20 = OpConstant %6 1
226           %4 = OpFunction %2 None %3
227           %5 = OpLabel
228           %8 = OpVariable %7 Function
229          %22 = OpVariable %7 Function
230                OpStore %8 %9
231                OpBranch %10
232          %10 = OpLabel
233          %32 = OpPhi %6 %9 %5 %21 %13
234                OpLoopMerge %12 %13 None
235                OpBranch %14
236          %14 = OpLabel
237          %18 = OpSLessThan %17 %32 %16
238                OpBranchConditional %18 %11 %12
239          %11 = OpLabel
240                OpBranch %13
241          %13 = OpLabel
242          %21 = OpIAdd %6 %32 %20
243                OpStore %8 %21
244                OpBranch %10
245          %12 = OpLabel
246                OpStore %22 %9
247                OpBranch %23
248          %23 = OpLabel
249          %33 = OpPhi %6 %9 %12 %31 %26
250                OpLoopMerge %25 %26 None
251                OpBranch %27
252          %27 = OpLabel
253          %29 = OpSLessThan %17 %33 %16
254                OpBranchConditional %29 %24 %25
255          %24 = OpLabel
256                OpBranch %26
257          %26 = OpLabel
258          %31 = OpIAdd %6 %33 %20
259                OpStore %22 %31
260                OpBranch %23
261          %25 = OpLabel
262                OpReturn
263                OpFunctionEnd
264   )";
265 
266   std::unique_ptr<IRContext> context =
267       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
268                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
269   Module* module = context->module();
270   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
271                              << text << std::endl;
272   Function& f = *module->begin();
273   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
274   EXPECT_EQ(ld.NumLoops(), 2u);
275 
276   auto loops = ld.GetLoopsInBinaryLayoutOrder();
277 
278   LoopFusion fusion(context.get(), loops[0], loops[1]);
279   EXPECT_TRUE(fusion.AreCompatible());
280 }
281 
282 /*
283 Generated from the following GLSL + --eliminate-local-multi-store
284 
285 #version 440 core
286 void main() {
287   // Can't fuse, different step
288   for (int i = 0; i < 10; i++) {}
289   for (int j = 0; j < 10; j=j+2) {}
290 }
291 
292 */
TEST_F(FusionCompatibilityTest,SameBoundsDifferentStep)293 TEST_F(FusionCompatibilityTest, SameBoundsDifferentStep) {
294   const std::string text = R"(
295                OpCapability Shader
296           %1 = OpExtInstImport "GLSL.std.450"
297                OpMemoryModel Logical GLSL450
298                OpEntryPoint Fragment %4 "main"
299                OpExecutionMode %4 OriginUpperLeft
300                OpSource GLSL 440
301                OpName %4 "main"
302                OpName %8 "i"
303                OpName %22 "j"
304           %2 = OpTypeVoid
305           %3 = OpTypeFunction %2
306           %6 = OpTypeInt 32 1
307           %7 = OpTypePointer Function %6
308           %9 = OpConstant %6 0
309          %16 = OpConstant %6 10
310          %17 = OpTypeBool
311          %20 = OpConstant %6 1
312          %31 = OpConstant %6 2
313           %4 = OpFunction %2 None %3
314           %5 = OpLabel
315           %8 = OpVariable %7 Function
316          %22 = OpVariable %7 Function
317                OpStore %8 %9
318                OpBranch %10
319          %10 = OpLabel
320          %33 = OpPhi %6 %9 %5 %21 %13
321                OpLoopMerge %12 %13 None
322                OpBranch %14
323          %14 = OpLabel
324          %18 = OpSLessThan %17 %33 %16
325                OpBranchConditional %18 %11 %12
326          %11 = OpLabel
327                OpBranch %13
328          %13 = OpLabel
329          %21 = OpIAdd %6 %33 %20
330                OpStore %8 %21
331                OpBranch %10
332          %12 = OpLabel
333                OpStore %22 %9
334                OpBranch %23
335          %23 = OpLabel
336          %34 = OpPhi %6 %9 %12 %32 %26
337                OpLoopMerge %25 %26 None
338                OpBranch %27
339          %27 = OpLabel
340          %29 = OpSLessThan %17 %34 %16
341                OpBranchConditional %29 %24 %25
342          %24 = OpLabel
343                OpBranch %26
344          %26 = OpLabel
345          %32 = OpIAdd %6 %34 %31
346                OpStore %22 %32
347                OpBranch %23
348          %25 = OpLabel
349                OpReturn
350                OpFunctionEnd
351   )";
352 
353   std::unique_ptr<IRContext> context =
354       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
355                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
356   Module* module = context->module();
357   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
358                              << text << std::endl;
359   Function& f = *module->begin();
360   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
361   EXPECT_EQ(ld.NumLoops(), 2u);
362 
363   auto loops = ld.GetLoopsInBinaryLayoutOrder();
364 
365   LoopFusion fusion(context.get(), loops[0], loops[1]);
366   EXPECT_FALSE(fusion.AreCompatible());
367 }
368 
369 /*
370 Generated from the following GLSL + --eliminate-local-multi-store
371 
372 // 4
373 #version 440 core
374 void main() {
375   // Can't fuse, different upper bound
376   for (int i = 0; i < 10; i++) {}
377   for (int j = 0; j < 20; j++) {}
378 }
379 
380 */
TEST_F(FusionCompatibilityTest,DifferentUpperBound)381 TEST_F(FusionCompatibilityTest, DifferentUpperBound) {
382   const std::string text = R"(
383                OpCapability Shader
384           %1 = OpExtInstImport "GLSL.std.450"
385                OpMemoryModel Logical GLSL450
386                OpEntryPoint Fragment %4 "main"
387                OpExecutionMode %4 OriginUpperLeft
388                OpSource GLSL 440
389                OpName %4 "main"
390                OpName %8 "i"
391                OpName %22 "j"
392           %2 = OpTypeVoid
393           %3 = OpTypeFunction %2
394           %6 = OpTypeInt 32 1
395           %7 = OpTypePointer Function %6
396           %9 = OpConstant %6 0
397          %16 = OpConstant %6 10
398          %17 = OpTypeBool
399          %20 = OpConstant %6 1
400          %29 = OpConstant %6 20
401           %4 = OpFunction %2 None %3
402           %5 = OpLabel
403           %8 = OpVariable %7 Function
404          %22 = OpVariable %7 Function
405                OpStore %8 %9
406                OpBranch %10
407          %10 = OpLabel
408          %33 = OpPhi %6 %9 %5 %21 %13
409                OpLoopMerge %12 %13 None
410                OpBranch %14
411          %14 = OpLabel
412          %18 = OpSLessThan %17 %33 %16
413                OpBranchConditional %18 %11 %12
414          %11 = OpLabel
415                OpBranch %13
416          %13 = OpLabel
417          %21 = OpIAdd %6 %33 %20
418                OpStore %8 %21
419                OpBranch %10
420          %12 = OpLabel
421                OpStore %22 %9
422                OpBranch %23
423          %23 = OpLabel
424          %34 = OpPhi %6 %9 %12 %32 %26
425                OpLoopMerge %25 %26 None
426                OpBranch %27
427          %27 = OpLabel
428          %30 = OpSLessThan %17 %34 %29
429                OpBranchConditional %30 %24 %25
430          %24 = OpLabel
431                OpBranch %26
432          %26 = OpLabel
433          %32 = OpIAdd %6 %34 %20
434                OpStore %22 %32
435                OpBranch %23
436          %25 = OpLabel
437                OpReturn
438                OpFunctionEnd
439   )";
440 
441   std::unique_ptr<IRContext> context =
442       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
443                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
444   Module* module = context->module();
445   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
446                              << text << std::endl;
447   Function& f = *module->begin();
448   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
449   EXPECT_EQ(ld.NumLoops(), 2u);
450 
451   auto loops = ld.GetLoopsInBinaryLayoutOrder();
452 
453   LoopFusion fusion(context.get(), loops[0], loops[1]);
454   EXPECT_FALSE(fusion.AreCompatible());
455 }
456 
457 /*
458 Generated from the following GLSL + --eliminate-local-multi-store
459 
460 // 5
461 #version 440 core
462 void main() {
463   // Can't fuse, different lower bound
464   for (int i = 5; i < 10; i++) {}
465   for (int j = 0; j < 10; j++) {}
466 }
467 
468 */
TEST_F(FusionCompatibilityTest,DifferentLowerBound)469 TEST_F(FusionCompatibilityTest, DifferentLowerBound) {
470   const std::string text = R"(
471                 OpCapability Shader
472           %1 = OpExtInstImport "GLSL.std.450"
473                OpMemoryModel Logical GLSL450
474                OpEntryPoint Fragment %4 "main"
475                OpExecutionMode %4 OriginUpperLeft
476                OpSource GLSL 440
477                OpName %4 "main"
478                OpName %8 "i"
479                OpName %22 "j"
480           %2 = OpTypeVoid
481           %3 = OpTypeFunction %2
482           %6 = OpTypeInt 32 1
483           %7 = OpTypePointer Function %6
484           %9 = OpConstant %6 5
485          %16 = OpConstant %6 10
486          %17 = OpTypeBool
487          %20 = OpConstant %6 1
488          %23 = OpConstant %6 0
489           %4 = OpFunction %2 None %3
490           %5 = OpLabel
491           %8 = OpVariable %7 Function
492          %22 = OpVariable %7 Function
493                OpStore %8 %9
494                OpBranch %10
495          %10 = OpLabel
496          %33 = OpPhi %6 %9 %5 %21 %13
497                OpLoopMerge %12 %13 None
498                OpBranch %14
499          %14 = OpLabel
500          %18 = OpSLessThan %17 %33 %16
501                OpBranchConditional %18 %11 %12
502          %11 = OpLabel
503                OpBranch %13
504          %13 = OpLabel
505          %21 = OpIAdd %6 %33 %20
506                OpStore %8 %21
507                OpBranch %10
508          %12 = OpLabel
509                OpStore %22 %23
510                OpBranch %24
511          %24 = OpLabel
512          %34 = OpPhi %6 %23 %12 %32 %27
513                OpLoopMerge %26 %27 None
514                OpBranch %28
515          %28 = OpLabel
516          %30 = OpSLessThan %17 %34 %16
517                OpBranchConditional %30 %25 %26
518          %25 = OpLabel
519                OpBranch %27
520          %27 = OpLabel
521          %32 = OpIAdd %6 %34 %20
522                OpStore %22 %32
523                OpBranch %24
524          %26 = OpLabel
525                OpReturn
526                OpFunctionEnd
527   )";
528 
529   std::unique_ptr<IRContext> context =
530       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
531                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
532   Module* module = context->module();
533   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
534                              << text << std::endl;
535   Function& f = *module->begin();
536   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
537   EXPECT_EQ(ld.NumLoops(), 2u);
538 
539   auto loops = ld.GetLoopsInBinaryLayoutOrder();
540 
541   LoopFusion fusion(context.get(), loops[0], loops[1]);
542   EXPECT_FALSE(fusion.AreCompatible());
543 }
544 
545 /*
546 Generated from the following GLSL + --eliminate-local-multi-store
547 
548 // 6
549 #version 440 core
550 void main() {
551   // Can't fuse, break in first loop
552   for (int i = 0; i < 10; i++) {
553     if (i == 5) {
554       break;
555     }
556   }
557   for (int j = 0; j < 10; j++) {}
558 }
559 
560 */
TEST_F(FusionCompatibilityTest,Break)561 TEST_F(FusionCompatibilityTest, Break) {
562   const std::string text = R"(
563                 OpCapability Shader
564           %1 = OpExtInstImport "GLSL.std.450"
565                OpMemoryModel Logical GLSL450
566                OpEntryPoint Fragment %4 "main"
567                OpExecutionMode %4 OriginUpperLeft
568                OpSource GLSL 440
569                OpName %4 "main"
570                OpName %8 "i"
571                OpName %28 "j"
572           %2 = OpTypeVoid
573           %3 = OpTypeFunction %2
574           %6 = OpTypeInt 32 1
575           %7 = OpTypePointer Function %6
576           %9 = OpConstant %6 0
577          %16 = OpConstant %6 10
578          %17 = OpTypeBool
579          %20 = OpConstant %6 5
580          %26 = OpConstant %6 1
581           %4 = OpFunction %2 None %3
582           %5 = OpLabel
583           %8 = OpVariable %7 Function
584          %28 = OpVariable %7 Function
585                OpStore %8 %9
586                OpBranch %10
587          %10 = OpLabel
588          %38 = OpPhi %6 %9 %5 %27 %13
589                OpLoopMerge %12 %13 None
590                OpBranch %14
591          %14 = OpLabel
592          %18 = OpSLessThan %17 %38 %16
593                OpBranchConditional %18 %11 %12
594          %11 = OpLabel
595          %21 = OpIEqual %17 %38 %20
596                OpSelectionMerge %23 None
597                OpBranchConditional %21 %22 %23
598          %22 = OpLabel
599                OpBranch %12
600          %23 = OpLabel
601                OpBranch %13
602          %13 = OpLabel
603          %27 = OpIAdd %6 %38 %26
604                OpStore %8 %27
605                OpBranch %10
606          %12 = OpLabel
607                OpStore %28 %9
608                OpBranch %29
609          %29 = OpLabel
610          %39 = OpPhi %6 %9 %12 %37 %32
611                OpLoopMerge %31 %32 None
612                OpBranch %33
613          %33 = OpLabel
614          %35 = OpSLessThan %17 %39 %16
615                OpBranchConditional %35 %30 %31
616          %30 = OpLabel
617                OpBranch %32
618          %32 = OpLabel
619          %37 = OpIAdd %6 %39 %26
620                OpStore %28 %37
621                OpBranch %29
622          %31 = OpLabel
623                OpReturn
624                OpFunctionEnd
625   )";
626 
627   std::unique_ptr<IRContext> context =
628       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
629                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
630   Module* module = context->module();
631   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
632                              << text << std::endl;
633   Function& f = *module->begin();
634   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
635   EXPECT_EQ(ld.NumLoops(), 2u);
636 
637   auto loops = ld.GetLoopsInBinaryLayoutOrder();
638 
639   LoopFusion fusion(context.get(), loops[0], loops[1]);
640   EXPECT_FALSE(fusion.AreCompatible());
641 }
642 
643 /*
644 Generated from the following GLSL + --eliminate-local-multi-store
645 
646 #version 440 core
647 layout(location = 0) in vec4 c;
648 void main() {
649   int N = int(c.x);
650   for (int i = 0; i < N; i++) {}
651   for (int j = 0; j < N; j++) {}
652 }
653 
654 */
TEST_F(FusionCompatibilityTest,UnknownButSameUpperBound)655 TEST_F(FusionCompatibilityTest, UnknownButSameUpperBound) {
656   const std::string text = R"(
657                OpCapability Shader
658           %1 = OpExtInstImport "GLSL.std.450"
659                OpMemoryModel Logical GLSL450
660                OpEntryPoint Fragment %4 "main" %12
661                OpExecutionMode %4 OriginUpperLeft
662                OpSource GLSL 440
663                OpName %4 "main"
664                OpName %8 "N"
665                OpName %12 "c"
666                OpName %19 "i"
667                OpName %33 "j"
668                OpDecorate %12 Location 0
669           %2 = OpTypeVoid
670           %3 = OpTypeFunction %2
671           %6 = OpTypeInt 32 1
672           %7 = OpTypePointer Function %6
673           %9 = OpTypeFloat 32
674          %10 = OpTypeVector %9 4
675          %11 = OpTypePointer Input %10
676          %12 = OpVariable %11 Input
677          %13 = OpTypeInt 32 0
678          %14 = OpConstant %13 0
679          %15 = OpTypePointer Input %9
680          %20 = OpConstant %6 0
681          %28 = OpTypeBool
682          %31 = OpConstant %6 1
683           %4 = OpFunction %2 None %3
684           %5 = OpLabel
685           %8 = OpVariable %7 Function
686          %19 = OpVariable %7 Function
687          %33 = OpVariable %7 Function
688          %16 = OpAccessChain %15 %12 %14
689          %17 = OpLoad %9 %16
690          %18 = OpConvertFToS %6 %17
691                OpStore %8 %18
692                OpStore %19 %20
693                OpBranch %21
694          %21 = OpLabel
695          %44 = OpPhi %6 %20 %5 %32 %24
696                OpLoopMerge %23 %24 None
697                OpBranch %25
698          %25 = OpLabel
699          %29 = OpSLessThan %28 %44 %18
700                OpBranchConditional %29 %22 %23
701          %22 = OpLabel
702                OpBranch %24
703          %24 = OpLabel
704          %32 = OpIAdd %6 %44 %31
705                OpStore %19 %32
706                OpBranch %21
707          %23 = OpLabel
708                OpStore %33 %20
709                OpBranch %34
710          %34 = OpLabel
711          %46 = OpPhi %6 %20 %23 %43 %37
712                OpLoopMerge %36 %37 None
713                OpBranch %38
714          %38 = OpLabel
715          %41 = OpSLessThan %28 %46 %18
716                OpBranchConditional %41 %35 %36
717          %35 = OpLabel
718                OpBranch %37
719          %37 = OpLabel
720          %43 = OpIAdd %6 %46 %31
721                OpStore %33 %43
722                OpBranch %34
723          %36 = OpLabel
724                OpReturn
725                OpFunctionEnd
726   )";
727 
728   std::unique_ptr<IRContext> context =
729       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
730                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
731   Module* module = context->module();
732   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
733                              << text << std::endl;
734   Function& f = *module->begin();
735   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
736   EXPECT_EQ(ld.NumLoops(), 2u);
737 
738   auto loops = ld.GetLoopsInBinaryLayoutOrder();
739 
740   LoopFusion fusion(context.get(), loops[0], loops[1]);
741   EXPECT_TRUE(fusion.AreCompatible());
742 }
743 
744 /*
745 Generated from the following GLSL + --eliminate-local-multi-store
746 
747 #version 440 core
748 layout(location = 0) in vec4 c;
749 void main() {
750   int N = int(c.x);
751   for (int i = 0; N > j; i++) {}
752   for (int j = 0; N > j; j++) {}
753 }
754 */
TEST_F(FusionCompatibilityTest,UnknownButSameUpperBoundReverseCondition)755 TEST_F(FusionCompatibilityTest, UnknownButSameUpperBoundReverseCondition) {
756   const std::string text = R"(
757                OpCapability Shader
758           %1 = OpExtInstImport "GLSL.std.450"
759                OpMemoryModel Logical GLSL450
760                OpEntryPoint Fragment %4 "main" %12
761                OpExecutionMode %4 OriginUpperLeft
762                OpSource GLSL 440
763                OpName %4 "main"
764                OpName %8 "N"
765                OpName %12 "c"
766                OpName %19 "i"
767                OpName %33 "j"
768                OpDecorate %12 Location 0
769           %2 = OpTypeVoid
770           %3 = OpTypeFunction %2
771           %6 = OpTypeInt 32 1
772           %7 = OpTypePointer Function %6
773           %9 = OpTypeFloat 32
774          %10 = OpTypeVector %9 4
775          %11 = OpTypePointer Input %10
776          %12 = OpVariable %11 Input
777          %13 = OpTypeInt 32 0
778          %14 = OpConstant %13 0
779          %15 = OpTypePointer Input %9
780          %20 = OpConstant %6 0
781          %28 = OpTypeBool
782          %31 = OpConstant %6 1
783           %4 = OpFunction %2 None %3
784           %5 = OpLabel
785           %8 = OpVariable %7 Function
786          %19 = OpVariable %7 Function
787          %33 = OpVariable %7 Function
788          %16 = OpAccessChain %15 %12 %14
789          %17 = OpLoad %9 %16
790          %18 = OpConvertFToS %6 %17
791                OpStore %8 %18
792                OpStore %19 %20
793                OpBranch %21
794          %21 = OpLabel
795          %45 = OpPhi %6 %20 %5 %32 %24
796                OpLoopMerge %23 %24 None
797                OpBranch %25
798          %25 = OpLabel
799          %29 = OpSGreaterThan %28 %18 %45
800                OpBranchConditional %29 %22 %23
801          %22 = OpLabel
802                OpBranch %24
803          %24 = OpLabel
804          %32 = OpIAdd %6 %45 %31
805                OpStore %19 %32
806                OpBranch %21
807          %23 = OpLabel
808                OpStore %33 %20
809                OpBranch %34
810          %34 = OpLabel
811          %47 = OpPhi %6 %20 %23 %43 %37
812                OpLoopMerge %36 %37 None
813                OpBranch %38
814          %38 = OpLabel
815          %41 = OpSGreaterThan %28 %18 %47
816                OpBranchConditional %41 %35 %36
817          %35 = OpLabel
818                OpBranch %37
819          %37 = OpLabel
820          %43 = OpIAdd %6 %47 %31
821                OpStore %33 %43
822                OpBranch %34
823          %36 = OpLabel
824                OpReturn
825                OpFunctionEnd
826   )";
827 
828   std::unique_ptr<IRContext> context =
829       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
830                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
831   Module* module = context->module();
832   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
833                              << text << std::endl;
834   Function& f = *module->begin();
835   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
836   EXPECT_EQ(ld.NumLoops(), 2u);
837 
838   auto loops = ld.GetLoopsInBinaryLayoutOrder();
839 
840   LoopFusion fusion(context.get(), loops[0], loops[1]);
841   EXPECT_TRUE(fusion.AreCompatible());
842 }
843 
844 /*
845 Generated from the following GLSL + --eliminate-local-multi-store
846 
847 #version 440 core
848 layout(location = 0) in vec4 c;
849 void main() {
850   // Can't fuse different bound
851   int N = int(c.x);
852   for (int i = 0; i < N; i++) {}
853   for (int j = 0; j < N+1; j++) {}
854 }
855 
856 */
TEST_F(FusionCompatibilityTest,UnknownUpperBoundAddition)857 TEST_F(FusionCompatibilityTest, UnknownUpperBoundAddition) {
858   const std::string text = R"(
859                OpCapability Shader
860           %1 = OpExtInstImport "GLSL.std.450"
861                OpMemoryModel Logical GLSL450
862                OpEntryPoint Fragment %4 "main" %12
863                OpExecutionMode %4 OriginUpperLeft
864                OpSource GLSL 440
865                OpName %4 "main"
866                OpName %8 "N"
867                OpName %12 "c"
868                OpName %19 "i"
869                OpName %33 "j"
870                OpDecorate %12 Location 0
871           %2 = OpTypeVoid
872           %3 = OpTypeFunction %2
873           %6 = OpTypeInt 32 1
874           %7 = OpTypePointer Function %6
875           %9 = OpTypeFloat 32
876          %10 = OpTypeVector %9 4
877          %11 = OpTypePointer Input %10
878          %12 = OpVariable %11 Input
879          %13 = OpTypeInt 32 0
880          %14 = OpConstant %13 0
881          %15 = OpTypePointer Input %9
882          %20 = OpConstant %6 0
883          %28 = OpTypeBool
884          %31 = OpConstant %6 1
885           %4 = OpFunction %2 None %3
886           %5 = OpLabel
887           %8 = OpVariable %7 Function
888          %19 = OpVariable %7 Function
889          %33 = OpVariable %7 Function
890          %16 = OpAccessChain %15 %12 %14
891          %17 = OpLoad %9 %16
892          %18 = OpConvertFToS %6 %17
893                OpStore %8 %18
894                OpStore %19 %20
895                OpBranch %21
896          %21 = OpLabel
897          %45 = OpPhi %6 %20 %5 %32 %24
898                OpLoopMerge %23 %24 None
899                OpBranch %25
900          %25 = OpLabel
901          %29 = OpSLessThan %28 %45 %18
902                OpBranchConditional %29 %22 %23
903          %22 = OpLabel
904                OpBranch %24
905          %24 = OpLabel
906          %32 = OpIAdd %6 %45 %31
907                OpStore %19 %32
908                OpBranch %21
909          %23 = OpLabel
910                OpStore %33 %20
911                OpBranch %34
912          %34 = OpLabel
913          %47 = OpPhi %6 %20 %23 %44 %37
914                OpLoopMerge %36 %37 None
915                OpBranch %38
916          %38 = OpLabel
917          %41 = OpIAdd %6 %18 %31
918          %42 = OpSLessThan %28 %47 %41
919                OpBranchConditional %42 %35 %36
920          %35 = OpLabel
921                OpBranch %37
922          %37 = OpLabel
923          %44 = OpIAdd %6 %47 %31
924                OpStore %33 %44
925                OpBranch %34
926          %36 = OpLabel
927                OpReturn
928                OpFunctionEnd
929   )";
930 
931   std::unique_ptr<IRContext> context =
932       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
933                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
934   Module* module = context->module();
935   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
936                              << text << std::endl;
937   Function& f = *module->begin();
938   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
939   EXPECT_EQ(ld.NumLoops(), 2u);
940 
941   auto loops = ld.GetLoopsInBinaryLayoutOrder();
942 
943   LoopFusion fusion(context.get(), loops[0], loops[1]);
944   EXPECT_FALSE(fusion.AreCompatible());
945 }
946 
947 /*
948 Generated from the following GLSL + --eliminate-local-multi-store
949 
950 // 10
951 #version 440 core
952 void main() {
953   for (int i = 0; i < 10; i++) {}
954   for (int j = 0; j < 10; j++) {}
955   for (int k = 0; k < 10; k++) {}
956 }
957 
958 */
TEST_F(FusionCompatibilityTest,SeveralAdjacentLoops)959 TEST_F(FusionCompatibilityTest, SeveralAdjacentLoops) {
960   const std::string text = R"(
961                OpCapability Shader
962           %1 = OpExtInstImport "GLSL.std.450"
963                OpMemoryModel Logical GLSL450
964                OpEntryPoint Fragment %4 "main"
965                OpExecutionMode %4 OriginUpperLeft
966                OpSource GLSL 440
967                OpName %4 "main"
968                OpName %8 "i"
969                OpName %22 "j"
970                OpName %32 "k"
971           %2 = OpTypeVoid
972           %3 = OpTypeFunction %2
973           %6 = OpTypeInt 32 1
974           %7 = OpTypePointer Function %6
975           %9 = OpConstant %6 0
976          %16 = OpConstant %6 10
977          %17 = OpTypeBool
978          %20 = OpConstant %6 1
979           %4 = OpFunction %2 None %3
980           %5 = OpLabel
981           %8 = OpVariable %7 Function
982          %22 = OpVariable %7 Function
983          %32 = OpVariable %7 Function
984                OpStore %8 %9
985                OpBranch %10
986          %10 = OpLabel
987          %42 = OpPhi %6 %9 %5 %21 %13
988                OpLoopMerge %12 %13 None
989                OpBranch %14
990          %14 = OpLabel
991          %18 = OpSLessThan %17 %42 %16
992                OpBranchConditional %18 %11 %12
993          %11 = OpLabel
994                OpBranch %13
995          %13 = OpLabel
996          %21 = OpIAdd %6 %42 %20
997                OpStore %8 %21
998                OpBranch %10
999          %12 = OpLabel
1000                OpStore %22 %9
1001                OpBranch %23
1002          %23 = OpLabel
1003          %43 = OpPhi %6 %9 %12 %31 %26
1004                OpLoopMerge %25 %26 None
1005                OpBranch %27
1006          %27 = OpLabel
1007          %29 = OpSLessThan %17 %43 %16
1008                OpBranchConditional %29 %24 %25
1009          %24 = OpLabel
1010                OpBranch %26
1011          %26 = OpLabel
1012          %31 = OpIAdd %6 %43 %20
1013                OpStore %22 %31
1014                OpBranch %23
1015          %25 = OpLabel
1016                OpStore %32 %9
1017                OpBranch %33
1018          %33 = OpLabel
1019          %44 = OpPhi %6 %9 %25 %41 %36
1020                OpLoopMerge %35 %36 None
1021                OpBranch %37
1022          %37 = OpLabel
1023          %39 = OpSLessThan %17 %44 %16
1024                OpBranchConditional %39 %34 %35
1025          %34 = OpLabel
1026                OpBranch %36
1027          %36 = OpLabel
1028          %41 = OpIAdd %6 %44 %20
1029                OpStore %32 %41
1030                OpBranch %33
1031          %35 = OpLabel
1032                OpReturn
1033                OpFunctionEnd
1034   )";
1035 
1036   std::unique_ptr<IRContext> context =
1037       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1038                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1039   Module* module = context->module();
1040   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
1041                              << text << std::endl;
1042   Function& f = *module->begin();
1043   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
1044   EXPECT_EQ(ld.NumLoops(), 3u);
1045 
1046   auto loops = ld.GetLoopsInBinaryLayoutOrder();
1047 
1048   auto loop_0 = loops[0];
1049   auto loop_1 = loops[1];
1050   auto loop_2 = loops[2];
1051 
1052   EXPECT_FALSE(LoopFusion(context.get(), loop_0, loop_0).AreCompatible());
1053   EXPECT_FALSE(LoopFusion(context.get(), loop_0, loop_2).AreCompatible());
1054   EXPECT_FALSE(LoopFusion(context.get(), loop_1, loop_0).AreCompatible());
1055   EXPECT_TRUE(LoopFusion(context.get(), loop_0, loop_1).AreCompatible());
1056   EXPECT_TRUE(LoopFusion(context.get(), loop_1, loop_2).AreCompatible());
1057 }
1058 
1059 /*
1060 Generated from the following GLSL + --eliminate-local-multi-store
1061 
1062 #version 440 core
1063 void main() {
1064   // Can't fuse, not adjacent
1065   int x = 0;
1066   for (int i = 0; i < 10; i++) {
1067     if (i > 10) {
1068       x++;
1069     }
1070   }
1071   x++;
1072   for (int j = 0; j < 10; j++) {}
1073   for (int k = 0; k < 10; k++) {}
1074 }
1075 
1076 */
TEST_F(FusionCompatibilityTest,NonAdjacentLoops)1077 TEST_F(FusionCompatibilityTest, NonAdjacentLoops) {
1078   const std::string text = R"(
1079                OpCapability Shader
1080           %1 = OpExtInstImport "GLSL.std.450"
1081                OpMemoryModel Logical GLSL450
1082                OpEntryPoint Fragment %4 "main"
1083                OpExecutionMode %4 OriginUpperLeft
1084                OpSource GLSL 440
1085                OpName %4 "main"
1086                OpName %8 "x"
1087                OpName %10 "i"
1088                OpName %31 "j"
1089                OpName %41 "k"
1090           %2 = OpTypeVoid
1091           %3 = OpTypeFunction %2
1092           %6 = OpTypeInt 32 1
1093           %7 = OpTypePointer Function %6
1094           %9 = OpConstant %6 0
1095          %17 = OpConstant %6 10
1096          %18 = OpTypeBool
1097          %25 = OpConstant %6 1
1098           %4 = OpFunction %2 None %3
1099           %5 = OpLabel
1100           %8 = OpVariable %7 Function
1101          %10 = OpVariable %7 Function
1102          %31 = OpVariable %7 Function
1103          %41 = OpVariable %7 Function
1104                OpStore %8 %9
1105                OpStore %10 %9
1106                OpBranch %11
1107          %11 = OpLabel
1108          %52 = OpPhi %6 %9 %5 %56 %14
1109          %51 = OpPhi %6 %9 %5 %28 %14
1110                OpLoopMerge %13 %14 None
1111                OpBranch %15
1112          %15 = OpLabel
1113          %19 = OpSLessThan %18 %51 %17
1114                OpBranchConditional %19 %12 %13
1115          %12 = OpLabel
1116          %21 = OpSGreaterThan %18 %52 %17
1117                OpSelectionMerge %23 None
1118                OpBranchConditional %21 %22 %23
1119          %22 = OpLabel
1120          %26 = OpIAdd %6 %52 %25
1121                OpStore %8 %26
1122                OpBranch %23
1123          %23 = OpLabel
1124          %56 = OpPhi %6 %52 %12 %26 %22
1125                OpBranch %14
1126          %14 = OpLabel
1127          %28 = OpIAdd %6 %51 %25
1128                OpStore %10 %28
1129                OpBranch %11
1130          %13 = OpLabel
1131          %30 = OpIAdd %6 %52 %25
1132                OpStore %8 %30
1133                OpStore %31 %9
1134                OpBranch %32
1135          %32 = OpLabel
1136          %53 = OpPhi %6 %9 %13 %40 %35
1137                OpLoopMerge %34 %35 None
1138                OpBranch %36
1139          %36 = OpLabel
1140          %38 = OpSLessThan %18 %53 %17
1141                OpBranchConditional %38 %33 %34
1142          %33 = OpLabel
1143                OpBranch %35
1144          %35 = OpLabel
1145          %40 = OpIAdd %6 %53 %25
1146                OpStore %31 %40
1147                OpBranch %32
1148          %34 = OpLabel
1149                OpStore %41 %9
1150                OpBranch %42
1151          %42 = OpLabel
1152          %54 = OpPhi %6 %9 %34 %50 %45
1153                OpLoopMerge %44 %45 None
1154                OpBranch %46
1155          %46 = OpLabel
1156          %48 = OpSLessThan %18 %54 %17
1157                OpBranchConditional %48 %43 %44
1158          %43 = OpLabel
1159                OpBranch %45
1160          %45 = OpLabel
1161          %50 = OpIAdd %6 %54 %25
1162                OpStore %41 %50
1163                OpBranch %42
1164          %44 = OpLabel
1165                OpReturn
1166                OpFunctionEnd
1167   )";
1168 
1169   std::unique_ptr<IRContext> context =
1170       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1171                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1172   Module* module = context->module();
1173   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
1174                              << text << std::endl;
1175   Function& f = *module->begin();
1176   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
1177   EXPECT_EQ(ld.NumLoops(), 3u);
1178 
1179   auto loops = ld.GetLoopsInBinaryLayoutOrder();
1180 
1181   auto loop_0 = loops[0];
1182   auto loop_1 = loops[1];
1183   auto loop_2 = loops[2];
1184 
1185   EXPECT_FALSE(LoopFusion(context.get(), loop_0, loop_0).AreCompatible());
1186   EXPECT_FALSE(LoopFusion(context.get(), loop_0, loop_2).AreCompatible());
1187   EXPECT_FALSE(LoopFusion(context.get(), loop_0, loop_1).AreCompatible());
1188   EXPECT_TRUE(LoopFusion(context.get(), loop_1, loop_2).AreCompatible());
1189 }
1190 
1191 /*
1192 Generated from the following GLSL + --eliminate-local-multi-store
1193 
1194 // 12
1195 #version 440 core
1196 void main() {
1197   int j = 0;
1198   int i = 0;
1199   for (; i < 10; i++) {}
1200   for (; j < 10; j++) {}
1201 }
1202 
1203 */
TEST_F(FusionCompatibilityTest,CompatibleInitDeclaredBeforeLoops)1204 TEST_F(FusionCompatibilityTest, CompatibleInitDeclaredBeforeLoops) {
1205   const std::string text = R"(
1206                OpCapability Shader
1207           %1 = OpExtInstImport "GLSL.std.450"
1208                OpMemoryModel Logical GLSL450
1209                OpEntryPoint Fragment %4 "main"
1210                OpExecutionMode %4 OriginUpperLeft
1211                OpSource GLSL 440
1212                OpName %4 "main"
1213                OpName %8 "j"
1214                OpName %10 "i"
1215           %2 = OpTypeVoid
1216           %3 = OpTypeFunction %2
1217           %6 = OpTypeInt 32 1
1218           %7 = OpTypePointer Function %6
1219           %9 = OpConstant %6 0
1220          %17 = OpConstant %6 10
1221          %18 = OpTypeBool
1222          %21 = OpConstant %6 1
1223           %4 = OpFunction %2 None %3
1224           %5 = OpLabel
1225           %8 = OpVariable %7 Function
1226          %10 = OpVariable %7 Function
1227                OpStore %8 %9
1228                OpStore %10 %9
1229                OpBranch %11
1230          %11 = OpLabel
1231          %32 = OpPhi %6 %9 %5 %22 %14
1232                OpLoopMerge %13 %14 None
1233                OpBranch %15
1234          %15 = OpLabel
1235          %19 = OpSLessThan %18 %32 %17
1236                OpBranchConditional %19 %12 %13
1237          %12 = OpLabel
1238                OpBranch %14
1239          %14 = OpLabel
1240          %22 = OpIAdd %6 %32 %21
1241                OpStore %10 %22
1242                OpBranch %11
1243          %13 = OpLabel
1244                OpBranch %23
1245          %23 = OpLabel
1246          %33 = OpPhi %6 %9 %13 %31 %26
1247                OpLoopMerge %25 %26 None
1248                OpBranch %27
1249          %27 = OpLabel
1250          %29 = OpSLessThan %18 %33 %17
1251                OpBranchConditional %29 %24 %25
1252          %24 = OpLabel
1253                OpBranch %26
1254          %26 = OpLabel
1255          %31 = OpIAdd %6 %33 %21
1256                OpStore %8 %31
1257                OpBranch %23
1258          %25 = OpLabel
1259                OpReturn
1260                OpFunctionEnd
1261   )";
1262 
1263   std::unique_ptr<IRContext> context =
1264       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1265                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1266   Module* module = context->module();
1267   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
1268                              << text << std::endl;
1269   Function& f = *module->begin();
1270   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
1271   EXPECT_EQ(ld.NumLoops(), 2u);
1272 
1273   auto loops = ld.GetLoopsInBinaryLayoutOrder();
1274 
1275   EXPECT_TRUE(LoopFusion(context.get(), loops[0], loops[1]).AreCompatible());
1276 }
1277 
1278 /*
1279 Generated from the following GLSL + --eliminate-local-multi-store
1280 
1281 // 13 regenerate!
1282 #version 440 core
1283 void main() {
1284   int[10] a;
1285   int[10] b;
1286   // Can't fuse, several induction variables
1287   for (int j = 0; j < 10; j++) {
1288     b[i] = a[i];
1289   }
1290   for (int i = 0, j = 0; i < 10; i++, j = j+2) {
1291   }
1292 }
1293 
1294 */
TEST_F(FusionCompatibilityTest,SeveralInductionVariables)1295 TEST_F(FusionCompatibilityTest, SeveralInductionVariables) {
1296   const std::string text = R"(
1297                OpCapability Shader
1298           %1 = OpExtInstImport "GLSL.std.450"
1299                OpMemoryModel Logical GLSL450
1300                OpEntryPoint Fragment %4 "main"
1301                OpExecutionMode %4 OriginUpperLeft
1302                OpSource GLSL 440
1303                OpName %4 "main"
1304                OpName %8 "j"
1305                OpName %23 "b"
1306                OpName %25 "a"
1307                OpName %33 "i"
1308                OpName %34 "j"
1309           %2 = OpTypeVoid
1310           %3 = OpTypeFunction %2
1311           %6 = OpTypeInt 32 1
1312           %7 = OpTypePointer Function %6
1313           %9 = OpConstant %6 0
1314          %16 = OpConstant %6 10
1315          %17 = OpTypeBool
1316          %19 = OpTypeInt 32 0
1317          %20 = OpConstant %19 10
1318          %21 = OpTypeArray %6 %20
1319          %22 = OpTypePointer Function %21
1320          %31 = OpConstant %6 1
1321          %48 = OpConstant %6 2
1322           %4 = OpFunction %2 None %3
1323           %5 = OpLabel
1324           %8 = OpVariable %7 Function
1325          %23 = OpVariable %22 Function
1326          %25 = OpVariable %22 Function
1327          %33 = OpVariable %7 Function
1328          %34 = OpVariable %7 Function
1329                OpStore %8 %9
1330                OpBranch %10
1331          %10 = OpLabel
1332          %50 = OpPhi %6 %9 %5 %32 %13
1333                OpLoopMerge %12 %13 None
1334                OpBranch %14
1335          %14 = OpLabel
1336          %18 = OpSLessThan %17 %50 %16
1337                OpBranchConditional %18 %11 %12
1338          %11 = OpLabel
1339          %27 = OpAccessChain %7 %25 %50
1340          %28 = OpLoad %6 %27
1341          %29 = OpAccessChain %7 %23 %50
1342                OpStore %29 %28
1343                OpBranch %13
1344          %13 = OpLabel
1345          %32 = OpIAdd %6 %50 %31
1346                OpStore %8 %32
1347                OpBranch %10
1348          %12 = OpLabel
1349                OpStore %33 %9
1350                OpStore %34 %9
1351                OpBranch %35
1352          %35 = OpLabel
1353          %52 = OpPhi %6 %9 %12 %49 %38
1354          %51 = OpPhi %6 %9 %12 %46 %38
1355                OpLoopMerge %37 %38 None
1356                OpBranch %39
1357          %39 = OpLabel
1358          %41 = OpSLessThan %17 %51 %16
1359                OpBranchConditional %41 %36 %37
1360          %36 = OpLabel
1361          %44 = OpAccessChain %7 %25 %52
1362                OpStore %44 %51
1363                OpBranch %38
1364          %38 = OpLabel
1365          %46 = OpIAdd %6 %51 %31
1366                OpStore %33 %46
1367          %49 = OpIAdd %6 %52 %48
1368                OpStore %34 %49
1369                OpBranch %35
1370          %37 = OpLabel
1371                OpReturn
1372                OpFunctionEnd
1373   )";
1374 
1375   std::unique_ptr<IRContext> context =
1376       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1377                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1378   Module* module = context->module();
1379   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
1380                              << text << std::endl;
1381   Function& f = *module->begin();
1382   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
1383   EXPECT_EQ(ld.NumLoops(), 2u);
1384 
1385   auto loops = ld.GetLoopsInBinaryLayoutOrder();
1386 
1387   EXPECT_FALSE(LoopFusion(context.get(), loops[0], loops[1]).AreCompatible());
1388 }
1389 
1390 /*
1391 Generated from the following GLSL + --eliminate-local-multi-store
1392 
1393 // 14
1394 #version 440 core
1395 void main() {
1396   // Fine
1397   for (int i = 0; i < 10; i = i + 2) {}
1398   for (int j = 0; j < 10; j = j + 2) {}
1399 }
1400 
1401 */
TEST_F(FusionCompatibilityTest,CompatibleNonIncrementStep)1402 TEST_F(FusionCompatibilityTest, CompatibleNonIncrementStep) {
1403   const std::string text = R"(
1404                OpCapability Shader
1405           %1 = OpExtInstImport "GLSL.std.450"
1406                OpMemoryModel Logical GLSL450
1407                OpEntryPoint Fragment %4 "main"
1408                OpExecutionMode %4 OriginUpperLeft
1409                OpSource GLSL 440
1410                OpName %4 "main"
1411                OpName %8 "j"
1412                OpName %10 "i"
1413                OpName %11 "i"
1414                OpName %24 "j"
1415           %2 = OpTypeVoid
1416           %3 = OpTypeFunction %2
1417           %6 = OpTypeInt 32 1
1418           %7 = OpTypePointer Function %6
1419           %9 = OpConstant %6 0
1420          %18 = OpConstant %6 10
1421          %19 = OpTypeBool
1422          %22 = OpConstant %6 2
1423           %4 = OpFunction %2 None %3
1424           %5 = OpLabel
1425           %8 = OpVariable %7 Function
1426          %10 = OpVariable %7 Function
1427          %11 = OpVariable %7 Function
1428          %24 = OpVariable %7 Function
1429                OpStore %8 %9
1430                OpStore %10 %9
1431                OpStore %11 %9
1432                OpBranch %12
1433          %12 = OpLabel
1434          %34 = OpPhi %6 %9 %5 %23 %15
1435                OpLoopMerge %14 %15 None
1436                OpBranch %16
1437          %16 = OpLabel
1438          %20 = OpSLessThan %19 %34 %18
1439                OpBranchConditional %20 %13 %14
1440          %13 = OpLabel
1441                OpBranch %15
1442          %15 = OpLabel
1443          %23 = OpIAdd %6 %34 %22
1444                OpStore %11 %23
1445                OpBranch %12
1446          %14 = OpLabel
1447                OpStore %24 %9
1448                OpBranch %25
1449          %25 = OpLabel
1450          %35 = OpPhi %6 %9 %14 %33 %28
1451                OpLoopMerge %27 %28 None
1452                OpBranch %29
1453          %29 = OpLabel
1454          %31 = OpSLessThan %19 %35 %18
1455                OpBranchConditional %31 %26 %27
1456          %26 = OpLabel
1457                OpBranch %28
1458          %28 = OpLabel
1459          %33 = OpIAdd %6 %35 %22
1460                OpStore %24 %33
1461                OpBranch %25
1462          %27 = OpLabel
1463                OpReturn
1464                OpFunctionEnd
1465   )";
1466 
1467   std::unique_ptr<IRContext> context =
1468       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1469                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1470   Module* module = context->module();
1471   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
1472                              << text << std::endl;
1473   Function& f = *module->begin();
1474   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
1475   EXPECT_EQ(ld.NumLoops(), 2u);
1476 
1477   auto loops = ld.GetLoopsInBinaryLayoutOrder();
1478 
1479   EXPECT_TRUE(LoopFusion(context.get(), loops[0], loops[1]).AreCompatible());
1480 }
1481 
1482 /*
1483 Generated from the following GLSL + --eliminate-local-multi-store
1484 
1485 // 15
1486 #version 440 core
1487 
1488 int j = 0;
1489 
1490 void main() {
1491   // Not compatible, unknown init for second.
1492   for (int i = 0; i < 10; i = i + 2) {}
1493   for (; j < 10; j = j + 2) {}
1494 }
1495 
1496 */
TEST_F(FusionCompatibilityTest,UnknonInitForSecondLoop)1497 TEST_F(FusionCompatibilityTest, UnknonInitForSecondLoop) {
1498   const std::string text = R"(
1499                OpCapability Shader
1500           %1 = OpExtInstImport "GLSL.std.450"
1501                OpMemoryModel Logical GLSL450
1502                OpEntryPoint Fragment %4 "main"
1503                OpExecutionMode %4 OriginUpperLeft
1504                OpSource GLSL 440
1505                OpName %4 "main"
1506                OpName %8 "j"
1507                OpName %11 "i"
1508           %2 = OpTypeVoid
1509           %3 = OpTypeFunction %2
1510           %6 = OpTypeInt 32 1
1511           %7 = OpTypePointer Private %6
1512           %8 = OpVariable %7 Private
1513           %9 = OpConstant %6 0
1514          %10 = OpTypePointer Function %6
1515          %18 = OpConstant %6 10
1516          %19 = OpTypeBool
1517          %22 = OpConstant %6 2
1518           %4 = OpFunction %2 None %3
1519           %5 = OpLabel
1520          %11 = OpVariable %10 Function
1521                OpStore %8 %9
1522                OpStore %11 %9
1523                OpBranch %12
1524          %12 = OpLabel
1525          %33 = OpPhi %6 %9 %5 %23 %15
1526                OpLoopMerge %14 %15 None
1527                OpBranch %16
1528          %16 = OpLabel
1529          %20 = OpSLessThan %19 %33 %18
1530                OpBranchConditional %20 %13 %14
1531          %13 = OpLabel
1532                OpBranch %15
1533          %15 = OpLabel
1534          %23 = OpIAdd %6 %33 %22
1535                OpStore %11 %23
1536                OpBranch %12
1537          %14 = OpLabel
1538                OpBranch %24
1539          %24 = OpLabel
1540                OpLoopMerge %26 %27 None
1541                OpBranch %28
1542          %28 = OpLabel
1543          %29 = OpLoad %6 %8
1544          %30 = OpSLessThan %19 %29 %18
1545                OpBranchConditional %30 %25 %26
1546          %25 = OpLabel
1547                OpBranch %27
1548          %27 = OpLabel
1549          %31 = OpLoad %6 %8
1550          %32 = OpIAdd %6 %31 %22
1551                OpStore %8 %32
1552                OpBranch %24
1553          %26 = OpLabel
1554                OpReturn
1555                OpFunctionEnd
1556   )";
1557 
1558   std::unique_ptr<IRContext> context =
1559       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1560                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1561   Module* module = context->module();
1562   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
1563                              << text << std::endl;
1564   Function& f = *module->begin();
1565   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
1566   EXPECT_EQ(ld.NumLoops(), 2u);
1567 
1568   auto loops = ld.GetLoopsInBinaryLayoutOrder();
1569 
1570   EXPECT_FALSE(LoopFusion(context.get(), loops[0], loops[1]).AreCompatible());
1571 }
1572 
1573 /*
1574 Generated from the following GLSL + --eliminate-local-multi-store
1575 
1576 // 16
1577 #version 440 core
1578 void main() {
1579   // Not compatible, continue in loop 0
1580   for (int i = 0; i < 10; ++i) {
1581     if (i % 2 == 1) {
1582       continue;
1583     }
1584   }
1585   for (int j = 0; j < 10; ++j) {}
1586 }
1587 
1588 */
TEST_F(FusionCompatibilityTest,Continue)1589 TEST_F(FusionCompatibilityTest, Continue) {
1590   const std::string text = R"(
1591                OpCapability Shader
1592           %1 = OpExtInstImport "GLSL.std.450"
1593                OpMemoryModel Logical GLSL450
1594                OpEntryPoint Fragment %4 "main"
1595                OpExecutionMode %4 OriginUpperLeft
1596                OpSource GLSL 440
1597                OpName %4 "main"
1598                OpName %8 "i"
1599                OpName %29 "j"
1600           %2 = OpTypeVoid
1601           %3 = OpTypeFunction %2
1602           %6 = OpTypeInt 32 1
1603           %7 = OpTypePointer Function %6
1604           %9 = OpConstant %6 0
1605          %16 = OpConstant %6 10
1606          %17 = OpTypeBool
1607          %20 = OpConstant %6 2
1608          %22 = OpConstant %6 1
1609           %4 = OpFunction %2 None %3
1610           %5 = OpLabel
1611           %8 = OpVariable %7 Function
1612          %29 = OpVariable %7 Function
1613                OpStore %8 %9
1614                OpBranch %10
1615          %10 = OpLabel
1616          %39 = OpPhi %6 %9 %5 %28 %13
1617                OpLoopMerge %12 %13 None
1618                OpBranch %14
1619          %14 = OpLabel
1620          %18 = OpSLessThan %17 %39 %16
1621                OpBranchConditional %18 %11 %12
1622          %11 = OpLabel
1623          %21 = OpSMod %6 %39 %20
1624          %23 = OpIEqual %17 %21 %22
1625                OpSelectionMerge %25 None
1626                OpBranchConditional %23 %24 %25
1627          %24 = OpLabel
1628                OpBranch %13
1629          %25 = OpLabel
1630                OpBranch %13
1631          %13 = OpLabel
1632          %28 = OpIAdd %6 %39 %22
1633                OpStore %8 %28
1634                OpBranch %10
1635          %12 = OpLabel
1636                OpStore %29 %9
1637                OpBranch %30
1638          %30 = OpLabel
1639          %40 = OpPhi %6 %9 %12 %38 %33
1640                OpLoopMerge %32 %33 None
1641                OpBranch %34
1642          %34 = OpLabel
1643          %36 = OpSLessThan %17 %40 %16
1644                OpBranchConditional %36 %31 %32
1645          %31 = OpLabel
1646                OpBranch %33
1647          %33 = OpLabel
1648          %38 = OpIAdd %6 %40 %22
1649                OpStore %29 %38
1650                OpBranch %30
1651          %32 = OpLabel
1652                OpReturn
1653                OpFunctionEnd
1654   )";
1655 
1656   std::unique_ptr<IRContext> context =
1657       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1658                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1659   Module* module = context->module();
1660   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
1661                              << text << std::endl;
1662   Function& f = *module->begin();
1663   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
1664   EXPECT_EQ(ld.NumLoops(), 2u);
1665 
1666   auto loops = ld.GetLoopsInBinaryLayoutOrder();
1667 
1668   EXPECT_FALSE(LoopFusion(context.get(), loops[0], loops[1]).AreCompatible());
1669 }
1670 
1671 /*
1672 Generated from the following GLSL + --eliminate-local-multi-store
1673 
1674 #version 440 core
1675 void main() {
1676   int[10] a;
1677   // Compatible
1678   for (int i = 0; i < 10; ++i) {
1679     if (i % 2 == 1) {
1680     } else {
1681       a[i] = i;
1682     }
1683   }
1684   for (int j = 0; j < 10; ++j) {}
1685 }
1686 
1687 */
TEST_F(FusionCompatibilityTest,IfElseInLoop)1688 TEST_F(FusionCompatibilityTest, IfElseInLoop) {
1689   const std::string text = R"(
1690                OpCapability Shader
1691           %1 = OpExtInstImport "GLSL.std.450"
1692                OpMemoryModel Logical GLSL450
1693                OpEntryPoint Fragment %4 "main"
1694                OpExecutionMode %4 OriginUpperLeft
1695                OpSource GLSL 440
1696                OpName %4 "main"
1697                OpName %8 "i"
1698                OpName %31 "a"
1699                OpName %37 "j"
1700           %2 = OpTypeVoid
1701           %3 = OpTypeFunction %2
1702           %6 = OpTypeInt 32 1
1703           %7 = OpTypePointer Function %6
1704           %9 = OpConstant %6 0
1705          %16 = OpConstant %6 10
1706          %17 = OpTypeBool
1707          %20 = OpConstant %6 2
1708          %22 = OpConstant %6 1
1709          %27 = OpTypeInt 32 0
1710          %28 = OpConstant %27 10
1711          %29 = OpTypeArray %6 %28
1712          %30 = OpTypePointer Function %29
1713           %4 = OpFunction %2 None %3
1714           %5 = OpLabel
1715           %8 = OpVariable %7 Function
1716          %31 = OpVariable %30 Function
1717          %37 = OpVariable %7 Function
1718                OpStore %8 %9
1719                OpBranch %10
1720          %10 = OpLabel
1721          %47 = OpPhi %6 %9 %5 %36 %13
1722                OpLoopMerge %12 %13 None
1723                OpBranch %14
1724          %14 = OpLabel
1725          %18 = OpSLessThan %17 %47 %16
1726                OpBranchConditional %18 %11 %12
1727          %11 = OpLabel
1728          %21 = OpSMod %6 %47 %20
1729          %23 = OpIEqual %17 %21 %22
1730                OpSelectionMerge %25 None
1731                OpBranchConditional %23 %24 %26
1732          %24 = OpLabel
1733                OpBranch %25
1734          %26 = OpLabel
1735          %34 = OpAccessChain %7 %31 %47
1736                OpStore %34 %47
1737                OpBranch %25
1738          %25 = OpLabel
1739                OpBranch %13
1740          %13 = OpLabel
1741          %36 = OpIAdd %6 %47 %22
1742                OpStore %8 %36
1743                OpBranch %10
1744          %12 = OpLabel
1745                OpStore %37 %9
1746                OpBranch %38
1747          %38 = OpLabel
1748          %48 = OpPhi %6 %9 %12 %46 %41
1749                OpLoopMerge %40 %41 None
1750                OpBranch %42
1751          %42 = OpLabel
1752          %44 = OpSLessThan %17 %48 %16
1753                OpBranchConditional %44 %39 %40
1754          %39 = OpLabel
1755                OpBranch %41
1756          %41 = OpLabel
1757          %46 = OpIAdd %6 %48 %22
1758                OpStore %37 %46
1759                OpBranch %38
1760          %40 = OpLabel
1761                OpReturn
1762                OpFunctionEnd
1763   )";
1764 
1765   std::unique_ptr<IRContext> context =
1766       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1767                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1768   Module* module = context->module();
1769   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
1770                              << text << std::endl;
1771   Function& f = *module->begin();
1772   LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
1773   EXPECT_EQ(ld.NumLoops(), 2u);
1774 
1775   auto loops = ld.GetLoopsInBinaryLayoutOrder();
1776 
1777   EXPECT_TRUE(LoopFusion(context.get(), loops[0], loops[1]).AreCompatible());
1778 }
1779 
1780 }  // namespace
1781 }  // namespace opt
1782 }  // namespace spvtools
1783