mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-29 14:31:04 +00:00
937227c761
Currently, handles promotion of divergence due to reconvergence rules, but doesn't handle "late merges" caused by a later-than-necessary declared merge block. Co-authored-by: Jakub Kuderski <kubak@google.com>
701 lines
23 KiB
C++
701 lines
23 KiB
C++
// Copyright (c) 2021 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 "source/lint/divergence_analysis.h"
|
|
|
|
#include <string>
|
|
|
|
#include "gtest/gtest.h"
|
|
#include "source/opt/build_module.h"
|
|
#include "source/opt/ir_context.h"
|
|
#include "source/opt/module.h"
|
|
#include "spirv-tools/libspirv.h"
|
|
|
|
namespace spvtools {
|
|
namespace lint {
|
|
namespace {
|
|
|
|
void CLIMessageConsumer(spv_message_level_t level, const char*,
|
|
const spv_position_t& position, const char* message) {
|
|
switch (level) {
|
|
case SPV_MSG_FATAL:
|
|
case SPV_MSG_INTERNAL_ERROR:
|
|
case SPV_MSG_ERROR:
|
|
std::cerr << "error: line " << position.index << ": " << message
|
|
<< std::endl;
|
|
break;
|
|
case SPV_MSG_WARNING:
|
|
std::cout << "warning: line " << position.index << ": " << message
|
|
<< std::endl;
|
|
break;
|
|
case SPV_MSG_INFO:
|
|
std::cout << "info: line " << position.index << ": " << message
|
|
<< std::endl;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
class DivergenceTest : public ::testing::Test {
|
|
protected:
|
|
std::unique_ptr<opt::IRContext> context_;
|
|
std::unique_ptr<DivergenceAnalysis> divergence_;
|
|
|
|
void Build(std::string text, uint32_t function_id = 1) {
|
|
context_ = BuildModule(SPV_ENV_UNIVERSAL_1_0, CLIMessageConsumer, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
ASSERT_NE(nullptr, context_.get());
|
|
opt::Module* module = context_->module();
|
|
ASSERT_NE(nullptr, module);
|
|
// First function should have the given ID.
|
|
ASSERT_NE(module->begin(), module->end());
|
|
opt::Function* function = &*module->begin();
|
|
ASSERT_EQ(function->result_id(), function_id);
|
|
divergence_.reset(new DivergenceAnalysis(*context_));
|
|
divergence_->Run(function);
|
|
}
|
|
};
|
|
|
|
// Makes assertions a bit shorter.
|
|
using Level = DivergenceAnalysis::DivergenceLevel;
|
|
|
|
namespace {
|
|
std::string Preamble() {
|
|
return R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %1 "main" %x %y
|
|
OpExecutionMode %1 OriginLowerLeft
|
|
OpDecorate %y Flat
|
|
%void = OpTypeVoid
|
|
%void_f = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%float = OpTypeFloat 32
|
|
%false = OpConstantFalse %bool
|
|
%true = OpConstantTrue %bool
|
|
%zero = OpConstant %float 0
|
|
%one = OpConstant %float 1
|
|
%x_t = OpTypePointer Input %float
|
|
%x = OpVariable %x_t Input
|
|
%y = OpVariable %x_t Input
|
|
%1 = OpFunction %void None %void_f
|
|
)";
|
|
}
|
|
} // namespace
|
|
|
|
TEST_F(DivergenceTest, SimpleTest) {
|
|
// pseudocode:
|
|
// %10:
|
|
// %11 = load x
|
|
// if (%12 = (%11 < 0)) {
|
|
// %13:
|
|
// // do nothing
|
|
// }
|
|
// %14:
|
|
// return
|
|
ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
|
|
%10 = OpLabel
|
|
%11 = OpLoad %float %x
|
|
%12 = OpFOrdLessThan %bool %11 %zero
|
|
OpSelectionMerge %14 None
|
|
OpBranchConditional %12 %13 %14
|
|
%13 = OpLabel
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)"));
|
|
// Control flow divergence.
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13));
|
|
EXPECT_EQ(12, divergence_->GetDivergenceSource(13));
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14));
|
|
// Value divergence.
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11));
|
|
EXPECT_EQ(0, divergence_->GetDivergenceSource(11));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12));
|
|
EXPECT_EQ(11, divergence_->GetDivergenceSource(12));
|
|
}
|
|
|
|
TEST_F(DivergenceTest, FlowTypesTest) {
|
|
// pseudocode:
|
|
// %10:
|
|
// %11 = load x
|
|
// %12 = x < 0 // data -> data
|
|
// if (%12) {
|
|
// %13: // data -> control
|
|
// if (true) {
|
|
// %14: // control -> control
|
|
// }
|
|
// %15:
|
|
// %16 = 1
|
|
// } else {
|
|
// %17:
|
|
// %18 = 2
|
|
// }
|
|
// %19:
|
|
// %19 = phi(%16 from %15, %18 from %17) // control -> data
|
|
// return
|
|
ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
|
|
%10 = OpLabel
|
|
%11 = OpLoad %float %x
|
|
%12 = OpFOrdLessThan %bool %11 %zero
|
|
OpSelectionMerge %19 None
|
|
OpBranchConditional %12 %13 %17
|
|
%13 = OpLabel
|
|
OpSelectionMerge %15 None
|
|
OpBranchConditional %true %14 %15
|
|
%14 = OpLabel
|
|
OpBranch %15
|
|
%15 = OpLabel
|
|
%16 = OpFAdd %float %zero %zero
|
|
OpBranch %19
|
|
%17 = OpLabel
|
|
%18 = OpFAdd %float %zero %one
|
|
OpBranch %19
|
|
%19 = OpLabel
|
|
%20 = OpPhi %float %16 %15 %18 %17
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)"));
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
|
|
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11));
|
|
EXPECT_EQ(0, divergence_->GetDivergenceSource(11));
|
|
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12));
|
|
EXPECT_EQ(11, divergence_->GetDivergenceSource(12));
|
|
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13));
|
|
EXPECT_EQ(12, divergence_->GetDivergenceSource(13));
|
|
EXPECT_EQ(10, divergence_->GetDivergenceDependenceSource(13));
|
|
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14));
|
|
EXPECT_EQ(13, divergence_->GetDivergenceSource(14));
|
|
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15));
|
|
EXPECT_EQ(12, divergence_->GetDivergenceSource(15));
|
|
EXPECT_EQ(10, divergence_->GetDivergenceDependenceSource(15));
|
|
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(16));
|
|
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(17));
|
|
EXPECT_EQ(12, divergence_->GetDivergenceSource(17));
|
|
EXPECT_EQ(10, divergence_->GetDivergenceDependenceSource(17));
|
|
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(18));
|
|
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(19));
|
|
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(20));
|
|
EXPECT_TRUE(divergence_->GetDivergenceSource(20) == 15 ||
|
|
divergence_->GetDivergenceDependenceSource(20) == 17)
|
|
<< "Got: " << divergence_->GetDivergenceDependenceSource(20);
|
|
}
|
|
|
|
TEST_F(DivergenceTest, ExitDependenceTest) {
|
|
// pseudocode:
|
|
// %10:
|
|
// %11 = load x
|
|
// %12 = %11 < 0
|
|
// %13:
|
|
// do {
|
|
// %14:
|
|
// if (%12) {
|
|
// %15:
|
|
// continue;
|
|
// }
|
|
// %16:
|
|
// %17:
|
|
// continue;
|
|
// } %18: while(false);
|
|
// %19:
|
|
// return
|
|
ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
|
|
%10 = OpLabel
|
|
%11 = OpLoad %float %x
|
|
%12 = OpFOrdLessThan %bool %11 %zero ; data -> data
|
|
OpBranch %13
|
|
%13 = OpLabel
|
|
OpLoopMerge %19 %18 None
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
OpSelectionMerge %16 None
|
|
OpBranchConditional %12 %15 %16
|
|
%15 = OpLabel
|
|
OpBranch %18 ; continue
|
|
%16 = OpLabel
|
|
OpBranch %17
|
|
%17 = OpLabel
|
|
OpBranch %18 ; continue
|
|
%18 = OpLabel
|
|
OpBranchConditional %false %13 %19
|
|
%19 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)"));
|
|
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
|
|
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11));
|
|
EXPECT_EQ(0, divergence_->GetDivergenceSource(11));
|
|
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12));
|
|
EXPECT_EQ(11, divergence_->GetDivergenceSource(12));
|
|
|
|
// Since both branches continue, there's no divergent control dependence
|
|
// to 13.
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(13));
|
|
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14));
|
|
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15));
|
|
EXPECT_EQ(12, divergence_->GetDivergenceSource(15));
|
|
EXPECT_EQ(14, divergence_->GetDivergenceDependenceSource(15));
|
|
|
|
// These two blocks are outside the if but are still control dependent.
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16));
|
|
EXPECT_EQ(12, divergence_->GetDivergenceSource(16));
|
|
EXPECT_EQ(14, divergence_->GetDivergenceDependenceSource(16));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(17));
|
|
EXPECT_EQ(12, divergence_->GetDivergenceSource(17));
|
|
EXPECT_EQ(14, divergence_->GetDivergenceDependenceSource(17));
|
|
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(18));
|
|
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(19));
|
|
}
|
|
|
|
TEST_F(DivergenceTest, ReconvergencePromotionTest) {
|
|
// pseudocode:
|
|
// %10:
|
|
// %11 = load y
|
|
// %12 = %11 < 0
|
|
// if (%12) {
|
|
// %13:
|
|
// %14:
|
|
// %15:
|
|
// if (true) {
|
|
// %16:
|
|
// }
|
|
// // Reconvergence *not* guaranteed as
|
|
// // control is not uniform on the IG level
|
|
// // at %15.
|
|
// %17:
|
|
// %18:
|
|
// %19:
|
|
// %20 = load x
|
|
// }
|
|
// %21:
|
|
// %22 = phi(%11, %20)
|
|
// return
|
|
ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
|
|
%10 = OpLabel
|
|
%11 = OpLoad %float %y
|
|
%12 = OpFOrdLessThan %bool %11 %zero
|
|
OpSelectionMerge %21 None
|
|
OpBranchConditional %12 %13 %21
|
|
%13 = OpLabel
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
OpBranch %15
|
|
%15 = OpLabel
|
|
OpSelectionMerge %17 None
|
|
OpBranchConditional %true %16 %17
|
|
%16 = OpLabel
|
|
OpBranch %17
|
|
%17 = OpLabel
|
|
OpBranch %18
|
|
%18 = OpLabel
|
|
OpBranch %19
|
|
%19 = OpLabel
|
|
%20 = OpLoad %float %y
|
|
OpBranch %21
|
|
%21 = OpLabel
|
|
%22 = OpPhi %float %11 %10 %20 %19
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
|
|
ASSERT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(21));
|
|
|
|
ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(11));
|
|
ASSERT_EQ(0, divergence_->GetDivergenceSource(11));
|
|
ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(12));
|
|
ASSERT_EQ(11, divergence_->GetDivergenceSource(12));
|
|
ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(13));
|
|
ASSERT_EQ(12, divergence_->GetDivergenceSource(13));
|
|
ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(13));
|
|
ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(14));
|
|
ASSERT_EQ(12, divergence_->GetDivergenceSource(14));
|
|
ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(14));
|
|
ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(15));
|
|
ASSERT_EQ(12, divergence_->GetDivergenceSource(15));
|
|
ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(15));
|
|
ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(16));
|
|
ASSERT_EQ(15, divergence_->GetDivergenceSource(16));
|
|
|
|
ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(17));
|
|
ASSERT_EQ(12, divergence_->GetDivergenceSource(17));
|
|
ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(18));
|
|
ASSERT_EQ(12, divergence_->GetDivergenceSource(18));
|
|
ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(19));
|
|
ASSERT_EQ(12, divergence_->GetDivergenceSource(19));
|
|
|
|
ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(20));
|
|
ASSERT_EQ(0, divergence_->GetDivergenceSource(20));
|
|
ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(22));
|
|
ASSERT_EQ(19, divergence_->GetDivergenceSource(22));
|
|
ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(15));
|
|
}
|
|
|
|
TEST_F(DivergenceTest, FunctionCallTest) {
|
|
// pseudocode:
|
|
// %2() {
|
|
// %20:
|
|
// %21 = load x
|
|
// %22 = %21 < 0
|
|
// if (%22) {
|
|
// %23:
|
|
// return
|
|
// }
|
|
// %24:
|
|
// return
|
|
// }
|
|
//
|
|
// main() {
|
|
// %10:
|
|
// %11 = %2();
|
|
// // Reconvergence *not* guaranteed.
|
|
// %12:
|
|
// return
|
|
// }
|
|
ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
|
|
%10 = OpLabel
|
|
%11 = OpFunctionCall %void %2
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
|
|
%2 = OpFunction %void None %void_f
|
|
%20 = OpLabel
|
|
%21 = OpLoad %float %x
|
|
%22 = OpFOrdLessThan %bool %21 %zero
|
|
OpSelectionMerge %24 None
|
|
OpBranchConditional %22 %23 %24
|
|
%23 = OpLabel
|
|
OpReturn
|
|
%24 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)"));
|
|
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
|
|
// Conservatively assume function return value is uniform.
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(11));
|
|
// TODO(dongja): blocks reachable from diverging function calls should be
|
|
// divergent.
|
|
// EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12));
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(12)); // Wrong!
|
|
}
|
|
|
|
TEST_F(DivergenceTest, LateMergeTest) {
|
|
// pseudocode:
|
|
// %10:
|
|
// %11 = load y
|
|
// %12 = %11 < 0
|
|
// [merge: %15]
|
|
// if (%12) {
|
|
// %13:
|
|
// }
|
|
// %14: // Reconvergence hasn't happened by here.
|
|
// %15:
|
|
// return
|
|
ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
|
|
%10 = OpLabel
|
|
%11 = OpLoad %float %x
|
|
%12 = OpFOrdLessThan %bool %11 %zero
|
|
OpSelectionMerge %15 None
|
|
OpBranchConditional %12 %13 %14
|
|
%13 = OpLabel
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
OpBranch %15
|
|
%15 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)"));
|
|
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13));
|
|
// TODO(dongja):
|
|
// EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14));
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14)); // Wrong!
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(15));
|
|
}
|
|
|
|
// The following series of tests makes sure that we find the least fixpoint.
|
|
TEST_F(DivergenceTest, UniformFixpointTest) {
|
|
// pseudocode:
|
|
// %10:
|
|
// %20 = load x
|
|
// %21 = load y
|
|
// do {
|
|
// %11:
|
|
// %12:
|
|
// %13 = phi(%zero from %11, %14 from %16)
|
|
// %14 = %13 + 1
|
|
// %15 = %13 < 1
|
|
// } %16: while (%15)
|
|
// %17:
|
|
ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
|
|
%10 = OpLabel
|
|
%20 = OpLoad %float %x
|
|
%21 = OpLoad %float %y
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
%13 = OpPhi %float %zero %10 %14 %16
|
|
OpLoopMerge %17 %16 None
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
%14 = OpFAdd %float %13 %one
|
|
%15 = OpFOrdLessThan %bool %13 %one
|
|
OpBranch %16
|
|
%16 = OpLabel
|
|
OpBranchConditional %15 %11 %17
|
|
%17 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)"));
|
|
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(11));
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(12));
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(13));
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14));
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(15));
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(16));
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17));
|
|
}
|
|
|
|
TEST_F(DivergenceTest, PartiallyUniformFixpointTest) {
|
|
// pseudocode:
|
|
// %10:
|
|
// %20 = load x
|
|
// %21 = load y
|
|
// do {
|
|
// %11:
|
|
// %12:
|
|
// %13 = phi(%zero from %11, %14 from %16)
|
|
// %14 = %13 + 1
|
|
// %15 = %13 < %21
|
|
// } %16: while (%15)
|
|
// %17:
|
|
ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
|
|
%10 = OpLabel
|
|
%20 = OpLoad %float %x
|
|
%21 = OpLoad %float %y
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
%13 = OpPhi %float %zero %10 %14 %16
|
|
OpLoopMerge %17 %16 None
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
%14 = OpFAdd %float %13 %one
|
|
%15 = OpFOrdLessThan %bool %13 %21
|
|
OpBranch %16
|
|
%16 = OpLabel
|
|
OpBranchConditional %15 %11 %17
|
|
%17 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)"));
|
|
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
|
|
EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(11));
|
|
EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(12));
|
|
EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(13));
|
|
EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(14));
|
|
EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(15));
|
|
EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(16));
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17));
|
|
}
|
|
|
|
TEST_F(DivergenceTest, DivergentFixpointTest) {
|
|
// pseudocode:
|
|
// %10:
|
|
// %20 = load x
|
|
// %21 = load y
|
|
// do {
|
|
// %11:
|
|
// %12:
|
|
// %13 = phi(%zero from %11, %14 from %16)
|
|
// %14 = %13 + 1
|
|
// %15 = %13 < %20
|
|
// } %16: while (%15)
|
|
// %17:
|
|
ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
|
|
%10 = OpLabel
|
|
%20 = OpLoad %float %x
|
|
%21 = OpLoad %float %y
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
%13 = OpPhi %float %zero %10 %14 %16
|
|
OpLoopMerge %17 %16 None
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
%14 = OpFAdd %float %13 %one
|
|
%15 = OpFOrdLessThan %bool %13 %20
|
|
OpBranch %16
|
|
%16 = OpLabel
|
|
OpBranchConditional %15 %11 %17
|
|
%17 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)"));
|
|
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16));
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17));
|
|
}
|
|
|
|
TEST_F(DivergenceTest, DivergentOverridesPartiallyUniformTest) {
|
|
// pseudocode:
|
|
// %10:
|
|
// %20 = load x
|
|
// %21 = load y
|
|
// %11:
|
|
// do {
|
|
// %12:
|
|
// %13 = phi(%21 from %11, %14 from %16)
|
|
// %14 = %13 + 1
|
|
// %15 = %13 < %20
|
|
// } %16: while (%15)
|
|
// %17:
|
|
ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
|
|
%10 = OpLabel
|
|
%20 = OpLoad %float %x
|
|
%21 = OpLoad %float %y
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
%13 = OpPhi %float %zero %10 %14 %16
|
|
OpLoopMerge %17 %16 None
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
%14 = OpFAdd %float %13 %one
|
|
%15 = OpFOrdLessThan %bool %13 %20
|
|
OpBranch %16
|
|
%16 = OpLabel
|
|
OpBranchConditional %15 %11 %17
|
|
%17 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)"));
|
|
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16));
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17));
|
|
}
|
|
|
|
TEST_F(DivergenceTest, NestedFixpointTest) {
|
|
// pseudocode:
|
|
// %10:
|
|
// %20 = load x
|
|
// %21 = load y
|
|
// do {
|
|
// %22:
|
|
// %23:
|
|
// %24 = phi(%zero from %22, %25 from %26)
|
|
// %11:
|
|
// do {
|
|
// %12:
|
|
// %13 = phi(%zero from %11, %14 from %16)
|
|
// %14 = %13 + 1
|
|
// %15 = %13 < %24
|
|
// } %16: while (%15)
|
|
// %17:
|
|
// %25 = load x
|
|
// } %26: while (false)
|
|
// %27:
|
|
// return
|
|
ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
|
|
%10 = OpLabel
|
|
%20 = OpLoad %float %x
|
|
%21 = OpLoad %float %y
|
|
OpBranch %22
|
|
%22 = OpLabel
|
|
%24 = OpPhi %float %zero %10 %25 %26
|
|
OpLoopMerge %27 %26 None
|
|
OpBranch %23
|
|
%23 = OpLabel
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
%13 = OpPhi %float %zero %23 %14 %16
|
|
OpLoopMerge %17 %16 None
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
%14 = OpFAdd %float %13 %one
|
|
%15 = OpFOrdLessThan %bool %13 %24
|
|
OpBranch %16
|
|
%16 = OpLabel
|
|
OpBranchConditional %15 %11 %17
|
|
%17 = OpLabel
|
|
%25 = OpLoad %float %x
|
|
OpBranch %26
|
|
%26 = OpLabel
|
|
OpBranchConditional %false %22 %27
|
|
%27 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)"));
|
|
// This test makes sure that divergent values flowing upward can influence the
|
|
// fixpoint of a loop.
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16));
|
|
// Control of the outer loop is still uniform.
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17));
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(22));
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(23));
|
|
// Seed divergent values.
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(24));
|
|
EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(25));
|
|
// Outer loop control.
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(26));
|
|
// Merged.
|
|
EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(27));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace lint
|
|
} // namespace spvtools
|