• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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