|  | // 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 <set> | 
|  | #include <string> | 
|  | #include <unordered_set> | 
|  | #include <utility> | 
|  | #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/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 DependencyAnalysis = ::testing::Test; | 
|  |  | 
|  | /* | 
|  | Generated from the following GLSL fragment shader | 
|  | with --eliminate-local-multi-store | 
|  | #version 440 core | 
|  | void main(){ | 
|  | int[10] arr; | 
|  | int[10] arr2; | 
|  | int a = 2; | 
|  | for (int i = 0; i < 10; i++) { | 
|  | arr[a] = arr[3]; | 
|  | arr[a*2] = arr[a+3]; | 
|  | arr[6] = arr2[6]; | 
|  | arr[a+5] = arr2[7]; | 
|  | } | 
|  | } | 
|  | */ | 
|  | TEST(DependencyAnalysis, ZIV) { | 
|  | 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 %25 "arr" | 
|  | OpName %39 "arr2" | 
|  | %2 = OpTypeVoid | 
|  | %3 = OpTypeFunction %2 | 
|  | %6 = OpTypeInt 32 1 | 
|  | %7 = OpTypePointer Function %6 | 
|  | %9 = OpConstant %6 2 | 
|  | %11 = OpConstant %6 0 | 
|  | %18 = OpConstant %6 10 | 
|  | %19 = OpTypeBool | 
|  | %21 = OpTypeInt 32 0 | 
|  | %22 = OpConstant %21 10 | 
|  | %23 = OpTypeArray %6 %22 | 
|  | %24 = OpTypePointer Function %23 | 
|  | %27 = OpConstant %6 3 | 
|  | %38 = OpConstant %6 6 | 
|  | %44 = OpConstant %6 5 | 
|  | %46 = OpConstant %6 7 | 
|  | %51 = OpConstant %6 1 | 
|  | %4 = OpFunction %2 None %3 | 
|  | %5 = OpLabel | 
|  | %25 = OpVariable %24 Function | 
|  | %39 = OpVariable %24 Function | 
|  | OpBranch %12 | 
|  | %12 = OpLabel | 
|  | %53 = OpPhi %6 %11 %5 %52 %15 | 
|  | OpLoopMerge %14 %15 None | 
|  | OpBranch %16 | 
|  | %16 = OpLabel | 
|  | %20 = OpSLessThan %19 %53 %18 | 
|  | OpBranchConditional %20 %13 %14 | 
|  | %13 = OpLabel | 
|  | %28 = OpAccessChain %7 %25 %27 | 
|  | %29 = OpLoad %6 %28 | 
|  | %30 = OpAccessChain %7 %25 %9 | 
|  | OpStore %30 %29 | 
|  | %32 = OpIMul %6 %9 %9 | 
|  | %34 = OpIAdd %6 %9 %27 | 
|  | %35 = OpAccessChain %7 %25 %34 | 
|  | %36 = OpLoad %6 %35 | 
|  | %37 = OpAccessChain %7 %25 %32 | 
|  | OpStore %37 %36 | 
|  | %40 = OpAccessChain %7 %39 %38 | 
|  | %41 = OpLoad %6 %40 | 
|  | %42 = OpAccessChain %7 %25 %38 | 
|  | OpStore %42 %41 | 
|  | %45 = OpIAdd %6 %9 %44 | 
|  | %47 = OpAccessChain %7 %39 %46 | 
|  | %48 = OpLoad %6 %47 | 
|  | %49 = OpAccessChain %7 %25 %45 | 
|  | OpStore %49 %48 | 
|  | OpBranch %15 | 
|  | %15 = OpLabel | 
|  | %52 = OpIAdd %6 %53 %51 | 
|  | OpBranch %12 | 
|  | %14 = 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; | 
|  | 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}; | 
|  |  | 
|  | const Instruction* store[4]; | 
|  | int stores_found = 0; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 13)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store[stores_found] = &inst; | 
|  | ++stores_found; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < 4; ++i) { | 
|  | EXPECT_TRUE(store[i]); | 
|  | } | 
|  |  | 
|  | // 29 -> 30 tests looking through constants. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(29), | 
|  | store[0], &distance_vector)); | 
|  | } | 
|  |  | 
|  | // 36 -> 37 tests looking through additions. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(36), | 
|  | store[1], &distance_vector)); | 
|  | } | 
|  |  | 
|  | // 41 -> 42 tests looking at same index across two different arrays. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(41), | 
|  | store[2], &distance_vector)); | 
|  | } | 
|  |  | 
|  | // 48 -> 49 tests looking through additions for same index in two different | 
|  | // arrays. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(48), | 
|  | store[3], &distance_vector)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | Generated from the following GLSL fragment shader | 
|  | with --eliminate-local-multi-store | 
|  | #version 440 core | 
|  | layout(location = 0) in vec4 c; | 
|  | void main(){ | 
|  | int[10] arr; | 
|  | int[10] arr2; | 
|  | int[10] arr3; | 
|  | int[10] arr4; | 
|  | int[10] arr5; | 
|  | int N = int(c.x); | 
|  | for (int i = 0; i < N; i++) { | 
|  | arr[2*N] = arr[N]; | 
|  | arr2[2*N+1] = arr2[N]; | 
|  | arr3[2*N] = arr3[N-1]; | 
|  | arr4[N] = arr5[N]; | 
|  | } | 
|  | } | 
|  | */ | 
|  | TEST(DependencyAnalysis, SymbolicZIV) { | 
|  | const std::string text = R"(               OpCapability Shader | 
|  | %1 = OpExtInstImport "GLSL.std.450" | 
|  | OpMemoryModel Logical GLSL450 | 
|  | OpEntryPoint Fragment %4 "main" %12 | 
|  | OpExecutionMode %4 OriginUpperLeft | 
|  | OpSource GLSL 440 | 
|  | OpName %4 "main" | 
|  | OpName %12 "c" | 
|  | OpName %33 "arr" | 
|  | OpName %41 "arr2" | 
|  | OpName %50 "arr3" | 
|  | OpName %58 "arr4" | 
|  | OpName %60 "arr5" | 
|  | OpDecorate %12 Location 0 | 
|  | %2 = OpTypeVoid | 
|  | %3 = OpTypeFunction %2 | 
|  | %6 = OpTypeInt 32 1 | 
|  | %7 = OpTypePointer Function %6 | 
|  | %9 = OpTypeFloat 32 | 
|  | %10 = OpTypeVector %9 4 | 
|  | %11 = OpTypePointer Input %10 | 
|  | %12 = OpVariable %11 Input | 
|  | %13 = OpTypeInt 32 0 | 
|  | %14 = OpConstant %13 0 | 
|  | %15 = OpTypePointer Input %9 | 
|  | %20 = OpConstant %6 0 | 
|  | %28 = OpTypeBool | 
|  | %30 = OpConstant %13 10 | 
|  | %31 = OpTypeArray %6 %30 | 
|  | %32 = OpTypePointer Function %31 | 
|  | %34 = OpConstant %6 2 | 
|  | %44 = OpConstant %6 1 | 
|  | %4 = OpFunction %2 None %3 | 
|  | %5 = OpLabel | 
|  | %33 = OpVariable %32 Function | 
|  | %41 = OpVariable %32 Function | 
|  | %50 = OpVariable %32 Function | 
|  | %58 = OpVariable %32 Function | 
|  | %60 = OpVariable %32 Function | 
|  | %16 = OpAccessChain %15 %12 %14 | 
|  | %17 = OpLoad %9 %16 | 
|  | %18 = OpConvertFToS %6 %17 | 
|  | OpBranch %21 | 
|  | %21 = OpLabel | 
|  | %67 = OpPhi %6 %20 %5 %66 %24 | 
|  | OpLoopMerge %23 %24 None | 
|  | OpBranch %25 | 
|  | %25 = OpLabel | 
|  | %29 = OpSLessThan %28 %67 %18 | 
|  | OpBranchConditional %29 %22 %23 | 
|  | %22 = OpLabel | 
|  | %36 = OpIMul %6 %34 %18 | 
|  | %38 = OpAccessChain %7 %33 %18 | 
|  | %39 = OpLoad %6 %38 | 
|  | %40 = OpAccessChain %7 %33 %36 | 
|  | OpStore %40 %39 | 
|  | %43 = OpIMul %6 %34 %18 | 
|  | %45 = OpIAdd %6 %43 %44 | 
|  | %47 = OpAccessChain %7 %41 %18 | 
|  | %48 = OpLoad %6 %47 | 
|  | %49 = OpAccessChain %7 %41 %45 | 
|  | OpStore %49 %48 | 
|  | %52 = OpIMul %6 %34 %18 | 
|  | %54 = OpISub %6 %18 %44 | 
|  | %55 = OpAccessChain %7 %50 %54 | 
|  | %56 = OpLoad %6 %55 | 
|  | %57 = OpAccessChain %7 %50 %52 | 
|  | OpStore %57 %56 | 
|  | %62 = OpAccessChain %7 %60 %18 | 
|  | %63 = OpLoad %6 %62 | 
|  | %64 = OpAccessChain %7 %58 %18 | 
|  | OpStore %64 %63 | 
|  | OpBranch %24 | 
|  | %24 = OpLabel | 
|  | %66 = OpIAdd %6 %67 %44 | 
|  | OpBranch %21 | 
|  | %23 = 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; | 
|  | 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}; | 
|  |  | 
|  | const Instruction* store[4]; | 
|  | int stores_found = 0; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 22)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store[stores_found] = &inst; | 
|  | ++stores_found; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < 4; ++i) { | 
|  | EXPECT_TRUE(store[i]); | 
|  | } | 
|  |  | 
|  | // independent due to loop bounds (won't enter if N <= 0). | 
|  | // 39 -> 40 tests looking through symbols and multiplicaiton. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(39), | 
|  | store[0], &distance_vector)); | 
|  | } | 
|  |  | 
|  | // 48 -> 49 tests looking through symbols and multiplication + addition. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(48), | 
|  | store[1], &distance_vector)); | 
|  | } | 
|  |  | 
|  | // 56 -> 57 tests looking through symbols and arithmetic on load and store. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(56), | 
|  | store[2], &distance_vector)); | 
|  | } | 
|  |  | 
|  | // independent as different arrays | 
|  | // 63 -> 64 tests looking through symbols and load/store from/to different | 
|  | // arrays. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(63), | 
|  | store[3], &distance_vector)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | Generated from the following GLSL fragment shader | 
|  | with --eliminate-local-multi-store | 
|  | #version 440 core | 
|  | void a(){ | 
|  | int[10] arr; | 
|  | int[11] arr2; | 
|  | int[20] arr3; | 
|  | int[20] arr4; | 
|  | int a = 2; | 
|  | for (int i = 0; i < 10; i++) { | 
|  | arr[i] = arr[i]; | 
|  | arr2[i] = arr2[i+1]; | 
|  | arr3[i] = arr3[i-1]; | 
|  | arr4[2*i] = arr4[i]; | 
|  | } | 
|  | } | 
|  | void b(){ | 
|  | int[10] arr; | 
|  | int[11] arr2; | 
|  | int[20] arr3; | 
|  | int[20] arr4; | 
|  | int a = 2; | 
|  | for (int i = 10; i > 0; i--) { | 
|  | arr[i] = arr[i]; | 
|  | arr2[i] = arr2[i+1]; | 
|  | arr3[i] = arr3[i-1]; | 
|  | arr4[2*i] = arr4[i]; | 
|  | } | 
|  | } | 
|  |  | 
|  | void main() { | 
|  | a(); | 
|  | b(); | 
|  | } | 
|  | */ | 
|  | TEST(DependencyAnalysis, SIV) { | 
|  | 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 "a" | 
|  | OpName %14 "i" | 
|  | OpName %29 "arr" | 
|  | OpName %38 "arr2" | 
|  | OpName %49 "arr3" | 
|  | OpName %56 "arr4" | 
|  | OpName %65 "a" | 
|  | OpName %66 "i" | 
|  | OpName %74 "arr" | 
|  | OpName %80 "arr2" | 
|  | OpName %87 "arr3" | 
|  | OpName %94 "arr4" | 
|  | %2 = OpTypeVoid | 
|  | %3 = OpTypeFunction %2 | 
|  | %10 = OpTypeInt 32 1 | 
|  | %11 = OpTypePointer Function %10 | 
|  | %13 = OpConstant %10 2 | 
|  | %15 = OpConstant %10 0 | 
|  | %22 = OpConstant %10 10 | 
|  | %23 = OpTypeBool | 
|  | %25 = OpTypeInt 32 0 | 
|  | %26 = OpConstant %25 10 | 
|  | %27 = OpTypeArray %10 %26 | 
|  | %28 = OpTypePointer Function %27 | 
|  | %35 = OpConstant %25 11 | 
|  | %36 = OpTypeArray %10 %35 | 
|  | %37 = OpTypePointer Function %36 | 
|  | %41 = OpConstant %10 1 | 
|  | %46 = OpConstant %25 20 | 
|  | %47 = OpTypeArray %10 %46 | 
|  | %48 = OpTypePointer Function %47 | 
|  | %4 = OpFunction %2 None %3 | 
|  | %5 = OpLabel | 
|  | %103 = OpFunctionCall %2 %6 | 
|  | %104 = OpFunctionCall %2 %8 | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %6 = OpFunction %2 None %3 | 
|  | %7 = OpLabel | 
|  | %12 = OpVariable %11 Function | 
|  | %14 = OpVariable %11 Function | 
|  | %29 = OpVariable %28 Function | 
|  | %38 = OpVariable %37 Function | 
|  | %49 = OpVariable %48 Function | 
|  | %56 = OpVariable %48 Function | 
|  | OpStore %12 %13 | 
|  | OpStore %14 %15 | 
|  | OpBranch %16 | 
|  | %16 = OpLabel | 
|  | %105 = OpPhi %10 %15 %7 %64 %19 | 
|  | OpLoopMerge %18 %19 None | 
|  | OpBranch %20 | 
|  | %20 = OpLabel | 
|  | %24 = OpSLessThan %23 %105 %22 | 
|  | OpBranchConditional %24 %17 %18 | 
|  | %17 = OpLabel | 
|  | %32 = OpAccessChain %11 %29 %105 | 
|  | %33 = OpLoad %10 %32 | 
|  | %34 = OpAccessChain %11 %29 %105 | 
|  | OpStore %34 %33 | 
|  | %42 = OpIAdd %10 %105 %41 | 
|  | %43 = OpAccessChain %11 %38 %42 | 
|  | %44 = OpLoad %10 %43 | 
|  | %45 = OpAccessChain %11 %38 %105 | 
|  | OpStore %45 %44 | 
|  | %52 = OpISub %10 %105 %41 | 
|  | %53 = OpAccessChain %11 %49 %52 | 
|  | %54 = OpLoad %10 %53 | 
|  | %55 = OpAccessChain %11 %49 %105 | 
|  | OpStore %55 %54 | 
|  | %58 = OpIMul %10 %13 %105 | 
|  | %60 = OpAccessChain %11 %56 %105 | 
|  | %61 = OpLoad %10 %60 | 
|  | %62 = OpAccessChain %11 %56 %58 | 
|  | OpStore %62 %61 | 
|  | OpBranch %19 | 
|  | %19 = OpLabel | 
|  | %64 = OpIAdd %10 %105 %41 | 
|  | OpStore %14 %64 | 
|  | OpBranch %16 | 
|  | %18 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %8 = OpFunction %2 None %3 | 
|  | %9 = OpLabel | 
|  | %65 = OpVariable %11 Function | 
|  | %66 = OpVariable %11 Function | 
|  | %74 = OpVariable %28 Function | 
|  | %80 = OpVariable %37 Function | 
|  | %87 = OpVariable %48 Function | 
|  | %94 = OpVariable %48 Function | 
|  | OpStore %65 %13 | 
|  | OpStore %66 %22 | 
|  | OpBranch %67 | 
|  | %67 = OpLabel | 
|  | %106 = OpPhi %10 %22 %9 %102 %70 | 
|  | OpLoopMerge %69 %70 None | 
|  | OpBranch %71 | 
|  | %71 = OpLabel | 
|  | %73 = OpSGreaterThan %23 %106 %15 | 
|  | OpBranchConditional %73 %68 %69 | 
|  | %68 = OpLabel | 
|  | %77 = OpAccessChain %11 %74 %106 | 
|  | %78 = OpLoad %10 %77 | 
|  | %79 = OpAccessChain %11 %74 %106 | 
|  | OpStore %79 %78 | 
|  | %83 = OpIAdd %10 %106 %41 | 
|  | %84 = OpAccessChain %11 %80 %83 | 
|  | %85 = OpLoad %10 %84 | 
|  | %86 = OpAccessChain %11 %80 %106 | 
|  | OpStore %86 %85 | 
|  | %90 = OpISub %10 %106 %41 | 
|  | %91 = OpAccessChain %11 %87 %90 | 
|  | %92 = OpLoad %10 %91 | 
|  | %93 = OpAccessChain %11 %87 %106 | 
|  | OpStore %93 %92 | 
|  | %96 = OpIMul %10 %13 %106 | 
|  | %98 = OpAccessChain %11 %94 %106 | 
|  | %99 = OpLoad %10 %98 | 
|  | %100 = OpAccessChain %11 %94 %96 | 
|  | OpStore %100 %99 | 
|  | OpBranch %70 | 
|  | %70 = OpLabel | 
|  | %102 = OpISub %10 %106 %41 | 
|  | OpStore %66 %102 | 
|  | OpBranch %67 | 
|  | %69 = 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; | 
|  | // For the loop in 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[4]; | 
|  | int stores_found = 0; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 17)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store[stores_found] = &inst; | 
|  | ++stores_found; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < 4; ++i) { | 
|  | EXPECT_TRUE(store[i]); | 
|  | } | 
|  |  | 
|  | // = dependence | 
|  | // 33 -> 34 tests looking at SIV in same array. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(33), store[0], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::DISTANCE); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].direction, | 
|  | DistanceEntry::Directions::EQ); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].distance, 0); | 
|  | } | 
|  |  | 
|  | // > -1 dependence | 
|  | // 44 -> 45 tests looking at SIV in same array with addition. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(44), store[1], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::DISTANCE); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].direction, | 
|  | DistanceEntry::Directions::GT); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].distance, -1); | 
|  | } | 
|  |  | 
|  | // < 1 dependence | 
|  | // 54 -> 55 tests looking at SIV in same array with subtraction. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(54), store[2], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::DISTANCE); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].direction, | 
|  | DistanceEntry::Directions::LT); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].distance, 1); | 
|  | } | 
|  |  | 
|  | // <=> dependence | 
|  | // 61 -> 62 tests looking at SIV in same array with multiplication. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(61), store[3], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::UNKNOWN); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].direction, | 
|  | DistanceEntry::Directions::ALL); | 
|  | } | 
|  | } | 
|  | // For the loop in 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[4]; | 
|  | int stores_found = 0; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 68)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store[stores_found] = &inst; | 
|  | ++stores_found; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < 4; ++i) { | 
|  | EXPECT_TRUE(store[i]); | 
|  | } | 
|  |  | 
|  | // = dependence | 
|  | // 78 -> 79 tests looking at SIV in same array. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(78), store[0], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::DISTANCE); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].direction, | 
|  | DistanceEntry::Directions::EQ); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].distance, 0); | 
|  | } | 
|  |  | 
|  | // < 1 dependence | 
|  | // 85 -> 86 tests looking at SIV in same array with addition. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(85), store[1], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::DISTANCE); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].direction, | 
|  | DistanceEntry::Directions::LT); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].distance, 1); | 
|  | } | 
|  |  | 
|  | // > -1 dependence | 
|  | // 92 -> 93 tests looking at SIV in same array with subtraction. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(92), store[2], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::DISTANCE); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].direction, | 
|  | DistanceEntry::Directions::GT); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].distance, -1); | 
|  | } | 
|  |  | 
|  | // <=> dependence | 
|  | // 99 -> 100 tests looking at SIV in same array with multiplication. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(99), store[3], &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 | 
|  | layout(location = 0) in vec4 c; | 
|  | void a() { | 
|  | int[13] arr; | 
|  | int[15] arr2; | 
|  | int[18] arr3; | 
|  | int[18] arr4; | 
|  | int N = int(c.x); | 
|  | int C = 2; | 
|  | int a = 2; | 
|  | for (int i = 0; i < N; i++) { // Bounds are N - 1 | 
|  | arr[i+2*N] = arr[i+N]; // |distance| = N | 
|  | arr2[i+N] = arr2[i+2*N] + C; // |distance| = N | 
|  | arr3[2*i+2*N+1] = arr3[2*i+N+1]; // |distance| = N | 
|  | arr4[a*i+N+1] = arr4[a*i+2*N+1]; // |distance| = N | 
|  | } | 
|  | } | 
|  | void b() { | 
|  | int[13] arr; | 
|  | int[15] arr2; | 
|  | int[18] arr3; | 
|  | int[18] arr4; | 
|  | int N = int(c.x); | 
|  | int C = 2; | 
|  | int a = 2; | 
|  | for (int i = N; i > 0; i--) { // Bounds are N - 1 | 
|  | arr[i+2*N] = arr[i+N]; // |distance| = N | 
|  | arr2[i+N] = arr2[i+2*N] + C; // |distance| = N | 
|  | arr3[2*i+2*N+1] = arr3[2*i+N+1]; // |distance| = N | 
|  | arr4[a*i+N+1] = arr4[a*i+2*N+1]; // |distance| = N | 
|  | } | 
|  | } | 
|  | void main(){ | 
|  | a(); | 
|  | b(); | 
|  | }*/ | 
|  | TEST(DependencyAnalysis, SymbolicSIV) { | 
|  | const std::string text = R"(               OpCapability Shader | 
|  | %1 = OpExtInstImport "GLSL.std.450" | 
|  | OpMemoryModel Logical GLSL450 | 
|  | OpEntryPoint Fragment %4 "main" %16 | 
|  | OpExecutionMode %4 OriginUpperLeft | 
|  | OpSource GLSL 440 | 
|  | OpName %4 "main" | 
|  | OpName %6 "a(" | 
|  | OpName %8 "b(" | 
|  | OpName %12 "N" | 
|  | OpName %16 "c" | 
|  | OpName %23 "C" | 
|  | OpName %25 "a" | 
|  | OpName %26 "i" | 
|  | OpName %40 "arr" | 
|  | OpName %54 "arr2" | 
|  | OpName %70 "arr3" | 
|  | OpName %86 "arr4" | 
|  | OpName %105 "N" | 
|  | OpName %109 "C" | 
|  | OpName %110 "a" | 
|  | OpName %111 "i" | 
|  | OpName %120 "arr" | 
|  | OpName %131 "arr2" | 
|  | OpName %144 "arr3" | 
|  | OpName %159 "arr4" | 
|  | OpDecorate %16 Location 0 | 
|  | %2 = OpTypeVoid | 
|  | %3 = OpTypeFunction %2 | 
|  | %10 = OpTypeInt 32 1 | 
|  | %11 = OpTypePointer Function %10 | 
|  | %13 = OpTypeFloat 32 | 
|  | %14 = OpTypeVector %13 4 | 
|  | %15 = OpTypePointer Input %14 | 
|  | %16 = OpVariable %15 Input | 
|  | %17 = OpTypeInt 32 0 | 
|  | %18 = OpConstant %17 0 | 
|  | %19 = OpTypePointer Input %13 | 
|  | %24 = OpConstant %10 2 | 
|  | %27 = OpConstant %10 0 | 
|  | %35 = OpTypeBool | 
|  | %37 = OpConstant %17 13 | 
|  | %38 = OpTypeArray %10 %37 | 
|  | %39 = OpTypePointer Function %38 | 
|  | %51 = OpConstant %17 15 | 
|  | %52 = OpTypeArray %10 %51 | 
|  | %53 = OpTypePointer Function %52 | 
|  | %67 = OpConstant %17 18 | 
|  | %68 = OpTypeArray %10 %67 | 
|  | %69 = OpTypePointer Function %68 | 
|  | %76 = OpConstant %10 1 | 
|  | %4 = OpFunction %2 None %3 | 
|  | %5 = OpLabel | 
|  | %178 = OpFunctionCall %2 %6 | 
|  | %179 = OpFunctionCall %2 %8 | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %6 = OpFunction %2 None %3 | 
|  | %7 = OpLabel | 
|  | %12 = OpVariable %11 Function | 
|  | %23 = OpVariable %11 Function | 
|  | %25 = OpVariable %11 Function | 
|  | %26 = OpVariable %11 Function | 
|  | %40 = OpVariable %39 Function | 
|  | %54 = OpVariable %53 Function | 
|  | %70 = OpVariable %69 Function | 
|  | %86 = OpVariable %69 Function | 
|  | %20 = OpAccessChain %19 %16 %18 | 
|  | %21 = OpLoad %13 %20 | 
|  | %22 = OpConvertFToS %10 %21 | 
|  | OpStore %12 %22 | 
|  | OpStore %23 %24 | 
|  | OpStore %25 %24 | 
|  | OpStore %26 %27 | 
|  | OpBranch %28 | 
|  | %28 = OpLabel | 
|  | %180 = OpPhi %10 %27 %7 %104 %31 | 
|  | OpLoopMerge %30 %31 None | 
|  | OpBranch %32 | 
|  | %32 = OpLabel | 
|  | %36 = OpSLessThan %35 %180 %22 | 
|  | OpBranchConditional %36 %29 %30 | 
|  | %29 = OpLabel | 
|  | %43 = OpIMul %10 %24 %22 | 
|  | %44 = OpIAdd %10 %180 %43 | 
|  | %47 = OpIAdd %10 %180 %22 | 
|  | %48 = OpAccessChain %11 %40 %47 | 
|  | %49 = OpLoad %10 %48 | 
|  | %50 = OpAccessChain %11 %40 %44 | 
|  | OpStore %50 %49 | 
|  | %57 = OpIAdd %10 %180 %22 | 
|  | %60 = OpIMul %10 %24 %22 | 
|  | %61 = OpIAdd %10 %180 %60 | 
|  | %62 = OpAccessChain %11 %54 %61 | 
|  | %63 = OpLoad %10 %62 | 
|  | %65 = OpIAdd %10 %63 %24 | 
|  | %66 = OpAccessChain %11 %54 %57 | 
|  | OpStore %66 %65 | 
|  | %72 = OpIMul %10 %24 %180 | 
|  | %74 = OpIMul %10 %24 %22 | 
|  | %75 = OpIAdd %10 %72 %74 | 
|  | %77 = OpIAdd %10 %75 %76 | 
|  | %79 = OpIMul %10 %24 %180 | 
|  | %81 = OpIAdd %10 %79 %22 | 
|  | %82 = OpIAdd %10 %81 %76 | 
|  | %83 = OpAccessChain %11 %70 %82 | 
|  | %84 = OpLoad %10 %83 | 
|  | %85 = OpAccessChain %11 %70 %77 | 
|  | OpStore %85 %84 | 
|  | %89 = OpIMul %10 %24 %180 | 
|  | %91 = OpIAdd %10 %89 %22 | 
|  | %92 = OpIAdd %10 %91 %76 | 
|  | %95 = OpIMul %10 %24 %180 | 
|  | %97 = OpIMul %10 %24 %22 | 
|  | %98 = OpIAdd %10 %95 %97 | 
|  | %99 = OpIAdd %10 %98 %76 | 
|  | %100 = OpAccessChain %11 %86 %99 | 
|  | %101 = OpLoad %10 %100 | 
|  | %102 = OpAccessChain %11 %86 %92 | 
|  | OpStore %102 %101 | 
|  | OpBranch %31 | 
|  | %31 = OpLabel | 
|  | %104 = OpIAdd %10 %180 %76 | 
|  | OpStore %26 %104 | 
|  | OpBranch %28 | 
|  | %30 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %8 = OpFunction %2 None %3 | 
|  | %9 = OpLabel | 
|  | %105 = OpVariable %11 Function | 
|  | %109 = OpVariable %11 Function | 
|  | %110 = OpVariable %11 Function | 
|  | %111 = OpVariable %11 Function | 
|  | %120 = OpVariable %39 Function | 
|  | %131 = OpVariable %53 Function | 
|  | %144 = OpVariable %69 Function | 
|  | %159 = OpVariable %69 Function | 
|  | %106 = OpAccessChain %19 %16 %18 | 
|  | %107 = OpLoad %13 %106 | 
|  | %108 = OpConvertFToS %10 %107 | 
|  | OpStore %105 %108 | 
|  | OpStore %109 %24 | 
|  | OpStore %110 %24 | 
|  | OpStore %111 %108 | 
|  | OpBranch %113 | 
|  | %113 = OpLabel | 
|  | %181 = OpPhi %10 %108 %9 %177 %116 | 
|  | OpLoopMerge %115 %116 None | 
|  | OpBranch %117 | 
|  | %117 = OpLabel | 
|  | %119 = OpSGreaterThan %35 %181 %27 | 
|  | OpBranchConditional %119 %114 %115 | 
|  | %114 = OpLabel | 
|  | %123 = OpIMul %10 %24 %108 | 
|  | %124 = OpIAdd %10 %181 %123 | 
|  | %127 = OpIAdd %10 %181 %108 | 
|  | %128 = OpAccessChain %11 %120 %127 | 
|  | %129 = OpLoad %10 %128 | 
|  | %130 = OpAccessChain %11 %120 %124 | 
|  | OpStore %130 %129 | 
|  | %134 = OpIAdd %10 %181 %108 | 
|  | %137 = OpIMul %10 %24 %108 | 
|  | %138 = OpIAdd %10 %181 %137 | 
|  | %139 = OpAccessChain %11 %131 %138 | 
|  | %140 = OpLoad %10 %139 | 
|  | %142 = OpIAdd %10 %140 %24 | 
|  | %143 = OpAccessChain %11 %131 %134 | 
|  | OpStore %143 %142 | 
|  | %146 = OpIMul %10 %24 %181 | 
|  | %148 = OpIMul %10 %24 %108 | 
|  | %149 = OpIAdd %10 %146 %148 | 
|  | %150 = OpIAdd %10 %149 %76 | 
|  | %152 = OpIMul %10 %24 %181 | 
|  | %154 = OpIAdd %10 %152 %108 | 
|  | %155 = OpIAdd %10 %154 %76 | 
|  | %156 = OpAccessChain %11 %144 %155 | 
|  | %157 = OpLoad %10 %156 | 
|  | %158 = OpAccessChain %11 %144 %150 | 
|  | OpStore %158 %157 | 
|  | %162 = OpIMul %10 %24 %181 | 
|  | %164 = OpIAdd %10 %162 %108 | 
|  | %165 = OpIAdd %10 %164 %76 | 
|  | %168 = OpIMul %10 %24 %181 | 
|  | %170 = OpIMul %10 %24 %108 | 
|  | %171 = OpIAdd %10 %168 %170 | 
|  | %172 = OpIAdd %10 %171 %76 | 
|  | %173 = OpAccessChain %11 %159 %172 | 
|  | %174 = OpLoad %10 %173 | 
|  | %175 = OpAccessChain %11 %159 %165 | 
|  | OpStore %175 %174 | 
|  | OpBranch %116 | 
|  | %116 = OpLabel | 
|  | %177 = OpISub %10 %181 %76 | 
|  | OpStore %111 %177 | 
|  | OpBranch %113 | 
|  | %115 = 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; | 
|  | // For the loop in 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[4]; | 
|  | int stores_found = 0; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 29)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store[stores_found] = &inst; | 
|  | ++stores_found; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < 4; ++i) { | 
|  | EXPECT_TRUE(store[i]); | 
|  | } | 
|  |  | 
|  | // independent due to loop bounds (won't enter when N <= 0) | 
|  | // 49 -> 50 tests looking through SIV and symbols with multiplication | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | // Independent but not yet supported. | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(49), store[0], &distance_vector)); | 
|  | } | 
|  |  | 
|  | // 63 -> 66 tests looking through SIV and symbols with multiplication and + | 
|  | // C | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | // Independent. | 
|  | EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(63), | 
|  | store[1], &distance_vector)); | 
|  | } | 
|  |  | 
|  | // 84 -> 85 tests looking through arithmetic on SIV and symbols | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | // Independent but not yet supported. | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(84), store[2], &distance_vector)); | 
|  | } | 
|  |  | 
|  | // 101 -> 102 tests looking through symbol arithmetic on SIV and symbols | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | // Independent. | 
|  | EXPECT_TRUE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(101), store[3], &distance_vector)); | 
|  | } | 
|  | } | 
|  | // For the loop in 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[4]; | 
|  | int stores_found = 0; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 114)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store[stores_found] = &inst; | 
|  | ++stores_found; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < 4; ++i) { | 
|  | EXPECT_TRUE(store[i]); | 
|  | } | 
|  |  | 
|  | // independent due to loop bounds (won't enter when N <= 0). | 
|  | // 129 -> 130 tests looking through SIV and symbols with multiplication. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | // Independent but not yet supported. | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(129), store[0], &distance_vector)); | 
|  | } | 
|  |  | 
|  | // 140 -> 143 tests looking through SIV and symbols with multiplication and | 
|  | // + C. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | // Independent. | 
|  | EXPECT_TRUE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(140), store[1], &distance_vector)); | 
|  | } | 
|  |  | 
|  | // 157 -> 158 tests looking through arithmetic on SIV and symbols. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | // Independent but not yet supported. | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(157), store[2], &distance_vector)); | 
|  | } | 
|  |  | 
|  | // 174 -> 175 tests looking through symbol arithmetic on SIV and symbols. | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | // Independent. | 
|  | EXPECT_TRUE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(174), store[3], &distance_vector)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | Generated from the following GLSL fragment shader | 
|  | with --eliminate-local-multi-store | 
|  | #version 440 core | 
|  | void a() { | 
|  | int[6] arr; | 
|  | int N = 5; | 
|  | for (int i = 1; i < N; i++) { | 
|  | arr[i] = arr[N-i]; | 
|  | } | 
|  | } | 
|  | void b() { | 
|  | int[6] arr; | 
|  | int N = 5; | 
|  | for (int i = 1; i < N; i++) { | 
|  | arr[N-i] = arr[i]; | 
|  | } | 
|  | } | 
|  | void c() { | 
|  | int[11] arr; | 
|  | int N = 10; | 
|  | for (int i = 1; i < N; i++) { | 
|  | arr[i] = arr[N-i+1]; | 
|  | } | 
|  | } | 
|  | void d() { | 
|  | int[11] arr; | 
|  | int N = 10; | 
|  | for (int i = 1; i < N; i++) { | 
|  | arr[N-i+1] = arr[i]; | 
|  | } | 
|  | } | 
|  | void e() { | 
|  | int[6] arr; | 
|  | int N = 5; | 
|  | for (int i = N; i > 0; i--) { | 
|  | arr[i] = arr[N-i]; | 
|  | } | 
|  | } | 
|  | void f() { | 
|  | int[6] arr; | 
|  | int N = 5; | 
|  | for (int i = N; i > 0; i--) { | 
|  | arr[N-i] = arr[i]; | 
|  | } | 
|  | } | 
|  | void g() { | 
|  | int[11] arr; | 
|  | int N = 10; | 
|  | for (int i = N; i > 0; i--) { | 
|  | arr[i] = arr[N-i+1]; | 
|  | } | 
|  | } | 
|  | void h() { | 
|  | int[11] arr; | 
|  | int N = 10; | 
|  | for (int i = N; i > 0; i--) { | 
|  | arr[N-i+1] = arr[i]; | 
|  | } | 
|  | } | 
|  | void main(){ | 
|  | a(); | 
|  | b(); | 
|  | c(); | 
|  | d(); | 
|  | e(); | 
|  | f(); | 
|  | g(); | 
|  | h(); | 
|  | } | 
|  | */ | 
|  | TEST(DependencyAnalysis, Crossing) { | 
|  | 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 %24 "N" | 
|  | OpName %26 "i" | 
|  | OpName %41 "arr" | 
|  | OpName %51 "N" | 
|  | OpName %52 "i" | 
|  | OpName %61 "arr" | 
|  | OpName %71 "N" | 
|  | OpName %73 "i" | 
|  | OpName %85 "arr" | 
|  | OpName %96 "N" | 
|  | OpName %97 "i" | 
|  | OpName %106 "arr" | 
|  | OpName %117 "N" | 
|  | OpName %118 "i" | 
|  | OpName %128 "arr" | 
|  | OpName %138 "N" | 
|  | OpName %139 "i" | 
|  | OpName %148 "arr" | 
|  | OpName %158 "N" | 
|  | OpName %159 "i" | 
|  | OpName %168 "arr" | 
|  | OpName %179 "N" | 
|  | OpName %180 "i" | 
|  | OpName %189 "arr" | 
|  | %2 = OpTypeVoid | 
|  | %3 = OpTypeFunction %2 | 
|  | %22 = OpTypeInt 32 1 | 
|  | %23 = OpTypePointer Function %22 | 
|  | %25 = OpConstant %22 5 | 
|  | %27 = OpConstant %22 1 | 
|  | %35 = OpTypeBool | 
|  | %37 = OpTypeInt 32 0 | 
|  | %38 = OpConstant %37 6 | 
|  | %39 = OpTypeArray %22 %38 | 
|  | %40 = OpTypePointer Function %39 | 
|  | %72 = OpConstant %22 10 | 
|  | %82 = OpConstant %37 11 | 
|  | %83 = OpTypeArray %22 %82 | 
|  | %84 = OpTypePointer Function %83 | 
|  | %126 = OpConstant %22 0 | 
|  | %4 = OpFunction %2 None %3 | 
|  | %5 = OpLabel | 
|  | %200 = OpFunctionCall %2 %6 | 
|  | %201 = OpFunctionCall %2 %8 | 
|  | %202 = OpFunctionCall %2 %10 | 
|  | %203 = OpFunctionCall %2 %12 | 
|  | %204 = OpFunctionCall %2 %14 | 
|  | %205 = OpFunctionCall %2 %16 | 
|  | %206 = OpFunctionCall %2 %18 | 
|  | %207 = OpFunctionCall %2 %20 | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %6 = OpFunction %2 None %3 | 
|  | %7 = OpLabel | 
|  | %24 = OpVariable %23 Function | 
|  | %26 = OpVariable %23 Function | 
|  | %41 = OpVariable %40 Function | 
|  | OpStore %24 %25 | 
|  | OpStore %26 %27 | 
|  | OpBranch %28 | 
|  | %28 = OpLabel | 
|  | %208 = OpPhi %22 %27 %7 %50 %31 | 
|  | OpLoopMerge %30 %31 None | 
|  | OpBranch %32 | 
|  | %32 = OpLabel | 
|  | %36 = OpSLessThan %35 %208 %25 | 
|  | OpBranchConditional %36 %29 %30 | 
|  | %29 = OpLabel | 
|  | %45 = OpISub %22 %25 %208 | 
|  | %46 = OpAccessChain %23 %41 %45 | 
|  | %47 = OpLoad %22 %46 | 
|  | %48 = OpAccessChain %23 %41 %208 | 
|  | OpStore %48 %47 | 
|  | OpBranch %31 | 
|  | %31 = OpLabel | 
|  | %50 = OpIAdd %22 %208 %27 | 
|  | OpStore %26 %50 | 
|  | OpBranch %28 | 
|  | %30 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %8 = OpFunction %2 None %3 | 
|  | %9 = OpLabel | 
|  | %51 = OpVariable %23 Function | 
|  | %52 = OpVariable %23 Function | 
|  | %61 = OpVariable %40 Function | 
|  | OpStore %51 %25 | 
|  | OpStore %52 %27 | 
|  | OpBranch %53 | 
|  | %53 = OpLabel | 
|  | %209 = OpPhi %22 %27 %9 %70 %56 | 
|  | OpLoopMerge %55 %56 None | 
|  | OpBranch %57 | 
|  | %57 = OpLabel | 
|  | %60 = OpSLessThan %35 %209 %25 | 
|  | OpBranchConditional %60 %54 %55 | 
|  | %54 = OpLabel | 
|  | %64 = OpISub %22 %25 %209 | 
|  | %66 = OpAccessChain %23 %61 %209 | 
|  | %67 = OpLoad %22 %66 | 
|  | %68 = OpAccessChain %23 %61 %64 | 
|  | OpStore %68 %67 | 
|  | OpBranch %56 | 
|  | %56 = OpLabel | 
|  | %70 = OpIAdd %22 %209 %27 | 
|  | OpStore %52 %70 | 
|  | OpBranch %53 | 
|  | %55 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %10 = OpFunction %2 None %3 | 
|  | %11 = OpLabel | 
|  | %71 = OpVariable %23 Function | 
|  | %73 = OpVariable %23 Function | 
|  | %85 = OpVariable %84 Function | 
|  | OpStore %71 %72 | 
|  | OpStore %73 %27 | 
|  | OpBranch %74 | 
|  | %74 = OpLabel | 
|  | %210 = OpPhi %22 %27 %11 %95 %77 | 
|  | OpLoopMerge %76 %77 None | 
|  | OpBranch %78 | 
|  | %78 = OpLabel | 
|  | %81 = OpSLessThan %35 %210 %72 | 
|  | OpBranchConditional %81 %75 %76 | 
|  | %75 = OpLabel | 
|  | %89 = OpISub %22 %72 %210 | 
|  | %90 = OpIAdd %22 %89 %27 | 
|  | %91 = OpAccessChain %23 %85 %90 | 
|  | %92 = OpLoad %22 %91 | 
|  | %93 = OpAccessChain %23 %85 %210 | 
|  | OpStore %93 %92 | 
|  | OpBranch %77 | 
|  | %77 = OpLabel | 
|  | %95 = OpIAdd %22 %210 %27 | 
|  | OpStore %73 %95 | 
|  | OpBranch %74 | 
|  | %76 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %12 = OpFunction %2 None %3 | 
|  | %13 = OpLabel | 
|  | %96 = OpVariable %23 Function | 
|  | %97 = OpVariable %23 Function | 
|  | %106 = OpVariable %84 Function | 
|  | OpStore %96 %72 | 
|  | OpStore %97 %27 | 
|  | OpBranch %98 | 
|  | %98 = OpLabel | 
|  | %211 = OpPhi %22 %27 %13 %116 %101 | 
|  | OpLoopMerge %100 %101 None | 
|  | OpBranch %102 | 
|  | %102 = OpLabel | 
|  | %105 = OpSLessThan %35 %211 %72 | 
|  | OpBranchConditional %105 %99 %100 | 
|  | %99 = OpLabel | 
|  | %109 = OpISub %22 %72 %211 | 
|  | %110 = OpIAdd %22 %109 %27 | 
|  | %112 = OpAccessChain %23 %106 %211 | 
|  | %113 = OpLoad %22 %112 | 
|  | %114 = OpAccessChain %23 %106 %110 | 
|  | OpStore %114 %113 | 
|  | OpBranch %101 | 
|  | %101 = OpLabel | 
|  | %116 = OpIAdd %22 %211 %27 | 
|  | OpStore %97 %116 | 
|  | OpBranch %98 | 
|  | %100 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %14 = OpFunction %2 None %3 | 
|  | %15 = OpLabel | 
|  | %117 = OpVariable %23 Function | 
|  | %118 = OpVariable %23 Function | 
|  | %128 = OpVariable %40 Function | 
|  | OpStore %117 %25 | 
|  | OpStore %118 %25 | 
|  | OpBranch %120 | 
|  | %120 = OpLabel | 
|  | %212 = OpPhi %22 %25 %15 %137 %123 | 
|  | OpLoopMerge %122 %123 None | 
|  | OpBranch %124 | 
|  | %124 = OpLabel | 
|  | %127 = OpSGreaterThan %35 %212 %126 | 
|  | OpBranchConditional %127 %121 %122 | 
|  | %121 = OpLabel | 
|  | %132 = OpISub %22 %25 %212 | 
|  | %133 = OpAccessChain %23 %128 %132 | 
|  | %134 = OpLoad %22 %133 | 
|  | %135 = OpAccessChain %23 %128 %212 | 
|  | OpStore %135 %134 | 
|  | OpBranch %123 | 
|  | %123 = OpLabel | 
|  | %137 = OpISub %22 %212 %27 | 
|  | OpStore %118 %137 | 
|  | OpBranch %120 | 
|  | %122 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %16 = OpFunction %2 None %3 | 
|  | %17 = OpLabel | 
|  | %138 = OpVariable %23 Function | 
|  | %139 = OpVariable %23 Function | 
|  | %148 = OpVariable %40 Function | 
|  | OpStore %138 %25 | 
|  | OpStore %139 %25 | 
|  | OpBranch %141 | 
|  | %141 = OpLabel | 
|  | %213 = OpPhi %22 %25 %17 %157 %144 | 
|  | OpLoopMerge %143 %144 None | 
|  | OpBranch %145 | 
|  | %145 = OpLabel | 
|  | %147 = OpSGreaterThan %35 %213 %126 | 
|  | OpBranchConditional %147 %142 %143 | 
|  | %142 = OpLabel | 
|  | %151 = OpISub %22 %25 %213 | 
|  | %153 = OpAccessChain %23 %148 %213 | 
|  | %154 = OpLoad %22 %153 | 
|  | %155 = OpAccessChain %23 %148 %151 | 
|  | OpStore %155 %154 | 
|  | OpBranch %144 | 
|  | %144 = OpLabel | 
|  | %157 = OpISub %22 %213 %27 | 
|  | OpStore %139 %157 | 
|  | OpBranch %141 | 
|  | %143 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %18 = OpFunction %2 None %3 | 
|  | %19 = OpLabel | 
|  | %158 = OpVariable %23 Function | 
|  | %159 = OpVariable %23 Function | 
|  | %168 = OpVariable %84 Function | 
|  | OpStore %158 %72 | 
|  | OpStore %159 %72 | 
|  | OpBranch %161 | 
|  | %161 = OpLabel | 
|  | %214 = OpPhi %22 %72 %19 %178 %164 | 
|  | OpLoopMerge %163 %164 None | 
|  | OpBranch %165 | 
|  | %165 = OpLabel | 
|  | %167 = OpSGreaterThan %35 %214 %126 | 
|  | OpBranchConditional %167 %162 %163 | 
|  | %162 = OpLabel | 
|  | %172 = OpISub %22 %72 %214 | 
|  | %173 = OpIAdd %22 %172 %27 | 
|  | %174 = OpAccessChain %23 %168 %173 | 
|  | %175 = OpLoad %22 %174 | 
|  | %176 = OpAccessChain %23 %168 %214 | 
|  | OpStore %176 %175 | 
|  | OpBranch %164 | 
|  | %164 = OpLabel | 
|  | %178 = OpISub %22 %214 %27 | 
|  | OpStore %159 %178 | 
|  | OpBranch %161 | 
|  | %163 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %20 = OpFunction %2 None %3 | 
|  | %21 = OpLabel | 
|  | %179 = OpVariable %23 Function | 
|  | %180 = OpVariable %23 Function | 
|  | %189 = OpVariable %84 Function | 
|  | OpStore %179 %72 | 
|  | OpStore %180 %72 | 
|  | OpBranch %182 | 
|  | %182 = OpLabel | 
|  | %215 = OpPhi %22 %72 %21 %199 %185 | 
|  | OpLoopMerge %184 %185 None | 
|  | OpBranch %186 | 
|  | %186 = OpLabel | 
|  | %188 = OpSGreaterThan %35 %215 %126 | 
|  | OpBranchConditional %188 %183 %184 | 
|  | %183 = OpLabel | 
|  | %192 = OpISub %22 %72 %215 | 
|  | %193 = OpIAdd %22 %192 %27 | 
|  | %195 = OpAccessChain %23 %189 %215 | 
|  | %196 = OpLoad %22 %195 | 
|  | %197 = OpAccessChain %23 %189 %193 | 
|  | OpStore %197 %196 | 
|  | OpBranch %185 | 
|  | %185 = OpLabel | 
|  | %199 = OpISub %22 %215 %27 | 
|  | OpStore %180 %199 | 
|  | OpBranch %182 | 
|  | %184 = 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; | 
|  |  | 
|  | // First two tests can be split into two loops. | 
|  | // Tests even crossing subscripts from low to high indexes. | 
|  | // 47 -> 48 | 
|  | { | 
|  | 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 = nullptr; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 29)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store = &inst; | 
|  | } | 
|  | } | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(47), | 
|  | store, &distance_vector)); | 
|  | } | 
|  |  | 
|  | // Tests even crossing subscripts from high to low indexes. | 
|  | // 67 -> 68 | 
|  | { | 
|  | 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 = nullptr; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 54)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store = &inst; | 
|  | } | 
|  | } | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(67), | 
|  | store, &distance_vector)); | 
|  | } | 
|  |  | 
|  | // Next two tests can have an end peeled, then be split. | 
|  | // Tests uneven crossing subscripts from low to high indexes. | 
|  | // 92 -> 93 | 
|  | { | 
|  | 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* store = nullptr; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 75)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store = &inst; | 
|  | } | 
|  | } | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(92), | 
|  | store, &distance_vector)); | 
|  | } | 
|  |  | 
|  | // Tests uneven crossing subscripts from high to low indexes. | 
|  | // 113 -> 114 | 
|  | { | 
|  | 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* store = nullptr; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 99)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store = &inst; | 
|  | } | 
|  | } | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(113), | 
|  | store, &distance_vector)); | 
|  | } | 
|  |  | 
|  | // First two tests can be split into two loops. | 
|  | // Tests even crossing subscripts from low to high indexes. | 
|  | // 134 -> 135 | 
|  | { | 
|  | 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}; | 
|  |  | 
|  | const Instruction* store = nullptr; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 121)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store = &inst; | 
|  | } | 
|  | } | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(134), | 
|  | store, &distance_vector)); | 
|  | } | 
|  |  | 
|  | // Tests even crossing subscripts from high to low indexes. | 
|  | // 154 -> 155 | 
|  | { | 
|  | 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}; | 
|  |  | 
|  | const Instruction* store = nullptr; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 142)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store = &inst; | 
|  | } | 
|  | } | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(154), | 
|  | store, &distance_vector)); | 
|  | } | 
|  |  | 
|  | // Next two tests can have an end peeled, then be split. | 
|  | // Tests uneven crossing subscripts from low to high indexes. | 
|  | // 175 -> 176 | 
|  | { | 
|  | 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}; | 
|  |  | 
|  | const Instruction* store = nullptr; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 162)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store = &inst; | 
|  | } | 
|  | } | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(175), | 
|  | store, &distance_vector)); | 
|  | } | 
|  |  | 
|  | // Tests uneven crossing subscripts from high to low indexes. | 
|  | // 196 -> 197 | 
|  | { | 
|  | 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}; | 
|  |  | 
|  | const Instruction* store = nullptr; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 183)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store = &inst; | 
|  | } | 
|  | } | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(196), | 
|  | store, &distance_vector)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | Generated from the following GLSL fragment shader | 
|  | with --eliminate-local-multi-store | 
|  | #version 440 core | 
|  | void a() { | 
|  | int[10] arr; | 
|  | for (int i = 0; i < 10; i++) { | 
|  | arr[0] = arr[i]; // peel first | 
|  | arr[i] = arr[0]; // peel first | 
|  | arr[9] = arr[i]; // peel last | 
|  | arr[i] = arr[9]; // peel last | 
|  | } | 
|  | } | 
|  | void b() { | 
|  | int[11] arr; | 
|  | for (int i = 0; i <= 10; i++) { | 
|  | arr[0] = arr[i]; // peel first | 
|  | arr[i] = arr[0]; // peel first | 
|  | arr[10] = arr[i]; // peel last | 
|  | arr[i] = arr[10]; // peel last | 
|  |  | 
|  | } | 
|  | } | 
|  | void c() { | 
|  | int[11] arr; | 
|  | for (int i = 10; i > 0; i--) { | 
|  | arr[10] = arr[i]; // peel first | 
|  | arr[i] = arr[10]; // peel first | 
|  | arr[1] = arr[i]; // peel last | 
|  | arr[i] = arr[1]; // peel last | 
|  |  | 
|  | } | 
|  | } | 
|  | void d() { | 
|  | int[11] arr; | 
|  | for (int i = 10; i >= 0; i--) { | 
|  | arr[10] = arr[i]; // peel first | 
|  | arr[i] = arr[10]; // peel first | 
|  | arr[0] = arr[i]; // peel last | 
|  | arr[i] = arr[0]; // peel last | 
|  |  | 
|  | } | 
|  | } | 
|  | void main(){ | 
|  | a(); | 
|  | b(); | 
|  | c(); | 
|  | d(); | 
|  | } | 
|  | */ | 
|  | TEST(DependencyAnalysis, WeakZeroSIV) { | 
|  | 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 %16 "i" | 
|  | OpName %31 "arr" | 
|  | OpName %52 "i" | 
|  | OpName %63 "arr" | 
|  | OpName %82 "i" | 
|  | OpName %90 "arr" | 
|  | OpName %109 "i" | 
|  | OpName %117 "arr" | 
|  | %2 = OpTypeVoid | 
|  | %3 = OpTypeFunction %2 | 
|  | %14 = OpTypeInt 32 1 | 
|  | %15 = OpTypePointer Function %14 | 
|  | %17 = OpConstant %14 0 | 
|  | %24 = OpConstant %14 10 | 
|  | %25 = OpTypeBool | 
|  | %27 = OpTypeInt 32 0 | 
|  | %28 = OpConstant %27 10 | 
|  | %29 = OpTypeArray %14 %28 | 
|  | %30 = OpTypePointer Function %29 | 
|  | %40 = OpConstant %14 9 | 
|  | %50 = OpConstant %14 1 | 
|  | %60 = OpConstant %27 11 | 
|  | %61 = OpTypeArray %14 %60 | 
|  | %62 = OpTypePointer Function %61 | 
|  | %4 = OpFunction %2 None %3 | 
|  | %5 = OpLabel | 
|  | %136 = OpFunctionCall %2 %6 | 
|  | %137 = OpFunctionCall %2 %8 | 
|  | %138 = OpFunctionCall %2 %10 | 
|  | %139 = OpFunctionCall %2 %12 | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %6 = OpFunction %2 None %3 | 
|  | %7 = OpLabel | 
|  | %16 = OpVariable %15 Function | 
|  | %31 = OpVariable %30 Function | 
|  | OpStore %16 %17 | 
|  | OpBranch %18 | 
|  | %18 = OpLabel | 
|  | %140 = OpPhi %14 %17 %7 %51 %21 | 
|  | OpLoopMerge %20 %21 None | 
|  | OpBranch %22 | 
|  | %22 = OpLabel | 
|  | %26 = OpSLessThan %25 %140 %24 | 
|  | OpBranchConditional %26 %19 %20 | 
|  | %19 = OpLabel | 
|  | %33 = OpAccessChain %15 %31 %140 | 
|  | %34 = OpLoad %14 %33 | 
|  | %35 = OpAccessChain %15 %31 %17 | 
|  | OpStore %35 %34 | 
|  | %37 = OpAccessChain %15 %31 %17 | 
|  | %38 = OpLoad %14 %37 | 
|  | %39 = OpAccessChain %15 %31 %140 | 
|  | OpStore %39 %38 | 
|  | %42 = OpAccessChain %15 %31 %140 | 
|  | %43 = OpLoad %14 %42 | 
|  | %44 = OpAccessChain %15 %31 %40 | 
|  | OpStore %44 %43 | 
|  | %46 = OpAccessChain %15 %31 %40 | 
|  | %47 = OpLoad %14 %46 | 
|  | %48 = OpAccessChain %15 %31 %140 | 
|  | OpStore %48 %47 | 
|  | OpBranch %21 | 
|  | %21 = OpLabel | 
|  | %51 = OpIAdd %14 %140 %50 | 
|  | OpStore %16 %51 | 
|  | OpBranch %18 | 
|  | %20 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %8 = OpFunction %2 None %3 | 
|  | %9 = OpLabel | 
|  | %52 = OpVariable %15 Function | 
|  | %63 = OpVariable %62 Function | 
|  | OpStore %52 %17 | 
|  | OpBranch %53 | 
|  | %53 = OpLabel | 
|  | %141 = OpPhi %14 %17 %9 %81 %56 | 
|  | OpLoopMerge %55 %56 None | 
|  | OpBranch %57 | 
|  | %57 = OpLabel | 
|  | %59 = OpSLessThanEqual %25 %141 %24 | 
|  | OpBranchConditional %59 %54 %55 | 
|  | %54 = OpLabel | 
|  | %65 = OpAccessChain %15 %63 %141 | 
|  | %66 = OpLoad %14 %65 | 
|  | %67 = OpAccessChain %15 %63 %17 | 
|  | OpStore %67 %66 | 
|  | %69 = OpAccessChain %15 %63 %17 | 
|  | %70 = OpLoad %14 %69 | 
|  | %71 = OpAccessChain %15 %63 %141 | 
|  | OpStore %71 %70 | 
|  | %73 = OpAccessChain %15 %63 %141 | 
|  | %74 = OpLoad %14 %73 | 
|  | %75 = OpAccessChain %15 %63 %24 | 
|  | OpStore %75 %74 | 
|  | %77 = OpAccessChain %15 %63 %24 | 
|  | %78 = OpLoad %14 %77 | 
|  | %79 = OpAccessChain %15 %63 %141 | 
|  | OpStore %79 %78 | 
|  | OpBranch %56 | 
|  | %56 = OpLabel | 
|  | %81 = OpIAdd %14 %141 %50 | 
|  | OpStore %52 %81 | 
|  | OpBranch %53 | 
|  | %55 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %10 = OpFunction %2 None %3 | 
|  | %11 = OpLabel | 
|  | %82 = OpVariable %15 Function | 
|  | %90 = OpVariable %62 Function | 
|  | OpStore %82 %24 | 
|  | OpBranch %83 | 
|  | %83 = OpLabel | 
|  | %142 = OpPhi %14 %24 %11 %108 %86 | 
|  | OpLoopMerge %85 %86 None | 
|  | OpBranch %87 | 
|  | %87 = OpLabel | 
|  | %89 = OpSGreaterThan %25 %142 %17 | 
|  | OpBranchConditional %89 %84 %85 | 
|  | %84 = OpLabel | 
|  | %92 = OpAccessChain %15 %90 %142 | 
|  | %93 = OpLoad %14 %92 | 
|  | %94 = OpAccessChain %15 %90 %24 | 
|  | OpStore %94 %93 | 
|  | %96 = OpAccessChain %15 %90 %24 | 
|  | %97 = OpLoad %14 %96 | 
|  | %98 = OpAccessChain %15 %90 %142 | 
|  | OpStore %98 %97 | 
|  | %100 = OpAccessChain %15 %90 %142 | 
|  | %101 = OpLoad %14 %100 | 
|  | %102 = OpAccessChain %15 %90 %50 | 
|  | OpStore %102 %101 | 
|  | %104 = OpAccessChain %15 %90 %50 | 
|  | %105 = OpLoad %14 %104 | 
|  | %106 = OpAccessChain %15 %90 %142 | 
|  | OpStore %106 %105 | 
|  | OpBranch %86 | 
|  | %86 = OpLabel | 
|  | %108 = OpISub %14 %142 %50 | 
|  | OpStore %82 %108 | 
|  | OpBranch %83 | 
|  | %85 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %12 = OpFunction %2 None %3 | 
|  | %13 = OpLabel | 
|  | %109 = OpVariable %15 Function | 
|  | %117 = OpVariable %62 Function | 
|  | OpStore %109 %24 | 
|  | OpBranch %110 | 
|  | %110 = OpLabel | 
|  | %143 = OpPhi %14 %24 %13 %135 %113 | 
|  | OpLoopMerge %112 %113 None | 
|  | OpBranch %114 | 
|  | %114 = OpLabel | 
|  | %116 = OpSGreaterThanEqual %25 %143 %17 | 
|  | OpBranchConditional %116 %111 %112 | 
|  | %111 = OpLabel | 
|  | %119 = OpAccessChain %15 %117 %143 | 
|  | %120 = OpLoad %14 %119 | 
|  | %121 = OpAccessChain %15 %117 %24 | 
|  | OpStore %121 %120 | 
|  | %123 = OpAccessChain %15 %117 %24 | 
|  | %124 = OpLoad %14 %123 | 
|  | %125 = OpAccessChain %15 %117 %143 | 
|  | OpStore %125 %124 | 
|  | %127 = OpAccessChain %15 %117 %143 | 
|  | %128 = OpLoad %14 %127 | 
|  | %129 = OpAccessChain %15 %117 %17 | 
|  | OpStore %129 %128 | 
|  | %131 = OpAccessChain %15 %117 %17 | 
|  | %132 = OpLoad %14 %131 | 
|  | %133 = OpAccessChain %15 %117 %143 | 
|  | OpStore %133 %132 | 
|  | OpBranch %113 | 
|  | %113 = OpLabel | 
|  | %135 = OpISub %14 %143 %50 | 
|  | OpStore %109 %135 | 
|  | OpBranch %110 | 
|  | %112 = 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; | 
|  | // For the loop in 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[4]; | 
|  | int stores_found = 0; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 19)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store[stores_found] = &inst; | 
|  | ++stores_found; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < 4; ++i) { | 
|  | EXPECT_TRUE(store[i]); | 
|  | } | 
|  |  | 
|  | // Tests identifying peel first with weak zero with destination as zero | 
|  | // index. | 
|  | // 34 -> 35 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(34), store[0], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::PEEL); | 
|  | EXPECT_TRUE(distance_vector.GetEntries()[0].peel_first); | 
|  | } | 
|  |  | 
|  | // Tests identifying peel first with weak zero with source as zero index. | 
|  | // 38 -> 39 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(38), store[1], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::PEEL); | 
|  | EXPECT_TRUE(distance_vector.GetEntries()[0].peel_first); | 
|  | } | 
|  |  | 
|  | // Tests identifying peel first with weak zero with destination as zero | 
|  | // index. | 
|  | // 43 -> 44 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(43), store[2], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::PEEL); | 
|  | EXPECT_TRUE(distance_vector.GetEntries()[0].peel_last); | 
|  | } | 
|  |  | 
|  | // Tests identifying peel first with weak zero with source as zero index. | 
|  | // 47 -> 48 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(47), store[3], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::PEEL); | 
|  | EXPECT_TRUE(distance_vector.GetEntries()[0].peel_last); | 
|  | } | 
|  | } | 
|  | // For the loop in 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[4]; | 
|  | int stores_found = 0; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 54)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store[stores_found] = &inst; | 
|  | ++stores_found; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < 4; ++i) { | 
|  | EXPECT_TRUE(store[i]); | 
|  | } | 
|  |  | 
|  | // Tests identifying peel first with weak zero with destination as zero | 
|  | // index. | 
|  | // 66 -> 67 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(66), store[0], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::PEEL); | 
|  | EXPECT_TRUE(distance_vector.GetEntries()[0].peel_first); | 
|  | } | 
|  |  | 
|  | // Tests identifying peel first with weak zero with source as zero index. | 
|  | // 70 -> 71 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(70), store[1], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::PEEL); | 
|  | EXPECT_TRUE(distance_vector.GetEntries()[0].peel_first); | 
|  | } | 
|  |  | 
|  | // Tests identifying peel first with weak zero with destination as zero | 
|  | // index. | 
|  | // 74 -> 75 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(74), store[2], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::PEEL); | 
|  | EXPECT_TRUE(distance_vector.GetEntries()[0].peel_last); | 
|  | } | 
|  |  | 
|  | // Tests identifying peel first with weak zero with source as zero index. | 
|  | // 78 -> 79 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(78), store[3], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::PEEL); | 
|  | EXPECT_TRUE(distance_vector.GetEntries()[0].peel_last); | 
|  | } | 
|  | } | 
|  | // For the loop in 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* store[4]; | 
|  | int stores_found = 0; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 84)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store[stores_found] = &inst; | 
|  | ++stores_found; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < 4; ++i) { | 
|  | EXPECT_TRUE(store[i]); | 
|  | } | 
|  |  | 
|  | // Tests identifying peel first with weak zero with destination as zero | 
|  | // index. | 
|  | // 93 -> 94 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(93), store[0], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::PEEL); | 
|  | EXPECT_TRUE(distance_vector.GetEntries()[0].peel_first); | 
|  | } | 
|  |  | 
|  | // Tests identifying peel first with weak zero with source as zero index. | 
|  | // 97 -> 98 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(97), store[1], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::PEEL); | 
|  | EXPECT_TRUE(distance_vector.GetEntries()[0].peel_first); | 
|  | } | 
|  |  | 
|  | // Tests identifying peel first with weak zero with destination as zero | 
|  | // index. | 
|  | // 101 -> 102 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(101), store[2], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::PEEL); | 
|  | EXPECT_TRUE(distance_vector.GetEntries()[0].peel_last); | 
|  | } | 
|  |  | 
|  | // Tests identifying peel first with weak zero with source as zero index. | 
|  | // 105 -> 106 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(105), store[3], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::PEEL); | 
|  | EXPECT_TRUE(distance_vector.GetEntries()[0].peel_last); | 
|  | } | 
|  | } | 
|  | // For the loop in 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* store[4]; | 
|  | int stores_found = 0; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 111)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store[stores_found] = &inst; | 
|  | ++stores_found; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < 4; ++i) { | 
|  | EXPECT_TRUE(store[i]); | 
|  | } | 
|  |  | 
|  | // Tests identifying peel first with weak zero with destination as zero | 
|  | // index. | 
|  | // 120 -> 121 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(120), store[0], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::PEEL); | 
|  | EXPECT_TRUE(distance_vector.GetEntries()[0].peel_first); | 
|  | } | 
|  |  | 
|  | // Tests identifying peel first with weak zero with source as zero index. | 
|  | // 124 -> 125 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(124), store[1], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::PEEL); | 
|  | EXPECT_TRUE(distance_vector.GetEntries()[0].peel_first); | 
|  | } | 
|  |  | 
|  | // Tests identifying peel first with weak zero with destination as zero | 
|  | // index. | 
|  | // 128 -> 129 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(128), store[2], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::PEEL); | 
|  | EXPECT_TRUE(distance_vector.GetEntries()[0].peel_last); | 
|  | } | 
|  |  | 
|  | // Tests identifying peel first with weak zero with source as zero index. | 
|  | // 132 -> 133 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(132), store[3], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::PEEL); | 
|  | EXPECT_TRUE(distance_vector.GetEntries()[0].peel_last); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | Generated from the following GLSL fragment shader | 
|  | with --eliminate-local-multi-store | 
|  | #version 440 core | 
|  | void main(){ | 
|  | int[10][10] arr; | 
|  | for (int i = 0; i < 10; i++) { | 
|  | arr[i][i] = arr[i][i]; | 
|  | arr[0][i] = arr[1][i]; | 
|  | arr[1][i] = arr[0][i]; | 
|  | arr[i][0] = arr[i][1]; | 
|  | arr[i][1] = arr[i][0]; | 
|  | arr[0][1] = arr[1][0]; | 
|  | } | 
|  | } | 
|  | */ | 
|  | TEST(DependencyAnalysis, MultipleSubscriptZIVSIV) { | 
|  | 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" | 
|  | OpName %24 "arr" | 
|  | %2 = OpTypeVoid | 
|  | %3 = OpTypeFunction %2 | 
|  | %6 = OpTypeInt 32 1 | 
|  | %7 = OpTypePointer Function %6 | 
|  | %9 = OpConstant %6 0 | 
|  | %16 = OpConstant %6 10 | 
|  | %17 = OpTypeBool | 
|  | %19 = OpTypeInt 32 0 | 
|  | %20 = OpConstant %19 10 | 
|  | %21 = OpTypeArray %6 %20 | 
|  | %22 = OpTypeArray %21 %20 | 
|  | %23 = OpTypePointer Function %22 | 
|  | %33 = OpConstant %6 1 | 
|  | %4 = OpFunction %2 None %3 | 
|  | %5 = OpLabel | 
|  | %8 = OpVariable %7 Function | 
|  | %24 = OpVariable %23 Function | 
|  | OpStore %8 %9 | 
|  | OpBranch %10 | 
|  | %10 = OpLabel | 
|  | %58 = OpPhi %6 %9 %5 %57 %13 | 
|  | OpLoopMerge %12 %13 None | 
|  | OpBranch %14 | 
|  | %14 = OpLabel | 
|  | %18 = OpSLessThan %17 %58 %16 | 
|  | OpBranchConditional %18 %11 %12 | 
|  | %11 = OpLabel | 
|  | %29 = OpAccessChain %7 %24 %58 %58 | 
|  | %30 = OpLoad %6 %29 | 
|  | %31 = OpAccessChain %7 %24 %58 %58 | 
|  | OpStore %31 %30 | 
|  | %35 = OpAccessChain %7 %24 %33 %58 | 
|  | %36 = OpLoad %6 %35 | 
|  | %37 = OpAccessChain %7 %24 %9 %58 | 
|  | OpStore %37 %36 | 
|  | %40 = OpAccessChain %7 %24 %9 %58 | 
|  | %41 = OpLoad %6 %40 | 
|  | %42 = OpAccessChain %7 %24 %33 %58 | 
|  | OpStore %42 %41 | 
|  | %45 = OpAccessChain %7 %24 %58 %33 | 
|  | %46 = OpLoad %6 %45 | 
|  | %47 = OpAccessChain %7 %24 %58 %9 | 
|  | OpStore %47 %46 | 
|  | %50 = OpAccessChain %7 %24 %58 %9 | 
|  | %51 = OpLoad %6 %50 | 
|  | %52 = OpAccessChain %7 %24 %58 %33 | 
|  | OpStore %52 %51 | 
|  | %53 = OpAccessChain %7 %24 %33 %9 | 
|  | %54 = OpLoad %6 %53 | 
|  | %55 = OpAccessChain %7 %24 %9 %33 | 
|  | OpStore %55 %54 | 
|  | OpBranch %13 | 
|  | %13 = OpLabel | 
|  | %57 = OpIAdd %6 %58 %33 | 
|  | OpStore %8 %57 | 
|  | 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; | 
|  | 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}; | 
|  |  | 
|  | const Instruction* store[6]; | 
|  | int stores_found = 0; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 11)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store[stores_found] = &inst; | 
|  | ++stores_found; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < 6; ++i) { | 
|  | EXPECT_TRUE(store[i]); | 
|  | } | 
|  |  | 
|  | // 30 -> 31 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(30), | 
|  | store[0], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::DISTANCE); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].direction, | 
|  | DistanceEntry::Directions::EQ); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].distance, 0); | 
|  | } | 
|  |  | 
|  | // 36 -> 37 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(36), | 
|  | store[1], &distance_vector)); | 
|  | } | 
|  |  | 
|  | // 41 -> 42 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(41), | 
|  | store[2], &distance_vector)); | 
|  | } | 
|  |  | 
|  | // 46 -> 47 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(46), | 
|  | store[3], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::DISTANCE); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].direction, | 
|  | DistanceEntry::Directions::EQ); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].distance, 0); | 
|  | } | 
|  |  | 
|  | // 51 -> 52 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(51), | 
|  | store[4], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::DISTANCE); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].direction, | 
|  | DistanceEntry::Directions::EQ); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].distance, 0); | 
|  | } | 
|  |  | 
|  | // 54 -> 55 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(54), | 
|  | store[5], &distance_vector)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | Generated from the following GLSL fragment shader | 
|  | with --eliminate-local-multi-store | 
|  | #version 440 core | 
|  | void a(){ | 
|  | int[10] arr; | 
|  | for (int i = 0; i < 10; i++) { | 
|  | for (int j = 0; j < 10; j++) { | 
|  | arr[j] = arr[j]; | 
|  | } | 
|  | } | 
|  | } | 
|  | void b(){ | 
|  | int[10] arr; | 
|  | for (int i = 0; i < 10; i++) { | 
|  | for (int j = 0; j < 10; j++) { | 
|  | arr[i] = arr[i]; | 
|  | } | 
|  | } | 
|  | } | 
|  | void main() { | 
|  | a(); | 
|  | b(); | 
|  | } | 
|  | */ | 
|  | TEST(DependencyAnalysis, IrrelevantSubscripts) { | 
|  | 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 %23 "j" | 
|  | OpName %35 "arr" | 
|  | OpName %46 "i" | 
|  | OpName %54 "j" | 
|  | OpName %62 "arr" | 
|  | %2 = OpTypeVoid | 
|  | %3 = OpTypeFunction %2 | 
|  | %10 = OpTypeInt 32 1 | 
|  | %11 = OpTypePointer Function %10 | 
|  | %13 = OpConstant %10 0 | 
|  | %20 = OpConstant %10 10 | 
|  | %21 = OpTypeBool | 
|  | %31 = OpTypeInt 32 0 | 
|  | %32 = OpConstant %31 10 | 
|  | %33 = OpTypeArray %10 %32 | 
|  | %34 = OpTypePointer Function %33 | 
|  | %42 = OpConstant %10 1 | 
|  | %4 = OpFunction %2 None %3 | 
|  | %5 = OpLabel | 
|  | %72 = OpFunctionCall %2 %6 | 
|  | %73 = OpFunctionCall %2 %8 | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %6 = OpFunction %2 None %3 | 
|  | %7 = OpLabel | 
|  | %12 = OpVariable %11 Function | 
|  | %23 = OpVariable %11 Function | 
|  | %35 = OpVariable %34 Function | 
|  | OpStore %12 %13 | 
|  | OpBranch %14 | 
|  | %14 = OpLabel | 
|  | %74 = OpPhi %10 %13 %7 %45 %17 | 
|  | OpLoopMerge %16 %17 None | 
|  | OpBranch %18 | 
|  | %18 = OpLabel | 
|  | %22 = OpSLessThan %21 %74 %20 | 
|  | OpBranchConditional %22 %15 %16 | 
|  | %15 = OpLabel | 
|  | OpStore %23 %13 | 
|  | OpBranch %24 | 
|  | %24 = OpLabel | 
|  | %75 = OpPhi %10 %13 %15 %43 %27 | 
|  | OpLoopMerge %26 %27 None | 
|  | OpBranch %28 | 
|  | %28 = OpLabel | 
|  | %30 = OpSLessThan %21 %75 %20 | 
|  | OpBranchConditional %30 %25 %26 | 
|  | %25 = OpLabel | 
|  | %38 = OpAccessChain %11 %35 %75 | 
|  | %39 = OpLoad %10 %38 | 
|  | %40 = OpAccessChain %11 %35 %75 | 
|  | OpStore %40 %39 | 
|  | OpBranch %27 | 
|  | %27 = OpLabel | 
|  | %43 = OpIAdd %10 %75 %42 | 
|  | OpStore %23 %43 | 
|  | OpBranch %24 | 
|  | %26 = OpLabel | 
|  | OpBranch %17 | 
|  | %17 = OpLabel | 
|  | %45 = OpIAdd %10 %74 %42 | 
|  | OpStore %12 %45 | 
|  | OpBranch %14 | 
|  | %16 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %8 = OpFunction %2 None %3 | 
|  | %9 = OpLabel | 
|  | %46 = OpVariable %11 Function | 
|  | %54 = OpVariable %11 Function | 
|  | %62 = OpVariable %34 Function | 
|  | OpStore %46 %13 | 
|  | OpBranch %47 | 
|  | %47 = OpLabel | 
|  | %77 = OpPhi %10 %13 %9 %71 %50 | 
|  | OpLoopMerge %49 %50 None | 
|  | OpBranch %51 | 
|  | %51 = OpLabel | 
|  | %53 = OpSLessThan %21 %77 %20 | 
|  | OpBranchConditional %53 %48 %49 | 
|  | %48 = OpLabel | 
|  | OpStore %54 %13 | 
|  | OpBranch %55 | 
|  | %55 = OpLabel | 
|  | %78 = OpPhi %10 %13 %48 %69 %58 | 
|  | OpLoopMerge %57 %58 None | 
|  | OpBranch %59 | 
|  | %59 = OpLabel | 
|  | %61 = OpSLessThan %21 %78 %20 | 
|  | OpBranchConditional %61 %56 %57 | 
|  | %56 = OpLabel | 
|  | %65 = OpAccessChain %11 %62 %77 | 
|  | %66 = OpLoad %10 %65 | 
|  | %67 = OpAccessChain %11 %62 %77 | 
|  | OpStore %67 %66 | 
|  | OpBranch %58 | 
|  | %58 = OpLabel | 
|  | %69 = OpIAdd %10 %78 %42 | 
|  | OpStore %54 %69 | 
|  | OpBranch %55 | 
|  | %57 = OpLabel | 
|  | OpBranch %50 | 
|  | %50 = OpLabel | 
|  | %71 = OpIAdd %10 %77 %42 | 
|  | OpStore %46 %71 | 
|  | OpBranch %47 | 
|  | %49 = 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; | 
|  | // For the loop in function a | 
|  | { | 
|  | const Function* f = spvtest::GetFunction(module, 6); | 
|  | LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
|  |  | 
|  | std::vector<const Loop*> loops{&ld.GetLoopByIndex(1), | 
|  | &ld.GetLoopByIndex(0)}; | 
|  | LoopDependenceAnalysis analysis{context.get(), loops}; | 
|  |  | 
|  | const Instruction* store[1]; | 
|  | int stores_found = 0; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 25)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store[stores_found] = &inst; | 
|  | ++stores_found; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < 1; ++i) { | 
|  | EXPECT_TRUE(store[i]); | 
|  | } | 
|  |  | 
|  | // 39 -> 40 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | analysis.SetDebugStream(std::cout); | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(39), store[0], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::IRRELEVANT); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[1].dependence_information, | 
|  | DistanceEntry::DependenceInformation::DISTANCE); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[1].distance, 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | // For the loop in function b | 
|  | { | 
|  | const Function* f = spvtest::GetFunction(module, 8); | 
|  | LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
|  |  | 
|  | std::vector<const Loop*> loops{&ld.GetLoopByIndex(1), | 
|  | &ld.GetLoopByIndex(0)}; | 
|  | LoopDependenceAnalysis analysis{context.get(), loops}; | 
|  |  | 
|  | const Instruction* store[1]; | 
|  | int stores_found = 0; | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, 56)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store[stores_found] = &inst; | 
|  | ++stores_found; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < 1; ++i) { | 
|  | EXPECT_TRUE(store[i]); | 
|  | } | 
|  |  | 
|  | // 66 -> 67 | 
|  | { | 
|  | DistanceVector distance_vector{loops.size()}; | 
|  | EXPECT_FALSE(analysis.GetDependence( | 
|  | context->get_def_use_mgr()->GetDef(66), store[0], &distance_vector)); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, | 
|  | DistanceEntry::DependenceInformation::DISTANCE); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[0].distance, 0); | 
|  | EXPECT_EQ(distance_vector.GetEntries()[1].dependence_information, | 
|  | DistanceEntry::DependenceInformation::IRRELEVANT); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void CheckDependenceAndDirection(const Instruction* source, | 
|  | const Instruction* destination, | 
|  | bool expected_dependence, | 
|  | DistanceVector expected_distance, | 
|  | LoopDependenceAnalysis* analysis) { | 
|  | DistanceVector dv_entry(2); | 
|  | EXPECT_EQ(expected_dependence, | 
|  | analysis->GetDependence(source, destination, &dv_entry)); | 
|  | EXPECT_EQ(expected_distance, dv_entry); | 
|  | } | 
|  |  | 
|  | /* | 
|  | Generated from the following GLSL fragment shader | 
|  | with --eliminate-local-multi-store | 
|  | #version 440 core | 
|  | layout(location = 0) in vec4 c; | 
|  | void main(){ | 
|  | int[10] arr; | 
|  | int a = 2; | 
|  | int b = 3; | 
|  | int N = int(c.x); | 
|  | for (int i = 0; i < 10; i++) { | 
|  | for (int j = 2; j < 10; j++) { | 
|  | arr[i] = arr[j]; // 0 | 
|  | arr[j] = arr[i]; // 1 | 
|  | arr[j-2] = arr[i+3]; // 2 | 
|  | arr[j-a] = arr[i+b]; // 3 | 
|  | arr[2*i] = arr[4*j+3]; // 4, independent | 
|  | arr[2*i] = arr[4*j]; // 5 | 
|  | arr[i+j] = arr[i+j]; // 6 | 
|  | arr[10*i+j] = arr[10*i+j]; // 7 | 
|  | arr[10*i+10*j] = arr[10*i+10*j+3]; // 8, independent | 
|  | arr[10*i+10*j] = arr[10*i+N*j+3]; // 9, bail out because of N coefficient | 
|  | arr[10*i+10*j] = arr[10*i+10*j+N]; // 10, bail out because of N constant | 
|  | // term | 
|  | arr[10*i+N*j] = arr[10*i+10*j+3]; // 11, bail out because of N coefficient | 
|  | arr[10*i+10*j+N] = arr[10*i+10*j]; // 12, bail out because of N constant | 
|  | // term | 
|  | arr[10*i] = arr[5*j]; // 13, independent | 
|  | arr[5*i] = arr[10*j]; // 14, independent | 
|  | arr[9*i] = arr[3*j]; // 15, independent | 
|  | arr[3*i] = arr[9*j]; // 16, independent | 
|  | } | 
|  | } | 
|  | } | 
|  | */ | 
|  | TEST(DependencyAnalysis, MIV) { | 
|  | const std::string text = R"( | 
|  | OpCapability Shader | 
|  | %1 = OpExtInstImport "GLSL.std.450" | 
|  | OpMemoryModel Logical GLSL450 | 
|  | OpEntryPoint Fragment %4 "main" %16 | 
|  | OpExecutionMode %4 OriginUpperLeft | 
|  | OpSource GLSL 440 | 
|  | OpName %4 "main" | 
|  | OpName %8 "a" | 
|  | OpName %10 "b" | 
|  | OpName %12 "N" | 
|  | OpName %16 "c" | 
|  | OpName %23 "i" | 
|  | OpName %34 "j" | 
|  | OpName %45 "arr" | 
|  | OpDecorate %16 Location 0 | 
|  | %2 = OpTypeVoid | 
|  | %3 = OpTypeFunction %2 | 
|  | %6 = OpTypeInt 32 1 | 
|  | %7 = OpTypePointer Function %6 | 
|  | %9 = OpConstant %6 2 | 
|  | %11 = OpConstant %6 3 | 
|  | %13 = OpTypeFloat 32 | 
|  | %14 = OpTypeVector %13 4 | 
|  | %15 = OpTypePointer Input %14 | 
|  | %16 = OpVariable %15 Input | 
|  | %17 = OpTypeInt 32 0 | 
|  | %18 = OpConstant %17 0 | 
|  | %19 = OpTypePointer Input %13 | 
|  | %24 = OpConstant %6 0 | 
|  | %31 = OpConstant %6 10 | 
|  | %32 = OpTypeBool | 
|  | %42 = OpConstant %17 10 | 
|  | %43 = OpTypeArray %6 %42 | 
|  | %44 = OpTypePointer Function %43 | 
|  | %74 = OpConstant %6 4 | 
|  | %184 = OpConstant %6 5 | 
|  | %197 = OpConstant %6 9 | 
|  | %213 = OpConstant %6 1 | 
|  | %218 = OpUndef %6 | 
|  | %4 = OpFunction %2 None %3 | 
|  | %5 = OpLabel | 
|  | %8 = OpVariable %7 Function | 
|  | %10 = OpVariable %7 Function | 
|  | %12 = OpVariable %7 Function | 
|  | %23 = OpVariable %7 Function | 
|  | %34 = OpVariable %7 Function | 
|  | %45 = OpVariable %44 Function | 
|  | OpStore %8 %9 | 
|  | OpStore %10 %11 | 
|  | %20 = OpAccessChain %19 %16 %18 | 
|  | %21 = OpLoad %13 %20 | 
|  | %22 = OpConvertFToS %6 %21 | 
|  | OpStore %12 %22 | 
|  | OpStore %23 %24 | 
|  | OpBranch %25 | 
|  | %25 = OpLabel | 
|  | %217 = OpPhi %6 %24 %5 %216 %28 | 
|  | %219 = OpPhi %6 %218 %5 %220 %28 | 
|  | OpLoopMerge %27 %28 None | 
|  | OpBranch %29 | 
|  | %29 = OpLabel | 
|  | %33 = OpSLessThan %32 %217 %31 | 
|  | OpBranchConditional %33 %26 %27 | 
|  | %26 = OpLabel | 
|  | OpStore %34 %9 | 
|  | OpBranch %35 | 
|  | %35 = OpLabel | 
|  | %220 = OpPhi %6 %9 %26 %214 %38 | 
|  | OpLoopMerge %37 %38 None | 
|  | OpBranch %39 | 
|  | %39 = OpLabel | 
|  | %41 = OpSLessThan %32 %220 %31 | 
|  | OpBranchConditional %41 %36 %37 | 
|  | %36 = OpLabel | 
|  | %48 = OpAccessChain %7 %45 %220 | 
|  | %49 = OpLoad %6 %48 | 
|  | %50 = OpAccessChain %7 %45 %217 | 
|  | OpStore %50 %49 | 
|  | %53 = OpAccessChain %7 %45 %217 | 
|  | %54 = OpLoad %6 %53 | 
|  | %55 = OpAccessChain %7 %45 %220 | 
|  | OpStore %55 %54 | 
|  | %57 = OpISub %6 %220 %9 | 
|  | %59 = OpIAdd %6 %217 %11 | 
|  | %60 = OpAccessChain %7 %45 %59 | 
|  | %61 = OpLoad %6 %60 | 
|  | %62 = OpAccessChain %7 %45 %57 | 
|  | OpStore %62 %61 | 
|  | %65 = OpISub %6 %220 %9 | 
|  | %68 = OpIAdd %6 %217 %11 | 
|  | %69 = OpAccessChain %7 %45 %68 | 
|  | %70 = OpLoad %6 %69 | 
|  | %71 = OpAccessChain %7 %45 %65 | 
|  | OpStore %71 %70 | 
|  | %73 = OpIMul %6 %9 %217 | 
|  | %76 = OpIMul %6 %74 %220 | 
|  | %77 = OpIAdd %6 %76 %11 | 
|  | %78 = OpAccessChain %7 %45 %77 | 
|  | %79 = OpLoad %6 %78 | 
|  | %80 = OpAccessChain %7 %45 %73 | 
|  | OpStore %80 %79 | 
|  | %82 = OpIMul %6 %9 %217 | 
|  | %84 = OpIMul %6 %74 %220 | 
|  | %85 = OpAccessChain %7 %45 %84 | 
|  | %86 = OpLoad %6 %85 | 
|  | %87 = OpAccessChain %7 %45 %82 | 
|  | OpStore %87 %86 | 
|  | %90 = OpIAdd %6 %217 %220 | 
|  | %93 = OpIAdd %6 %217 %220 | 
|  | %94 = OpAccessChain %7 %45 %93 | 
|  | %95 = OpLoad %6 %94 | 
|  | %96 = OpAccessChain %7 %45 %90 | 
|  | OpStore %96 %95 | 
|  | %98 = OpIMul %6 %31 %217 | 
|  | %100 = OpIAdd %6 %98 %220 | 
|  | %102 = OpIMul %6 %31 %217 | 
|  | %104 = OpIAdd %6 %102 %220 | 
|  | %105 = OpAccessChain %7 %45 %104 | 
|  | %106 = OpLoad %6 %105 | 
|  | %107 = OpAccessChain %7 %45 %100 | 
|  | OpStore %107 %106 | 
|  | %109 = OpIMul %6 %31 %217 | 
|  | %111 = OpIMul %6 %31 %220 | 
|  | %112 = OpIAdd %6 %109 %111 | 
|  | %114 = OpIMul %6 %31 %217 | 
|  | %116 = OpIMul %6 %31 %220 | 
|  | %117 = OpIAdd %6 %114 %116 | 
|  | %118 = OpIAdd %6 %117 %11 | 
|  | %119 = OpAccessChain %7 %45 %118 | 
|  | %120 = OpLoad %6 %119 | 
|  | %121 = OpAccessChain %7 %45 %112 | 
|  | OpStore %121 %120 | 
|  | %123 = OpIMul %6 %31 %217 | 
|  | %125 = OpIMul %6 %31 %220 | 
|  | %126 = OpIAdd %6 %123 %125 | 
|  | %128 = OpIMul %6 %31 %217 | 
|  | %131 = OpIMul %6 %22 %220 | 
|  | %132 = OpIAdd %6 %128 %131 | 
|  | %133 = OpIAdd %6 %132 %11 | 
|  | %134 = OpAccessChain %7 %45 %133 | 
|  | %135 = OpLoad %6 %134 | 
|  | %136 = OpAccessChain %7 %45 %126 | 
|  | OpStore %136 %135 | 
|  | %138 = OpIMul %6 %31 %217 | 
|  | %140 = OpIMul %6 %31 %220 | 
|  | %141 = OpIAdd %6 %138 %140 | 
|  | %143 = OpIMul %6 %31 %217 | 
|  | %145 = OpIMul %6 %31 %220 | 
|  | %146 = OpIAdd %6 %143 %145 | 
|  | %148 = OpIAdd %6 %146 %22 | 
|  | %149 = OpAccessChain %7 %45 %148 | 
|  | %150 = OpLoad %6 %149 | 
|  | %151 = OpAccessChain %7 %45 %141 | 
|  | OpStore %151 %150 | 
|  | %153 = OpIMul %6 %31 %217 | 
|  | %156 = OpIMul %6 %22 %220 | 
|  | %157 = OpIAdd %6 %153 %156 | 
|  | %159 = OpIMul %6 %31 %217 | 
|  | %161 = OpIMul %6 %31 %220 | 
|  | %162 = OpIAdd %6 %159 %161 | 
|  | %163 = OpIAdd %6 %162 %11 | 
|  | %164 = OpAccessChain %7 %45 %163 | 
|  | %165 = OpLoad %6 %164 | 
|  | %166 = OpAccessChain %7 %45 %157 | 
|  | OpStore %166 %165 | 
|  | %168 = OpIMul %6 %31 %217 | 
|  | %170 = OpIMul %6 %31 %220 | 
|  | %171 = OpIAdd %6 %168 %170 | 
|  | %173 = OpIAdd %6 %171 %22 | 
|  | %175 = OpIMul %6 %31 %217 | 
|  | %177 = OpIMul %6 %31 %220 | 
|  | %178 = OpIAdd %6 %175 %177 | 
|  | %179 = OpAccessChain %7 %45 %178 | 
|  | %180 = OpLoad %6 %179 | 
|  | %181 = OpAccessChain %7 %45 %173 | 
|  | OpStore %181 %180 | 
|  | %183 = OpIMul %6 %31 %217 | 
|  | %186 = OpIMul %6 %184 %220 | 
|  | %187 = OpAccessChain %7 %45 %186 | 
|  | %188 = OpLoad %6 %187 | 
|  | %189 = OpAccessChain %7 %45 %183 | 
|  | OpStore %189 %188 | 
|  | %191 = OpIMul %6 %184 %217 | 
|  | %193 = OpIMul %6 %31 %220 | 
|  | %194 = OpAccessChain %7 %45 %193 | 
|  | %195 = OpLoad %6 %194 | 
|  | %196 = OpAccessChain %7 %45 %191 | 
|  | OpStore %196 %195 | 
|  | %199 = OpIMul %6 %197 %217 | 
|  | %201 = OpIMul %6 %11 %220 | 
|  | %202 = OpAccessChain %7 %45 %201 | 
|  | %203 = OpLoad %6 %202 | 
|  | %204 = OpAccessChain %7 %45 %199 | 
|  | OpStore %204 %203 | 
|  | %206 = OpIMul %6 %11 %217 | 
|  | %208 = OpIMul %6 %197 %220 | 
|  | %209 = OpAccessChain %7 %45 %208 | 
|  | %210 = OpLoad %6 %209 | 
|  | %211 = OpAccessChain %7 %45 %206 | 
|  | OpStore %211 %210 | 
|  | OpBranch %38 | 
|  | %38 = OpLabel | 
|  | %214 = OpIAdd %6 %220 %213 | 
|  | OpStore %34 %214 | 
|  | OpBranch %35 | 
|  | %37 = OpLabel | 
|  | OpBranch %28 | 
|  | %28 = OpLabel | 
|  | %216 = OpIAdd %6 %217 %213 | 
|  | OpStore %23 %216 | 
|  | OpBranch %25 | 
|  | %27 = 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; | 
|  | const Function* f = spvtest::GetFunction(module, 4); | 
|  | LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
|  |  | 
|  | std::vector<const Loop*> loops{&ld.GetLoopByIndex(0), &ld.GetLoopByIndex(1)}; | 
|  |  | 
|  | LoopDependenceAnalysis analysis{context.get(), loops}; | 
|  |  | 
|  | const int instructions_expected = 17; | 
|  | const Instruction* store[instructions_expected]; | 
|  | const Instruction* load[instructions_expected]; | 
|  | int stores_found = 0; | 
|  | int loads_found = 0; | 
|  |  | 
|  | int block_id = 36; | 
|  | ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id)); | 
|  |  | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store[stores_found] = &inst; | 
|  | ++stores_found; | 
|  | } | 
|  |  | 
|  | if (inst.opcode() == SpvOp::SpvOpLoad) { | 
|  | load[loads_found] = &inst; | 
|  | ++loads_found; | 
|  | } | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(instructions_expected, stores_found); | 
|  | EXPECT_EQ(instructions_expected, loads_found); | 
|  |  | 
|  | auto directions_all = DistanceEntry(DistanceEntry::Directions::ALL); | 
|  | auto directions_none = DistanceEntry(DistanceEntry::Directions::NONE); | 
|  |  | 
|  | auto dependent = DistanceVector({directions_all, directions_all}); | 
|  | auto independent = DistanceVector({directions_none, directions_none}); | 
|  |  | 
|  | CheckDependenceAndDirection(load[0], store[0], false, dependent, &analysis); | 
|  | CheckDependenceAndDirection(load[1], store[1], false, dependent, &analysis); | 
|  | CheckDependenceAndDirection(load[2], store[2], false, dependent, &analysis); | 
|  | CheckDependenceAndDirection(load[3], store[3], false, dependent, &analysis); | 
|  | CheckDependenceAndDirection(load[4], store[4], true, independent, &analysis); | 
|  | CheckDependenceAndDirection(load[5], store[5], false, dependent, &analysis); | 
|  | CheckDependenceAndDirection(load[6], store[6], false, dependent, &analysis); | 
|  | CheckDependenceAndDirection(load[7], store[7], false, dependent, &analysis); | 
|  | CheckDependenceAndDirection(load[8], store[8], true, independent, &analysis); | 
|  | CheckDependenceAndDirection(load[9], store[9], false, dependent, &analysis); | 
|  | CheckDependenceAndDirection(load[10], store[10], false, dependent, &analysis); | 
|  | CheckDependenceAndDirection(load[11], store[11], false, dependent, &analysis); | 
|  | CheckDependenceAndDirection(load[12], store[12], false, dependent, &analysis); | 
|  | CheckDependenceAndDirection(load[13], store[13], true, independent, | 
|  | &analysis); | 
|  | CheckDependenceAndDirection(load[14], store[14], true, independent, | 
|  | &analysis); | 
|  | CheckDependenceAndDirection(load[15], store[15], true, independent, | 
|  | &analysis); | 
|  | CheckDependenceAndDirection(load[16], store[16], true, independent, | 
|  | &analysis); | 
|  | } | 
|  |  | 
|  | void PartitionSubscripts(const Instruction* instruction_0, | 
|  | const Instruction* instruction_1, | 
|  | LoopDependenceAnalysis* analysis, | 
|  | std::vector<std::vector<int>> expected_ids) { | 
|  | auto subscripts_0 = analysis->GetSubscripts(instruction_0); | 
|  | auto subscripts_1 = analysis->GetSubscripts(instruction_1); | 
|  |  | 
|  | std::vector<std::set<std::pair<Instruction*, Instruction*>>> | 
|  | expected_partition{}; | 
|  |  | 
|  | for (const auto& partition : expected_ids) { | 
|  | expected_partition.push_back( | 
|  | std::set<std::pair<Instruction*, Instruction*>>{}); | 
|  | for (auto id : partition) { | 
|  | expected_partition.back().insert({subscripts_0[id], subscripts_1[id]}); | 
|  | } | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(expected_partition, | 
|  | analysis->PartitionSubscripts(subscripts_0, subscripts_1)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | Generated from the following GLSL fragment shader | 
|  | with --eliminate-local-multi-store | 
|  | #version 440 core | 
|  | void main(){ | 
|  | int[10][10][10][10] arr; | 
|  | for (int i = 0; i < 10; i++) { | 
|  | for (int j = 0; j < 10; j++) { | 
|  | for (int k = 0; k < 10; k++) { | 
|  | for (int l = 0; l < 10; l++) { | 
|  | arr[i][j][k][l] = arr[i][j][k][l]; // 0, all independent | 
|  | arr[i][j][k][l] = arr[i][j][l][0]; // 1, last 2 coupled | 
|  | arr[i][j][k][l] = arr[j][i][k][l]; // 2, first 2 coupled | 
|  | arr[i][j][k][l] = arr[l][j][k][i]; // 3, first & last coupled | 
|  | arr[i][j][k][l] = arr[i][k][j][l]; // 4, middle 2 coupled | 
|  | arr[i+j][j][k][l] = arr[i][j][k][l]; // 5, first 2 coupled | 
|  | arr[i+j+k][j][k][l] = arr[i][j][k][l]; // 6, first 3 coupled | 
|  | arr[i+j+k+l][j][k][l] = arr[i][j][k][l]; // 7, all 4 coupled | 
|  | arr[i][j][k][l] = arr[i][l][j][k]; // 8, last 3 coupled | 
|  | arr[i][j-k][k][l] = arr[i][j][l][k]; // 9, last 3 coupled | 
|  | arr[i][j][k][l] = arr[l][i][j][k]; // 10, all 4 coupled | 
|  | arr[i][j][k][l] = arr[j][i][l][k]; // 11, 2 coupled partitions (i,j) & | 
|  | (l&k) | 
|  | arr[i][j][k][l] = arr[k][l][i][j]; // 12, 2 coupled partitions (i,k) & | 
|  | (j&l) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | */ | 
|  | TEST(DependencyAnalysis, SubscriptPartitioning) { | 
|  | 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" | 
|  | OpName %19 "j" | 
|  | OpName %27 "k" | 
|  | OpName %35 "l" | 
|  | OpName %50 "arr" | 
|  | %2 = OpTypeVoid | 
|  | %3 = OpTypeFunction %2 | 
|  | %6 = OpTypeInt 32 1 | 
|  | %7 = OpTypePointer Function %6 | 
|  | %9 = OpConstant %6 0 | 
|  | %16 = OpConstant %6 10 | 
|  | %17 = OpTypeBool | 
|  | %43 = OpTypeInt 32 0 | 
|  | %44 = OpConstant %43 10 | 
|  | %45 = OpTypeArray %6 %44 | 
|  | %46 = OpTypeArray %45 %44 | 
|  | %47 = OpTypeArray %46 %44 | 
|  | %48 = OpTypeArray %47 %44 | 
|  | %49 = OpTypePointer Function %48 | 
|  | %208 = OpConstant %6 1 | 
|  | %217 = OpUndef %6 | 
|  | %4 = OpFunction %2 None %3 | 
|  | %5 = OpLabel | 
|  | %8 = OpVariable %7 Function | 
|  | %19 = OpVariable %7 Function | 
|  | %27 = OpVariable %7 Function | 
|  | %35 = OpVariable %7 Function | 
|  | %50 = OpVariable %49 Function | 
|  | OpStore %8 %9 | 
|  | OpBranch %10 | 
|  | %10 = OpLabel | 
|  | %216 = OpPhi %6 %9 %5 %215 %13 | 
|  | %218 = OpPhi %6 %217 %5 %221 %13 | 
|  | %219 = OpPhi %6 %217 %5 %222 %13 | 
|  | %220 = OpPhi %6 %217 %5 %223 %13 | 
|  | OpLoopMerge %12 %13 None | 
|  | OpBranch %14 | 
|  | %14 = OpLabel | 
|  | %18 = OpSLessThan %17 %216 %16 | 
|  | OpBranchConditional %18 %11 %12 | 
|  | %11 = OpLabel | 
|  | OpStore %19 %9 | 
|  | OpBranch %20 | 
|  | %20 = OpLabel | 
|  | %221 = OpPhi %6 %9 %11 %213 %23 | 
|  | %222 = OpPhi %6 %219 %11 %224 %23 | 
|  | %223 = OpPhi %6 %220 %11 %225 %23 | 
|  | OpLoopMerge %22 %23 None | 
|  | OpBranch %24 | 
|  | %24 = OpLabel | 
|  | %26 = OpSLessThan %17 %221 %16 | 
|  | OpBranchConditional %26 %21 %22 | 
|  | %21 = OpLabel | 
|  | OpStore %27 %9 | 
|  | OpBranch %28 | 
|  | %28 = OpLabel | 
|  | %224 = OpPhi %6 %9 %21 %211 %31 | 
|  | %225 = OpPhi %6 %223 %21 %226 %31 | 
|  | OpLoopMerge %30 %31 None | 
|  | OpBranch %32 | 
|  | %32 = OpLabel | 
|  | %34 = OpSLessThan %17 %224 %16 | 
|  | OpBranchConditional %34 %29 %30 | 
|  | %29 = OpLabel | 
|  | OpStore %35 %9 | 
|  | OpBranch %36 | 
|  | %36 = OpLabel | 
|  | %226 = OpPhi %6 %9 %29 %209 %39 | 
|  | OpLoopMerge %38 %39 None | 
|  | OpBranch %40 | 
|  | %40 = OpLabel | 
|  | %42 = OpSLessThan %17 %226 %16 | 
|  | OpBranchConditional %42 %37 %38 | 
|  | %37 = OpLabel | 
|  | %59 = OpAccessChain %7 %50 %216 %221 %224 %226 | 
|  | %60 = OpLoad %6 %59 | 
|  | %61 = OpAccessChain %7 %50 %216 %221 %224 %226 | 
|  | OpStore %61 %60 | 
|  | %69 = OpAccessChain %7 %50 %216 %221 %226 %9 | 
|  | %70 = OpLoad %6 %69 | 
|  | %71 = OpAccessChain %7 %50 %216 %221 %224 %226 | 
|  | OpStore %71 %70 | 
|  | %80 = OpAccessChain %7 %50 %221 %216 %224 %226 | 
|  | %81 = OpLoad %6 %80 | 
|  | %82 = OpAccessChain %7 %50 %216 %221 %224 %226 | 
|  | OpStore %82 %81 | 
|  | %91 = OpAccessChain %7 %50 %226 %221 %224 %216 | 
|  | %92 = OpLoad %6 %91 | 
|  | %93 = OpAccessChain %7 %50 %216 %221 %224 %226 | 
|  | OpStore %93 %92 | 
|  | %102 = OpAccessChain %7 %50 %216 %224 %221 %226 | 
|  | %103 = OpLoad %6 %102 | 
|  | %104 = OpAccessChain %7 %50 %216 %221 %224 %226 | 
|  | OpStore %104 %103 | 
|  | %107 = OpIAdd %6 %216 %221 | 
|  | %115 = OpAccessChain %7 %50 %216 %221 %224 %226 | 
|  | %116 = OpLoad %6 %115 | 
|  | %117 = OpAccessChain %7 %50 %107 %221 %224 %226 | 
|  | OpStore %117 %116 | 
|  | %120 = OpIAdd %6 %216 %221 | 
|  | %122 = OpIAdd %6 %120 %224 | 
|  | %130 = OpAccessChain %7 %50 %216 %221 %224 %226 | 
|  | %131 = OpLoad %6 %130 | 
|  | %132 = OpAccessChain %7 %50 %122 %221 %224 %226 | 
|  | OpStore %132 %131 | 
|  | %135 = OpIAdd %6 %216 %221 | 
|  | %137 = OpIAdd %6 %135 %224 | 
|  | %139 = OpIAdd %6 %137 %226 | 
|  | %147 = OpAccessChain %7 %50 %216 %221 %224 %226 | 
|  | %148 = OpLoad %6 %147 | 
|  | %149 = OpAccessChain %7 %50 %139 %221 %224 %226 | 
|  | OpStore %149 %148 | 
|  | %158 = OpAccessChain %7 %50 %216 %226 %221 %224 | 
|  | %159 = OpLoad %6 %158 | 
|  | %160 = OpAccessChain %7 %50 %216 %221 %224 %226 | 
|  | OpStore %160 %159 | 
|  | %164 = OpISub %6 %221 %224 | 
|  | %171 = OpAccessChain %7 %50 %216 %221 %226 %224 | 
|  | %172 = OpLoad %6 %171 | 
|  | %173 = OpAccessChain %7 %50 %216 %164 %224 %226 | 
|  | OpStore %173 %172 | 
|  | %182 = OpAccessChain %7 %50 %226 %216 %221 %224 | 
|  | %183 = OpLoad %6 %182 | 
|  | %184 = OpAccessChain %7 %50 %216 %221 %224 %226 | 
|  | OpStore %184 %183 | 
|  | %193 = OpAccessChain %7 %50 %221 %216 %226 %224 | 
|  | %194 = OpLoad %6 %193 | 
|  | %195 = OpAccessChain %7 %50 %216 %221 %224 %226 | 
|  | OpStore %195 %194 | 
|  | %204 = OpAccessChain %7 %50 %224 %226 %216 %221 | 
|  | %205 = OpLoad %6 %204 | 
|  | %206 = OpAccessChain %7 %50 %216 %221 %224 %226 | 
|  | OpStore %206 %205 | 
|  | OpBranch %39 | 
|  | %39 = OpLabel | 
|  | %209 = OpIAdd %6 %226 %208 | 
|  | OpStore %35 %209 | 
|  | OpBranch %36 | 
|  | %38 = OpLabel | 
|  | OpBranch %31 | 
|  | %31 = OpLabel | 
|  | %211 = OpIAdd %6 %224 %208 | 
|  | OpStore %27 %211 | 
|  | OpBranch %28 | 
|  | %30 = OpLabel | 
|  | OpBranch %23 | 
|  | %23 = OpLabel | 
|  | %213 = OpIAdd %6 %221 %208 | 
|  | OpStore %19 %213 | 
|  | OpBranch %20 | 
|  | %22 = OpLabel | 
|  | OpBranch %13 | 
|  | %13 = OpLabel | 
|  | %215 = OpIAdd %6 %216 %208 | 
|  | OpStore %8 %215 | 
|  | 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; | 
|  | const Function* f = spvtest::GetFunction(module, 4); | 
|  | LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
|  |  | 
|  | std::vector<const Loop*> loop_nest{ | 
|  | &ld.GetLoopByIndex(0), &ld.GetLoopByIndex(1), &ld.GetLoopByIndex(2), | 
|  | &ld.GetLoopByIndex(3)}; | 
|  | LoopDependenceAnalysis analysis{context.get(), loop_nest}; | 
|  |  | 
|  | const int instructions_expected = 13; | 
|  | const Instruction* store[instructions_expected]; | 
|  | const Instruction* load[instructions_expected]; | 
|  | int stores_found = 0; | 
|  | int loads_found = 0; | 
|  |  | 
|  | int block_id = 37; | 
|  | ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id)); | 
|  |  | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store[stores_found] = &inst; | 
|  | ++stores_found; | 
|  | } | 
|  |  | 
|  | if (inst.opcode() == SpvOp::SpvOpLoad) { | 
|  | load[loads_found] = &inst; | 
|  | ++loads_found; | 
|  | } | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(instructions_expected, stores_found); | 
|  | EXPECT_EQ(instructions_expected, loads_found); | 
|  |  | 
|  | PartitionSubscripts(load[0], store[0], &analysis, {{0}, {1}, {2}, {3}}); | 
|  | PartitionSubscripts(load[1], store[1], &analysis, {{0}, {1}, {2, 3}}); | 
|  | PartitionSubscripts(load[2], store[2], &analysis, {{0, 1}, {2}, {3}}); | 
|  | PartitionSubscripts(load[3], store[3], &analysis, {{0, 3}, {1}, {2}}); | 
|  | PartitionSubscripts(load[4], store[4], &analysis, {{0}, {1, 2}, {3}}); | 
|  | PartitionSubscripts(load[5], store[5], &analysis, {{0, 1}, {2}, {3}}); | 
|  | PartitionSubscripts(load[6], store[6], &analysis, {{0, 1, 2}, {3}}); | 
|  | PartitionSubscripts(load[7], store[7], &analysis, {{0, 1, 2, 3}}); | 
|  | PartitionSubscripts(load[8], store[8], &analysis, {{0}, {1, 2, 3}}); | 
|  | PartitionSubscripts(load[9], store[9], &analysis, {{0}, {1, 2, 3}}); | 
|  | PartitionSubscripts(load[10], store[10], &analysis, {{0, 1, 2, 3}}); | 
|  | PartitionSubscripts(load[11], store[11], &analysis, {{0, 1}, {2, 3}}); | 
|  | PartitionSubscripts(load[12], store[12], &analysis, {{0, 2}, {1, 3}}); | 
|  | } | 
|  |  | 
|  | /* | 
|  | Generated from the following GLSL fragment shader | 
|  | with --eliminate-local-multi-store | 
|  |  | 
|  | #version 440 core | 
|  | void a() { | 
|  | int[10][10] arr; | 
|  | for (int i = 0; i < 10; ++i) { | 
|  | for (int j = 0; j < 10; ++j) { | 
|  | // Dependent, distance vector (1, -1) | 
|  | arr[i+1][i+j] = arr[i][i+j]; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void b() { | 
|  | int[10][10] arr; | 
|  | for (int i = 0; i < 10; ++i) { | 
|  | // Independent | 
|  | arr[i+1][i+2] = arr[i][i] + 2; | 
|  | } | 
|  | } | 
|  |  | 
|  | void c() { | 
|  | int[10][10] arr; | 
|  | for (int i = 0; i < 10; ++i) { | 
|  | // Dependence point (1,2) | 
|  | arr[i][i] = arr[1][i-1] + 2; | 
|  | } | 
|  | } | 
|  |  | 
|  | void d() { | 
|  | int[10][10][10] arr; | 
|  | for (int i = 0; i < 10; ++i) { | 
|  | for (int j = 0; j < 10; ++j) { | 
|  | for (int k = 0; k < 10; ++k) { | 
|  | // Dependent, distance vector (1,1,-1) | 
|  | arr[j-i][i+1][j+k] = arr[j-i][i][j+k]; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void e() { | 
|  | int[10][10] arr; | 
|  | for (int i = 0; i < 10; ++i) { | 
|  | for (int j = 0; j < 10; ++j) { | 
|  | // Independent with GCD after propagation | 
|  | arr[i][2*j+i] = arr[i][2*j-i+5]; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void main(){ | 
|  | a(); | 
|  | b(); | 
|  | c(); | 
|  | d(); | 
|  | e(); | 
|  | } | 
|  | */ | 
|  | TEST(DependencyAnalysis, Delta) { | 
|  | 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 %18 "i" | 
|  | OpName %29 "j" | 
|  | OpName %42 "arr" | 
|  | OpName %60 "i" | 
|  | OpName %68 "arr" | 
|  | OpName %82 "i" | 
|  | OpName %90 "arr" | 
|  | OpName %101 "i" | 
|  | OpName %109 "j" | 
|  | OpName %117 "k" | 
|  | OpName %127 "arr" | 
|  | OpName %152 "i" | 
|  | OpName %160 "j" | 
|  | OpName %168 "arr" | 
|  | %2 = OpTypeVoid | 
|  | %3 = OpTypeFunction %2 | 
|  | %16 = OpTypeInt 32 1 | 
|  | %17 = OpTypePointer Function %16 | 
|  | %19 = OpConstant %16 0 | 
|  | %26 = OpConstant %16 10 | 
|  | %27 = OpTypeBool | 
|  | %37 = OpTypeInt 32 0 | 
|  | %38 = OpConstant %37 10 | 
|  | %39 = OpTypeArray %16 %38 | 
|  | %40 = OpTypeArray %39 %38 | 
|  | %41 = OpTypePointer Function %40 | 
|  | %44 = OpConstant %16 1 | 
|  | %72 = OpConstant %16 2 | 
|  | %125 = OpTypeArray %40 %38 | 
|  | %126 = OpTypePointer Function %125 | 
|  | %179 = OpConstant %16 5 | 
|  | %4 = OpFunction %2 None %3 | 
|  | %5 = OpLabel | 
|  | %188 = OpFunctionCall %2 %6 | 
|  | %189 = OpFunctionCall %2 %8 | 
|  | %190 = OpFunctionCall %2 %10 | 
|  | %191 = OpFunctionCall %2 %12 | 
|  | %192 = OpFunctionCall %2 %14 | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %6 = OpFunction %2 None %3 | 
|  | %7 = OpLabel | 
|  | %18 = OpVariable %17 Function | 
|  | %29 = OpVariable %17 Function | 
|  | %42 = OpVariable %41 Function | 
|  | OpStore %18 %19 | 
|  | OpBranch %20 | 
|  | %20 = OpLabel | 
|  | %193 = OpPhi %16 %19 %7 %59 %23 | 
|  | OpLoopMerge %22 %23 None | 
|  | OpBranch %24 | 
|  | %24 = OpLabel | 
|  | %28 = OpSLessThan %27 %193 %26 | 
|  | OpBranchConditional %28 %21 %22 | 
|  | %21 = OpLabel | 
|  | OpStore %29 %19 | 
|  | OpBranch %30 | 
|  | %30 = OpLabel | 
|  | %194 = OpPhi %16 %19 %21 %57 %33 | 
|  | OpLoopMerge %32 %33 None | 
|  | OpBranch %34 | 
|  | %34 = OpLabel | 
|  | %36 = OpSLessThan %27 %194 %26 | 
|  | OpBranchConditional %36 %31 %32 | 
|  | %31 = OpLabel | 
|  | %45 = OpIAdd %16 %193 %44 | 
|  | %48 = OpIAdd %16 %193 %194 | 
|  | %52 = OpIAdd %16 %193 %194 | 
|  | %53 = OpAccessChain %17 %42 %193 %52 | 
|  | %54 = OpLoad %16 %53 | 
|  | %55 = OpAccessChain %17 %42 %45 %48 | 
|  | OpStore %55 %54 | 
|  | OpBranch %33 | 
|  | %33 = OpLabel | 
|  | %57 = OpIAdd %16 %194 %44 | 
|  | OpStore %29 %57 | 
|  | OpBranch %30 | 
|  | %32 = OpLabel | 
|  | OpBranch %23 | 
|  | %23 = OpLabel | 
|  | %59 = OpIAdd %16 %193 %44 | 
|  | OpStore %18 %59 | 
|  | OpBranch %20 | 
|  | %22 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %8 = OpFunction %2 None %3 | 
|  | %9 = OpLabel | 
|  | %60 = OpVariable %17 Function | 
|  | %68 = OpVariable %41 Function | 
|  | OpStore %60 %19 | 
|  | OpBranch %61 | 
|  | %61 = OpLabel | 
|  | %196 = OpPhi %16 %19 %9 %81 %64 | 
|  | OpLoopMerge %63 %64 None | 
|  | OpBranch %65 | 
|  | %65 = OpLabel | 
|  | %67 = OpSLessThan %27 %196 %26 | 
|  | OpBranchConditional %67 %62 %63 | 
|  | %62 = OpLabel | 
|  | %70 = OpIAdd %16 %196 %44 | 
|  | %73 = OpIAdd %16 %196 %72 | 
|  | %76 = OpAccessChain %17 %68 %196 %196 | 
|  | %77 = OpLoad %16 %76 | 
|  | %78 = OpIAdd %16 %77 %72 | 
|  | %79 = OpAccessChain %17 %68 %70 %73 | 
|  | OpStore %79 %78 | 
|  | OpBranch %64 | 
|  | %64 = OpLabel | 
|  | %81 = OpIAdd %16 %196 %44 | 
|  | OpStore %60 %81 | 
|  | OpBranch %61 | 
|  | %63 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %10 = OpFunction %2 None %3 | 
|  | %11 = OpLabel | 
|  | %82 = OpVariable %17 Function | 
|  | %90 = OpVariable %41 Function | 
|  | OpStore %82 %19 | 
|  | OpBranch %83 | 
|  | %83 = OpLabel | 
|  | %197 = OpPhi %16 %19 %11 %100 %86 | 
|  | OpLoopMerge %85 %86 None | 
|  | OpBranch %87 | 
|  | %87 = OpLabel | 
|  | %89 = OpSLessThan %27 %197 %26 | 
|  | OpBranchConditional %89 %84 %85 | 
|  | %84 = OpLabel | 
|  | %94 = OpISub %16 %197 %44 | 
|  | %95 = OpAccessChain %17 %90 %44 %94 | 
|  | %96 = OpLoad %16 %95 | 
|  | %97 = OpIAdd %16 %96 %72 | 
|  | %98 = OpAccessChain %17 %90 %197 %197 | 
|  | OpStore %98 %97 | 
|  | OpBranch %86 | 
|  | %86 = OpLabel | 
|  | %100 = OpIAdd %16 %197 %44 | 
|  | OpStore %82 %100 | 
|  | OpBranch %83 | 
|  | %85 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %12 = OpFunction %2 None %3 | 
|  | %13 = OpLabel | 
|  | %101 = OpVariable %17 Function | 
|  | %109 = OpVariable %17 Function | 
|  | %117 = OpVariable %17 Function | 
|  | %127 = OpVariable %126 Function | 
|  | OpStore %101 %19 | 
|  | OpBranch %102 | 
|  | %102 = OpLabel | 
|  | %198 = OpPhi %16 %19 %13 %151 %105 | 
|  | OpLoopMerge %104 %105 None | 
|  | OpBranch %106 | 
|  | %106 = OpLabel | 
|  | %108 = OpSLessThan %27 %198 %26 | 
|  | OpBranchConditional %108 %103 %104 | 
|  | %103 = OpLabel | 
|  | OpStore %109 %19 | 
|  | OpBranch %110 | 
|  | %110 = OpLabel | 
|  | %199 = OpPhi %16 %19 %103 %149 %113 | 
|  | OpLoopMerge %112 %113 None | 
|  | OpBranch %114 | 
|  | %114 = OpLabel | 
|  | %116 = OpSLessThan %27 %199 %26 | 
|  | OpBranchConditional %116 %111 %112 | 
|  | %111 = OpLabel | 
|  | OpStore %117 %19 | 
|  | OpBranch %118 | 
|  | %118 = OpLabel | 
|  | %201 = OpPhi %16 %19 %111 %147 %121 | 
|  | OpLoopMerge %120 %121 None | 
|  | OpBranch %122 | 
|  | %122 = OpLabel | 
|  | %124 = OpSLessThan %27 %201 %26 | 
|  | OpBranchConditional %124 %119 %120 | 
|  | %119 = OpLabel | 
|  | %130 = OpISub %16 %199 %198 | 
|  | %132 = OpIAdd %16 %198 %44 | 
|  | %135 = OpIAdd %16 %199 %201 | 
|  | %138 = OpISub %16 %199 %198 | 
|  | %142 = OpIAdd %16 %199 %201 | 
|  | %143 = OpAccessChain %17 %127 %138 %198 %142 | 
|  | %144 = OpLoad %16 %143 | 
|  | %145 = OpAccessChain %17 %127 %130 %132 %135 | 
|  | OpStore %145 %144 | 
|  | OpBranch %121 | 
|  | %121 = OpLabel | 
|  | %147 = OpIAdd %16 %201 %44 | 
|  | OpStore %117 %147 | 
|  | OpBranch %118 | 
|  | %120 = OpLabel | 
|  | OpBranch %113 | 
|  | %113 = OpLabel | 
|  | %149 = OpIAdd %16 %199 %44 | 
|  | OpStore %109 %149 | 
|  | OpBranch %110 | 
|  | %112 = OpLabel | 
|  | OpBranch %105 | 
|  | %105 = OpLabel | 
|  | %151 = OpIAdd %16 %198 %44 | 
|  | OpStore %101 %151 | 
|  | OpBranch %102 | 
|  | %104 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | %14 = OpFunction %2 None %3 | 
|  | %15 = OpLabel | 
|  | %152 = OpVariable %17 Function | 
|  | %160 = OpVariable %17 Function | 
|  | %168 = OpVariable %41 Function | 
|  | OpStore %152 %19 | 
|  | OpBranch %153 | 
|  | %153 = OpLabel | 
|  | %204 = OpPhi %16 %19 %15 %187 %156 | 
|  | OpLoopMerge %155 %156 None | 
|  | OpBranch %157 | 
|  | %157 = OpLabel | 
|  | %159 = OpSLessThan %27 %204 %26 | 
|  | OpBranchConditional %159 %154 %155 | 
|  | %154 = OpLabel | 
|  | OpStore %160 %19 | 
|  | OpBranch %161 | 
|  | %161 = OpLabel | 
|  | %205 = OpPhi %16 %19 %154 %185 %164 | 
|  | OpLoopMerge %163 %164 None | 
|  | OpBranch %165 | 
|  | %165 = OpLabel | 
|  | %167 = OpSLessThan %27 %205 %26 | 
|  | OpBranchConditional %167 %162 %163 | 
|  | %162 = OpLabel | 
|  | %171 = OpIMul %16 %72 %205 | 
|  | %173 = OpIAdd %16 %171 %204 | 
|  | %176 = OpIMul %16 %72 %205 | 
|  | %178 = OpISub %16 %176 %204 | 
|  | %180 = OpIAdd %16 %178 %179 | 
|  | %181 = OpAccessChain %17 %168 %204 %180 | 
|  | %182 = OpLoad %16 %181 | 
|  | %183 = OpAccessChain %17 %168 %204 %173 | 
|  | OpStore %183 %182 | 
|  | OpBranch %164 | 
|  | %164 = OpLabel | 
|  | %185 = OpIAdd %16 %205 %44 | 
|  | OpStore %160 %185 | 
|  | OpBranch %161 | 
|  | %163 = OpLabel | 
|  | OpBranch %156 | 
|  | %156 = OpLabel | 
|  | %187 = OpIAdd %16 %204 %44 | 
|  | OpStore %152 %187 | 
|  | OpBranch %153 | 
|  | %155 = OpLabel | 
|  | OpReturn | 
|  | OpFunctionEnd | 
|  | )"; | 
|  |  | 
|  | std::unique_ptr<IRContext> context = | 
|  | BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, | 
|  | SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | 
|  | ASSERT_NE(nullptr, context); | 
|  | Module* module = context->module(); | 
|  | EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | 
|  | << text << std::endl; | 
|  |  | 
|  | { | 
|  | const Function* f = spvtest::GetFunction(module, 6); | 
|  | LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
|  |  | 
|  | const Instruction* store = nullptr; | 
|  | const Instruction* load = nullptr; | 
|  |  | 
|  | int block_id = 31; | 
|  | ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id)); | 
|  |  | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store = &inst; | 
|  | } | 
|  |  | 
|  | if (inst.opcode() == SpvOp::SpvOpLoad) { | 
|  | load = &inst; | 
|  | } | 
|  | } | 
|  |  | 
|  | EXPECT_NE(nullptr, store); | 
|  | EXPECT_NE(nullptr, load); | 
|  |  | 
|  | std::vector<const Loop*> loop_nest{&ld.GetLoopByIndex(0), | 
|  | &ld.GetLoopByIndex(1)}; | 
|  | LoopDependenceAnalysis analysis{context.get(), loop_nest}; | 
|  |  | 
|  | DistanceVector dv_entry(loop_nest.size()); | 
|  |  | 
|  | std::vector<DistanceEntry> expected_entries{ | 
|  | DistanceEntry(DistanceEntry::Directions::LT, 1), | 
|  | DistanceEntry(DistanceEntry::Directions::LT, 1)}; | 
|  |  | 
|  | DistanceVector expected_distance_vector(expected_entries); | 
|  |  | 
|  | auto is_independent = analysis.GetDependence(load, store, &dv_entry); | 
|  |  | 
|  | EXPECT_FALSE(is_independent); | 
|  | EXPECT_EQ(expected_distance_vector, dv_entry); | 
|  | } | 
|  |  | 
|  | { | 
|  | const Function* f = spvtest::GetFunction(module, 8); | 
|  | LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
|  |  | 
|  | const Instruction* store = nullptr; | 
|  | const Instruction* load = nullptr; | 
|  |  | 
|  | int block_id = 62; | 
|  | ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id)); | 
|  |  | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store = &inst; | 
|  | } | 
|  |  | 
|  | if (inst.opcode() == SpvOp::SpvOpLoad) { | 
|  | load = &inst; | 
|  | } | 
|  | } | 
|  |  | 
|  | EXPECT_NE(nullptr, store); | 
|  | EXPECT_NE(nullptr, load); | 
|  |  | 
|  | std::vector<const Loop*> loop_nest{&ld.GetLoopByIndex(0)}; | 
|  | LoopDependenceAnalysis analysis{context.get(), loop_nest}; | 
|  |  | 
|  | DistanceVector dv_entry(loop_nest.size()); | 
|  | auto is_independent = analysis.GetDependence(load, store, &dv_entry); | 
|  |  | 
|  | EXPECT_TRUE(is_independent); | 
|  | } | 
|  |  | 
|  | { | 
|  | const Function* f = spvtest::GetFunction(module, 10); | 
|  | LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
|  |  | 
|  | const Instruction* store = nullptr; | 
|  | const Instruction* load = nullptr; | 
|  |  | 
|  | int block_id = 84; | 
|  | ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id)); | 
|  |  | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store = &inst; | 
|  | } | 
|  |  | 
|  | if (inst.opcode() == SpvOp::SpvOpLoad) { | 
|  | load = &inst; | 
|  | } | 
|  | } | 
|  |  | 
|  | EXPECT_NE(nullptr, store); | 
|  | EXPECT_NE(nullptr, load); | 
|  |  | 
|  | std::vector<const Loop*> loop_nest{&ld.GetLoopByIndex(0)}; | 
|  | LoopDependenceAnalysis analysis{context.get(), loop_nest}; | 
|  |  | 
|  | DistanceVector dv_entry(loop_nest.size()); | 
|  | auto is_independent = analysis.GetDependence(load, store, &dv_entry); | 
|  |  | 
|  | DistanceVector expected_distance_vector({DistanceEntry(1, 2)}); | 
|  |  | 
|  | EXPECT_FALSE(is_independent); | 
|  | EXPECT_EQ(expected_distance_vector, dv_entry); | 
|  | } | 
|  |  | 
|  | { | 
|  | const Function* f = spvtest::GetFunction(module, 12); | 
|  | LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
|  |  | 
|  | const Instruction* store = nullptr; | 
|  | const Instruction* load = nullptr; | 
|  |  | 
|  | int block_id = 119; | 
|  | ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id)); | 
|  |  | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store = &inst; | 
|  | } | 
|  |  | 
|  | if (inst.opcode() == SpvOp::SpvOpLoad) { | 
|  | load = &inst; | 
|  | } | 
|  | } | 
|  |  | 
|  | EXPECT_NE(nullptr, store); | 
|  | EXPECT_NE(nullptr, load); | 
|  |  | 
|  | std::vector<const Loop*> loop_nest{ | 
|  | &ld.GetLoopByIndex(0), &ld.GetLoopByIndex(1), &ld.GetLoopByIndex(2)}; | 
|  | LoopDependenceAnalysis analysis{context.get(), loop_nest}; | 
|  |  | 
|  | DistanceVector dv_entry(loop_nest.size()); | 
|  |  | 
|  | std::vector<DistanceEntry> expected_entries{ | 
|  | DistanceEntry(DistanceEntry::Directions::LT, 1), | 
|  | DistanceEntry(DistanceEntry::Directions::LT, 1), | 
|  | DistanceEntry(DistanceEntry::Directions::GT, -1)}; | 
|  |  | 
|  | DistanceVector expected_distance_vector(expected_entries); | 
|  |  | 
|  | auto is_independent = analysis.GetDependence(store, load, &dv_entry); | 
|  |  | 
|  | EXPECT_FALSE(is_independent); | 
|  | EXPECT_EQ(expected_distance_vector, dv_entry); | 
|  | } | 
|  |  | 
|  | { | 
|  | const Function* f = spvtest::GetFunction(module, 14); | 
|  | LoopDescriptor& ld = *context->GetLoopDescriptor(f); | 
|  |  | 
|  | const Instruction* store = nullptr; | 
|  | const Instruction* load = nullptr; | 
|  |  | 
|  | int block_id = 162; | 
|  | ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id)); | 
|  |  | 
|  | for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) { | 
|  | if (inst.opcode() == SpvOp::SpvOpStore) { | 
|  | store = &inst; | 
|  | } | 
|  |  | 
|  | if (inst.opcode() == SpvOp::SpvOpLoad) { | 
|  | load = &inst; | 
|  | } | 
|  | } | 
|  |  | 
|  | EXPECT_NE(nullptr, store); | 
|  | EXPECT_NE(nullptr, load); | 
|  |  | 
|  | std::vector<const Loop*> loop_nest{&ld.GetLoopByIndex(0), | 
|  | &ld.GetLoopByIndex(1)}; | 
|  | LoopDependenceAnalysis analysis{context.get(), loop_nest}; | 
|  |  | 
|  | DistanceVector dv_entry(loop_nest.size()); | 
|  | auto is_independent = analysis.GetDependence(load, store, &dv_entry); | 
|  |  | 
|  | EXPECT_TRUE(is_independent); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(DependencyAnalysis, ConstraintIntersection) { | 
|  | LoopDependenceAnalysis analysis{nullptr, std::vector<const Loop*>{}}; | 
|  | auto scalar_evolution = analysis.GetScalarEvolution(); | 
|  | { | 
|  | // One is none. Other should be returned | 
|  | auto none = analysis.make_constraint<DependenceNone>(); | 
|  | auto x = scalar_evolution->CreateConstant(1); | 
|  | auto y = scalar_evolution->CreateConstant(10); | 
|  | auto point = analysis.make_constraint<DependencePoint>(x, y, nullptr); | 
|  |  | 
|  | auto ret_0 = analysis.IntersectConstraints(none, point, nullptr, nullptr); | 
|  |  | 
|  | auto ret_point_0 = ret_0->AsDependencePoint(); | 
|  | ASSERT_NE(nullptr, ret_point_0); | 
|  | EXPECT_EQ(*x, *ret_point_0->GetSource()); | 
|  | EXPECT_EQ(*y, *ret_point_0->GetDestination()); | 
|  |  | 
|  | auto ret_1 = analysis.IntersectConstraints(point, none, nullptr, nullptr); | 
|  |  | 
|  | auto ret_point_1 = ret_1->AsDependencePoint(); | 
|  | ASSERT_NE(nullptr, ret_point_1); | 
|  | EXPECT_EQ(*x, *ret_point_1->GetSource()); | 
|  | EXPECT_EQ(*y, *ret_point_1->GetDestination()); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Both distances | 
|  | auto x = scalar_evolution->CreateConstant(1); | 
|  | auto y = scalar_evolution->CreateConstant(10); | 
|  |  | 
|  | auto distance_0 = analysis.make_constraint<DependenceDistance>(x, nullptr); | 
|  | auto distance_1 = analysis.make_constraint<DependenceDistance>(y, nullptr); | 
|  |  | 
|  | // Equal distances | 
|  | auto ret_0 = | 
|  | analysis.IntersectConstraints(distance_1, distance_1, nullptr, nullptr); | 
|  |  | 
|  | auto ret_distance = ret_0->AsDependenceDistance(); | 
|  | ASSERT_NE(nullptr, ret_distance); | 
|  | EXPECT_EQ(*y, *ret_distance->GetDistance()); | 
|  |  | 
|  | // Non-equal distances | 
|  | auto ret_1 = | 
|  | analysis.IntersectConstraints(distance_0, distance_1, nullptr, nullptr); | 
|  | EXPECT_NE(nullptr, ret_1->AsDependenceEmpty()); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Both points | 
|  | auto x = scalar_evolution->CreateConstant(1); | 
|  | auto y = scalar_evolution->CreateConstant(10); | 
|  |  | 
|  | auto point_0 = analysis.make_constraint<DependencePoint>(x, y, nullptr); | 
|  | auto point_1 = analysis.make_constraint<DependencePoint>(x, y, nullptr); | 
|  | auto point_2 = analysis.make_constraint<DependencePoint>(y, y, nullptr); | 
|  |  | 
|  | // Equal points | 
|  | auto ret_0 = | 
|  | analysis.IntersectConstraints(point_0, point_1, nullptr, nullptr); | 
|  | auto ret_point_0 = ret_0->AsDependencePoint(); | 
|  | ASSERT_NE(nullptr, ret_point_0); | 
|  | EXPECT_EQ(*x, *ret_point_0->GetSource()); | 
|  | EXPECT_EQ(*y, *ret_point_0->GetDestination()); | 
|  |  | 
|  | // Non-equal points | 
|  | auto ret_1 = | 
|  | analysis.IntersectConstraints(point_0, point_2, nullptr, nullptr); | 
|  | EXPECT_NE(nullptr, ret_1->AsDependenceEmpty()); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Both lines, parallel | 
|  | auto a0 = scalar_evolution->CreateConstant(3); | 
|  | auto b0 = scalar_evolution->CreateConstant(6); | 
|  | auto c0 = scalar_evolution->CreateConstant(9); | 
|  |  | 
|  | auto a1 = scalar_evolution->CreateConstant(6); | 
|  | auto b1 = scalar_evolution->CreateConstant(12); | 
|  | auto c1 = scalar_evolution->CreateConstant(18); | 
|  |  | 
|  | auto line_0 = analysis.make_constraint<DependenceLine>(a0, b0, c0, nullptr); | 
|  | auto line_1 = analysis.make_constraint<DependenceLine>(a1, b1, c1, nullptr); | 
|  |  | 
|  | // Same line, both ways | 
|  | auto ret_0 = | 
|  | analysis.IntersectConstraints(line_0, line_1, nullptr, nullptr); | 
|  | auto ret_1 = | 
|  | analysis.IntersectConstraints(line_1, line_0, nullptr, nullptr); | 
|  |  | 
|  | auto ret_line_0 = ret_0->AsDependenceLine(); | 
|  | auto ret_line_1 = ret_1->AsDependenceLine(); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_line_0); | 
|  | EXPECT_NE(nullptr, ret_line_1); | 
|  |  | 
|  | // Non-intersecting parallel lines | 
|  | auto c2 = scalar_evolution->CreateConstant(12); | 
|  | auto line_2 = analysis.make_constraint<DependenceLine>(a1, b1, c2, nullptr); | 
|  |  | 
|  | auto ret_2 = | 
|  | analysis.IntersectConstraints(line_0, line_2, nullptr, nullptr); | 
|  | auto ret_3 = | 
|  | analysis.IntersectConstraints(line_2, line_0, nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_2->AsDependenceEmpty()); | 
|  | EXPECT_NE(nullptr, ret_3->AsDependenceEmpty()); | 
|  |  | 
|  | auto c3 = scalar_evolution->CreateConstant(20); | 
|  | auto line_3 = analysis.make_constraint<DependenceLine>(a1, b1, c3, nullptr); | 
|  |  | 
|  | auto ret_4 = | 
|  | analysis.IntersectConstraints(line_0, line_3, nullptr, nullptr); | 
|  | auto ret_5 = | 
|  | analysis.IntersectConstraints(line_3, line_0, nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_4->AsDependenceEmpty()); | 
|  | EXPECT_NE(nullptr, ret_5->AsDependenceEmpty()); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Non-constant line | 
|  | auto unknown = scalar_evolution->CreateCantComputeNode(); | 
|  | auto constant = scalar_evolution->CreateConstant(10); | 
|  |  | 
|  | auto line_0 = analysis.make_constraint<DependenceLine>(constant, constant, | 
|  | constant, nullptr); | 
|  | auto line_1 = analysis.make_constraint<DependenceLine>(unknown, unknown, | 
|  | unknown, nullptr); | 
|  |  | 
|  | auto ret_0 = | 
|  | analysis.IntersectConstraints(line_0, line_1, nullptr, nullptr); | 
|  | auto ret_1 = | 
|  | analysis.IntersectConstraints(line_1, line_0, nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_0->AsDependenceNone()); | 
|  | EXPECT_NE(nullptr, ret_1->AsDependenceNone()); | 
|  | } | 
|  |  | 
|  | { | 
|  | auto bound_0 = scalar_evolution->CreateConstant(0); | 
|  | auto bound_1 = scalar_evolution->CreateConstant(20); | 
|  |  | 
|  | auto a0 = scalar_evolution->CreateConstant(1); | 
|  | auto b0 = scalar_evolution->CreateConstant(2); | 
|  | auto c0 = scalar_evolution->CreateConstant(6); | 
|  |  | 
|  | auto a1 = scalar_evolution->CreateConstant(-1); | 
|  | auto b1 = scalar_evolution->CreateConstant(2); | 
|  | auto c1 = scalar_evolution->CreateConstant(2); | 
|  |  | 
|  | auto line_0 = analysis.make_constraint<DependenceLine>(a0, b0, c0, nullptr); | 
|  | auto line_1 = analysis.make_constraint<DependenceLine>(a1, b1, c1, nullptr); | 
|  |  | 
|  | // Intersecting lines, has integer solution, in bounds | 
|  | auto ret_0 = | 
|  | analysis.IntersectConstraints(line_0, line_1, bound_0, bound_1); | 
|  | auto ret_1 = | 
|  | analysis.IntersectConstraints(line_1, line_0, bound_0, bound_1); | 
|  |  | 
|  | auto ret_point_0 = ret_0->AsDependencePoint(); | 
|  | auto ret_point_1 = ret_1->AsDependencePoint(); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_point_0); | 
|  | EXPECT_NE(nullptr, ret_point_1); | 
|  |  | 
|  | auto const_2 = scalar_evolution->CreateConstant(2); | 
|  |  | 
|  | EXPECT_EQ(*const_2, *ret_point_0->GetSource()); | 
|  | EXPECT_EQ(*const_2, *ret_point_0->GetDestination()); | 
|  |  | 
|  | EXPECT_EQ(*const_2, *ret_point_1->GetSource()); | 
|  | EXPECT_EQ(*const_2, *ret_point_1->GetDestination()); | 
|  |  | 
|  | // Intersecting lines, has integer solution, out of bounds | 
|  | auto ret_2 = | 
|  | analysis.IntersectConstraints(line_0, line_1, bound_0, bound_0); | 
|  | auto ret_3 = | 
|  | analysis.IntersectConstraints(line_1, line_0, bound_0, bound_0); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_2->AsDependenceEmpty()); | 
|  | EXPECT_NE(nullptr, ret_3->AsDependenceEmpty()); | 
|  |  | 
|  | auto a2 = scalar_evolution->CreateConstant(-4); | 
|  | auto b2 = scalar_evolution->CreateConstant(1); | 
|  | auto c2 = scalar_evolution->CreateConstant(0); | 
|  |  | 
|  | auto a3 = scalar_evolution->CreateConstant(4); | 
|  | auto b3 = scalar_evolution->CreateConstant(1); | 
|  | auto c3 = scalar_evolution->CreateConstant(4); | 
|  |  | 
|  | auto line_2 = analysis.make_constraint<DependenceLine>(a2, b2, c2, nullptr); | 
|  | auto line_3 = analysis.make_constraint<DependenceLine>(a3, b3, c3, nullptr); | 
|  |  | 
|  | // Intersecting, no integer solution | 
|  | auto ret_4 = | 
|  | analysis.IntersectConstraints(line_2, line_3, bound_0, bound_1); | 
|  | auto ret_5 = | 
|  | analysis.IntersectConstraints(line_3, line_2, bound_0, bound_1); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_4->AsDependenceEmpty()); | 
|  | EXPECT_NE(nullptr, ret_5->AsDependenceEmpty()); | 
|  |  | 
|  | auto unknown = scalar_evolution->CreateCantComputeNode(); | 
|  |  | 
|  | // Non-constant bound | 
|  | auto ret_6 = | 
|  | analysis.IntersectConstraints(line_0, line_1, unknown, bound_1); | 
|  | auto ret_7 = | 
|  | analysis.IntersectConstraints(line_1, line_0, bound_0, unknown); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_6->AsDependenceNone()); | 
|  | EXPECT_NE(nullptr, ret_7->AsDependenceNone()); | 
|  | } | 
|  |  | 
|  | { | 
|  | auto constant_0 = scalar_evolution->CreateConstant(0); | 
|  | auto constant_1 = scalar_evolution->CreateConstant(1); | 
|  | auto constant_neg_1 = scalar_evolution->CreateConstant(-1); | 
|  | auto constant_2 = scalar_evolution->CreateConstant(2); | 
|  | auto constant_neg_2 = scalar_evolution->CreateConstant(-2); | 
|  |  | 
|  | auto point_0_0 = analysis.make_constraint<DependencePoint>( | 
|  | constant_0, constant_0, nullptr); | 
|  | auto point_0_1 = analysis.make_constraint<DependencePoint>( | 
|  | constant_0, constant_1, nullptr); | 
|  | auto point_1_0 = analysis.make_constraint<DependencePoint>( | 
|  | constant_1, constant_0, nullptr); | 
|  | auto point_1_1 = analysis.make_constraint<DependencePoint>( | 
|  | constant_1, constant_1, nullptr); | 
|  | auto point_1_2 = analysis.make_constraint<DependencePoint>( | 
|  | constant_1, constant_2, nullptr); | 
|  | auto point_1_neg_1 = analysis.make_constraint<DependencePoint>( | 
|  | constant_1, constant_neg_1, nullptr); | 
|  | auto point_neg_1_1 = analysis.make_constraint<DependencePoint>( | 
|  | constant_neg_1, constant_1, nullptr); | 
|  |  | 
|  | auto line_y_0 = analysis.make_constraint<DependenceLine>( | 
|  | constant_0, constant_1, constant_0, nullptr); | 
|  | auto line_y_1 = analysis.make_constraint<DependenceLine>( | 
|  | constant_0, constant_1, constant_1, nullptr); | 
|  | auto line_y_2 = analysis.make_constraint<DependenceLine>( | 
|  | constant_0, constant_1, constant_2, nullptr); | 
|  |  | 
|  | // Parallel horizontal lines, y = 0 & y = 1, should return no intersection | 
|  | auto ret = | 
|  | analysis.IntersectConstraints(line_y_0, line_y_1, nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret->AsDependenceEmpty()); | 
|  |  | 
|  | // Parallel horizontal lines, y = 1 & y = 2, should return no intersection | 
|  | auto ret_y_12 = | 
|  | analysis.IntersectConstraints(line_y_1, line_y_2, nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_y_12->AsDependenceEmpty()); | 
|  |  | 
|  | // Same horizontal lines, y = 0 & y = 0, should return the line | 
|  | auto ret_y_same_0 = | 
|  | analysis.IntersectConstraints(line_y_0, line_y_0, nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_y_same_0->AsDependenceLine()); | 
|  |  | 
|  | // Same horizontal lines, y = 1 & y = 1, should return the line | 
|  | auto ret_y_same_1 = | 
|  | analysis.IntersectConstraints(line_y_1, line_y_1, nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_y_same_1->AsDependenceLine()); | 
|  |  | 
|  | auto line_x_0 = analysis.make_constraint<DependenceLine>( | 
|  | constant_1, constant_0, constant_0, nullptr); | 
|  | auto line_x_1 = analysis.make_constraint<DependenceLine>( | 
|  | constant_1, constant_0, constant_1, nullptr); | 
|  | auto line_x_2 = analysis.make_constraint<DependenceLine>( | 
|  | constant_1, constant_0, constant_2, nullptr); | 
|  | auto line_2x_1 = analysis.make_constraint<DependenceLine>( | 
|  | constant_2, constant_0, constant_1, nullptr); | 
|  | auto line_2x_2 = analysis.make_constraint<DependenceLine>( | 
|  | constant_2, constant_0, constant_2, nullptr); | 
|  |  | 
|  | // Parallel vertical lines, x = 0 & x = 1, should return no intersection | 
|  | auto ret_x = | 
|  | analysis.IntersectConstraints(line_x_0, line_x_1, nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_x->AsDependenceEmpty()); | 
|  |  | 
|  | // Parallel vertical lines, x = 1 & x = 2, should return no intersection | 
|  | auto ret_x_12 = | 
|  | analysis.IntersectConstraints(line_x_1, line_x_2, nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_x_12->AsDependenceEmpty()); | 
|  |  | 
|  | // Parallel vertical lines, 2x = 1 & 2x = 2, should return no intersection | 
|  | auto ret_2x_2_2x_1 = | 
|  | analysis.IntersectConstraints(line_2x_2, line_2x_1, nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_2x_2_2x_1->AsDependenceEmpty()); | 
|  |  | 
|  | // same line, 2x=2 & x = 1 | 
|  | auto ret_2x_2_x_1 = | 
|  | analysis.IntersectConstraints(line_2x_2, line_x_1, nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_2x_2_x_1->AsDependenceLine()); | 
|  |  | 
|  | // Same vertical lines, x = 0 & x = 0, should return the line | 
|  | auto ret_x_same_0 = | 
|  | analysis.IntersectConstraints(line_x_0, line_x_0, nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_x_same_0->AsDependenceLine()); | 
|  | // EXPECT_EQ(*line_x_0, *ret_x_same_0->AsDependenceLine()); | 
|  |  | 
|  | // Same vertical lines, x = 1 & x = 1, should return the line | 
|  | auto ret_x_same_1 = | 
|  | analysis.IntersectConstraints(line_x_1, line_x_1, nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_x_same_1->AsDependenceLine()); | 
|  | EXPECT_EQ(*line_x_1, *ret_x_same_1->AsDependenceLine()); | 
|  |  | 
|  | // x=1 & y = 0, intersect at (1, 0) | 
|  | auto ret_1_0 = analysis.IntersectConstraints(line_x_1, line_y_0, | 
|  | constant_neg_1, constant_2); | 
|  |  | 
|  | auto ret_point_1_0 = ret_1_0->AsDependencePoint(); | 
|  | EXPECT_NE(nullptr, ret_point_1_0); | 
|  | EXPECT_EQ(*point_1_0, *ret_point_1_0); | 
|  |  | 
|  | // x=1 & y = 1, intersect at (1, 1) | 
|  | auto ret_1_1 = analysis.IntersectConstraints(line_x_1, line_y_1, | 
|  | constant_neg_1, constant_2); | 
|  |  | 
|  | auto ret_point_1_1 = ret_1_1->AsDependencePoint(); | 
|  | EXPECT_NE(nullptr, ret_point_1_1); | 
|  | EXPECT_EQ(*point_1_1, *ret_point_1_1); | 
|  |  | 
|  | // x=0 & y = 0, intersect at (0, 0) | 
|  | auto ret_0_0 = analysis.IntersectConstraints(line_x_0, line_y_0, | 
|  | constant_neg_1, constant_2); | 
|  |  | 
|  | auto ret_point_0_0 = ret_0_0->AsDependencePoint(); | 
|  | EXPECT_NE(nullptr, ret_point_0_0); | 
|  | EXPECT_EQ(*point_0_0, *ret_point_0_0); | 
|  |  | 
|  | // x=0 & y = 1, intersect at (0, 1) | 
|  | auto ret_0_1 = analysis.IntersectConstraints(line_x_0, line_y_1, | 
|  | constant_neg_1, constant_2); | 
|  | auto ret_point_0_1 = ret_0_1->AsDependencePoint(); | 
|  | EXPECT_NE(nullptr, ret_point_0_1); | 
|  | EXPECT_EQ(*point_0_1, *ret_point_0_1); | 
|  |  | 
|  | // x = 1 & y = 2 | 
|  | auto ret_1_2 = analysis.IntersectConstraints(line_x_1, line_y_2, | 
|  | constant_neg_1, constant_2); | 
|  | auto ret_point_1_2 = ret_1_2->AsDependencePoint(); | 
|  | EXPECT_NE(nullptr, ret_point_1_2); | 
|  | EXPECT_EQ(*point_1_2, *ret_point_1_2); | 
|  |  | 
|  | auto line_x_y_0 = analysis.make_constraint<DependenceLine>( | 
|  | constant_1, constant_1, constant_0, nullptr); | 
|  | auto line_x_y_1 = analysis.make_constraint<DependenceLine>( | 
|  | constant_1, constant_1, constant_1, nullptr); | 
|  |  | 
|  | // x+y=0 & x=0, intersect (0, 0) | 
|  | auto ret_xy_0_x_0 = analysis.IntersectConstraints( | 
|  | line_x_y_0, line_x_0, constant_neg_1, constant_2); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_xy_0_x_0->AsDependencePoint()); | 
|  | EXPECT_EQ(*point_0_0, *ret_xy_0_x_0); | 
|  |  | 
|  | // x+y=0 & y=0, intersect (0, 0) | 
|  | auto ret_xy_0_y_0 = analysis.IntersectConstraints( | 
|  | line_x_y_0, line_y_0, constant_neg_1, constant_2); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_xy_0_y_0->AsDependencePoint()); | 
|  | EXPECT_EQ(*point_0_0, *ret_xy_0_y_0); | 
|  |  | 
|  | // x+y=0 & x=1, intersect (1, -1) | 
|  | auto ret_xy_0_x_1 = analysis.IntersectConstraints( | 
|  | line_x_y_0, line_x_1, constant_neg_2, constant_2); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_xy_0_x_1->AsDependencePoint()); | 
|  | EXPECT_EQ(*point_1_neg_1, *ret_xy_0_x_1); | 
|  |  | 
|  | // x+y=0 & y=1, intersect (-1, 1) | 
|  | auto ret_xy_0_y_1 = analysis.IntersectConstraints( | 
|  | line_x_y_0, line_y_1, constant_neg_2, constant_2); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_xy_0_y_1->AsDependencePoint()); | 
|  | EXPECT_EQ(*point_neg_1_1, *ret_xy_0_y_1); | 
|  |  | 
|  | // x=0 & x+y=0, intersect (0, 0) | 
|  | auto ret_x_0_xy_0 = analysis.IntersectConstraints( | 
|  | line_x_0, line_x_y_0, constant_neg_1, constant_2); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_x_0_xy_0->AsDependencePoint()); | 
|  | EXPECT_EQ(*point_0_0, *ret_x_0_xy_0); | 
|  |  | 
|  | // y=0 & x+y=0, intersect (0, 0) | 
|  | auto ret_y_0_xy_0 = analysis.IntersectConstraints( | 
|  | line_y_0, line_x_y_0, constant_neg_1, constant_2); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_y_0_xy_0->AsDependencePoint()); | 
|  | EXPECT_EQ(*point_0_0, *ret_y_0_xy_0); | 
|  |  | 
|  | // x=1 & x+y=0, intersect (1, -1) | 
|  | auto ret_x_1_xy_0 = analysis.IntersectConstraints( | 
|  | line_x_1, line_x_y_0, constant_neg_2, constant_2); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_x_1_xy_0->AsDependencePoint()); | 
|  | EXPECT_EQ(*point_1_neg_1, *ret_x_1_xy_0); | 
|  |  | 
|  | // y=1 & x+y=0, intersect (-1, 1) | 
|  | auto ret_y_1_xy_0 = analysis.IntersectConstraints( | 
|  | line_y_1, line_x_y_0, constant_neg_2, constant_2); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_y_1_xy_0->AsDependencePoint()); | 
|  | EXPECT_EQ(*point_neg_1_1, *ret_y_1_xy_0); | 
|  |  | 
|  | // x+y=1 & x=0, intersect (0, 1) | 
|  | auto ret_xy_1_x_0 = analysis.IntersectConstraints( | 
|  | line_x_y_1, line_x_0, constant_neg_1, constant_2); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_xy_1_x_0->AsDependencePoint()); | 
|  | EXPECT_EQ(*point_0_1, *ret_xy_1_x_0); | 
|  |  | 
|  | // x+y=1 & y=0, intersect (1, 0) | 
|  | auto ret_xy_1_y_0 = analysis.IntersectConstraints( | 
|  | line_x_y_1, line_y_0, constant_neg_1, constant_2); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_xy_1_y_0->AsDependencePoint()); | 
|  | EXPECT_EQ(*point_1_0, *ret_xy_1_y_0); | 
|  |  | 
|  | // x+y=1 & x=1, intersect (1, 0) | 
|  | auto ret_xy_1_x_1 = analysis.IntersectConstraints( | 
|  | line_x_y_1, line_x_1, constant_neg_1, constant_2); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_xy_1_x_1->AsDependencePoint()); | 
|  | EXPECT_EQ(*point_1_0, *ret_xy_1_x_1); | 
|  |  | 
|  | // x+y=1 & y=1, intersect (0, 1) | 
|  | auto ret_xy_1_y_1 = analysis.IntersectConstraints( | 
|  | line_x_y_1, line_y_1, constant_neg_1, constant_2); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_xy_1_y_1->AsDependencePoint()); | 
|  | EXPECT_EQ(*point_0_1, *ret_xy_1_y_1); | 
|  |  | 
|  | // x=0 & x+y=1, intersect (0, 1) | 
|  | auto ret_x_0_xy_1 = analysis.IntersectConstraints( | 
|  | line_x_0, line_x_y_1, constant_neg_1, constant_2); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_x_0_xy_1->AsDependencePoint()); | 
|  | EXPECT_EQ(*point_0_1, *ret_x_0_xy_1); | 
|  |  | 
|  | // y=0 & x+y=1, intersect (1, 0) | 
|  | auto ret_y_0_xy_1 = analysis.IntersectConstraints( | 
|  | line_y_0, line_x_y_1, constant_neg_1, constant_2); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_y_0_xy_1->AsDependencePoint()); | 
|  | EXPECT_EQ(*point_1_0, *ret_y_0_xy_1); | 
|  |  | 
|  | // x=1 & x+y=1, intersect (1, 0) | 
|  | auto ret_x_1_xy_1 = analysis.IntersectConstraints( | 
|  | line_x_1, line_x_y_1, constant_neg_2, constant_2); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_x_1_xy_1->AsDependencePoint()); | 
|  | EXPECT_EQ(*point_1_0, *ret_x_1_xy_1); | 
|  |  | 
|  | // y=1 & x+y=1, intersect (0, 1) | 
|  | auto ret_y_1_xy_1 = analysis.IntersectConstraints( | 
|  | line_y_1, line_x_y_1, constant_neg_2, constant_2); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_y_1_xy_1->AsDependencePoint()); | 
|  | EXPECT_EQ(*point_0_1, *ret_y_1_xy_1); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Line and point | 
|  | auto a = scalar_evolution->CreateConstant(3); | 
|  | auto b = scalar_evolution->CreateConstant(10); | 
|  | auto c = scalar_evolution->CreateConstant(16); | 
|  |  | 
|  | auto line = analysis.make_constraint<DependenceLine>(a, b, c, nullptr); | 
|  |  | 
|  | // Point on line | 
|  | auto x = scalar_evolution->CreateConstant(2); | 
|  | auto y = scalar_evolution->CreateConstant(1); | 
|  | auto point_0 = analysis.make_constraint<DependencePoint>(x, y, nullptr); | 
|  |  | 
|  | auto ret_0 = analysis.IntersectConstraints(line, point_0, nullptr, nullptr); | 
|  | auto ret_1 = analysis.IntersectConstraints(point_0, line, nullptr, nullptr); | 
|  |  | 
|  | auto ret_point_0 = ret_0->AsDependencePoint(); | 
|  | auto ret_point_1 = ret_1->AsDependencePoint(); | 
|  | ASSERT_NE(nullptr, ret_point_0); | 
|  | ASSERT_NE(nullptr, ret_point_1); | 
|  |  | 
|  | EXPECT_EQ(*x, *ret_point_0->GetSource()); | 
|  | EXPECT_EQ(*y, *ret_point_0->GetDestination()); | 
|  |  | 
|  | EXPECT_EQ(*x, *ret_point_1->GetSource()); | 
|  | EXPECT_EQ(*y, *ret_point_1->GetDestination()); | 
|  |  | 
|  | // Point not on line | 
|  | auto point_1 = analysis.make_constraint<DependencePoint>(a, a, nullptr); | 
|  |  | 
|  | auto ret_2 = analysis.IntersectConstraints(line, point_1, nullptr, nullptr); | 
|  | auto ret_3 = analysis.IntersectConstraints(point_1, line, nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_2->AsDependenceEmpty()); | 
|  | EXPECT_NE(nullptr, ret_3->AsDependenceEmpty()); | 
|  |  | 
|  | // Non-constant | 
|  | auto unknown = scalar_evolution->CreateCantComputeNode(); | 
|  |  | 
|  | auto point_2 = | 
|  | analysis.make_constraint<DependencePoint>(unknown, x, nullptr); | 
|  |  | 
|  | auto ret_4 = analysis.IntersectConstraints(line, point_2, nullptr, nullptr); | 
|  | auto ret_5 = analysis.IntersectConstraints(point_2, line, nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_4->AsDependenceNone()); | 
|  | EXPECT_NE(nullptr, ret_5->AsDependenceNone()); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Distance and point | 
|  | auto d = scalar_evolution->CreateConstant(5); | 
|  | auto distance = analysis.make_constraint<DependenceDistance>(d, nullptr); | 
|  |  | 
|  | // Point on line | 
|  | auto x = scalar_evolution->CreateConstant(10); | 
|  | auto point_0 = analysis.make_constraint<DependencePoint>(d, x, nullptr); | 
|  |  | 
|  | auto ret_0 = | 
|  | analysis.IntersectConstraints(distance, point_0, nullptr, nullptr); | 
|  | auto ret_1 = | 
|  | analysis.IntersectConstraints(point_0, distance, nullptr, nullptr); | 
|  |  | 
|  | auto ret_point_0 = ret_0->AsDependencePoint(); | 
|  | auto ret_point_1 = ret_1->AsDependencePoint(); | 
|  | ASSERT_NE(nullptr, ret_point_0); | 
|  | ASSERT_NE(nullptr, ret_point_1); | 
|  |  | 
|  | // Point not on line | 
|  | auto point_1 = analysis.make_constraint<DependencePoint>(x, x, nullptr); | 
|  |  | 
|  | auto ret_2 = | 
|  | analysis.IntersectConstraints(distance, point_1, nullptr, nullptr); | 
|  | auto ret_3 = | 
|  | analysis.IntersectConstraints(point_1, distance, nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_2->AsDependenceEmpty()); | 
|  | EXPECT_NE(nullptr, ret_3->AsDependenceEmpty()); | 
|  |  | 
|  | // Non-constant | 
|  | auto unknown = scalar_evolution->CreateCantComputeNode(); | 
|  | auto unknown_distance = | 
|  | analysis.make_constraint<DependenceDistance>(unknown, nullptr); | 
|  |  | 
|  | auto ret_4 = analysis.IntersectConstraints(unknown_distance, point_1, | 
|  | nullptr, nullptr); | 
|  | auto ret_5 = analysis.IntersectConstraints(point_1, unknown_distance, | 
|  | nullptr, nullptr); | 
|  |  | 
|  | EXPECT_NE(nullptr, ret_4->AsDependenceNone()); | 
|  | EXPECT_NE(nullptr, ret_5->AsDependenceNone()); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace opt | 
|  | }  // namespace spvtools |