| // 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 |