| // Copyright (c) 2018 Google LLC. | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | #include <memory> | 
 | #include <string> | 
 | #include <unordered_set> | 
 | #include <vector> | 
 |  | 
 | #include "gmock/gmock.h" | 
 | #include "source/opt/iterator.h" | 
 | #include "source/opt/loop_dependence.h" | 
 | #include "source/opt/loop_descriptor.h" | 
 | #include "source/opt/pass.h" | 
 | #include "source/opt/scalar_analysis.h" | 
 | #include "source/opt/tree_iterator.h" | 
 | #include "test/opt/assembly_builder.h" | 
 | #include "test/opt/function_utils.h" | 
 | #include "test/opt/pass_fixture.h" | 
 | #include "test/opt/pass_utils.h" | 
 |  | 
 | namespace spvtools { | 
 | namespace opt { | 
 | namespace { | 
 |  | 
 | using DependencyAnalysisHelpers = ::testing::Test; | 
 |  | 
 | /* | 
 |   Generated from the following GLSL fragment shader | 
 |   with --eliminate-local-multi-store | 
 | #version 440 core | 
 | void a() { | 
 |   int[10][10] arr; | 
 |   int i = 0; | 
 |   int j = 0; | 
 |   for (; i < 10 && j < 10; i++, j++) { | 
 |     arr[i][j] = arr[i][j]; | 
 |   } | 
 | } | 
 | void b() { | 
 |   int[10] arr; | 
 |   for (int i = 0; i < 10; i+=2) { | 
 |     arr[i] = arr[i]; | 
 |   } | 
 | } | 
 | void main(){ | 
 |   a(); | 
 |   b(); | 
 | } | 
 | */ | 
 | TEST(DependencyAnalysisHelpers, UnsupportedLoops) { | 
 |   const std::string text = R"(               OpCapability Shader | 
 |           %1 = OpExtInstImport "GLSL.std.450" | 
 |                OpMemoryModel Logical GLSL450 | 
 |                OpEntryPoint Fragment %4 "main" | 
 |                OpExecutionMode %4 OriginUpperLeft | 
 |                OpSource GLSL 440 | 
 |                OpName %4 "main" | 
 |                OpName %6 "a(" | 
 |                OpName %8 "b(" | 
 |                OpName %12 "i" | 
 |                OpName %14 "j" | 
 |                OpName %32 "arr" | 
 |                OpName %45 "i" | 
 |                OpName %54 "arr" | 
 |           %2 = OpTypeVoid | 
 |           %3 = OpTypeFunction %2 | 
 |          %10 = OpTypeInt 32 1 | 
 |          %11 = OpTypePointer Function %10 | 
 |          %13 = OpConstant %10 0 | 
 |          %21 = OpConstant %10 10 | 
 |          %22 = OpTypeBool | 
 |          %27 = OpTypeInt 32 0 | 
 |          %28 = OpConstant %27 10 | 
 |          %29 = OpTypeArray %10 %28 | 
 |          %30 = OpTypeArray %29 %28 | 
 |          %31 = OpTypePointer Function %30 | 
 |          %41 = OpConstant %10 1 | 
 |          %53 = OpTypePointer Function %29 | 
 |          %60 = OpConstant %10 2 | 
 |           %4 = OpFunction %2 None %3 | 
 |           %5 = OpLabel | 
 |          %63 = OpFunctionCall %2 %6 | 
 |          %64 = OpFunctionCall %2 %8 | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |           %6 = OpFunction %2 None %3 | 
 |           %7 = OpLabel | 
 |          %12 = OpVariable %11 Function | 
 |          %14 = OpVariable %11 Function | 
 |          %32 = OpVariable %31 Function | 
 |                OpStore %12 %13 | 
 |                OpStore %14 %13 | 
 |                OpBranch %15 | 
 |          %15 = OpLabel | 
 |          %65 = OpPhi %10 %13 %7 %42 %18 | 
 |          %66 = OpPhi %10 %13 %7 %44 %18 | 
 |                OpLoopMerge %17 %18 None | 
 |                OpBranch %19 | 
 |          %19 = OpLabel | 
 |          %23 = OpSLessThan %22 %65 %21 | 
 |          %25 = OpSLessThan %22 %66 %21 | 
 |          %26 = OpLogicalAnd %22 %23 %25 | 
 |                OpBranchConditional %26 %16 %17 | 
 |          %16 = OpLabel | 
 |          %37 = OpAccessChain %11 %32 %65 %66 | 
 |          %38 = OpLoad %10 %37 | 
 |          %39 = OpAccessChain %11 %32 %65 %66 | 
 |                OpStore %39 %38 | 
 |                OpBranch %18 | 
 |          %18 = OpLabel | 
 |          %42 = OpIAdd %10 %65 %41 | 
 |                OpStore %12 %42 | 
 |          %44 = OpIAdd %10 %66 %41 | 
 |                OpStore %14 %44 | 
 |                OpBranch %15 | 
 |          %17 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |           %8 = OpFunction %2 None %3 | 
 |           %9 = OpLabel | 
 |          %45 = OpVariable %11 Function | 
 |          %54 = OpVariable %53 Function | 
 |                OpStore %45 %13 | 
 |                OpBranch %46 | 
 |          %46 = OpLabel | 
 |          %67 = OpPhi %10 %13 %9 %62 %49 | 
 |                OpLoopMerge %48 %49 None | 
 |                OpBranch %50 | 
 |          %50 = OpLabel | 
 |          %52 = OpSLessThan %22 %67 %21 | 
 |                OpBranchConditional %52 %47 %48 | 
 |          %47 = OpLabel | 
 |          %57 = OpAccessChain %11 %54 %67 | 
 |          %58 = OpLoad %10 %57 | 
 |          %59 = OpAccessChain %11 %54 %67 | 
 |                OpStore %59 %58 | 
 |                OpBranch %49 | 
 |          %49 = OpLabel | 
 |          %62 = OpIAdd %10 %67 %60 | 
 |                OpStore %45 %62 | 
 |                OpBranch %46 | 
 |          %48 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 | )"; | 
 |   std::unique_ptr<IRContext> context = | 
 |       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, | 
 |                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | 
 |   Module* module = context->module(); | 
 |   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | 
 |                              << text << std::endl; | 
 |   { | 
 |     // Function a | 
 |     const Function* f = spvtest::GetFunction(module, 6); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |  | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     const Instruction* store[1] = {nullptr}; | 
 |     int stores_found = 0; | 
 |     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 16)) { | 
 |       if (inst.opcode() == SpvOp::SpvOpStore) { | 
 |         store[stores_found] = &inst; | 
 |         ++stores_found; | 
 |       } | 
 |     } | 
 |     // 38 -> 39 | 
 |     DistanceVector distance_vector{loops.size()}; | 
 |     EXPECT_FALSE(analysis.IsSupportedLoop(loops[0])); | 
 |     EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(38), | 
 |                                         store[0], &distance_vector)); | 
 |     EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
 |               DistanceEntry::DependenceInformation::UNKNOWN); | 
 |     EXPECT_EQ(distance_vector.GetEntries()[0].direction, | 
 |               DistanceEntry::Directions::ALL); | 
 |   } | 
 |   { | 
 |     // Function b | 
 |     const Function* f = spvtest::GetFunction(module, 8); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |  | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     const Instruction* store[1] = {nullptr}; | 
 |     int stores_found = 0; | 
 |     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 47)) { | 
 |       if (inst.opcode() == SpvOp::SpvOpStore) { | 
 |         store[stores_found] = &inst; | 
 |         ++stores_found; | 
 |       } | 
 |     } | 
 |     // 58 -> 59 | 
 |     DistanceVector distance_vector{loops.size()}; | 
 |     EXPECT_FALSE(analysis.IsSupportedLoop(loops[0])); | 
 |     EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(58), | 
 |                                         store[0], &distance_vector)); | 
 |     EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
 |               DistanceEntry::DependenceInformation::UNKNOWN); | 
 |     EXPECT_EQ(distance_vector.GetEntries()[0].direction, | 
 |               DistanceEntry::Directions::ALL); | 
 |   } | 
 | } | 
 |  | 
 | /* | 
 |   Generated from the following GLSL fragment shader | 
 |   with --eliminate-local-multi-store | 
 | #version 440 core | 
 | void a() { | 
 |   for (int i = -10; i < 0; i++) { | 
 |  | 
 |   } | 
 | } | 
 | void b() { | 
 |   for (int i = -5; i < 5; i++) { | 
 |  | 
 |   } | 
 | } | 
 | void c() { | 
 |   for (int i = 0; i < 10; i++) { | 
 |  | 
 |   } | 
 | } | 
 | void d() { | 
 |   for (int i = 5; i < 15; i++) { | 
 |  | 
 |   } | 
 | } | 
 | void e() { | 
 |   for (int i = -10; i <= 0; i++) { | 
 |  | 
 |   } | 
 | } | 
 | void f() { | 
 |   for (int i = -5; i <= 5; i++) { | 
 |  | 
 |   } | 
 | } | 
 | void g() { | 
 |   for (int i = 0; i <= 10; i++) { | 
 |  | 
 |   } | 
 | } | 
 | void h() { | 
 |   for (int i = 5; i <= 15; i++) { | 
 |  | 
 |   } | 
 | } | 
 | void i() { | 
 |   for (int i = 0; i > -10; i--) { | 
 |  | 
 |   } | 
 | } | 
 | void j() { | 
 |   for (int i = 5; i > -5; i--) { | 
 |  | 
 |   } | 
 | } | 
 | void k() { | 
 |   for (int i = 10; i > 0; i--) { | 
 |  | 
 |   } | 
 | } | 
 | void l() { | 
 |   for (int i = 15; i > 5; i--) { | 
 |  | 
 |   } | 
 | } | 
 | void m() { | 
 |   for (int i = 0; i >= -10; i--) { | 
 |  | 
 |   } | 
 | } | 
 | void n() { | 
 |   for (int i = 5; i >= -5; i--) { | 
 |  | 
 |   } | 
 | } | 
 | void o() { | 
 |   for (int i = 10; i >= 0; i--) { | 
 |  | 
 |   } | 
 | } | 
 | void p() { | 
 |   for (int i = 15; i >= 5; i--) { | 
 |  | 
 |   } | 
 | } | 
 | void main(){ | 
 |   a(); | 
 |   b(); | 
 |   c(); | 
 |   d(); | 
 |   e(); | 
 |   f(); | 
 |   g(); | 
 |   h(); | 
 |   i(); | 
 |   j(); | 
 |   k(); | 
 |   l(); | 
 |   m(); | 
 |   n(); | 
 |   o(); | 
 |   p(); | 
 | } | 
 | */ | 
 | TEST(DependencyAnalysisHelpers, loop_information) { | 
 |   const std::string text = R"(               OpCapability Shader | 
 |           %1 = OpExtInstImport "GLSL.std.450" | 
 |                OpMemoryModel Logical GLSL450 | 
 |                OpEntryPoint Fragment %4 "main" | 
 |                OpExecutionMode %4 OriginUpperLeft | 
 |                OpSource GLSL 440 | 
 |                OpName %4 "main" | 
 |                OpName %6 "a(" | 
 |                OpName %8 "b(" | 
 |                OpName %10 "c(" | 
 |                OpName %12 "d(" | 
 |                OpName %14 "e(" | 
 |                OpName %16 "f(" | 
 |                OpName %18 "g(" | 
 |                OpName %20 "h(" | 
 |                OpName %22 "i(" | 
 |                OpName %24 "j(" | 
 |                OpName %26 "k(" | 
 |                OpName %28 "l(" | 
 |                OpName %30 "m(" | 
 |                OpName %32 "n(" | 
 |                OpName %34 "o(" | 
 |                OpName %36 "p(" | 
 |                OpName %40 "i" | 
 |                OpName %54 "i" | 
 |                OpName %66 "i" | 
 |                OpName %77 "i" | 
 |                OpName %88 "i" | 
 |                OpName %98 "i" | 
 |                OpName %108 "i" | 
 |                OpName %118 "i" | 
 |                OpName %128 "i" | 
 |                OpName %138 "i" | 
 |                OpName %148 "i" | 
 |                OpName %158 "i" | 
 |                OpName %168 "i" | 
 |                OpName %178 "i" | 
 |                OpName %188 "i" | 
 |                OpName %198 "i" | 
 |           %2 = OpTypeVoid | 
 |           %3 = OpTypeFunction %2 | 
 |          %38 = OpTypeInt 32 1 | 
 |          %39 = OpTypePointer Function %38 | 
 |          %41 = OpConstant %38 -10 | 
 |          %48 = OpConstant %38 0 | 
 |          %49 = OpTypeBool | 
 |          %52 = OpConstant %38 1 | 
 |          %55 = OpConstant %38 -5 | 
 |          %62 = OpConstant %38 5 | 
 |          %73 = OpConstant %38 10 | 
 |          %84 = OpConstant %38 15 | 
 |           %4 = OpFunction %2 None %3 | 
 |           %5 = OpLabel | 
 |         %208 = OpFunctionCall %2 %6 | 
 |         %209 = OpFunctionCall %2 %8 | 
 |         %210 = OpFunctionCall %2 %10 | 
 |         %211 = OpFunctionCall %2 %12 | 
 |         %212 = OpFunctionCall %2 %14 | 
 |         %213 = OpFunctionCall %2 %16 | 
 |         %214 = OpFunctionCall %2 %18 | 
 |         %215 = OpFunctionCall %2 %20 | 
 |         %216 = OpFunctionCall %2 %22 | 
 |         %217 = OpFunctionCall %2 %24 | 
 |         %218 = OpFunctionCall %2 %26 | 
 |         %219 = OpFunctionCall %2 %28 | 
 |         %220 = OpFunctionCall %2 %30 | 
 |         %221 = OpFunctionCall %2 %32 | 
 |         %222 = OpFunctionCall %2 %34 | 
 |         %223 = OpFunctionCall %2 %36 | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |           %6 = OpFunction %2 None %3 | 
 |           %7 = OpLabel | 
 |          %40 = OpVariable %39 Function | 
 |                OpStore %40 %41 | 
 |                OpBranch %42 | 
 |          %42 = OpLabel | 
 |         %224 = OpPhi %38 %41 %7 %53 %45 | 
 |                OpLoopMerge %44 %45 None | 
 |                OpBranch %46 | 
 |          %46 = OpLabel | 
 |          %50 = OpSLessThan %49 %224 %48 | 
 |                OpBranchConditional %50 %43 %44 | 
 |          %43 = OpLabel | 
 |                OpBranch %45 | 
 |          %45 = OpLabel | 
 |          %53 = OpIAdd %38 %224 %52 | 
 |                OpStore %40 %53 | 
 |                OpBranch %42 | 
 |          %44 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |           %8 = OpFunction %2 None %3 | 
 |           %9 = OpLabel | 
 |          %54 = OpVariable %39 Function | 
 |                OpStore %54 %55 | 
 |                OpBranch %56 | 
 |          %56 = OpLabel | 
 |         %225 = OpPhi %38 %55 %9 %65 %59 | 
 |                OpLoopMerge %58 %59 None | 
 |                OpBranch %60 | 
 |          %60 = OpLabel | 
 |          %63 = OpSLessThan %49 %225 %62 | 
 |                OpBranchConditional %63 %57 %58 | 
 |          %57 = OpLabel | 
 |                OpBranch %59 | 
 |          %59 = OpLabel | 
 |          %65 = OpIAdd %38 %225 %52 | 
 |                OpStore %54 %65 | 
 |                OpBranch %56 | 
 |          %58 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %10 = OpFunction %2 None %3 | 
 |          %11 = OpLabel | 
 |          %66 = OpVariable %39 Function | 
 |                OpStore %66 %48 | 
 |                OpBranch %67 | 
 |          %67 = OpLabel | 
 |         %226 = OpPhi %38 %48 %11 %76 %70 | 
 |                OpLoopMerge %69 %70 None | 
 |                OpBranch %71 | 
 |          %71 = OpLabel | 
 |          %74 = OpSLessThan %49 %226 %73 | 
 |                OpBranchConditional %74 %68 %69 | 
 |          %68 = OpLabel | 
 |                OpBranch %70 | 
 |          %70 = OpLabel | 
 |          %76 = OpIAdd %38 %226 %52 | 
 |                OpStore %66 %76 | 
 |                OpBranch %67 | 
 |          %69 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %12 = OpFunction %2 None %3 | 
 |          %13 = OpLabel | 
 |          %77 = OpVariable %39 Function | 
 |                OpStore %77 %62 | 
 |                OpBranch %78 | 
 |          %78 = OpLabel | 
 |         %227 = OpPhi %38 %62 %13 %87 %81 | 
 |                OpLoopMerge %80 %81 None | 
 |                OpBranch %82 | 
 |          %82 = OpLabel | 
 |          %85 = OpSLessThan %49 %227 %84 | 
 |                OpBranchConditional %85 %79 %80 | 
 |          %79 = OpLabel | 
 |                OpBranch %81 | 
 |          %81 = OpLabel | 
 |          %87 = OpIAdd %38 %227 %52 | 
 |                OpStore %77 %87 | 
 |                OpBranch %78 | 
 |          %80 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %14 = OpFunction %2 None %3 | 
 |          %15 = OpLabel | 
 |          %88 = OpVariable %39 Function | 
 |                OpStore %88 %41 | 
 |                OpBranch %89 | 
 |          %89 = OpLabel | 
 |         %228 = OpPhi %38 %41 %15 %97 %92 | 
 |                OpLoopMerge %91 %92 None | 
 |                OpBranch %93 | 
 |          %93 = OpLabel | 
 |          %95 = OpSLessThanEqual %49 %228 %48 | 
 |                OpBranchConditional %95 %90 %91 | 
 |          %90 = OpLabel | 
 |                OpBranch %92 | 
 |          %92 = OpLabel | 
 |          %97 = OpIAdd %38 %228 %52 | 
 |                OpStore %88 %97 | 
 |                OpBranch %89 | 
 |          %91 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %16 = OpFunction %2 None %3 | 
 |          %17 = OpLabel | 
 |          %98 = OpVariable %39 Function | 
 |                OpStore %98 %55 | 
 |                OpBranch %99 | 
 |          %99 = OpLabel | 
 |         %229 = OpPhi %38 %55 %17 %107 %102 | 
 |                OpLoopMerge %101 %102 None | 
 |                OpBranch %103 | 
 |         %103 = OpLabel | 
 |         %105 = OpSLessThanEqual %49 %229 %62 | 
 |                OpBranchConditional %105 %100 %101 | 
 |         %100 = OpLabel | 
 |                OpBranch %102 | 
 |         %102 = OpLabel | 
 |         %107 = OpIAdd %38 %229 %52 | 
 |                OpStore %98 %107 | 
 |                OpBranch %99 | 
 |         %101 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %18 = OpFunction %2 None %3 | 
 |          %19 = OpLabel | 
 |         %108 = OpVariable %39 Function | 
 |                OpStore %108 %48 | 
 |                OpBranch %109 | 
 |         %109 = OpLabel | 
 |         %230 = OpPhi %38 %48 %19 %117 %112 | 
 |                OpLoopMerge %111 %112 None | 
 |                OpBranch %113 | 
 |         %113 = OpLabel | 
 |         %115 = OpSLessThanEqual %49 %230 %73 | 
 |                OpBranchConditional %115 %110 %111 | 
 |         %110 = OpLabel | 
 |                OpBranch %112 | 
 |         %112 = OpLabel | 
 |         %117 = OpIAdd %38 %230 %52 | 
 |                OpStore %108 %117 | 
 |                OpBranch %109 | 
 |         %111 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %20 = OpFunction %2 None %3 | 
 |          %21 = OpLabel | 
 |         %118 = OpVariable %39 Function | 
 |                OpStore %118 %62 | 
 |                OpBranch %119 | 
 |         %119 = OpLabel | 
 |         %231 = OpPhi %38 %62 %21 %127 %122 | 
 |                OpLoopMerge %121 %122 None | 
 |                OpBranch %123 | 
 |         %123 = OpLabel | 
 |         %125 = OpSLessThanEqual %49 %231 %84 | 
 |                OpBranchConditional %125 %120 %121 | 
 |         %120 = OpLabel | 
 |                OpBranch %122 | 
 |         %122 = OpLabel | 
 |         %127 = OpIAdd %38 %231 %52 | 
 |                OpStore %118 %127 | 
 |                OpBranch %119 | 
 |         %121 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %22 = OpFunction %2 None %3 | 
 |          %23 = OpLabel | 
 |         %128 = OpVariable %39 Function | 
 |                OpStore %128 %48 | 
 |                OpBranch %129 | 
 |         %129 = OpLabel | 
 |         %232 = OpPhi %38 %48 %23 %137 %132 | 
 |                OpLoopMerge %131 %132 None | 
 |                OpBranch %133 | 
 |         %133 = OpLabel | 
 |         %135 = OpSGreaterThan %49 %232 %41 | 
 |                OpBranchConditional %135 %130 %131 | 
 |         %130 = OpLabel | 
 |                OpBranch %132 | 
 |         %132 = OpLabel | 
 |         %137 = OpISub %38 %232 %52 | 
 |                OpStore %128 %137 | 
 |                OpBranch %129 | 
 |         %131 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %24 = OpFunction %2 None %3 | 
 |          %25 = OpLabel | 
 |         %138 = OpVariable %39 Function | 
 |                OpStore %138 %62 | 
 |                OpBranch %139 | 
 |         %139 = OpLabel | 
 |         %233 = OpPhi %38 %62 %25 %147 %142 | 
 |                OpLoopMerge %141 %142 None | 
 |                OpBranch %143 | 
 |         %143 = OpLabel | 
 |         %145 = OpSGreaterThan %49 %233 %55 | 
 |                OpBranchConditional %145 %140 %141 | 
 |         %140 = OpLabel | 
 |                OpBranch %142 | 
 |         %142 = OpLabel | 
 |         %147 = OpISub %38 %233 %52 | 
 |                OpStore %138 %147 | 
 |                OpBranch %139 | 
 |         %141 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %26 = OpFunction %2 None %3 | 
 |          %27 = OpLabel | 
 |         %148 = OpVariable %39 Function | 
 |                OpStore %148 %73 | 
 |                OpBranch %149 | 
 |         %149 = OpLabel | 
 |         %234 = OpPhi %38 %73 %27 %157 %152 | 
 |                OpLoopMerge %151 %152 None | 
 |                OpBranch %153 | 
 |         %153 = OpLabel | 
 |         %155 = OpSGreaterThan %49 %234 %48 | 
 |                OpBranchConditional %155 %150 %151 | 
 |         %150 = OpLabel | 
 |                OpBranch %152 | 
 |         %152 = OpLabel | 
 |         %157 = OpISub %38 %234 %52 | 
 |                OpStore %148 %157 | 
 |                OpBranch %149 | 
 |         %151 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %28 = OpFunction %2 None %3 | 
 |          %29 = OpLabel | 
 |         %158 = OpVariable %39 Function | 
 |                OpStore %158 %84 | 
 |                OpBranch %159 | 
 |         %159 = OpLabel | 
 |         %235 = OpPhi %38 %84 %29 %167 %162 | 
 |                OpLoopMerge %161 %162 None | 
 |                OpBranch %163 | 
 |         %163 = OpLabel | 
 |         %165 = OpSGreaterThan %49 %235 %62 | 
 |                OpBranchConditional %165 %160 %161 | 
 |         %160 = OpLabel | 
 |                OpBranch %162 | 
 |         %162 = OpLabel | 
 |         %167 = OpISub %38 %235 %52 | 
 |                OpStore %158 %167 | 
 |                OpBranch %159 | 
 |         %161 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %30 = OpFunction %2 None %3 | 
 |          %31 = OpLabel | 
 |         %168 = OpVariable %39 Function | 
 |                OpStore %168 %48 | 
 |                OpBranch %169 | 
 |         %169 = OpLabel | 
 |         %236 = OpPhi %38 %48 %31 %177 %172 | 
 |                OpLoopMerge %171 %172 None | 
 |                OpBranch %173 | 
 |         %173 = OpLabel | 
 |         %175 = OpSGreaterThanEqual %49 %236 %41 | 
 |                OpBranchConditional %175 %170 %171 | 
 |         %170 = OpLabel | 
 |                OpBranch %172 | 
 |         %172 = OpLabel | 
 |         %177 = OpISub %38 %236 %52 | 
 |                OpStore %168 %177 | 
 |                OpBranch %169 | 
 |         %171 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %32 = OpFunction %2 None %3 | 
 |          %33 = OpLabel | 
 |         %178 = OpVariable %39 Function | 
 |                OpStore %178 %62 | 
 |                OpBranch %179 | 
 |         %179 = OpLabel | 
 |         %237 = OpPhi %38 %62 %33 %187 %182 | 
 |                OpLoopMerge %181 %182 None | 
 |                OpBranch %183 | 
 |         %183 = OpLabel | 
 |         %185 = OpSGreaterThanEqual %49 %237 %55 | 
 |                OpBranchConditional %185 %180 %181 | 
 |         %180 = OpLabel | 
 |                OpBranch %182 | 
 |         %182 = OpLabel | 
 |         %187 = OpISub %38 %237 %52 | 
 |                OpStore %178 %187 | 
 |                OpBranch %179 | 
 |         %181 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %34 = OpFunction %2 None %3 | 
 |          %35 = OpLabel | 
 |         %188 = OpVariable %39 Function | 
 |                OpStore %188 %73 | 
 |                OpBranch %189 | 
 |         %189 = OpLabel | 
 |         %238 = OpPhi %38 %73 %35 %197 %192 | 
 |                OpLoopMerge %191 %192 None | 
 |                OpBranch %193 | 
 |         %193 = OpLabel | 
 |         %195 = OpSGreaterThanEqual %49 %238 %48 | 
 |                OpBranchConditional %195 %190 %191 | 
 |         %190 = OpLabel | 
 |                OpBranch %192 | 
 |         %192 = OpLabel | 
 |         %197 = OpISub %38 %238 %52 | 
 |                OpStore %188 %197 | 
 |                OpBranch %189 | 
 |         %191 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %36 = OpFunction %2 None %3 | 
 |          %37 = OpLabel | 
 |         %198 = OpVariable %39 Function | 
 |                OpStore %198 %84 | 
 |                OpBranch %199 | 
 |         %199 = OpLabel | 
 |         %239 = OpPhi %38 %84 %37 %207 %202 | 
 |                OpLoopMerge %201 %202 None | 
 |                OpBranch %203 | 
 |         %203 = OpLabel | 
 |         %205 = OpSGreaterThanEqual %49 %239 %62 | 
 |                OpBranchConditional %205 %200 %201 | 
 |         %200 = OpLabel | 
 |                OpBranch %202 | 
 |         %202 = OpLabel | 
 |         %207 = OpISub %38 %239 %52 | 
 |                OpStore %198 %207 | 
 |                OpBranch %199 | 
 |         %201 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 | )"; | 
 |   std::unique_ptr<IRContext> context = | 
 |       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, | 
 |                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | 
 |   Module* module = context->module(); | 
 |   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | 
 |                              << text << std::endl; | 
 |   { | 
 |     // Function a | 
 |     const Function* f = spvtest::GetFunction(module, 6); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         -10); | 
 |     EXPECT_EQ( | 
 |         analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         -1); | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         10); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), | 
 |               analysis.GetScalarEvolution()->CreateConstant(-10)); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFinalTripInductionNode( | 
 |                   loop, analysis.GetScalarEvolution()->CreateConstant(1)), | 
 |               analysis.GetScalarEvolution()->CreateConstant(-1)); | 
 |   } | 
 |   { | 
 |     // Function b | 
 |     const Function* f = spvtest::GetFunction(module, 8); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         -5); | 
 |     EXPECT_EQ( | 
 |         analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         4); | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         10); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), | 
 |               analysis.GetScalarEvolution()->CreateConstant(-5)); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFinalTripInductionNode( | 
 |                   loop, analysis.GetScalarEvolution()->CreateConstant(1)), | 
 |               analysis.GetScalarEvolution()->CreateConstant(4)); | 
 |   } | 
 |   { | 
 |     // Function c | 
 |     const Function* f = spvtest::GetFunction(module, 10); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         0); | 
 |     EXPECT_EQ( | 
 |         analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         9); | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         10); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), | 
 |               analysis.GetScalarEvolution()->CreateConstant(0)); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFinalTripInductionNode( | 
 |                   loop, analysis.GetScalarEvolution()->CreateConstant(1)), | 
 |               analysis.GetScalarEvolution()->CreateConstant(9)); | 
 |   } | 
 |   { | 
 |     // Function d | 
 |     const Function* f = spvtest::GetFunction(module, 12); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         5); | 
 |     EXPECT_EQ( | 
 |         analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         14); | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         10); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), | 
 |               analysis.GetScalarEvolution()->CreateConstant(5)); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFinalTripInductionNode( | 
 |                   loop, analysis.GetScalarEvolution()->CreateConstant(1)), | 
 |               analysis.GetScalarEvolution()->CreateConstant(14)); | 
 |   } | 
 |   { | 
 |     // Function e | 
 |     const Function* f = spvtest::GetFunction(module, 14); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         -10); | 
 |     EXPECT_EQ( | 
 |         analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         0); | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         11); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), | 
 |               analysis.GetScalarEvolution()->CreateConstant(-10)); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFinalTripInductionNode( | 
 |                   loop, analysis.GetScalarEvolution()->CreateConstant(1)), | 
 |               analysis.GetScalarEvolution()->CreateConstant(0)); | 
 |   } | 
 |   { | 
 |     // Function f | 
 |     const Function* f = spvtest::GetFunction(module, 16); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         -5); | 
 |     EXPECT_EQ( | 
 |         analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         5); | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         11); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), | 
 |               analysis.GetScalarEvolution()->CreateConstant(-5)); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFinalTripInductionNode( | 
 |                   loop, analysis.GetScalarEvolution()->CreateConstant(1)), | 
 |               analysis.GetScalarEvolution()->CreateConstant(5)); | 
 |   } | 
 |   { | 
 |     // Function g | 
 |     const Function* f = spvtest::GetFunction(module, 18); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         0); | 
 |     EXPECT_EQ( | 
 |         analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         10); | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         11); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), | 
 |               analysis.GetScalarEvolution()->CreateConstant(0)); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFinalTripInductionNode( | 
 |                   loop, analysis.GetScalarEvolution()->CreateConstant(1)), | 
 |               analysis.GetScalarEvolution()->CreateConstant(10)); | 
 |   } | 
 |   { | 
 |     // Function h | 
 |     const Function* f = spvtest::GetFunction(module, 20); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         5); | 
 |     EXPECT_EQ( | 
 |         analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         15); | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         11); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), | 
 |               analysis.GetScalarEvolution()->CreateConstant(5)); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFinalTripInductionNode( | 
 |                   loop, analysis.GetScalarEvolution()->CreateConstant(1)), | 
 |               analysis.GetScalarEvolution()->CreateConstant(15)); | 
 |   } | 
 |   { | 
 |     // Function i | 
 |     const Function* f = spvtest::GetFunction(module, 22); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         0); | 
 |     EXPECT_EQ( | 
 |         analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         -9); | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         10); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), | 
 |               analysis.GetScalarEvolution()->CreateConstant(0)); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFinalTripInductionNode( | 
 |                   loop, analysis.GetScalarEvolution()->CreateConstant(-1)), | 
 |               analysis.GetScalarEvolution()->CreateConstant(-9)); | 
 |   } | 
 |   { | 
 |     // Function j | 
 |     const Function* f = spvtest::GetFunction(module, 24); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         5); | 
 |     EXPECT_EQ( | 
 |         analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         -4); | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         10); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), | 
 |               analysis.GetScalarEvolution()->CreateConstant(5)); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFinalTripInductionNode( | 
 |                   loop, analysis.GetScalarEvolution()->CreateConstant(-1)), | 
 |               analysis.GetScalarEvolution()->CreateConstant(-4)); | 
 |   } | 
 |   { | 
 |     // Function k | 
 |     const Function* f = spvtest::GetFunction(module, 26); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         10); | 
 |     EXPECT_EQ( | 
 |         analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         1); | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         10); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), | 
 |               analysis.GetScalarEvolution()->CreateConstant(10)); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFinalTripInductionNode( | 
 |                   loop, analysis.GetScalarEvolution()->CreateConstant(-1)), | 
 |               analysis.GetScalarEvolution()->CreateConstant(1)); | 
 |   } | 
 |   { | 
 |     // Function l | 
 |     const Function* f = spvtest::GetFunction(module, 28); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         15); | 
 |     EXPECT_EQ( | 
 |         analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         6); | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         10); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), | 
 |               analysis.GetScalarEvolution()->CreateConstant(15)); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFinalTripInductionNode( | 
 |                   loop, analysis.GetScalarEvolution()->CreateConstant(-1)), | 
 |               analysis.GetScalarEvolution()->CreateConstant(6)); | 
 |   } | 
 |   { | 
 |     // Function m | 
 |     const Function* f = spvtest::GetFunction(module, 30); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         0); | 
 |     EXPECT_EQ( | 
 |         analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         -10); | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         11); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), | 
 |               analysis.GetScalarEvolution()->CreateConstant(0)); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFinalTripInductionNode( | 
 |                   loop, analysis.GetScalarEvolution()->CreateConstant(-1)), | 
 |               analysis.GetScalarEvolution()->CreateConstant(-10)); | 
 |   } | 
 |   { | 
 |     // Function n | 
 |     const Function* f = spvtest::GetFunction(module, 32); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         5); | 
 |     EXPECT_EQ( | 
 |         analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         -5); | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         11); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), | 
 |               analysis.GetScalarEvolution()->CreateConstant(5)); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFinalTripInductionNode( | 
 |                   loop, analysis.GetScalarEvolution()->CreateConstant(-1)), | 
 |               analysis.GetScalarEvolution()->CreateConstant(-5)); | 
 |   } | 
 |   { | 
 |     // Function o | 
 |     const Function* f = spvtest::GetFunction(module, 34); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         10); | 
 |     EXPECT_EQ( | 
 |         analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         0); | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         11); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), | 
 |               analysis.GetScalarEvolution()->CreateConstant(10)); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFinalTripInductionNode( | 
 |                   loop, analysis.GetScalarEvolution()->CreateConstant(-1)), | 
 |               analysis.GetScalarEvolution()->CreateConstant(0)); | 
 |   } | 
 |   { | 
 |     // Function p | 
 |     const Function* f = spvtest::GetFunction(module, 36); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         15); | 
 |     EXPECT_EQ( | 
 |         analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         5); | 
 |  | 
 |     EXPECT_EQ( | 
 |         analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), | 
 |         11); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), | 
 |               analysis.GetScalarEvolution()->CreateConstant(15)); | 
 |  | 
 |     EXPECT_EQ(analysis.GetFinalTripInductionNode( | 
 |                   loop, analysis.GetScalarEvolution()->CreateConstant(-1)), | 
 |               analysis.GetScalarEvolution()->CreateConstant(5)); | 
 |   } | 
 | } | 
 |  | 
 | /* | 
 |   Generated from the following GLSL fragment shader | 
 |   with --eliminate-local-multi-store | 
 | #version 440 core | 
 | void main(){ | 
 |   for (int i = 0; i < 10; i++) { | 
 |  | 
 |   } | 
 | } | 
 | */ | 
 | TEST(DependencyAnalysisHelpers, bounds_checks) { | 
 |   const std::string text = R"(               OpCapability Shader | 
 |           %1 = OpExtInstImport "GLSL.std.450" | 
 |                OpMemoryModel Logical GLSL450 | 
 |                OpEntryPoint Fragment %4 "main" | 
 |                OpExecutionMode %4 OriginUpperLeft | 
 |                OpSource GLSL 440 | 
 |                OpName %4 "main" | 
 |                OpName %8 "i" | 
 |           %2 = OpTypeVoid | 
 |           %3 = OpTypeFunction %2 | 
 |           %6 = OpTypeInt 32 1 | 
 |           %7 = OpTypePointer Function %6 | 
 |           %9 = OpConstant %6 0 | 
 |          %16 = OpConstant %6 10 | 
 |          %17 = OpTypeBool | 
 |          %20 = OpConstant %6 1 | 
 |           %4 = OpFunction %2 None %3 | 
 |           %5 = OpLabel | 
 |           %8 = OpVariable %7 Function | 
 |                OpStore %8 %9 | 
 |                OpBranch %10 | 
 |          %10 = OpLabel | 
 |          %22 = OpPhi %6 %9 %5 %21 %13 | 
 |                OpLoopMerge %12 %13 None | 
 |                OpBranch %14 | 
 |          %14 = OpLabel | 
 |          %18 = OpSLessThan %17 %22 %16 | 
 |                OpBranchConditional %18 %11 %12 | 
 |          %11 = OpLabel | 
 |                OpBranch %13 | 
 |          %13 = OpLabel | 
 |          %21 = OpIAdd %6 %22 %20 | 
 |                OpStore %8 %21 | 
 |                OpBranch %10 | 
 |          %12 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 | )"; | 
 |   std::unique_ptr<IRContext> context = | 
 |       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, | 
 |                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | 
 |   Module* module = context->module(); | 
 |   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | 
 |                              << text << std::endl; | 
 |   // We need a shader that includes a loop for this test so we can build a | 
 |   // LoopDependenceAnalaysis | 
 |   const Function* f = spvtest::GetFunction(module, 4); | 
 |   LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |   Loop* loop = &ld.GetLoopByIndex(0); | 
 |   std::vector<const Loop*> loops{loop}; | 
 |   LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |   EXPECT_TRUE(analysis.IsWithinBounds(0, 0, 0)); | 
 |   EXPECT_TRUE(analysis.IsWithinBounds(0, -1, 0)); | 
 |   EXPECT_TRUE(analysis.IsWithinBounds(0, 0, 1)); | 
 |   EXPECT_TRUE(analysis.IsWithinBounds(0, -1, 1)); | 
 |   EXPECT_TRUE(analysis.IsWithinBounds(-2, -2, -2)); | 
 |   EXPECT_TRUE(analysis.IsWithinBounds(-2, -3, 0)); | 
 |   EXPECT_TRUE(analysis.IsWithinBounds(-2, 0, -3)); | 
 |   EXPECT_TRUE(analysis.IsWithinBounds(2, 2, 2)); | 
 |   EXPECT_TRUE(analysis.IsWithinBounds(2, 3, 0)); | 
 |  | 
 |   EXPECT_FALSE(analysis.IsWithinBounds(2, 3, 3)); | 
 |   EXPECT_FALSE(analysis.IsWithinBounds(0, 1, 5)); | 
 |   EXPECT_FALSE(analysis.IsWithinBounds(0, -1, -4)); | 
 |   EXPECT_FALSE(analysis.IsWithinBounds(-2, -4, -3)); | 
 | } | 
 |  | 
 | /* | 
 |   Generated from the following GLSL fragment shader | 
 |   with --eliminate-local-multi-store | 
 | #version 440 core | 
 | layout(location = 0) in vec4 in_vec; | 
 | // Loop iterates from constant to symbolic | 
 | void a() { | 
 |   int N = int(in_vec.x); | 
 |   int arr[10]; | 
 |   for (int i = 0; i < N; i++) { // Bounds are N - 0 - 1 | 
 |     arr[i] = arr[i+N]; // |distance| = N | 
 |     arr[i+N] = arr[i]; // |distance| = N | 
 |   } | 
 | } | 
 | void b() { | 
 |   int N = int(in_vec.x); | 
 |   int arr[10]; | 
 |   for (int i = 0; i <= N; i++) { // Bounds are N - 0 | 
 |     arr[i] = arr[i+N]; // |distance| = N | 
 |     arr[i+N] = arr[i]; // |distance| = N | 
 |   } | 
 | } | 
 | void c() { | 
 |   int N = int(in_vec.x); | 
 |   int arr[10]; | 
 |   for (int i = 9; i > N; i--) { // Bounds are 9 - N - 1 | 
 |     arr[i] = arr[i+N]; // |distance| = N | 
 |     arr[i+N] = arr[i]; // |distance| = N | 
 |   } | 
 | } | 
 | void d() { | 
 |   int N = int(in_vec.x); | 
 |   int arr[10]; | 
 |   for (int i = 9; i >= N; i--) { // Bounds are 9 - N | 
 |     arr[i] = arr[i+N]; // |distance| = N | 
 |     arr[i+N] = arr[i]; // |distance| = N | 
 |   } | 
 | } | 
 | void main(){ | 
 |   a(); | 
 |   b(); | 
 |   c(); | 
 |   d(); | 
 | } | 
 | */ | 
 | TEST(DependencyAnalysisHelpers, const_to_symbolic) { | 
 |   const std::string text = R"(               OpCapability Shader | 
 |           %1 = OpExtInstImport "GLSL.std.450" | 
 |                OpMemoryModel Logical GLSL450 | 
 |                OpEntryPoint Fragment %4 "main" %20 | 
 |                OpExecutionMode %4 OriginUpperLeft | 
 |                OpSource GLSL 440 | 
 |                OpName %4 "main" | 
 |                OpName %6 "a(" | 
 |                OpName %8 "b(" | 
 |                OpName %10 "c(" | 
 |                OpName %12 "d(" | 
 |                OpName %16 "N" | 
 |                OpName %20 "in_vec" | 
 |                OpName %27 "i" | 
 |                OpName %41 "arr" | 
 |                OpName %59 "N" | 
 |                OpName %63 "i" | 
 |                OpName %72 "arr" | 
 |                OpName %89 "N" | 
 |                OpName %93 "i" | 
 |                OpName %103 "arr" | 
 |                OpName %120 "N" | 
 |                OpName %124 "i" | 
 |                OpName %133 "arr" | 
 |                OpDecorate %20 Location 0 | 
 |           %2 = OpTypeVoid | 
 |           %3 = OpTypeFunction %2 | 
 |          %14 = OpTypeInt 32 1 | 
 |          %15 = OpTypePointer Function %14 | 
 |          %17 = OpTypeFloat 32 | 
 |          %18 = OpTypeVector %17 4 | 
 |          %19 = OpTypePointer Input %18 | 
 |          %20 = OpVariable %19 Input | 
 |          %21 = OpTypeInt 32 0 | 
 |          %22 = OpConstant %21 0 | 
 |          %23 = OpTypePointer Input %17 | 
 |          %28 = OpConstant %14 0 | 
 |          %36 = OpTypeBool | 
 |          %38 = OpConstant %21 10 | 
 |          %39 = OpTypeArray %14 %38 | 
 |          %40 = OpTypePointer Function %39 | 
 |          %57 = OpConstant %14 1 | 
 |          %94 = OpConstant %14 9 | 
 |           %4 = OpFunction %2 None %3 | 
 |           %5 = OpLabel | 
 |         %150 = OpFunctionCall %2 %6 | 
 |         %151 = OpFunctionCall %2 %8 | 
 |         %152 = OpFunctionCall %2 %10 | 
 |         %153 = OpFunctionCall %2 %12 | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |           %6 = OpFunction %2 None %3 | 
 |           %7 = OpLabel | 
 |          %16 = OpVariable %15 Function | 
 |          %27 = OpVariable %15 Function | 
 |          %41 = OpVariable %40 Function | 
 |          %24 = OpAccessChain %23 %20 %22 | 
 |          %25 = OpLoad %17 %24 | 
 |          %26 = OpConvertFToS %14 %25 | 
 |                OpStore %16 %26 | 
 |                OpStore %27 %28 | 
 |                OpBranch %29 | 
 |          %29 = OpLabel | 
 |         %154 = OpPhi %14 %28 %7 %58 %32 | 
 |                OpLoopMerge %31 %32 None | 
 |                OpBranch %33 | 
 |          %33 = OpLabel | 
 |          %37 = OpSLessThan %36 %154 %26 | 
 |                OpBranchConditional %37 %30 %31 | 
 |          %30 = OpLabel | 
 |          %45 = OpIAdd %14 %154 %26 | 
 |          %46 = OpAccessChain %15 %41 %45 | 
 |          %47 = OpLoad %14 %46 | 
 |          %48 = OpAccessChain %15 %41 %154 | 
 |                OpStore %48 %47 | 
 |          %51 = OpIAdd %14 %154 %26 | 
 |          %53 = OpAccessChain %15 %41 %154 | 
 |          %54 = OpLoad %14 %53 | 
 |          %55 = OpAccessChain %15 %41 %51 | 
 |                OpStore %55 %54 | 
 |                OpBranch %32 | 
 |          %32 = OpLabel | 
 |          %58 = OpIAdd %14 %154 %57 | 
 |                OpStore %27 %58 | 
 |                OpBranch %29 | 
 |          %31 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |           %8 = OpFunction %2 None %3 | 
 |           %9 = OpLabel | 
 |          %59 = OpVariable %15 Function | 
 |          %63 = OpVariable %15 Function | 
 |          %72 = OpVariable %40 Function | 
 |          %60 = OpAccessChain %23 %20 %22 | 
 |          %61 = OpLoad %17 %60 | 
 |          %62 = OpConvertFToS %14 %61 | 
 |                OpStore %59 %62 | 
 |                OpStore %63 %28 | 
 |                OpBranch %64 | 
 |          %64 = OpLabel | 
 |         %155 = OpPhi %14 %28 %9 %88 %67 | 
 |                OpLoopMerge %66 %67 None | 
 |                OpBranch %68 | 
 |          %68 = OpLabel | 
 |          %71 = OpSLessThanEqual %36 %155 %62 | 
 |                OpBranchConditional %71 %65 %66 | 
 |          %65 = OpLabel | 
 |          %76 = OpIAdd %14 %155 %62 | 
 |          %77 = OpAccessChain %15 %72 %76 | 
 |          %78 = OpLoad %14 %77 | 
 |          %79 = OpAccessChain %15 %72 %155 | 
 |                OpStore %79 %78 | 
 |          %82 = OpIAdd %14 %155 %62 | 
 |          %84 = OpAccessChain %15 %72 %155 | 
 |          %85 = OpLoad %14 %84 | 
 |          %86 = OpAccessChain %15 %72 %82 | 
 |                OpStore %86 %85 | 
 |                OpBranch %67 | 
 |          %67 = OpLabel | 
 |          %88 = OpIAdd %14 %155 %57 | 
 |                OpStore %63 %88 | 
 |                OpBranch %64 | 
 |          %66 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %10 = OpFunction %2 None %3 | 
 |          %11 = OpLabel | 
 |          %89 = OpVariable %15 Function | 
 |          %93 = OpVariable %15 Function | 
 |         %103 = OpVariable %40 Function | 
 |          %90 = OpAccessChain %23 %20 %22 | 
 |          %91 = OpLoad %17 %90 | 
 |          %92 = OpConvertFToS %14 %91 | 
 |                OpStore %89 %92 | 
 |                OpStore %93 %94 | 
 |                OpBranch %95 | 
 |          %95 = OpLabel | 
 |         %156 = OpPhi %14 %94 %11 %119 %98 | 
 |                OpLoopMerge %97 %98 None | 
 |                OpBranch %99 | 
 |          %99 = OpLabel | 
 |         %102 = OpSGreaterThan %36 %156 %92 | 
 |                OpBranchConditional %102 %96 %97 | 
 |          %96 = OpLabel | 
 |         %107 = OpIAdd %14 %156 %92 | 
 |         %108 = OpAccessChain %15 %103 %107 | 
 |         %109 = OpLoad %14 %108 | 
 |         %110 = OpAccessChain %15 %103 %156 | 
 |                OpStore %110 %109 | 
 |         %113 = OpIAdd %14 %156 %92 | 
 |         %115 = OpAccessChain %15 %103 %156 | 
 |         %116 = OpLoad %14 %115 | 
 |         %117 = OpAccessChain %15 %103 %113 | 
 |                OpStore %117 %116 | 
 |                OpBranch %98 | 
 |          %98 = OpLabel | 
 |         %119 = OpISub %14 %156 %57 | 
 |                OpStore %93 %119 | 
 |                OpBranch %95 | 
 |          %97 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %12 = OpFunction %2 None %3 | 
 |          %13 = OpLabel | 
 |         %120 = OpVariable %15 Function | 
 |         %124 = OpVariable %15 Function | 
 |         %133 = OpVariable %40 Function | 
 |         %121 = OpAccessChain %23 %20 %22 | 
 |         %122 = OpLoad %17 %121 | 
 |         %123 = OpConvertFToS %14 %122 | 
 |                OpStore %120 %123 | 
 |                OpStore %124 %94 | 
 |                OpBranch %125 | 
 |         %125 = OpLabel | 
 |         %157 = OpPhi %14 %94 %13 %149 %128 | 
 |                OpLoopMerge %127 %128 None | 
 |                OpBranch %129 | 
 |         %129 = OpLabel | 
 |         %132 = OpSGreaterThanEqual %36 %157 %123 | 
 |                OpBranchConditional %132 %126 %127 | 
 |         %126 = OpLabel | 
 |         %137 = OpIAdd %14 %157 %123 | 
 |         %138 = OpAccessChain %15 %133 %137 | 
 |         %139 = OpLoad %14 %138 | 
 |         %140 = OpAccessChain %15 %133 %157 | 
 |                OpStore %140 %139 | 
 |         %143 = OpIAdd %14 %157 %123 | 
 |         %145 = OpAccessChain %15 %133 %157 | 
 |         %146 = OpLoad %14 %145 | 
 |         %147 = OpAccessChain %15 %133 %143 | 
 |                OpStore %147 %146 | 
 |                OpBranch %128 | 
 |         %128 = OpLabel | 
 |         %149 = OpISub %14 %157 %57 | 
 |                OpStore %124 %149 | 
 |                OpBranch %125 | 
 |         %127 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 | )"; | 
 |   std::unique_ptr<IRContext> context = | 
 |       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, | 
 |                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | 
 |   Module* module = context->module(); | 
 |   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | 
 |                              << text << std::endl; | 
 |  | 
 |   { | 
 |     // Function a | 
 |     const Function* f = spvtest::GetFunction(module, 6); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     const Instruction* stores[2]; | 
 |     int stores_found = 0; | 
 |     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 30)) { | 
 |       if (inst.opcode() == SpvOp::SpvOpStore) { | 
 |         stores[stores_found] = &inst; | 
 |         ++stores_found; | 
 |       } | 
 |     } | 
 |  | 
 |     for (int i = 0; i < 2; ++i) { | 
 |       EXPECT_TRUE(stores[i]); | 
 |     } | 
 |  | 
 |     // 47 -> 48 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(47) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[0]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       // Independent and supported. | 
 |       EXPECT_TRUE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |  | 
 |     // 54 -> 55 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(54) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[1]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       // Independent but not supported. | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |   } | 
 |   { | 
 |     // Function b | 
 |     const Function* f = spvtest::GetFunction(module, 8); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     const Instruction* stores[2]; | 
 |     int stores_found = 0; | 
 |     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 65)) { | 
 |       if (inst.opcode() == SpvOp::SpvOpStore) { | 
 |         stores[stores_found] = &inst; | 
 |         ++stores_found; | 
 |       } | 
 |     } | 
 |  | 
 |     for (int i = 0; i < 2; ++i) { | 
 |       EXPECT_TRUE(stores[i]); | 
 |     } | 
 |  | 
 |     // 78 -> 79 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(78) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[0]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       // Dependent. | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |  | 
 |     // 85 -> 86 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(85) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[1]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       // Dependent. | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |   } | 
 |   { | 
 |     // Function c | 
 |     const Function* f = spvtest::GetFunction(module, 10); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     const Instruction* stores[2]; | 
 |     int stores_found = 0; | 
 |     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 96)) { | 
 |       if (inst.opcode() == SpvOp::SpvOpStore) { | 
 |         stores[stores_found] = &inst; | 
 |         ++stores_found; | 
 |       } | 
 |     } | 
 |  | 
 |     for (int i = 0; i < 2; ++i) { | 
 |       EXPECT_TRUE(stores[i]); | 
 |     } | 
 |  | 
 |     // 109 -> 110 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(109) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[0]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       // Independent but not supported. | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |  | 
 |     // 116 -> 117 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(116) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[1]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       // Independent but not supported. | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |   } | 
 |   { | 
 |     // Function d | 
 |     const Function* f = spvtest::GetFunction(module, 12); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     const Instruction* stores[2]; | 
 |     int stores_found = 0; | 
 |     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 126)) { | 
 |       if (inst.opcode() == SpvOp::SpvOpStore) { | 
 |         stores[stores_found] = &inst; | 
 |         ++stores_found; | 
 |       } | 
 |     } | 
 |  | 
 |     for (int i = 0; i < 2; ++i) { | 
 |       EXPECT_TRUE(stores[i]); | 
 |     } | 
 |  | 
 |     // 139 -> 140 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(139) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[0]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       // Dependent. | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |  | 
 |     // 146 -> 147 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(146) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[1]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       // Dependent. | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | /* | 
 |   Generated from the following GLSL fragment shader | 
 |   with --eliminate-local-multi-store | 
 | #version 440 core | 
 | layout(location = 0) in vec4 in_vec; | 
 | // Loop iterates from symbolic to constant | 
 | void a() { | 
 |   int N = int(in_vec.x); | 
 |   int arr[10]; | 
 |   for (int i = N; i < 9; i++) { // Bounds are 9 - N - 1 | 
 |     arr[i] = arr[i+N]; // |distance| = N | 
 |     arr[i+N] = arr[i]; // |distance| = N | 
 |   } | 
 | } | 
 | void b() { | 
 |   int N = int(in_vec.x); | 
 |   int arr[10]; | 
 |   for (int i = N; i <= 9; i++) { // Bounds are 9 - N | 
 |     arr[i] = arr[i+N]; // |distance| = N | 
 |     arr[i+N] = arr[i]; // |distance| = N | 
 |   } | 
 | } | 
 | void c() { | 
 |   int N = int(in_vec.x); | 
 |   int arr[10]; | 
 |   for (int i = N; i > 0; i--) { // Bounds are N - 0 - 1 | 
 |     arr[i] = arr[i+N]; // |distance| = N | 
 |     arr[i+N] = arr[i]; // |distance| = N | 
 |   } | 
 | } | 
 | void d() { | 
 |   int N = int(in_vec.x); | 
 |   int arr[10]; | 
 |   for (int i = N; i >= 0; i--) { // Bounds are N - 0 | 
 |     arr[i] = arr[i+N]; // |distance| = N | 
 |     arr[i+N] = arr[i]; // |distance| = N | 
 |   } | 
 | } | 
 | void main(){ | 
 |   a(); | 
 |   b(); | 
 |   c(); | 
 |   d(); | 
 | } | 
 | */ | 
 | TEST(DependencyAnalysisHelpers, symbolic_to_const) { | 
 |   const std::string text = R"(               OpCapability Shader | 
 |           %1 = OpExtInstImport "GLSL.std.450" | 
 |                OpMemoryModel Logical GLSL450 | 
 |                OpEntryPoint Fragment %4 "main" %20 | 
 |                OpExecutionMode %4 OriginUpperLeft | 
 |                OpSource GLSL 440 | 
 |                OpName %4 "main" | 
 |                OpName %6 "a(" | 
 |                OpName %8 "b(" | 
 |                OpName %10 "c(" | 
 |                OpName %12 "d(" | 
 |                OpName %16 "N" | 
 |                OpName %20 "in_vec" | 
 |                OpName %27 "i" | 
 |                OpName %41 "arr" | 
 |                OpName %59 "N" | 
 |                OpName %63 "i" | 
 |                OpName %72 "arr" | 
 |                OpName %89 "N" | 
 |                OpName %93 "i" | 
 |                OpName %103 "arr" | 
 |                OpName %120 "N" | 
 |                OpName %124 "i" | 
 |                OpName %133 "arr" | 
 |                OpDecorate %20 Location 0 | 
 |           %2 = OpTypeVoid | 
 |           %3 = OpTypeFunction %2 | 
 |          %14 = OpTypeInt 32 1 | 
 |          %15 = OpTypePointer Function %14 | 
 |          %17 = OpTypeFloat 32 | 
 |          %18 = OpTypeVector %17 4 | 
 |          %19 = OpTypePointer Input %18 | 
 |          %20 = OpVariable %19 Input | 
 |          %21 = OpTypeInt 32 0 | 
 |          %22 = OpConstant %21 0 | 
 |          %23 = OpTypePointer Input %17 | 
 |          %35 = OpConstant %14 9 | 
 |          %36 = OpTypeBool | 
 |          %38 = OpConstant %21 10 | 
 |          %39 = OpTypeArray %14 %38 | 
 |          %40 = OpTypePointer Function %39 | 
 |          %57 = OpConstant %14 1 | 
 |         %101 = OpConstant %14 0 | 
 |           %4 = OpFunction %2 None %3 | 
 |           %5 = OpLabel | 
 |         %150 = OpFunctionCall %2 %6 | 
 |         %151 = OpFunctionCall %2 %8 | 
 |         %152 = OpFunctionCall %2 %10 | 
 |         %153 = OpFunctionCall %2 %12 | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |           %6 = OpFunction %2 None %3 | 
 |           %7 = OpLabel | 
 |          %16 = OpVariable %15 Function | 
 |          %27 = OpVariable %15 Function | 
 |          %41 = OpVariable %40 Function | 
 |          %24 = OpAccessChain %23 %20 %22 | 
 |          %25 = OpLoad %17 %24 | 
 |          %26 = OpConvertFToS %14 %25 | 
 |                OpStore %16 %26 | 
 |                OpStore %27 %26 | 
 |                OpBranch %29 | 
 |          %29 = OpLabel | 
 |         %154 = OpPhi %14 %26 %7 %58 %32 | 
 |                OpLoopMerge %31 %32 None | 
 |                OpBranch %33 | 
 |          %33 = OpLabel | 
 |          %37 = OpSLessThan %36 %154 %35 | 
 |                OpBranchConditional %37 %30 %31 | 
 |          %30 = OpLabel | 
 |          %45 = OpIAdd %14 %154 %26 | 
 |          %46 = OpAccessChain %15 %41 %45 | 
 |          %47 = OpLoad %14 %46 | 
 |          %48 = OpAccessChain %15 %41 %154 | 
 |                OpStore %48 %47 | 
 |          %51 = OpIAdd %14 %154 %26 | 
 |          %53 = OpAccessChain %15 %41 %154 | 
 |          %54 = OpLoad %14 %53 | 
 |          %55 = OpAccessChain %15 %41 %51 | 
 |                OpStore %55 %54 | 
 |                OpBranch %32 | 
 |          %32 = OpLabel | 
 |          %58 = OpIAdd %14 %154 %57 | 
 |                OpStore %27 %58 | 
 |                OpBranch %29 | 
 |          %31 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |           %8 = OpFunction %2 None %3 | 
 |           %9 = OpLabel | 
 |          %59 = OpVariable %15 Function | 
 |          %63 = OpVariable %15 Function | 
 |          %72 = OpVariable %40 Function | 
 |          %60 = OpAccessChain %23 %20 %22 | 
 |          %61 = OpLoad %17 %60 | 
 |          %62 = OpConvertFToS %14 %61 | 
 |                OpStore %59 %62 | 
 |                OpStore %63 %62 | 
 |                OpBranch %65 | 
 |          %65 = OpLabel | 
 |         %155 = OpPhi %14 %62 %9 %88 %68 | 
 |                OpLoopMerge %67 %68 None | 
 |                OpBranch %69 | 
 |          %69 = OpLabel | 
 |          %71 = OpSLessThanEqual %36 %155 %35 | 
 |                OpBranchConditional %71 %66 %67 | 
 |          %66 = OpLabel | 
 |          %76 = OpIAdd %14 %155 %62 | 
 |          %77 = OpAccessChain %15 %72 %76 | 
 |          %78 = OpLoad %14 %77 | 
 |          %79 = OpAccessChain %15 %72 %155 | 
 |                OpStore %79 %78 | 
 |          %82 = OpIAdd %14 %155 %62 | 
 |          %84 = OpAccessChain %15 %72 %155 | 
 |          %85 = OpLoad %14 %84 | 
 |          %86 = OpAccessChain %15 %72 %82 | 
 |                OpStore %86 %85 | 
 |                OpBranch %68 | 
 |          %68 = OpLabel | 
 |          %88 = OpIAdd %14 %155 %57 | 
 |                OpStore %63 %88 | 
 |                OpBranch %65 | 
 |          %67 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %10 = OpFunction %2 None %3 | 
 |          %11 = OpLabel | 
 |          %89 = OpVariable %15 Function | 
 |          %93 = OpVariable %15 Function | 
 |         %103 = OpVariable %40 Function | 
 |          %90 = OpAccessChain %23 %20 %22 | 
 |          %91 = OpLoad %17 %90 | 
 |          %92 = OpConvertFToS %14 %91 | 
 |                OpStore %89 %92 | 
 |                OpStore %93 %92 | 
 |                OpBranch %95 | 
 |          %95 = OpLabel | 
 |         %156 = OpPhi %14 %92 %11 %119 %98 | 
 |                OpLoopMerge %97 %98 None | 
 |                OpBranch %99 | 
 |          %99 = OpLabel | 
 |         %102 = OpSGreaterThan %36 %156 %101 | 
 |                OpBranchConditional %102 %96 %97 | 
 |          %96 = OpLabel | 
 |         %107 = OpIAdd %14 %156 %92 | 
 |         %108 = OpAccessChain %15 %103 %107 | 
 |         %109 = OpLoad %14 %108 | 
 |         %110 = OpAccessChain %15 %103 %156 | 
 |                OpStore %110 %109 | 
 |         %113 = OpIAdd %14 %156 %92 | 
 |         %115 = OpAccessChain %15 %103 %156 | 
 |         %116 = OpLoad %14 %115 | 
 |         %117 = OpAccessChain %15 %103 %113 | 
 |                OpStore %117 %116 | 
 |                OpBranch %98 | 
 |          %98 = OpLabel | 
 |         %119 = OpISub %14 %156 %57 | 
 |                OpStore %93 %119 | 
 |                OpBranch %95 | 
 |          %97 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %12 = OpFunction %2 None %3 | 
 |          %13 = OpLabel | 
 |         %120 = OpVariable %15 Function | 
 |         %124 = OpVariable %15 Function | 
 |         %133 = OpVariable %40 Function | 
 |         %121 = OpAccessChain %23 %20 %22 | 
 |         %122 = OpLoad %17 %121 | 
 |         %123 = OpConvertFToS %14 %122 | 
 |                OpStore %120 %123 | 
 |                OpStore %124 %123 | 
 |                OpBranch %126 | 
 |         %126 = OpLabel | 
 |         %157 = OpPhi %14 %123 %13 %149 %129 | 
 |                OpLoopMerge %128 %129 None | 
 |                OpBranch %130 | 
 |         %130 = OpLabel | 
 |         %132 = OpSGreaterThanEqual %36 %157 %101 | 
 |                OpBranchConditional %132 %127 %128 | 
 |         %127 = OpLabel | 
 |         %137 = OpIAdd %14 %157 %123 | 
 |         %138 = OpAccessChain %15 %133 %137 | 
 |         %139 = OpLoad %14 %138 | 
 |         %140 = OpAccessChain %15 %133 %157 | 
 |                OpStore %140 %139 | 
 |         %143 = OpIAdd %14 %157 %123 | 
 |         %145 = OpAccessChain %15 %133 %157 | 
 |         %146 = OpLoad %14 %145 | 
 |         %147 = OpAccessChain %15 %133 %143 | 
 |                OpStore %147 %146 | 
 |                OpBranch %129 | 
 |         %129 = OpLabel | 
 |         %149 = OpISub %14 %157 %57 | 
 |                OpStore %124 %149 | 
 |                OpBranch %126 | 
 |         %128 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 | )"; | 
 |   std::unique_ptr<IRContext> context = | 
 |       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, | 
 |                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | 
 |   Module* module = context->module(); | 
 |   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | 
 |                              << text << std::endl; | 
 |   { | 
 |     // Function a | 
 |     const Function* f = spvtest::GetFunction(module, 6); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     const Instruction* stores[2]; | 
 |     int stores_found = 0; | 
 |     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 30)) { | 
 |       if (inst.opcode() == SpvOp::SpvOpStore) { | 
 |         stores[stores_found] = &inst; | 
 |         ++stores_found; | 
 |       } | 
 |     } | 
 |  | 
 |     for (int i = 0; i < 2; ++i) { | 
 |       EXPECT_TRUE(stores[i]); | 
 |     } | 
 |  | 
 |     // 47 -> 48 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(47) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[0]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       // Independent but not supported. | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |  | 
 |     // 54 -> 55 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(54) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[1]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       // Independent but not supported. | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |   } | 
 |   { | 
 |     // Function b | 
 |     const Function* f = spvtest::GetFunction(module, 8); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     const Instruction* stores[2]; | 
 |     int stores_found = 0; | 
 |     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 66)) { | 
 |       if (inst.opcode() == SpvOp::SpvOpStore) { | 
 |         stores[stores_found] = &inst; | 
 |         ++stores_found; | 
 |       } | 
 |     } | 
 |  | 
 |     for (int i = 0; i < 2; ++i) { | 
 |       EXPECT_TRUE(stores[i]); | 
 |     } | 
 |  | 
 |     // 78 -> 79 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(78) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[0]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       // Dependent. | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |  | 
 |     // 85 -> 86 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(85) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[1]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       // Dependent. | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |   } | 
 |   { | 
 |     // Function c | 
 |     const Function* f = spvtest::GetFunction(module, 10); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     const Instruction* stores[2]; | 
 |     int stores_found = 0; | 
 |     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 96)) { | 
 |       if (inst.opcode() == SpvOp::SpvOpStore) { | 
 |         stores[stores_found] = &inst; | 
 |         ++stores_found; | 
 |       } | 
 |     } | 
 |  | 
 |     for (int i = 0; i < 2; ++i) { | 
 |       EXPECT_TRUE(stores[i]); | 
 |     } | 
 |  | 
 |     // 109 -> 110 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(109) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[0]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       // Independent and supported. | 
 |       EXPECT_TRUE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |  | 
 |     // 116 -> 117 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(116) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[1]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       // Independent but not supported. | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |   } | 
 |   { | 
 |     // Function d | 
 |     const Function* f = spvtest::GetFunction(module, 12); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     const Instruction* stores[2]; | 
 |     int stores_found = 0; | 
 |     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 127)) { | 
 |       if (inst.opcode() == SpvOp::SpvOpStore) { | 
 |         stores[stores_found] = &inst; | 
 |         ++stores_found; | 
 |       } | 
 |     } | 
 |  | 
 |     for (int i = 0; i < 2; ++i) { | 
 |       EXPECT_TRUE(stores[i]); | 
 |     } | 
 |  | 
 |     // 139 -> 140 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(139) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[0]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       // Dependent | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |  | 
 |     // 146 -> 147 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(146) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[1]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       // Dependent | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | /* | 
 |   Generated from the following GLSL fragment shader | 
 |   with --eliminate-local-multi-store | 
 | #version 440 core | 
 | layout(location = 0) in vec4 in_vec; | 
 | // Loop iterates from symbolic to symbolic | 
 | void a() { | 
 |   int M = int(in_vec.x); | 
 |   int N = int(in_vec.y); | 
 |   int arr[10]; | 
 |   for (int i = M; i < N; i++) { // Bounds are N - M - 1 | 
 |     arr[i+M+N] = arr[i+M+2*N]; // |distance| = N | 
 |     arr[i+M+2*N] = arr[i+M+N]; // |distance| = N | 
 |   } | 
 | } | 
 | void b() { | 
 |   int M = int(in_vec.x); | 
 |   int N = int(in_vec.y); | 
 |   int arr[10]; | 
 |   for (int i = M; i <= N; i++) { // Bounds are N - M | 
 |     arr[i+M+N] = arr[i+M+2*N]; // |distance| = N | 
 |     arr[i+M+2*N] = arr[i+M+N]; // |distance| = N | 
 |   } | 
 | } | 
 | void c() { | 
 |   int M = int(in_vec.x); | 
 |   int N = int(in_vec.y); | 
 |   int arr[10]; | 
 |   for (int i = M; i > N; i--) { // Bounds are M - N - 1 | 
 |     arr[i+M+N] = arr[i+M+2*N]; // |distance| = N | 
 |     arr[i+M+2*N] = arr[i+M+N]; // |distance| = N | 
 |   } | 
 | } | 
 | void d() { | 
 |   int M = int(in_vec.x); | 
 |   int N = int(in_vec.y); | 
 |   int arr[10]; | 
 |   for (int i = M; i >= N; i--) { // Bounds are M - N | 
 |     arr[i+M+N] = arr[i+M+2*N]; // |distance| = N | 
 |     arr[i+M+2*N] = arr[i+M+N]; // |distance| = N | 
 |   } | 
 | } | 
 | void main(){ | 
 |   a(); | 
 |   b(); | 
 |   c(); | 
 |   d(); | 
 | } | 
 | */ | 
 | TEST(DependencyAnalysisHelpers, symbolic_to_symbolic) { | 
 |   const std::string text = R"(               OpCapability Shader | 
 |           %1 = OpExtInstImport "GLSL.std.450" | 
 |                OpMemoryModel Logical GLSL450 | 
 |                OpEntryPoint Fragment %4 "main" %20 | 
 |                OpExecutionMode %4 OriginUpperLeft | 
 |                OpSource GLSL 440 | 
 |                OpName %4 "main" | 
 |                OpName %6 "a(" | 
 |                OpName %8 "b(" | 
 |                OpName %10 "c(" | 
 |                OpName %12 "d(" | 
 |                OpName %16 "M" | 
 |                OpName %20 "in_vec" | 
 |                OpName %27 "N" | 
 |                OpName %32 "i" | 
 |                OpName %46 "arr" | 
 |                OpName %79 "M" | 
 |                OpName %83 "N" | 
 |                OpName %87 "i" | 
 |                OpName %97 "arr" | 
 |                OpName %128 "M" | 
 |                OpName %132 "N" | 
 |                OpName %136 "i" | 
 |                OpName %146 "arr" | 
 |                OpName %177 "M" | 
 |                OpName %181 "N" | 
 |                OpName %185 "i" | 
 |                OpName %195 "arr" | 
 |                OpDecorate %20 Location 0 | 
 |           %2 = OpTypeVoid | 
 |           %3 = OpTypeFunction %2 | 
 |          %14 = OpTypeInt 32 1 | 
 |          %15 = OpTypePointer Function %14 | 
 |          %17 = OpTypeFloat 32 | 
 |          %18 = OpTypeVector %17 4 | 
 |          %19 = OpTypePointer Input %18 | 
 |          %20 = OpVariable %19 Input | 
 |          %21 = OpTypeInt 32 0 | 
 |          %22 = OpConstant %21 0 | 
 |          %23 = OpTypePointer Input %17 | 
 |          %28 = OpConstant %21 1 | 
 |          %41 = OpTypeBool | 
 |          %43 = OpConstant %21 10 | 
 |          %44 = OpTypeArray %14 %43 | 
 |          %45 = OpTypePointer Function %44 | 
 |          %55 = OpConstant %14 2 | 
 |          %77 = OpConstant %14 1 | 
 |           %4 = OpFunction %2 None %3 | 
 |           %5 = OpLabel | 
 |         %226 = OpFunctionCall %2 %6 | 
 |         %227 = OpFunctionCall %2 %8 | 
 |         %228 = OpFunctionCall %2 %10 | 
 |         %229 = OpFunctionCall %2 %12 | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |           %6 = OpFunction %2 None %3 | 
 |           %7 = OpLabel | 
 |          %16 = OpVariable %15 Function | 
 |          %27 = OpVariable %15 Function | 
 |          %32 = OpVariable %15 Function | 
 |          %46 = OpVariable %45 Function | 
 |          %24 = OpAccessChain %23 %20 %22 | 
 |          %25 = OpLoad %17 %24 | 
 |          %26 = OpConvertFToS %14 %25 | 
 |                OpStore %16 %26 | 
 |          %29 = OpAccessChain %23 %20 %28 | 
 |          %30 = OpLoad %17 %29 | 
 |          %31 = OpConvertFToS %14 %30 | 
 |                OpStore %27 %31 | 
 |                OpStore %32 %26 | 
 |                OpBranch %34 | 
 |          %34 = OpLabel | 
 |         %230 = OpPhi %14 %26 %7 %78 %37 | 
 |                OpLoopMerge %36 %37 None | 
 |                OpBranch %38 | 
 |          %38 = OpLabel | 
 |          %42 = OpSLessThan %41 %230 %31 | 
 |                OpBranchConditional %42 %35 %36 | 
 |          %35 = OpLabel | 
 |          %49 = OpIAdd %14 %230 %26 | 
 |          %51 = OpIAdd %14 %49 %31 | 
 |          %54 = OpIAdd %14 %230 %26 | 
 |          %57 = OpIMul %14 %55 %31 | 
 |          %58 = OpIAdd %14 %54 %57 | 
 |          %59 = OpAccessChain %15 %46 %58 | 
 |          %60 = OpLoad %14 %59 | 
 |          %61 = OpAccessChain %15 %46 %51 | 
 |                OpStore %61 %60 | 
 |          %64 = OpIAdd %14 %230 %26 | 
 |          %66 = OpIMul %14 %55 %31 | 
 |          %67 = OpIAdd %14 %64 %66 | 
 |          %70 = OpIAdd %14 %230 %26 | 
 |          %72 = OpIAdd %14 %70 %31 | 
 |          %73 = OpAccessChain %15 %46 %72 | 
 |          %74 = OpLoad %14 %73 | 
 |          %75 = OpAccessChain %15 %46 %67 | 
 |                OpStore %75 %74 | 
 |                OpBranch %37 | 
 |          %37 = OpLabel | 
 |          %78 = OpIAdd %14 %230 %77 | 
 |                OpStore %32 %78 | 
 |                OpBranch %34 | 
 |          %36 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |           %8 = OpFunction %2 None %3 | 
 |           %9 = OpLabel | 
 |          %79 = OpVariable %15 Function | 
 |          %83 = OpVariable %15 Function | 
 |          %87 = OpVariable %15 Function | 
 |          %97 = OpVariable %45 Function | 
 |          %80 = OpAccessChain %23 %20 %22 | 
 |          %81 = OpLoad %17 %80 | 
 |          %82 = OpConvertFToS %14 %81 | 
 |                OpStore %79 %82 | 
 |          %84 = OpAccessChain %23 %20 %28 | 
 |          %85 = OpLoad %17 %84 | 
 |          %86 = OpConvertFToS %14 %85 | 
 |                OpStore %83 %86 | 
 |                OpStore %87 %82 | 
 |                OpBranch %89 | 
 |          %89 = OpLabel | 
 |         %231 = OpPhi %14 %82 %9 %127 %92 | 
 |                OpLoopMerge %91 %92 None | 
 |                OpBranch %93 | 
 |          %93 = OpLabel | 
 |          %96 = OpSLessThanEqual %41 %231 %86 | 
 |                OpBranchConditional %96 %90 %91 | 
 |          %90 = OpLabel | 
 |         %100 = OpIAdd %14 %231 %82 | 
 |         %102 = OpIAdd %14 %100 %86 | 
 |         %105 = OpIAdd %14 %231 %82 | 
 |         %107 = OpIMul %14 %55 %86 | 
 |         %108 = OpIAdd %14 %105 %107 | 
 |         %109 = OpAccessChain %15 %97 %108 | 
 |         %110 = OpLoad %14 %109 | 
 |         %111 = OpAccessChain %15 %97 %102 | 
 |                OpStore %111 %110 | 
 |         %114 = OpIAdd %14 %231 %82 | 
 |         %116 = OpIMul %14 %55 %86 | 
 |         %117 = OpIAdd %14 %114 %116 | 
 |         %120 = OpIAdd %14 %231 %82 | 
 |         %122 = OpIAdd %14 %120 %86 | 
 |         %123 = OpAccessChain %15 %97 %122 | 
 |         %124 = OpLoad %14 %123 | 
 |         %125 = OpAccessChain %15 %97 %117 | 
 |                OpStore %125 %124 | 
 |                OpBranch %92 | 
 |          %92 = OpLabel | 
 |         %127 = OpIAdd %14 %231 %77 | 
 |                OpStore %87 %127 | 
 |                OpBranch %89 | 
 |          %91 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %10 = OpFunction %2 None %3 | 
 |          %11 = OpLabel | 
 |         %128 = OpVariable %15 Function | 
 |         %132 = OpVariable %15 Function | 
 |         %136 = OpVariable %15 Function | 
 |         %146 = OpVariable %45 Function | 
 |         %129 = OpAccessChain %23 %20 %22 | 
 |         %130 = OpLoad %17 %129 | 
 |         %131 = OpConvertFToS %14 %130 | 
 |                OpStore %128 %131 | 
 |         %133 = OpAccessChain %23 %20 %28 | 
 |         %134 = OpLoad %17 %133 | 
 |         %135 = OpConvertFToS %14 %134 | 
 |                OpStore %132 %135 | 
 |                OpStore %136 %131 | 
 |                OpBranch %138 | 
 |         %138 = OpLabel | 
 |         %232 = OpPhi %14 %131 %11 %176 %141 | 
 |                OpLoopMerge %140 %141 None | 
 |                OpBranch %142 | 
 |         %142 = OpLabel | 
 |         %145 = OpSGreaterThan %41 %232 %135 | 
 |                OpBranchConditional %145 %139 %140 | 
 |         %139 = OpLabel | 
 |         %149 = OpIAdd %14 %232 %131 | 
 |         %151 = OpIAdd %14 %149 %135 | 
 |         %154 = OpIAdd %14 %232 %131 | 
 |         %156 = OpIMul %14 %55 %135 | 
 |         %157 = OpIAdd %14 %154 %156 | 
 |         %158 = OpAccessChain %15 %146 %157 | 
 |         %159 = OpLoad %14 %158 | 
 |         %160 = OpAccessChain %15 %146 %151 | 
 |                OpStore %160 %159 | 
 |         %163 = OpIAdd %14 %232 %131 | 
 |         %165 = OpIMul %14 %55 %135 | 
 |         %166 = OpIAdd %14 %163 %165 | 
 |         %169 = OpIAdd %14 %232 %131 | 
 |         %171 = OpIAdd %14 %169 %135 | 
 |         %172 = OpAccessChain %15 %146 %171 | 
 |         %173 = OpLoad %14 %172 | 
 |         %174 = OpAccessChain %15 %146 %166 | 
 |                OpStore %174 %173 | 
 |                OpBranch %141 | 
 |         %141 = OpLabel | 
 |         %176 = OpISub %14 %232 %77 | 
 |                OpStore %136 %176 | 
 |                OpBranch %138 | 
 |         %140 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |          %12 = OpFunction %2 None %3 | 
 |          %13 = OpLabel | 
 |         %177 = OpVariable %15 Function | 
 |         %181 = OpVariable %15 Function | 
 |         %185 = OpVariable %15 Function | 
 |         %195 = OpVariable %45 Function | 
 |         %178 = OpAccessChain %23 %20 %22 | 
 |         %179 = OpLoad %17 %178 | 
 |         %180 = OpConvertFToS %14 %179 | 
 |                OpStore %177 %180 | 
 |         %182 = OpAccessChain %23 %20 %28 | 
 |         %183 = OpLoad %17 %182 | 
 |         %184 = OpConvertFToS %14 %183 | 
 |                OpStore %181 %184 | 
 |                OpStore %185 %180 | 
 |                OpBranch %187 | 
 |         %187 = OpLabel | 
 |         %233 = OpPhi %14 %180 %13 %225 %190 | 
 |                OpLoopMerge %189 %190 None | 
 |                OpBranch %191 | 
 |         %191 = OpLabel | 
 |         %194 = OpSGreaterThanEqual %41 %233 %184 | 
 |                OpBranchConditional %194 %188 %189 | 
 |         %188 = OpLabel | 
 |         %198 = OpIAdd %14 %233 %180 | 
 |         %200 = OpIAdd %14 %198 %184 | 
 |         %203 = OpIAdd %14 %233 %180 | 
 |         %205 = OpIMul %14 %55 %184 | 
 |         %206 = OpIAdd %14 %203 %205 | 
 |         %207 = OpAccessChain %15 %195 %206 | 
 |         %208 = OpLoad %14 %207 | 
 |         %209 = OpAccessChain %15 %195 %200 | 
 |                OpStore %209 %208 | 
 |         %212 = OpIAdd %14 %233 %180 | 
 |         %214 = OpIMul %14 %55 %184 | 
 |         %215 = OpIAdd %14 %212 %214 | 
 |         %218 = OpIAdd %14 %233 %180 | 
 |         %220 = OpIAdd %14 %218 %184 | 
 |         %221 = OpAccessChain %15 %195 %220 | 
 |         %222 = OpLoad %14 %221 | 
 |         %223 = OpAccessChain %15 %195 %215 | 
 |                OpStore %223 %222 | 
 |                OpBranch %190 | 
 |         %190 = OpLabel | 
 |         %225 = OpISub %14 %233 %77 | 
 |                OpStore %185 %225 | 
 |                OpBranch %187 | 
 |         %189 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 | )"; | 
 |   std::unique_ptr<IRContext> context = | 
 |       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, | 
 |                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | 
 |   Module* module = context->module(); | 
 |   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | 
 |                              << text << std::endl; | 
 |   { | 
 |     // Function a | 
 |     const Function* f = spvtest::GetFunction(module, 6); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     const Instruction* stores[2]; | 
 |     int stores_found = 0; | 
 |     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 35)) { | 
 |       if (inst.opcode() == SpvOp::SpvOpStore) { | 
 |         stores[stores_found] = &inst; | 
 |         ++stores_found; | 
 |       } | 
 |     } | 
 |  | 
 |     for (int i = 0; i < 2; ++i) { | 
 |       EXPECT_TRUE(stores[i]); | 
 |     } | 
 |  | 
 |     // 60 -> 61 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(60) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[0]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |  | 
 |     // 74 -> 75 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(74) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[1]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |   } | 
 |   { | 
 |     // Function b | 
 |     const Function* f = spvtest::GetFunction(module, 8); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     const Instruction* stores[2]; | 
 |     int stores_found = 0; | 
 |     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 90)) { | 
 |       if (inst.opcode() == SpvOp::SpvOpStore) { | 
 |         stores[stores_found] = &inst; | 
 |         ++stores_found; | 
 |       } | 
 |     } | 
 |  | 
 |     for (int i = 0; i < 2; ++i) { | 
 |       EXPECT_TRUE(stores[i]); | 
 |     } | 
 |  | 
 |     // 110 -> 111 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(110) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[0]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |  | 
 |     // 124 -> 125 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(124) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[1]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |   } | 
 |   { | 
 |     // Function c | 
 |     const Function* f = spvtest::GetFunction(module, 10); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     const Instruction* stores[2]; | 
 |     int stores_found = 0; | 
 |     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 139)) { | 
 |       if (inst.opcode() == SpvOp::SpvOpStore) { | 
 |         stores[stores_found] = &inst; | 
 |         ++stores_found; | 
 |       } | 
 |     } | 
 |  | 
 |     for (int i = 0; i < 2; ++i) { | 
 |       EXPECT_TRUE(stores[i]); | 
 |     } | 
 |  | 
 |     // 159 -> 160 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(159) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[0]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |  | 
 |     // 173 -> 174 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(173) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[1]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |   } | 
 |   { | 
 |     // Function d | 
 |     const Function* f = spvtest::GetFunction(module, 12); | 
 |     LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
 |     Loop* loop = &ld.GetLoopByIndex(0); | 
 |     std::vector<const Loop*> loops{loop}; | 
 |     LoopDependenceAnalysis analysis{context.get(), loops}; | 
 |  | 
 |     const Instruction* stores[2]; | 
 |     int stores_found = 0; | 
 |     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 188)) { | 
 |       if (inst.opcode() == SpvOp::SpvOpStore) { | 
 |         stores[stores_found] = &inst; | 
 |         ++stores_found; | 
 |       } | 
 |     } | 
 |  | 
 |     for (int i = 0; i < 2; ++i) { | 
 |       EXPECT_TRUE(stores[i]); | 
 |     } | 
 |  | 
 |     // 208 -> 209 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(208) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[0]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |  | 
 |     // 222 -> 223 | 
 |     { | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // load. | 
 |       Instruction* load_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(context->get_def_use_mgr() | 
 |                            ->GetDef(222) | 
 |                            ->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); | 
 |  | 
 |       // Analyse and simplify the instruction behind the access chain of this | 
 |       // store. | 
 |       Instruction* store_var = context->get_def_use_mgr()->GetDef( | 
 |           context->get_def_use_mgr() | 
 |               ->GetDef(stores[1]->GetSingleWordInOperand(0)) | 
 |               ->GetSingleWordInOperand(1)); | 
 |       SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); | 
 |  | 
 |       SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( | 
 |           analysis.GetScalarEvolution()->CreateSubtraction(load, store)); | 
 |  | 
 |       EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( | 
 |           loop, delta, store->AsSERecurrentNode()->GetCoefficient())); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace opt | 
 | }  // namespace spvtools |