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