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