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