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