mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-11 03:10:05 +00:00
eda2cfbe12
This Cl cleans up the include paths to be relative to the top level directory. Various include-what-you-use fixes have been added.
4206 lines
139 KiB
C++
4206 lines
139 KiB
C++
// 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
|