Home | History | Annotate | Download | only in loop_optimizations
      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 */
     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:
    200   PartialUnrollerTestPass() : Pass() {}
    201 
    202   const char* name() const override { return "Loop unroller"; }
    203 
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
   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 */
   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 */
   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 */
   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 */
   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 */
   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 
   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 
   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 
   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 */
   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 
   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 
   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 */
   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.
   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