1 // Copyright (c) 2018 Google LLC.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <memory>
16 #include <string>
17 #include <vector>
18
19 #include "gmock/gmock.h"
20 #include "source/opt/loop_unroller.h"
21 #include "source/opt/loop_utils.h"
22 #include "source/opt/pass.h"
23 #include "test/opt/assembly_builder.h"
24 #include "test/opt/function_utils.h"
25 #include "test/opt/pass_fixture.h"
26 #include "test/opt/pass_utils.h"
27
28 namespace spvtools {
29 namespace opt {
30 namespace {
31
32 using ::testing::UnorderedElementsAre;
33 using PassClassTest = PassTest<::testing::Test>;
34
35 /*
36 Generated from the following GLSL
37 #version 330 core
38 layout(location = 0) out vec4 c;
39 void main() {
40 float x[4];
41 for (int i = 0; i < 4; ++i) {
42 x[i] = 1.0f;
43 }
44 }
45 */
TEST_F(PassClassTest,SimpleFullyUnrollTest)46 TEST_F(PassClassTest, SimpleFullyUnrollTest) {
47 // With LocalMultiStoreElimPass
48 const std::string text = R"(
49 OpCapability Shader
50 %1 = OpExtInstImport "GLSL.std.450"
51 OpMemoryModel Logical GLSL450
52 OpEntryPoint Fragment %2 "main" %3
53 OpExecutionMode %2 OriginUpperLeft
54 OpSource GLSL 330
55 OpName %2 "main"
56 OpName %5 "x"
57 OpName %3 "c"
58 OpDecorate %3 Location 0
59 %6 = OpTypeVoid
60 %7 = OpTypeFunction %6
61 %8 = OpTypeInt 32 1
62 %9 = OpTypePointer Function %8
63 %10 = OpConstant %8 0
64 %11 = OpConstant %8 4
65 %12 = OpTypeBool
66 %13 = OpTypeFloat 32
67 %14 = OpTypeInt 32 0
68 %15 = OpConstant %14 4
69 %16 = OpTypeArray %13 %15
70 %17 = OpTypePointer Function %16
71 %18 = OpConstant %13 1
72 %19 = OpTypePointer Function %13
73 %20 = OpConstant %8 1
74 %21 = OpTypeVector %13 4
75 %22 = OpTypePointer Output %21
76 %3 = OpVariable %22 Output
77 %2 = OpFunction %6 None %7
78 %23 = OpLabel
79 %5 = OpVariable %17 Function
80 OpBranch %24
81 %24 = OpLabel
82 %35 = OpPhi %8 %10 %23 %34 %26
83 OpLoopMerge %25 %26 Unroll
84 OpBranch %27
85 %27 = OpLabel
86 %29 = OpSLessThan %12 %35 %11
87 OpBranchConditional %29 %30 %25
88 %30 = OpLabel
89 %32 = OpAccessChain %19 %5 %35
90 OpStore %32 %18
91 OpBranch %26
92 %26 = OpLabel
93 %34 = OpIAdd %8 %35 %20
94 OpBranch %24
95 %25 = OpLabel
96 OpReturn
97 OpFunctionEnd
98 )";
99
100 const std::string output = R"(OpCapability Shader
101 %1 = OpExtInstImport "GLSL.std.450"
102 OpMemoryModel Logical GLSL450
103 OpEntryPoint Fragment %2 "main" %3
104 OpExecutionMode %2 OriginUpperLeft
105 OpSource GLSL 330
106 OpName %2 "main"
107 OpName %4 "x"
108 OpName %3 "c"
109 OpDecorate %3 Location 0
110 %5 = OpTypeVoid
111 %6 = OpTypeFunction %5
112 %7 = OpTypeInt 32 1
113 %8 = OpTypePointer Function %7
114 %9 = OpConstant %7 0
115 %10 = OpConstant %7 4
116 %11 = OpTypeBool
117 %12 = OpTypeFloat 32
118 %13 = OpTypeInt 32 0
119 %14 = OpConstant %13 4
120 %15 = OpTypeArray %12 %14
121 %16 = OpTypePointer Function %15
122 %17 = OpConstant %12 1
123 %18 = OpTypePointer Function %12
124 %19 = OpConstant %7 1
125 %20 = OpTypeVector %12 4
126 %21 = OpTypePointer Output %20
127 %3 = OpVariable %21 Output
128 %2 = OpFunction %5 None %6
129 %22 = OpLabel
130 %4 = OpVariable %16 Function
131 OpBranch %23
132 %23 = OpLabel
133 OpBranch %28
134 %28 = OpLabel
135 %29 = OpSLessThan %11 %9 %10
136 OpBranch %30
137 %30 = OpLabel
138 %31 = OpAccessChain %18 %4 %9
139 OpStore %31 %17
140 OpBranch %26
141 %26 = OpLabel
142 %25 = OpIAdd %7 %9 %19
143 OpBranch %32
144 %32 = OpLabel
145 OpBranch %34
146 %34 = OpLabel
147 %35 = OpSLessThan %11 %25 %10
148 OpBranch %36
149 %36 = OpLabel
150 %37 = OpAccessChain %18 %4 %25
151 OpStore %37 %17
152 OpBranch %38
153 %38 = OpLabel
154 %39 = OpIAdd %7 %25 %19
155 OpBranch %40
156 %40 = OpLabel
157 OpBranch %42
158 %42 = OpLabel
159 %43 = OpSLessThan %11 %39 %10
160 OpBranch %44
161 %44 = OpLabel
162 %45 = OpAccessChain %18 %4 %39
163 OpStore %45 %17
164 OpBranch %46
165 %46 = OpLabel
166 %47 = OpIAdd %7 %39 %19
167 OpBranch %48
168 %48 = OpLabel
169 OpBranch %50
170 %50 = OpLabel
171 %51 = OpSLessThan %11 %47 %10
172 OpBranch %52
173 %52 = OpLabel
174 %53 = OpAccessChain %18 %4 %47
175 OpStore %53 %17
176 OpBranch %54
177 %54 = OpLabel
178 %55 = OpIAdd %7 %47 %19
179 OpBranch %27
180 %27 = OpLabel
181 OpReturn
182 OpFunctionEnd
183 )";
184
185 std::unique_ptr<IRContext> context =
186 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
187 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
188 Module* module = context->module();
189 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
190 << text << std::endl;
191
192 LoopUnroller loop_unroller;
193 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
194 SinglePassRunAndCheck<LoopUnroller>(text, output, false);
195 }
196
197 template <int factor>
198 class PartialUnrollerTestPass : public Pass {
199 public:
PartialUnrollerTestPass()200 PartialUnrollerTestPass() : Pass() {}
201
name() const202 const char* name() const override { return "Loop unroller"; }
203
Process()204 Status Process() override {
205 for (Function& f : *context()->module()) {
206 LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(&f);
207 for (auto& loop : loop_descriptor) {
208 LoopUtils loop_utils{context(), &loop};
209 loop_utils.PartiallyUnroll(factor);
210 }
211 }
212
213 return Pass::Status::SuccessWithChange;
214 }
215 };
216
217 /*
218 Generated from the following GLSL
219 #version 330 core
220 layout(location = 0) out vec4 c;
221 void main() {
222 float x[10];
223 for (int i = 0; i < 10; ++i) {
224 x[i] = 1.0f;
225 }
226 }
227 */
TEST_F(PassClassTest,SimplePartialUnroll)228 TEST_F(PassClassTest, SimplePartialUnroll) {
229 // With LocalMultiStoreElimPass
230 const std::string text = R"(
231 OpCapability Shader
232 %1 = OpExtInstImport "GLSL.std.450"
233 OpMemoryModel Logical GLSL450
234 OpEntryPoint Fragment %2 "main" %3
235 OpExecutionMode %2 OriginUpperLeft
236 OpSource GLSL 330
237 OpName %2 "main"
238 OpName %5 "x"
239 OpName %3 "c"
240 OpDecorate %3 Location 0
241 %6 = OpTypeVoid
242 %7 = OpTypeFunction %6
243 %8 = OpTypeInt 32 1
244 %9 = OpTypePointer Function %8
245 %10 = OpConstant %8 0
246 %11 = OpConstant %8 10
247 %12 = OpTypeBool
248 %13 = OpTypeFloat 32
249 %14 = OpTypeInt 32 0
250 %15 = OpConstant %14 10
251 %16 = OpTypeArray %13 %15
252 %17 = OpTypePointer Function %16
253 %18 = OpConstant %13 1
254 %19 = OpTypePointer Function %13
255 %20 = OpConstant %8 1
256 %21 = OpTypeVector %13 4
257 %22 = OpTypePointer Output %21
258 %3 = OpVariable %22 Output
259 %2 = OpFunction %6 None %7
260 %23 = OpLabel
261 %5 = OpVariable %17 Function
262 OpBranch %24
263 %24 = OpLabel
264 %35 = OpPhi %8 %10 %23 %34 %26
265 OpLoopMerge %25 %26 Unroll
266 OpBranch %27
267 %27 = OpLabel
268 %29 = OpSLessThan %12 %35 %11
269 OpBranchConditional %29 %30 %25
270 %30 = OpLabel
271 %32 = OpAccessChain %19 %5 %35
272 OpStore %32 %18
273 OpBranch %26
274 %26 = OpLabel
275 %34 = OpIAdd %8 %35 %20
276 OpBranch %24
277 %25 = OpLabel
278 OpReturn
279 OpFunctionEnd
280 )";
281
282 const std::string output = R"(OpCapability Shader
283 %1 = OpExtInstImport "GLSL.std.450"
284 OpMemoryModel Logical GLSL450
285 OpEntryPoint Fragment %2 "main" %3
286 OpExecutionMode %2 OriginUpperLeft
287 OpSource GLSL 330
288 OpName %2 "main"
289 OpName %4 "x"
290 OpName %3 "c"
291 OpDecorate %3 Location 0
292 %5 = OpTypeVoid
293 %6 = OpTypeFunction %5
294 %7 = OpTypeInt 32 1
295 %8 = OpTypePointer Function %7
296 %9 = OpConstant %7 0
297 %10 = OpConstant %7 10
298 %11 = OpTypeBool
299 %12 = OpTypeFloat 32
300 %13 = OpTypeInt 32 0
301 %14 = OpConstant %13 10
302 %15 = OpTypeArray %12 %14
303 %16 = OpTypePointer Function %15
304 %17 = OpConstant %12 1
305 %18 = OpTypePointer Function %12
306 %19 = OpConstant %7 1
307 %20 = OpTypeVector %12 4
308 %21 = OpTypePointer Output %20
309 %3 = OpVariable %21 Output
310 %2 = OpFunction %5 None %6
311 %22 = OpLabel
312 %4 = OpVariable %16 Function
313 OpBranch %23
314 %23 = OpLabel
315 %24 = OpPhi %7 %9 %22 %39 %38
316 OpLoopMerge %27 %38 DontUnroll
317 OpBranch %28
318 %28 = OpLabel
319 %29 = OpSLessThan %11 %24 %10
320 OpBranchConditional %29 %30 %27
321 %30 = OpLabel
322 %31 = OpAccessChain %18 %4 %24
323 OpStore %31 %17
324 OpBranch %26
325 %26 = OpLabel
326 %25 = OpIAdd %7 %24 %19
327 OpBranch %32
328 %32 = OpLabel
329 OpBranch %34
330 %34 = OpLabel
331 %35 = OpSLessThan %11 %25 %10
332 OpBranch %36
333 %36 = OpLabel
334 %37 = OpAccessChain %18 %4 %25
335 OpStore %37 %17
336 OpBranch %38
337 %38 = OpLabel
338 %39 = OpIAdd %7 %25 %19
339 OpBranch %23
340 %27 = OpLabel
341 OpReturn
342 OpFunctionEnd
343 )";
344
345 std::unique_ptr<IRContext> context =
346 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
347 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
348 Module* module = context->module();
349 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
350 << text << std::endl;
351
352 LoopUnroller loop_unroller;
353 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
354 SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(text, output, false);
355 }
356
357 /*
358 Generated from the following GLSL
359 #version 330 core
360 layout(location = 0) out vec4 c;
361 void main() {
362 float x[10];
363 for (int i = 0; i < 10; ++i) {
364 x[i] = 1.0f;
365 }
366 }
367 */
TEST_F(PassClassTest,SimpleUnevenPartialUnroll)368 TEST_F(PassClassTest, SimpleUnevenPartialUnroll) {
369 // With LocalMultiStoreElimPass
370 const std::string text = R"(
371 OpCapability Shader
372 %1 = OpExtInstImport "GLSL.std.450"
373 OpMemoryModel Logical GLSL450
374 OpEntryPoint Fragment %2 "main" %3
375 OpExecutionMode %2 OriginUpperLeft
376 OpSource GLSL 330
377 OpName %2 "main"
378 OpName %5 "x"
379 OpName %3 "c"
380 OpDecorate %3 Location 0
381 %6 = OpTypeVoid
382 %7 = OpTypeFunction %6
383 %8 = OpTypeInt 32 1
384 %9 = OpTypePointer Function %8
385 %10 = OpConstant %8 0
386 %11 = OpConstant %8 10
387 %12 = OpTypeBool
388 %13 = OpTypeFloat 32
389 %14 = OpTypeInt 32 0
390 %15 = OpConstant %14 10
391 %16 = OpTypeArray %13 %15
392 %17 = OpTypePointer Function %16
393 %18 = OpConstant %13 1
394 %19 = OpTypePointer Function %13
395 %20 = OpConstant %8 1
396 %21 = OpTypeVector %13 4
397 %22 = OpTypePointer Output %21
398 %3 = OpVariable %22 Output
399 %2 = OpFunction %6 None %7
400 %23 = OpLabel
401 %5 = OpVariable %17 Function
402 OpBranch %24
403 %24 = OpLabel
404 %35 = OpPhi %8 %10 %23 %34 %26
405 OpLoopMerge %25 %26 Unroll
406 OpBranch %27
407 %27 = OpLabel
408 %29 = OpSLessThan %12 %35 %11
409 OpBranchConditional %29 %30 %25
410 %30 = OpLabel
411 %32 = OpAccessChain %19 %5 %35
412 OpStore %32 %18
413 OpBranch %26
414 %26 = OpLabel
415 %34 = OpIAdd %8 %35 %20
416 OpBranch %24
417 %25 = OpLabel
418 OpReturn
419 OpFunctionEnd
420 )";
421
422 const std::string output = R"(OpCapability Shader
423 %1 = OpExtInstImport "GLSL.std.450"
424 OpMemoryModel Logical GLSL450
425 OpEntryPoint Fragment %2 "main" %3
426 OpExecutionMode %2 OriginUpperLeft
427 OpSource GLSL 330
428 OpName %2 "main"
429 OpName %4 "x"
430 OpName %3 "c"
431 OpDecorate %3 Location 0
432 %5 = OpTypeVoid
433 %6 = OpTypeFunction %5
434 %7 = OpTypeInt 32 1
435 %8 = OpTypePointer Function %7
436 %9 = OpConstant %7 0
437 %10 = OpConstant %7 10
438 %11 = OpTypeBool
439 %12 = OpTypeFloat 32
440 %13 = OpTypeInt 32 0
441 %14 = OpConstant %13 10
442 %15 = OpTypeArray %12 %14
443 %16 = OpTypePointer Function %15
444 %17 = OpConstant %12 1
445 %18 = OpTypePointer Function %12
446 %19 = OpConstant %7 1
447 %20 = OpTypeVector %12 4
448 %21 = OpTypePointer Output %20
449 %3 = OpVariable %21 Output
450 %58 = OpConstant %13 1
451 %2 = OpFunction %5 None %6
452 %22 = OpLabel
453 %4 = OpVariable %16 Function
454 OpBranch %23
455 %23 = OpLabel
456 %24 = OpPhi %7 %9 %22 %25 %26
457 OpLoopMerge %32 %26 Unroll
458 OpBranch %28
459 %28 = OpLabel
460 %29 = OpSLessThan %11 %24 %58
461 OpBranchConditional %29 %30 %32
462 %30 = OpLabel
463 %31 = OpAccessChain %18 %4 %24
464 OpStore %31 %17
465 OpBranch %26
466 %26 = OpLabel
467 %25 = OpIAdd %7 %24 %19
468 OpBranch %23
469 %32 = OpLabel
470 OpBranch %33
471 %33 = OpLabel
472 %34 = OpPhi %7 %24 %32 %57 %56
473 OpLoopMerge %41 %56 DontUnroll
474 OpBranch %35
475 %35 = OpLabel
476 %36 = OpSLessThan %11 %34 %10
477 OpBranchConditional %36 %37 %41
478 %37 = OpLabel
479 %38 = OpAccessChain %18 %4 %34
480 OpStore %38 %17
481 OpBranch %39
482 %39 = OpLabel
483 %40 = OpIAdd %7 %34 %19
484 OpBranch %42
485 %42 = OpLabel
486 OpBranch %44
487 %44 = OpLabel
488 %45 = OpSLessThan %11 %40 %10
489 OpBranch %46
490 %46 = OpLabel
491 %47 = OpAccessChain %18 %4 %40
492 OpStore %47 %17
493 OpBranch %48
494 %48 = OpLabel
495 %49 = OpIAdd %7 %40 %19
496 OpBranch %50
497 %50 = OpLabel
498 OpBranch %52
499 %52 = OpLabel
500 %53 = OpSLessThan %11 %49 %10
501 OpBranch %54
502 %54 = OpLabel
503 %55 = OpAccessChain %18 %4 %49
504 OpStore %55 %17
505 OpBranch %56
506 %56 = OpLabel
507 %57 = OpIAdd %7 %49 %19
508 OpBranch %33
509 %41 = OpLabel
510 OpReturn
511 %27 = OpLabel
512 OpReturn
513 OpFunctionEnd
514 )";
515
516 std::unique_ptr<IRContext> context =
517 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
518 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
519 Module* module = context->module();
520 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
521 << text << std::endl;
522
523 LoopUnroller loop_unroller;
524 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
525 // By unrolling by a factor that doesn't divide evenly into the number of loop
526 // iterations we perfom an additional transform when partially unrolling to
527 // account for the remainder.
528 SinglePassRunAndCheck<PartialUnrollerTestPass<3>>(text, output, false);
529 }
530
531 /* Generated from
532 #version 410 core
533 layout(location=0) flat in int upper_bound;
534 void main() {
535 float x[10];
536 for (int i = 2; i < 8; i+=2) {
537 x[i] = i;
538 }
539 }
540 */
TEST_F(PassClassTest,SimpleLoopIterationsCheck)541 TEST_F(PassClassTest, SimpleLoopIterationsCheck) {
542 // With LocalMultiStoreElimPass
543 const std::string text = R"(
544 OpCapability Shader
545 %1 = OpExtInstImport "GLSL.std.450"
546 OpMemoryModel Logical GLSL450
547 OpEntryPoint Fragment %2 "main" %3
548 OpExecutionMode %2 OriginUpperLeft
549 OpSource GLSL 410
550 OpName %2 "main"
551 OpName %5 "x"
552 OpName %3 "upper_bound"
553 OpDecorate %3 Flat
554 OpDecorate %3 Location 0
555 %6 = OpTypeVoid
556 %7 = OpTypeFunction %6
557 %8 = OpTypeInt 32 1
558 %9 = OpTypePointer Function %8
559 %10 = OpConstant %8 2
560 %11 = OpConstant %8 8
561 %12 = OpTypeBool
562 %13 = OpTypeFloat 32
563 %14 = OpTypeInt 32 0
564 %15 = OpConstant %14 10
565 %16 = OpTypeArray %13 %15
566 %17 = OpTypePointer Function %16
567 %18 = OpTypePointer Function %13
568 %19 = OpTypePointer Input %8
569 %3 = OpVariable %19 Input
570 %2 = OpFunction %6 None %7
571 %20 = OpLabel
572 %5 = OpVariable %17 Function
573 OpBranch %21
574 %21 = OpLabel
575 %34 = OpPhi %8 %10 %20 %33 %23
576 OpLoopMerge %22 %23 Unroll
577 OpBranch %24
578 %24 = OpLabel
579 %26 = OpSLessThan %12 %34 %11
580 OpBranchConditional %26 %27 %22
581 %27 = OpLabel
582 %30 = OpConvertSToF %13 %34
583 %31 = OpAccessChain %18 %5 %34
584 OpStore %31 %30
585 OpBranch %23
586 %23 = OpLabel
587 %33 = OpIAdd %8 %34 %10
588 OpBranch %21
589 %22 = OpLabel
590 OpReturn
591 OpFunctionEnd
592 )";
593
594 std::unique_ptr<IRContext> context =
595 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
596 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
597 Module* module = context->module();
598 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
599 << text << std::endl;
600
601 Function* f = spvtest::GetFunction(module, 2);
602
603 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
604 EXPECT_EQ(loop_descriptor.NumLoops(), 1u);
605
606 Loop& loop = loop_descriptor.GetLoopByIndex(0);
607
608 EXPECT_TRUE(loop.HasUnrollLoopControl());
609
610 BasicBlock* condition = loop.FindConditionBlock();
611 EXPECT_EQ(condition->id(), 24u);
612
613 Instruction* induction = loop.FindConditionVariable(condition);
614 EXPECT_EQ(induction->result_id(), 34u);
615
616 LoopUtils loop_utils{context.get(), &loop};
617 EXPECT_TRUE(loop_utils.CanPerformUnroll());
618
619 size_t iterations = 0;
620 EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(),
621 &iterations));
622 EXPECT_EQ(iterations, 3u);
623 }
624
625 /* Generated from
626 #version 410 core
627 void main() {
628 float x[10];
629 for (int i = -1; i < 6; i+=3) {
630 x[i] = i;
631 }
632 }
633 */
TEST_F(PassClassTest,SimpleLoopIterationsCheckSignedInit)634 TEST_F(PassClassTest, SimpleLoopIterationsCheckSignedInit) {
635 // With LocalMultiStoreElimPass
636 const std::string text = R"(
637 OpCapability Shader
638 %1 = OpExtInstImport "GLSL.std.450"
639 OpMemoryModel Logical GLSL450
640 OpEntryPoint Fragment %2 "main" %3
641 OpExecutionMode %2 OriginUpperLeft
642 OpSource GLSL 410
643 OpName %2 "main"
644 OpName %5 "x"
645 OpName %3 "upper_bound"
646 OpDecorate %3 Flat
647 OpDecorate %3 Location 0
648 %6 = OpTypeVoid
649 %7 = OpTypeFunction %6
650 %8 = OpTypeInt 32 1
651 %9 = OpTypePointer Function %8
652 %10 = OpConstant %8 -1
653 %11 = OpConstant %8 6
654 %12 = OpTypeBool
655 %13 = OpTypeFloat 32
656 %14 = OpTypeInt 32 0
657 %15 = OpConstant %14 10
658 %16 = OpTypeArray %13 %15
659 %17 = OpTypePointer Function %16
660 %18 = OpTypePointer Function %13
661 %19 = OpConstant %8 3
662 %20 = OpTypePointer Input %8
663 %3 = OpVariable %20 Input
664 %2 = OpFunction %6 None %7
665 %21 = OpLabel
666 %5 = OpVariable %17 Function
667 OpBranch %22
668 %22 = OpLabel
669 %35 = OpPhi %8 %10 %21 %34 %24
670 OpLoopMerge %23 %24 None
671 OpBranch %25
672 %25 = OpLabel
673 %27 = OpSLessThan %12 %35 %11
674 OpBranchConditional %27 %28 %23
675 %28 = OpLabel
676 %31 = OpConvertSToF %13 %35
677 %32 = OpAccessChain %18 %5 %35
678 OpStore %32 %31
679 OpBranch %24
680 %24 = OpLabel
681 %34 = OpIAdd %8 %35 %19
682 OpBranch %22
683 %23 = OpLabel
684 OpReturn
685 OpFunctionEnd
686 )";
687
688 std::unique_ptr<IRContext> context =
689 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
690 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
691 Module* module = context->module();
692 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
693 << text << std::endl;
694
695 Function* f = spvtest::GetFunction(module, 2);
696
697 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
698
699 EXPECT_EQ(loop_descriptor.NumLoops(), 1u);
700
701 Loop& loop = loop_descriptor.GetLoopByIndex(0);
702
703 EXPECT_FALSE(loop.HasUnrollLoopControl());
704
705 BasicBlock* condition = loop.FindConditionBlock();
706 EXPECT_EQ(condition->id(), 25u);
707
708 Instruction* induction = loop.FindConditionVariable(condition);
709 EXPECT_EQ(induction->result_id(), 35u);
710
711 LoopUtils loop_utils{context.get(), &loop};
712 EXPECT_TRUE(loop_utils.CanPerformUnroll());
713
714 size_t iterations = 0;
715 EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(),
716 &iterations));
717 EXPECT_EQ(iterations, 3u);
718 }
719
720 /*
721 Generated from the following GLSL
722 #version 410 core
723 void main() {
724 float out_array[6];
725 for (uint i = 0; i < 2; i++) {
726 for (int x = 0; x < 3; ++x) {
727 out_array[x + i*3] = i;
728 }
729 }
730 }
731 */
TEST_F(PassClassTest,UnrollNestedLoops)732 TEST_F(PassClassTest, UnrollNestedLoops) {
733 // With LocalMultiStoreElimPass
734 const std::string text = R"(
735 OpCapability Shader
736 %1 = OpExtInstImport "GLSL.std.450"
737 OpMemoryModel Logical GLSL450
738 OpEntryPoint Fragment %4 "main"
739 OpExecutionMode %4 OriginUpperLeft
740 OpSource GLSL 410
741 OpName %4 "main"
742 OpName %35 "out_array"
743 %2 = OpTypeVoid
744 %3 = OpTypeFunction %2
745 %6 = OpTypeInt 32 0
746 %7 = OpTypePointer Function %6
747 %9 = OpConstant %6 0
748 %16 = OpConstant %6 2
749 %17 = OpTypeBool
750 %19 = OpTypeInt 32 1
751 %20 = OpTypePointer Function %19
752 %22 = OpConstant %19 0
753 %29 = OpConstant %19 3
754 %31 = OpTypeFloat 32
755 %32 = OpConstant %6 6
756 %33 = OpTypeArray %31 %32
757 %34 = OpTypePointer Function %33
758 %39 = OpConstant %6 3
759 %44 = OpTypePointer Function %31
760 %47 = OpConstant %19 1
761 %4 = OpFunction %2 None %3
762 %5 = OpLabel
763 %35 = OpVariable %34 Function
764 OpBranch %10
765 %10 = OpLabel
766 %51 = OpPhi %6 %9 %5 %50 %13
767 OpLoopMerge %12 %13 Unroll
768 OpBranch %14
769 %14 = OpLabel
770 %18 = OpULessThan %17 %51 %16
771 OpBranchConditional %18 %11 %12
772 %11 = OpLabel
773 OpBranch %23
774 %23 = OpLabel
775 %54 = OpPhi %19 %22 %11 %48 %26
776 OpLoopMerge %25 %26 Unroll
777 OpBranch %27
778 %27 = OpLabel
779 %30 = OpSLessThan %17 %54 %29
780 OpBranchConditional %30 %24 %25
781 %24 = OpLabel
782 %37 = OpBitcast %6 %54
783 %40 = OpIMul %6 %51 %39
784 %41 = OpIAdd %6 %37 %40
785 %43 = OpConvertUToF %31 %51
786 %45 = OpAccessChain %44 %35 %41
787 OpStore %45 %43
788 OpBranch %26
789 %26 = OpLabel
790 %48 = OpIAdd %19 %54 %47
791 OpBranch %23
792 %25 = OpLabel
793 OpBranch %13
794 %13 = OpLabel
795 %50 = OpIAdd %6 %51 %47
796 OpBranch %10
797 %12 = OpLabel
798 OpReturn
799 OpFunctionEnd
800 )";
801
802 const std::string output = R"(OpCapability Shader
803 %1 = OpExtInstImport "GLSL.std.450"
804 OpMemoryModel Logical GLSL450
805 OpEntryPoint Fragment %2 "main"
806 OpExecutionMode %2 OriginUpperLeft
807 OpSource GLSL 410
808 OpName %2 "main"
809 OpName %3 "out_array"
810 %4 = OpTypeVoid
811 %5 = OpTypeFunction %4
812 %6 = OpTypeInt 32 0
813 %7 = OpTypePointer Function %6
814 %8 = OpConstant %6 0
815 %9 = OpConstant %6 2
816 %10 = OpTypeBool
817 %11 = OpTypeInt 32 1
818 %12 = OpTypePointer Function %11
819 %13 = OpConstant %11 0
820 %14 = OpConstant %11 3
821 %15 = OpTypeFloat 32
822 %16 = OpConstant %6 6
823 %17 = OpTypeArray %15 %16
824 %18 = OpTypePointer Function %17
825 %19 = OpConstant %6 3
826 %20 = OpTypePointer Function %15
827 %21 = OpConstant %11 1
828 %2 = OpFunction %4 None %5
829 %22 = OpLabel
830 %3 = OpVariable %18 Function
831 OpBranch %23
832 %23 = OpLabel
833 OpBranch %28
834 %28 = OpLabel
835 %29 = OpULessThan %10 %8 %9
836 OpBranch %30
837 %30 = OpLabel
838 OpBranch %31
839 %31 = OpLabel
840 OpBranch %36
841 %36 = OpLabel
842 %37 = OpSLessThan %10 %13 %14
843 OpBranch %38
844 %38 = OpLabel
845 %39 = OpBitcast %6 %13
846 %40 = OpIMul %6 %8 %19
847 %41 = OpIAdd %6 %39 %40
848 %42 = OpConvertUToF %15 %8
849 %43 = OpAccessChain %20 %3 %41
850 OpStore %43 %42
851 OpBranch %34
852 %34 = OpLabel
853 %33 = OpIAdd %11 %13 %21
854 OpBranch %44
855 %44 = OpLabel
856 OpBranch %46
857 %46 = OpLabel
858 %47 = OpSLessThan %10 %33 %14
859 OpBranch %48
860 %48 = OpLabel
861 %49 = OpBitcast %6 %33
862 %50 = OpIMul %6 %8 %19
863 %51 = OpIAdd %6 %49 %50
864 %52 = OpConvertUToF %15 %8
865 %53 = OpAccessChain %20 %3 %51
866 OpStore %53 %52
867 OpBranch %54
868 %54 = OpLabel
869 %55 = OpIAdd %11 %33 %21
870 OpBranch %56
871 %56 = OpLabel
872 OpBranch %58
873 %58 = OpLabel
874 %59 = OpSLessThan %10 %55 %14
875 OpBranch %60
876 %60 = OpLabel
877 %61 = OpBitcast %6 %55
878 %62 = OpIMul %6 %8 %19
879 %63 = OpIAdd %6 %61 %62
880 %64 = OpConvertUToF %15 %8
881 %65 = OpAccessChain %20 %3 %63
882 OpStore %65 %64
883 OpBranch %66
884 %66 = OpLabel
885 %67 = OpIAdd %11 %55 %21
886 OpBranch %35
887 %35 = OpLabel
888 OpBranch %26
889 %26 = OpLabel
890 %25 = OpIAdd %6 %8 %21
891 OpBranch %68
892 %68 = OpLabel
893 OpBranch %70
894 %70 = OpLabel
895 %71 = OpULessThan %10 %25 %9
896 OpBranch %72
897 %72 = OpLabel
898 OpBranch %73
899 %73 = OpLabel
900 OpBranch %74
901 %74 = OpLabel
902 %75 = OpSLessThan %10 %13 %14
903 OpBranch %76
904 %76 = OpLabel
905 %77 = OpBitcast %6 %13
906 %78 = OpIMul %6 %25 %19
907 %79 = OpIAdd %6 %77 %78
908 %80 = OpConvertUToF %15 %25
909 %81 = OpAccessChain %20 %3 %79
910 OpStore %81 %80
911 OpBranch %82
912 %82 = OpLabel
913 %83 = OpIAdd %11 %13 %21
914 OpBranch %84
915 %84 = OpLabel
916 OpBranch %85
917 %85 = OpLabel
918 %86 = OpSLessThan %10 %83 %14
919 OpBranch %87
920 %87 = OpLabel
921 %88 = OpBitcast %6 %83
922 %89 = OpIMul %6 %25 %19
923 %90 = OpIAdd %6 %88 %89
924 %91 = OpConvertUToF %15 %25
925 %92 = OpAccessChain %20 %3 %90
926 OpStore %92 %91
927 OpBranch %93
928 %93 = OpLabel
929 %94 = OpIAdd %11 %83 %21
930 OpBranch %95
931 %95 = OpLabel
932 OpBranch %96
933 %96 = OpLabel
934 %97 = OpSLessThan %10 %94 %14
935 OpBranch %98
936 %98 = OpLabel
937 %99 = OpBitcast %6 %94
938 %100 = OpIMul %6 %25 %19
939 %101 = OpIAdd %6 %99 %100
940 %102 = OpConvertUToF %15 %25
941 %103 = OpAccessChain %20 %3 %101
942 OpStore %103 %102
943 OpBranch %104
944 %104 = OpLabel
945 %105 = OpIAdd %11 %94 %21
946 OpBranch %106
947 %106 = OpLabel
948 OpBranch %107
949 %107 = OpLabel
950 %108 = OpIAdd %6 %25 %21
951 OpBranch %27
952 %27 = OpLabel
953 OpReturn
954 OpFunctionEnd
955 )";
956
957 std::unique_ptr<IRContext> context =
958 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
959 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
960 Module* module = context->module();
961 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
962 << text << std::endl;
963 LoopUnroller loop_unroller;
964 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
965 SinglePassRunAndCheck<LoopUnroller>(text, output, false);
966 }
967
968 /*
969 Generated from the following GLSL
970 #version 410 core
971 void main() {
972 float out_array[2];
973 for (int i = -3; i < -1; i++) {
974 out_array[3 + i] = i;
975 }
976 }
977 */
TEST_F(PassClassTest,NegativeConditionAndInit)978 TEST_F(PassClassTest, NegativeConditionAndInit) {
979 // With LocalMultiStoreElimPass
980 const std::string text = R"(
981 OpCapability Shader
982 %1 = OpExtInstImport "GLSL.std.450"
983 OpMemoryModel Logical GLSL450
984 OpEntryPoint Fragment %4 "main"
985 OpExecutionMode %4 OriginUpperLeft
986 OpSource GLSL 410
987 OpName %4 "main"
988 OpName %23 "out_array"
989 %2 = OpTypeVoid
990 %3 = OpTypeFunction %2
991 %6 = OpTypeInt 32 1
992 %7 = OpTypePointer Function %6
993 %9 = OpConstant %6 -3
994 %16 = OpConstant %6 -1
995 %17 = OpTypeBool
996 %19 = OpTypeInt 32 0
997 %20 = OpConstant %19 2
998 %21 = OpTypeArray %6 %20
999 %22 = OpTypePointer Function %21
1000 %25 = OpConstant %6 3
1001 %30 = OpConstant %6 1
1002 %4 = OpFunction %2 None %3
1003 %5 = OpLabel
1004 %23 = OpVariable %22 Function
1005 OpBranch %10
1006 %10 = OpLabel
1007 %32 = OpPhi %6 %9 %5 %31 %13
1008 OpLoopMerge %12 %13 Unroll
1009 OpBranch %14
1010 %14 = OpLabel
1011 %18 = OpSLessThan %17 %32 %16
1012 OpBranchConditional %18 %11 %12
1013 %11 = OpLabel
1014 %26 = OpIAdd %6 %32 %25
1015 %28 = OpAccessChain %7 %23 %26
1016 OpStore %28 %32
1017 OpBranch %13
1018 %13 = OpLabel
1019 %31 = OpIAdd %6 %32 %30
1020 OpBranch %10
1021 %12 = OpLabel
1022 OpReturn
1023 OpFunctionEnd
1024 )";
1025
1026 const std::string expected = R"(OpCapability Shader
1027 %1 = OpExtInstImport "GLSL.std.450"
1028 OpMemoryModel Logical GLSL450
1029 OpEntryPoint Fragment %2 "main"
1030 OpExecutionMode %2 OriginUpperLeft
1031 OpSource GLSL 410
1032 OpName %2 "main"
1033 OpName %3 "out_array"
1034 %4 = OpTypeVoid
1035 %5 = OpTypeFunction %4
1036 %6 = OpTypeInt 32 1
1037 %7 = OpTypePointer Function %6
1038 %8 = OpConstant %6 -3
1039 %9 = OpConstant %6 -1
1040 %10 = OpTypeBool
1041 %11 = OpTypeInt 32 0
1042 %12 = OpConstant %11 2
1043 %13 = OpTypeArray %6 %12
1044 %14 = OpTypePointer Function %13
1045 %15 = OpConstant %6 3
1046 %16 = OpConstant %6 1
1047 %2 = OpFunction %4 None %5
1048 %17 = OpLabel
1049 %3 = OpVariable %14 Function
1050 OpBranch %18
1051 %18 = OpLabel
1052 OpBranch %23
1053 %23 = OpLabel
1054 %24 = OpSLessThan %10 %8 %9
1055 OpBranch %25
1056 %25 = OpLabel
1057 %26 = OpIAdd %6 %8 %15
1058 %27 = OpAccessChain %7 %3 %26
1059 OpStore %27 %8
1060 OpBranch %21
1061 %21 = OpLabel
1062 %20 = OpIAdd %6 %8 %16
1063 OpBranch %28
1064 %28 = OpLabel
1065 OpBranch %30
1066 %30 = OpLabel
1067 %31 = OpSLessThan %10 %20 %9
1068 OpBranch %32
1069 %32 = OpLabel
1070 %33 = OpIAdd %6 %20 %15
1071 %34 = OpAccessChain %7 %3 %33
1072 OpStore %34 %20
1073 OpBranch %35
1074 %35 = OpLabel
1075 %36 = OpIAdd %6 %20 %16
1076 OpBranch %22
1077 %22 = OpLabel
1078 OpReturn
1079 OpFunctionEnd
1080 )";
1081
1082 std::unique_ptr<IRContext> context =
1083 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1084 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1085 Module* module = context->module();
1086 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
1087 << text << std::endl;
1088
1089 LoopUnroller loop_unroller;
1090 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
1091 // SinglePassRunAndCheck<LoopUnroller>(text, expected, false);
1092
1093 Function* f = spvtest::GetFunction(module, 4);
1094
1095 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
1096 EXPECT_EQ(loop_descriptor.NumLoops(), 1u);
1097
1098 Loop& loop = loop_descriptor.GetLoopByIndex(0);
1099
1100 EXPECT_TRUE(loop.HasUnrollLoopControl());
1101
1102 BasicBlock* condition = loop.FindConditionBlock();
1103 EXPECT_EQ(condition->id(), 14u);
1104
1105 Instruction* induction = loop.FindConditionVariable(condition);
1106 EXPECT_EQ(induction->result_id(), 32u);
1107
1108 LoopUtils loop_utils{context.get(), &loop};
1109 EXPECT_TRUE(loop_utils.CanPerformUnroll());
1110
1111 size_t iterations = 0;
1112 EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(),
1113 &iterations));
1114 EXPECT_EQ(iterations, 2u);
1115 SinglePassRunAndCheck<LoopUnroller>(text, expected, false);
1116 }
1117
1118 /*
1119 Generated from the following GLSL
1120 #version 410 core
1121 void main() {
1122 float out_array[9];
1123 for (int i = -10; i < -1; i++) {
1124 out_array[i] = i;
1125 }
1126 }
1127 */
TEST_F(PassClassTest,NegativeConditionAndInitResidualUnroll)1128 TEST_F(PassClassTest, NegativeConditionAndInitResidualUnroll) {
1129 // With LocalMultiStoreElimPass
1130 const std::string text = R"(
1131 OpCapability Shader
1132 %1 = OpExtInstImport "GLSL.std.450"
1133 OpMemoryModel Logical GLSL450
1134 OpEntryPoint Fragment %4 "main"
1135 OpExecutionMode %4 OriginUpperLeft
1136 OpSource GLSL 410
1137 OpName %4 "main"
1138 OpName %23 "out_array"
1139 %2 = OpTypeVoid
1140 %3 = OpTypeFunction %2
1141 %6 = OpTypeInt 32 1
1142 %7 = OpTypePointer Function %6
1143 %9 = OpConstant %6 -10
1144 %16 = OpConstant %6 -1
1145 %17 = OpTypeBool
1146 %19 = OpTypeInt 32 0
1147 %20 = OpConstant %19 9
1148 %21 = OpTypeArray %6 %20
1149 %22 = OpTypePointer Function %21
1150 %25 = OpConstant %6 10
1151 %30 = OpConstant %6 1
1152 %4 = OpFunction %2 None %3
1153 %5 = OpLabel
1154 %23 = OpVariable %22 Function
1155 OpBranch %10
1156 %10 = OpLabel
1157 %32 = OpPhi %6 %9 %5 %31 %13
1158 OpLoopMerge %12 %13 Unroll
1159 OpBranch %14
1160 %14 = OpLabel
1161 %18 = OpSLessThan %17 %32 %16
1162 OpBranchConditional %18 %11 %12
1163 %11 = OpLabel
1164 %26 = OpIAdd %6 %32 %25
1165 %28 = OpAccessChain %7 %23 %26
1166 OpStore %28 %32
1167 OpBranch %13
1168 %13 = OpLabel
1169 %31 = OpIAdd %6 %32 %30
1170 OpBranch %10
1171 %12 = OpLabel
1172 OpReturn
1173 OpFunctionEnd
1174 )";
1175
1176 const std::string expected = R"(OpCapability Shader
1177 %1 = OpExtInstImport "GLSL.std.450"
1178 OpMemoryModel Logical GLSL450
1179 OpEntryPoint Fragment %2 "main"
1180 OpExecutionMode %2 OriginUpperLeft
1181 OpSource GLSL 410
1182 OpName %2 "main"
1183 OpName %3 "out_array"
1184 %4 = OpTypeVoid
1185 %5 = OpTypeFunction %4
1186 %6 = OpTypeInt 32 1
1187 %7 = OpTypePointer Function %6
1188 %8 = OpConstant %6 -10
1189 %9 = OpConstant %6 -1
1190 %10 = OpTypeBool
1191 %11 = OpTypeInt 32 0
1192 %12 = OpConstant %11 9
1193 %13 = OpTypeArray %6 %12
1194 %14 = OpTypePointer Function %13
1195 %15 = OpConstant %6 10
1196 %16 = OpConstant %6 1
1197 %48 = OpConstant %6 -9
1198 %2 = OpFunction %4 None %5
1199 %17 = OpLabel
1200 %3 = OpVariable %14 Function
1201 OpBranch %18
1202 %18 = OpLabel
1203 %19 = OpPhi %6 %8 %17 %20 %21
1204 OpLoopMerge %28 %21 Unroll
1205 OpBranch %23
1206 %23 = OpLabel
1207 %24 = OpSLessThan %10 %19 %48
1208 OpBranchConditional %24 %25 %28
1209 %25 = OpLabel
1210 %26 = OpIAdd %6 %19 %15
1211 %27 = OpAccessChain %7 %3 %26
1212 OpStore %27 %19
1213 OpBranch %21
1214 %21 = OpLabel
1215 %20 = OpIAdd %6 %19 %16
1216 OpBranch %18
1217 %28 = OpLabel
1218 OpBranch %29
1219 %29 = OpLabel
1220 %30 = OpPhi %6 %19 %28 %47 %46
1221 OpLoopMerge %38 %46 DontUnroll
1222 OpBranch %31
1223 %31 = OpLabel
1224 %32 = OpSLessThan %10 %30 %9
1225 OpBranchConditional %32 %33 %38
1226 %33 = OpLabel
1227 %34 = OpIAdd %6 %30 %15
1228 %35 = OpAccessChain %7 %3 %34
1229 OpStore %35 %30
1230 OpBranch %36
1231 %36 = OpLabel
1232 %37 = OpIAdd %6 %30 %16
1233 OpBranch %39
1234 %39 = OpLabel
1235 OpBranch %41
1236 %41 = OpLabel
1237 %42 = OpSLessThan %10 %37 %9
1238 OpBranch %43
1239 %43 = OpLabel
1240 %44 = OpIAdd %6 %37 %15
1241 %45 = OpAccessChain %7 %3 %44
1242 OpStore %45 %37
1243 OpBranch %46
1244 %46 = OpLabel
1245 %47 = OpIAdd %6 %37 %16
1246 OpBranch %29
1247 %38 = OpLabel
1248 OpReturn
1249 %22 = OpLabel
1250 OpReturn
1251 OpFunctionEnd
1252 )";
1253
1254 std::unique_ptr<IRContext> context =
1255 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1256 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1257 Module* module = context->module();
1258 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
1259 << text << std::endl;
1260
1261 LoopUnroller loop_unroller;
1262 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
1263
1264 Function* f = spvtest::GetFunction(module, 4);
1265
1266 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
1267 EXPECT_EQ(loop_descriptor.NumLoops(), 1u);
1268
1269 Loop& loop = loop_descriptor.GetLoopByIndex(0);
1270
1271 EXPECT_TRUE(loop.HasUnrollLoopControl());
1272
1273 BasicBlock* condition = loop.FindConditionBlock();
1274 EXPECT_EQ(condition->id(), 14u);
1275
1276 Instruction* induction = loop.FindConditionVariable(condition);
1277 EXPECT_EQ(induction->result_id(), 32u);
1278
1279 LoopUtils loop_utils{context.get(), &loop};
1280 EXPECT_TRUE(loop_utils.CanPerformUnroll());
1281
1282 size_t iterations = 0;
1283 EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(),
1284 &iterations));
1285 EXPECT_EQ(iterations, 9u);
1286 SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(text, expected, false);
1287 }
1288
1289 /*
1290 Generated from the following GLSL
1291 #version 410 core
1292 void main() {
1293 float out_array[10];
1294 for (uint i = 0; i < 2; i++) {
1295 for (int x = 0; x < 5; ++x) {
1296 out_array[x + i*5] = i;
1297 }
1298 }
1299 }
1300 */
TEST_F(PassClassTest,UnrollNestedLoopsValidateDescriptor)1301 TEST_F(PassClassTest, UnrollNestedLoopsValidateDescriptor) {
1302 // With LocalMultiStoreElimPass
1303 const std::string text = R"(
1304 OpCapability Shader
1305 %1 = OpExtInstImport "GLSL.std.450"
1306 OpMemoryModel Logical GLSL450
1307 OpEntryPoint Fragment %4 "main"
1308 OpExecutionMode %4 OriginUpperLeft
1309 OpSource GLSL 410
1310 OpName %4 "main"
1311 OpName %35 "out_array"
1312 %2 = OpTypeVoid
1313 %3 = OpTypeFunction %2
1314 %6 = OpTypeInt 32 0
1315 %7 = OpTypePointer Function %6
1316 %9 = OpConstant %6 0
1317 %16 = OpConstant %6 2
1318 %17 = OpTypeBool
1319 %19 = OpTypeInt 32 1
1320 %20 = OpTypePointer Function %19
1321 %22 = OpConstant %19 0
1322 %29 = OpConstant %19 5
1323 %31 = OpTypeFloat 32
1324 %32 = OpConstant %6 10
1325 %33 = OpTypeArray %31 %32
1326 %34 = OpTypePointer Function %33
1327 %39 = OpConstant %6 5
1328 %44 = OpTypePointer Function %31
1329 %47 = OpConstant %19 1
1330 %4 = OpFunction %2 None %3
1331 %5 = OpLabel
1332 %35 = OpVariable %34 Function
1333 OpBranch %10
1334 %10 = OpLabel
1335 %51 = OpPhi %6 %9 %5 %50 %13
1336 OpLoopMerge %12 %13 Unroll
1337 OpBranch %14
1338 %14 = OpLabel
1339 %18 = OpULessThan %17 %51 %16
1340 OpBranchConditional %18 %11 %12
1341 %11 = OpLabel
1342 OpBranch %23
1343 %23 = OpLabel
1344 %54 = OpPhi %19 %22 %11 %48 %26
1345 OpLoopMerge %25 %26 Unroll
1346 OpBranch %27
1347 %27 = OpLabel
1348 %30 = OpSLessThan %17 %54 %29
1349 OpBranchConditional %30 %24 %25
1350 %24 = OpLabel
1351 %37 = OpBitcast %6 %54
1352 %40 = OpIMul %6 %51 %39
1353 %41 = OpIAdd %6 %37 %40
1354 %43 = OpConvertUToF %31 %51
1355 %45 = OpAccessChain %44 %35 %41
1356 OpStore %45 %43
1357 OpBranch %26
1358 %26 = OpLabel
1359 %48 = OpIAdd %19 %54 %47
1360 OpBranch %23
1361 %25 = OpLabel
1362 OpBranch %13
1363 %13 = OpLabel
1364 %50 = OpIAdd %6 %51 %47
1365 OpBranch %10
1366 %12 = OpLabel
1367 OpReturn
1368 OpFunctionEnd
1369 )";
1370
1371 { // Test fully unroll
1372 std::unique_ptr<IRContext> context =
1373 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1374 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1375 Module* module = context->module();
1376 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
1377 << text << std::endl;
1378 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
1379
1380 Function* f = spvtest::GetFunction(module, 4);
1381 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
1382 EXPECT_EQ(loop_descriptor.NumLoops(), 2u);
1383
1384 Loop& outer_loop = loop_descriptor.GetLoopByIndex(1);
1385
1386 EXPECT_TRUE(outer_loop.HasUnrollLoopControl());
1387
1388 Loop& inner_loop = loop_descriptor.GetLoopByIndex(0);
1389
1390 EXPECT_TRUE(inner_loop.HasUnrollLoopControl());
1391
1392 EXPECT_EQ(outer_loop.GetBlocks().size(), 9u);
1393
1394 EXPECT_EQ(inner_loop.GetBlocks().size(), 4u);
1395 EXPECT_EQ(outer_loop.NumImmediateChildren(), 1u);
1396 EXPECT_EQ(inner_loop.NumImmediateChildren(), 0u);
1397
1398 {
1399 LoopUtils loop_utils{context.get(), &inner_loop};
1400 loop_utils.FullyUnroll();
1401 loop_utils.Finalize();
1402 }
1403
1404 EXPECT_EQ(loop_descriptor.NumLoops(), 1u);
1405 EXPECT_EQ(outer_loop.GetBlocks().size(), 25u);
1406 EXPECT_EQ(outer_loop.NumImmediateChildren(), 0u);
1407 {
1408 LoopUtils loop_utils{context.get(), &outer_loop};
1409 loop_utils.FullyUnroll();
1410 loop_utils.Finalize();
1411 }
1412 EXPECT_EQ(loop_descriptor.NumLoops(), 0u);
1413 }
1414
1415 { // Test partially unroll
1416 std::unique_ptr<IRContext> context =
1417 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1418 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1419 Module* module = context->module();
1420 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
1421 << text << std::endl;
1422 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
1423
1424 Function* f = spvtest::GetFunction(module, 4);
1425 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
1426 EXPECT_EQ(loop_descriptor.NumLoops(), 2u);
1427
1428 Loop& outer_loop = loop_descriptor.GetLoopByIndex(1);
1429
1430 EXPECT_TRUE(outer_loop.HasUnrollLoopControl());
1431
1432 Loop& inner_loop = loop_descriptor.GetLoopByIndex(0);
1433
1434 EXPECT_TRUE(inner_loop.HasUnrollLoopControl());
1435
1436 EXPECT_EQ(outer_loop.GetBlocks().size(), 9u);
1437
1438 EXPECT_EQ(inner_loop.GetBlocks().size(), 4u);
1439
1440 EXPECT_EQ(outer_loop.NumImmediateChildren(), 1u);
1441 EXPECT_EQ(inner_loop.NumImmediateChildren(), 0u);
1442
1443 LoopUtils loop_utils{context.get(), &inner_loop};
1444 loop_utils.PartiallyUnroll(2);
1445 loop_utils.Finalize();
1446
1447 // The number of loops should actually grow.
1448 EXPECT_EQ(loop_descriptor.NumLoops(), 3u);
1449 EXPECT_EQ(outer_loop.GetBlocks().size(), 18u);
1450 EXPECT_EQ(outer_loop.NumImmediateChildren(), 2u);
1451 }
1452 }
1453
1454 /*
1455 Generated from the following GLSL
1456 #version 410 core
1457 void main() {
1458 float out_array[3];
1459 for (int i = 3; i > 0; --i) {
1460 out_array[i] = i;
1461 }
1462 }
1463 */
TEST_F(PassClassTest,FullyUnrollNegativeStepLoopTest)1464 TEST_F(PassClassTest, FullyUnrollNegativeStepLoopTest) {
1465 // With LocalMultiStoreElimPass
1466 const std::string text = R"(
1467 OpCapability Shader
1468 %1 = OpExtInstImport "GLSL.std.450"
1469 OpMemoryModel Logical GLSL450
1470 OpEntryPoint Fragment %4 "main"
1471 OpExecutionMode %4 OriginUpperLeft
1472 OpSource GLSL 410
1473 OpName %4 "main"
1474 OpName %24 "out_array"
1475 %2 = OpTypeVoid
1476 %3 = OpTypeFunction %2
1477 %6 = OpTypeInt 32 1
1478 %7 = OpTypePointer Function %6
1479 %9 = OpConstant %6 3
1480 %16 = OpConstant %6 0
1481 %17 = OpTypeBool
1482 %19 = OpTypeFloat 32
1483 %20 = OpTypeInt 32 0
1484 %21 = OpConstant %20 3
1485 %22 = OpTypeArray %19 %21
1486 %23 = OpTypePointer Function %22
1487 %28 = OpTypePointer Function %19
1488 %31 = OpConstant %6 1
1489 %4 = OpFunction %2 None %3
1490 %5 = OpLabel
1491 %24 = OpVariable %23 Function
1492 OpBranch %10
1493 %10 = OpLabel
1494 %33 = OpPhi %6 %9 %5 %32 %13
1495 OpLoopMerge %12 %13 Unroll
1496 OpBranch %14
1497 %14 = OpLabel
1498 %18 = OpSGreaterThan %17 %33 %16
1499 OpBranchConditional %18 %11 %12
1500 %11 = OpLabel
1501 %27 = OpConvertSToF %19 %33
1502 %29 = OpAccessChain %28 %24 %33
1503 OpStore %29 %27
1504 OpBranch %13
1505 %13 = OpLabel
1506 %32 = OpISub %6 %33 %31
1507 OpBranch %10
1508 %12 = OpLabel
1509 OpReturn
1510 OpFunctionEnd
1511 )";
1512
1513 const std::string output = R"(OpCapability Shader
1514 %1 = OpExtInstImport "GLSL.std.450"
1515 OpMemoryModel Logical GLSL450
1516 OpEntryPoint Fragment %2 "main"
1517 OpExecutionMode %2 OriginUpperLeft
1518 OpSource GLSL 410
1519 OpName %2 "main"
1520 OpName %3 "out_array"
1521 %4 = OpTypeVoid
1522 %5 = OpTypeFunction %4
1523 %6 = OpTypeInt 32 1
1524 %7 = OpTypePointer Function %6
1525 %8 = OpConstant %6 3
1526 %9 = OpConstant %6 0
1527 %10 = OpTypeBool
1528 %11 = OpTypeFloat 32
1529 %12 = OpTypeInt 32 0
1530 %13 = OpConstant %12 3
1531 %14 = OpTypeArray %11 %13
1532 %15 = OpTypePointer Function %14
1533 %16 = OpTypePointer Function %11
1534 %17 = OpConstant %6 1
1535 %2 = OpFunction %4 None %5
1536 %18 = OpLabel
1537 %3 = OpVariable %15 Function
1538 OpBranch %19
1539 %19 = OpLabel
1540 OpBranch %24
1541 %24 = OpLabel
1542 %25 = OpSGreaterThan %10 %8 %9
1543 OpBranch %26
1544 %26 = OpLabel
1545 %27 = OpConvertSToF %11 %8
1546 %28 = OpAccessChain %16 %3 %8
1547 OpStore %28 %27
1548 OpBranch %22
1549 %22 = OpLabel
1550 %21 = OpISub %6 %8 %17
1551 OpBranch %29
1552 %29 = OpLabel
1553 OpBranch %31
1554 %31 = OpLabel
1555 %32 = OpSGreaterThan %10 %21 %9
1556 OpBranch %33
1557 %33 = OpLabel
1558 %34 = OpConvertSToF %11 %21
1559 %35 = OpAccessChain %16 %3 %21
1560 OpStore %35 %34
1561 OpBranch %36
1562 %36 = OpLabel
1563 %37 = OpISub %6 %21 %17
1564 OpBranch %38
1565 %38 = OpLabel
1566 OpBranch %40
1567 %40 = OpLabel
1568 %41 = OpSGreaterThan %10 %37 %9
1569 OpBranch %42
1570 %42 = OpLabel
1571 %43 = OpConvertSToF %11 %37
1572 %44 = OpAccessChain %16 %3 %37
1573 OpStore %44 %43
1574 OpBranch %45
1575 %45 = OpLabel
1576 %46 = OpISub %6 %37 %17
1577 OpBranch %23
1578 %23 = OpLabel
1579 OpReturn
1580 OpFunctionEnd
1581 )";
1582
1583 std::unique_ptr<IRContext> context =
1584 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1585 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1586 Module* module = context->module();
1587 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
1588 << text << std::endl;
1589
1590 LoopUnroller loop_unroller;
1591 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
1592 SinglePassRunAndCheck<LoopUnroller>(text, output, false);
1593 }
1594
1595 /*
1596 Generated from the following GLSL
1597 #version 410 core
1598 void main() {
1599 float out_array[3];
1600 for (int i = 9; i > 0; i-=3) {
1601 out_array[i] = i;
1602 }
1603 }
1604 */
TEST_F(PassClassTest,FullyUnrollNegativeNonOneStepLoop)1605 TEST_F(PassClassTest, FullyUnrollNegativeNonOneStepLoop) {
1606 // With LocalMultiStoreElimPass
1607 const std::string text = R"(
1608 OpCapability Shader
1609 %1 = OpExtInstImport "GLSL.std.450"
1610 OpMemoryModel Logical GLSL450
1611 OpEntryPoint Fragment %4 "main"
1612 OpExecutionMode %4 OriginUpperLeft
1613 OpSource GLSL 410
1614 OpName %4 "main"
1615 OpName %24 "out_array"
1616 %2 = OpTypeVoid
1617 %3 = OpTypeFunction %2
1618 %6 = OpTypeInt 32 1
1619 %7 = OpTypePointer Function %6
1620 %9 = OpConstant %6 9
1621 %16 = OpConstant %6 0
1622 %17 = OpTypeBool
1623 %19 = OpTypeFloat 32
1624 %20 = OpTypeInt 32 0
1625 %21 = OpConstant %20 3
1626 %22 = OpTypeArray %19 %21
1627 %23 = OpTypePointer Function %22
1628 %28 = OpTypePointer Function %19
1629 %30 = OpConstant %6 3
1630 %4 = OpFunction %2 None %3
1631 %5 = OpLabel
1632 %24 = OpVariable %23 Function
1633 OpBranch %10
1634 %10 = OpLabel
1635 %33 = OpPhi %6 %9 %5 %32 %13
1636 OpLoopMerge %12 %13 Unroll
1637 OpBranch %14
1638 %14 = OpLabel
1639 %18 = OpSGreaterThan %17 %33 %16
1640 OpBranchConditional %18 %11 %12
1641 %11 = OpLabel
1642 %27 = OpConvertSToF %19 %33
1643 %29 = OpAccessChain %28 %24 %33
1644 OpStore %29 %27
1645 OpBranch %13
1646 %13 = OpLabel
1647 %32 = OpISub %6 %33 %30
1648 OpBranch %10
1649 %12 = OpLabel
1650 OpReturn
1651 OpFunctionEnd
1652 )";
1653
1654 const std::string output = R"(OpCapability Shader
1655 %1 = OpExtInstImport "GLSL.std.450"
1656 OpMemoryModel Logical GLSL450
1657 OpEntryPoint Fragment %2 "main"
1658 OpExecutionMode %2 OriginUpperLeft
1659 OpSource GLSL 410
1660 OpName %2 "main"
1661 OpName %3 "out_array"
1662 %4 = OpTypeVoid
1663 %5 = OpTypeFunction %4
1664 %6 = OpTypeInt 32 1
1665 %7 = OpTypePointer Function %6
1666 %8 = OpConstant %6 9
1667 %9 = OpConstant %6 0
1668 %10 = OpTypeBool
1669 %11 = OpTypeFloat 32
1670 %12 = OpTypeInt 32 0
1671 %13 = OpConstant %12 3
1672 %14 = OpTypeArray %11 %13
1673 %15 = OpTypePointer Function %14
1674 %16 = OpTypePointer Function %11
1675 %17 = OpConstant %6 3
1676 %2 = OpFunction %4 None %5
1677 %18 = OpLabel
1678 %3 = OpVariable %15 Function
1679 OpBranch %19
1680 %19 = OpLabel
1681 OpBranch %24
1682 %24 = OpLabel
1683 %25 = OpSGreaterThan %10 %8 %9
1684 OpBranch %26
1685 %26 = OpLabel
1686 %27 = OpConvertSToF %11 %8
1687 %28 = OpAccessChain %16 %3 %8
1688 OpStore %28 %27
1689 OpBranch %22
1690 %22 = OpLabel
1691 %21 = OpISub %6 %8 %17
1692 OpBranch %29
1693 %29 = OpLabel
1694 OpBranch %31
1695 %31 = OpLabel
1696 %32 = OpSGreaterThan %10 %21 %9
1697 OpBranch %33
1698 %33 = OpLabel
1699 %34 = OpConvertSToF %11 %21
1700 %35 = OpAccessChain %16 %3 %21
1701 OpStore %35 %34
1702 OpBranch %36
1703 %36 = OpLabel
1704 %37 = OpISub %6 %21 %17
1705 OpBranch %38
1706 %38 = OpLabel
1707 OpBranch %40
1708 %40 = OpLabel
1709 %41 = OpSGreaterThan %10 %37 %9
1710 OpBranch %42
1711 %42 = OpLabel
1712 %43 = OpConvertSToF %11 %37
1713 %44 = OpAccessChain %16 %3 %37
1714 OpStore %44 %43
1715 OpBranch %45
1716 %45 = OpLabel
1717 %46 = OpISub %6 %37 %17
1718 OpBranch %23
1719 %23 = OpLabel
1720 OpReturn
1721 OpFunctionEnd
1722 )";
1723
1724 std::unique_ptr<IRContext> context =
1725 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1726 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1727 Module* module = context->module();
1728 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
1729 << text << std::endl;
1730
1731 LoopUnroller loop_unroller;
1732 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
1733 SinglePassRunAndCheck<LoopUnroller>(text, output, false);
1734 }
1735
1736 /*
1737 Generated from the following GLSL
1738 #version 410 core
1739 void main() {
1740 float out_array[3];
1741 for (int i = 0; i < 7; i+=3) {
1742 out_array[i] = i;
1743 }
1744 }
1745 */
TEST_F(PassClassTest,FullyUnrollNonDivisibleStepLoop)1746 TEST_F(PassClassTest, FullyUnrollNonDivisibleStepLoop) {
1747 // With LocalMultiStoreElimPass
1748 const std::string text = R"(OpCapability Shader
1749 %1 = OpExtInstImport "GLSL.std.450"
1750 OpMemoryModel Logical GLSL450
1751 OpEntryPoint Fragment %4 "main"
1752 OpExecutionMode %4 OriginUpperLeft
1753 OpSource GLSL 410
1754 OpName %4 "main"
1755 OpName %24 "out_array"
1756 %2 = OpTypeVoid
1757 %3 = OpTypeFunction %2
1758 %6 = OpTypeInt 32 1
1759 %7 = OpTypePointer Function %6
1760 %9 = OpConstant %6 0
1761 %16 = OpConstant %6 7
1762 %17 = OpTypeBool
1763 %19 = OpTypeFloat 32
1764 %20 = OpTypeInt 32 0
1765 %21 = OpConstant %20 3
1766 %22 = OpTypeArray %19 %21
1767 %23 = OpTypePointer Function %22
1768 %28 = OpTypePointer Function %19
1769 %30 = OpConstant %6 3
1770 %4 = OpFunction %2 None %3
1771 %5 = OpLabel
1772 %24 = OpVariable %23 Function
1773 OpBranch %10
1774 %10 = OpLabel
1775 %33 = OpPhi %6 %9 %5 %32 %13
1776 OpLoopMerge %12 %13 Unroll
1777 OpBranch %14
1778 %14 = OpLabel
1779 %18 = OpSLessThan %17 %33 %16
1780 OpBranchConditional %18 %11 %12
1781 %11 = OpLabel
1782 %27 = OpConvertSToF %19 %33
1783 %29 = OpAccessChain %28 %24 %33
1784 OpStore %29 %27
1785 OpBranch %13
1786 %13 = OpLabel
1787 %32 = OpIAdd %6 %33 %30
1788 OpBranch %10
1789 %12 = OpLabel
1790 OpReturn
1791 OpFunctionEnd
1792 )";
1793
1794 const std::string output = R"(OpCapability Shader
1795 %1 = OpExtInstImport "GLSL.std.450"
1796 OpMemoryModel Logical GLSL450
1797 OpEntryPoint Fragment %2 "main"
1798 OpExecutionMode %2 OriginUpperLeft
1799 OpSource GLSL 410
1800 OpName %2 "main"
1801 OpName %3 "out_array"
1802 %4 = OpTypeVoid
1803 %5 = OpTypeFunction %4
1804 %6 = OpTypeInt 32 1
1805 %7 = OpTypePointer Function %6
1806 %8 = OpConstant %6 0
1807 %9 = OpConstant %6 7
1808 %10 = OpTypeBool
1809 %11 = OpTypeFloat 32
1810 %12 = OpTypeInt 32 0
1811 %13 = OpConstant %12 3
1812 %14 = OpTypeArray %11 %13
1813 %15 = OpTypePointer Function %14
1814 %16 = OpTypePointer Function %11
1815 %17 = OpConstant %6 3
1816 %2 = OpFunction %4 None %5
1817 %18 = OpLabel
1818 %3 = OpVariable %15 Function
1819 OpBranch %19
1820 %19 = OpLabel
1821 OpBranch %24
1822 %24 = OpLabel
1823 %25 = OpSLessThan %10 %8 %9
1824 OpBranch %26
1825 %26 = OpLabel
1826 %27 = OpConvertSToF %11 %8
1827 %28 = OpAccessChain %16 %3 %8
1828 OpStore %28 %27
1829 OpBranch %22
1830 %22 = OpLabel
1831 %21 = OpIAdd %6 %8 %17
1832 OpBranch %29
1833 %29 = OpLabel
1834 OpBranch %31
1835 %31 = OpLabel
1836 %32 = OpSLessThan %10 %21 %9
1837 OpBranch %33
1838 %33 = OpLabel
1839 %34 = OpConvertSToF %11 %21
1840 %35 = OpAccessChain %16 %3 %21
1841 OpStore %35 %34
1842 OpBranch %36
1843 %36 = OpLabel
1844 %37 = OpIAdd %6 %21 %17
1845 OpBranch %38
1846 %38 = OpLabel
1847 OpBranch %40
1848 %40 = OpLabel
1849 %41 = OpSLessThan %10 %37 %9
1850 OpBranch %42
1851 %42 = OpLabel
1852 %43 = OpConvertSToF %11 %37
1853 %44 = OpAccessChain %16 %3 %37
1854 OpStore %44 %43
1855 OpBranch %45
1856 %45 = OpLabel
1857 %46 = OpIAdd %6 %37 %17
1858 OpBranch %23
1859 %23 = OpLabel
1860 OpReturn
1861 OpFunctionEnd
1862 )";
1863
1864 std::unique_ptr<IRContext> context =
1865 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1866 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1867 Module* module = context->module();
1868 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
1869 << text << std::endl;
1870
1871 LoopUnroller loop_unroller;
1872 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
1873 SinglePassRunAndCheck<LoopUnroller>(text, output, false);
1874 }
1875
1876 /*
1877 Generated from the following GLSL
1878 #version 410 core
1879 void main() {
1880 float out_array[4];
1881 for (int i = 11; i > 0; i-=3) {
1882 out_array[i] = i;
1883 }
1884 }
1885 */
TEST_F(PassClassTest,FullyUnrollNegativeNonDivisibleStepLoop)1886 TEST_F(PassClassTest, FullyUnrollNegativeNonDivisibleStepLoop) {
1887 // With LocalMultiStoreElimPass
1888 const std::string text = R"(OpCapability Shader
1889 %1 = OpExtInstImport "GLSL.std.450"
1890 OpMemoryModel Logical GLSL450
1891 OpEntryPoint Fragment %4 "main"
1892 OpExecutionMode %4 OriginUpperLeft
1893 OpSource GLSL 410
1894 OpName %4 "main"
1895 OpName %24 "out_array"
1896 %2 = OpTypeVoid
1897 %3 = OpTypeFunction %2
1898 %6 = OpTypeInt 32 1
1899 %7 = OpTypePointer Function %6
1900 %9 = OpConstant %6 11
1901 %16 = OpConstant %6 0
1902 %17 = OpTypeBool
1903 %19 = OpTypeFloat 32
1904 %20 = OpTypeInt 32 0
1905 %21 = OpConstant %20 4
1906 %22 = OpTypeArray %19 %21
1907 %23 = OpTypePointer Function %22
1908 %28 = OpTypePointer Function %19
1909 %30 = OpConstant %6 3
1910 %4 = OpFunction %2 None %3
1911 %5 = OpLabel
1912 %24 = OpVariable %23 Function
1913 OpBranch %10
1914 %10 = OpLabel
1915 %33 = OpPhi %6 %9 %5 %32 %13
1916 OpLoopMerge %12 %13 Unroll
1917 OpBranch %14
1918 %14 = OpLabel
1919 %18 = OpSGreaterThan %17 %33 %16
1920 OpBranchConditional %18 %11 %12
1921 %11 = OpLabel
1922 %27 = OpConvertSToF %19 %33
1923 %29 = OpAccessChain %28 %24 %33
1924 OpStore %29 %27
1925 OpBranch %13
1926 %13 = OpLabel
1927 %32 = OpISub %6 %33 %30
1928 OpBranch %10
1929 %12 = OpLabel
1930 OpReturn
1931 OpFunctionEnd
1932 )";
1933
1934 const std::string output = R"(OpCapability Shader
1935 %1 = OpExtInstImport "GLSL.std.450"
1936 OpMemoryModel Logical GLSL450
1937 OpEntryPoint Fragment %2 "main"
1938 OpExecutionMode %2 OriginUpperLeft
1939 OpSource GLSL 410
1940 OpName %2 "main"
1941 OpName %3 "out_array"
1942 %4 = OpTypeVoid
1943 %5 = OpTypeFunction %4
1944 %6 = OpTypeInt 32 1
1945 %7 = OpTypePointer Function %6
1946 %8 = OpConstant %6 11
1947 %9 = OpConstant %6 0
1948 %10 = OpTypeBool
1949 %11 = OpTypeFloat 32
1950 %12 = OpTypeInt 32 0
1951 %13 = OpConstant %12 4
1952 %14 = OpTypeArray %11 %13
1953 %15 = OpTypePointer Function %14
1954 %16 = OpTypePointer Function %11
1955 %17 = OpConstant %6 3
1956 %2 = OpFunction %4 None %5
1957 %18 = OpLabel
1958 %3 = OpVariable %15 Function
1959 OpBranch %19
1960 %19 = OpLabel
1961 OpBranch %24
1962 %24 = OpLabel
1963 %25 = OpSGreaterThan %10 %8 %9
1964 OpBranch %26
1965 %26 = OpLabel
1966 %27 = OpConvertSToF %11 %8
1967 %28 = OpAccessChain %16 %3 %8
1968 OpStore %28 %27
1969 OpBranch %22
1970 %22 = OpLabel
1971 %21 = OpISub %6 %8 %17
1972 OpBranch %29
1973 %29 = OpLabel
1974 OpBranch %31
1975 %31 = OpLabel
1976 %32 = OpSGreaterThan %10 %21 %9
1977 OpBranch %33
1978 %33 = OpLabel
1979 %34 = OpConvertSToF %11 %21
1980 %35 = OpAccessChain %16 %3 %21
1981 OpStore %35 %34
1982 OpBranch %36
1983 %36 = OpLabel
1984 %37 = OpISub %6 %21 %17
1985 OpBranch %38
1986 %38 = OpLabel
1987 OpBranch %40
1988 %40 = OpLabel
1989 %41 = OpSGreaterThan %10 %37 %9
1990 OpBranch %42
1991 %42 = OpLabel
1992 %43 = OpConvertSToF %11 %37
1993 %44 = OpAccessChain %16 %3 %37
1994 OpStore %44 %43
1995 OpBranch %45
1996 %45 = OpLabel
1997 %46 = OpISub %6 %37 %17
1998 OpBranch %47
1999 %47 = OpLabel
2000 OpBranch %49
2001 %49 = OpLabel
2002 %50 = OpSGreaterThan %10 %46 %9
2003 OpBranch %51
2004 %51 = OpLabel
2005 %52 = OpConvertSToF %11 %46
2006 %53 = OpAccessChain %16 %3 %46
2007 OpStore %53 %52
2008 OpBranch %54
2009 %54 = OpLabel
2010 %55 = OpISub %6 %46 %17
2011 OpBranch %23
2012 %23 = OpLabel
2013 OpReturn
2014 OpFunctionEnd
2015 )";
2016
2017 std::unique_ptr<IRContext> context =
2018 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
2019 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
2020 Module* module = context->module();
2021 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
2022 << text << std::endl;
2023
2024 LoopUnroller loop_unroller;
2025 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
2026 SinglePassRunAndCheck<LoopUnroller>(text, output, false);
2027 }
2028
2029 // With LocalMultiStoreElimPass
2030 static const std::string multiple_phi_shader = R"(
2031 OpCapability Shader
2032 %1 = OpExtInstImport "GLSL.std.450"
2033 OpMemoryModel Logical GLSL450
2034 OpEntryPoint Fragment %4 "main"
2035 OpExecutionMode %4 OriginUpperLeft
2036 OpSource GLSL 410
2037 OpName %4 "main"
2038 OpName %8 "foo("
2039 %2 = OpTypeVoid
2040 %3 = OpTypeFunction %2
2041 %6 = OpTypeInt 32 1
2042 %7 = OpTypeFunction %6
2043 %10 = OpTypePointer Function %6
2044 %12 = OpConstant %6 0
2045 %14 = OpConstant %6 3
2046 %22 = OpConstant %6 6
2047 %23 = OpTypeBool
2048 %31 = OpConstant %6 1
2049 %4 = OpFunction %2 None %3
2050 %5 = OpLabel
2051 %40 = OpFunctionCall %6 %8
2052 OpReturn
2053 OpFunctionEnd
2054 %8 = OpFunction %6 None %7
2055 %9 = OpLabel
2056 OpBranch %16
2057 %16 = OpLabel
2058 %41 = OpPhi %6 %12 %9 %34 %19
2059 %42 = OpPhi %6 %14 %9 %29 %19
2060 %43 = OpPhi %6 %12 %9 %32 %19
2061 OpLoopMerge %18 %19 Unroll
2062 OpBranch %20
2063 %20 = OpLabel
2064 %24 = OpSLessThan %23 %43 %22
2065 OpBranchConditional %24 %17 %18
2066 %17 = OpLabel
2067 %27 = OpIMul %6 %43 %41
2068 %29 = OpIAdd %6 %42 %27
2069 OpBranch %19
2070 %19 = OpLabel
2071 %32 = OpIAdd %6 %43 %31
2072 %34 = OpISub %6 %41 %31
2073 OpBranch %16
2074 %18 = OpLabel
2075 %37 = OpIAdd %6 %42 %41
2076 OpReturnValue %37
2077 OpFunctionEnd
2078 )";
2079
TEST_F(PassClassTest,PartiallyUnrollResidualMultipleInductionVariables)2080 TEST_F(PassClassTest, PartiallyUnrollResidualMultipleInductionVariables) {
2081 const std::string output = R"(OpCapability Shader
2082 %1 = OpExtInstImport "GLSL.std.450"
2083 OpMemoryModel Logical GLSL450
2084 OpEntryPoint Fragment %2 "main"
2085 OpExecutionMode %2 OriginUpperLeft
2086 OpSource GLSL 410
2087 OpName %2 "main"
2088 OpName %3 "foo("
2089 %4 = OpTypeVoid
2090 %5 = OpTypeFunction %4
2091 %6 = OpTypeInt 32 1
2092 %7 = OpTypeFunction %6
2093 %8 = OpTypePointer Function %6
2094 %9 = OpConstant %6 0
2095 %10 = OpConstant %6 3
2096 %11 = OpConstant %6 6
2097 %12 = OpTypeBool
2098 %13 = OpConstant %6 1
2099 %82 = OpTypeInt 32 0
2100 %83 = OpConstant %82 2
2101 %2 = OpFunction %4 None %5
2102 %14 = OpLabel
2103 %15 = OpFunctionCall %6 %3
2104 OpReturn
2105 OpFunctionEnd
2106 %3 = OpFunction %6 None %7
2107 %16 = OpLabel
2108 OpBranch %17
2109 %17 = OpLabel
2110 %18 = OpPhi %6 %9 %16 %19 %20
2111 %21 = OpPhi %6 %10 %16 %22 %20
2112 %23 = OpPhi %6 %9 %16 %24 %20
2113 OpLoopMerge %31 %20 Unroll
2114 OpBranch %26
2115 %26 = OpLabel
2116 %27 = OpSLessThan %12 %23 %83
2117 OpBranchConditional %27 %28 %31
2118 %28 = OpLabel
2119 %29 = OpIMul %6 %23 %18
2120 %22 = OpIAdd %6 %21 %29
2121 OpBranch %20
2122 %20 = OpLabel
2123 %24 = OpIAdd %6 %23 %13
2124 %19 = OpISub %6 %18 %13
2125 OpBranch %17
2126 %31 = OpLabel
2127 OpBranch %32
2128 %32 = OpLabel
2129 %33 = OpPhi %6 %18 %31 %81 %79
2130 %34 = OpPhi %6 %21 %31 %78 %79
2131 %35 = OpPhi %6 %23 %31 %80 %79
2132 OpLoopMerge %44 %79 DontUnroll
2133 OpBranch %36
2134 %36 = OpLabel
2135 %37 = OpSLessThan %12 %35 %11
2136 OpBranchConditional %37 %38 %44
2137 %38 = OpLabel
2138 %39 = OpIMul %6 %35 %33
2139 %40 = OpIAdd %6 %34 %39
2140 OpBranch %41
2141 %41 = OpLabel
2142 %42 = OpIAdd %6 %35 %13
2143 %43 = OpISub %6 %33 %13
2144 OpBranch %46
2145 %46 = OpLabel
2146 OpBranch %50
2147 %50 = OpLabel
2148 %51 = OpSLessThan %12 %42 %11
2149 OpBranch %52
2150 %52 = OpLabel
2151 %53 = OpIMul %6 %42 %43
2152 %54 = OpIAdd %6 %40 %53
2153 OpBranch %55
2154 %55 = OpLabel
2155 %56 = OpIAdd %6 %42 %13
2156 %57 = OpISub %6 %43 %13
2157 OpBranch %58
2158 %58 = OpLabel
2159 OpBranch %62
2160 %62 = OpLabel
2161 %63 = OpSLessThan %12 %56 %11
2162 OpBranch %64
2163 %64 = OpLabel
2164 %65 = OpIMul %6 %56 %57
2165 %66 = OpIAdd %6 %54 %65
2166 OpBranch %67
2167 %67 = OpLabel
2168 %68 = OpIAdd %6 %56 %13
2169 %69 = OpISub %6 %57 %13
2170 OpBranch %70
2171 %70 = OpLabel
2172 OpBranch %74
2173 %74 = OpLabel
2174 %75 = OpSLessThan %12 %68 %11
2175 OpBranch %76
2176 %76 = OpLabel
2177 %77 = OpIMul %6 %68 %69
2178 %78 = OpIAdd %6 %66 %77
2179 OpBranch %79
2180 %79 = OpLabel
2181 %80 = OpIAdd %6 %68 %13
2182 %81 = OpISub %6 %69 %13
2183 OpBranch %32
2184 %44 = OpLabel
2185 %45 = OpIAdd %6 %34 %33
2186 OpReturnValue %45
2187 %25 = OpLabel
2188 %30 = OpIAdd %6 %34 %33
2189 OpReturnValue %30
2190 OpFunctionEnd
2191 )";
2192
2193 std::unique_ptr<IRContext> context =
2194 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, multiple_phi_shader,
2195 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
2196 Module* module = context->module();
2197 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
2198 << multiple_phi_shader << std::endl;
2199
2200 LoopUnroller loop_unroller;
2201 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
2202 SinglePassRunAndCheck<PartialUnrollerTestPass<4>>(multiple_phi_shader, output,
2203 false);
2204 }
2205
TEST_F(PassClassTest,PartiallyUnrollMultipleInductionVariables)2206 TEST_F(PassClassTest, PartiallyUnrollMultipleInductionVariables) {
2207 const std::string output = R"(OpCapability Shader
2208 %1 = OpExtInstImport "GLSL.std.450"
2209 OpMemoryModel Logical GLSL450
2210 OpEntryPoint Fragment %2 "main"
2211 OpExecutionMode %2 OriginUpperLeft
2212 OpSource GLSL 410
2213 OpName %2 "main"
2214 OpName %3 "foo("
2215 %4 = OpTypeVoid
2216 %5 = OpTypeFunction %4
2217 %6 = OpTypeInt 32 1
2218 %7 = OpTypeFunction %6
2219 %8 = OpTypePointer Function %6
2220 %9 = OpConstant %6 0
2221 %10 = OpConstant %6 3
2222 %11 = OpConstant %6 6
2223 %12 = OpTypeBool
2224 %13 = OpConstant %6 1
2225 %2 = OpFunction %4 None %5
2226 %14 = OpLabel
2227 %15 = OpFunctionCall %6 %3
2228 OpReturn
2229 OpFunctionEnd
2230 %3 = OpFunction %6 None %7
2231 %16 = OpLabel
2232 OpBranch %17
2233 %17 = OpLabel
2234 %18 = OpPhi %6 %9 %16 %42 %40
2235 %21 = OpPhi %6 %10 %16 %39 %40
2236 %23 = OpPhi %6 %9 %16 %41 %40
2237 OpLoopMerge %25 %40 DontUnroll
2238 OpBranch %26
2239 %26 = OpLabel
2240 %27 = OpSLessThan %12 %23 %11
2241 OpBranchConditional %27 %28 %25
2242 %28 = OpLabel
2243 %29 = OpIMul %6 %23 %18
2244 %22 = OpIAdd %6 %21 %29
2245 OpBranch %20
2246 %20 = OpLabel
2247 %24 = OpIAdd %6 %23 %13
2248 %19 = OpISub %6 %18 %13
2249 OpBranch %31
2250 %31 = OpLabel
2251 OpBranch %35
2252 %35 = OpLabel
2253 %36 = OpSLessThan %12 %24 %11
2254 OpBranch %37
2255 %37 = OpLabel
2256 %38 = OpIMul %6 %24 %19
2257 %39 = OpIAdd %6 %22 %38
2258 OpBranch %40
2259 %40 = OpLabel
2260 %41 = OpIAdd %6 %24 %13
2261 %42 = OpISub %6 %19 %13
2262 OpBranch %17
2263 %25 = OpLabel
2264 %30 = OpIAdd %6 %21 %18
2265 OpReturnValue %30
2266 OpFunctionEnd
2267 )";
2268
2269 std::unique_ptr<IRContext> context =
2270 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, multiple_phi_shader,
2271 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
2272 Module* module = context->module();
2273 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
2274 << multiple_phi_shader << std::endl;
2275
2276 LoopUnroller loop_unroller;
2277 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
2278 SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(multiple_phi_shader, output,
2279 false);
2280 }
2281
TEST_F(PassClassTest,FullyUnrollMultipleInductionVariables)2282 TEST_F(PassClassTest, FullyUnrollMultipleInductionVariables) {
2283 const std::string output = R"(OpCapability Shader
2284 %1 = OpExtInstImport "GLSL.std.450"
2285 OpMemoryModel Logical GLSL450
2286 OpEntryPoint Fragment %2 "main"
2287 OpExecutionMode %2 OriginUpperLeft
2288 OpSource GLSL 410
2289 OpName %2 "main"
2290 OpName %3 "foo("
2291 %4 = OpTypeVoid
2292 %5 = OpTypeFunction %4
2293 %6 = OpTypeInt 32 1
2294 %7 = OpTypeFunction %6
2295 %8 = OpTypePointer Function %6
2296 %9 = OpConstant %6 0
2297 %10 = OpConstant %6 3
2298 %11 = OpConstant %6 6
2299 %12 = OpTypeBool
2300 %13 = OpConstant %6 1
2301 %2 = OpFunction %4 None %5
2302 %14 = OpLabel
2303 %15 = OpFunctionCall %6 %3
2304 OpReturn
2305 OpFunctionEnd
2306 %3 = OpFunction %6 None %7
2307 %16 = OpLabel
2308 OpBranch %17
2309 %17 = OpLabel
2310 OpBranch %26
2311 %26 = OpLabel
2312 %27 = OpSLessThan %12 %9 %11
2313 OpBranch %28
2314 %28 = OpLabel
2315 %29 = OpIMul %6 %9 %9
2316 %22 = OpIAdd %6 %10 %29
2317 OpBranch %20
2318 %20 = OpLabel
2319 %24 = OpIAdd %6 %9 %13
2320 %19 = OpISub %6 %9 %13
2321 OpBranch %31
2322 %31 = OpLabel
2323 OpBranch %35
2324 %35 = OpLabel
2325 %36 = OpSLessThan %12 %24 %11
2326 OpBranch %37
2327 %37 = OpLabel
2328 %38 = OpIMul %6 %24 %19
2329 %39 = OpIAdd %6 %22 %38
2330 OpBranch %40
2331 %40 = OpLabel
2332 %41 = OpIAdd %6 %24 %13
2333 %42 = OpISub %6 %19 %13
2334 OpBranch %43
2335 %43 = OpLabel
2336 OpBranch %47
2337 %47 = OpLabel
2338 %48 = OpSLessThan %12 %41 %11
2339 OpBranch %49
2340 %49 = OpLabel
2341 %50 = OpIMul %6 %41 %42
2342 %51 = OpIAdd %6 %39 %50
2343 OpBranch %52
2344 %52 = OpLabel
2345 %53 = OpIAdd %6 %41 %13
2346 %54 = OpISub %6 %42 %13
2347 OpBranch %55
2348 %55 = OpLabel
2349 OpBranch %59
2350 %59 = OpLabel
2351 %60 = OpSLessThan %12 %53 %11
2352 OpBranch %61
2353 %61 = OpLabel
2354 %62 = OpIMul %6 %53 %54
2355 %63 = OpIAdd %6 %51 %62
2356 OpBranch %64
2357 %64 = OpLabel
2358 %65 = OpIAdd %6 %53 %13
2359 %66 = OpISub %6 %54 %13
2360 OpBranch %67
2361 %67 = OpLabel
2362 OpBranch %71
2363 %71 = OpLabel
2364 %72 = OpSLessThan %12 %65 %11
2365 OpBranch %73
2366 %73 = OpLabel
2367 %74 = OpIMul %6 %65 %66
2368 %75 = OpIAdd %6 %63 %74
2369 OpBranch %76
2370 %76 = OpLabel
2371 %77 = OpIAdd %6 %65 %13
2372 %78 = OpISub %6 %66 %13
2373 OpBranch %79
2374 %79 = OpLabel
2375 OpBranch %83
2376 %83 = OpLabel
2377 %84 = OpSLessThan %12 %77 %11
2378 OpBranch %85
2379 %85 = OpLabel
2380 %86 = OpIMul %6 %77 %78
2381 %87 = OpIAdd %6 %75 %86
2382 OpBranch %88
2383 %88 = OpLabel
2384 %89 = OpIAdd %6 %77 %13
2385 %90 = OpISub %6 %78 %13
2386 OpBranch %25
2387 %25 = OpLabel
2388 %30 = OpIAdd %6 %87 %90
2389 OpReturnValue %30
2390 OpFunctionEnd
2391 )";
2392
2393 std::unique_ptr<IRContext> context =
2394 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, multiple_phi_shader,
2395 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
2396 Module* module = context->module();
2397 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
2398 << multiple_phi_shader << std::endl;
2399
2400 LoopUnroller loop_unroller;
2401 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
2402 SinglePassRunAndCheck<LoopUnroller>(multiple_phi_shader, output, false);
2403 }
2404
2405 /*
2406 Generated from the following GLSL
2407 #version 440 core
2408 void main()
2409 {
2410 int j = 0;
2411 for (int i = 0; i <= 2; ++i)
2412 ++j;
2413
2414 for (int i = 1; i >= 0; --i)
2415 ++j;
2416 }
2417 */
TEST_F(PassClassTest,FullyUnrollEqualToOperations)2418 TEST_F(PassClassTest, FullyUnrollEqualToOperations) {
2419 // With LocalMultiStoreElimPass
2420 const std::string text = R"(
2421 OpCapability Shader
2422 %1 = OpExtInstImport "GLSL.std.450"
2423 OpMemoryModel Logical GLSL450
2424 OpEntryPoint Fragment %4 "main"
2425 OpExecutionMode %4 OriginUpperLeft
2426 OpSource GLSL 440
2427 OpName %4 "main"
2428 %2 = OpTypeVoid
2429 %3 = OpTypeFunction %2
2430 %6 = OpTypeInt 32 1
2431 %7 = OpTypePointer Function %6
2432 %9 = OpConstant %6 0
2433 %17 = OpConstant %6 2
2434 %18 = OpTypeBool
2435 %21 = OpConstant %6 1
2436 %4 = OpFunction %2 None %3
2437 %5 = OpLabel
2438 OpBranch %11
2439 %11 = OpLabel
2440 %37 = OpPhi %6 %9 %5 %22 %14
2441 %38 = OpPhi %6 %9 %5 %24 %14
2442 OpLoopMerge %13 %14 Unroll
2443 OpBranch %15
2444 %15 = OpLabel
2445 %19 = OpSLessThanEqual %18 %38 %17
2446 OpBranchConditional %19 %12 %13
2447 %12 = OpLabel
2448 %22 = OpIAdd %6 %37 %21
2449 OpBranch %14
2450 %14 = OpLabel
2451 %24 = OpIAdd %6 %38 %21
2452 OpBranch %11
2453 %13 = OpLabel
2454 OpBranch %26
2455 %26 = OpLabel
2456 %39 = OpPhi %6 %37 %13 %34 %29
2457 %40 = OpPhi %6 %21 %13 %36 %29
2458 OpLoopMerge %28 %29 Unroll
2459 OpBranch %30
2460 %30 = OpLabel
2461 %32 = OpSGreaterThanEqual %18 %40 %9
2462 OpBranchConditional %32 %27 %28
2463 %27 = OpLabel
2464 %34 = OpIAdd %6 %39 %21
2465 OpBranch %29
2466 %29 = OpLabel
2467 %36 = OpISub %6 %40 %21
2468 OpBranch %26
2469 %28 = OpLabel
2470 OpReturn
2471 OpFunctionEnd
2472 )";
2473
2474 const std::string output = R"(OpCapability Shader
2475 %1 = OpExtInstImport "GLSL.std.450"
2476 OpMemoryModel Logical GLSL450
2477 OpEntryPoint Fragment %2 "main"
2478 OpExecutionMode %2 OriginUpperLeft
2479 OpSource GLSL 440
2480 OpName %2 "main"
2481 %3 = OpTypeVoid
2482 %4 = OpTypeFunction %3
2483 %5 = OpTypeInt 32 1
2484 %6 = OpTypePointer Function %5
2485 %7 = OpConstant %5 0
2486 %8 = OpConstant %5 2
2487 %9 = OpTypeBool
2488 %10 = OpConstant %5 1
2489 %2 = OpFunction %3 None %4
2490 %11 = OpLabel
2491 OpBranch %12
2492 %12 = OpLabel
2493 OpBranch %19
2494 %19 = OpLabel
2495 %20 = OpSLessThanEqual %9 %7 %8
2496 OpBranch %21
2497 %21 = OpLabel
2498 %14 = OpIAdd %5 %7 %10
2499 OpBranch %15
2500 %15 = OpLabel
2501 %17 = OpIAdd %5 %7 %10
2502 OpBranch %41
2503 %41 = OpLabel
2504 OpBranch %44
2505 %44 = OpLabel
2506 %45 = OpSLessThanEqual %9 %17 %8
2507 OpBranch %46
2508 %46 = OpLabel
2509 %47 = OpIAdd %5 %14 %10
2510 OpBranch %48
2511 %48 = OpLabel
2512 %49 = OpIAdd %5 %17 %10
2513 OpBranch %50
2514 %50 = OpLabel
2515 OpBranch %53
2516 %53 = OpLabel
2517 %54 = OpSLessThanEqual %9 %49 %8
2518 OpBranch %55
2519 %55 = OpLabel
2520 %56 = OpIAdd %5 %47 %10
2521 OpBranch %57
2522 %57 = OpLabel
2523 %58 = OpIAdd %5 %49 %10
2524 OpBranch %18
2525 %18 = OpLabel
2526 OpBranch %22
2527 %22 = OpLabel
2528 OpBranch %29
2529 %29 = OpLabel
2530 %30 = OpSGreaterThanEqual %9 %10 %7
2531 OpBranch %31
2532 %31 = OpLabel
2533 %24 = OpIAdd %5 %56 %10
2534 OpBranch %25
2535 %25 = OpLabel
2536 %27 = OpISub %5 %10 %10
2537 OpBranch %32
2538 %32 = OpLabel
2539 OpBranch %35
2540 %35 = OpLabel
2541 %36 = OpSGreaterThanEqual %9 %27 %7
2542 OpBranch %37
2543 %37 = OpLabel
2544 %38 = OpIAdd %5 %24 %10
2545 OpBranch %39
2546 %39 = OpLabel
2547 %40 = OpISub %5 %27 %10
2548 OpBranch %28
2549 %28 = OpLabel
2550 OpReturn
2551 OpFunctionEnd
2552 )";
2553
2554 std::unique_ptr<IRContext> context =
2555 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
2556 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
2557 Module* module = context->module();
2558 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
2559 << text << std::endl;
2560
2561 LoopUnroller loop_unroller;
2562 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
2563 SinglePassRunAndCheck<LoopUnroller>(text, output, false);
2564 }
2565
2566 // With LocalMultiStoreElimPass
2567 const std::string condition_in_header = R"(
2568 OpCapability Shader
2569 OpMemoryModel Logical GLSL450
2570 OpEntryPoint Fragment %main "main" %o
2571 OpExecutionMode %main OriginUpperLeft
2572 OpSource GLSL 430
2573 OpDecorate %o Location 0
2574 %void = OpTypeVoid
2575 %6 = OpTypeFunction %void
2576 %int = OpTypeInt 32 1
2577 %int_n2 = OpConstant %int -2
2578 %int_2 = OpConstant %int 2
2579 %bool = OpTypeBool
2580 %float = OpTypeFloat 32
2581 %_ptr_Output_float = OpTypePointer Output %float
2582 %o = OpVariable %_ptr_Output_float Output
2583 %float_1 = OpConstant %float 1
2584 %main = OpFunction %void None %6
2585 %15 = OpLabel
2586 OpBranch %16
2587 %16 = OpLabel
2588 %27 = OpPhi %int %int_n2 %15 %26 %18
2589 %21 = OpSLessThanEqual %bool %27 %int_2
2590 OpLoopMerge %17 %18 Unroll
2591 OpBranchConditional %21 %22 %17
2592 %22 = OpLabel
2593 %23 = OpLoad %float %o
2594 %24 = OpFAdd %float %23 %float_1
2595 OpStore %o %24
2596 OpBranch %18
2597 %18 = OpLabel
2598 %26 = OpIAdd %int %27 %int_2
2599 OpBranch %16
2600 %17 = OpLabel
2601 OpReturn
2602 OpFunctionEnd
2603 )";
2604
TEST_F(PassClassTest,FullyUnrollConditionIsInHeaderBlock)2605 TEST_F(PassClassTest, FullyUnrollConditionIsInHeaderBlock) {
2606 const std::string output = R"(OpCapability Shader
2607 OpMemoryModel Logical GLSL450
2608 OpEntryPoint Fragment %1 "main" %2
2609 OpExecutionMode %1 OriginUpperLeft
2610 OpSource GLSL 430
2611 OpDecorate %2 Location 0
2612 %3 = OpTypeVoid
2613 %4 = OpTypeFunction %3
2614 %5 = OpTypeInt 32 1
2615 %6 = OpConstant %5 -2
2616 %7 = OpConstant %5 2
2617 %8 = OpTypeBool
2618 %9 = OpTypeFloat 32
2619 %10 = OpTypePointer Output %9
2620 %2 = OpVariable %10 Output
2621 %11 = OpConstant %9 1
2622 %1 = OpFunction %3 None %4
2623 %12 = OpLabel
2624 OpBranch %13
2625 %13 = OpLabel
2626 %17 = OpSLessThanEqual %8 %6 %7
2627 OpBranch %19
2628 %19 = OpLabel
2629 %20 = OpLoad %9 %2
2630 %21 = OpFAdd %9 %20 %11
2631 OpStore %2 %21
2632 OpBranch %16
2633 %16 = OpLabel
2634 %15 = OpIAdd %5 %6 %7
2635 OpBranch %22
2636 %22 = OpLabel
2637 %24 = OpSLessThanEqual %8 %15 %7
2638 OpBranch %25
2639 %25 = OpLabel
2640 %26 = OpLoad %9 %2
2641 %27 = OpFAdd %9 %26 %11
2642 OpStore %2 %27
2643 OpBranch %28
2644 %28 = OpLabel
2645 %29 = OpIAdd %5 %15 %7
2646 OpBranch %30
2647 %30 = OpLabel
2648 %32 = OpSLessThanEqual %8 %29 %7
2649 OpBranch %33
2650 %33 = OpLabel
2651 %34 = OpLoad %9 %2
2652 %35 = OpFAdd %9 %34 %11
2653 OpStore %2 %35
2654 OpBranch %36
2655 %36 = OpLabel
2656 %37 = OpIAdd %5 %29 %7
2657 OpBranch %18
2658 %18 = OpLabel
2659 OpReturn
2660 OpFunctionEnd
2661 )";
2662
2663 std::unique_ptr<IRContext> context =
2664 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, condition_in_header,
2665 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
2666 Module* module = context->module();
2667 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
2668 << condition_in_header << std::endl;
2669
2670 LoopUnroller loop_unroller;
2671 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
2672 SinglePassRunAndCheck<LoopUnroller>(condition_in_header, output, false);
2673 }
2674
TEST_F(PassClassTest,PartiallyUnrollResidualConditionIsInHeaderBlock)2675 TEST_F(PassClassTest, PartiallyUnrollResidualConditionIsInHeaderBlock) {
2676 const std::string output = R"(OpCapability Shader
2677 OpMemoryModel Logical GLSL450
2678 OpEntryPoint Fragment %1 "main" %2
2679 OpExecutionMode %1 OriginUpperLeft
2680 OpSource GLSL 430
2681 OpDecorate %2 Location 0
2682 %3 = OpTypeVoid
2683 %4 = OpTypeFunction %3
2684 %5 = OpTypeInt 32 1
2685 %6 = OpConstant %5 -2
2686 %7 = OpConstant %5 2
2687 %8 = OpTypeBool
2688 %9 = OpTypeFloat 32
2689 %10 = OpTypePointer Output %9
2690 %2 = OpVariable %10 Output
2691 %11 = OpConstant %9 1
2692 %40 = OpTypeInt 32 0
2693 %41 = OpConstant %40 1
2694 %1 = OpFunction %3 None %4
2695 %12 = OpLabel
2696 OpBranch %13
2697 %13 = OpLabel
2698 %14 = OpPhi %5 %6 %12 %15 %16
2699 %17 = OpSLessThanEqual %8 %14 %41
2700 OpLoopMerge %22 %16 Unroll
2701 OpBranchConditional %17 %19 %22
2702 %19 = OpLabel
2703 %20 = OpLoad %9 %2
2704 %21 = OpFAdd %9 %20 %11
2705 OpStore %2 %21
2706 OpBranch %16
2707 %16 = OpLabel
2708 %15 = OpIAdd %5 %14 %7
2709 OpBranch %13
2710 %22 = OpLabel
2711 OpBranch %23
2712 %23 = OpLabel
2713 %24 = OpPhi %5 %14 %22 %39 %38
2714 %25 = OpSLessThanEqual %8 %24 %7
2715 OpLoopMerge %31 %38 DontUnroll
2716 OpBranchConditional %25 %26 %31
2717 %26 = OpLabel
2718 %27 = OpLoad %9 %2
2719 %28 = OpFAdd %9 %27 %11
2720 OpStore %2 %28
2721 OpBranch %29
2722 %29 = OpLabel
2723 %30 = OpIAdd %5 %24 %7
2724 OpBranch %32
2725 %32 = OpLabel
2726 %34 = OpSLessThanEqual %8 %30 %7
2727 OpBranch %35
2728 %35 = OpLabel
2729 %36 = OpLoad %9 %2
2730 %37 = OpFAdd %9 %36 %11
2731 OpStore %2 %37
2732 OpBranch %38
2733 %38 = OpLabel
2734 %39 = OpIAdd %5 %30 %7
2735 OpBranch %23
2736 %31 = OpLabel
2737 OpReturn
2738 %18 = OpLabel
2739 OpReturn
2740 OpFunctionEnd
2741 )";
2742
2743 std::unique_ptr<IRContext> context =
2744 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, condition_in_header,
2745 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
2746 Module* module = context->module();
2747 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
2748 << condition_in_header << std::endl;
2749
2750 LoopUnroller loop_unroller;
2751 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
2752 SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(condition_in_header, output,
2753 false);
2754 }
2755
2756 /*
2757 Generated from following GLSL with latch block artificially inserted to be
2758 seperate from continue.
2759 #version 430
2760 void main(void) {
2761 float x[10];
2762 for (int i = 0; i < 10; ++i) {
2763 x[i] = i;
2764 }
2765 }
2766 */
TEST_F(PassClassTest,PartiallyUnrollLatchNotContinue)2767 TEST_F(PassClassTest, PartiallyUnrollLatchNotContinue) {
2768 const std::string text = R"(OpCapability Shader
2769 %1 = OpExtInstImport "GLSL.std.450"
2770 OpMemoryModel Logical GLSL450
2771 OpEntryPoint Fragment %2 "main"
2772 OpExecutionMode %2 OriginUpperLeft
2773 OpSource GLSL 430
2774 OpName %2 "main"
2775 OpName %3 "i"
2776 OpName %4 "x"
2777 %5 = OpTypeVoid
2778 %6 = OpTypeFunction %5
2779 %7 = OpTypeInt 32 1
2780 %8 = OpTypePointer Function %7
2781 %9 = OpConstant %7 0
2782 %10 = OpConstant %7 10
2783 %11 = OpTypeBool
2784 %12 = OpTypeFloat 32
2785 %13 = OpTypeInt 32 0
2786 %14 = OpConstant %13 10
2787 %15 = OpTypeArray %12 %14
2788 %16 = OpTypePointer Function %15
2789 %17 = OpTypePointer Function %12
2790 %18 = OpConstant %7 1
2791 %2 = OpFunction %5 None %6
2792 %19 = OpLabel
2793 %3 = OpVariable %8 Function
2794 %4 = OpVariable %16 Function
2795 OpStore %3 %9
2796 OpBranch %20
2797 %20 = OpLabel
2798 %21 = OpPhi %7 %9 %19 %22 %30
2799 OpLoopMerge %24 %23 Unroll
2800 OpBranch %25
2801 %25 = OpLabel
2802 %26 = OpSLessThan %11 %21 %10
2803 OpBranchConditional %26 %27 %24
2804 %27 = OpLabel
2805 %28 = OpConvertSToF %12 %21
2806 %29 = OpAccessChain %17 %4 %21
2807 OpStore %29 %28
2808 OpBranch %23
2809 %23 = OpLabel
2810 %22 = OpIAdd %7 %21 %18
2811 OpStore %3 %22
2812 OpBranch %30
2813 %30 = OpLabel
2814 OpBranch %20
2815 %24 = OpLabel
2816 OpReturn
2817 OpFunctionEnd
2818 )";
2819
2820 const std::string expected = R"(OpCapability Shader
2821 %1 = OpExtInstImport "GLSL.std.450"
2822 OpMemoryModel Logical GLSL450
2823 OpEntryPoint Fragment %2 "main"
2824 OpExecutionMode %2 OriginUpperLeft
2825 OpSource GLSL 430
2826 OpName %2 "main"
2827 OpName %3 "i"
2828 OpName %4 "x"
2829 %5 = OpTypeVoid
2830 %6 = OpTypeFunction %5
2831 %7 = OpTypeInt 32 1
2832 %8 = OpTypePointer Function %7
2833 %9 = OpConstant %7 0
2834 %10 = OpConstant %7 10
2835 %11 = OpTypeBool
2836 %12 = OpTypeFloat 32
2837 %13 = OpTypeInt 32 0
2838 %14 = OpConstant %13 10
2839 %15 = OpTypeArray %12 %14
2840 %16 = OpTypePointer Function %15
2841 %17 = OpTypePointer Function %12
2842 %18 = OpConstant %7 1
2843 %63 = OpConstant %13 1
2844 %2 = OpFunction %5 None %6
2845 %19 = OpLabel
2846 %3 = OpVariable %8 Function
2847 %4 = OpVariable %16 Function
2848 OpStore %3 %9
2849 OpBranch %20
2850 %20 = OpLabel
2851 %21 = OpPhi %7 %9 %19 %22 %23
2852 OpLoopMerge %31 %25 Unroll
2853 OpBranch %26
2854 %26 = OpLabel
2855 %27 = OpSLessThan %11 %21 %63
2856 OpBranchConditional %27 %28 %31
2857 %28 = OpLabel
2858 %29 = OpConvertSToF %12 %21
2859 %30 = OpAccessChain %17 %4 %21
2860 OpStore %30 %29
2861 OpBranch %25
2862 %25 = OpLabel
2863 %22 = OpIAdd %7 %21 %18
2864 OpStore %3 %22
2865 OpBranch %23
2866 %23 = OpLabel
2867 OpBranch %20
2868 %31 = OpLabel
2869 OpBranch %32
2870 %32 = OpLabel
2871 %33 = OpPhi %7 %21 %31 %61 %62
2872 OpLoopMerge %42 %60 DontUnroll
2873 OpBranch %34
2874 %34 = OpLabel
2875 %35 = OpSLessThan %11 %33 %10
2876 OpBranchConditional %35 %36 %42
2877 %36 = OpLabel
2878 %37 = OpConvertSToF %12 %33
2879 %38 = OpAccessChain %17 %4 %33
2880 OpStore %38 %37
2881 OpBranch %39
2882 %39 = OpLabel
2883 %40 = OpIAdd %7 %33 %18
2884 OpStore %3 %40
2885 OpBranch %41
2886 %41 = OpLabel
2887 OpBranch %43
2888 %43 = OpLabel
2889 OpBranch %45
2890 %45 = OpLabel
2891 %46 = OpSLessThan %11 %40 %10
2892 OpBranch %47
2893 %47 = OpLabel
2894 %48 = OpConvertSToF %12 %40
2895 %49 = OpAccessChain %17 %4 %40
2896 OpStore %49 %48
2897 OpBranch %50
2898 %50 = OpLabel
2899 %51 = OpIAdd %7 %40 %18
2900 OpStore %3 %51
2901 OpBranch %52
2902 %52 = OpLabel
2903 OpBranch %53
2904 %53 = OpLabel
2905 OpBranch %55
2906 %55 = OpLabel
2907 %56 = OpSLessThan %11 %51 %10
2908 OpBranch %57
2909 %57 = OpLabel
2910 %58 = OpConvertSToF %12 %51
2911 %59 = OpAccessChain %17 %4 %51
2912 OpStore %59 %58
2913 OpBranch %60
2914 %60 = OpLabel
2915 %61 = OpIAdd %7 %51 %18
2916 OpStore %3 %61
2917 OpBranch %62
2918 %62 = OpLabel
2919 OpBranch %32
2920 %42 = OpLabel
2921 OpReturn
2922 %24 = OpLabel
2923 OpReturn
2924 OpFunctionEnd
2925 )";
2926
2927 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
2928 SinglePassRunAndCheck<PartialUnrollerTestPass<3>>(text, expected, true);
2929
2930 // Make sure the latch block information is preserved and propagated correctly
2931 // by the pass.
2932 std::unique_ptr<IRContext> context =
2933 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
2934 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
2935
2936 PartialUnrollerTestPass<3> unroller;
2937 unroller.SetContextForTesting(context.get());
2938 unroller.Process();
2939
2940 Module* module = context->module();
2941 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
2942 << text << std::endl;
2943 const Function* f = spvtest::GetFunction(module, 2);
2944 LoopDescriptor ld{context.get(), f};
2945
2946 EXPECT_EQ(ld.NumLoops(), 2u);
2947
2948 Loop& loop_1 = ld.GetLoopByIndex(0u);
2949 EXPECT_NE(loop_1.GetLatchBlock(), loop_1.GetContinueBlock());
2950
2951 Loop& loop_2 = ld.GetLoopByIndex(1u);
2952 EXPECT_NE(loop_2.GetLatchBlock(), loop_2.GetContinueBlock());
2953 }
2954
2955 // Test that a loop with a self-referencing OpPhi instruction is handled
2956 // correctly.
TEST_F(PassClassTest,OpPhiSelfReference)2957 TEST_F(PassClassTest, OpPhiSelfReference) {
2958 const std::string text = R"(
2959 ; Find the two adds from the unrolled loop
2960 ; CHECK: OpIAdd
2961 ; CHECK: OpIAdd
2962 ; CHECK: OpIAdd %uint %uint_0 %uint_1
2963 ; CHECK-NEXT: OpReturn
2964 OpCapability Shader
2965 %1 = OpExtInstImport "GLSL.std.450"
2966 OpMemoryModel Logical GLSL450
2967 OpEntryPoint GLCompute %2 "main"
2968 OpExecutionMode %2 LocalSize 8 8 1
2969 OpSource HLSL 600
2970 %uint = OpTypeInt 32 0
2971 %void = OpTypeVoid
2972 %5 = OpTypeFunction %void
2973 %uint_0 = OpConstant %uint 0
2974 %uint_1 = OpConstant %uint 1
2975 %bool = OpTypeBool
2976 %true = OpConstantTrue %bool
2977 %2 = OpFunction %void None %5
2978 %10 = OpLabel
2979 OpBranch %19
2980 %19 = OpLabel
2981 %20 = OpPhi %uint %uint_0 %10 %20 %21
2982 %22 = OpPhi %uint %uint_0 %10 %23 %21
2983 %24 = OpULessThanEqual %bool %22 %uint_1
2984 OpLoopMerge %25 %21 Unroll
2985 OpBranchConditional %24 %21 %25
2986 %21 = OpLabel
2987 %23 = OpIAdd %uint %22 %uint_1
2988 OpBranch %19
2989 %25 = OpLabel
2990 %14 = OpIAdd %uint %20 %uint_1
2991 OpReturn
2992 OpFunctionEnd
2993 )";
2994
2995 const bool kFullyUnroll = true;
2996 const uint32_t kUnrollFactor = 0;
2997 SinglePassRunAndMatch<opt::LoopUnroller>(text, true, kFullyUnroll,
2998 kUnrollFactor);
2999 }
3000
3001 } // namespace
3002 } // namespace opt
3003 } // namespace spvtools
3004