mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-27 02:10:15 +00:00
6c7db9c630
* Handle nested breaks from switches. There was a recent decision made to allow branches to the merge node of a switch even if the switch is not the first enclosing construct. They can be generated by glslang from break statements in switches. Dead branch elimination seems to be the only optimization that will break because of this change, so I will update that optimizations. The change made are: - Track switches in structured cfg analysis. - In Dead branch elimination: - Look for nested breaks that will require a switch instruction. - Rewrite, but don't delete, switchs that are required even if it could be replaced by an unconditional branch. - When looking for the first break, consider the merge of a switch as well. See #2612. * Fix variable names and comments. * Add tests for the struct cfg analysis and switches. * Fix typos in comments.
843 lines
26 KiB
C++
843 lines
26 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 "source/opt/struct_cfg_analysis.h"
|
|
|
|
#include <string>
|
|
|
|
#include "gmock/gmock.h"
|
|
#include "test/opt/assembly_builder.h"
|
|
#include "test/opt/pass_fixture.h"
|
|
#include "test/opt/pass_utils.h"
|
|
|
|
namespace spvtools {
|
|
namespace opt {
|
|
namespace {
|
|
|
|
using StructCFGAnalysisTest = PassTest<::testing::Test>;
|
|
|
|
TEST_F(StructCFGAnalysisTest, BBInSelection) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%bool_undef = OpUndef %bool
|
|
%uint = OpTypeInt 32 0
|
|
%uint_undef = OpUndef %uint
|
|
%void_func = OpTypeFunction %void
|
|
%main = OpFunction %void None %void_func
|
|
%1 = OpLabel
|
|
OpSelectionMerge %3 None
|
|
OpBranchConditional %undef_bool %2 %3
|
|
%2 = OpLabel
|
|
OpBranch %3
|
|
%3 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::unique_ptr<IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
|
|
StructuredCFGAnalysis analysis(context.get());
|
|
|
|
// The header is not in the construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(1), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(1), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
|
|
|
|
// BB2 is in the construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(2), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(2), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
|
|
|
|
// The merge node is not in the construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(3), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
|
|
}
|
|
|
|
TEST_F(StructCFGAnalysisTest, BBInLoop) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%bool_undef = OpUndef %bool
|
|
%uint = OpTypeInt 32 0
|
|
%uint_undef = OpUndef %uint
|
|
%void_func = OpTypeFunction %void
|
|
%main = OpFunction %void None %void_func
|
|
%entry_lab = OpLabel
|
|
OpBranch %1
|
|
%1 = OpLabel
|
|
OpLoopMerge %3 %4 None
|
|
OpBranchConditional %undef_bool %2 %3
|
|
%2 = OpLabel
|
|
OpBranch %3
|
|
%4 = OpLabel
|
|
OpBranch %1
|
|
%3 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::unique_ptr<IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
|
|
StructuredCFGAnalysis analysis(context.get());
|
|
|
|
// The header is not in the construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(1), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(1), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
|
|
|
|
// BB2 is in the construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(2), 1);
|
|
EXPECT_EQ(analysis.MergeBlock(2), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
|
|
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
|
|
|
|
// The merge node is not in the construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(3), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
|
|
|
|
// The continue block is in the construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(4), 1);
|
|
EXPECT_EQ(analysis.MergeBlock(4), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
|
|
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
|
|
}
|
|
|
|
TEST_F(StructCFGAnalysisTest, SelectionInLoop) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%bool_undef = OpUndef %bool
|
|
%uint = OpTypeInt 32 0
|
|
%uint_undef = OpUndef %uint
|
|
%void_func = OpTypeFunction %void
|
|
%main = OpFunction %void None %void_func
|
|
%entry_lab = OpLabel
|
|
OpBranch %1
|
|
%1 = OpLabel
|
|
OpLoopMerge %3 %4 None
|
|
OpBranchConditional %undef_bool %2 %3
|
|
%2 = OpLabel
|
|
OpSelectionMerge %6 None
|
|
OpBranchConditional %undef_bool %5 %6
|
|
%5 = OpLabel
|
|
OpBranch %6
|
|
%6 = OpLabel
|
|
OpBranch %3
|
|
%4 = OpLabel
|
|
OpBranch %1
|
|
%3 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::unique_ptr<IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
|
|
StructuredCFGAnalysis analysis(context.get());
|
|
|
|
// The loop header is not in either construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(1), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(1), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
|
|
|
|
// Selection header is in the loop only.
|
|
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(2), 1);
|
|
EXPECT_EQ(analysis.MergeBlock(2), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
|
|
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
|
|
|
|
// The loop merge node is not in either construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(3), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
|
|
|
|
// The continue block is in the loop only.
|
|
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(4), 1);
|
|
EXPECT_EQ(analysis.MergeBlock(4), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
|
|
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
|
|
|
|
// BB5 is in the selection fist and the loop.
|
|
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
|
|
EXPECT_EQ(analysis.ContainingLoop(5), 1);
|
|
EXPECT_EQ(analysis.MergeBlock(5), 6);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(5), 3);
|
|
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
|
|
|
|
// The selection merge is in the loop only.
|
|
EXPECT_EQ(analysis.ContainingConstruct(6), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(6), 1);
|
|
EXPECT_EQ(analysis.MergeBlock(6), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(6), 3);
|
|
EXPECT_EQ(analysis.ContainingSwitch(6), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
|
|
}
|
|
|
|
TEST_F(StructCFGAnalysisTest, LoopInSelection) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%bool_undef = OpUndef %bool
|
|
%uint = OpTypeInt 32 0
|
|
%uint_undef = OpUndef %uint
|
|
%void_func = OpTypeFunction %void
|
|
%main = OpFunction %void None %void_func
|
|
%entry_lab = OpLabel
|
|
OpBranch %1
|
|
%1 = OpLabel
|
|
OpSelectionMerge %3 None
|
|
OpBranchConditional %undef_bool %2 %3
|
|
%2 = OpLabel
|
|
OpLoopMerge %4 %5 None
|
|
OpBranchConditional %undef_bool %4 %6
|
|
%5 = OpLabel
|
|
OpBranch %2
|
|
%6 = OpLabel
|
|
OpBranch %4
|
|
%4 = OpLabel
|
|
OpBranch %3
|
|
%3 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::unique_ptr<IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
|
|
StructuredCFGAnalysis analysis(context.get());
|
|
|
|
// The selection header is not in either construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(1), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(1), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
|
|
|
|
// Loop header is in the selection only.
|
|
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(2), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(2), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
|
|
|
|
// The selection merge node is not in either construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(3), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
|
|
|
|
// The loop merge is in the selection only.
|
|
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(4), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(4), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
|
|
|
|
// The loop continue target is in the loop.
|
|
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
|
|
EXPECT_EQ(analysis.ContainingLoop(5), 2);
|
|
EXPECT_EQ(analysis.MergeBlock(5), 4);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
|
|
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
|
|
|
|
// BB6 is in the loop.
|
|
EXPECT_EQ(analysis.ContainingConstruct(6), 2);
|
|
EXPECT_EQ(analysis.ContainingLoop(6), 2);
|
|
EXPECT_EQ(analysis.MergeBlock(6), 4);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
|
|
EXPECT_EQ(analysis.ContainingSwitch(6), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
|
|
}
|
|
|
|
TEST_F(StructCFGAnalysisTest, SelectionInSelection) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%bool_undef = OpUndef %bool
|
|
%uint = OpTypeInt 32 0
|
|
%uint_undef = OpUndef %uint
|
|
%void_func = OpTypeFunction %void
|
|
%main = OpFunction %void None %void_func
|
|
%entry_lab = OpLabel
|
|
OpBranch %1
|
|
%1 = OpLabel
|
|
OpSelectionMerge %3 None
|
|
OpBranchConditional %undef_bool %2 %3
|
|
%2 = OpLabel
|
|
OpSelectionMerge %4 None
|
|
OpBranchConditional %undef_bool %4 %5
|
|
%5 = OpLabel
|
|
OpBranch %4
|
|
%4 = OpLabel
|
|
OpBranch %3
|
|
%3 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::unique_ptr<IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
|
|
StructuredCFGAnalysis analysis(context.get());
|
|
|
|
// The outer selection header is not in either construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(1), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(1), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
|
|
|
|
// The inner header is in the outer selection.
|
|
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(2), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(2), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
|
|
|
|
// The outer merge node is not in either construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(3), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
|
|
|
|
// The inner merge is in the outer selection.
|
|
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(4), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(4), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
|
|
|
|
// BB5 is in the inner selection.
|
|
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
|
|
EXPECT_EQ(analysis.ContainingLoop(5), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(5), 4);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(5), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
|
|
}
|
|
|
|
TEST_F(StructCFGAnalysisTest, LoopInLoop) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%bool_undef = OpUndef %bool
|
|
%uint = OpTypeInt 32 0
|
|
%uint_undef = OpUndef %uint
|
|
%void_func = OpTypeFunction %void
|
|
%main = OpFunction %void None %void_func
|
|
%entry_lab = OpLabel
|
|
OpBranch %1
|
|
%1 = OpLabel
|
|
OpLoopMerge %3 %7 None
|
|
OpBranchConditional %undef_bool %2 %3
|
|
%2 = OpLabel
|
|
OpLoopMerge %4 %5 None
|
|
OpBranchConditional %undef_bool %4 %6
|
|
%5 = OpLabel
|
|
OpBranch %2
|
|
%6 = OpLabel
|
|
OpBranch %4
|
|
%4 = OpLabel
|
|
OpBranch %3
|
|
%7 = OpLabel
|
|
OpBranch %1
|
|
%3 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::unique_ptr<IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
|
|
StructuredCFGAnalysis analysis(context.get());
|
|
|
|
// The outer loop header is not in either construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(1), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(1), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
|
|
|
|
// The inner loop header is in the outer loop.
|
|
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(2), 1);
|
|
EXPECT_EQ(analysis.MergeBlock(2), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
|
|
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
|
|
|
|
// The outer merge node is not in either construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(3), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
|
|
|
|
// The inner merge is in the outer loop.
|
|
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(4), 1);
|
|
EXPECT_EQ(analysis.MergeBlock(4), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
|
|
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
|
|
|
|
// The inner continue target is in the inner loop.
|
|
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
|
|
EXPECT_EQ(analysis.ContainingLoop(5), 2);
|
|
EXPECT_EQ(analysis.MergeBlock(5), 4);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
|
|
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
|
|
|
|
// BB6 is in the loop.
|
|
EXPECT_EQ(analysis.ContainingConstruct(6), 2);
|
|
EXPECT_EQ(analysis.ContainingLoop(6), 2);
|
|
EXPECT_EQ(analysis.MergeBlock(6), 4);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
|
|
EXPECT_EQ(analysis.ContainingSwitch(6), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
|
|
|
|
// The outer continue target is in the outer loop.
|
|
EXPECT_EQ(analysis.ContainingConstruct(7), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(7), 1);
|
|
EXPECT_EQ(analysis.MergeBlock(7), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(7), 3);
|
|
EXPECT_EQ(analysis.ContainingSwitch(7), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(7), 0);
|
|
}
|
|
|
|
TEST_F(StructCFGAnalysisTest, KernelTest) {
|
|
const std::string text = R"(
|
|
OpCapability Kernel
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%bool_undef = OpUndef %bool
|
|
%void_func = OpTypeFunction %void
|
|
%main = OpFunction %void None %void_func
|
|
%1 = OpLabel
|
|
OpBranchConditional %undef_bool %2 %3
|
|
%2 = OpLabel
|
|
OpBranch %3
|
|
%3 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::unique_ptr<IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
|
|
StructuredCFGAnalysis analysis(context.get());
|
|
|
|
// No structured control flow, so none of the basic block are in any
|
|
// construct.
|
|
for (uint32_t i = 1; i <= 3; i++) {
|
|
EXPECT_EQ(analysis.ContainingConstruct(i), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(i), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(i), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(i), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(i), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(i), 0);
|
|
}
|
|
}
|
|
|
|
TEST_F(StructCFGAnalysisTest, EmptyFunctionTest) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical GLSL450
|
|
OpDecorate %func LinkageAttributes "x" Import
|
|
%void = OpTypeVoid
|
|
%void_fn = OpTypeFunction %void
|
|
%func = OpFunction %void None %void_fn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::unique_ptr<IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
|
|
// #2451: This segfaulted on empty functions.
|
|
StructuredCFGAnalysis analysis(context.get());
|
|
}
|
|
|
|
TEST_F(StructCFGAnalysisTest, BBInSwitch) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%bool_undef = OpUndef %bool
|
|
%uint = OpTypeInt 32 0
|
|
%uint_undef = OpUndef %uint
|
|
%void_func = OpTypeFunction %void
|
|
%main = OpFunction %void None %void_func
|
|
%1 = OpLabel
|
|
OpSelectionMerge %3 None
|
|
OpSwitch %uint_undef %2 0 %3
|
|
%2 = OpLabel
|
|
OpBranch %3
|
|
%3 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::unique_ptr<IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
|
|
StructuredCFGAnalysis analysis(context.get());
|
|
|
|
// The header is not in the construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(1), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(1), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
|
|
|
|
// BB2 is in the construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(2), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(2), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(2), 1);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(2), 3);
|
|
|
|
// The merge node is not in the construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(3), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
|
|
}
|
|
|
|
TEST_F(StructCFGAnalysisTest, LoopInSwitch) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%bool_undef = OpUndef %bool
|
|
%uint = OpTypeInt 32 0
|
|
%uint_undef = OpUndef %uint
|
|
%void_func = OpTypeFunction %void
|
|
%main = OpFunction %void None %void_func
|
|
%entry_lab = OpLabel
|
|
OpBranch %1
|
|
%1 = OpLabel
|
|
OpSelectionMerge %3 None
|
|
OpSwitch %uint_undef %2 1 %3
|
|
%2 = OpLabel
|
|
OpLoopMerge %4 %5 None
|
|
OpBranchConditional %undef_bool %4 %6
|
|
%5 = OpLabel
|
|
OpBranch %2
|
|
%6 = OpLabel
|
|
OpBranch %4
|
|
%4 = OpLabel
|
|
OpBranch %3
|
|
%3 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::unique_ptr<IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
|
|
StructuredCFGAnalysis analysis(context.get());
|
|
|
|
// The selection header is not in either construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(1), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(1), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
|
|
|
|
// Loop header is in the selection only.
|
|
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(2), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(2), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(2), 1);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(2), 3);
|
|
|
|
// The selection merge node is not in either construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(3), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
|
|
|
|
// The loop merge is in the selection only.
|
|
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(4), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(4), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(4), 1);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(4), 3);
|
|
|
|
// The loop continue target is in the loop.
|
|
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
|
|
EXPECT_EQ(analysis.ContainingLoop(5), 2);
|
|
EXPECT_EQ(analysis.MergeBlock(5), 4);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
|
|
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
|
|
|
|
// BB6 is in the loop.
|
|
EXPECT_EQ(analysis.ContainingConstruct(6), 2);
|
|
EXPECT_EQ(analysis.ContainingLoop(6), 2);
|
|
EXPECT_EQ(analysis.MergeBlock(6), 4);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
|
|
EXPECT_EQ(analysis.ContainingSwitch(6), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
|
|
}
|
|
|
|
TEST_F(StructCFGAnalysisTest, SelectionInSwitch) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%bool_undef = OpUndef %bool
|
|
%uint = OpTypeInt 32 0
|
|
%uint_undef = OpUndef %uint
|
|
%void_func = OpTypeFunction %void
|
|
%main = OpFunction %void None %void_func
|
|
%entry_lab = OpLabel
|
|
OpBranch %1
|
|
%1 = OpLabel
|
|
OpSelectionMerge %3 None
|
|
OpSwitch %uint_undef %2 10 %3
|
|
%2 = OpLabel
|
|
OpSelectionMerge %4 None
|
|
OpBranchConditional %undef_bool %4 %5
|
|
%5 = OpLabel
|
|
OpBranch %4
|
|
%4 = OpLabel
|
|
OpBranch %3
|
|
%3 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::unique_ptr<IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
|
|
StructuredCFGAnalysis analysis(context.get());
|
|
|
|
// The outer selection header is not in either construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(1), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(1), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
|
|
|
|
// The inner header is in the outer selection.
|
|
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(2), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(2), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(2), 1);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(2), 3);
|
|
|
|
// The outer merge node is not in either construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(3), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
|
|
|
|
// The inner merge is in the outer selection.
|
|
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(4), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(4), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(4), 1);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(4), 3);
|
|
|
|
// BB5 is in the inner selection.
|
|
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
|
|
EXPECT_EQ(analysis.ContainingLoop(5), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(5), 4);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(5), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(5), 1);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(5), 3);
|
|
}
|
|
|
|
TEST_F(StructCFGAnalysisTest, SwitchInSelection) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%bool_undef = OpUndef %bool
|
|
%uint = OpTypeInt 32 0
|
|
%uint_undef = OpUndef %uint
|
|
%void_func = OpTypeFunction %void
|
|
%main = OpFunction %void None %void_func
|
|
%entry_lab = OpLabel
|
|
OpBranch %1
|
|
%1 = OpLabel
|
|
OpSelectionMerge %3 None
|
|
OpBranchConditional %undef_bool %2 %3
|
|
%2 = OpLabel
|
|
OpSelectionMerge %4 None
|
|
OpSwitch %uint_undef %4 7 %5
|
|
%5 = OpLabel
|
|
OpBranch %4
|
|
%4 = OpLabel
|
|
OpBranch %3
|
|
%3 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::unique_ptr<IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
|
|
StructuredCFGAnalysis analysis(context.get());
|
|
|
|
// The outer selection header is not in either construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(1), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(1), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
|
|
|
|
// The inner header is in the outer selection.
|
|
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(2), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(2), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
|
|
|
|
// The outer merge node is not in either construct.
|
|
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
|
|
EXPECT_EQ(analysis.ContainingLoop(3), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
|
|
|
|
// The inner merge is in the outer selection.
|
|
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
|
|
EXPECT_EQ(analysis.ContainingLoop(4), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(4), 3);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
|
|
|
|
// BB5 is in the inner selection.
|
|
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
|
|
EXPECT_EQ(analysis.ContainingLoop(5), 0);
|
|
EXPECT_EQ(analysis.MergeBlock(5), 4);
|
|
EXPECT_EQ(analysis.LoopMergeBlock(5), 0);
|
|
EXPECT_EQ(analysis.ContainingSwitch(5), 2);
|
|
EXPECT_EQ(analysis.SwitchMergeBlock(5), 4);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace opt
|
|
} // namespace spvtools
|